Skip to content

Commit

Permalink
feat: bound all allocations to parent atom size or file size
Browse files Browse the repository at this point in the history
  • Loading branch information
saecki committed Feb 23, 2025
1 parent 824cc6c commit 474d044
Show file tree
Hide file tree
Showing 34 changed files with 220 additions and 208 deletions.
7 changes: 5 additions & 2 deletions src/atom/change.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,11 +361,14 @@ macro_rules! atom_ref {
let mut cursor = std::io::Cursor::new(&mut buf);
atom.write(&mut cursor, &changes).unwrap();

let buf_size = buf.len() as u64;

let mut cursor = std::io::Cursor::new(&buf);
cursor.seek(SeekFrom::Start(0)).unwrap();
let head = head::parse(&mut cursor).unwrap();
let head = head::parse(&mut cursor, buf_size).unwrap();

assert_eq!(atom.len(), head.len());
assert_eq!(atom.len(), buf.len() as u64);
assert_eq!(atom.len(), buf_size);
}
}
)+
Expand Down
10 changes: 8 additions & 2 deletions src/atom/chap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub struct Chap {
}

impl Atom for Chap {
const FOURCC: Fourcc = CHAPTER;
const FOURCC: Fourcc = CHAPTER_REFERENCE;
}

impl ParseAtom for Chap {
Expand All @@ -19,9 +19,15 @@ impl ParseAtom for Chap {
size: Size,
) -> crate::Result<Self> {
let bounds = find_bounds(reader, size)?;
if size.content_len() % 4 != 0 {
return Err(crate::Error::new(
ErrorKind::InvalidAtomSize,
"Chapter reference (chap) atom size is not a multiple of 4",
));
}

let num_entries = size.content_len() / ENTRY_SIZE;
let mut chapter_ids = Vec::with_capacity(num_entries as usize);

for _ in 0..num_entries {
chapter_ids.push(reader.read_be_u32()?);
}
Expand Down
37 changes: 19 additions & 18 deletions src/atom/chpl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use super::*;

pub const DEFAULT_TIMESCALE: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(10_000_000) };

pub const HEADER_SIZE: u64 = 5;
pub const HEADER_SIZE_V0: u64 = 5;
pub const HEADER_SIZE_V1: u64 = 9;
pub const ITEM_HEADER_SIZE: u64 = 9;

#[derive(Clone, Debug, Default, PartialEq, Eq)]
Expand Down Expand Up @@ -41,34 +42,34 @@ impl ParseAtom for Chpl<'_> {
) -> crate::Result<Self> {
let bounds = find_bounds(reader, size)?;
let (version, _) = head::parse_full(reader)?;
let mut parsed_bytes = HEADER_SIZE;

match version {
0 => (),
let header_size = match version {
0 => HEADER_SIZE_V0,
1 => {
reader.skip(4)?; // ???
parsed_bytes += 4;
HEADER_SIZE_V1
}
_ => {
return Err(crate::Error::new(
crate::ErrorKind::UnknownVersion(version),
"Unknown chapter list (chpl) version",
));
return unknown_version("chapter list (chpl)", version);
}
}
};

expect_min_size("Chapter list (chpl)", size, header_size)?;

let num_entries = reader.read_u8()?;
let table_size = size.content_len() - header_size;
let mut buf = vec![0; table_size as usize];
reader.read_exact(&mut buf)?;

let mut cursor = std::io::Cursor::new(buf);

let mut chpl = Vec::with_capacity(num_entries as usize);
while parsed_bytes < size.content_len() {
let start = reader.read_be_u64()?;
for _ in 0..num_entries {
let start = cursor.read_be_u64()?;

let str_len = reader.read_u8()?;
let title = reader.read_utf8(str_len as u64)?;
let str_len = cursor.read_u8()?;
let title = cursor.read_utf8(str_len as u64)?;

chpl.push(ChplItem { start, title });

parsed_bytes += ITEM_HEADER_SIZE + str_len as u64;
}

Ok(Self {
Expand All @@ -88,7 +89,7 @@ impl AtomSize for Chpl<'_> {
v.iter().map(|c| ITEM_HEADER_SIZE + title_len(&c.title) as u64).sum::<u64>()
}
};
let content_len = HEADER_SIZE + data_len;
let content_len = HEADER_SIZE_V0 + data_len;
Size::from(content_len)
}
}
Expand Down
12 changes: 2 additions & 10 deletions src/atom/co64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,12 @@ impl ParseAtom for Co64 {
let (version, _) = head::parse_full(reader)?;

if version != 0 {
return Err(crate::Error::new(
crate::ErrorKind::UnknownVersion(version),
"Unknown 64bit sample table chunk offset (co64) version",
));
return unknown_version("64bit sample table chunk offset (co64)", version);
}

let num_entries = reader.read_be_u32()?;
let table_size = ENTRY_SIZE * num_entries as u64;
if HEADER_SIZE + table_size != size.content_len() {
return Err(crate::Error::new(
crate::ErrorKind::SizeMismatch,
"Sample table chunk offset 64 (co64) offset table size doesn't match atom length",
));
}
expect_size("Sample table chunk offset 64 (co64)", size, HEADER_SIZE + table_size)?;

let offsets = if cfg.write {
let offsets = Table::read_items(reader, num_entries)?;
Expand Down
2 changes: 2 additions & 0 deletions src/atom/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ impl Data {
}
let datatype = u32::from_be_bytes([0, b2, b1, b0]);

expect_min_size("Data (data)", size, HEADER_SIZE)?;

let len = size.content_len() - HEADER_SIZE;
Ok(match datatype {
RESERVED => Data::Reserved(reader.read_u8_vec(len)?),
Expand Down
3 changes: 2 additions & 1 deletion src/atom/dinf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ impl ParseAtom for Dinf {
let mut parsed_bytes = 0;

while parsed_bytes < size.content_len() {
let head = head::parse(reader)?;
let remaining_bytes = size.content_len() - parsed_bytes;
let head = head::parse(reader, remaining_bytes)?;

match head.fourcc() {
DATA_REFERENCE => dinf.dref = Some(Dref::parse(reader, cfg, head.size())?),
Expand Down
16 changes: 8 additions & 8 deletions src/atom/dref.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use super::*;

pub const HEADER_SIZE: u64 = 8;

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Dref {
pub state: State,
Expand All @@ -20,22 +22,20 @@ impl ParseAtom for Dref {
let (version, _) = head::parse_full(reader)?;

if version != 0 {
return Err(crate::Error::new(
ErrorKind::UnknownVersion(version),
"Unknown data reference (dref) atom version",
));
return unknown_version("data reference (dref)", version);
}

reader.skip(4)?; // number of entries

expect_min_size("Data ", size, HEADER_SIZE)?;

let mut dref = Self {
state: State::Existing(bounds),
..Default::default()
};
let mut parsed_bytes = 8;

let mut parsed_bytes = HEADER_SIZE;
while parsed_bytes < size.content_len() {
let head = head::parse(reader)?;
let remaining_bytes = size.content_len() - parsed_bytes;
let head = head::parse(reader, remaining_bytes)?;

match head.fourcc() {
URL_MEDIA => dref.url = Some(Url::parse(reader, cfg, head.size())?),
Expand Down
13 changes: 8 additions & 5 deletions src/atom/ftyp.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
use super::*;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Ftyp(pub String);
pub struct Ftyp {
pub size: Size,
pub string: String,
}

impl Ftyp {
pub fn parse(reader: &mut (impl Read + Seek)) -> crate::Result<Self> {
let head = head::parse(reader)?;
pub fn parse(reader: &mut (impl Read + Seek), file_len: u64) -> crate::Result<Self> {
let head = head::parse(reader, file_len)?;
if head.fourcc() != FILETYPE {
return Err(crate::Error::new(ErrorKind::NoFtyp, "No filetype atom found."));
}

let ftyp = reader.read_utf8(head.content_len())?;
let string = reader.read_utf8(head.content_len())?;

Ok(Ftyp(ftyp))
Ok(Ftyp { size: head.size(), string })
}
}
3 changes: 2 additions & 1 deletion src/atom/gmhd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ impl ParseAtom for Gmhd {
let mut parsed_bytes = 0;

while parsed_bytes < size.content_len() {
let head = head::parse(reader)?;
let remaining_bytes = size.content_len() - parsed_bytes;
let head = head::parse(reader, remaining_bytes)?;

match head.fourcc() {
BASE_MEDIA_INFORMATION => gmhd.gmin = Some(Gmin::parse(reader, cfg, head.size())?),
Expand Down
5 changes: 1 addition & 4 deletions src/atom/gmin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ impl ParseAtom for Gmin {
gmin.version = version;
gmin.flags = flags;
if version != 0 {
return Err(crate::Error::new(
crate::ErrorKind::UnknownVersion(version),
format!("Unknown base media information (gmin) version {version}"),
));
return unknown_version("base media information (gmin)", version);
}

gmin.graphics_mode = reader.read_be_u16()?;
Expand Down
45 changes: 31 additions & 14 deletions src/atom/head.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl Head {
/// 4 bytes identifier
/// 8 bytes optional extended length
/// ```
pub fn parse(reader: &mut impl Read) -> crate::Result<Head> {
pub fn parse(reader: &mut impl Read, remaining_bytes: u64) -> crate::Result<Head> {
let mut buf = [[0u8; 4]; 2];

// SAFETY: the buffer has the same size and alignment
Expand All @@ -110,30 +110,47 @@ pub fn parse(reader: &mut impl Read) -> crate::Result<Head> {
.read_exact(byte_buf)
.map_err(|e| crate::Error::new(ErrorKind::Io(e), "Error reading atom head"))?;

let len = u32::from_be_bytes(buf[0]) as u64;
let mut len = u32::from_be_bytes(buf[0]) as u64;
let fourcc = Fourcc(buf[1]);

if len == 1 {
let ext = if len == 1 {
match reader.read_be_u64() {
Ok(ext_len) if ext_len < 16 => Err(crate::Error::new(
crate::ErrorKind::InvalidAtomSize,
format!(
"Read extended length of '{fourcc}' which is less than 16 bytes: {ext_len}"
),
)),
Ok(ext_len) => Ok(Head::new(true, ext_len, fourcc)),
Ok(ext_len) if ext_len < 16 => {
return Err(crate::Error::new(
crate::ErrorKind::InvalidAtomSize,
format!(
"Read extended length of '{fourcc}' which is less than 16 bytes: {ext_len}"
),
));
}
Ok(ext_len) => len = ext_len,
Err(e) => {
Err(crate::Error::new(ErrorKind::Io(e), "Error reading extended atom length"))
return Err(crate::Error::new(
ErrorKind::Io(e),
"Error reading extended atom length",
));
}
}
true
} else if len < 8 {
Err(crate::Error::new(
return Err(crate::Error::new(
crate::ErrorKind::InvalidAtomSize,
format!("Read length of '{fourcc}' which is less than 8 bytes: {len}"),
))
));
} else {
Ok(Head::new(false, len, fourcc))
false
};

if len > remaining_bytes {
return Err(crate::Error::new(
ErrorKind::AtomSizeOutOfBounds,
format!(
"Atom size {len} of {fourcc} out larger than the remaining number of bytes {remaining_bytes}"
),
));
}

Ok(Head::new(ext, len, fourcc))
}

pub fn write(writer: &mut impl Write, head: Head) -> crate::Result<()> {
Expand Down
2 changes: 1 addition & 1 deletion src/atom/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub(crate) const TRACK_HEADER: Fourcc = Fourcc(*b"tkhd");
/// (`tref`)
pub(crate) const TRACK_REFERENCE: Fourcc = Fourcc(*b"tref");
/// (`chap`)
pub(crate) const CHAPTER: Fourcc = Fourcc(*b"chap");
pub(crate) const CHAPTER_REFERENCE: Fourcc = Fourcc(*b"chap");
/// (`mdia`) Identifier of an atom containing information about a tracks media type and data.
pub(crate) const MEDIA: Fourcc = Fourcc(*b"mdia");
/// (`mdhd`)
Expand Down
3 changes: 2 additions & 1 deletion src/atom/ilst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ impl ParseAtom for Ilst<'_> {
let mut parsed_bytes = 0;

while parsed_bytes < size.content_len() {
let head = head::parse(reader)?;
let remaining_bytes = size.content_len() - parsed_bytes;
let head = head::parse(reader, remaining_bytes)?;

match head.fourcc() {
FREE => reader.skip(head.content_len() as i64)?,
Expand Down
13 changes: 7 additions & 6 deletions src/atom/mdhd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl ParseAtom for Mdhd {
fn parse_atom(
reader: &mut (impl Read + Seek),
_cfg: &ParseConfig<'_>,
_size: Size,
size: Size,
) -> crate::Result<Self> {
let mut mdhd = Self::default();

Expand All @@ -72,22 +72,23 @@ impl ParseAtom for Mdhd {

match version {
0 => {
expect_size("Media header (mdhd) version 0", size, HEADER_SIZE_V0 as u64)?;

let mut buf = MdhdBufV0::default();
reader.read_exact(buf.bytes_mut())?;
mdhd.timescale = u32::from_be_bytes(buf.timescale);
mdhd.duration = u32::from_be_bytes(buf.duration) as u64;
}
1 => {
expect_size("Media header (mdhd) version 1", size, HEADER_SIZE_V1 as u64)?;

let mut buf = MdhdBufV1::default();
reader.read_exact(buf.bytes_mut())?;
mdhd.timescale = u32::from_be_bytes(buf.timescale);
mdhd.duration = u64::from_be_bytes(buf.duration);
}
v => {
return Err(crate::Error::new(
crate::ErrorKind::UnknownVersion(version),
format!("Unknown media header (mdhd) version {v}"),
));
_ => {
return unknown_version("media header (mdhd)", version);
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/atom/mdia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ impl ParseAtom for Mdia {
let mut minf = None;

while parsed_bytes < size.content_len() {
let head = head::parse(reader)?;
let remaining_bytes = size.content_len() - parsed_bytes;
let head = head::parse(reader, remaining_bytes)?;

match head.fourcc() {
MEDIA_HEADER => mdhd = Some(Mdhd::parse(reader, cfg, head.size())?),
Expand Down
Loading

0 comments on commit 474d044

Please sign in to comment.