Bug 1389470 - use fallible memory allocation to avoid OOM. r=kinetik
authorAlfredo.Yang <ayang@mozilla.com>
Wed, 13 Sep 2017 14:59:45 +0800
changeset 430338 8d242a53eca76fca4931aa7e1cbb86402066aeb2
parent 430337 33964845834caf5d447d89104c0db3533a03fc56
child 430339 4f22ef5d805833c848d6276d17ce41fea1df56e0
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1389470
milestone57.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 1389470 - use fallible memory allocation to avoid OOM. r=kinetik MozReview-Commit-ID: BvqIe685Rfs
media/libstagefright/binding/MP4Metadata.cpp
media/libstagefright/binding/include/mp4parse.h
media/libstagefright/binding/mp4parse-cargo.patch
media/libstagefright/binding/mp4parse/Cargo.toml
media/libstagefright/binding/mp4parse/src/lib.rs
media/libstagefright/binding/mp4parse_capi/src/lib.rs
media/libstagefright/binding/mp4parse_fallible/Cargo.toml
media/libstagefright/binding/mp4parse_fallible/README
media/libstagefright/binding/mp4parse_fallible/lib.rs
media/libstagefright/binding/update-rust.sh
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
--- a/media/libstagefright/binding/MP4Metadata.cpp
+++ b/media/libstagefright/binding/MP4Metadata.cpp
@@ -758,16 +758,18 @@ MP4MetadataRust::Init()
   mp4parse_io io = { read_source, &mRustSource };
   mRustParser.reset(mp4parse_new(&io));
   MOZ_ASSERT(mRustParser);
 
   if (MOZ_LOG_TEST(sLog, LogLevel::Debug)) {
     mp4parse_log(true);
   }
 
+  mp4parse_fallible_allocation(true);
+
   mp4parse_status rv = mp4parse_read(mRustParser.get());
   MOZ_LOG(sLog, LogLevel::Debug, ("rust parser returned %d\n", rv));
   Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_SUCCESS,
                         rv == mp4parse_status_OK);
   if (rv != mp4parse_status_OK && rv != mp4parse_status_TABLE_TOO_LARGE) {
     MOZ_LOG(sLog, LogLevel::Info, ("Rust mp4 parser fails to parse this stream."));
     MOZ_ASSERT(rv > 0);
     Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_ERROR_CODE, rv);
