зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
341d05c4ee
Коммит
348ae4d9df
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче