Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8459f413a | ||
|
|
26e760e303 | ||
|
|
a4878e9658 | ||
|
|
73285481cd | ||
|
|
b065539c07 | ||
|
|
1c5800daa0 | ||
|
|
885d762def | ||
|
|
2c9e02d347 | ||
|
|
b318481c55 | ||
|
|
1774b362d3 | ||
|
|
f2fec92283 | ||
|
|
be0619d6a1 | ||
|
|
e92cf219a9 | ||
|
|
3e5f977460 | ||
|
|
f0666aebbf | ||
|
|
65a1fbe8ae | ||
|
|
03b6ed413b | ||
|
|
5b7814c994 | ||
|
|
a15d619471 | ||
|
|
458a713827 | ||
|
|
2f6ad07f28 | ||
|
|
153ed48ee7 | ||
|
|
420667955a | ||
|
|
3bf19822a2 | ||
|
|
2bb37a4ecd | ||
|
|
b41ac105ab | ||
|
|
1e10b9803b | ||
|
|
2f4e491874 | ||
|
|
285b881e27 | ||
|
|
bdfe2f55fd | ||
|
|
eda5a8ac30 | ||
|
|
bd1e37d78b | ||
|
|
ad0f75222a |
No files matched your search
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust: ["1.34.2", stable, beta, nightly]
|
||||
rust: ["1.48", stable, beta, nightly]
|
||||
command: [build, test]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: test
|
||||
run: >
|
||||
cargo test --tests --benches --no-default-features --features "$FEATURES"
|
||||
if: ${{ matrix.rust != '1.34.2' }}
|
||||
if: ${{ matrix.rust != '1.48' }}
|
||||
env:
|
||||
FEATURES: ${{ matrix.features }}
|
||||
rustfmt:
|
||||
|
||||
+33
@@ -1,3 +1,36 @@
|
||||
# Version 0.7.1
|
||||
|
||||
New features:
|
||||
* Encoding signed integer formats is now supported.
|
||||
* Extensive fuzzing with `cargo fuzz`.
|
||||
|
||||
Changes:
|
||||
* Tile decoding should be a little faster, requires one less intermediate buffer.
|
||||
* Images whose IFDs form a cycle due to their offsets will now raise an error
|
||||
when the cycle would be entered (jumping back should still be supported).
|
||||
|
||||
Fixes:
|
||||
* Fixed a regression that caused conflict between strips and tile images,
|
||||
causing errors in decoding some images.
|
||||
* Use checked integer arithmetic in limit calculations, fixes overflows.
|
||||
* IFD Tags are now always cleared between images.
|
||||
* Found by fuzzing: Several memory limit overflows; JPEG now correctly
|
||||
validates offsets and a minimum size of its table; Check upper limit of strip
|
||||
byte size correctly;
|
||||
|
||||
Notes:
|
||||
Our CI has warned that this version no longer builds on `1.34.2` out of the
|
||||
box. We're still committed to the MSRV on this major version yet one
|
||||
dependency—`flate2`—has already bumped it in a SemVer compatible version of its
|
||||
own. This is out-of-our-control (cargo's dependency resolution does not allow
|
||||
us to address this in a reasonable manner).
|
||||
|
||||
This can be address this by pinning the version of `flate2` to `1.0.21` in your
|
||||
own files. However, you should understand that this puts you in considerable
|
||||
maintenance debt as you will no longer receive any updates for this dependency
|
||||
and any package that _requires_ a new version of the `1.0` series would be
|
||||
incompatible with this requirement (cargo might yell at you very loudly).
|
||||
|
||||
# Version 0.7.0
|
||||
|
||||
New features:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tiff"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
authors = [
|
||||
"ccgn",
|
||||
"bvssvni <bvssvni@gmail.com>",
|
||||
|
||||
@@ -44,3 +44,11 @@ Formats and interpretations not listed above or with empty entries are unsupport
|
||||
- Baseline tags
|
||||
- `ExtraSamples`
|
||||
- Extension tags
|
||||
|
||||
## Fuzzing
|
||||
|
||||
This crate uses [cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz) in order to test the image parser.
|
||||
|
||||
After installing it with `cargo install cargo-fuzz` on a nightly rustc, the
|
||||
fuzzing harness can be run with recommended settings using
|
||||
`cargo fuzz run decode_image -snone -- -timeout=5`.
|
||||
@@ -0,0 +1,3 @@
|
||||
target
|
||||
corpus
|
||||
artifacts
|
||||
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "tiff-fuzz"
|
||||
version = "0.0.0"
|
||||
authors = ["Automatically generated"]
|
||||
publish = false
|
||||
edition = "2018"
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
libfuzzer-sys = "0.4"
|
||||
|
||||
[dependencies.tiff]
|
||||
path = ".."
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[[bin]]
|
||||
name = "decode_image"
|
||||
path = "fuzz_targets/decode_image.rs"
|
||||
test = false
|
||||
doc = false
|
||||
@@ -0,0 +1,31 @@
|
||||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let mut decoder = if let Ok(d) = tiff::decoder::Decoder::new(std::io::Cursor::new(data)) {
|
||||
d
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut limits = tiff::decoder::Limits::default();
|
||||
limits.decoding_buffer_size = 1_000_000;
|
||||
limits.ifd_value_size = 1_000_000;
|
||||
limits.intermediate_buffer_size = 1_000_000;
|
||||
|
||||
decoder = decoder.with_limits(limits);
|
||||
|
||||
loop {
|
||||
if let Err(_) = decoder.read_image() {
|
||||
break;
|
||||
}
|
||||
|
||||
if !decoder.more_images() {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Err(_) = decoder.next_image() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
+20
-13
@@ -349,19 +349,26 @@ impl Entry {
|
||||
}
|
||||
|
||||
let bo = decoder.byte_order();
|
||||
let value_bytes = self.count
|
||||
* match self.type_ {
|
||||
Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1,
|
||||
Type::SHORT | Type::SSHORT => 2,
|
||||
Type::LONG | Type::SLONG | Type::FLOAT | Type::IFD => 4,
|
||||
Type::LONG8
|
||||
| Type::SLONG8
|
||||
| Type::DOUBLE
|
||||
| Type::RATIONAL
|
||||
| Type::SRATIONAL
|
||||
| Type::IFD8 => 8,
|
||||
Type::__NonExhaustive => unreachable!(),
|
||||
};
|
||||
|
||||
let tag_size = match self.type_ {
|
||||
Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1,
|
||||
Type::SHORT | Type::SSHORT => 2,
|
||||
Type::LONG | Type::SLONG | Type::FLOAT | Type::IFD => 4,
|
||||
Type::LONG8
|
||||
| Type::SLONG8
|
||||
| Type::DOUBLE
|
||||
| Type::RATIONAL
|
||||
| Type::SRATIONAL
|
||||
| Type::IFD8 => 8,
|
||||
Type::__NonExhaustive => unreachable!(),
|
||||
};
|
||||
|
||||
let value_bytes = match self.count.checked_mul(tag_size) {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
};
|
||||
|
||||
// Case 2: there is one value.
|
||||
if self.count == 1 {
|
||||
|
||||
+151
-87
@@ -1,4 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{self, Read, Seek};
|
||||
use std::{cmp, ops::Range};
|
||||
@@ -251,7 +251,6 @@ struct TileAttributes {
|
||||
padding_right: usize,
|
||||
/// length of padding for bottommost tile in pixels
|
||||
padding_down: usize,
|
||||
/// A simple buffer right paddding is read into
|
||||
tile_samples: usize,
|
||||
/// Sample count of one row of one tile
|
||||
row_samples: usize,
|
||||
@@ -294,8 +293,6 @@ struct TileDecodeState {
|
||||
current_tile: usize,
|
||||
tile_offsets: Vec<u64>,
|
||||
tile_bytes: Vec<u64>,
|
||||
/// Buffer used for skipping horizontal padding
|
||||
padding_buffer: Vec<u8>,
|
||||
/// Pixel width of one row of the decoding result (tile / whole image)
|
||||
result_width: usize,
|
||||
}
|
||||
@@ -381,6 +378,7 @@ where
|
||||
strip_decoder: Option<StripDecodeState>,
|
||||
tile_decoder: Option<TileDecodeState>,
|
||||
tile_attributes: Option<TileAttributes>,
|
||||
seen_ifds: HashSet<u64>,
|
||||
}
|
||||
|
||||
trait Wrapping {
|
||||
@@ -547,6 +545,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
strip_decoder: None,
|
||||
tile_decoder: None,
|
||||
tile_attributes: None,
|
||||
seen_ifds: HashSet::new(),
|
||||
}
|
||||
.init()
|
||||
}
|
||||
@@ -643,7 +642,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
))
|
||||
}
|
||||
}
|
||||
self.next_ifd = match self.read_ifd_offset()? {
|
||||
self.next_ifd = match self.read_ifd_offset_nonrepeating()? {
|
||||
0 => None,
|
||||
n => Some(n),
|
||||
};
|
||||
@@ -665,58 +664,66 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
self.width = self.get_tag_u32(Tag::ImageWidth)?;
|
||||
self.height = self.get_tag_u32(Tag::ImageLength)?;
|
||||
self.strip_decoder = None;
|
||||
self.tile_decoder = None;
|
||||
self.tile_attributes = None;
|
||||
|
||||
self.photometric_interpretation = self
|
||||
.find_tag_unsigned(Tag::PhotometricInterpretation)?
|
||||
.and_then(PhotometricInterpretation::from_u16)
|
||||
.ok_or(TiffUnsupportedError::UnknownInterpretation)?;
|
||||
|
||||
if let Some(val) = self.find_tag_unsigned(Tag::Compression)? {
|
||||
self.compression_method = CompressionMethod::from_u16(val)
|
||||
.ok_or(TiffUnsupportedError::UnknownCompressionMethod)?;
|
||||
}
|
||||
if let Some(val) = self.find_tag_unsigned(Tag::SamplesPerPixel)? {
|
||||
self.samples = val;
|
||||
}
|
||||
if let Some(vals) = self.find_tag_unsigned_vec(Tag::SampleFormat)? {
|
||||
self.sample_format = vals
|
||||
.into_iter()
|
||||
.map(SampleFormat::from_u16_exhaustive)
|
||||
.collect();
|
||||
// Try to parse both the compression method and the number, format, and bits of the included samples.
|
||||
// If they are not explicitly specified, those tags are reset to their default values and not carried from previous images.
|
||||
self.compression_method = match self.find_tag_unsigned(Tag::Compression)? {
|
||||
Some(val) => CompressionMethod::from_u16(val)
|
||||
.ok_or(TiffUnsupportedError::UnknownCompressionMethod)?,
|
||||
None => CompressionMethod::None,
|
||||
};
|
||||
|
||||
// TODO: for now, only homogenous formats across samples are supported.
|
||||
if !self.sample_format.windows(2).all(|s| s[0] == s[1]) {
|
||||
return Err(TiffUnsupportedError::UnsupportedSampleFormat(
|
||||
self.sample_format.clone(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
match self.samples {
|
||||
1 | 3 | 4 => {
|
||||
if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? {
|
||||
self.bits_per_sample = val;
|
||||
self.samples = self.find_tag_unsigned(Tag::SamplesPerPixel)?.unwrap_or(1);
|
||||
|
||||
self.sample_format = match self.find_tag_unsigned_vec(Tag::SampleFormat)? {
|
||||
Some(vals) => {
|
||||
let sample_format: Vec<_> = vals
|
||||
.into_iter()
|
||||
.map(SampleFormat::from_u16_exhaustive)
|
||||
.collect();
|
||||
|
||||
// TODO: for now, only homogenous formats across samples are supported.
|
||||
if !sample_format.windows(2).all(|s| s[0] == s[1]) {
|
||||
return Err(TiffUnsupportedError::UnsupportedSampleFormat(
|
||||
self.sample_format.clone(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
|
||||
}
|
||||
|
||||
self.chunk_type =
|
||||
match (
|
||||
self.get_tag_u32(Tag::RowsPerStrip),
|
||||
self.get_tag_u32(Tag::TileWidth),
|
||||
self.get_tag_u32(Tag::TileLength),
|
||||
) {
|
||||
(Ok(_), Err(_), Err(_)) => ChunkType::Strip,
|
||||
(Err(_), Ok(_), Ok(_)) => ChunkType::Tile,
|
||||
// TODO: The spec says not to use both strip-oriented fields and tile-oriented fields.
|
||||
// We can relax this later if it becomes a problem
|
||||
_ => return Err(TiffError::FormatError(TiffFormatError::Format(
|
||||
String::from(
|
||||
"Neither strips nor tiles were found or both were used in the same file",
|
||||
),
|
||||
))),
|
||||
};
|
||||
sample_format
|
||||
}
|
||||
None => vec![SampleFormat::Uint],
|
||||
};
|
||||
|
||||
self.bits_per_sample = match self.samples {
|
||||
1 | 3 | 4 => self
|
||||
.find_tag_unsigned_vec(Tag::BitsPerSample)?
|
||||
.unwrap_or_else(|| vec![1]),
|
||||
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
|
||||
};
|
||||
|
||||
let ifd = self.ifd.as_ref().unwrap();
|
||||
self.chunk_type = match (
|
||||
ifd.contains_key(&Tag::StripByteCounts),
|
||||
ifd.contains_key(&Tag::StripOffsets),
|
||||
ifd.contains_key(&Tag::TileByteCounts),
|
||||
ifd.contains_key(&Tag::TileOffsets),
|
||||
) {
|
||||
(true, true, false, false) => ChunkType::Strip,
|
||||
(false, false, true, true) => ChunkType::Tile,
|
||||
(_, _, _, _) => {
|
||||
return Err(TiffError::FormatError(
|
||||
TiffFormatError::StripTileTagConflict,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -731,6 +738,15 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
self.byte_order
|
||||
}
|
||||
|
||||
fn read_ifd_offset_nonrepeating(&mut self) -> TiffResult<u64> {
|
||||
let result = self.read_ifd_offset()?;
|
||||
if self.seen_ifds.insert(result) {
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(TiffError::FormatError(TiffFormatError::CycleInOffsets))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_ifd_offset(&mut self) -> Result<u64, io::Error> {
|
||||
if self.bigtiff {
|
||||
@@ -889,7 +905,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
};
|
||||
dir.insert(tag, entry);
|
||||
}
|
||||
self.next_ifd = match self.read_ifd_offset()? {
|
||||
self.next_ifd = match self.read_ifd_offset_nonrepeating()? {
|
||||
0 => None,
|
||||
n => Some(n),
|
||||
};
|
||||
@@ -1119,6 +1135,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
length,
|
||||
buffer.len(),
|
||||
buffer.byte_len(),
|
||||
self.limits.intermediate_buffer_size,
|
||||
)?;
|
||||
|
||||
// Read into output buffer.
|
||||
@@ -1176,7 +1193,6 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
|
||||
let tile_decoder = self.tile_decoder.as_mut().unwrap();
|
||||
let line_samples = tile_decoder.result_width * self.bits_per_sample.len();
|
||||
let padding_buffer = &mut tile_decoder.padding_buffer;
|
||||
|
||||
let mut reader = Self::create_reader(
|
||||
&mut self.reader,
|
||||
@@ -1184,6 +1200,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
compressed_length,
|
||||
tile_samples,
|
||||
byte_len,
|
||||
self.limits.intermediate_buffer_size,
|
||||
)?;
|
||||
|
||||
for row in 0..(tile_length - padding_down) {
|
||||
@@ -1205,10 +1222,11 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
|
||||
let row = &mut buf[(row_start * byte_len)..(row_end * byte_len)];
|
||||
reader.read_exact(row)?;
|
||||
|
||||
// Skip horizontal padding
|
||||
// TODO: find a better way of skipping the padding
|
||||
if padding_right > 0 {
|
||||
reader.read_exact(padding_buffer)?
|
||||
let len = u64::try_from(padding_right_samples * byte_len)?;
|
||||
io::copy(&mut reader.by_ref().take(len), &mut io::sink())?;
|
||||
}
|
||||
|
||||
Self::fix_endianness(&mut buffer.subrange(row_start..row_end), self.byte_order);
|
||||
@@ -1227,17 +1245,20 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
compressed_length: u64,
|
||||
samples: usize, // Expected chunk length in samples
|
||||
byte_len: usize, // Byte length of the samples in result buffer
|
||||
intermediate_buffer_size: usize,
|
||||
) -> TiffResult<Box<dyn Read + 'r>> {
|
||||
Ok(match compression_method {
|
||||
CompressionMethod::None => Box::new(reader),
|
||||
CompressionMethod::LZW => Box::new(
|
||||
LZWReader::new(
|
||||
reader,
|
||||
usize::try_from(compressed_length)?,
|
||||
samples * byte_len,
|
||||
)?
|
||||
.1,
|
||||
),
|
||||
CompressionMethod::LZW => {
|
||||
let clen = usize::try_from(compressed_length)?;
|
||||
|
||||
if samples * byte_len > intermediate_buffer_size || clen > intermediate_buffer_size
|
||||
{
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
|
||||
Box::new(LZWReader::new(reader, clen, samples * byte_len)?.1)
|
||||
}
|
||||
CompressionMethod::PackBits => {
|
||||
Box::new(PackBitsReader::new(reader, usize::try_from(compressed_length)?)?.1)
|
||||
}
|
||||
@@ -1277,7 +1298,13 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
Ok((self.height + rows_per_strip - 1) / rows_per_strip)
|
||||
// rows_per_strip - 1 can never fail since we know it's at least 1
|
||||
let height = match self.height.checked_add(rows_per_strip - 1) {
|
||||
Some(h) => h,
|
||||
None => return Err(TiffError::IntSizeError),
|
||||
};
|
||||
|
||||
Ok(height / rows_per_strip)
|
||||
}
|
||||
|
||||
/// Number of tiles in image
|
||||
@@ -1309,6 +1336,14 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
let tile_width = usize::try_from(self.get_tag_u32(Tag::TileWidth)?)?;
|
||||
let tile_length = usize::try_from(self.get_tag_u32(Tag::TileLength)?)?;
|
||||
|
||||
if tile_width == 0 {
|
||||
return Err(TiffFormatError::InvalidTagValueType(Tag::TileWidth).into());
|
||||
}
|
||||
|
||||
if tile_length == 0 {
|
||||
return Err(TiffFormatError::InvalidTagValueType(Tag::TileLength).into());
|
||||
}
|
||||
|
||||
let tiles_across = (usize::try_from(self.width)? + tile_width - 1) / tile_width;
|
||||
let tiles_down = (usize::try_from(self.height)? + tile_length - 1) / tile_length;
|
||||
|
||||
@@ -1335,27 +1370,12 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_tile_decoder(
|
||||
&mut self,
|
||||
result_width: usize,
|
||||
buffer_byte_len: usize,
|
||||
) -> TiffResult<()> {
|
||||
let samples_per_pixel = self.bits_per_sample.len();
|
||||
|
||||
fn update_tile_decoder(&mut self, result_width: usize) -> TiffResult<()> {
|
||||
if self.tile_decoder.is_none() {
|
||||
let tile_attrs = self.tile_attributes.as_ref().unwrap();
|
||||
|
||||
let padding_buffer_size =
|
||||
tile_attrs.padding_right * samples_per_pixel * buffer_byte_len;
|
||||
if padding_buffer_size > self.limits.intermediate_buffer_size {
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
|
||||
self.tile_decoder = Some(TileDecodeState {
|
||||
current_tile: 0,
|
||||
tile_offsets: self.get_tag_u64_vec(Tag::TileOffsets)?,
|
||||
tile_bytes: self.get_tag_u64_vec(Tag::TileByteCounts)?,
|
||||
padding_buffer: vec![0; padding_buffer_size],
|
||||
result_width: 0, // needs to be updated for differently padded_tiles, see below
|
||||
})
|
||||
}
|
||||
@@ -1371,7 +1391,17 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
|
||||
let jpeg_tables: Option<Vec<u8>> = match self.find_tag(Tag::JPEGTables) {
|
||||
Ok(None) => None,
|
||||
Ok(_) => Some(self.get_tag_u8_vec(Tag::JPEGTables)?),
|
||||
Ok(_) => {
|
||||
let vec = self.get_tag_u8_vec(Tag::JPEGTables)?;
|
||||
|
||||
if vec.len() < 2 {
|
||||
return Err(TiffError::FormatError(
|
||||
TiffFormatError::InvalidTagValueType(Tag::JPEGTables),
|
||||
));
|
||||
}
|
||||
|
||||
Some(vec)
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
@@ -1386,11 +1416,26 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
));
|
||||
}
|
||||
|
||||
if offsets[0] as usize > self.limits.intermediate_buffer_size
|
||||
|| bytes
|
||||
.iter()
|
||||
.any(|&x| x as usize > self.limits.intermediate_buffer_size)
|
||||
{
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
|
||||
let mut res_img = Vec::with_capacity(offsets[0] as usize);
|
||||
|
||||
for (idx, offset) in offsets.iter().enumerate() {
|
||||
self.goto_offset(*offset)?;
|
||||
let jpeg_reader = JpegReader::new(&mut self.reader, bytes[idx], &jpeg_tables)?;
|
||||
let length = bytes[idx];
|
||||
|
||||
if jpeg_tables.is_some() && length < 2 {
|
||||
return Err(TiffError::FormatError(
|
||||
TiffFormatError::InvalidTagValueType(Tag::JPEGTables),
|
||||
));
|
||||
}
|
||||
let jpeg_reader = JpegReader::new(&mut self.reader, length, &jpeg_tables)?;
|
||||
let mut decoder = jpeg::Decoder::new(jpeg_reader);
|
||||
|
||||
match decoder.decode() {
|
||||
@@ -1442,10 +1487,18 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
let sized_width = usize::try_from(self.width)?;
|
||||
let sized_height = usize::try_from(self.height)?;
|
||||
|
||||
// Ignore potential vertical padding on the bottommost strip
|
||||
let strip_height = cmp::min(rows_per_strip, sized_height - index * rows_per_strip);
|
||||
let strip_height_without_padding = index
|
||||
.checked_mul(rows_per_strip)
|
||||
.and_then(|x| sized_height.checked_sub(x))
|
||||
.ok_or(TiffError::IntSizeError)?;
|
||||
|
||||
let buffer_size = sized_width * strip_height * self.bits_per_sample.len();
|
||||
// Ignore potential vertical padding on the bottommost strip
|
||||
let strip_height = rows_per_strip.min(strip_height_without_padding);
|
||||
|
||||
let buffer_size = sized_width
|
||||
.checked_mul(strip_height)
|
||||
.and_then(|x| x.checked_mul(self.bits_per_sample.len()))
|
||||
.ok_or(TiffError::LimitsExceeded)?;
|
||||
|
||||
if buffer.len() < buffer_size {
|
||||
return Err(TiffError::FormatError(
|
||||
@@ -1536,7 +1589,13 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
}
|
||||
|
||||
fn result_buffer(&self, width: usize, height: usize) -> TiffResult<DecodingResult> {
|
||||
let buffer_size = width * height * self.bits_per_sample.len();
|
||||
let buffer_size = match width
|
||||
.checked_mul(height)
|
||||
.and_then(|x| x.checked_mul(self.bits_per_sample.len()))
|
||||
{
|
||||
Some(s) => s,
|
||||
None => return Err(TiffError::LimitsExceeded),
|
||||
};
|
||||
|
||||
let max_sample_bits = self.bits_per_sample.iter().cloned().max().unwrap_or(8);
|
||||
match self.sample_format.first().unwrap_or(&SampleFormat::Uint) {
|
||||
@@ -1605,7 +1664,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
let tile_length = tile_attrs.tile_length - padding_down;
|
||||
|
||||
let mut result = self.result_buffer(tile_width, tile_length)?;
|
||||
self.update_tile_decoder(tile_width, result.as_buffer(0).byte_len())?;
|
||||
self.update_tile_decoder(tile_width)?;
|
||||
|
||||
self.read_tile_to_buffer(&mut result.as_buffer(0), tile)?;
|
||||
|
||||
@@ -1619,7 +1678,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
let mut result = self.result_buffer(width, usize::try_from(self.height)?)?;
|
||||
|
||||
self.init_tile_attributes()?;
|
||||
self.update_tile_decoder(width, result.as_buffer(0).byte_len())?;
|
||||
self.update_tile_decoder(width)?;
|
||||
|
||||
let tile_attrs = self.tile_attributes.as_ref().unwrap();
|
||||
let tiles_across = tile_attrs.tiles_across;
|
||||
@@ -1638,8 +1697,13 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
let rows_per_strip =
|
||||
usize::try_from(self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height))?;
|
||||
|
||||
let samples_per_strip =
|
||||
usize::try_from(self.width)? * rows_per_strip * self.bits_per_sample.len();
|
||||
let samples_per_strip = match usize::try_from(self.width)?
|
||||
.checked_mul(rows_per_strip)
|
||||
.and_then(|x| x.checked_mul(self.bits_per_sample.len()))
|
||||
{
|
||||
Some(s) => s,
|
||||
None => return Err(TiffError::LimitsExceeded),
|
||||
};
|
||||
|
||||
let mut result =
|
||||
self.result_buffer(usize::try_from(self.width)?, usize::try_from(self.height)?)?;
|
||||
|
||||
@@ -225,6 +225,18 @@ impl JpegReader {
|
||||
|
||||
match jpeg_tables {
|
||||
Some(tables) => {
|
||||
assert!(
|
||||
tables.len() >= 2,
|
||||
"jpeg_tables, if given, must be at least 2 bytes long. Got {:?}",
|
||||
tables
|
||||
);
|
||||
|
||||
assert!(
|
||||
length >= 2,
|
||||
"if jpeg_tables is given, length must be at least 2 bytes long, got {}",
|
||||
length
|
||||
);
|
||||
|
||||
let mut jpeg_data = tables.clone();
|
||||
let truncated_length = jpeg_data.len() - 2;
|
||||
jpeg_data.truncate(truncated_length);
|
||||
|
||||
@@ -20,6 +20,14 @@ impl ColorType for Gray8 {
|
||||
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
|
||||
}
|
||||
|
||||
pub struct GrayI8;
|
||||
impl ColorType for GrayI8 {
|
||||
type Inner = i8;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[8];
|
||||
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Int];
|
||||
}
|
||||
|
||||
pub struct Gray16;
|
||||
impl ColorType for Gray16 {
|
||||
type Inner = u16;
|
||||
@@ -28,6 +36,14 @@ impl ColorType for Gray16 {
|
||||
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
|
||||
}
|
||||
|
||||
pub struct GrayI16;
|
||||
impl ColorType for GrayI16 {
|
||||
type Inner = i16;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[16];
|
||||
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Int];
|
||||
}
|
||||
|
||||
pub struct Gray32;
|
||||
impl ColorType for Gray32 {
|
||||
type Inner = u32;
|
||||
@@ -36,6 +52,14 @@ impl ColorType for Gray32 {
|
||||
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
|
||||
}
|
||||
|
||||
pub struct GrayI32;
|
||||
impl ColorType for GrayI32 {
|
||||
type Inner = i32;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[32];
|
||||
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Int];
|
||||
}
|
||||
|
||||
pub struct Gray32Float;
|
||||
impl ColorType for Gray32Float {
|
||||
type Inner = f32;
|
||||
@@ -52,6 +76,14 @@ impl ColorType for Gray64 {
|
||||
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
|
||||
}
|
||||
|
||||
pub struct GrayI64;
|
||||
impl ColorType for GrayI64 {
|
||||
type Inner = i64;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[64];
|
||||
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Int];
|
||||
}
|
||||
|
||||
pub struct Gray64Float;
|
||||
impl ColorType for Gray64Float {
|
||||
type Inner = f64;
|
||||
|
||||
@@ -63,6 +63,8 @@ pub enum TiffFormatError {
|
||||
SignedIntegerExpected(Value),
|
||||
Format(String),
|
||||
RequiredTagEmpty(Tag),
|
||||
StripTileTagConflict,
|
||||
CycleInOffsets,
|
||||
#[doc(hidden)]
|
||||
/// Do not match against this variant. It may get removed.
|
||||
__NonExhaustive,
|
||||
@@ -113,6 +115,8 @@ impl fmt::Display for TiffFormatError {
|
||||
}
|
||||
Format(ref val) => write!(fmt, "Invalid format: {:?}.", val),
|
||||
RequiredTagEmpty(ref val) => write!(fmt, "Required tag {:?} was empty.", val),
|
||||
StripTileTagConflict => write!(fmt, "File should contain either (StripByteCounts and StripOffsets) or (TileByteCounts and TileOffsets), other combination was found."),
|
||||
CycleInOffsets => write!(fmt, "File contained a cycle in the list of IFDs"),
|
||||
__NonExhaustive => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,3 +284,242 @@ fn test_tiled_incremental() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_zero() {
|
||||
use tiff::{TiffError, TiffFormatError};
|
||||
|
||||
let image = [
|
||||
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 40, 1, 0, 0,
|
||||
0, 158, 0, 0, 251, 3, 1, 3, 0, 1, 0, 0, 0, 1, 0, 0, 39, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 0, 1, 0, 0, 0, 158, 0, 0, 251, 67, 1, 3, 0,
|
||||
1, 0, 0, 0, 40, 0, 0, 0, 66, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 178, 178, 178, 178,
|
||||
178, 178, 178,
|
||||
];
|
||||
|
||||
let err = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
|
||||
|
||||
match err {
|
||||
TiffError::FormatError(TiffFormatError::StripTileTagConflict) => {}
|
||||
unexpected => panic!("Unexpected error {}", unexpected),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_too_many_value_bytes() {
|
||||
let image = [
|
||||
73, 73, 43, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 8, 0, 0, 0,
|
||||
23, 0, 12, 0, 0, 65, 4, 0, 1, 6, 0, 0, 1, 16, 0, 1, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0,
|
||||
0, 0, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, 59, 73, 84, 186, 202, 83, 240, 66, 1, 53, 22, 56, 47,
|
||||
0, 0, 0, 0, 0, 0, 1, 222, 4, 0, 58, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 4, 0, 0, 100, 0,
|
||||
0, 89, 89, 89, 89, 89, 89, 89, 89, 96, 1, 20, 89, 89, 89, 89, 18,
|
||||
];
|
||||
|
||||
let error = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
|
||||
|
||||
match error {
|
||||
tiff::TiffError::LimitsExceeded => {}
|
||||
unexpected => panic!("Unexpected error {}", unexpected),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_strip_count() {
|
||||
let image = [
|
||||
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 100, 0, 0, 0, 1, 1, 4, 0, 1, 0, 0,
|
||||
0, 158, 0, 0, 251, 3, 1, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 1, 3, 0, 0, 0, 0, 0, 246, 16, 0, 0, 22, 1, 4, 0, 1,
|
||||
0, 0, 0, 40, 0, 251, 255, 23, 1, 4, 0, 1, 0, 0, 0, 48, 178, 178, 178, 178, 178, 178, 178,
|
||||
178, 178, 178,
|
||||
];
|
||||
|
||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
||||
|
||||
let err = decoder.read_image().unwrap_err();
|
||||
|
||||
match err {
|
||||
tiff::TiffError::IntSizeError => {}
|
||||
unexpected => panic!("Unexpected error {}", unexpected),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_too_big_buffer_size() {
|
||||
let image = [
|
||||
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 99, 255, 255, 254, 1, 1, 4, 0, 1,
|
||||
0, 0, 0, 158, 0, 0, 251, 3, 1, 3, 255, 254, 255, 255, 0, 1, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 17, 1, 4, 0, 9, 0, 0, 0, 0, 0, 0, 0, 2, 1, 3, 0, 2, 0, 0, 0, 63, 0, 0, 0,
|
||||
22, 1, 4, 0, 1, 0, 0, 0, 44, 0, 0, 0, 23, 1, 4, 0, 0, 0, 0, 0, 0, 0, 2, 1, 3, 1, 0, 178,
|
||||
178,
|
||||
];
|
||||
|
||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
||||
|
||||
let err = decoder.read_image().unwrap_err();
|
||||
|
||||
match err {
|
||||
tiff::TiffError::LimitsExceeded => {}
|
||||
unexpected => panic!("Unexpected error {}", unexpected),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stripped_image_overflow() {
|
||||
let image = [
|
||||
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 100, 0, 0, 148, 1, 1, 4, 0, 1, 0,
|
||||
0, 0, 158, 0, 0, 251, 3, 1, 3, 255, 254, 255, 255, 0, 1, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 17, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 1, 3, 0, 2, 0, 0, 0, 63, 0, 0, 0, 22,
|
||||
1, 4, 0, 1, 0, 0, 0, 44, 0, 248, 255, 23, 1, 4, 0, 1, 0, 0, 0, 178, 178, 178, 0, 1, 178,
|
||||
178, 178,
|
||||
];
|
||||
|
||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
||||
|
||||
let err = decoder.read_image().unwrap_err();
|
||||
|
||||
match err {
|
||||
tiff::TiffError::LimitsExceeded => {}
|
||||
unexpected => panic!("Unexpected error {}", unexpected),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oom() {
|
||||
let image = [
|
||||
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 40, 1, 0, 0,
|
||||
0, 158, 0, 0, 251, 3, 1, 3, 0, 1, 0, 0, 0, 7, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0,
|
||||
17, 1, 4, 0, 1, 0, 0, 0, 3, 77, 0, 0, 1, 1, 3, 0, 1, 0, 0, 0, 3, 128, 0, 0, 22, 1, 4, 0, 1,
|
||||
0, 0, 0, 40, 0, 0, 0, 23, 1, 4, 0, 1, 0, 0, 0, 178, 48, 178, 178, 178, 178, 162, 178,
|
||||
];
|
||||
|
||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
||||
|
||||
let err = decoder.read_image().unwrap_err();
|
||||
|
||||
match err {
|
||||
tiff::TiffError::LimitsExceeded => {}
|
||||
unexpected => panic!("Unexpected error {}", unexpected),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oom_2() {
|
||||
let image = [
|
||||
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 40, 1, 0, 0,
|
||||
0, 158, 0, 0, 251, 3, 1, 3, 0, 1, 0, 0, 0, 5, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 0, 1, 0, 0, 0, 3, 128, 0, 0, 22, 1, 4, 0, 1,
|
||||
0, 0, 0, 40, 0, 0, 0, 23, 1, 4, 0, 1, 0, 0, 0, 48, 178, 178, 178, 0, 1, 0, 13, 13,
|
||||
];
|
||||
|
||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
||||
|
||||
let err = decoder.read_image().unwrap_err();
|
||||
|
||||
match err {
|
||||
tiff::TiffError::LimitsExceeded => {}
|
||||
unexpected => panic!("Unexpected error {}", unexpected),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_jpeg_tag() {
|
||||
use tiff::tags;
|
||||
use tiff::{TiffError, TiffFormatError};
|
||||
|
||||
let image = [
|
||||
73, 73, 42, 0, 8, 0, 0, 0, 15, 0, 0, 254, 44, 1, 0, 0, 0, 0, 0, 32, 0, 0, 0, 1, 4, 0, 1, 0,
|
||||
0, 0, 0, 1, 0, 0, 91, 1, 1, 0, 0, 0, 0, 0, 242, 4, 0, 0, 0, 22, 0, 56, 77, 0, 77, 1, 0, 0,
|
||||
73, 42, 0, 1, 4, 0, 1, 0, 0, 0, 4, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 158, 0, 0, 251, 3, 1,
|
||||
3, 0, 1, 0, 0, 0, 7, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, 17, 1, 4, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 4, 61, 1, 18, 0, 1, 0, 0, 0, 202, 0, 0, 0, 17,
|
||||
1, 100, 0, 129, 0, 0, 0, 0, 0, 0, 0, 232, 254, 252, 255, 254, 255, 255, 255, 1, 29, 0, 0,
|
||||
22, 1, 3, 0, 1, 0, 0, 0, 16, 0, 0, 0, 23, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 123, 73, 254, 0,
|
||||
73,
|
||||
];
|
||||
|
||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
||||
|
||||
let err = decoder.read_image().unwrap_err();
|
||||
|
||||
match err {
|
||||
TiffError::FormatError(TiffFormatError::InvalidTagValueType(tags::Tag::JPEGTables)) => {}
|
||||
unexpected => panic!("Unexpected error {}", unexpected),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_jpeg_tag_2() {
|
||||
use tiff::tags;
|
||||
use tiff::{TiffError, TiffFormatError};
|
||||
|
||||
let image = [
|
||||
73, 73, 42, 0, 8, 0, 0, 0, 16, 0, 254, 0, 4, 0, 1, 0, 0, 0, 0, 0, 0, 242, 0, 1, 4, 0, 1, 0,
|
||||
0, 0, 0, 129, 16, 0, 1, 1, 4, 0, 1, 0, 0, 0, 214, 0, 0, 248, 253, 1, 3, 0, 1, 0, 0, 0, 64,
|
||||
0, 0, 0, 3, 1, 3, 0, 1, 0, 0, 0, 7, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 1, 0, 0, 64, 14, 1, 0,
|
||||
2, 0, 0, 148, 0, 206, 0, 0, 0, 17, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 0, 0, 0, 22, 1, 4, 0, 17, 0, 0, 201, 1, 0, 0, 0, 23, 1, 2, 0, 20, 0, 0, 0, 194, 0, 0, 0,
|
||||
91, 1, 7, 0, 5, 0, 0, 0, 64, 0, 0, 0, 237, 254, 65, 255, 255, 255, 255, 255, 1, 0, 0, 0,
|
||||
22, 1, 4, 0, 1, 0, 0, 0, 42, 0, 0, 0, 23, 1, 255, 255, 255, 255, 255, 36, 36, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 36, 73, 73, 0, 42, 36, 36, 36, 36, 0, 0, 8, 0,
|
||||
];
|
||||
|
||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
||||
|
||||
let err = decoder.read_image().unwrap_err();
|
||||
|
||||
match err {
|
||||
TiffError::FormatError(TiffFormatError::InvalidTagValueType(tags::Tag::JPEGTables)) => {}
|
||||
unexpected => panic!("Unexpected error {}", unexpected),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_images_strip_height() {
|
||||
use tiff::TiffError;
|
||||
|
||||
let image = [
|
||||
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 2, 0, 0, 0, 61, 1, 9, 0, 46, 22,
|
||||
128, 0, 0, 0, 0, 1, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 17, 1, 4, 0, 27, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 3, 0, 1, 0, 0, 0, 17, 1, 0, 231, 22, 1, 1, 0, 1, 0, 0, 0, 130, 0, 0, 0, 23, 1, 4,
|
||||
0, 14, 0, 0, 0, 0, 0, 0, 0, 133, 133, 133, 77, 77, 77, 0, 0, 22, 128, 0, 255, 255, 255,
|
||||
255, 255,
|
||||
];
|
||||
|
||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
||||
|
||||
let _ = decoder.read_image().unwrap();
|
||||
let err = decoder.read_image().unwrap_err();
|
||||
|
||||
match err {
|
||||
TiffError::IntSizeError => {}
|
||||
unexpected => panic!("Unexpected error {}", unexpected),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timeout() {
|
||||
use tiff::{TiffError, TiffFormatError};
|
||||
|
||||
let image = [
|
||||
73, 73, 42, 0, 8, 0, 0, 0, 16, 0, 254, 0, 4, 0, 1, 68, 0, 0, 0, 2, 0, 32, 254, 252, 0, 109,
|
||||
0, 129, 0, 0, 0, 32, 0, 58, 0, 1, 4, 0, 1, 0, 6, 0, 0, 0, 8, 0, 0, 1, 73, 73, 42, 0, 8, 0,
|
||||
0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 21, 0, 0, 0, 61, 1, 255, 128, 9, 0, 0, 8, 0, 1, 113, 2,
|
||||
3, 1, 3, 0, 1, 0, 0, 0, 5, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 112, 0, 0, 36, 0, 0,
|
||||
0, 112, 56, 200, 0, 5, 0, 0, 64, 0, 0, 1, 0, 4, 0, 0, 0, 2, 0, 6, 1, 3, 0, 1, 0, 0, 0, 0,
|
||||
0, 0, 4, 17, 1, 1, 0, 93, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 3, 6, 0, 231, 22, 1,
|
||||
1, 0, 1, 0, 0, 0, 2, 64, 118, 36, 23, 1, 1, 0, 43, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 4, 0, 8,
|
||||
0, 0, 73, 73, 42, 0, 8, 0, 0, 0, 0, 0, 32,
|
||||
];
|
||||
|
||||
let error = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
|
||||
|
||||
match error {
|
||||
TiffError::FormatError(TiffFormatError::CycleInOffsets) => {}
|
||||
e => panic!("Unexpected error {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_rows_per_strip() {
|
||||
test_image_sum_u8("no_rows_per_strip.tiff", ColorType::RGB(8), 99448840);
|
||||
}
|
||||
Binary file not shown.
Reference in new issue
Block a user