Bug 1389470 - use fallible memory allocation to avoid OOM. r?kinetik draft
authorAlfredo.Yang <ayang@mozilla.com>
Fri, 08 Sep 2017 15:38:59 +0800
changeset 662968 a3deab318cef3844572762ebba508e7d8411a0e6
parent 658813 1401e3eec44df87963d3af329ef8a4183ab0483f
child 731035 0c6d5ae63135e9f5712c042e5abe7ec53c2b55d0
push id79257
push userbmo:ayang@mozilla.com
push dateTue, 12 Sep 2017 09:49:19 +0000
reviewerskinetik
bugs1389470
milestone57.0a1
Bug 1389470 - use fallible memory allocation to avoid OOM. r?kinetik MozReview-Commit-ID: LSGPHPRyCMS
media/libstagefright/binding/MP4Metadata.cpp
media/libstagefright/binding/fallible/Cargo.toml
media/libstagefright/binding/fallible/lib.rs
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/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
@@ -757,16 +757,17 @@ 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);
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/fallible/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "fallible"
+version = "0.0.1"
+authors = ["The Servo Project Developers"]
+license = "MPL-2.0"
+publish = false
+
+[lib]
+name = "fallible"
+path = "lib.rs"
+
+[dependencies]
+smallvec = "0.4"
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/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/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"
+-fallible = { path = "../fallible" }
 +byteorder = "1.0.0"
 +bitreader = { version = "0.3.0" }
 +num-traits = "0.1.37"
++fallible = { path = "../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"
+fallible = { path = "../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 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 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),
--- 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=500dd97c307c346eca8f3bc61dd374d67fc9b802
 
 # 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 fallible
+mkdir -p fallible
+cp _upstream/mp4parse/fallible/* 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
@@ -481,16 +481,23 @@ source = "registry+https://github.com/ru
 dependencies = [
  "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "fallible"
+version = "0.0.1"
+dependencies = [
+ "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "fnv"
 version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "freetype"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -769,16 +776,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)",
+ "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]]
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -479,16 +479,23 @@ source = "registry+https://github.com/ru
 dependencies = [
  "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "fallible"
+version = "0.0.1"
+dependencies = [
+ "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "fnv"
 version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "freetype"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -767,16 +774,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)",
+ "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)",