Skip to content

Commit bbb4a12

Browse files
baumanjkinetiknz
authored andcommitted
Replace casts with infallible or error-checked conversions
Add to_u64 and to_usize trait functions for infallible conversions which can be checked at compile time to be platform safe. Additionally, convert some arithmetic to checked versions so we can error rather than panic or give invalid results on overflow. Remove some unnecessary trait bounds on struct definitions. Update skip() to avoid allocating a buffer.
1 parent b2f954a commit bbb4a12

File tree

2 files changed

+91
-55
lines changed

2 files changed

+91
-55
lines changed

mp4parse/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ bitreader = { version = "0.3.0" }
3030
num-traits = "0.2.0"
3131
mp4parse_fallible = { version = "0.0.1", optional = true }
3232
log = "0.4"
33+
static_assertions = "1.1.0"
3334

3435
[dev-dependencies]
3536
test-assembler = "0.1.2"

mp4parse/src/lib.rs

Lines changed: 90 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ extern crate bitreader;
1414
extern crate num_traits;
1515
use byteorder::{ReadBytesExt, WriteBytesExt};
1616
use bitreader::{BitReader, ReadInto};
17+
use std::convert::TryInto as _;
1718
use std::io::{Read, Take};
1819
use std::io::Cursor;
19-
use std::cmp;
2020
use num_traits::Num;
2121

2222
#[cfg(feature = "mp4parse_fallible")]
@@ -42,6 +42,46 @@ const BUF_SIZE_LIMIT: usize = 10 * 1024 * 1024;
4242
// frame per table entry in 30 fps.
4343
const TABLE_SIZE_LIMIT: u32 = 30 * 60 * 60 * 24 * 7;
4444

45+
/// A trait to indicate a type can be infallibly converted to `u64`.
46+
/// This should only be implemented for infallible conversions, so only unsigned types are valid.
47+
trait ToU64 {
48+
fn to_u64(self) -> u64;
49+
}
50+
51+
/// Statically verify that the platform `usize` can fit within a `u64`.
52+
/// If the size won't fit on the given platform, this will fail at compile time, but if a type
53+
/// which can fail TryInto<usize> is used, it may panic.
54+
impl ToU64 for usize {
55+
fn to_u64(self) -> u64 {
56+
static_assertions::const_assert!(std::mem::size_of::<usize>() <= std::mem::size_of::<u64>());
57+
self.try_into().expect("usize -> u64 conversion failed")
58+
}
59+
}
60+
61+
/// A trait to indicate a type can be infallibly converted to `usize`.
62+
/// This should only be implemented for infallible conversions, so only unsigned types are valid.
63+
trait ToUsize {
64+
fn to_usize(self) -> usize;
65+
}
66+
67+
/// Statically verify that the given type can fit within a `usize`.
68+
/// If the size won't fit on the given platform, this will fail at compile time, but if a type
69+
/// which can fail TryInto<usize> is used, it may panic.
70+
macro_rules! impl_to_usize_from {
71+
( $from_type:ty ) => {
72+
impl ToUsize for $from_type {
73+
fn to_usize(self) -> usize {
74+
static_assertions::const_assert!(std::mem::size_of::<$from_type>() <= std::mem::size_of::<usize>());
75+
self.try_into().expect(concat!(stringify!($from_type), " -> usize conversion failed"))
76+
}
77+
}
78+
}
79+
}
80+
81+
impl_to_usize_from!(u8);
82+
impl_to_usize_from!(u16);
83+
impl_to_usize_from!(u32);
84+
4585
// TODO: vec_push() needs to be replaced when Rust supports fallible memory
4686
// allocation in raw_vec.
4787
#[allow(unreachable_code)]
@@ -109,6 +149,12 @@ impl From<std::string::FromUtf8Error> for Error {
109149
}
110150
}
111151

