Bug 1367508 - Update rust parser to limit table size in case of OOM. r=kinetik
authorAlfredo Yang <ayang@mozilla.com>
Thu, 01 Jun 2017 10:27:48 +0800
changeset 361940 4229107601dd76168852410c0858a5ca42519acc
parent 361939 8e3f7a301dc18cf2b580f44fb8018dabd2c3c79c
child 361941 d6d27f07a746cba107c67a8d801b0efca31094ee
push id31952
push usercbook@mozilla.com
push dateFri, 02 Jun 2017 12:17:25 +0000
treeherdermozilla-central@194c009d6295 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1367508
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1367508 - Update rust parser to limit table size in case of OOM. r=kinetik MozReview-Commit-ID: H5dAp12Hnbn
media/libstagefright/binding/mp4parse/src/lib.rs
media/libstagefright/binding/mp4parse/src/tests.rs
media/libstagefright/binding/update-rust.sh
--- a/media/libstagefright/binding/mp4parse/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse/src/lib.rs
@@ -23,16 +23,20 @@ use boxes::{BoxType, FourCC};
 
 // Unit tests.
 #[cfg(test)]
 mod tests;
 
 // Arbitrary buffer size limit used for raw read_bufs on a box.
 const BUF_SIZE_LIMIT: usize = 1024 * 1024;
 
