Repository: image-rs/image-tiff (rust)

Issue:
Panic decoding tiff malformed (again)
This appears to be different than https://github.com/image-rs/image-tiff/issues/168

A couple of my users have reported a crash in the image-tiff decoder:
```
thread '<unnamed>' panicked at 'range start index 16380 out of range for slice of length 9660', /Users/davidlaxer/clamav-0.105.1/libclamav_rust/.cargo/vendor/tiff/src/decoder/mod.rs:131:72
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fatal runtime error: failed to initiate panic, error 5
```

It appears to me that `DecodingResult::as_buffer()` is blindly trusting the start offset as being within the buffer.  My guess is that `start` value is read from the file and should be verified before using it. 

If you require it for testing, please let me know.  I don't have a copy of the file to test with or share (yet).  Hopefully I will get a sample soon. 

Pull request description:
Validate that images contain the proper number of strips
Fixes #171

Patch:
diff --git a/src/decoder/image.rs b/src/decoder/image.rs
index 00cdd65..c84d73f 100644
--- a/src/decoder/image.rs
+++ b/src/decoder/image.rs
@@ -208,7 +208,11 @@ impl Image {
                 strip_decoder = Some(StripDecodeState { rows_per_strip });
                 tile_attributes = None;
 
-                if chunk_offsets.len() != chunk_bytes.len() {
+                if chunk_offsets.len() != chunk_bytes.len()
+                    || rows_per_strip == 0
+                    || u32::try_from(chunk_offsets.len())?
+                        != height.saturating_sub(1) / rows_per_strip + 1
+                {
                     return Err(TiffError::FormatError(
                         TiffFormatError::InconsistentSizesEncountered,
                     ));


Test patch:
diff --git a/tests/decode_images.rs b/tests/decode_images.rs
index 6a262bf..5584f53 100644
--- a/tests/decode_images.rs
+++ b/tests/decode_images.rs
@@ -380,16 +380,7 @@ fn oom() {
         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::UnsupportedError(tiff::TiffUnsupportedError::InterpretationWithBits(
-            ..,
-        )) => {}
-        unexpected => panic!("Unexpected error {}", unexpected),
-    }
+    let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
 }
 
 #[test]
@@ -401,14 +392,7 @@ fn fuzzer_testcase4() {
         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::IoError(_) => {}
-        unexpected => panic!("Unexpected error {}", unexpected),
-    }
+    let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
 }
 
 #[test]
@@ -443,15 +427,7 @@ fn invalid_jpeg_tag_2() {
         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)) => {}
-        TiffError::LimitsExceeded => {}
-        unexpected => panic!("Unexpected error {}", unexpected),
-    }
+    let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
 }
 
 #[test]
diff --git a/tests/encode_images.rs b/tests/encode_images.rs
index 9fd9474..0feac97 100644
--- a/tests/encode_images.rs
+++ b/tests/encode_images.rs
@@ -94,6 +94,7 @@ fn test_encode_ifd() {
     {
         let mut tiff = TiffEncoder::new(&mut data).unwrap();
         let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
+        image_encoder.write_strip(&[1]);
         let encoder = image_encoder.encoder();
 
         // Use the "reusable" tags section as per the TIFF6 spec
@@ -320,6 +321,7 @@ fn test_multiple_byte() {
     {
         let mut tiff = TiffEncoder::new(&mut data).unwrap();
         let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
+        image_encoder.write_strip(&[1]);
         let encoder = image_encoder.encoder();
 
         encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap();
@@ -360,6 +362,7 @@ fn test_signed() {
     {
         let mut tiff = TiffEncoder::new(&mut data).unwrap();
         let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
+        image_encoder.write_strip(&[1]);
         let encoder = image_encoder.encoder();
 
         //Use the "reusable" tags section as per the TIFF6 spec