Skip to content

Commit 97a53c0

Browse files
uklotzdeSerial-ATA
authored andcommitted
id3v2: Fix skipping of frames
1 parent fb0838b commit 97a53c0

File tree

3 files changed

+64
-26
lines changed

3 files changed

+64
-26
lines changed

src/id3/v2/frame/header.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use std::io::Read;
99

1010
pub(crate) fn parse_v2_header<R>(
1111
reader: &mut R,
12-
) -> Result<Option<(FrameId<'static>, u32, FrameFlags)>>
12+
size: &mut u32,
13+
) -> Result<Option<(FrameId<'static>, FrameFlags)>>
1314
where
1415
R: Read,
1516
{
@@ -24,6 +25,8 @@ where
2425
return Ok(None);
2526
}
2627

28+
*size = u32::from_be_bytes([0, header[3], header[4], header[5]]);
29+
2730
let id_bytes = &header[..3];
2831
let id_str = std::str::from_utf8(id_bytes)
2932
.map_err(|_| Id3v2Error::new(Id3v2ErrorKind::BadFrameId(id_bytes.to_vec())))
@@ -32,16 +35,15 @@ where
3235
})?;
3336
let id = FrameId::new_cow(id_str)?;
3437

35-
let size = u32::from_be_bytes([0, header[3], header[4], header[5]]);
36-
3738
// V2 doesn't store flags
38-
Ok(Some((id, size, FrameFlags::default())))
39+
Ok(Some((id, FrameFlags::default())))
3940
}
4041

