mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-08 19:42:12 +00:00
media: cap a Matroska track's content encoding chain (#541)
A track could list any number of ContentEncoding entries, and readers walk the whole list for every frame they return (frames_readable, stripped_header). A track with more than 16 entries, far beyond the one or two a muxer writes, is now treated as having an unsupported compression, so its frames are returned as stored and the list is never long.
This commit is contained in:
1 parent
6865897f14
commit
32b2fd8adf
2 files changed
+21
No files matched your search
@@ -28,6 +28,11 @@ const MAX_META_DEPTH: usize = 64;
|
||||
/// produce. Real files strip a few bytes of codec header; a longer one is treated as an
|
||||
/// unsupported compression and its frames are not reconstructed.
|
||||
const MAX_STRIPPED_HEADER: usize = 256;
|
||||
/// Most `ContentEncoding` entries a track may list. A muxer writes one or two (compression,
|
||||
/// encryption); readers check the whole chain for every frame they return, so a longer chain
|
||||
/// would multiply that work by the frame count. A track with more is treated as having an
|
||||
/// unsupported compression, and its frames are returned as stored.
|
||||
const MAX_CONTENT_ENCODINGS: usize = 16;
|
||||
/// Largest accepted `TimestampScale` (about 4.3 s per tick). It keeps tick to nanosecond
|
||||
/// arithmetic on 64-bit timestamps within `i128`.
|
||||
const MAX_TIMESTAMP_SCALE: u64 = 1 << 32;
|
||||
@@ -459,6 +464,9 @@ fn parse_audio(d: &[u8]) -> AudioInfo {
|
||||
}
|
||||
|
||||
fn parse_encodings(d: &[u8]) -> Vec<ContentEncoding> {
|
||||
if Children::new(d).filter(|e| e.id == CONTENT_ENCODING).nth(MAX_CONTENT_ENCODINGS).is_some() {
|
||||
return vec![ContentEncoding::Compression { algo: u64::MAX, settings: Vec::new(), scope: 1 }];
|
||||
}
|
||||
let mut list: Vec<(u64, ContentEncoding)> = Vec::new();
|
||||
for ce in Children::new(d).filter(|e| e.id == CONTENT_ENCODING) {
|
||||
let (mut order, mut scope, mut ty) = (0u64, 1u64, 0u64);
|
||||
@@ -628,6 +636,18 @@ mod metadata_depth_tests {
|
||||
parsed.remove(0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_long_encoding_chain_is_not_rechecked_for_every_frame() {
|
||||
let strip: (&[u8], u64) = (b"", 2);
|
||||
let most = track_with_private(b"private", &[strip; MAX_CONTENT_ENCODINGS]);
|
||||
assert_eq!(most.encodings.len(), MAX_CONTENT_ENCODINGS);
|
||||
assert!(most.frames_readable());
|
||||
let many = track_with_private(b"private", &vec![strip; 10_000]);
|
||||
assert!(matches!(&many.encodings[..], [ContentEncoding::Compression { scope: 1, .. }]), "{:?}", many.encodings.len());
|
||||
assert!(!many.frames_readable());
|
||||
assert_eq!(many.codec_private, b"private");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codec_private_stripped_headers_are_restored_in_reverse_order() {
|
||||
let strips: [(&[u8], u64); 4] = [(b"first", 2), (b"", 2), (b"second", 2), (b"frame", 1)];
|
||||
|
||||
@@ -169,6 +169,7 @@ pub enum ContentEncoding {
|
||||
/// `ContentCompAlgo` 3: the given bytes were stripped from the start of every frame.
|
||||
HeaderStripping { bytes: Vec<u8>, scope: u64 },
|
||||
/// Other compression (0 zlib, 1 bzlib, 2 lzo1x): not supported for reading frame data.
|
||||
/// `algo` is `u64::MAX` for a chain of more encodings than a reader checks.
|
||||
Compression { algo: u64, settings: Vec<u8>, scope: u64 },
|
||||
/// Encryption (not supported for reading frame data).
|
||||
Encryption { scope: u64 },
|
||||
|
||||
Reference in new issue
Block a user