Skip to content

Commit e9ec05b

Browse files
committed
AIFF: Fix parsing of invalid f80 sample rates
1 parent 37fdc65 commit e9ec05b

File tree

6 files changed

+245
-87
lines changed

6 files changed

+245
-87
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2929
- **WAV**: Fix panic when reading properties with large written bytes per second ([issue](https://github.com/Serial-ATA/lofty-rs/issues/420))
3030
- **Vorbis**: Fix panic when reading properties of a file with large absolute granule positions ([issue](https://github.com/Serial-ATA/lofty-rs/issues/421))
3131
- **FLAC**: Fix panic when reading properties of a file with incorrect block sizes ([issue](https://github.com/Serial-ATA/lofty-rs/issues/422))
32+
- **AIFF**: Fix panic when reading properties of a file with invalid f80 sample rate ([issue](https://github.com/Serial-ATA/lofty-rs/issues/424))
3233

3334
## [0.20.1] - 2024-07-02
3435

lofty/src/iff/aiff/properties.rs

Lines changed: 58 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::borrow::Cow;
88
use std::io::Read;
99
use std::time::Duration;
1010

11+
use crate::io::ReadExt;
1112
use byteorder::{BigEndian, ReadBytesExt};
1213

1314
/// The AIFC compression type
@@ -166,34 +167,13 @@ pub(super) fn read_properties(
166167
let sample_frames = comm.read_u32::<BigEndian>()?;
167168
let sample_size = comm.read_u16::<BigEndian>()?;
168169

169-
let mut sample_rate_bytes = [0; 10];
170-
comm.read_exact(&mut sample_rate_bytes)?;
171-
172-
let sign = u64::from(sample_rate_bytes[0] & 0x80);
173-
174-
sample_rate_bytes[0] &= 0x7F;
175-
176-
let mut exponent = u16::from(sample_rate_bytes[0]) << 8 | u16::from(sample_rate_bytes[1]);
177-
exponent = exponent - 16383 + 1023;
178-
179-
let fraction = &mut sample_rate_bytes[2..];
180-
fraction[0] &= 0x7F;
181-
182-
let fraction: Vec<u64> = fraction.iter_mut().map(|v| u64::from(*v)).collect();
183-
184-
let fraction = fraction[0] << 56
185-
| fraction[1] << 48
186-
| fraction[2] << 40
187-
| fraction[3] << 32
188-
| fraction[4] << 24
189-
| fraction[5] << 16
190-
| fraction[6] << 8
191-
| fraction[7];
192-
193-
let f64_bytes = sign << 56 | u64::from(exponent) << 52 | fraction >> 11;
194-
let float = f64::from_be_bytes(f64_bytes.to_be_bytes());
170+
let sample_rate_extended = comm.read_f80()?;
171+
let sample_rate_64 = sample_rate_extended.as_f64();
172+
if !sample_rate_64.is_finite() || !sample_rate_64.is_sign_positive() {
173+
decode_err!(@BAIL Aiff, "Invalid sample rate");
174+
}
195175

196-
let sample_rate = float.round() as u32;
176+
let sample_rate = sample_rate_64.round() as u32;
197177

198178
let (duration, overall_bitrate, audio_bitrate) = if sample_rate > 0 && sample_frames > 0 {
199179
let length = (f64::from(sample_frames) * 1000.0) / f64::from(sample_rate);
@@ -207,50 +187,60 @@ pub(super) fn read_properties(
207187
(Duration::ZERO, 0, 0)
208188
};
209189

210-
let mut compression = None;
211-
if comm.len() >= 5 && compression_present == CompressionPresent::Yes {
212-
let mut compression_type = [0u8; 4];
213-
comm.read_exact(&mut compression_type)?;
214-
215-
compression = Some(match &compression_type {
216-
b"NONE" => AiffCompressionType::None,
217-
b"ACE2" => AiffCompressionType::ACE2,
218-
b"ACE8" => AiffCompressionType::ACE8,
219-
b"MAC3" => AiffCompressionType::MAC3,
220-
b"MAC6" => AiffCompressionType::MAC6,
221-
b"sowt" => AiffCompressionType::sowt,
222-
b"fl32" => AiffCompressionType::fl32,
223-
b"fl64" => AiffCompressionType::fl64,
224-
b"alaw" => AiffCompressionType::alaw,
225-
b"ulaw" => AiffCompressionType::ulaw,
226-
b"ULAW" => AiffCompressionType::ULAW,
227-
b"ALAW" => AiffCompressionType::ALAW,
228-
b"FL32" => AiffCompressionType::FL32,
229-
_ => {
230-
log::debug!(
231-
"Encountered unknown compression type: {:?}",
232-
compression_type
233-
);
234-
235-
// We have to read the compression name string
236-
let mut compression_name = String::new();
237-
238-
let compression_name_size = comm.read_u8()?;
239-
if compression_name_size > 0 {
240-
let mut compression_name_bytes = try_vec![0u8; compression_name_size as usize];
241-
comm.read_exact(&mut compression_name_bytes)?;
242-
243-
compression_name = utf8_decode(compression_name_bytes)?;
244-
}
245-
246-
AiffCompressionType::Other {
247-
compression_type,
248-
compression_name,
249-
}
250-
},
190+
let is_compressed = comm.len() >= 5 && compression_present == CompressionPresent::Yes;
191+
if !is_compressed {
192+
return Ok(AiffProperties {
193+
duration,
194+
overall_bitrate,
195+
audio_bitrate,
196+
sample_rate,
197+
sample_size,
198+
channels,
199+
compression_type: None,
251200
});
252201
}
253202

203+
let mut compression_type = [0u8; 4];
204+
comm.read_exact(&mut compression_type)?;
205+
206+
let compression = Some(match &compression_type {
207+
b"NONE" => AiffCompressionType::None,
208+
b"ACE2" => AiffCompressionType::ACE2,
209+
b"ACE8" => AiffCompressionType::ACE8,
210+
b"MAC3" => AiffCompressionType::MAC3,
211+
b"MAC6" => AiffCompressionType::MAC6,
212+
b"sowt" => AiffCompressionType::sowt,
213+
b"fl32" => AiffCompressionType::fl32,
214+
b"fl64" => AiffCompressionType::fl64,
215+
b"alaw" => AiffCompressionType::alaw,
216+
b"ulaw" => AiffCompressionType::ulaw,
217+
b"ULAW" => AiffCompressionType::ULAW,
218+
b"ALAW" => AiffCompressionType::ALAW,
219+
b"FL32" => AiffCompressionType::FL32,
220+
_ => {
221+
log::debug!(
222+
"Encountered unknown compression type: {:?}",
223+
compression_type
224+
);
225+
226+
// We have to read the compression name string
227+
let mut compression_name = String::new();
228+
229+
let compression_name_size = comm.read_u8()?;
230+
if compression_name_size > 0 {
231+
let mut compression_name_bytes = try_vec![0u8; compression_name_size as usize];
232+
comm.read_exact(&mut compression_name_bytes)?;
233+
234+
compression_name = utf8_decode(compression_name_bytes)?;
235+
}
236+
237+
AiffCompressionType::Other {
238+
compression_type,
239+
compression_name,
240+
}
241+
},
242+
});
243+
254244
Ok(AiffProperties {
255245
duration,
256246
overall_bitrate,

lofty/src/util/io.rs

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Various traits for reading and writing to file-like objects
22
3-
use crate::error::LoftyError;
3+
use crate::error::{LoftyError, Result};
4+
use crate::util::math::F80;
45

56
use std::collections::VecDeque;
67
use std::fs::File;
@@ -49,21 +50,21 @@ pub trait Truncate {
4950
/// # Errors
5051
///
5152
/// Errors depend on the object being truncated, which may not always be fallible.
52-
fn truncate(&mut self, new_len: u64) -> Result<(), Self::Error>;
53+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error>;
5354
}
5455

5556
impl Truncate for File {
5657
type Error = std::io::Error;
5758

58-
fn truncate(&mut self, new_len: u64) -> Result<(), Self::Error> {
59+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error> {
5960
self.set_len(new_len)
6061
}
6162
}
6263

6364
impl Truncate for Vec<u8> {
6465
type Error = std::convert::Infallible;
6566

66-
fn truncate(&mut self, new_len: u64) -> Result<(), Self::Error> {
67+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error> {
6768
self.truncate(new_len as usize);
6869
Ok(())
6970
}
@@ -72,7 +73,7 @@ impl Truncate for Vec<u8> {
7273
impl Truncate for VecDeque<u8> {
7374
type Error = std::convert::Infallible;
7475

75-
fn truncate(&mut self, new_len: u64) -> Result<(), Self::Error> {
76+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error> {
7677
self.truncate(new_len as usize);
7778
Ok(())
7879
}
@@ -84,7 +85,7 @@ where
8485
{
8586
type Error = <T as Truncate>::Error;
8687

87-
fn truncate(&mut self, new_len: u64) -> Result<(), Self::Error> {
88+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error> {
8889
self.get_mut().truncate(new_len)
8990
}
9091
}
@@ -95,7 +96,7 @@ where
9596
{
9697
type Error = <T as Truncate>::Error;
9798

98-
fn truncate(&mut self, new_len: u64) -> Result<(), Self::Error> {
99+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error> {
99100
self.as_mut().truncate(new_len)
100101
}
101102
}
@@ -106,7 +107,7 @@ where
106107
{
107108
type Error = <T as Truncate>::Error;
108109

109-
fn truncate(&mut self, new_len: u64) -> Result<(), Self::Error> {
110+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error> {
110111
(**self).truncate(new_len)
111112
}
112113
}
@@ -136,29 +137,29 @@ pub trait Length {
136137
/// # Errors
137138
///
138139
/// Errors depend on the object being read, which may not always be fallible.
139-
fn len(&self) -> Result<u64, Self::Error>;
140+
fn len(&self) -> std::result::Result<u64, Self::Error>;
140141
}
141142

142143
impl Length for File {
143144
type Error = std::io::Error;
144145

145-
fn len(&self) -> Result<u64, Self::Error> {
146+
fn len(&self) -> std::result::Result<u64, Self::Error> {
146147
self.metadata().map(|m| m.len())
147148
}
148149
}
149150

150151
impl Length for Vec<u8> {
151152
type Error = std::convert::Infallible;
152153

153-
fn len(&self) -> Result<u64, Self::Error> {
154+
fn len(&self) -> std::result::Result<u64, Self::Error> {
154155
Ok(self.len() as u64)
155156
}
156157
}
157158

158159
impl Length for VecDeque<u8> {
159160
type Error = std::convert::Infallible;
160161

161-
fn len(&self) -> Result<u64, Self::Error> {
162+
fn len(&self) -> std::result::Result<u64, Self::Error> {
162163
Ok(self.len() as u64)
163164
}
164165
}
@@ -169,7 +170,7 @@ where
169170
{
170171
type Error = <T as Length>::Error;
171172

172-
fn len(&self) -> Result<u64, Self::Error> {
173+
fn len(&self) -> std::result::Result<u64, Self::Error> {
173174
Length::len(self.get_ref())
174175
}
175176
}
@@ -180,7 +181,7 @@ where
180181
{
181182
type Error = <T as Length>::Error;
182183

183-
fn len(&self) -> Result<u64, Self::Error> {
184+
fn len(&self) -> std::result::Result<u64, Self::Error> {
184185
Length::len(self.as_ref())
185186
}
186187
}
@@ -191,7 +192,7 @@ where
191192
{
192193
type Error = <T as Length>::Error;
193194

194-
fn len(&self) -> Result<u64, Self::Error> {
195+
fn len(&self) -> std::result::Result<u64, Self::Error> {
195196
Length::len(*self)
196197
}
197198
}
@@ -202,7 +203,7 @@ where
202203
{
203204
type Error = <T as Length>::Error;
204205

205-
fn len(&self) -> Result<u64, Self::Error> {
206+
fn len(&self) -> std::result::Result<u64, Self::Error> {
206207
Length::len(*self)
207208
}
208209
}
@@ -229,6 +230,22 @@ where
229230
{
230231
}
231232

233+
pub(crate) trait ReadExt: Read {
234+
fn read_f80(&mut self) -> Result<F80>;
235+
}
236+
237+
impl<R> ReadExt for R
238+
where
239+
R: Read,
240+
{
241+
fn read_f80(&mut self) -> Result<F80> {
242+
let mut bytes = [0; 10];
243+
self.read_exact(&mut bytes)?;
244+
245+
Ok(F80::from_be_bytes(bytes))
246+
}
247+
}
248+
232249
#[cfg(test)]
233250
mod tests {
234251
use crate::config::{ParseOptions, WriteOptions};

0 commit comments

Comments
 (0)