152+
impl From<std::num::TryFromIntError> for Error {
153+
fn from(_: std::num::TryFromIntError) -> Error {
154+
Error::Unsupported("integer conversion failed")
155+
}
156+
}
157+
112158
impl From<()> for Error {
113159
fn from(_: ()) -> Error {
114160
Error::OutOfMemory
@@ -728,12 +774,12 @@ impl Track {
728774
}
729775
}
730776

731-
struct BMFFBox<'a, T: 'a + Read> {
777+
struct BMFFBox<'a, T: 'a> {
732778
head: BoxHeader,
733779
content: Take<&'a mut T>,
734780
}
735781

736-
struct BoxIter<'a, T: 'a + Read> {
782+
struct BoxIter<'a, T: 'a> {
737783
src: &'a mut T,
738784
}
739785

@@ -762,8 +808,8 @@ impl<'a, T: Read> Read for BMFFBox<'a, T> {
762808
}
763809

764810
impl<'a, T: Read> BMFFBox<'a, T> {
765-
fn bytes_left(&self) -> usize {
766-
self.content.limit() as usize
811+
fn bytes_left(&self) -> u64 {
812+
self.content.limit()
767813
}
768814

769815
fn get_header(&self) -> &BoxHeader {
@@ -775,7 +821,7 @@ impl<'a, T: Read> BMFFBox<'a, T> {
775821
}
776822
}
777823

778-
impl<'a, T: Read> Drop for BMFFBox<'a, T> {
824+
impl<'a, T> Drop for BMFFBox<'a, T> {
779825
fn drop(&mut self) {
780826
if self.content.limit() > 0 {
781827
let name: FourCC = From::from(self.head.name);
@@ -814,7 +860,7 @@ fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
814860
if size >= offset + 16 {
815861
let mut buffer = [0u8; 16];
816862
let count = src.read(&mut buffer)?;
817-
offset += count as u64;
863+
offset += count.to_u64();
818864
if count == 16 {
819865
Some(buffer)
820866
} else {
@@ -853,7 +899,7 @@ fn skip_box_content<T: Read>(src: &mut BMFFBox<T>) -> Result<()> {
853899
let to_skip = {
854900
let header = src.get_header();
855901
debug!("{:?} (skipped)", header);
856-
(header.size - header.offset) as usize
902+
header.size.checked_sub(header.offset).expect("header offset > size")
857903
};
858904
assert_eq!(to_skip, src.bytes_left());
859905
skip(src, to_skip)
@@ -974,7 +1020,7 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: &mut MediaContext) -> Result<
9741020
}
9751021

9761022
fn read_pssh<T: Read>(src: &mut BMFFBox<T>) -> Result<ProtectionSystemSpecificHeaderBox> {
977-
let len = src.bytes_left();
1023+
let len = src.bytes_left().try_into()?;
9781024
let mut box_content = read_buf(src, len)?;
9791025
let (system_id, kid, data) = {
9801026
let pssh = &mut Cursor::new(box_content.as_slice());
@@ -992,14 +1038,14 @@ fn read_pssh<T: Read>(src: &mut BMFFBox<T>) -> Result<ProtectionSystemSpecificHe
9921038
}
9931039
}
9941040

995-
let data_size = be_u32_with_limit(pssh)? as usize;
1041+
let data_size = be_u32_with_limit(pssh)?.to_usize();
9961042
let data = read_buf(pssh, data_size)?;
9971043

9981044
(system_id, kid, data)
9991045
};
10001046

10011047
let mut pssh_box = Vec::new();
1002-
write_be_u32(&mut pssh_box, src.head.size as u32)?;
1048+
write_be_u32(&mut pssh_box, src.head.size.try_into()?)?;
10031049
pssh_box.extend_from_slice(b"pssh");
10041050
pssh_box.append(&mut box_content);
10051051

@@ -1456,9 +1502,9 @@ fn read_stsc<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleToChunkBox> {
14561502
fn read_ctts<T: Read>(src: &mut BMFFBox<T>) -> Result<CompositionOffsetBox> {
14571503
let (version, _) = read_fullbox_extra(src)?;
14581504

1459-
let counts = be_u32_with_limit(src)?;
1505+
let counts = u64::from(be_u32_with_limit(src)?);
14601506

1461-
if src.bytes_left() < (counts as usize * 8) {
1507+
if src.bytes_left() < counts.checked_mul(8).expect("counts -> bytes overflow") {
14621508
return Err(Error::InvalidData("insufficient data in 'ctts' box"));
14631509
}
14641510

@@ -1588,7 +1634,7 @@ fn read_vpcc<T: Read>(src: &mut BMFFBox<T>) -> Result<VPxConfigBox> {
15881634
};
15891635

15901636
let codec_init_size = be_u16(src)?;
1591-
let codec_init = read_buf(src, codec_init_size as usize)?;
1637+
let codec_init = read_buf(src, codec_init_size.to_usize())?;
15921638

15931639
// TODO(rillian): validate field value ranges.
15941640
Ok(VPxConfigBox {
@@ -1636,7 +1682,7 @@ fn read_av1c<T: Read>(src: &mut BMFFBox<T>) -> Result<AV1ConfigBox> {
16361682
};
16371683

16381684
let config_obus_size = src.bytes_left();
1639-
let config_obus = read_buf(src, config_obus_size as usize)?;
1685+
let config_obus = read_buf(src, config_obus_size.try_into()?)?;
16401686

16411687
Ok(AV1ConfigBox {
16421688
profile,
@@ -1657,11 +1703,11 @@ fn read_flac_metadata<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACMetadataBlock
16571703
let temp = src.read_u8()?;
16581704
let block_type = temp & 0x7f;
16591705
let length = be_u24(src)?;
1660-
if length as usize > src.bytes_left() {
1706+
if u64::from(length) > src.bytes_left() {
16611707
return Err(Error::InvalidData(
16621708
"FLACMetadataBlock larger than parent box"));
16631709
}
1664-
let data = read_buf(src, length as usize)?;
1710+
let data = read_buf(src, length.to_usize())?;
16651711
Ok(FLACMetadataBlock {
16661712
block_type,
16671713
data,
@@ -1684,7 +1730,7 @@ fn find_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
16841730
let mut end: u32 = 0; // It's u8 without declaration type that is incorrect.
16851731
// MSB of extend_or_len indicates more bytes, up to 4 bytes.
16861732
for _ in 0..4 {
1687-
if (des.position() as usize) == remains.len() {
1733+
if des.position() == remains.len().to_u64() {
16881734
// There's nothing more to read, the 0x80 was actually part of
16891735
// the content, and not an extension size.
16901736
end = des.position() as u32;
@@ -1698,11 +1744,11 @@ fn find_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
16981744
}
16991745
};
17001746

1701-
if (end as usize) > remains.len() || u64::from(end) < des.position() {
1747+
if end.to_usize() > remains.len() || u64::from(end) < des.position() {
17021748
return Err(Error::InvalidData("Invalid descriptor."));
17031749
}
17041750

1705-
let descriptor = &remains[des.position() as usize .. end as usize];
1751+
let descriptor = &remains[des.position().try_into()? .. end.to_usize()];
17061752

17071753
match tag {
17081754
ESDESCR_TAG => {
@@ -1719,7 +1765,7 @@ fn find_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
17191765
},
17201766
}
17211767

1722-
remains = &remains[end as usize .. remains.len()];
1768+
remains = &remains[end.to_usize() .. remains.len()];
17231769
}
17241770

17251771
Ok(())
@@ -1897,8 +1943,8 @@ fn read_dc_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
18971943
// Skip uninteresting fields.
18981944
skip(des, 12)?;
18991945

1900-
if data.len() > des.position() as usize {
1901-
find_descriptor(&data[des.position() as usize .. data.len()], esds)?;
1946+
if data.len().to_u64() > des.position() {
1947+
find_descriptor(&data[des.position().try_into()? .. data.len()], esds)?;
19021948
}
19031949

19041950
esds.audio_codec = match object_profile {
@@ -1926,12 +1972,12 @@ fn read_es_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
19261972
// Url flag, second bit from left most.
19271973
if esds_flags & 0x40 > 0 {
19281974
// Skip uninteresting fields.
1929-
let skip_es_len: usize = des.read_u8()? as usize + 2;
1975+
let skip_es_len = u64::from(des.read_u8()?) + 2;
19301976
skip(des, skip_es_len)?;
19311977
}
19321978

1933-
if data.len() > des.position() as usize {
1934-
find_descriptor(&data[des.position() as usize .. data.len()], esds)?;
1979+
if data.len().to_u64() > des.position() {
1980+
find_descriptor(&data[des.position().try_into()? .. data.len()], esds)?;
19351981
}
19361982

19371983
Ok(())
@@ -1942,8 +1988,8 @@ fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
19421988

19431989
// Subtract 4 extra to offset the members of fullbox not accounted for in
19441990
// head.offset
1945-
let esds_size = src.head.size - src.head.offset - 4;
1946-
let esds_array = read_buf(src, esds_size as usize)?;
1991+
let esds_size = src.head.size.checked_sub(src.head.offset + 4).expect("offset invalid");
1992+
let esds_array = read_buf(src, esds_size.try_into()?)?;
19471993

19481994
let mut es_data = ES_Descriptor::default();
19491995
find_descriptor(&esds_array, &mut es_data)?;
@@ -2002,7 +2048,7 @@ fn read_dops<T: Read>(src: &mut BMFFBox<T>) -> Result<OpusSpecificBox> {
20022048
} else {
20032049
let stream_count = src.read_u8()?;
20042050
let coupled_count = src.read_u8()?;
2005-
let channel_mapping = read_buf(src, output_channel_count as usize)?;
2051+
let channel_mapping = read_buf(src, output_channel_count.to_usize())?;
20062052

20072053
Some(ChannelMappingTable {
20082054
stream_count,
@@ -2076,10 +2122,10 @@ fn read_alac<T: Read>(src: &mut BMFFBox<T>) -> Result<ALACSpecificBox> {
20762122
return Err(Error::InvalidData("no-zero alac (ALAC) flags"));
20772123
}
20782124

2079-
let length = src.bytes_left();
2080-
if length != 24 && length != 48 {
2081-
return Err(Error::InvalidData("ALACSpecificBox magic cookie is the wrong size"));
2082-
}
2125+
let length = match src.bytes_left() {
2126+
x @ 24 | x @ 48 => x.try_into().expect("infallible conversion to usize"),
2127+
_ => return Err(Error::InvalidData("ALACSpecificBox magic cookie is the wrong size")),
2128+
};
20832129
let data = read_buf(src, length)?;
20842130

20852131
Ok(ALACSpecificBox {
@@ -2151,8 +2197,8 @@ fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleEntry>
21512197
codec_specific.is_some() {
21522198
return Err(Error::InvalidData("malformed video sample entry"));
21532199
}
2154-
let avcc_size = b.head.size - b.head.offset;
2155-
let avcc = read_buf(&mut b.content, avcc_size as usize)?;
2200+
let avcc_size = b.head.size.checked_sub(b.head.offset).expect("offset invalid");
2201+
let avcc = read_buf(&mut b.content, avcc_size.try_into()?)?;
21562202
debug!("{:?} (avcc)", avcc);
21572203
// TODO(kinetik): Parse avcC box? For now we just stash the data.
21582204
codec_specific = Some(VideoCodecSpecific::AVCConfig(avcc));
@@ -2181,8 +2227,8 @@ fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleEntry>
21812227
let (_, _) = read_fullbox_extra(&mut b.content)?;
21822228
// Subtract 4 extra to offset the members of fullbox not
21832229
// accounted for in head.offset
2184-
let esds_size = b.head.size - b.head.offset - 4;
2185-
let esds = read_buf(&mut b.content, esds_size as usize)?;
2230+
let esds_size = b.head.size.checked_sub(b.head.offset + 4).expect("offset invalid");
2231+
let esds = read_buf(&mut b.content, esds_size.try_into()?)?;
21862232
codec_specific = Some(VideoCodecSpecific::ESDSConfig(esds));
21872233
}
21882234
BoxType::ProtectionSchemeInformationBox => {
@@ -2385,7 +2431,7 @@ fn read_stsd<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleD
23852431
};
23862432
vec_push(&mut descriptions, description)?;
23872433
check_parser_state!(b.content);
2388-
if descriptions.len() == description_count as usize {
2434+
if descriptions.len() == description_count.to_usize() {
23892435
break;
23902436
}
23912437
}
@@ -2467,7 +2513,7 @@ fn read_tenc<T: Read>(src: &mut BMFFBox<T>) -> Result<TrackEncryptionBox> {
24672513
let default_constant_iv = match (default_is_encrypted, default_iv_size) {
24682514
(1, 0) => {
24692515
let default_constant_iv_size = src.read_u8()?;
2470-
Some(read_buf(src, default_constant_iv_size as usize)?)
2516+
Some(read_buf(src, default_constant_iv_size.to_usize())?)
24712517
},
24722518
_ => None,
24732519
};
@@ -2661,23 +2707,14 @@ fn read_ilst_multiple_u8_data<T: Read>(src: &mut BMFFBox<T>) -> Result<Vec<Vec<u
26612707

26622708
fn read_ilst_data<T: Read>(src: &mut BMFFBox<T>) -> Result<Vec<u8>> {
26632709
// Skip past the padding bytes
2664-
skip(&mut src.content, src.head.offset as usize)?;
2665-
let size = src.content.limit() as usize;
2710+
skip(&mut src.content, src.head.offset)?;
2711+
let size = src.content.limit().try_into()?;
26662712
read_buf(&mut src.content, size)
26672713
}
26682714

26692715
/// Skip a number of bytes that we don't care to parse.
2670-
fn skip<T: Read>(src: &mut T, mut bytes: usize) -> Result<()> {
2671-
const BUF_SIZE: usize = 64 * 1024;
2672-
let mut buf = vec![0; BUF_SIZE];
2673-
while bytes > 0 {
2674-
let buf_size = cmp::min(bytes, BUF_SIZE);
2675-
let len = src.take(buf_size as u64).read(&mut buf)?;
2676-
if len == 0 {
2677-
return Err(Error::UnexpectedEOF);
2678-
}
2679-
bytes -= len;
2680-
}
2716+
fn skip<T: Read>(src: &mut T, bytes: u64) -> Result<()> {
2717+
std::io::copy(&mut src.take(bytes), &mut std::io::sink())?;
26812718
Ok(())
26822719
}
26832720

@@ -2714,9 +2751,7 @@ fn be_u16<T: ReadBytesExt>(src: &mut T) -> Result<u16> {
27142751
}
27152752

27162753
fn be_u24<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
2717-
src.read_uint::<byteorder::BigEndian>(3)
2718-
.map(|v| v as u32)
2719-
.map_err(From::from)
2754+
src.read_u24::<byteorder::BigEndian>().map_err(From::from)
27202755
}
27212756

27222757
fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> {

0 commit comments

Comments
 (0)