fix: enforce ifd_value_size limits and detect PackBits under-decode
Two input validation fixes: - vec_with_capacity now checks raw IFD value bytes against ifd_value_size before allocating. Previously only the in-memory Value representation was bounded (by decoding_buffer_size), so a crafted IFD with a large count of small-typed values could allocate based on raw byte count without limit checking. Also fix ASCII value parsing to check ifd_value_size instead of decoding_buffer_size. - LogLuv PackBits decoder now verifies that the expected number of pixels was actually decoded per plane, returning UnexpectedEof if the RLE stream ends short.
This commit is contained in:
1 parent
6a3282e9f2
commit
e4c3c1fcfd
2 files changed
+34
-16
No files matched your search
+26
-16
@@ -583,19 +583,19 @@ impl Entry {
|
||||
|
||||
match self.type_ {
|
||||
Type::BYTE | Type::UNDEFINED => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(bytes.iter().copied().map(Byte))
|
||||
})
|
||||
}
|
||||
Type::SBYTE => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(bytes.iter().copied().map(|v| SignedByte(v as i8)))
|
||||
})
|
||||
}
|
||||
Type::SHORT => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
@@ -605,7 +605,7 @@ impl Entry {
|
||||
})
|
||||
}
|
||||
Type::SSHORT => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
@@ -615,7 +615,7 @@ impl Entry {
|
||||
})
|
||||
}
|
||||
Type::LONG => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
@@ -625,7 +625,7 @@ impl Entry {
|
||||
})
|
||||
}
|
||||
Type::SLONG => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
@@ -635,7 +635,7 @@ impl Entry {
|
||||
})
|
||||
}
|
||||
Type::FLOAT => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
@@ -645,7 +645,7 @@ impl Entry {
|
||||
})
|
||||
}
|
||||
Type::DOUBLE => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
@@ -655,7 +655,7 @@ impl Entry {
|
||||
})
|
||||
}
|
||||
Type::RATIONAL => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(bytes.chunks_exact(8).map(|ch| {
|
||||
Rational(
|
||||
@@ -666,7 +666,7 @@ impl Entry {
|
||||
})
|
||||
}
|
||||
Type::SRATIONAL => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(bytes.chunks_exact(8).map(|ch| {
|
||||
SRational(
|
||||
@@ -677,7 +677,7 @@ impl Entry {
|
||||
})
|
||||
}
|
||||
Type::LONG8 => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
@@ -687,7 +687,7 @@ impl Entry {
|
||||
})
|
||||
}
|
||||
Type::SLONG8 => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
@@ -697,7 +697,7 @@ impl Entry {
|
||||
})
|
||||
}
|
||||
Type::IFD => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
@@ -707,7 +707,7 @@ impl Entry {
|
||||
})
|
||||
}
|
||||
Type::IFD8 => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
v = Self::vec_with_capacity(self.count, self.type_, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
@@ -719,7 +719,7 @@ impl Entry {
|
||||
Type::ASCII => {
|
||||
let n = usize::try_from(self.count)?;
|
||||
|
||||
if n > limits.decoding_buffer_size {
|
||||
if n > limits.ifd_value_size {
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
|
||||
@@ -846,10 +846,20 @@ impl Entry {
|
||||
|
||||
fn vec_with_capacity(
|
||||
value_count: u64,
|
||||
type_: Type,
|
||||
limits: &super::Limits,
|
||||
) -> Result<Vec<Value>, TiffError> {
|
||||
let value_count = usize::try_from(value_count)?;
|
||||
// Check raw file bytes against ifd_value_size.
|
||||
if !type_
|
||||
.value_bytes(value_count)
|
||||
.and_then(|b| usize::try_from(b).map_err(|_| TiffError::LimitsExceeded))
|
||||
.is_ok_and(|raw_len| raw_len <= limits.ifd_value_size)
|
||||
{
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
|
||||
// Check in-memory Value representation against decoding_buffer_size.
|
||||
let value_count = usize::try_from(value_count)?;
|
||||
if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() {
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ fn packbits_decode<R: Read, const N: usize>(
|
||||
reader: &mut R,
|
||||
buf: &mut [[u8; N]],
|
||||
) -> std::io::Result<()> {
|
||||
let expected = buf.len();
|
||||
for offset in (0..N).rev() {
|
||||
let mut i = 0;
|
||||
let mut code = [0u8; 1];
|
||||
@@ -124,6 +125,13 @@ fn packbits_decode<R: Read, const N: usize>(
|
||||
i += usize::from(repeat);
|
||||
}
|
||||
}
|
||||
|
||||
if i < expected {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::UnexpectedEof,
|
||||
"PackBits RLE decoded fewer pixels than expected",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in new issue
Block a user