rc2: macOS cross-compile fix + security/recovery hardening
- build.rs: pass target -arch to cc so macos_shim cross-compiles (x86_64-apple-darwin) - AACS/CSS: unit-aligned decrypting sweep; per-VTS CSS title keys (hard-fail on wrong VTS); reject truncated Unit_Key_RO; AACS 2.0 sig-verify skip; CSS bus-auth random nonce - recovery: gap-filling mapfile load; sweep/copy resume reconciliation; stale-mapfile abort; patch wedge/damage-window range reset - mux: TS continuity + PSI CC desync guards; HEVC numTemporalLayers clamp; MPEG-2 pending byte-cap; PS parse_pts marker-bit validation; HdrFormat strict parse; Unknown-variant metadata - net/keydb: network:// SSRF parity (IPv4-mapped, CGNAT, 0.0.0.0/8, Class-E); bounded keydb header read + size cap + error context - io: durable mapfile fsync; NFS writeback degrade; sync_file_range error capture; Windows SCSI u32 transfer guard
This commit is contained in:
@@ -447,7 +447,10 @@ impl CodecParser for HevcParser {
|
||||
// numTemporalLayers u(3) = sps_max_sub_layers_minus1 + 1
|
||||
// temporalIdNested u(1) = sps_temporal_id_nesting_flag
|
||||
// lengthSizeMinusOne u(2) = 3 (4-byte length prefix)
|
||||
let num_temporal_layers = (chroma.max_sub_layers_minus1 + 1) & 0x07;
|
||||
// sps_max_sub_layers_minus1 is u(3) (0..7), so +1 is 1..8. The hvcC
|
||||
// numTemporalLayers field is u(3) (0..7); the max legal value (8) is
|
||||
// saturated to 7 rather than wrapping to 0 via the & 0x07 mask.
|
||||
let num_temporal_layers = chroma.max_sub_layers_minus1.saturating_add(1).min(7) & 0x07;
|
||||
let temporal_id_nested = chroma.temporal_id_nesting_flag & 0x01;
|
||||
record.push((num_temporal_layers << 3) | (temporal_id_nested << 2) | 0x03);
|
||||
// numOfArrays
|
||||
|
||||
@@ -105,7 +105,7 @@ impl PassthroughParser {
|
||||
|
||||
impl CodecParser for PassthroughParser {
|
||||
fn parse(&mut self, pes: &PesPacket) -> Vec<Frame> {
|
||||
let pts_ns = pes.pts.map(pts_to_ns).unwrap_or(0);
|
||||
let pts_ns = pes.pts.or(pes.dts).map(pts_to_ns).unwrap_or(0);
|
||||
vec![Frame {
|
||||
pts_ns,
|
||||
keyframe: self.keyframe,
|
||||
|
||||
+26
-1
@@ -58,6 +58,13 @@ const MAX_AU_BUFFER: usize = 8 * 1024 * 1024;
|
||||
/// ever arrives within the cap, buffered frames are released on a 0 base.
|
||||
const MAX_PENDING_FRAMES: usize = 600;
|
||||
|
||||
/// Byte cap on frames held awaiting the first PES PTS anchor. `MAX_PENDING_FRAMES`
|
||||
/// alone bounds the *count*, but 600 full HD/UHD intra pictures can be ~1 GiB.
|
||||
/// Mirror the AC-3/DTS/PGS byte caps: once the held data exceeds this, release
|
||||
/// on the 0 base instead of accumulating further. 8 MiB ≈ a few large I-frames,
|
||||
/// far more than the ~15 frames a well-formed DVD buffers before its first PTS.
|
||||
const MAX_PENDING_BYTES: usize = 8 * 1024 * 1024;
|
||||
|
||||
/// Frame rate table (index from sequence header frame_rate_code).
|
||||
const FRAME_RATES: [(u32, u32); 9] = [
|
||||
(0, 1), // 0: forbidden
|
||||
@@ -117,6 +124,9 @@ pub struct Mpeg2Parser {
|
||||
/// sequence whose PTS lands a few frames in; buffering until the anchor lets
|
||||
/// those leading frames take the disc's real timeline instead of a 0 base.
|
||||
pending: Vec<(u64, Frame)>,
|
||||
/// Accumulated `data.len()` of frames currently in `pending`. Bounds the
|
||||
/// pre-anchor hold by BYTES, not just frame count (see [`MAX_PENDING_BYTES`]).
|
||||
pending_bytes: usize,
|
||||
}
|
||||
|
||||
impl Default for Mpeg2Parser {
|
||||
@@ -139,6 +149,7 @@ impl Mpeg2Parser {
|
||||
anchor_index: None,
|
||||
anchor_pts: 0,
|
||||
pending: Vec::new(),
|
||||
pending_bytes: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,6 +298,7 @@ impl Mpeg2Parser {
|
||||
p + (di as i64 - display_index as i64) * self.frame_duration_ns;
|
||||
out.push(held);
|
||||
}
|
||||
self.pending_bytes = 0;
|
||||
frame.pts_ns = p;
|
||||
out.push(frame);
|
||||
}
|
||||
@@ -296,13 +308,25 @@ impl Mpeg2Parser {
|
||||
+ (display_index as i64 - ai as i64) * self.frame_duration_ns;
|
||||
out.push(frame);
|
||||
}
|
||||
None if self.pending.len() < MAX_PENDING_FRAMES => {
|
||||
None if self.pending.len() < MAX_PENDING_FRAMES
|
||||
&& self.pending_bytes < MAX_PENDING_BYTES =>
|
||||
{
|
||||
// No anchor yet — hold so leading frames get the
|
||||
// disc's real timeline once the first PTS arrives,
|
||||
// not a 0 base.
|
||||
self.pending_bytes += frame.data.len();
|
||||
self.pending.push((display_index, frame));
|
||||
}
|
||||
None => {
|
||||
// Hold cap (count OR bytes) reached without a PTS
|
||||
// anchor ever arriving. Release everything held so
|
||||
// far on the 0-base timeline rather than growing the
|
||||
// buffer unbounded, then emit this frame the same way.
|
||||
for (di, mut held) in self.pending.drain(..) {
|
||||
held.pts_ns = di as i64 * self.frame_duration_ns;
|
||||
out.push(held);
|
||||
}
|
||||
self.pending_bytes = 0;
|
||||
frame.pts_ns = display_index as i64 * self.frame_duration_ns;
|
||||
out.push(frame);
|
||||
}
|
||||
@@ -364,6 +388,7 @@ impl CodecParser for Mpeg2Parser {
|
||||
frame.pts_ns = di as i64 * self.frame_duration_ns;
|
||||
out.push(frame);
|
||||
}
|
||||
self.pending_bytes = 0;
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user