diff --git a/src/mp4box/co64.rs b/src/mp4box/co64.rs index a7ea68a..e3bce1b 100644 --- a/src/mp4box/co64.rs +++ b/src/mp4box/co64.rs @@ -49,6 +49,11 @@ impl ReadBox<&mut R> for Co64Box { let (version, flags) = read_box_header_ext(reader)?; let entry_count = reader.read_u32::()?; + if u64::from(entry_count) > size.saturating_sub(4) / 8 { + return Err(Error::InvalidData( + "co64 entry_count indicates more entries than could fit in the box", + )); + } let mut entries = Vec::with_capacity(entry_count as usize); for _i in 0..entry_count { let chunk_offset = reader.read_u64::()?; diff --git a/src/mp4box/ctts.rs b/src/mp4box/ctts.rs index f0ed002..1107e23 100644 --- a/src/mp4box/ctts.rs +++ b/src/mp4box/ctts.rs @@ -55,6 +55,11 @@ impl ReadBox<&mut R> for CttsBox { let (version, flags) = read_box_header_ext(reader)?; let entry_count = reader.read_u32::()?; + if u64::from(entry_count) > size.saturating_sub(4) / 8 { + return Err(Error::InvalidData( + "ctts entry_count indicates more entries than could fit in the box", + )); + } let mut entries = Vec::with_capacity(entry_count as usize); for _ in 0..entry_count { let entry = CttsEntry { diff --git a/src/mp4box/dinf.rs b/src/mp4box/dinf.rs index 3d9a95b..4c468e4 100644 --- a/src/mp4box/dinf.rs +++ b/src/mp4box/dinf.rs @@ -254,7 +254,7 @@ impl ReadBox<&mut R> for UrlBox { let (version, flags) = read_box_header_ext(reader)?; - let location = if size - HEADER_SIZE - HEADER_EXT_SIZE > 0 { + let location = if size.saturating_sub(HEADER_SIZE + HEADER_EXT_SIZE) > 0 { let buf_size = size - HEADER_SIZE - HEADER_EXT_SIZE - 1; let mut buf = vec![0u8; buf_size as usize]; reader.read_exact(&mut buf)?; diff --git a/src/mp4box/elst.rs b/src/mp4box/elst.rs index 1c48952..e647a43 100644 --- a/src/mp4box/elst.rs +++ b/src/mp4box/elst.rs @@ -63,6 +63,13 @@ impl ReadBox<&mut R> for ElstBox { let (version, flags) = read_box_header_ext(reader)?; let entry_count = reader.read_u32::()?; + let header_size = 4; + let entry_size = if version == 1 { 20 } else { 12 }; + if u64::from(entry_count) > size.saturating_sub(header_size) / entry_size { + return Err(Error::InvalidData( + "elst entry_count indicates more entries than could fit in the box", + )); + } let mut entries = Vec::with_capacity(entry_count as usize); for _ in 0..entry_count { let (segment_duration, media_time) = if version == 1 { diff --git a/src/mp4box/ftyp.rs b/src/mp4box/ftyp.rs index ba1358d..789cd4e 100644 --- a/src/mp4box/ftyp.rs +++ b/src/mp4box/ftyp.rs @@ -53,12 +53,12 @@ impl ReadBox<&mut R> for FtypBox { fn read_box(reader: &mut R, size: u64) -> Result { let start = box_start(reader)?; - let major = reader.read_u32::()?; - let minor = reader.read_u32::()?; - if size % 4 != 0 { - return Err(Error::InvalidData("invalid ftyp size")); + if size < 16 || size % 4 != 0 { + return Err(Error::InvalidData("ftyp size too small or not aligned")); } let brand_count = (size - 16) / 4; // header + major + minor + let major = reader.read_u32::()?; + let minor = reader.read_u32::()?; let mut brands = Vec::new(); for _ in 0..brand_count { diff --git a/src/mp4box/hdlr.rs b/src/mp4box/hdlr.rs index 9fb0368..81b45b7 100644 --- a/src/mp4box/hdlr.rs +++ b/src/mp4box/hdlr.rs @@ -52,7 +52,11 @@ impl ReadBox<&mut R> for HdlrBox { skip_bytes(reader, 12)?; // reserved - let buf_size = size - HEADER_SIZE - HEADER_EXT_SIZE - 20 - 1; + let buf_size = size + .checked_sub(HEADER_SIZE + HEADER_EXT_SIZE + 20 + 1) + .ok_or(Error::InvalidData( + "hdlr size too small", + ))?; let mut buf = vec![0u8; buf_size as usize]; reader.read_exact(&mut buf)?; diff --git a/src/mp4box/stco.rs b/src/mp4box/stco.rs index 2f252f5..1491951 100644 --- a/src/mp4box/stco.rs +++ b/src/mp4box/stco.rs @@ -49,6 +49,11 @@ impl ReadBox<&mut R> for StcoBox { let (version, flags) = read_box_header_ext(reader)?; let entry_count = reader.read_u32::()?; + if u64::from(entry_count) > size.saturating_sub(4) / 4 { + return Err(Error::InvalidData( + "stco entry_count indicates more entries than could fit in the box", + )); + } let mut entries = Vec::with_capacity(entry_count as usize); for _i in 0..entry_count { let chunk_offset = reader.read_u32::()?; diff --git a/src/mp4box/stsc.rs b/src/mp4box/stsc.rs index 53d45c5..385e87d 100644 --- a/src/mp4box/stsc.rs +++ b/src/mp4box/stsc.rs @@ -57,6 +57,11 @@ impl ReadBox<&mut R> for StscBox { let (version, flags) = read_box_header_ext(reader)?; let entry_count = reader.read_u32::()?; + if u64::from(entry_count) > size.saturating_sub(4) / 12 { + return Err(Error::InvalidData( + "stsc entry_count indicates more entries than could fit in the box", + )); + } let mut entries = Vec::with_capacity(entry_count as usize); for _ in 0..entry_count { let entry = StscEntry { @@ -77,7 +82,14 @@ impl ReadBox<&mut R> for StscBox { }; if i < entry_count - 1 { let next_entry = entries.get(i as usize + 1).unwrap(); - sample_id += (next_entry.first_chunk - first_chunk) * samples_per_chunk; + sample_id = next_entry + .first_chunk + .checked_sub(first_chunk) + .and_then(|n| n.checked_mul(samples_per_chunk)) + .and_then(|n| n.checked_add(sample_id)) + .ok_or(Error::InvalidData( + "attempt to calculate stsc sample_id with overflow", + ))?; } } diff --git a/src/mp4box/stss.rs b/src/mp4box/stss.rs index 775131b..3ff50b8 100644 --- a/src/mp4box/stss.rs +++ b/src/mp4box/stss.rs @@ -49,6 +49,11 @@ impl ReadBox<&mut R> for StssBox { let (version, flags) = read_box_header_ext(reader)?; let entry_count = reader.read_u32::()?; + if u64::from(entry_count) > size.saturating_sub(4) / 4 { + return Err(Error::InvalidData( + "stss entry_count indicates more entries than could fit in the box", + )); + } let mut entries = Vec::with_capacity(entry_count as usize); for _i in 0..entry_count { let sample_number = reader.read_u32::()?; diff --git a/src/mp4box/stsz.rs b/src/mp4box/stsz.rs index bd0e184..8b3b9d5 100644 --- a/src/mp4box/stsz.rs +++ b/src/mp4box/stsz.rs @@ -57,8 +57,14 @@ impl ReadBox<&mut R> for StszBox { let sample_size = reader.read_u32::()?; let sample_count = reader.read_u32::()?; - let mut sample_sizes = Vec::with_capacity(sample_count as usize); + let mut sample_sizes = Vec::new(); if sample_size == 0 { + if u64::from(sample_count) > size.saturating_sub(8) / 4 { + return Err(Error::InvalidData( + "stsz sample_count indicates more values than could fit in the box", + )); + } + sample_sizes.reserve(sample_count as usize); for _ in 0..sample_count { let sample_number = reader.read_u32::()?; sample_sizes.push(sample_number); diff --git a/src/mp4box/stts.rs b/src/mp4box/stts.rs index ea84692..a0762c1 100644 --- a/src/mp4box/stts.rs +++ b/src/mp4box/stts.rs @@ -55,6 +55,11 @@ impl ReadBox<&mut R> for SttsBox { let (version, flags) = read_box_header_ext(reader)?; let entry_count = reader.read_u32::()?; + if u64::from(entry_count) > size.saturating_sub(4) / 8 { + return Err(Error::InvalidData( + "stts entry_count indicates more entries than could fit in the box", + )); + } let mut entries = Vec::with_capacity(entry_count as usize); for _i in 0..entry_count { let entry = SttsEntry { diff --git a/src/mp4box/trun.rs b/src/mp4box/trun.rs index 7a8278d..be8e135 100644 --- a/src/mp4box/trun.rs +++ b/src/mp4box/trun.rs @@ -97,10 +97,31 @@ impl ReadBox<&mut R> for TrunBox { None }; - let mut sample_durations = Vec::with_capacity(sample_count as usize); - let mut sample_sizes = Vec::with_capacity(sample_count as usize); - let mut sample_flags = Vec::with_capacity(sample_count as usize); - let mut sample_cts = Vec::with_capacity(sample_count as usize); + let mut sample_durations = Vec::new(); + let mut sample_sizes = Vec::new(); + let mut sample_flags = Vec::new(); + let mut sample_cts = Vec::new(); + let header_size = ((0x0000ff & flags).count_ones() + 1) * 4; + let entry_size = (0x00ff00 & flags).count_ones() * 4; + if u64::from(sample_count) + > size.saturating_sub(u64::from(header_size)) / u64::from(entry_size) + { + return Err(Error::InvalidData( + "trun sample_count indicates more values than could fit in the box", + )); + } + if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 { + sample_durations.reserve(sample_count as usize); + } + if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 { + sample_sizes.reserve(sample_count as usize); + } + if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 { + sample_flags.reserve(sample_count as usize); + } + if TrunBox::FLAG_SAMPLE_CTS & flags > 0 { + sample_cts.reserve(sample_count as usize); + } for _ in 0..sample_count { if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 { let duration = reader.read_u32::()?; diff --git a/src/track.rs b/src/track.rs index ed8b12e..fecbd25 100644 --- a/src/track.rs +++ b/src/track.rs @@ -234,7 +234,9 @@ impl Mp4Track { let mut sample_count = 0u32; for traf in self.trafs.iter() { if let Some(ref trun) = traf.trun { - sample_count += trun.sample_count; + sample_count = sample_count + .checked_add(trun.sample_count) + .expect("attempt to sum trun sample_count with overflow"); } } sample_count @@ -342,12 +344,18 @@ impl Mp4Track { fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32)> { let ctts = self.trak.mdia.minf.stbl.ctts.as_ref().unwrap(); - let mut sample_count = 1; + let mut sample_count: u32 = 1; for (i, entry) in ctts.entries.iter().enumerate() { - if sample_id < sample_count + entry.sample_count { + let next_sample_count = + sample_count + .checked_add(entry.sample_count) + .ok_or(Error::InvalidData( + "attempt to sum ctts entries sample_count with overflow", + ))?; + if sample_id < next_sample_count { return Ok((i, sample_count)); } - sample_count += entry.sample_count; + sample_count = next_sample_count; } Err(Error::EntryInStblNotFound( @@ -367,7 +375,9 @@ impl Mp4Track { if sample_count > (global_idx - offset) { return Some((traf_idx, (global_idx - offset) as _)); } - offset += sample_count; + offset = offset + .checked_add(sample_count) + .expect("attempt to sum trun sample_count with overflow"); } } None @@ -441,7 +451,13 @@ impl Mp4Track { let first_sample = stsc_entry.first_sample; let samples_per_chunk = stsc_entry.samples_per_chunk; - let chunk_id = first_chunk + (sample_id - first_sample) / samples_per_chunk; + let chunk_id = sample_id + .checked_sub(first_sample) + .and_then(|n| n.checked_add(first_chunk)) + .map(|n| n / samples_per_chunk) + .ok_or(Error::InvalidData( + "attempt to calculate stsc chunk_id with overflow", + ))?; let chunk_offset = self.chunk_offset(chunk_id)?; @@ -459,7 +475,7 @@ impl Mp4Track { fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> { let stts = &self.trak.mdia.minf.stbl.stts; - let mut sample_count = 1; + let mut sample_count: u32 = 1; let mut elapsed = 0; if !self.trafs.is_empty() { @@ -467,13 +483,19 @@ impl Mp4Track { Ok((start_time, self.default_sample_duration)) } else { for entry in stts.entries.iter() { - if sample_id < sample_count + entry.sample_count { + let new_sample_count = + sample_count + .checked_add(entry.sample_count) + .ok_or(Error::InvalidData( + "attempt to sum stts entries sample_count with overflow", + ))?; + if sample_id < new_sample_count { let start_time = (sample_id - sample_count) as u64 * entry.sample_delta as u64 + elapsed; return Ok((start_time, entry.sample_delta)); } - sample_count += entry.sample_count; + sample_count = new_sample_count; elapsed += entry.sample_count as u64 * entry.sample_delta as u64; }