diff --git a/src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs
index f4b0543b84..bb97ff79eb 100644
--- a/src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs
+++ b/src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs
@@ -161,6 +161,11 @@ public override int Read(byte[] buffer, int offset, int count)
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 @@ public override int Read(byte[] buffer, int offset, int count)
}
///
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new NotSupportedException();
- }
+ public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
///
- public override void SetLength(long value)
- {
- throw new NotSupportedException();
- }
+ public override void SetLength(long value) => throw new NotSupportedException();
///
- 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();
///
protected override void Dispose(bool disposing)
@@ -245,22 +241,17 @@ private bool InitializeInflateStream(bool isCriticalChunk)
// 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 @@ private bool InitializeInflateStream(bool isCriticalChunk)
{
// 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;
}
diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs
index cce1d278cc..1d7592679c 100644
--- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs
+++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs
@@ -43,7 +43,7 @@ public DirectoryReader(Stream stream, MemoryAllocator allocator)
public IEnumerable 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 @@ public IEnumerable Read()
private static ByteOrder ReadByteOrder(Stream stream)
{
Span 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 @@ private IEnumerable ReadIfds(bool isBigTiff)
var readers = new List();
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 @@ private IEnumerable ReadIfds(bool isBigTiff)
}
}
+ if (this.nextIfdOffset >= reader.NextIfdOffset && reader.NextIfdOffset != 0)
+ {
+ TiffThrowHelper.ThrowImageFormatException("TIFF image contains circular directory offsets");
+ }
+
this.nextIfdOffset = reader.NextIfdOffset;
readers.Add(reader);
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
index 2a8cbcbf78..a4243c94b6 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
@@ -668,6 +668,18 @@ public void TiffDecoder_ThrowsException_WithTooManyDirectories(TestImage
}
});
+ [Theory]
+ [WithFile(JpegCompressedGray0000539558, PixelTypes.Rgba32)]
+ public void TiffDecoder_ThrowsException_WithCircular_IFD_Offsets(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ => Assert.Throws(
+ () =>
+ {
+ using (provider.GetImage(TiffDecoder))
+ {
+ }
+ });
+
[Theory]
[WithFileCollection(nameof(MultiframeTestImages), PixelTypes.Rgba32)]
public void DecodeMultiframe(TestImageProvider provider)
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 874a980881..bcae124659 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -916,6 +916,7 @@ public static class Tiff
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";
diff --git a/tests/Images/Input/Tiff/Issues/JpegCompressedGray-0000539558.tiff b/tests/Images/Input/Tiff/Issues/JpegCompressedGray-0000539558.tiff
new file mode 100644
index 0000000000..934bf3c9a3
--- /dev/null
+++ b/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