Skip to content

Commit 2b814cd

Browse files
uklotzdeSerial-ATA
authored andcommitted
mp4: Skip unexpected or empty "data" ilst atoms
1 parent be0f24b commit 2b814cd

File tree

4 files changed

+95
-36
lines changed

4 files changed

+95
-36
lines changed

src/mp4/ilst/mod.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ mod tests {
782782
let cursor = Cursor::new(tag);
783783
let mut reader = AtomReader::new(cursor).unwrap();
784784

785-
super::read::parse_ilst(&mut reader, len as u64).unwrap()
785+
super::read::parse_ilst(&mut reader, crate::ParsingMode::Strict, len as u64).unwrap()
786786
}
787787

788788
fn verify_atom(ilst: &Ilst, ident: [u8; 4], data: &AtomData) {
@@ -850,7 +850,8 @@ mod tests {
850850
let cursor = Cursor::new(tag);
851851
let mut reader = AtomReader::new(cursor).unwrap();
852852

853-
let parsed_tag = super::read::parse_ilst(&mut reader, len as u64).unwrap();
853+
let parsed_tag =
854+
super::read::parse_ilst(&mut reader, crate::ParsingMode::Strict, len as u64).unwrap();
854855

855856
assert_eq!(expected_tag, parsed_tag);
856857
}
@@ -866,8 +867,12 @@ mod tests {
866867
let mut reader = AtomReader::new(cursor).unwrap();
867868

868869
// Remove the ilst identifier and size
869-
let temp_parsed_tag =
870-
super::read::parse_ilst(&mut reader, (writer.len() - 8) as u64).unwrap();
870+
let temp_parsed_tag = super::read::parse_ilst(
871+
&mut reader,
872+
crate::ParsingMode::Strict,
873+
(writer.len() - 8) as u64,
874+
)
875+
.unwrap();
871876

872877
assert_eq!(parsed_tag, temp_parsed_tag);
873878
}
@@ -880,7 +885,8 @@ mod tests {
880885
let cursor = Cursor::new(tag);
881886
let mut reader = AtomReader::new(cursor).unwrap();
882887

883-
let ilst = super::read::parse_ilst(&mut reader, len as u64).unwrap();
888+
let ilst =
889+
super::read::parse_ilst(&mut reader, crate::ParsingMode::Strict, len as u64).unwrap();
884890

885891
let tag: Tag = ilst.into();
886892

@@ -999,7 +1005,12 @@ mod tests {
9991005
let cursor = Cursor::new(ilst_bytes);
10001006
let mut reader = AtomReader::new(cursor).unwrap();
10011007

1002-
ilst = super::read::parse_ilst(&mut reader, ilst_bytes.len() as u64).unwrap();
1008+
ilst = super::read::parse_ilst(
1009+
&mut reader,
1010+
crate::ParsingMode::Strict,
1011+
ilst_bytes.len() as u64,
1012+
)
1013+
.unwrap();
10031014
}
10041015

10051016
let mut file = tempfile::tempfile().unwrap();

src/mp4/ilst/read.rs

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@ use crate::mp4::ilst::atom::AtomDataStorage;
1010
use crate::mp4::read::{skip_unneeded, AtomReader};
1111
use crate::picture::{MimeType, Picture, PictureType};
1212
use crate::util::text::utf16_decode;
13+
use crate::ParsingMode;
1314

1415
use std::borrow::Cow;
1516
use std::io::{Cursor, Read, Seek, SeekFrom};
1617

17-
pub(in crate::mp4) fn parse_ilst<R>(reader: &mut AtomReader<R>, len: u64) -> Result<Ilst>
18+
pub(in crate::mp4) fn parse_ilst<R>(
19+
reader: &mut AtomReader<R>,
20+
parsing_mode: ParsingMode,
21+
len: u64,
22+
) -> Result<Ilst>
1823
where
1924
R: Read + Seek,
2025
{
@@ -35,12 +40,14 @@ where
3540
continue;
3641
},
3742
b"covr" => {
38-
handle_covr(&mut ilst_reader, &mut tag, &atom)?;
43+
handle_covr(&mut ilst_reader, parsing_mode, &mut tag, &atom)?;
3944
continue;
4045
},
4146
// Upgrade this to a \xa9gen atom
4247
b"gnre" => {
43-
if let Some(atom_data) = parse_data_inner(&mut ilst_reader, &atom)? {
48+
if let Some(atom_data) =
49+
parse_data_inner(&mut ilst_reader, parsing_mode, &atom)?
50+
{
4451
let mut data = Vec::new();
4552

4653
for (_, content) in atom_data {
@@ -70,7 +77,9 @@ where
7077
// Special case the "Album ID", as it has the code "BE signed integer" (21), but
7178
// must be interpreted as a "BE 64-bit Signed Integer" (74)
7279
b"plID" => {
73-
if let Some(atom_data) = parse_data_inner(&mut ilst_reader, &atom)? {
80+
if let Some(atom_data) =
81+
parse_data_inner(&mut ilst_reader, parsing_mode, &atom)?
82+
{
7483
let mut data = Vec::new();
7584

7685
for (code, content) in atom_data {
@@ -98,7 +107,9 @@ where
98107
continue;
99108
},
100109
b"cpil" | b"hdvd" | b"pcst" | b"pgap" | b"shwm" => {
101-
if let Some(atom_data) = parse_data_inner(&mut ilst_reader, &atom)? {
110+
if let Some(atom_data) =
111+
parse_data_inner(&mut ilst_reader, parsing_mode, &atom)?
112+
{
102113
if let Some((_, content)) = atom_data.first() {
103114
let data = match content[..] {
104115
[0, ..] => AtomData::Bool(false),
@@ -118,17 +129,22 @@ where
118129
}
119130
}
120131

121-
parse_data(&mut ilst_reader, &mut tag, atom)?;
132+
parse_data(&mut ilst_reader, parsing_mode, &mut tag, atom)?;
122133
}
123134

124135
Ok(tag)
125136
}
126137

127-
fn parse_data<R>(reader: &mut AtomReader<R>, tag: &mut Ilst, atom_info: AtomInfo) -> Result<()>
138+
fn parse_data<R>(
139+
reader: &mut AtomReader<R>,
140+
parsing_mode: ParsingMode,
141+
tag: &mut Ilst,
142+
atom_info: AtomInfo,
143+
) -> Result<()>
128144
where
129145
R: Read + Seek,
130146
{
131-
if let Some(mut atom_data) = parse_data_inner(reader, &atom_info)? {
147+
if let Some(mut atom_data) = parse_data_inner(reader, parsing_mode, &atom_info)? {
132148
// Most atoms we encounter are only going to have 1 value, so store them as such
133149
if atom_data.len() == 1 {
134150
let (flags, content) = atom_data.remove(0);
@@ -157,8 +173,11 @@ where
157173
Ok(())
158174
}
159175

176+
const DATA_ATOM_IDENT: AtomIdent<'static> = AtomIdent::Fourcc(*b"data");
177+
160178
fn parse_data_inner<R>(
161179
reader: &mut AtomReader<R>,
180+
parsing_mode: ParsingMode,
162181
atom_info: &AtomInfo,
163182
) -> Result<Option<Vec<(u32, Vec<u8>)>>>
164183
where
@@ -170,11 +189,7 @@ where
170189
let to_read = (atom_info.start + atom_info.len) - reader.stream_position()?;
171190
let mut pos = 0;
172191
while pos < to_read {
173-
let data_atom = reader.next()?;
174-
match data_atom.ident {
175-
AtomIdent::Fourcc(ref name) if name == b"data" => {},
176-
_ => err!(BadAtom("Expected atom \"data\" to follow name")),
177-
}
192+
let next_atom = reader.next()?;
178193

179194
// We don't care about the version
180195
let _version = reader.read_u8()?;
@@ -187,17 +202,32 @@ where
187202
// We don't care about the locale
188203
reader.seek(SeekFrom::Current(4))?;
189204

190-
let content_len = (data_atom.len - 16) as usize;
191-
if content_len == 0 {
192-
// We won't add empty atoms
193-
return Ok(None);
205+
match next_atom.ident {
206+
DATA_ATOM_IDENT => {
207+
debug_assert!(next_atom.len >= 16);
208+
let content_len = (next_atom.len - 16) as usize;
209+
if content_len > 0 {
210+
let mut content = try_vec![0; content_len];
211+
reader.read_exact(&mut content)?;
212+
ret.push((flags, content));
213+
} else {
214+
log::warn!("Skipping empty \"data\" atom");
215+
}
216+
},
217+
_ => match parsing_mode {
218+
ParsingMode::Strict => {
219+
err!(BadAtom("Expected atom \"data\" to follow name"))
220+
},
221+
ParsingMode::BestAttempt | ParsingMode::Relaxed => {
222+
log::warn!(
223+
"Skipping unexpected atom {actual_ident:?}, expected {expected_ident:?}",
224+
actual_ident = next_atom.ident,
225+
expected_ident = DATA_ATOM_IDENT
226+
)
227+
},
228+
},
194229
}
195-
196-
let mut content = try_vec![0; content_len];
197-
reader.read_exact(&mut content)?;
198-
199-
pos += data_atom.len;
200-
ret.push((flags, content));
230+
pos += next_atom.len;
201231
}
202232

203233
let ret = if ret.is_empty() { None } else { Some(ret) };
@@ -228,11 +258,16 @@ fn parse_int(bytes: &[u8]) -> Result<i32> {
228258
})
229259
}
230260

231-
fn handle_covr<R>(reader: &mut AtomReader<R>, tag: &mut Ilst, atom_info: &AtomInfo) -> Result<()>
261+
fn handle_covr<R>(
262+
reader: &mut AtomReader<R>,
263+
parsing_mode: ParsingMode,
264+
tag: &mut Ilst,
265+
atom_info: &AtomInfo,
266+
) -> Result<()>
232267
where
233268
R: Read + Seek,
234269
{
235-
if let Some(atom_data) = parse_data_inner(reader, atom_info)? {
270+
if let Some(atom_data) = parse_data_inner(reader, parsing_mode, atom_info)? {
236271
let mut data = Vec::new();
237272

238273
let len = atom_data.len();

src/mp4/moov.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use super::ilst::Ilst;
44
use super::read::{meta_is_full, nested_atom, skip_unneeded, AtomReader};
55
use crate::error::Result;
66
use crate::macros::decode_err;
7+
use crate::ParsingMode;
78

89
use std::io::{Read, Seek};
910

@@ -33,7 +34,11 @@ impl Moov {
3334
moov.ok_or_else(|| decode_err!(Mp4, "No \"moov\" atom found"))
3435
}
3536

36-
pub(super) fn parse<R>(reader: &mut AtomReader<R>, read_properties: bool) -> Result<Self>
37+
pub(super) fn parse<R>(
38+
reader: &mut AtomReader<R>,
39+
parsing_mode: ParsingMode,
40+
read_properties: bool,
41+
) -> Result<Self>
3742
where
3843
R: Read + Seek,
3944
{
@@ -51,7 +56,7 @@ impl Moov {
5156
}
5257
},
5358
b"udta" => {
54-
meta = meta_from_udta(reader, atom.len - 8)?;
59+
meta = meta_from_udta(reader, parsing_mode, atom.len - 8)?;
5560
},
5661
_ => skip_unneeded(reader, atom.extended, atom.len)?,
5762
}
@@ -66,7 +71,11 @@ impl Moov {
6671
}
6772
}
6873

69-
fn meta_from_udta<R>(reader: &mut AtomReader<R>, len: u64) -> Result<Option<Ilst>>
74+
fn meta_from_udta<R>(
75+
reader: &mut AtomReader<R>,
76+
parsing_mode: ParsingMode,
77+
len: u64,
78+
) -> Result<Option<Ilst>>
7079
where
7180
R: Read + Seek,
7281
{
@@ -118,7 +127,7 @@ where
118127
}
119128

120129
if found_ilst {
121-
return parse_ilst(reader, ilst_atom_size - 8).map(Some);
130+
return parse_ilst(reader, parsing_mode, ilst_atom_size - 8).map(Some);
122131
}
123132

124133
Ok(None)

src/mp4/read.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,11 @@ where
178178
let moov_info = Moov::find(&mut reader)?;
179179
reader.reset_bounds(moov_info.start + 8, moov_info.len - 8);
180180

181-
let moov = Moov::parse(&mut reader, parse_options.read_properties)?;
181+
let moov = Moov::parse(
182+
&mut reader,
183+
parse_options.parsing_mode,
184+
parse_options.read_properties,
185+
)?;
182186

183187
Ok(Mp4File {
184188
ftyp,

0 commit comments

Comments
 (0)