--- a/media/libstagefright/binding/include/mp4parse.h
+++ b/media/libstagefright/binding/include/mp4parse.h
@@ -19,16 +19,17 @@ extern "C" {
 typedef enum mp4parse_status {
 	mp4parse_status_OK = 0,
 	mp4parse_status_BAD_ARG = 1,
 	mp4parse_status_INVALID = 2,
 	mp4parse_status_UNSUPPORTED = 3,
 	mp4parse_status_EOF = 4,
 	mp4parse_status_IO = 5,
 	mp4parse_status_TABLE_TOO_LARGE = 6,
+	mp4parse_status_OOM = 7,
 } mp4parse_status;
 
 typedef enum mp4parse_track_type {
 	mp4parse_track_type_VIDEO = 0,
 	mp4parse_track_type_AUDIO = 1,
 } mp4parse_track_type;
 
 typedef enum mp4parse_codec {
@@ -113,16 +114,18 @@ typedef struct mp4parse_io {
 mp4parse_parser* mp4parse_new(mp4parse_io const* io);
 
 /// Free an `mp4parse_parser*` allocated by `mp4parse_new()`.
 void mp4parse_free(mp4parse_parser* parser);
 
 /// Enable `mp4_parser` log.
 void mp4parse_log(bool enable);
 
+void mp4parse_fallible_allocation(bool enable);
+
 /// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error.
 mp4parse_status mp4parse_read(mp4parse_parser* parser);
 
 /// Return the number of tracks parsed by previous `mp4parse_read()` call.
 mp4parse_status mp4parse_get_track_count(mp4parse_parser const* parser, uint32_t* count);
 
 /// Fill the supplied `mp4parse_track_info` with metadata for `track`.
 mp4parse_status mp4parse_get_track_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_info* info);
--- a/media/libstagefright/binding/mp4parse-cargo.patch
+++ b/media/libstagefright/binding/mp4parse-cargo.patch
@@ -1,28 +1,30 @@
 diff --git a/media/libstagefright/binding/mp4parse/Cargo.toml b/media/libstagefright/binding/mp4parse/Cargo.toml
 index ff9422c..814c4c6 100644
 --- a/media/libstagefright/binding/mp4parse/Cargo.toml
 +++ b/media/libstagefright/binding/mp4parse/Cargo.toml
-@@ -20,19 +20,11 @@ exclude = [
+@@ -20,20 +20,12 @@ exclude = [
  ]
  
 -[badges]
 -travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" }
  
  [dependencies]
 -byteorder = "1.0.0"
 -afl = { version = "0.1.1", optional = true }
 -afl-plugin = { version = "0.1.1", optional = true }
 -abort_on_panic = { version = "1.0.0", optional = true }
 -bitreader = { version = "0.3.0" }
 -num-traits = "0.1.37"
+-mp4parse_fallible = { path = "../mp4parse_fallible" }
 +byteorder = "1.0.0"
 +bitreader = { version = "0.3.0" }
 +num-traits = "0.1.37"
++mp4parse_fallible = { path = "../mp4parse_fallible" }
  
  [dev-dependencies]
  test-assembler = "0.1.2"
  
 -[features]
 -fuzz = ["afl", "afl-plugin", "abort_on_panic"]
 -
  # Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on.
--- a/media/libstagefright/binding/mp4parse/Cargo.toml
+++ b/media/libstagefright/binding/mp4parse/Cargo.toml
@@ -19,12 +19,13 @@ exclude = [
   "*.mp4",
 ]
 
 
 [dependencies]
 byteorder = "1.0.0"
 bitreader = { version = "0.3.0" }
 num-traits = "0.1.37"
+mp4parse_fallible = { path = "../mp4parse_fallible" }
 
 [dev-dependencies]
 test-assembler = "0.1.2"
 
--- a/media/libstagefright/binding/mp4parse/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse/src/lib.rs
@@ -6,43 +6,50 @@
 #![cfg_attr(feature = "fuzz", feature(plugin))]
 #![cfg_attr(feature = "fuzz", plugin(afl_plugin))]
 #[cfg(feature = "fuzz")]
 extern crate afl;
 
 extern crate byteorder;
 extern crate bitreader;
 extern crate num_traits;
+extern crate mp4parse_fallible;
 use byteorder::{ReadBytesExt, WriteBytesExt};
 use bitreader::{BitReader, ReadInto};
 use std::io::{Read, Take};
 use std::io::Cursor;
 use std::cmp;
 use num_traits::Num;
+use mp4parse_fallible::FallibleVec;
 
 mod boxes;
 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.
-#[cfg(target_pointer_width = "64")]
 const TABLE_SIZE_LIMIT: u32 = 30 * 60 * 60 * 24 * 7;
 
-// Reduce max table length if it is in 32 arch for memory problem.
-#[cfg(target_pointer_width = "32")]
-const TABLE_SIZE_LIMIT: u32 = 30 * 60 * 60 * 24;
+static DEBUG_MODE: std::sync::atomic::AtomicBool = std::sync::atomic::ATOMIC_BOOL_INIT;
+
+static FALLIBLE_ALLOCATION: std::sync::atomic::AtomicBool = std::sync::atomic::ATOMIC_BOOL_INIT;
 
-static DEBUG_MODE: std::sync::atomic::AtomicBool = std::sync::atomic::ATOMIC_BOOL_INIT;
+pub fn set_fallible_allocation_mode(fallible: bool) {
+    FALLIBLE_ALLOCATION.store(fallible, std::sync::atomic::Ordering::SeqCst);
+}
+
+fn get_fallible_allocation_mode() -> bool {
+    FALLIBLE_ALLOCATION.load(std::sync::atomic::Ordering::Relaxed)
+}
 
 pub fn set_debug_mode(mode: bool) {
     DEBUG_MODE.store(mode, std::sync::atomic::Ordering::SeqCst);
 }
 
 #[inline(always)]
 fn get_debug_mode() -> bool {
     DEBUG_MODE.load(std::sync::atomic::Ordering::Relaxed)
@@ -51,16 +58,47 @@ fn get_debug_mode() -> bool {
 macro_rules! log {
     ($($args:tt)*) => (
         if get_debug_mode() {
             println!( $( $args )* );
         }
     )
 }
 
+// TODO: vec_push() and vec_reserve() needs to be replaced when Rust supports
+// fallible memory allocation in raw_vec.
+pub fn vec_push<T>(vec: &mut Vec<T>, val: T) -> std::result::Result<(), ()> {
+    if get_fallible_allocation_mode() {
+        return vec.try_push(val);
+    }
+
+    vec.push(val);
+    Ok(())
+}
+
+pub fn vec_reserve<T>(vec: &mut Vec<T>, size: usize) -> std::result::Result<(), ()> {
+    if get_fallible_allocation_mode() {
+        return vec.try_reserve(size);
+    }
+
+    vec.reserve(size);
+    Ok(())
+}
+
+fn reserve_read_buf(size: usize) -> std::result::Result<Vec<u8>, ()> {
+    if get_fallible_allocation_mode() {
+        let mut buf: Vec<u8> = Vec::new();
+        buf.try_reserve(size)?;
+        unsafe { buf.set_len(size); }
+        return Ok(buf);
+    }
+
+    Ok(vec![0; size])
+}
+
 /// Describes parser failures.
 ///
 /// This enum wraps the standard `io::Error` type, unified with
 /// our own parser error states and those of crates we use.
 #[derive(Debug)]
 pub enum Error {
     /// Parse error caused by corrupt or malformed data.
     InvalidData(&'static str),
@@ -69,16 +107,18 @@ pub enum Error {
     /// Reflect `std::io::ErrorKind::UnexpectedEof` for short data.
     UnexpectedEOF,
     /// Propagate underlying errors from `std::io`.
     Io(std::io::Error),
     /// read_mp4 terminated without detecting a moov box.
     NoMoov,
     /// Parse error caused by table size is over limitation.
     TableTooLarge,
+    /// Out of memory
+    OutOfMemory,
 }
 
 impl From<bitreader::BitReaderError> for Error {
     fn from(_: bitreader::BitReaderError) -> Error {
         Error::InvalidData("invalid data")
     }
 }
 
@@ -92,16 +132,22 @@ impl From<std::io::Error> for Error {
 }
 
 impl From<std::string::FromUtf8Error> for Error {
     fn from(_: std::string::FromUtf8Error) -> Error {
         Error::InvalidData("invalid utf8")
     }
 }
 
+impl From<()> for Error {
+    fn from(_: ()) -> Error {
+        Error::OutOfMemory
+    }
+}
+
 /// Result shorthand using our Error enum.
 pub type Result<T> = std::result::Result<T, Error>;
 
 /// Basic ISO box structure.
 ///
 /// mp4 files are a sequence of possibly-nested 'box' structures.  Each box
 /// begins with a header describing the length of the box's data and a
 /// four-byte box type which identifies the type of the box. Together these
@@ -684,27 +730,27 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>
             BoxType::MovieHeaderBox => {
                 let (mvhd, timescale) = parse_mvhd(&mut b)?;
                 context.timescale = timescale;
                 log!("{:?}", mvhd);
             }
             BoxType::TrackBox => {
                 let mut track = Track::new(context.tracks.len());
                 read_trak(&mut b, &mut track)?;
-                context.tracks.push(track);
+                vec_push(&mut context.tracks, track)?;
             }
             BoxType::MovieExtendsBox => {
                 let mvex = read_mvex(&mut b)?;
                 log!("{:?}", mvex);
                 context.mvex = Some(mvex);
             }
             BoxType::ProtectionSystemSpecificHeaderBox => {
                 let pssh = read_pssh(&mut b)?;
                 log!("{:?}", pssh);
-                context.psshs.push(pssh);
+                vec_push(&mut context.psshs, pssh)?;
             }
             _ => skip_box_content(&mut b)?,
         };
         check_parser_state!(b.content);
     }
     Ok(())
 }
 
@@ -718,29 +764,29 @@ fn read_pssh<T: Read>(src: &mut BMFFBox<
 
         let system_id = read_buf(pssh, 16)?;
 
         let mut kid: Vec<ByteData> = Vec::new();
         if version > 0 {
             let count = be_u32_with_limit(pssh)?;
             for _ in 0..count {
                 let item = read_buf(pssh, 16)?;
-                kid.push(item);
+                vec_push(&mut kid, item)?;
             }
         }
 
         let data_size = be_u32_with_limit(pssh)? as usize;
         let data = read_buf(pssh, data_size)?;
 
         (system_id, kid, data)
     };
 
     let mut pssh_box = Vec::new();
     write_be_u32(&mut pssh_box, src.head.size as u32)?;
-    pssh_box.append(&mut b"pssh".to_vec());
+    pssh_box.extend_from_slice(b"pssh");
     pssh_box.append(&mut box_content);
 
     Ok(ProtectionSystemSpecificHeaderBox {
         system_id: system_id,
         kid: kid,
         data: data,
         box_content: pssh_box,
     })
@@ -935,17 +981,17 @@ fn read_ftyp<T: Read>(src: &mut BMFFBox<
     let bytes_left = src.bytes_left();
     if bytes_left % 4 != 0 {
         return Err(Error::InvalidData("invalid ftyp size"));
     }
     // Is a brand_count of zero valid?
     let brand_count = bytes_left / 4;
     let mut brands = Vec::new();
     for _ in 0..brand_count {
-        brands.push(From::from(be_u32(src)?));
+        vec_push(&mut brands, From::from(be_u32(src)?))?;
     }
     Ok(FileTypeBox {
         major_brand: From::from(major),
         minor_version: minor,
         compatible_brands: brands,
     })
 }
 
@@ -1044,22 +1090,22 @@ fn read_elst<T: Read>(src: &mut BMFFBox<
             0 => {
                 // 32 bit segment duration and media times.
                 (be_u32(src)? as u64, be_i32(src)? as i64)
             }
             _ => return Err(Error::InvalidData("unhandled elst version")),
         };
         let media_rate_integer = be_i16(src)?;
         let media_rate_fraction = be_i16(src)?;
-        edits.push(Edit {
+        vec_push(&mut edits, Edit {
             segment_duration: segment_duration,
             media_time: media_time,
             media_rate_integer: media_rate_integer,
             media_rate_fraction: media_rate_fraction,
-        })
+        })?;
     }
 
     Ok(EditListBox {
         edits: edits,
     })
 }
 
 /// Parse a mdhd box.
@@ -1105,51 +1151,51 @@ fn read_mdhd<T: Read>(src: &mut BMFFBox<
 }
 
 /// 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_with_limit(src)?;
     let mut offsets = Vec::new();
     for _ in 0..offset_count {
-        offsets.push(be_u32(src)? as u64);
+        vec_push(&mut offsets, 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_with_limit(src)?;
     let mut offsets = Vec::new();
     for _ in 0..offset_count {
-        offsets.push(be_u64(src)?);
+        vec_push(&mut offsets, 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_with_limit(src)?;
     let mut samples = Vec::new();
     for _ in 0..sample_count {
-        samples.push(be_u32(src)?);
+        vec_push(&mut samples, be_u32(src)?)?;
     }
 
     // Padding could be added in some contents.
     skip_box_remain(src)?;
 
     Ok(SyncSampleBox {
         samples: samples,
     })
@@ -1159,21 +1205,21 @@ fn read_stss<T: Read>(src: &mut BMFFBox<
 fn read_stsc<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleToChunkBox> {
     let (_, _) = read_fullbox_extra(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_with_limit(src)?;
         let sample_description_index = be_u32(src)?;
-        samples.push(SampleToChunk {
+        vec_push(&mut samples, SampleToChunk {
             first_chunk: first_chunk,
             samples_per_chunk: samples_per_chunk,
             sample_description_index: sample_description_index,
-        });
+        })?;
     }
 
     // Padding could be added in some contents.
     skip_box_remain(src)?;
 
     Ok(SampleToChunkBox {
         samples: samples,
     })
@@ -1198,20 +1244,20 @@ fn read_ctts<T: Read>(src: &mut BMFFBox<
                 let count = be_u32_with_limit(src)?;
                 let offset = TimeOffsetVersion::Version1(be_i32(src)?);
                 (count, offset)
             },
             _ => {
                 return Err(Error::InvalidData("unsupported version in 'ctts' box"));
             }
         };
-        offsets.push(TimeOffset {
+        vec_push(&mut offsets, TimeOffset {
             sample_count: sample_count,
             time_offset: time_offset,
-        });
+        })?;
     }
 
     skip_box_remain(src)?;
 
     Ok(CompositionOffsetBox {
         samples: offsets,
     })
 }
@@ -1219,17 +1265,17 @@ fn read_ctts<T: Read>(src: &mut BMFFBox<
 /// 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_with_limit(src)?;
     let mut sample_sizes = Vec::new();
     if sample_size == 0 {
         for _ in 0..sample_count {
-            sample_sizes.push(be_u32(src)?);
+            vec_push(&mut sample_sizes, be_u32(src)?)?;
         }
     }
 
     // Padding could be added in some contents.
     skip_box_remain(src)?;
 
     Ok(SampleSizeBox {
         sample_size: sample_size,
@@ -1240,20 +1286,20 @@ fn read_stsz<T: Read>(src: &mut BMFFBox<
 /// 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_with_limit(src)?;
     let mut samples = Vec::new();
     for _ in 0..sample_count {
         let sample_count = be_u32_with_limit(src)?;
         let sample_delta = be_u32(src)?;
-        samples.push(Sample {
+        vec_push(&mut samples, Sample {
             sample_count: sample_count,
             sample_delta: sample_delta,
-        });
+        })?;
     }
 
     // Padding could be added in some contents.
     skip_box_remain(src)?;
 
     Ok(TimeToSampleBox {
         samples: samples,
     })
@@ -1450,17 +1496,17 @@ fn read_ds_descriptor(data: &[u8], esds:
         channel_counts += read_surround_channel_count(bit_reader, num_back_channel)?;
         channel_counts += read_surround_channel_count(bit_reader, num_lfe_channel)?;
     }
 
     esds.audio_object_type = Some(audio_object_type);
     esds.audio_sample_rate = sample_frequency;
     esds.audio_channel_count = Some(channel_counts);
     assert!(esds.decoder_specific_data.is_empty());
-    esds.decoder_specific_data.extend(data.iter());
+    esds.decoder_specific_data.extend_from_slice(data);
 
     Ok(())
 }
 
 fn read_surround_channel_count(bit_reader: &mut BitReader, channels: u8) -> Result<u16> {
     let mut count = 0;
     for _ in 0..channels {
         let is_cpe: bool = ReadInto::read(bit_reader, 1)?;
@@ -1538,17 +1584,17 @@ fn read_dfla<T: Read>(src: &mut BMFFBox<
         return Err(Error::Unsupported("unknown dfLa (FLAC) version"));
     }
     if flags != 0 {
         return Err(Error::InvalidData("no-zero dfLa (FLAC) flags"));
     }
     let mut blocks = Vec::new();
     while src.bytes_left() > 0 {
         let block = read_flac_metadata(src)?;
-        blocks.push(block);
+        vec_push(&mut blocks, block)?;
     }
     // The box must have at least one meta block, and the first block
     // must be the METADATA_BLOCK_STREAMINFO
     if blocks.is_empty() {
         return Err(Error::InvalidData("FLACSpecificBox missing metadata"));
     } else if blocks[0].block_type != 0 {
         return Err(Error::InvalidData(
                 "FLACSpecificBox must have STREAMINFO metadata first"));
@@ -1732,17 +1778,17 @@ fn read_video_sample_entry<T: Read>(src:
                 codec_specific = Some(VideoCodecSpecific::ESDSConfig(esds));
             }
             BoxType::ProtectionSchemeInformationBox => {
                 if name != BoxType::ProtectedVisualSampleEntry {
                     return Err(Error::InvalidData("malformed video sample entry"));
                 }
                 let sinf = read_sinf(&mut b)?;
                 log!("{:?} (sinf)", sinf);
-                protection_info.push(sinf);
+                vec_push(&mut protection_info, sinf)?;
             }
             _ => {
                 log!("Unsupported video codec, box {:?} found", b.head.name);
                 skip_box_content(&mut b)?;
             }
         }
         check_parser_state!(b.content);
     }
@@ -1857,17 +1903,17 @@ fn read_audio_sample_entry<T: Read>(src:
             }
             BoxType::ProtectionSchemeInformationBox => {
                 if name != BoxType::ProtectedAudioSampleEntry {
                     return Err(Error::InvalidData("malformed audio sample entry"));
                 }
                 let sinf = read_sinf(&mut b)?;
                 log!("{:?} (sinf)", sinf);
                 codec_type = CodecType::EncryptedAudio;
-                protection_info.push(sinf);
+                vec_push(&mut protection_info, sinf)?;
             }
             _ => {
                 log!("Unsupported audio codec, box {:?} found", b.head.name);
                 skip_box_content(&mut b)?;
             }
         }
         check_parser_state!(b.content);
     }
@@ -1915,17 +1961,17 @@ fn read_stsd<T: Read>(src: &mut BMFFBox<
                 }
                 Err(e) => return Err(e),
             };
             if track.data.is_none() {
                 track.data = Some(description.clone());
             } else {
                 log!("** don't know how to handle multiple descriptions **");
             }
-            descriptions.push(description);
+            vec_push(&mut descriptions, description)?;
             check_parser_state!(b.content);
             if descriptions.len() == description_count as usize {
                 break;
             }
         }
     }
 
     // Padding could be added in some contents.
@@ -2010,22 +2056,25 @@ fn skip<T: Read>(src: &mut T, mut bytes:
     Ok(())
 }
 
 /// Read size bytes into a Vector or return error.
 fn read_buf<T: ReadBytesExt>(src: &mut T, size: usize) -> Result<Vec<u8>> {
     if size > BUF_SIZE_LIMIT {
         return Err(Error::InvalidData("read_buf size exceeds BUF_SIZE_LIMIT"));
     }
-    let mut buf = vec![0; size];
-    let r = src.read(&mut buf)?;
-    if r != size {
-        return Err(Error::InvalidData("failed buffer read"));
+    if let Ok(mut buf) = reserve_read_buf(size) {
+        let r = src.read(&mut buf)?;
+        if r != size {
+          return Err(Error::InvalidData("failed buffer read"));
+        }
+        return Ok(buf);
     }
-    Ok(buf)
+
+    Err(Error::OutOfMemory)
 }
 
 fn be_i16<T: ReadBytesExt>(src: &mut T) -> Result<i16> {
     src.read_i16::<byteorder::BigEndian>().map_err(From::from)
 }
 
 fn be_i32<T: ReadBytesExt>(src: &mut T) -> Result<i32> {
     src.read_i32::<byteorder::BigEndian>().map_err(From::from)
--- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs
@@ -53,28 +53,30 @@ use mp4parse::AudioCodecSpecific;
 use mp4parse::VideoCodecSpecific;
 use mp4parse::MediaTimeScale;
 use mp4parse::MediaScaledTime;
 use mp4parse::TrackTimeScale;
 use mp4parse::TrackScaledTime;
 use mp4parse::serialize_opus_header;
 use mp4parse::CodecType;
 use mp4parse::Track;
+use mp4parse::vec_push;
 
 #[allow(non_camel_case_types)]
 #[repr(C)]
 #[derive(PartialEq, Debug)]
 pub enum mp4parse_status {
     OK = 0,
     BAD_ARG = 1,
     INVALID = 2,
     UNSUPPORTED = 3,
     EOF = 4,
     IO = 5,
     TABLE_TOO_LARGE = 6,
+    OOM = 7,
 }
 
 #[allow(non_camel_case_types)]
 #[repr(C)]
 #[derive(PartialEq, Debug)]
 pub enum mp4parse_track_type {
     VIDEO = 0,
     AUDIO = 1,
@@ -303,26 +305,31 @@ pub unsafe extern fn mp4parse_free(parse
 }
 
 /// Enable `mp4_parser` log.
 #[no_mangle]
 pub unsafe extern fn mp4parse_log(enable: bool) {
     mp4parse::set_debug_mode(enable);
 }
 
+#[no_mangle]
+pub unsafe extern fn mp4parse_fallible_allocation(enable: bool) {
+    mp4parse::set_fallible_allocation_mode(enable);
+}
+
 /// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error.
 #[no_mangle]
 pub unsafe extern fn mp4parse_read(parser: *mut mp4parse_parser) -> mp4parse_status {
     // Validate arguments from C.
     if parser.is_null() || (*parser).poisoned() {
         return mp4parse_status::BAD_ARG;
     }
 
-    let mut context = (*parser).context_mut();
-    let mut io = (*parser).io_mut();
+    let context = (*parser).context_mut();
+    let io = (*parser).io_mut();
 
     let r = read_mp4(io, context);
     match r {
         Ok(_) => mp4parse_status::OK,
         Err(Error::NoMoov) | Err(Error::InvalidData(_)) => {
             // Block further calls. We've probable lost sync.
             (*parser).set_poisoned(true);
             mp4parse_status::INVALID
@@ -333,16 +340,17 @@ pub unsafe extern fn mp4parse_read(parse
             // Block further calls after a read failure.
             // Getting std::io::ErrorKind::UnexpectedEof is normal
             // but our From trait implementation should have converted
             // those to our Error::UnexpectedEOF variant.
             (*parser).set_poisoned(true);
             mp4parse_status::IO
         },
         Err(Error::TableTooLarge) => mp4parse_status::TABLE_TOO_LARGE,
+        Err(Error::OutOfMemory) => mp4parse_status::OOM,
     }
 }
 
 /// Return the number of tracks parsed by previous `mp4parse_read()` call.
 #[no_mangle]
 pub unsafe extern fn mp4parse_get_track_count(parser: *const mp4parse_parser, count: *mut u32) -> mp4parse_status {
     // Validate arguments from C.
     if parser.is_null() || count.is_null() || (*parser).poisoned() {
@@ -895,26 +903,27 @@ fn create_sample_table(track: &Track, tr
                 (t, _) if t > 0 => start_offset + t as u64,
                 _ => 0,
             };
             if end_offset == 0 {
                 return None;
             }
             cur_position = end_offset;
 
-            sample_table.push(
-                mp4parse_indice {
-                    start_offset: start_offset,
-                    end_offset: end_offset,
-                    start_composition: 0,
-                    end_composition: 0,
-                    start_decode: 0,
-                    sync: !has_sync_table,
-                }
-            );
+            let res = vec_push(&mut sample_table, mp4parse_indice {
+                start_offset: start_offset,
+                end_offset: end_offset,
+                start_composition: 0,
+                end_composition: 0,
+                start_decode: 0,
+                sync: !has_sync_table,
+            });
+            if res.is_err() {
+                return None;
+            }
         }
     }
 
     // Mark the sync sample in sample_table according to 'stss'.
     if let Some(ref v) = track.stss {
         for iter in &v.samples {
             if let Some(elem) = sample_table.get_mut((iter - 1) as usize) {
                 elem.sync = true;
@@ -972,22 +981,23 @@ fn create_sample_table(track: &Track, tr
             _ => return None,
         }
     }
 
     // Correct composition end time due to 'ctts' causes composition time re-ordering.
     //
     // Composition end time is not in specification. However, gecko needs it, so we need to
     // calculate to correct the composition end time.
-    if track.ctts.is_some() {
+    if sample_table.len() > 0 {
         // Create an index table refers to sample_table and sorted by start_composisiton time.
         let mut sort_table = Vec::new();
-        sort_table.reserve(sample_table.len());
         for i in 0 .. sample_table.len() {
-            sort_table.push(i);
+            if vec_push(&mut sort_table, i).is_err() {
+                return None;
+            }
         }
 
         sort_table.sort_by_key(|i| {
             match sample_table.get(*i) {
                 Some(v) => {
                     v.start_composition
                 },
                 _ => 0,
@@ -1111,17 +1121,17 @@ extern fn panic_read(_: *mut u8, _: usiz
 
 #[cfg(test)]
 extern fn error_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
     -1
 }
 
 #[cfg(test)]
 extern fn valid_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
-    let mut input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) };
+    let input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) };
 
     let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) };
     match input.read(&mut buf) {
         Ok(n) => n as isize,
         Err(_) => -1,
     }
 }
 
@@ -1275,16 +1285,18 @@ fn get_track_count_poisoned_parser() {
         assert!(!parser.is_null());
 
         // Our mp4parse_io read should simply fail with an error.
         assert_eq!(mp4parse_status::IO, mp4parse_read(parser));
 
         let mut count: u32 = 0;
         let rv = mp4parse_get_track_count(parser, &mut count);
         assert_eq!(rv, mp4parse_status::BAD_ARG);
+
+        mp4parse_free(parser);
     }
 }
 
 #[test]
 fn arg_validation_with_data() {
     unsafe {
         let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
         let io = mp4parse_io { read: Some(valid_read),
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse_fallible/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "mp4parse_fallible"
+version = "0.0.1"
+authors = ["The Servo Project Developers"]
+license = "MPL-2.0"
+publish = false
+
+[lib]
+name = "mp4parse_fallible"
+path = "lib.rs"
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse_fallible/README
@@ -0,0 +1,2 @@
+This is from https://github.com/servo/servo/tree/master/components/fallible
+with modificaion for mp4 demuxer.
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse_fallible/lib.rs
@@ -0,0 +1,92 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::mem;
+use std::vec::Vec;
+
+extern "C" {
+    fn realloc(ptr: *mut u8, bytes: usize) -> *mut u8;
+    fn malloc(bytes: usize) -> *mut u8;
+}
+
+pub trait FallibleVec<T> {
+    /// Append |val| to the end of |vec|.  Returns Ok(()) on success,
+    /// Err(()) if it fails, which can only be due to lack of memory.
+    fn try_push(&mut self, value: T) -> Result<(), ()>;
+
+    /// Expand the vector size. Return Ok(()) on success, Err(()) if it
+    /// fails.
+    fn try_reserve(&mut self, new_cap: usize) -> Result<(), ()>;
+}
+
+/////////////////////////////////////////////////////////////////
+// Vec
+
+impl<T> FallibleVec<T> for Vec<T> {
+    #[inline]
+    fn try_push(&mut self, val: T) -> Result<(), ()> {
+        if self.capacity() == self.len() {
+            let old_cap: usize = self.capacity();
+            let new_cap: usize
+                = if old_cap == 0 { 4 } else { old_cap.checked_mul(2).ok_or(()) ? };
+
+            try_extend_vec(self, new_cap)?;
+            debug_assert!(self.capacity() > self.len());
+        }
+        self.push(val);
+        Ok(())
+    }
+
+    #[inline]
+    fn try_reserve(&mut self, cap: usize) -> Result<(), ()> {
+        let new_cap = cap + self.capacity();
+        try_extend_vec(self, new_cap)?;
+        debug_assert!(self.capacity() == new_cap);
+        Ok(())
+    }
+}
+
+#[inline(never)]
+#[cold]
+fn try_extend_vec<T>(vec: &mut Vec<T>, new_cap: usize) -> Result<(), ()> {
+    let old_ptr = vec.as_mut_ptr();
+    let old_len = vec.len();
+
+    let old_cap: usize = vec.capacity();
+
+    if old_cap > new_cap {
+        return Ok(());
+    }
+
+    let new_size_bytes
+        = new_cap.checked_mul(mem::size_of::<T>()).ok_or(()) ? ;
+
+    let new_ptr = unsafe {
+        if old_cap == 0 {
+            malloc(new_size_bytes)
+        } else {
+            realloc(old_ptr as *mut u8, new_size_bytes)
+        }
+    };
+
+    if new_ptr.is_null() {
+        return Err(());
+    }
+
+    let new_vec = unsafe {
+        Vec::from_raw_parts(new_ptr as *mut T, old_len, new_cap)
+    };
+
+    mem::forget(mem::replace(vec, new_vec));
+    Ok(())
+}
+
+#[test]
+fn oom_test() {
+    let mut vec: Vec<char> = Vec::new();
+    match vec.try_reserve(std::usize::MAX) {
+        Ok(_) => panic!("it should be OOM"),
+        _ => (),
+    }
+}
--- 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=81260ded506dce968716720e10544c510f37d222
+VER=759cf90957b1fd5a01632b1aa90cead16c26d0db
 
 # Accept version or commit from the command line.
 if test -n "$1"; then
   VER=$1
 fi
 
 echo "Fetching sources..."
 rm -rf _upstream
@@ -33,16 +33,19 @@ mkdir -p mp4parse/tests
 cp _upstream/mp4parse/mp4parse/tests/*.rs mp4parse/tests/
 cp _upstream/mp4parse/mp4parse/tests/*.mp4 mp4parse/tests/
 rm -rf mp4parse_capi
 mkdir -p mp4parse_capi/src
 cp _upstream/mp4parse/mp4parse_capi/Cargo.toml mp4parse_capi/
 cp _upstream/mp4parse/mp4parse_capi/build.rs mp4parse_capi/
 cp _upstream/mp4parse/mp4parse_capi/include/mp4parse.h include/
 cp _upstream/mp4parse/mp4parse_capi/src/*.rs mp4parse_capi/src/
+rm -rf mp4parse_fallible
+mkdir -p mp4parse_fallible
+cp _upstream/mp4parse/mp4parse_fallible/* mp4parse_fallible/
 
 echo "Applying patches..."
 patch -p4 < mp4parse-cargo.patch
 
 echo "Cleaning up..."
 rm -rf _upstream
 
 echo "Updating gecko Cargo.lock..."
--- a/toolkit/library/gtest/rust/Cargo.lock
+++ b/toolkit/library/gtest/rust/Cargo.lock
@@ -797,16 +797,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "mp4parse"
 version = "0.8.0"
 dependencies = [
  "bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mp4parse_fallible 0.0.1",
  "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp4parse-gtest"
 version = "0.1.0"
 
 [[package]]
@@ -814,16 +815,20 @@ name = "mp4parse_capi"
 version = "0.8.0"
 dependencies = [
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "mp4parse 0.8.0",
  "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "mp4parse_fallible"
+version = "0.0.1"
+
+[[package]]
 name = "net2"
 version = "0.2.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -795,29 +795,34 @@ dependencies = [
 ]
 
 [[package]]
 name = "mp4parse"
 version = "0.8.0"
 dependencies = [
  "bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mp4parse_fallible 0.0.1",
  "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp4parse_capi"
 version = "0.8.0"
 dependencies = [
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "mp4parse 0.8.0",
  "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "mp4parse_fallible"
+version = "0.0.1"
+
+[[package]]
 name = "net2"
 version = "0.2.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",