Bug 1329126 - update rust mp4 parser for preventing buffer overflow. r=kinetik

MozReview-Commit-ID: Ih85J4nejLx

--HG--
extra : rebase_source : 033d42491bbaf556a1e5adc09406cf6fa2f81e18
This commit is contained in:
Alfredo.Yang 2017-03-31 15:49:43 +08:00
Родитель 341d05c4ee
Коммит 348ae4d9df
6 изменённых файлов: 56 добавлений и 84 удалений

Просмотреть файл

@ -45,7 +45,7 @@ index aeeebc65..5c0836a 100644
num-traits = "0.1.37"
-[build-dependencies]
-rusty-cheddar = "0.3.2"
-rusty-cheddar = { git = "https://github.com/kinetiknz/rusty-cheddar" }
-
-[features]
-fuzz = ["mp4parse/fuzz"]

Просмотреть файл

@ -49,7 +49,7 @@ impl From<u32> for FourCC {
fn from(number: u32) -> FourCC {
let mut box_chars = Vec::new();
for x in 0..4 {
let c = (number >> x * 8 & 0x000000FF) as u8;
let c = (number >> (x * 8) & 0x000000FF) as u8;
box_chars.push(c);
}
box_chars.reverse();

Просмотреть файл

@ -26,7 +26,7 @@ use boxes::{BoxType, FourCC};
mod tests;
// Arbitrary buffer size limit used for raw read_bufs on a box.
const BUF_SIZE_LIMIT: u64 = 1024 * 1024;
const BUF_SIZE_LIMIT: usize = 1024 * 1024;
static DEBUG_MODE: std::sync::atomic::AtomicBool = std::sync::atomic::ATOMIC_BOOL_INIT;
@ -1510,9 +1510,6 @@ fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
let (_, _) = read_fullbox_extra(src)?;
let esds_size = src.head.size - src.head.offset - 4;
if esds_size > BUF_SIZE_LIMIT {
return Err(Error::InvalidData("esds box exceeds BUF_SIZE_LIMIT"));
}
let esds_array = read_buf(src, esds_size as usize)?;
let mut es_data = ES_Descriptor::default();
@ -1700,9 +1697,6 @@ fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>) -> Result<(CodecType,
return Err(Error::InvalidData("malformed video sample entry"));
}
let avcc_size = b.head.size - b.head.offset;
if avcc_size > BUF_SIZE_LIMIT {
return Err(Error::InvalidData("avcC box exceeds BUF_SIZE_LIMIT"));
}
let avcc = read_buf(&mut b.content, avcc_size as usize)?;
log!("{:?} (avcc)", avcc);
// TODO(kinetik): Parse avcC box? For now we just stash the data.
@ -1993,6 +1987,9 @@ fn skip<T: Read>(src: &mut T, mut bytes: usize) -> Result<()> {
/// 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 {

Просмотреть файл

@ -641,7 +641,7 @@ fn avcc_limit() {
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
match super::read_video_sample_entry(&mut stream) {
Err(Error::InvalidData(s)) => assert_eq!(s, "avcC box exceeds BUF_SIZE_LIMIT"),
Err(Error::InvalidData(s)) => assert_eq!(s, "read_buf size exceeds BUF_SIZE_LIMIT"),
Ok(_) => panic!("expected an error result"),
_ => panic!("expected a different error result"),
}
@ -666,7 +666,7 @@ fn esds_limit() {
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
match super::read_audio_sample_entry(&mut stream) {
Err(Error::InvalidData(s)) => assert_eq!(s, "esds box exceeds BUF_SIZE_LIMIT"),
Err(Error::InvalidData(s)) => assert_eq!(s, "read_buf size exceeds BUF_SIZE_LIMIT"),
Ok(_) => panic!("expected an error result"),
_ => panic!("expected a different error result"),
}

Просмотреть файл

@ -19,7 +19,7 @@
//!
//! let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
//! let io = mp4parse_capi::mp4parse_io {
//! read: buf_read,
//! read: Some(buf_read),
//! userdata: &mut file as *mut _ as *mut std::os::raw::c_void
//! };
//! unsafe {
@ -261,7 +261,7 @@ impl mp4parse_parser {
#[repr(C)]
#[derive(Clone)]
pub struct mp4parse_io {
pub read: extern fn(buffer: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize,
pub read: Option<extern fn(buffer: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize>,
pub userdata: *mut std::os::raw::c_void,
}
@ -270,7 +270,7 @@ impl Read for mp4parse_io {
if buf.len() > isize::max_value() as usize {
return Err(std::io::Error::new(std::io::ErrorKind::Other, "buf length overflow in mp4parse_io Read impl"));
}
let rv = (self.read)(buf.as_mut_ptr(), buf.len(), self.userdata);
let rv = self.read.unwrap()(buf.as_mut_ptr(), buf.len(), self.userdata);
if rv >= 0 {
Ok(rv as usize)
} else {
@ -287,12 +287,7 @@ pub unsafe extern fn mp4parse_new(io: *const mp4parse_io) -> *mut mp4parse_parse
if io.is_null() || (*io).userdata.is_null() {
return std::ptr::null_mut();
}
// is_null() isn't available on a fn type because it can't be null (in
// Rust) by definition. But since this value is coming from the C API,
// it *could* be null. Ideally, we'd wrap it in an Option to represent
// reality, but this causes rusty-cheddar to emit the wrong type
// (https://github.com/Sean1708/rusty-cheddar/issues/30).
if ((*io).read as *mut std::os::raw::c_void).is_null() {
if (*io).read.is_none() {
return std::ptr::null_mut();
}
let parser = Box::new(mp4parse_parser(Wrap {
@ -568,15 +563,12 @@ pub unsafe extern fn mp4parse_get_track_audio_info(parser: *mut mp4parse_parser,
Ok(_) => {
let header = (*parser).opus_header_mut();
header.insert(track_index, v);
match header.get(&track_index) {
None => {}
Some(v) => {
if v.len() > std::u32::MAX as usize {
return MP4PARSE_ERROR_INVALID;
}
(*info).codec_specific_config.length = v.len() as u32;
(*info).codec_specific_config.data = v.as_ptr();
if let Some(v) = header.get(&track_index) {
if v.len() > std::u32::MAX as usize {
return MP4PARSE_ERROR_INVALID;
}
(*info).codec_specific_config.length = v.len() as u32;
(*info).codec_specific_config.data = v.as_ptr();
}
}
}
@ -584,15 +576,12 @@ pub unsafe extern fn mp4parse_get_track_audio_info(parser: *mut mp4parse_parser,
AudioCodecSpecific::MP3 => (),
}
match audio.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
Some(p) => {
if let Some(ref tenc) = p.tenc {
(*info).protected_data.is_encrypted = tenc.is_encrypted;
(*info).protected_data.iv_size = tenc.iv_size;
(*info).protected_data.kid.set_data(&(tenc.kid));
}
},
_ => {},
if let Some(p) = audio.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
if let Some(ref tenc) = p.tenc {
(*info).protected_data.is_encrypted = tenc.is_encrypted;
(*info).protected_data.iv_size = tenc.iv_size;
(*info).protected_data.kid.set_data(&(tenc.kid));
}
}
MP4PARSE_OK
@ -648,22 +637,16 @@ pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser,
(*info).image_width = video.width;
(*info).image_height = video.height;
match video.codec_specific {
VideoCodecSpecific::AVCConfig(ref avc) => {
(*info).extra_data.set_data(avc);
},
_ => {},
if let VideoCodecSpecific::AVCConfig(ref avc) = video.codec_specific {
(*info).extra_data.set_data(avc);
}
match video.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
Some(p) => {
if let Some(ref tenc) = p.tenc {
(*info).protected_data.is_encrypted = tenc.is_encrypted;
(*info).protected_data.iv_size = tenc.iv_size;
(*info).protected_data.kid.set_data(&(tenc.kid));
}
},
_ => {},
if let Some(p) = video.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
if let Some(ref tenc) = p.tenc {
(*info).protected_data.is_encrypted = tenc.is_encrypted;
(*info).protected_data.iv_size = tenc.iv_size;
(*info).protected_data.kid.set_data(&(tenc.kid));
}
}
MP4PARSE_OK
@ -686,12 +669,9 @@ pub unsafe extern fn mp4parse_get_indice_table(parser: *mut mp4parse_parser, tra
};
let index_table = (*parser).sample_table_mut();
match index_table.get(&track_id) {
Some(v) => {
(*indices).set_indices(v);
return MP4PARSE_OK;
},
_ => {},
if let Some(v) = index_table.get(&track_id) {
(*indices).set_indices(v);
return MP4PARSE_OK;
}
let media_time = match (&track.media_time, &track.timescale) {
@ -718,13 +698,10 @@ pub unsafe extern fn mp4parse_get_indice_table(parser: *mut mp4parse_parser, tra
_ => 0,
};
match create_sample_table(track, offset_time) {
Some(v) => {
(*indices).set_indices(&v);
index_table.insert(track_id, v);
return MP4PARSE_OK;
},
_ => {},
if let Some(v) = create_sample_table(track, offset_time) {
(*indices).set_indices(&v);
index_table.insert(track_id, v);
return MP4PARSE_OK;
}
MP4PARSE_ERROR_INVALID
@ -934,13 +911,10 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<mp4p
}
// Mark the sync sample in sample_table according to 'stss'.
match track.stss {
Some(ref v) => {
for iter in &v.samples {
sample_table[(iter - 1) as usize].sync = true;
}
},
_ => {}
if let Some(ref v) = track.stss {
for iter in &v.samples {
sample_table[(iter - 1) as usize].sync = true;
}
}
let ctts_iter = match track.ctts {
@ -1114,12 +1088,13 @@ pub unsafe extern fn mp4parse_get_pssh_info(parser: *mut mp4parse_parser, info:
pssh_data.clear();
for pssh in &context.psshs {
let content_len = pssh.box_content.len();
if content_len > std::u32::MAX as usize {
return MP4PARSE_ERROR_INVALID;
}
let mut data_len = Vec::new();
match data_len.write_u32::<byteorder::NativeEndian>(pssh.box_content.len() as u32) {
Err(_) => {
return MP4PARSE_ERROR_IO;
},
_ => (),
if data_len.write_u32::<byteorder::NativeEndian>(content_len as u32).is_err() {
return MP4PARSE_ERROR_IO;
}
pssh_data.extend_from_slice(pssh.system_id.as_slice());
pssh_data.extend_from_slice(data_len.as_slice());
@ -1156,7 +1131,7 @@ extern fn valid_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_v
fn new_parser() {
let mut dummy_value: u32 = 42;
let io = mp4parse_io {
read: panic_read,
read: Some(panic_read),
userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
};
unsafe {
@ -1195,19 +1170,19 @@ fn arg_validation() {
let null_mut: *mut std::os::raw::c_void = std::ptr::null_mut();
// Passing an mp4parse_io with null members is an error.
let io = mp4parse_io { read: std::mem::transmute(null_mut),
let io = mp4parse_io { read: None,
userdata: null_mut };
let parser = mp4parse_new(&io);
assert!(parser.is_null());
let io = mp4parse_io { read: panic_read,
let io = mp4parse_io { read: Some(panic_read),
userdata: null_mut };
let parser = mp4parse_new(&io);
assert!(parser.is_null());
let mut dummy_value = 42;
let io = mp4parse_io {
read: std::mem::transmute(null_mut),
read: None,
userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
};
let parser = mp4parse_new(&io);
@ -1246,7 +1221,7 @@ fn arg_validation_with_parser() {
unsafe {
let mut dummy_value = 42;
let io = mp4parse_io {
read: error_read,
read: Some(error_read),
userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
};
let parser = mp4parse_new(&io);
@ -1295,7 +1270,7 @@ fn get_track_count_poisoned_parser() {
unsafe {
let mut dummy_value = 42;
let io = mp4parse_io {
read: error_read,
read: Some(error_read),
userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
};
let parser = mp4parse_new(&io);
@ -1314,7 +1289,7 @@ fn get_track_count_poisoned_parser() {
fn arg_validation_with_data() {
unsafe {
let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
let io = mp4parse_io { read: valid_read,
let io = mp4parse_io { read: Some(valid_read),
userdata: &mut file as *mut _ as *mut std::os::raw::c_void };
let parser = mp4parse_new(&io);
assert!(!parser.is_null());

Просмотреть файл

@ -2,7 +2,7 @@
# Script to update mp4parse-rust sources to latest upstream
# Default version.
VER=b78dc3e4e80ce4132e7880ae068d0672cbfeaa48
VER=2f595d947adc981360df4ce34bc157202ca566ae
# Accept version or commit from the command line.
if test -n "$1"; then