Skip to content

Commit 64ed99d

Browse files
Zaggy1024kinetiknz
authored andcommitted
Allow skipped top-level boxes to extend past EOF.
This allows files with garbage appended to the end to still load.
1 parent 2b572e8 commit 64ed99d

File tree

5 files changed

+49
-10
lines changed

5 files changed

+49
-10
lines changed

mp4parse/src/lib.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2273,11 +2273,11 @@ impl<'a, T: Read> BoxIter<'a, T> {
22732273
fn next_box(&mut self) -> Result<Option<BMFFBox<T>>> {
22742274
let r = read_box_header(self.src);
22752275
match r {
2276-
Ok(h) => Ok(Some(BMFFBox {
2276+
Ok(Some(h)) => Ok(Some(BMFFBox {
22772277
head: h,
22782278
content: self.src.take(h.size.saturating_sub(h.offset)),
22792279
})),
2280-
Err(Error::UnexpectedEOF) => Ok(None),
2280+
Ok(None) => Ok(None),
22812281
Err(e) => Err(e),
22822282
}
22832283
}
@@ -2332,8 +2332,12 @@ impl<'a, T> Drop for BMFFBox<'a, T> {
23322332
/// skip unknown or uninteresting boxes.
23332333
///
23342334
/// See ISOBMFF (ISO 14496-12:2020) § 4.2
2335-
fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
2336-
let size32 = be_u32(src)?;
2335+
fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<Option<BoxHeader>> {
2336+
let size32 = match be_u32(src) {
2337+
Ok(v) => v,
2338+
Err(Error::UnexpectedEOF) => return Ok(None),
2339+
Err(error) => return Err(error),
2340+
};
23372341
let name = BoxType::from(be_u32(src)?);
23382342
let size = match size32 {
23392343
// valid only for top-level box and indicates it's the last box in the file. usually mdat.
@@ -2382,12 +2386,12 @@ fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
23822386
None
23832387
};
23842388
assert!(offset <= size || size == 0);
2385-
Ok(BoxHeader {
2389+
Ok(Some(BoxHeader {
23862390
name,
23872391
size,
23882392
offset,
23892393
uuid,
2390-
})
2394+
}))
23912395
}
23922396

23932397
/// Parse the extra header fields for a full box.
@@ -2521,7 +2525,19 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
25212525
let mut image_sequence = None;
25222526
let mut media_storage = TryVec::new();
25232527

2524-
while let Some(mut b) = iter.next_box()? {
2528+
loop {
2529+
let mut b = match iter.next_box() {
2530+
Ok(Some(b)) => b,
2531+
Ok(_) => break,
2532+
Err(Error::UnexpectedEOF) => {
2533+
if strictness == ParseStrictness::Strict {
2534+
return Err(Error::UnexpectedEOF);
2535+
}
2536+
break;
2537+
}
2538+
Err(error) => return Err(error),
2539+
};
2540+
25252541
trace!("read_avif parsing {:?} box", b.head.name);
25262542
match b.head.name {
25272543
BoxType::MetadataBox => {
@@ -2564,7 +2580,14 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
25642580
};
25652581
media_storage.push(DataBox::from_mdat(file_offset, data))?;
25662582
}
2567-
_ => skip_box_content(&mut b)?,
2583+
_ => {
2584+
let result = skip_box_content(&mut b);
2585+
// Allow garbage at EOF if we aren't in strict mode.
2586+
if b.bytes_left() > 0 && strictness != ParseStrictness::Strict {
2587+
break;
2588+
}
2589+
result?;
2590+
}
25682591
}
25692592

25702593
check_parser_state!(b.content);

mp4parse/src/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ where
9393
#[test]
9494
fn read_box_header_short() {
9595
let mut stream = make_box(BoxSize::Short(8), b"test", |s| s);
96-
let header = super::read_box_header(&mut stream).unwrap();
96+
let header = super::read_box_header(&mut stream).unwrap().unwrap();
9797
assert_eq!(header.name, BoxType::UnknownBox(0x7465_7374)); // "test"
9898
assert_eq!(header.size, 8);
9999
assert!(header.uuid.is_none());
@@ -102,7 +102,7 @@ fn read_box_header_short() {
102102
#[test]
103103
fn read_box_header_long() {
104104
let mut stream = make_box(BoxSize::Long(16), b"test", |s| s);
105-
let header = super::read_box_header(&mut stream).unwrap();
105+
let header = super::read_box_header(&mut stream).unwrap().unwrap();
106106
assert_eq!(header.name, BoxType::UnknownBox(0x7465_7374)); // "test"
107107
assert_eq!(header.size, 16);
108108
assert!(header.uuid.is_none());

mp4parse/tests/public.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ static IMAGE_AVIF_CLAP_MISSING_ESSENTIAL: &str = "tests/clap-missing-essential.a
6868
static IMAGE_AVIF_UNKNOWN_MDAT_SIZE: &str = "tests/unknown_mdat.avif";
6969
static IMAGE_AVIF_UNKNOWN_MDAT_SIZE_IN_OVERSIZED_META: &str =
7070
"tests/unknown_mdat_in_oversized_meta.avif";
71+
static IMAGE_AVIF_VALID_WITH_GARBAGE_OVERREAD_AT_END: &str =
72+
"tests/valid_with_garbage_overread.avif";
73+
static IMAGE_AVIF_VALID_WITH_GARBAGE_BYTE_AT_END: &str = "tests/valid_with_garbage_byte.avif";
7174
static AVIF_TEST_DIRS: &[&str] = &["tests", "av1-avif/testFiles", "link-u-avif-sample-images"];
7275

7376
// These files are
@@ -1279,6 +1282,19 @@ fn public_avif_avis_with_pitm_no_iloc() {
12791282
assert_avif_should(AVIF_AVIS_WITH_PITM_NO_ILOC, Status::PitmNotFound);
12801283
}
12811284

1285+
#[test]
1286+
fn public_avif_valid_with_garbage_overread_at_end() {
1287+
assert_avif_should(
1288+
IMAGE_AVIF_VALID_WITH_GARBAGE_OVERREAD_AT_END,
1289+
Status::CheckParserStateErr,
1290+
);
1291+
}
1292+
1293+
#[test]
1294+
fn public_avif_valid_with_garbage_byte_at_end() {
1295+
assert_avif_should(IMAGE_AVIF_VALID_WITH_GARBAGE_BYTE_AT_END, Status::Eof);
1296+
}
1297+
12821298
fn public_avis_loop_impl(path: &str, looped: bool) {
12831299
let input = &mut File::open(path).expect("Unknown file");
12841300
match mp4::read_avif(input, ParseStrictness::Normal) {
374 Bytes
Binary file not shown.
390 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)