4142
pub(crate) fn parse_header<R>(
4243
reader: &mut R,
44+
size: &mut u32,
4345
synchsafe: bool,
44-
) -> Result<Option<(FrameId<'static>, u32, FrameFlags)>>
46+
) -> Result<Option<(FrameId<'static>, FrameFlags)>>
4547
where
4648
R: Read,
4749
{
@@ -56,6 +58,12 @@ where
5658
return Ok(None);
5759
}
5860

61+
*size = u32::from_be_bytes([header[4], header[5], header[6], header[7]]);
62+
// unsynch the frame size if necessary
63+
if synchsafe {
64+
*size = size.unsynch();
65+
}
66+
5967
// For some reason, some apps make v3 tags with v2 frame IDs.
6068
// The actual frame header is v3 though
6169
let mut id_end = 4;
@@ -69,8 +77,6 @@ where
6977
let id_str = std::str::from_utf8(id_bytes)
7078
.map_err(|_| Id3v2Error::new(Id3v2ErrorKind::BadFrameId(id_bytes.to_vec())))?;
7179

72-
let mut size = u32::from_be_bytes([header[4], header[5], header[6], header[7]]);
73-
7480
// Now upgrade the FrameId
7581
let id = if invalid_v2_frame {
7682
if let Some(id) = upgrade_v2(id_str) {
@@ -85,15 +91,10 @@ where
8591
};
8692
let frame_id = FrameId::new_cow(id)?;
8793

88-
// unsynch the frame size if necessary
89-
if synchsafe {
90-
size = size.unsynch();
91-
}
92-
9394
let flags = u16::from_be_bytes([header[8], header[9]]);
9495
let flags = parse_flags(flags, synchsafe);
9596

96-
Ok(Some((frame_id, size, flags)))
97+
Ok(Some((frame_id, flags)))
9798
}
9899

99100
pub(crate) fn parse_flags(flags: u16, v4: bool) -> FrameFlags {

src/id3/v2/frame/read.rs

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use byteorder::{BigEndian, ReadBytesExt};
1313

1414
pub(crate) enum ParsedFrame<'a> {
1515
Next(Frame<'a>),
16-
Skipped,
16+
Skip { size: u32 },
1717
Eof,
1818
}
1919

@@ -26,13 +26,15 @@ impl<'a> ParsedFrame<'a> {
2626
where
2727
R: Read,
2828
{
29+
let mut size = 0u32;
30+
2931
// The header will be upgraded to ID3v2.4 past this point, so they can all be treated the same
3032
let parse_header_result = match version {
31-
Id3v2Version::V2 => parse_v2_header(reader),
32-
Id3v2Version::V3 => parse_header(reader, false),
33-
Id3v2Version::V4 => parse_header(reader, true),
33+
Id3v2Version::V2 => parse_v2_header(reader, &mut size),
34+
Id3v2Version::V3 => parse_header(reader, &mut size, false),
35+
Id3v2Version::V4 => parse_header(reader, &mut size, true),
3436
};
35-
let (id, mut size, mut flags) = match parse_header_result {
37+
let (id, mut flags) = match parse_header_result {
3638
Ok(None) => {
3739
// Stop reading
3840
return Ok(Self::Eof);
@@ -44,7 +46,7 @@ impl<'a> ParsedFrame<'a> {
4446
ParsingMode::BestAttempt | ParsingMode::Relaxed => {
4547
// Skip this frame and continue reading
4648
// TODO: Log error?
47-
return Ok(Self::Skipped);
49+
return Ok(Self::Skip { size });
4850
},
4951
}
5052
},
@@ -116,14 +118,28 @@ impl<'a> ParsedFrame<'a> {
116118
return handle_encryption(&mut compression_reader, size, id, flags);
117119
}
118120

119-
return parse_frame(&mut compression_reader, id, flags, version, parse_mode);
121+
return parse_frame(
122+
&mut compression_reader,
123+
size,
124+
id,
125+
flags,
126+
version,
127+
parse_mode,
128+
);
120129
}
121130

122131
if flags.encryption.is_some() {
123132
return handle_encryption(&mut unsynchronized_reader, size, id, flags);
124133
}
125134

126-
return parse_frame(&mut unsynchronized_reader, id, flags, version, parse_mode);
135+
return parse_frame(
136+
&mut unsynchronized_reader,
137+
size,
138+
id,
139+
flags,
140+
version,
141+
parse_mode,
142+
);
127143
},
128144
// Possible combinations:
129145
//
@@ -138,7 +154,14 @@ impl<'a> ParsedFrame<'a> {
138154
return handle_encryption(&mut compression_reader, size, id, flags);
139155
}
140156

141-
return parse_frame(&mut compression_reader, id, flags, version, parse_mode);
157+
return parse_frame(
158+
&mut compression_reader,
159+
size,
160+
id,
161+
flags,
162+
version,
163+
parse_mode,
164+
);
142165
},
143166
// Possible combinations:
144167
//
@@ -151,7 +174,7 @@ impl<'a> ParsedFrame<'a> {
151174
},
152175
// Everything else that doesn't have special flags
153176
_ => {
154-
return parse_frame(&mut reader, id, flags, version, parse_mode);
177+
return parse_frame(&mut reader, size, id, flags, version, parse_mode);
155178
},
156179
}
157180
}
@@ -194,13 +217,14 @@ fn handle_encryption<R: Read>(
194217

195218
fn parse_frame<R: Read>(
196219
reader: &mut R,
220+
size: u32,
197221
id: FrameId<'static>,
198222
flags: FrameFlags,
199223
version: Id3v2Version,
200224
parse_mode: ParsingMode,
201225
) -> Result<ParsedFrame<'static>> {
202226
match parse_content(reader, id.as_str(), version, parse_mode)? {
203227
Some(value) => Ok(ParsedFrame::Next(Frame { id, value, flags })),
204-
None => Ok(ParsedFrame::Skipped),
228+
None => Ok(ParsedFrame::Skip { size }),
205229
}
206230
}

src/id3/v2/read.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::frame::read::ParsedFrame;
22
use super::tag::Id3v2Tag;
33
use super::Id3v2Header;
4-
use crate::error::Result;
4+
use crate::error::{Id3v2Error, Id3v2ErrorKind, Result};
55
use crate::id3::v2::util::synchsafe::UnsynchronizedStream;
66
use crate::probe::ParsingMode;
77

@@ -34,6 +34,17 @@ where
3434
Ok(ret)
3535
}
3636

37+
fn skip_frame(reader: &mut impl Read, size: u32) -> Result<()> {
38+
let size = u64::from(size);
39+
let mut reader = reader.take(size);
40+
let skipped = std::io::copy(&mut reader, &mut std::io::sink())?;
41+
debug_assert!(skipped <= size);
42+
if skipped != size {
43+
return Err(Id3v2Error::new(Id3v2ErrorKind::BadFrameLength).into());
44+
}
45+
Ok(())
46+
}
47+
3748
fn read_all_frames_into_tag<R>(
3849
reader: &mut R,
3950
header: Id3v2Header,
@@ -50,7 +61,9 @@ where
5061
match ParsedFrame::read(reader, header.version, parse_mode)? {
5162
ParsedFrame::Next(frame) => drop(tag.insert(frame)),
5263
// No frame content found or ignored due to errors, but we can expect more frames
53-
ParsedFrame::Skipped => continue,
64+
ParsedFrame::Skip { size } => {
65+
skip_frame(reader, size)?;
66+
},
5467
// No frame content found, and we can expect there are no more frames
5568
ParsedFrame::Eof => break,
5669
}

0 commit comments

Comments
 (0)