Fix lzw decoding stopping short of full decode

When the required amount of data was just a tiny bit larger than full a
page size (one buffer increment), the decoder would stop since all
compressed data had been read. However, the lzw decoder contains a small
internal bit buffer for code decoding. That is, some remaining bytes had
been retained and were still left to be decoded.
This commit is contained in:
Andreas Molzer committed 2020-12-05 14:57:28 +01:00
1 parent 8ab9d30eff
commit 4d8c64bf1e
3 files changed
+44 -9

No files matched your search

+19 -7
View File
@@ -354,6 +354,11 @@ impl<R: Read + Seek> Decoder<R> {
PhotometricInterpretation::RGB => match self.bits_per_sample[..] { PhotometricInterpretation::RGB => match self.bits_per_sample[..] {
[r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)), [r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
[r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)), [r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
// FIXME: We should _ignore_ other components. In particular:
// > Beware of extra components. Some TIFF files may have more components per pixel
// than you think. A Baseline TIFF reader must skip over them gracefully,using the
// values of the SamplesPerPixel and BitsPerSample fields.
// > -- TIFF 6.0 Specification, Section 7, Additional Baseline requirements.
_ => Err(TiffError::UnsupportedError( _ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::InterpretationWithBits( TiffUnsupportedError::InterpretationWithBits(
self.photometric_interpretation, self.photometric_interpretation,
@@ -766,7 +771,7 @@ impl<R: Read + Seek> Decoder<R> {
buffer: DecodingBuffer<'a>, buffer: DecodingBuffer<'a>,
offset: u64, offset: u64,
length: u64, length: u64,
max_uncompressed_length: usize, strip_sample_count: usize,
) -> TiffResult<usize> { ) -> TiffResult<usize> {
let color_type = self.colortype()?; let color_type = self.colortype()?;
self.goto_offset_u64(offset)?; self.goto_offset_u64(offset)?;
@@ -782,7 +787,7 @@ impl<R: Read + Seek> Decoder<R> {
let (bytes, reader) = LZWReader::new( let (bytes, reader) = LZWReader::new(
&mut self.reader, &mut self.reader,
usize::try_from(length)?, usize::try_from(length)?,
max_uncompressed_length * buffer.byte_len(), strip_sample_count * buffer.byte_len(),
)?; )?;
(bytes, Box::new(reader)) (bytes, Box::new(reader))
} }
@@ -793,8 +798,7 @@ impl<R: Read + Seek> Decoder<R> {
(bytes, Box::new(reader)) (bytes, Box::new(reader))
} }
CompressionMethod::OldDeflate => { CompressionMethod::OldDeflate => {
let (bytes, reader) = let (bytes, reader) = DeflateReader::new(&mut self.reader, strip_sample_count)?;
DeflateReader::new(&mut self.reader, max_uncompressed_length)?;
(bytes, Box::new(reader)) (bytes, Box::new(reader))
} }
method => { method => {
@@ -804,9 +808,14 @@ impl<R: Read + Seek> Decoder<R> {
} }
}; };
if bytes / buffer.byte_len() > max_uncompressed_length { // FIXME: this might be suboptimal. We might default remaining bits to ´0`, which some
// other decoders might do.
if bytes / buffer.byte_len() > strip_sample_count {
return Err(TiffError::FormatError( return Err(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::UnexpectedCompressedData {
actual: bytes,
required: strip_sample_count * buffer.byte_len(),
},
)); ));
} }
@@ -1039,7 +1048,10 @@ impl<R: Read + Seek> Decoder<R> {
if units_read < buffer_size { if units_read < buffer_size {
return Err(TiffError::FormatError( return Err(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::InconsistentStripElements {
actual: units_read,
required: buffer_size,
},
)); ));
} }
if let Ok(predictor) = self.get_tag_unsigned(Tag::Predictor) { if let Ok(predictor) = self.get_tag_unsigned(Tag::Predictor) {
+3 -2
View File
@@ -267,10 +267,11 @@ impl LZWReader {
let mut uncompressed = Vec::with_capacity(max_uncompressed_length); let mut uncompressed = Vec::with_capacity(max_uncompressed_length);
let mut decoder = weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8); let mut decoder = weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8);
let mut bytes_read = 0; let mut bytes_read = 0;
while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
while uncompressed.len() < max_uncompressed_length {
let bytes_written = uncompressed.len(); let bytes_written = uncompressed.len();
uncompressed.reserve(1 << 12); uncompressed.reserve(1 << 12);
let buffer_space = uncompressed.capacity(); let buffer_space = uncompressed.capacity().min(max_uncompressed_length);
uncompressed.resize(buffer_space, 0u8); uncompressed.resize(buffer_space, 0u8);
let result = decoder.decode_bytes( let result = decoder.decode_bytes(
+22
View File
@@ -43,6 +43,14 @@ pub enum TiffFormatError {
TiffSignatureInvalid, TiffSignatureInvalid,
ImageFileDirectoryNotFound, ImageFileDirectoryNotFound,
InconsistentSizesEncountered, InconsistentSizesEncountered,
UnexpectedCompressedData {
actual: usize,
required: usize,
},
InconsistentStripElements {
actual: usize,
required: usize,
},
InvalidTag, InvalidTag,
InvalidTagValueType(Tag), InvalidTagValueType(Tag),
RequiredTagNotFound(Tag), RequiredTagNotFound(Tag),
@@ -66,6 +74,20 @@ impl fmt::Display for TiffFormatError {
TiffSignatureInvalid => write!(fmt, "TIFF signature invalid."), TiffSignatureInvalid => write!(fmt, "TIFF signature invalid."),
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."), ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."), InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
UnexpectedCompressedData { actual, required } => {
write!(
fmt,
"Decompression returned different amount of bytes than expected: got {}, expected {}.",
actual, required
)
}
InconsistentStripElements { actual, required } => {
write!(
fmt,
"Inconsistent elements in strip: got {}, expected {}.",
actual, required
)
}
InvalidTag => write!(fmt, "Image contains invalid tag."), InvalidTag => write!(fmt, "Image contains invalid tag."),
InvalidTagValueType(ref tag) => { InvalidTagValueType(ref tag) => {
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag) write!(fmt, "Tag `{:?}` did not have the expected value type.", tag)