Browse Source

[release/2.1] Tiff decoding robustness improvements (#2550) (#2554)

* Tiff decoding robustness improvements (#2550)

* tiled Tiff is not supported in 2.1, delete TiffDecoder_CanDecode_TiledWithBadZlib

* also delete the files

* and the constant
pull/2717/head
Anton Firszov 2 years ago
committed by GitHub
parent
commit
749b1c04d7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 45
      src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs
  2. 16
      src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs
  3. 12
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  4. 1
      tests/ImageSharp.Tests/TestImages.cs
  5. 3
      tests/Images/Input/Tiff/Issues/JpegCompressedGray-0000539558.tiff

45
src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs

@ -161,6 +161,11 @@ namespace SixLabors.ImageSharp.Compression.Zlib
bytesToRead = Math.Min(count - totalBytesRead, this.currentDataRemaining);
this.currentDataRemaining -= bytesToRead;
bytesRead = this.innerStream.Read(buffer, offset, bytesToRead);
if (bytesRead == 0)
{
return totalBytesRead;
}
totalBytesRead += bytesRead;
}
@ -168,22 +173,13 @@ namespace SixLabors.ImageSharp.Compression.Zlib
}
/// <inheritdoc/>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
/// <inheritdoc/>
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void SetLength(long value) => throw new NotSupportedException();
/// <inheritdoc/>
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
/// <inheritdoc/>
protected override void Dispose(bool disposing)
@ -245,22 +241,17 @@ namespace SixLabors.ImageSharp.Compression.Zlib
// CINFO is not defined in this specification for CM not equal to 8.
throw new ImageFormatException($"Invalid window size for ZLIB header: cinfo={cinfo}");
}
else
{
return false;
}
return false;
}
}
else if (isCriticalChunk)
{
throw new ImageFormatException($"Bad method for ZLIB header: cmf={cmf}");
}
else
{
if (isCriticalChunk)
{
throw new ImageFormatException($"Bad method for ZLIB header: cmf={cmf}");
}
else
{
return false;
}
return false;
}
// The preset dictionary.
@ -269,7 +260,11 @@ namespace SixLabors.ImageSharp.Compression.Zlib
{
// We don't need this for inflate so simply skip by the next four bytes.
// https://tools.ietf.org/html/rfc1950#page-6
this.innerStream.Read(ChecksumBuffer, 0, 4);
if (this.innerStream.Read(ChecksumBuffer, 0, 4) != 4)
{
return false;
}
this.currentDataRemaining -= 4;
}

16
src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs

@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
public IEnumerable<ExifProfile> Read()
{
this.ByteOrder = ReadByteOrder(this.stream);
var headerReader = new HeaderReader(this.stream, this.ByteOrder);
HeaderReader headerReader = new(this.stream, this.ByteOrder);
headerReader.ReadFileHeader();
this.nextIfdOffset = headerReader.FirstIfdOffset;
@ -55,7 +55,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
private static ByteOrder ReadByteOrder(Stream stream)
{
Span<byte> headerBytes = stackalloc byte[2];
stream.Read(headerBytes);
if (stream.Read(headerBytes) != 2)
{
throw TiffThrowHelper.ThrowInvalidHeader();
}
if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian)
{
return ByteOrder.LittleEndian;
@ -74,7 +79,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
var readers = new List<EntryReader>();
while (this.nextIfdOffset != 0 && this.nextIfdOffset < (ulong)this.stream.Length)
{
var reader = new EntryReader(this.stream, this.ByteOrder, this.allocator);
EntryReader reader = new(this.stream, this.ByteOrder, this.allocator);
reader.ReadTags(isBigTiff, this.nextIfdOffset);
if (reader.BigValues.Count > 0)
@ -88,6 +93,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
}
}
if (this.nextIfdOffset >= reader.NextIfdOffset && reader.NextIfdOffset != 0)
{
TiffThrowHelper.ThrowImageFormatException("TIFF image contains circular directory offsets");
}
this.nextIfdOffset = reader.NextIfdOffset;
readers.Add(reader);

12
tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

@ -668,6 +668,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
}
});
[Theory]
[WithFile(JpegCompressedGray0000539558, PixelTypes.Rgba32)]
public void TiffDecoder_ThrowsException_WithCircular_IFD_Offsets<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
=> Assert.Throws<ImageFormatException>(
() =>
{
using (provider.GetImage(TiffDecoder))
{
}
});
[Theory]
[WithFileCollection(nameof(MultiframeTestImages), PixelTypes.Rgba32)]
public void DecodeMultiframe<TPixel>(TestImageProvider<TPixel> provider)

1
tests/ImageSharp.Tests/TestImages.cs

@ -916,6 +916,7 @@ namespace SixLabors.ImageSharp.Tests
public const string Issues1716Rgb161616BitLittleEndian = "Tiff/Issues/Issue1716.tiff";
public const string Issues1891 = "Tiff/Issues/Issue1891.tiff";
public const string Issues2123 = "Tiff/Issues/Issue2123.tiff";
public const string JpegCompressedGray0000539558 = "Tiff/Issues/JpegCompressedGray-0000539558.tiff";
public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff";
public const string SmallRgbLzw = "Tiff/rgb_small_lzw.tiff";

3
tests/Images/Input/Tiff/Issues/JpegCompressedGray-0000539558.tiff

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1f1ca630b5e46c7b5f21100fa8c0fbf27b79ca9da8cd95897667b64aedccf6e5
size 539558
Loading…
Cancel
Save