mirror of
https://github.com/alfg/mp4-rust.git
synced 2024-05-20 01:08:06 +00:00
Fix several overflows in box and track processing (#94)
* Fix several overflows in box and track processing * Use size_of::<Type>() instead of magic numbers * Fix a panic in Mp4Track::read_sample() for one-past-the-end This appears to be a bug unmasked by other changes. read_sample() calls sample_offset() then sample_size(), and assumes that if the former returns Ok then the latter does as well. However, if the sample_id is one past the end, sample_offset() might succeed (it only checks samples _up to_ the given sample_id but not _including_ it) while sample_size() fails (because the sample doesn't exist). read_sample() will then panic. Fix this by duplicating the error propagation (that is currently done for sample_offset) for sample_size, instead of unwrapping. This is a cautious change that fixes the bug; alternatively, having sample_offset() call sample_size() on the given sample_id and propagate any error might also work. * Account for header size in box processing overflow fixes * Ensure that boxes aren't bigger than their containers Together with the entry_count checks, this eliminates several OOMs when reading incorrect mp4 files. * Fix order of arithmetic operations This was due to an incorrect transcription when switching to checked arithmetic, and fixes a bug that could cause attempted lookups of the wrong chunk_id.
This commit is contained in:
parent
024c26ade7
commit
7cfdffbd71
|
@ -103,6 +103,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for Avc1Box {
|
||||||
|
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"avc1 box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
if name == BoxType::AvcCBox {
|
if name == BoxType::AvcCBox {
|
||||||
let avcc = AvcCBox::read_box(reader, s)?;
|
let avcc = AvcCBox::read_box(reader, s)?;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::io::{Read, Seek, Write};
|
use std::io::{Read, Seek, Write};
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use crate::mp4box::*;
|
use crate::mp4box::*;
|
||||||
|
|
||||||
|
@ -48,7 +49,20 @@ impl<R: Read + Seek> ReadBox<&mut R> for Co64Box {
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||||
|
let other_size = size_of::<u32>(); // entry_count
|
||||||
|
let entry_size = size_of::<u64>(); // chunk_offset
|
||||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
if u64::from(entry_count)
|
||||||
|
> size
|
||||||
|
.saturating_sub(header_size)
|
||||||
|
.saturating_sub(other_size as u64)
|
||||||
|
/ entry_size as u64
|
||||||
|
{
|
||||||
|
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);
|
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||||
for _i in 0..entry_count {
|
for _i in 0..entry_count {
|
||||||
let chunk_offset = reader.read_u64::<BigEndian>()?;
|
let chunk_offset = reader.read_u64::<BigEndian>()?;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::io::{Read, Seek, Write};
|
use std::io::{Read, Seek, Write};
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use crate::mp4box::*;
|
use crate::mp4box::*;
|
||||||
|
|
||||||
|
@ -54,7 +55,21 @@ impl<R: Read + Seek> ReadBox<&mut R> for CttsBox {
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
let entry_size = size_of::<u32>() + size_of::<i32>(); // sample_count + sample_offset
|
||||||
|
// (sample_offset might be a u32, but the size is the same.)
|
||||||
|
let other_size = size_of::<i32>(); // entry_count
|
||||||
|
if u64::from(entry_count)
|
||||||
|
> size
|
||||||
|
.saturating_sub(header_size)
|
||||||
|
.saturating_sub(other_size as u64)
|
||||||
|
/ entry_size as u64
|
||||||
|
{
|
||||||
|
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);
|
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||||
for _ in 0..entry_count {
|
for _ in 0..entry_count {
|
||||||
let entry = CttsEntry {
|
let entry = CttsEntry {
|
||||||
|
|
|
@ -49,6 +49,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for DinfBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"dinf box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::DrefBox => {
|
BoxType::DrefBox => {
|
||||||
|
@ -156,6 +161,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for DrefBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"dinf box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::UrlBox => {
|
BoxType::UrlBox => {
|
||||||
|
@ -254,7 +264,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for UrlBox {
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
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 buf_size = size - HEADER_SIZE - HEADER_EXT_SIZE - 1;
|
||||||
let mut buf = vec![0u8; buf_size as usize];
|
let mut buf = vec![0u8; buf_size as usize];
|
||||||
reader.read_exact(&mut buf)?;
|
reader.read_exact(&mut buf)?;
|
||||||
|
|
|
@ -54,6 +54,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for EdtsBox {
|
||||||
|
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"edts box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if let BoxType::ElstBox = name {
|
if let BoxType::ElstBox = name {
|
||||||
let elst = ElstBox::read_box(reader, s)?;
|
let elst = ElstBox::read_box(reader, s)?;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::io::{Read, Seek, Write};
|
use std::io::{Read, Seek, Write};
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use crate::mp4box::*;
|
use crate::mp4box::*;
|
||||||
|
|
||||||
|
@ -62,7 +63,29 @@ impl<R: Read + Seek> ReadBox<&mut R> for ElstBox {
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
let other_size = size_of::<i32>(); // entry_count
|
||||||
|
let entry_size = {
|
||||||
|
let mut entry_size = 0;
|
||||||
|
entry_size += if version == 1 {
|
||||||
|
size_of::<u64>() + size_of::<i64>() // segment_duration + media_time
|
||||||
|
} else {
|
||||||
|
size_of::<u32>() + size_of::<i32>() // segment_duration + media_time
|
||||||
|
};
|
||||||
|
entry_size += size_of::<i16>() + size_of::<i16>(); // media_rate_integer + media_rate_fraction
|
||||||
|
entry_size
|
||||||
|
};
|
||||||
|
if u64::from(entry_count)
|
||||||
|
> size
|
||||||
|
.saturating_sub(header_size)
|
||||||
|
.saturating_sub(other_size as u64)
|
||||||
|
/ entry_size as u64
|
||||||
|
{
|
||||||
|
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);
|
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||||
for _ in 0..entry_count {
|
for _ in 0..entry_count {
|
||||||
let (segment_duration, media_time) = if version == 1 {
|
let (segment_duration, media_time) = if version == 1 {
|
||||||
|
|
|
@ -53,12 +53,12 @@ impl<R: Read + Seek> ReadBox<&mut R> for FtypBox {
|
||||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let start = box_start(reader)?;
|
let start = box_start(reader)?;
|
||||||
|
|
||||||
let major = reader.read_u32::<BigEndian>()?;
|
if size < 16 || size % 4 != 0 {
|
||||||
let minor = reader.read_u32::<BigEndian>()?;
|
return Err(Error::InvalidData("ftyp size too small or not aligned"));
|
||||||
if size % 4 != 0 {
|
|
||||||
return Err(Error::InvalidData("invalid ftyp size"));
|
|
||||||
}
|
}
|
||||||
let brand_count = (size - 16) / 4; // header + major + minor
|
let brand_count = (size - 16) / 4; // header + major + minor
|
||||||
|
let major = reader.read_u32::<BigEndian>()?;
|
||||||
|
let minor = reader.read_u32::<BigEndian>()?;
|
||||||
|
|
||||||
let mut brands = Vec::new();
|
let mut brands = Vec::new();
|
||||||
for _ in 0..brand_count {
|
for _ in 0..brand_count {
|
||||||
|
|
|
@ -52,7 +52,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for HdlrBox {
|
||||||
|
|
||||||
skip_bytes(reader, 12)?; // reserved
|
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];
|
let mut buf = vec![0u8; buf_size as usize];
|
||||||
reader.read_exact(&mut buf)?;
|
reader.read_exact(&mut buf)?;
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for Hev1Box {
|
||||||
|
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"hev1 box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
if name == BoxType::HvcCBox {
|
if name == BoxType::HvcCBox {
|
||||||
let hvcc = HvcCBox::read_box(reader, s)?;
|
let hvcc = HvcCBox::read_box(reader, s)?;
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for IlstBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"ilst box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::NameBox => {
|
BoxType::NameBox => {
|
||||||
|
@ -129,6 +134,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for IlstItemBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"ilst item box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::DataBox => {
|
BoxType::DataBox => {
|
||||||
|
|
|
@ -54,6 +54,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for MdiaBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"mdia box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::MdhdBox => {
|
BoxType::MdhdBox => {
|
||||||
|
|
|
@ -69,6 +69,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for MinfBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"minf box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::VmhdBox => {
|
BoxType::VmhdBox => {
|
||||||
|
|
|
@ -58,6 +58,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoofBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"moof box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::MfhdBox => {
|
BoxType::MfhdBox => {
|
||||||
|
|
|
@ -77,6 +77,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"moov box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::MvhdBox => {
|
BoxType::MvhdBox => {
|
||||||
|
|
|
@ -94,6 +94,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for Mp4aBox {
|
||||||
if current < start + size {
|
if current < start + size {
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"mp4a box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if name == BoxType::EsdsBox {
|
if name == BoxType::EsdsBox {
|
||||||
esds = Some(EsdsBox::read_box(reader, s)?);
|
esds = Some(EsdsBox::read_box(reader, s)?);
|
||||||
|
|
|
@ -52,6 +52,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvexBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"mvex box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::MehdBox => {
|
BoxType::MehdBox => {
|
||||||
|
|
|
@ -92,6 +92,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for StblBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"stbl box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::StsdBox => {
|
BoxType::StsdBox => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::io::{Read, Seek, Write};
|
use std::io::{Read, Seek, Write};
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use crate::mp4box::*;
|
use crate::mp4box::*;
|
||||||
|
|
||||||
|
@ -48,7 +49,20 @@ impl<R: Read + Seek> ReadBox<&mut R> for StcoBox {
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||||
|
let other_size = size_of::<u32>(); // entry_count
|
||||||
|
let entry_size = size_of::<u32>(); // chunk_offset
|
||||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
if u64::from(entry_count)
|
||||||
|
> size
|
||||||
|
.saturating_sub(header_size)
|
||||||
|
.saturating_sub(other_size as u64)
|
||||||
|
/ entry_size as u64
|
||||||
|
{
|
||||||
|
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);
|
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||||
for _i in 0..entry_count {
|
for _i in 0..entry_count {
|
||||||
let chunk_offset = reader.read_u32::<BigEndian>()?;
|
let chunk_offset = reader.read_u32::<BigEndian>()?;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::io::{Read, Seek, Write};
|
use std::io::{Read, Seek, Write};
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use crate::mp4box::*;
|
use crate::mp4box::*;
|
||||||
|
|
||||||
|
@ -56,7 +57,20 @@ impl<R: Read + Seek> ReadBox<&mut R> for StscBox {
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||||
|
let other_size = size_of::<u32>(); // entry_count
|
||||||
|
let entry_size = size_of::<u32>() + size_of::<u32>() + size_of::<u32>(); // first_chunk + samples_per_chunk + sample_description_index
|
||||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
if u64::from(entry_count)
|
||||||
|
> size
|
||||||
|
.saturating_sub(header_size)
|
||||||
|
.saturating_sub(other_size as u64)
|
||||||
|
/ entry_size as u64
|
||||||
|
{
|
||||||
|
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);
|
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||||
for _ in 0..entry_count {
|
for _ in 0..entry_count {
|
||||||
let entry = StscEntry {
|
let entry = StscEntry {
|
||||||
|
@ -77,7 +91,14 @@ impl<R: Read + Seek> ReadBox<&mut R> for StscBox {
|
||||||
};
|
};
|
||||||
if i < entry_count - 1 {
|
if i < entry_count - 1 {
|
||||||
let next_entry = entries.get(i as usize + 1).unwrap();
|
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",
|
||||||
|
))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"stsd box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::Avc1Box => {
|
BoxType::Avc1Box => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::io::{Read, Seek, Write};
|
use std::io::{Read, Seek, Write};
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use crate::mp4box::*;
|
use crate::mp4box::*;
|
||||||
|
|
||||||
|
@ -48,7 +49,20 @@ impl<R: Read + Seek> ReadBox<&mut R> for StssBox {
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||||
|
let other_size = size_of::<u32>(); // entry_count
|
||||||
|
let entry_size = size_of::<u32>(); // sample_number
|
||||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
if u64::from(entry_count)
|
||||||
|
> size
|
||||||
|
.saturating_sub(header_size)
|
||||||
|
.saturating_sub(other_size as u64)
|
||||||
|
/ entry_size as u64
|
||||||
|
{
|
||||||
|
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);
|
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||||
for _i in 0..entry_count {
|
for _i in 0..entry_count {
|
||||||
let sample_number = reader.read_u32::<BigEndian>()?;
|
let sample_number = reader.read_u32::<BigEndian>()?;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::io::{Read, Seek, Write};
|
use std::io::{Read, Seek, Write};
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use crate::mp4box::*;
|
use crate::mp4box::*;
|
||||||
|
|
||||||
|
@ -55,10 +56,28 @@ impl<R: Read + Seek> ReadBox<&mut R> for StszBox {
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||||
|
let other_size = size_of::<u32>() + size_of::<u32>(); // sample_size + sample_count
|
||||||
let sample_size = reader.read_u32::<BigEndian>()?;
|
let sample_size = reader.read_u32::<BigEndian>()?;
|
||||||
|
let stsz_item_size = if sample_size == 0 {
|
||||||
|
size_of::<u32>() // entry_size
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
let sample_count = reader.read_u32::<BigEndian>()?;
|
let sample_count = reader.read_u32::<BigEndian>()?;
|
||||||
let mut sample_sizes = Vec::with_capacity(sample_count as usize);
|
let mut sample_sizes = Vec::new();
|
||||||
if sample_size == 0 {
|
if sample_size == 0 {
|
||||||
|
if u64::from(sample_count)
|
||||||
|
> size
|
||||||
|
.saturating_sub(header_size)
|
||||||
|
.saturating_sub(other_size as u64)
|
||||||
|
/ stsz_item_size as u64
|
||||||
|
{
|
||||||
|
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 {
|
for _ in 0..sample_count {
|
||||||
let sample_number = reader.read_u32::<BigEndian>()?;
|
let sample_number = reader.read_u32::<BigEndian>()?;
|
||||||
sample_sizes.push(sample_number);
|
sample_sizes.push(sample_number);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::io::{Read, Seek, Write};
|
use std::io::{Read, Seek, Write};
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use crate::mp4box::*;
|
use crate::mp4box::*;
|
||||||
|
|
||||||
|
@ -54,7 +55,20 @@ impl<R: Read + Seek> ReadBox<&mut R> for SttsBox {
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||||
|
let other_size = size_of::<u32>(); // entry_count
|
||||||
|
let entry_size = size_of::<u32>() + size_of::<u32>(); // sample_count + sample_delta
|
||||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
if u64::from(entry_count)
|
||||||
|
> size
|
||||||
|
.saturating_sub(header_size)
|
||||||
|
.saturating_sub(other_size as u64)
|
||||||
|
/ entry_size as u64
|
||||||
|
{
|
||||||
|
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);
|
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||||
for _i in 0..entry_count {
|
for _i in 0..entry_count {
|
||||||
let entry = SttsEntry {
|
let entry = SttsEntry {
|
||||||
|
|
|
@ -59,6 +59,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"traf box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::TfhdBox => {
|
BoxType::TfhdBox => {
|
||||||
|
|
|
@ -68,6 +68,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrakBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"trak box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::TkhdBox => {
|
BoxType::TkhdBox => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::io::{Read, Seek, Write};
|
use std::io::{Read, Seek, Write};
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use crate::mp4box::*;
|
use crate::mp4box::*;
|
||||||
|
|
||||||
|
@ -83,6 +84,15 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrunBox {
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||||
|
let other_size = size_of::<u32>() // sample_count
|
||||||
|
+ if TrunBox::FLAG_DATA_OFFSET & flags > 0 { size_of::<i32>() } else { 0 } // data_offset
|
||||||
|
+ if TrunBox::FLAG_FIRST_SAMPLE_FLAGS & flags > 0 { size_of::<u32>() } else { 0 }; // first_sample_flags
|
||||||
|
let sample_size = if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 { size_of::<u32>() } else { 0 } // sample_duration
|
||||||
|
+ if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 { size_of::<u32>() } else { 0 } // sample_size
|
||||||
|
+ if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 { size_of::<u32>() } else { 0 } // sample_flags
|
||||||
|
+ if TrunBox::FLAG_SAMPLE_CTS & flags > 0 { size_of::<u32>() } else { 0 }; // sample_composition_time_offset
|
||||||
|
|
||||||
let sample_count = reader.read_u32::<BigEndian>()?;
|
let sample_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
|
||||||
let data_offset = if TrunBox::FLAG_DATA_OFFSET & flags > 0 {
|
let data_offset = if TrunBox::FLAG_DATA_OFFSET & flags > 0 {
|
||||||
|
@ -97,10 +107,32 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrunBox {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut sample_durations = Vec::with_capacity(sample_count as usize);
|
let mut sample_durations = Vec::new();
|
||||||
let mut sample_sizes = Vec::with_capacity(sample_count as usize);
|
let mut sample_sizes = Vec::new();
|
||||||
let mut sample_flags = Vec::with_capacity(sample_count as usize);
|
let mut sample_flags = Vec::new();
|
||||||
let mut sample_cts = Vec::with_capacity(sample_count as usize);
|
let mut sample_cts = Vec::new();
|
||||||
|
if u64::from(sample_count) * sample_size as u64
|
||||||
|
> size
|
||||||
|
.saturating_sub(header_size)
|
||||||
|
.saturating_sub(other_size as u64)
|
||||||
|
{
|
||||||
|
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 {
|
for _ in 0..sample_count {
|
||||||
if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 {
|
if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 {
|
||||||
let duration = reader.read_u32::<BigEndian>()?;
|
let duration = reader.read_u32::<BigEndian>()?;
|
||||||
|
|
|
@ -55,6 +55,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for UdtaBox {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"udta box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::MetaBox => {
|
BoxType::MetaBox => {
|
||||||
|
|
|
@ -121,6 +121,11 @@ impl<R: Read + Seek> ReadBox<&mut R> for Vp09Box {
|
||||||
|
|
||||||
let vpcc = {
|
let vpcc = {
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
|
if header.size > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"vp09 box contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
VpccBox::read_box(reader, header.size)?
|
VpccBox::read_box(reader, header.size)?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,11 @@ impl<R: Read + Seek> Mp4Reader<R> {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(&mut reader)?;
|
let header = BoxHeader::read(&mut reader)?;
|
||||||
let BoxHeader { name, size: s } = header;
|
let BoxHeader { name, size: s } = header;
|
||||||
|
if s > size {
|
||||||
|
return Err(Error::InvalidData(
|
||||||
|
"file contains a box with a larger size than it",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// Break if size zero BoxHeader, which can result in dead-loop.
|
// Break if size zero BoxHeader, which can result in dead-loop.
|
||||||
if s == 0 {
|
if s == 0 {
|
||||||
|
|
46
src/track.rs
46
src/track.rs
|
@ -234,7 +234,9 @@ impl Mp4Track {
|
||||||
let mut sample_count = 0u32;
|
let mut sample_count = 0u32;
|
||||||
for traf in self.trafs.iter() {
|
for traf in self.trafs.iter() {
|
||||||
if let Some(ref trun) = traf.trun {
|
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
|
sample_count
|
||||||
|
@ -342,12 +344,18 @@ impl Mp4Track {
|
||||||
|
|
||||||
fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32)> {
|
fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32)> {
|
||||||
let ctts = self.trak.mdia.minf.stbl.ctts.as_ref().unwrap();
|
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() {
|
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));
|
return Ok((i, sample_count));
|
||||||
}
|
}
|
||||||
sample_count += entry.sample_count;
|
sample_count = next_sample_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Error::EntryInStblNotFound(
|
Err(Error::EntryInStblNotFound(
|
||||||
|
@ -367,7 +375,9 @@ impl Mp4Track {
|
||||||
if sample_count > (global_idx - offset) {
|
if sample_count > (global_idx - offset) {
|
||||||
return Some((traf_idx, (global_idx - offset) as _));
|
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
|
None
|
||||||
|
@ -441,7 +451,13 @@ impl Mp4Track {
|
||||||
let first_sample = stsc_entry.first_sample;
|
let first_sample = stsc_entry.first_sample;
|
||||||
let samples_per_chunk = stsc_entry.samples_per_chunk;
|
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)
|
||||||
|
.map(|n| n / samples_per_chunk)
|
||||||
|
.and_then(|n| n.checked_add(first_chunk))
|
||||||
|
.ok_or(Error::InvalidData(
|
||||||
|
"attempt to calculate stsc chunk_id with overflow",
|
||||||
|
))?;
|
||||||
|
|
||||||
let chunk_offset = self.chunk_offset(chunk_id)?;
|
let chunk_offset = self.chunk_offset(chunk_id)?;
|
||||||
|
|
||||||
|
@ -459,7 +475,7 @@ impl Mp4Track {
|
||||||
fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
|
fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
|
||||||
let stts = &self.trak.mdia.minf.stbl.stts;
|
let stts = &self.trak.mdia.minf.stbl.stts;
|
||||||
|
|
||||||
let mut sample_count = 1;
|
let mut sample_count: u32 = 1;
|
||||||
let mut elapsed = 0;
|
let mut elapsed = 0;
|
||||||
|
|
||||||
if !self.trafs.is_empty() {
|
if !self.trafs.is_empty() {
|
||||||
|
@ -467,13 +483,19 @@ impl Mp4Track {
|
||||||
Ok((start_time, self.default_sample_duration))
|
Ok((start_time, self.default_sample_duration))
|
||||||
} else {
|
} else {
|
||||||
for entry in stts.entries.iter() {
|
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 =
|
let start_time =
|
||||||
(sample_id - sample_count) as u64 * entry.sample_delta as u64 + elapsed;
|
(sample_id - sample_count) as u64 * entry.sample_delta as u64 + elapsed;
|
||||||
return Ok((start_time, entry.sample_delta));
|
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;
|
elapsed += entry.sample_count as u64 * entry.sample_delta as u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,7 +540,11 @@ impl Mp4Track {
|
||||||
Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None),
|
Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None),
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
};
|
};
|
||||||
let sample_size = self.sample_size(sample_id).unwrap();
|
let sample_size = match self.sample_size(sample_id) {
|
||||||
|
Ok(size) => size,
|
||||||
|
Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None),
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
};
|
||||||
|
|
||||||
let mut buffer = vec![0x0u8; sample_size as usize];
|
let mut buffer = vec![0x0u8; sample_size as usize];
|
||||||
reader.seek(SeekFrom::Start(sample_offset))?;
|
reader.seek(SeekFrom::Start(sample_offset))?;
|
||||||
|
|
Loading…
Reference in a new issue