Audit fixes + DVD support foundation (IFO, PS demux, MPEG-2, CSS crack)
Audit fixes (14 critical, 22 warnings): - UDF: bounds checks on all ICB/FID parsing from disc data - SCSI Linux: saturating_sub on residual, CDB length guard, buffer size guard - SCSI macOS: SCSITaskStatus u32 (was u8 — stack corruption) - AACS: EC mod_inv returns infinity instead of panic, key reduced mod n - AACS: do_handshake tries all host certs (was returning on first failure) - H.264: bounds check on SPS < 4 bytes - ContentReader: error on missing unit key (was zero-fill) - KEYDB: flat redirect loop (was recursive), 100MB response limit, Windows HOME fallback - ISO writer: AVDP extent order, partition length, allocation cap - Network: removed TCP_NODELAY on bulk stream - MKV: guard on u64::MAX seek - disc.rs: saturating_sub on extent offset, simplified dead region code - cargo fmt (610 violations), cargo clippy --fix (55 auto-fixes) DVD support (new files): - src/ifo.rs — IFO parser (VIDEO_TS.IFO, VTS_XX_0.IFO, PGC chains, cells, streams) — 13 tests - src/mux/ps.rs — MPEG-2 Program Stream demuxer (pack headers, PES, private stream 1) — 12 tests - src/mux/codec/mpeg2.rs — MPEG-2 video parser (sequence headers, I-frame detection) — 15 tests - src/css/crack.rs — split-attack algorithm (LFSR cipher needs verification — test ignored) 226 tests total (was 186), 1 ignored (CSS crack needs cipher verification).
This commit is contained in:
+129
-30
@@ -3,10 +3,10 @@
|
||||
mod variant_a;
|
||||
mod variant_b;
|
||||
|
||||
use super::PlatformDriver;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::profile::DriveProfile;
|
||||
use crate::scsi::{self, DataDirection, ScsiTransport};
|
||||
use super::PlatformDriver;
|
||||
|
||||
// ── Variant constants ──────────────────────────────────────────────────
|
||||
// Every vendor command: 3C [mode] [buffer_id] [sub_cmd] [addr] ...
|
||||
@@ -58,7 +58,9 @@ impl Mt1959 {
|
||||
(MODE_A, BUFFER_ID_A)
|
||||
};
|
||||
Mt1959 {
|
||||
profile, mode, buffer_id,
|
||||
profile,
|
||||
mode,
|
||||
buffer_id,
|
||||
unlocked: false,
|
||||
probed: false,
|
||||
}
|
||||
@@ -68,20 +70,35 @@ impl Mt1959 {
|
||||
|
||||
pub(crate) fn read_buffer_sub(&self, sub_cmd: u8, address: u16, length: u8) -> [u8; 10] {
|
||||
[
|
||||
SCSI_READ_BUFFER, self.mode, self.buffer_id, sub_cmd,
|
||||
(address >> 8) as u8, address as u8,
|
||||
0x00, 0x00, length, 0x00,
|
||||
SCSI_READ_BUFFER,
|
||||
self.mode,
|
||||
self.buffer_id,
|
||||
sub_cmd,
|
||||
(address >> 8) as u8,
|
||||
address as u8,
|
||||
0x00,
|
||||
0x00,
|
||||
length,
|
||||
0x00,
|
||||
]
|
||||
}
|
||||
|
||||
pub(crate) fn read_buffer_probe(
|
||||
&self, scsi: &mut dyn ScsiTransport,
|
||||
sub_cmd: u8, address: u16, buf: &mut [u8], expected: usize,
|
||||
&self,
|
||||
scsi: &mut dyn ScsiTransport,
|
||||
sub_cmd: u8,
|
||||
address: u16,
|
||||
buf: &mut [u8],
|
||||
expected: usize,
|
||||
) -> Result<usize> {
|
||||
let cdb = self.read_buffer_sub(sub_cmd, address, expected as u8);
|
||||
let result = scsi.execute(&cdb, DataDirection::FromDevice, buf, 5_000)?;
|
||||
if result.bytes_transferred != expected {
|
||||
return Err(Error::ScsiError { opcode: SCSI_READ_BUFFER, status: 0xFF, sense_key: 0 });
|
||||
return Err(Error::ScsiError {
|
||||
opcode: SCSI_READ_BUFFER,
|
||||
status: 0xFF,
|
||||
sense_key: 0,
|
||||
});
|
||||
}
|
||||
Ok(result.bytes_transferred)
|
||||
}
|
||||
@@ -97,9 +114,16 @@ impl Mt1959 {
|
||||
|
||||
pub(crate) fn do_unlock(&mut self, scsi: &mut dyn ScsiTransport) -> Result<Vec<u8>> {
|
||||
let cdb = [
|
||||
0x3C, self.mode, self.buffer_id,
|
||||
SUB_CMD_UNLOCK, 0x00, 0x00,
|
||||
0x00, 0x00, UNLOCK_RESPONSE_SIZE, 0x00,
|
||||
0x3C,
|
||||
self.mode,
|
||||
self.buffer_id,
|
||||
SUB_CMD_UNLOCK,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
UNLOCK_RESPONSE_SIZE,
|
||||
0x00,
|
||||
];
|
||||
let mut response = vec![0u8; UNLOCK_RESPONSE_SIZE as usize];
|
||||
scsi.execute(&cdb, DataDirection::FromDevice, &mut response, 30_000)?;
|
||||
@@ -112,7 +136,8 @@ impl Mt1959 {
|
||||
}
|
||||
|
||||
if response.len() >= FIRMWARE_ACTIVE_OFFSET + 4
|
||||
&& response[FIRMWARE_ACTIVE_OFFSET..FIRMWARE_ACTIVE_OFFSET + 4] != FIRMWARE_ACTIVE_SIG {
|
||||
&& response[FIRMWARE_ACTIVE_OFFSET..FIRMWARE_ACTIVE_OFFSET + 4] != FIRMWARE_ACTIVE_SIG
|
||||
{
|
||||
return Err(Error::UnlockFailed);
|
||||
}
|
||||
|
||||
@@ -123,16 +148,30 @@ impl Mt1959 {
|
||||
fn validate(&self, scsi: &mut dyn ScsiTransport) -> Result<()> {
|
||||
for _attempt in 0..5 {
|
||||
let cdb = [
|
||||
0x3C, self.mode, self.buffer_id,
|
||||
SUB_CMD_UNLOCK, 0x00, 0x00,
|
||||
0x00, 0x00, VALIDATE_RESPONSE_SIZE, 0x00,
|
||||
0x3C,
|
||||
self.mode,
|
||||
self.buffer_id,
|
||||
SUB_CMD_UNLOCK,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
VALIDATE_RESPONSE_SIZE,
|
||||
0x00,
|
||||
];
|
||||
let mut resp = [0u8; 4];
|
||||
if scsi.execute(&cdb, DataDirection::FromDevice, &mut resp, 5_000).is_ok() {
|
||||
if scsi
|
||||
.execute(&cdb, DataDirection::FromDevice, &mut resp, 5_000)
|
||||
.is_ok()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err(Error::ScsiError { opcode: SCSI_READ_BUFFER, status: 0xFF, sense_key: 0 })
|
||||
Err(Error::ScsiError {
|
||||
opcode: SCSI_READ_BUFFER,
|
||||
status: 0xFF,
|
||||
sense_key: 0,
|
||||
})
|
||||
}
|
||||
|
||||
// ── Init (unlock + firmware) ───────────────────────────────────────
|
||||
@@ -141,7 +180,10 @@ impl Mt1959 {
|
||||
let mut unlocked = false;
|
||||
for _attempt in 0..6 {
|
||||
match self.do_unlock(scsi) {
|
||||
Ok(_) => { unlocked = true; break; }
|
||||
Ok(_) => {
|
||||
unlocked = true;
|
||||
break;
|
||||
}
|
||||
Err(Error::SignatureMismatch { .. }) => {
|
||||
return Err(Error::UnlockFailed);
|
||||
}
|
||||
@@ -151,7 +193,10 @@ impl Mt1959 {
|
||||
} else {
|
||||
variant_b::load_firmware(self, scsi).is_ok()
|
||||
};
|
||||
if ok { unlocked = true; break; }
|
||||
if ok {
|
||||
unlocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,22 +212,48 @@ impl Mt1959 {
|
||||
/// per region. Two passes, then SET_CD_SPEED(max). After this the
|
||||
/// drive manages per-zone speeds internally.
|
||||
fn run_probe(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
|
||||
if !self.unlocked { self.do_unlock(scsi)?; }
|
||||
if !self.unlocked {
|
||||
self.do_unlock(scsi)?;
|
||||
}
|
||||
|
||||
// Detect disc type from capacity to select probe mode.
|
||||
// BD: 3C 01 44 12 01 00 00 00 04 00 (init_addr = 0x0100)
|
||||
// UHD: 3C 01 44 12 02 00 00 00 04 00 (init_addr = 0x0200)
|
||||
// Verified from MakeMKV strace: BD and UHD use different init addresses.
|
||||
let cap_cdb = [SCSI_READ_CAPACITY, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
let cap_cdb = [
|
||||
SCSI_READ_CAPACITY,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
];
|
||||
let mut cap_buf = [0u8; READ_CAPACITY_RESPONSE_SIZE];
|
||||
let disc_sectors = if scsi.execute(&cap_cdb, DataDirection::FromDevice, &mut cap_buf, 5_000).is_ok() {
|
||||
let disc_sectors = if scsi
|
||||
.execute(&cap_cdb, DataDirection::FromDevice, &mut cap_buf, 5_000)
|
||||
.is_ok()
|
||||
{
|
||||
u32::from_be_bytes([cap_buf[0], cap_buf[1], cap_buf[2], cap_buf[3]]) + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let init_addr = if disc_sectors > UHD_SECTOR_THRESHOLD { INIT_ADDR_UHD } else { INIT_ADDR_BD };
|
||||
let init_addr = if disc_sectors > UHD_SECTOR_THRESHOLD {
|
||||
INIT_ADDR_UHD
|
||||
} else {
|
||||
INIT_ADDR_BD
|
||||
};
|
||||
let mut init_resp = [0u8; PROBE_RESPONSE_SIZE as usize];
|
||||
let _ = self.read_buffer_probe(scsi, SUB_CMD_INIT, init_addr, &mut init_resp, PROBE_RESPONSE_SIZE as usize);
|
||||
let _ = self.read_buffer_probe(
|
||||
scsi,
|
||||
SUB_CMD_INIT,
|
||||
init_addr,
|
||||
&mut init_resp,
|
||||
PROBE_RESPONSE_SIZE as usize,
|
||||
);
|
||||
|
||||
self.validate(scsi)?;
|
||||
|
||||
@@ -190,8 +261,21 @@ impl Mt1959 {
|
||||
let mut addr: u16 = 0;
|
||||
while addr < PROBE_COARSE_END {
|
||||
let mut resp = [0u8; PROBE_RESPONSE_SIZE as usize];
|
||||
if self.read_buffer_probe(scsi, SUB_CMD_PROBE, addr, &mut resp, PROBE_RESPONSE_SIZE as usize).is_err() {
|
||||
return Err(Error::ScsiError { opcode: SCSI_READ_BUFFER, status: 0xFF, sense_key: 0 });
|
||||
if self
|
||||
.read_buffer_probe(
|
||||
scsi,
|
||||
SUB_CMD_PROBE,
|
||||
addr,
|
||||
&mut resp,
|
||||
PROBE_RESPONSE_SIZE as usize,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return Err(Error::ScsiError {
|
||||
opcode: SCSI_READ_BUFFER,
|
||||
status: 0xFF,
|
||||
sense_key: 0,
|
||||
});
|
||||
}
|
||||
addr = addr.wrapping_add(PROBE_STEP);
|
||||
}
|
||||
@@ -200,7 +284,16 @@ impl Mt1959 {
|
||||
let mut addr: u32 = 0;
|
||||
while addr < PROBE_FINE_END {
|
||||
let mut resp = [0u8; PROBE_RESPONSE_SIZE as usize];
|
||||
if self.read_buffer_probe(scsi, SUB_CMD_PROBE, addr as u16, &mut resp, PROBE_RESPONSE_SIZE as usize).is_err() {
|
||||
if self
|
||||
.read_buffer_probe(
|
||||
scsi,
|
||||
SUB_CMD_PROBE,
|
||||
addr as u16,
|
||||
&mut resp,
|
||||
PROBE_RESPONSE_SIZE as usize,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
break;
|
||||
}
|
||||
addr += PROBE_STEP as u32;
|
||||
@@ -218,13 +311,19 @@ impl Mt1959 {
|
||||
|
||||
impl PlatformDriver for Mt1959 {
|
||||
fn init(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
|
||||
if self.unlocked { return Ok(()); }
|
||||
if self.unlocked {
|
||||
return Ok(());
|
||||
}
|
||||
self.run_init(scsi)
|
||||
}
|
||||
|
||||
fn probe_disc(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
|
||||
if !self.unlocked { self.run_init(scsi)?; }
|
||||
if self.probed { return Ok(()); }
|
||||
if !self.unlocked {
|
||||
self.run_init(scsi)?;
|
||||
}
|
||||
if self.probed {
|
||||
return Ok(());
|
||||
}
|
||||
self.run_probe(scsi)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user