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_ {
|
match self.type_ {
|
||||||
Type::BYTE | Type::UNDEFINED => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(bytes.iter().copied().map(Byte))
|
v.extend(bytes.iter().copied().map(Byte))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::SBYTE => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(bytes.iter().copied().map(|v| SignedByte(v as i8)))
|
v.extend(bytes.iter().copied().map(|v| SignedByte(v as i8)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::SHORT => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(
|
v.extend(
|
||||||
bytes
|
bytes
|
||||||
@@ -605,7 +605,7 @@ impl Entry {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::SSHORT => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(
|
v.extend(
|
||||||
bytes
|
bytes
|
||||||
@@ -615,7 +615,7 @@ impl Entry {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::LONG => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(
|
v.extend(
|
||||||
bytes
|
bytes
|
||||||
@@ -625,7 +625,7 @@ impl Entry {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::SLONG => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(
|
v.extend(
|
||||||
bytes
|
bytes
|
||||||
@@ -635,7 +635,7 @@ impl Entry {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::FLOAT => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(
|
v.extend(
|
||||||
bytes
|
bytes
|
||||||
@@ -645,7 +645,7 @@ impl Entry {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::DOUBLE => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(
|
v.extend(
|
||||||
bytes
|
bytes
|
||||||
@@ -655,7 +655,7 @@ impl Entry {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::RATIONAL => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(bytes.chunks_exact(8).map(|ch| {
|
v.extend(bytes.chunks_exact(8).map(|ch| {
|
||||||
Rational(
|
Rational(
|
||||||
@@ -666,7 +666,7 @@ impl Entry {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::SRATIONAL => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(bytes.chunks_exact(8).map(|ch| {
|
v.extend(bytes.chunks_exact(8).map(|ch| {
|
||||||
SRational(
|
SRational(
|
||||||
@@ -677,7 +677,7 @@ impl Entry {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::LONG8 => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(
|
v.extend(
|
||||||
bytes
|
bytes
|
||||||
@@ -687,7 +687,7 @@ impl Entry {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::SLONG8 => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(
|
v.extend(
|
||||||
bytes
|
bytes
|
||||||
@@ -697,7 +697,7 @@ impl Entry {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::IFD => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(
|
v.extend(
|
||||||
bytes
|
bytes
|
||||||
@@ -707,7 +707,7 @@ impl Entry {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::IFD8 => {
|
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| {
|
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||||
v.extend(
|
v.extend(
|
||||||
bytes
|
bytes
|
||||||
@@ -719,7 +719,7 @@ impl Entry {
|
|||||||
Type::ASCII => {
|
Type::ASCII => {
|
||||||
let n = usize::try_from(self.count)?;
|
let n = usize::try_from(self.count)?;
|
||||||
|
|
||||||
if n > limits.decoding_buffer_size {
|
if n > limits.ifd_value_size {
|
||||||
return Err(TiffError::LimitsExceeded);
|
return Err(TiffError::LimitsExceeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -846,10 +846,20 @@ impl Entry {
|
|||||||
|
|
||||||
fn vec_with_capacity(
|
fn vec_with_capacity(
|
||||||
value_count: u64,
|
value_count: u64,
|
||||||
|
type_: Type,
|
||||||
limits: &super::Limits,
|
limits: &super::Limits,
|
||||||
) -> Result<Vec<Value>, TiffError> {
|
) -> 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>() {
|
if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() {
|
||||||
return Err(TiffError::LimitsExceeded);
|
return Err(TiffError::LimitsExceeded);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ fn packbits_decode<R: Read, const N: usize>(
|
|||||||
reader: &mut R,
|
reader: &mut R,
|
||||||
buf: &mut [[u8; N]],
|
buf: &mut [[u8; N]],
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
|
let expected = buf.len();
|
||||||
for offset in (0..N).rev() {
|
for offset in (0..N).rev() {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
let mut code = [0u8; 1];
|
let mut code = [0u8; 1];
|
||||||
@@ -124,6 +125,13 @@ fn packbits_decode<R: Read, const N: usize>(
|
|||||||
i += usize::from(repeat);
|
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(())
|
Ok(())
|
||||||
|
|||||||
Reference in new issue
Block a user