Skip to content

Commit aaf14d5

Browse files
committed
Check ipma validity and preallocate associations vec
Resolves https://oss-fuzz.com/testcase-detail/5139142796902400
1 parent 61512dd commit aaf14d5

File tree

1 file changed

+38
-3
lines changed

1 file changed

+38
-3
lines changed

mp4parse/src/lib.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,13 +1629,49 @@ pub struct AssociatedProperty {
16291629
pub property: ItemProperty,
16301630
}
16311631

1632+
const MAX_IPMA_ASSOCIATION_COUNT: u64 = u8::MAX as u64;
1633+
1634+
/// Parse an ItemPropertyAssociation box
1635+
/// See HEIF (ISO 23008-12:2017) § 9.3.1
16321636
fn read_ipma<T: Read>(
16331637
src: &mut BMFFBox<T>,
16341638
(version, flags): (u8, u32),
16351639
) -> Result<TryVec<Association>> {
1636-
let mut associations = TryVec::new();
1640+
// Static analysis shows that none of this unchecked arithmetic can fail
1641+
let entry_count: u64 = be_u32(src)?.into();
1642+
let min_entry_bytes: u64 = 1 /* association_count */ + if version == 0 { 2 } else { 4 };
1643+
let total_non_association_bytes = entry_count * min_entry_bytes;
1644+
let total_association_bytes;
1645+
1646+
if let Some(difference) = src.bytes_left().checked_sub(total_non_association_bytes) {
1647+
// All the storage for the `essential` and `property_index` parts (assuming a valid ipma box size)
1648+
total_association_bytes = difference;
1649+
} else {
1650+
return Err(Error::InvalidData(
1651+
"ipma box below minimum size for entry_count",
1652+
));
1653+
}
1654+
1655+
let num_association_bytes = if flags & 1 == 1 { 2 } else { 1 };
1656+
1657+
// total_association_bytes must be a multiple of num_association_bytes
1658+
if total_association_bytes % num_association_bytes != 0 {
1659+
return Err(Error::InvalidData("ipma box has invalid size"));
1660+
}
1661+
1662+
let max_association_bytes_per_entry = MAX_IPMA_ASSOCIATION_COUNT * num_association_bytes;
1663+
let max_total_association_bytes = entry_count * max_association_bytes_per_entry;
1664+
let max_bytes_left = total_non_association_bytes + max_total_association_bytes;
1665+
1666+
if src.bytes_left() > max_bytes_left {
1667+
return Err(Error::InvalidData(
1668+
"ipma box exceeds maximum size for entry_count",
1669+
));
1670+
}
1671+
1672+
let total_associations = total_association_bytes / num_association_bytes;
1673+
let mut associations = TryVec::with_capacity(total_associations.try_into()?)?;
16371674

1638-
let entry_count = be_u32(src)?;
16391675
for _ in 0..entry_count {
16401676
let item_id = if version == 0 {
16411677
be_u16(src)?.into()
@@ -1644,7 +1680,6 @@ fn read_ipma<T: Read>(
16441680
};
16451681
let association_count = src.read_u8()?;
16461682
for _ in 0..association_count {
1647-
let num_association_bytes = if flags & 1 == 1 { 2 } else { 1 };
16481683
let association = src.take(num_association_bytes).read_into_try_vec()?;
16491684
let mut association = BitReader::new(association.as_slice());
16501685
let essential = association.read_bool()?;

0 commit comments

Comments
 (0)