+// Max table length. Calculating in worth case for one week long video, one
+// frame per table entry in 30 fps.
+const TABLE_SIZE_LIMIT: u32 = 30 * 60 * 60 * 24 * 7;
+
 static DEBUG_MODE: std::sync::atomic::AtomicBool = std::sync::atomic::ATOMIC_BOOL_INIT;
 
 pub fn set_debug_mode(mode: bool) {
     DEBUG_MODE.store(mode, std::sync::atomic::Ordering::SeqCst);
 }
 
 #[inline(always)]
 fn get_debug_mode() -> bool {
@@ -1015,17 +1019,17 @@ fn read_tkhd<T: Read>(src: &mut BMFFBox<
         height: height,
         matrix: matrix,
     })
 }
 
 /// Parse a elst box.
 fn read_elst<T: Read>(src: &mut BMFFBox<T>) -> Result<EditListBox> {
     let (version, _) = read_fullbox_extra(src)?;
-    let edit_count = be_u32(src)?;
+    let edit_count = be_u32_with_limit(src)?;
     if edit_count == 0 {
         return Err(Error::InvalidData("invalid edit count"));
     }
     let mut edits = Vec::new();
     for _ in 0..edit_count {
         let (segment_duration, media_time) = match version {
             1 => {
                 // 64 bit segment duration and media times.
@@ -1092,68 +1096,68 @@ fn read_mdhd<T: Read>(src: &mut BMFFBox<
         timescale: timescale,
         duration: duration,
     })
 }
 
 /// Parse a stco box.
 fn read_stco<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> {
     let (_, _) = read_fullbox_extra(src)?;
-    let offset_count = be_u32(src)?;
+    let offset_count = be_u32_with_limit(src)?;
     let mut offsets = Vec::new();
     for _ in 0..offset_count {
         offsets.push(be_u32(src)? as u64);
     }
 
     // Padding could be added in some contents.
     skip_box_remain(src)?;
 
     Ok(ChunkOffsetBox {
         offsets: offsets,
     })
 }
 
 /// Parse a co64 box.
 fn read_co64<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> {
     let (_, _) = read_fullbox_extra(src)?;
-    let offset_count = be_u32(src)?;
+    let offset_count = be_u32_with_limit(src)?;
     let mut offsets = Vec::new();
     for _ in 0..offset_count {
         offsets.push(be_u64(src)?);
     }
 
     // Padding could be added in some contents.
     skip_box_remain(src)?;
 
     Ok(ChunkOffsetBox {
         offsets: offsets,
     })
 }
 
 /// Parse a stss box.
 fn read_stss<T: Read>(src: &mut BMFFBox<T>) -> Result<SyncSampleBox> {
     let (_, _) = read_fullbox_extra(src)?;
-    let sample_count = be_u32(src)?;
+    let sample_count = be_u32_with_limit(src)?;
     let mut samples = Vec::new();
     for _ in 0..sample_count {
         samples.push(be_u32(src)?);
     }
 
     // Padding could be added in some contents.
     skip_box_remain(src)?;
 
     Ok(SyncSampleBox {
         samples: samples,
     })
 }
 
 /// Parse a stsc box.
 fn read_stsc<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleToChunkBox> {
     let (_, _) = read_fullbox_extra(src)?;
-    let sample_count = be_u32(src)?;
+    let sample_count = be_u32_with_limit(src)?;
     let mut samples = Vec::new();
     for _ in 0..sample_count {
         let first_chunk = be_u32(src)?;
         let samples_per_chunk = be_u32(src)?;
         let sample_description_index = be_u32(src)?;
         samples.push(SampleToChunk {
             first_chunk: first_chunk,
             samples_per_chunk: samples_per_chunk,
@@ -1167,17 +1171,17 @@ fn read_stsc<T: Read>(src: &mut BMFFBox<
     Ok(SampleToChunkBox {
         samples: samples,
     })
 }
 
 fn read_ctts<T: Read>(src: &mut BMFFBox<T>) -> Result<CompositionOffsetBox> {
     let (version, _) = read_fullbox_extra(src)?;
 
-    let counts = be_u32(src)?;
+    let counts = be_u32_with_limit(src)?;
 
     if src.bytes_left() < (counts as usize * 8) {
         return Err(Error::InvalidData("insufficient data in 'ctts' box"));
     }
 
     let mut offsets = Vec::new();
     for _ in 0..counts {
         let (sample_count, time_offset) = match version {
@@ -1205,17 +1209,17 @@ fn read_ctts<T: Read>(src: &mut BMFFBox<
         samples: offsets,
     })
 }
 
 /// Parse a stsz box.
 fn read_stsz<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleSizeBox> {
     let (_, _) = read_fullbox_extra(src)?;
     let sample_size = be_u32(src)?;
-    let sample_count = be_u32(src)?;
+    let sample_count = be_u32_with_limit(src)?;
     let mut sample_sizes = Vec::new();
     if sample_size == 0 {
         for _ in 0..sample_count {
             sample_sizes.push(be_u32(src)?);
         }
     }
 
     // Padding could be added in some contents.
@@ -1225,17 +1229,17 @@ fn read_stsz<T: Read>(src: &mut BMFFBox<
         sample_size: sample_size,
         sample_sizes: sample_sizes,
     })
 }
 
 /// Parse a stts box.
 fn read_stts<T: Read>(src: &mut BMFFBox<T>) -> Result<TimeToSampleBox> {
     let (_, _) = read_fullbox_extra(src)?;
-    let sample_count = be_u32(src)?;
+    let sample_count = be_u32_with_limit(src)?;
     let mut samples = Vec::new();
     for _ in 0..sample_count {
         let sample_count = be_u32(src)?;
         let sample_delta = be_u32(src)?;
         samples.push(Sample {
             sample_count: sample_count,
             sample_delta: sample_delta,
         });
@@ -2026,15 +2030,25 @@ fn be_u24<T: ReadBytesExt>(src: &mut T) 
         .map(|v| v as u32)
         .map_err(From::from)
 }
 
 fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
     src.read_u32::<byteorder::BigEndian>().map_err(From::from)
 }
 
+/// Using in reading table size and return error if it exceeds limitation.
+fn be_u32_with_limit<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
+    be_u32(src).and_then(|v| {
+        if v > TABLE_SIZE_LIMIT {
+            return Err(Error::Unsupported("Over limited value"));
+        }
+        Ok(v)
+    })
+}
+
 fn be_u64<T: ReadBytesExt>(src: &mut T) -> Result<u64> {
     src.read_u64::<byteorder::BigEndian>().map_err(From::from)
 }
 
 fn write_be_u32<T: WriteBytesExt>(des: &mut T, num: u32) -> Result<()> {
     des.write_u32::<byteorder::BigEndian>(num).map_err(From::from)
 }
--- a/media/libstagefright/binding/mp4parse/src/tests.rs
+++ b/media/libstagefright/binding/mp4parse/src/tests.rs
@@ -988,8 +988,26 @@ fn read_f4v_stsd() {
     });
 
     let mut iter = super::BoxIter::new(&mut stream);
     let mut stream = iter.next_box().unwrap().unwrap();
     let (codec_type, _) = super::read_audio_sample_entry(&mut stream)
           .expect("failed to read f4v stsd atom");
     assert_eq!(codec_type, super::CodecType::MP3);
 }
+
+#[test]
+fn max_table_limit() {
+    let elst = make_fullbox(BoxSize::Auto, b"elst", 1, |s| {
+        s.B32(super::TABLE_SIZE_LIMIT + 1)
+    }).into_inner();
+    let mut stream = make_box(BoxSize::Auto, b"edts", |s| {
+        s.append_bytes(elst.as_slice())
+    });
+    let mut iter = super::BoxIter::new(&mut stream);
+    let mut stream = iter.next_box().unwrap().unwrap();
+    let mut track = super::Track::new(0);
+    match super::read_edts(&mut stream, &mut track) {
+        Err(Error::Unsupported(s)) => assert_eq!(s, "Over limited value"),
+        Ok(_) => panic!("expected an error result"),
+        _ => panic!("expected a different error result"),
+    }
+}
--- a/media/libstagefright/binding/update-rust.sh
+++ b/media/libstagefright/binding/update-rust.sh
@@ -1,13 +1,13 @@
 #!/bin/sh -e
 # Script to update mp4parse-rust sources to latest upstream
 
 # Default version.
-VER=70b2008dc9fd5cd09fb5b047e72616c5cf52c1d7
+VER=5bff34a85f2c0b1f147798ea701f7b704e651ae8
 
 # Accept version or commit from the command line.
 if test -n "$1"; then
   VER=$1
 fi
 
 echo "Fetching sources..."
 rm -rf _upstream