diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index 9bc5a5079..037f648f0 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -5,6 +5,7 @@ using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
+using System.IO.Compression;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
@@ -175,11 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.InitializeImage(metadata, out image);
}
- using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk))
- {
- deframeStream.AllocateNewBytes(chunk.Length);
- this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetadata);
- }
+ this.ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata);
break;
case PngChunkType.Palette:
@@ -465,19 +462,25 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Reads the scanlines within the image.
///
/// The pixel format.
- /// The containing data.
+ /// The png chunk containing the compressed scanline data.
/// The pixel data.
/// The png metadata
- private void ReadScanlines(Stream dataStream, ImageFrame image, PngMetadata pngMetadata)
+ private void ReadScanlines(PngChunk chunk, ImageFrame image, PngMetadata pngMetadata)
where TPixel : struct, IPixel
{
- if (this.header.InterlaceMethod == PngInterlaceMode.Adam7)
- {
- this.DecodeInterlacedPixelData(dataStream, image, pngMetadata);
- }
- else
+ using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk))
{
- this.DecodePixelData(dataStream, image, pngMetadata);
+ deframeStream.AllocateNewBytes(chunk.Length, true);
+ DeflateStream dataStream = deframeStream.CompressedStream;
+
+ if (this.header.InterlaceMethod == PngInterlaceMode.Adam7)
+ {
+ this.DecodeInterlacedPixelData(dataStream, image, pngMetadata);
+ }
+ else
+ {
+ this.DecodePixelData(dataStream, image, pngMetadata);
+ }
}
}
@@ -924,7 +927,11 @@ namespace SixLabors.ImageSharp.Formats.Png
}
ReadOnlySpan compressedData = data.Slice(zeroIndex + 2);
- metadata.TextData.Add(new PngTextData(name, this.UncompressTextData(compressedData, PngConstants.Encoding), string.Empty, string.Empty));
+
+ if (this.TryUncompressTextData(compressedData, PngConstants.Encoding, out string uncompressed))
+ {
+ metadata.TextData.Add(new PngTextData(name, uncompressed, string.Empty, string.Empty));
+ }
}
///
@@ -987,7 +994,11 @@ namespace SixLabors.ImageSharp.Formats.Png
if (compressionFlag == 1)
{
ReadOnlySpan compressedData = data.Slice(dataStartIdx);
- metadata.TextData.Add(new PngTextData(keyword, this.UncompressTextData(compressedData, PngConstants.TranslatedEncoding), language, translatedKeyword));
+
+ if (this.TryUncompressTextData(compressedData, PngConstants.TranslatedEncoding, out string uncompressed))
+ {
+ metadata.TextData.Add(new PngTextData(keyword, uncompressed, language, translatedKeyword));
+ }
}
else
{
@@ -1001,13 +1012,19 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// Compressed text data bytes.
/// The string encoding to use.
- /// A string.
- private string UncompressTextData(ReadOnlySpan compressedData, Encoding encoding)
+ /// The uncompressed value.
+ /// The .
+ private bool TryUncompressTextData(ReadOnlySpan compressedData, Encoding encoding, out string value)
{
using (var memoryStream = new MemoryStream(compressedData.ToArray()))
- using (var inflateStream = new ZlibInflateStream(memoryStream, () => 0))
+ using (var inflateStream = new ZlibInflateStream(memoryStream))
{
- inflateStream.AllocateNewBytes(compressedData.Length);
+ if (!inflateStream.AllocateNewBytes(compressedData.Length, false))
+ {
+ value = null;
+ return false;
+ }
+
var uncompressedBytes = new List();
// Note: this uses the a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here.
@@ -1018,7 +1035,8 @@ namespace SixLabors.ImageSharp.Formats.Png
bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
}
- return encoding.GetString(uncompressedBytes.ToArray());
+ value = encoding.GetString(uncompressedBytes.ToArray());
+ return true;
}
}
diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
index 405eeafeb..e4645c44a 100644
--- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
@@ -20,14 +20,14 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private static readonly byte[] ChecksumBuffer = new byte[4];
///
- /// The inner raw memory stream
+ /// A default delegate to get more data from the inner stream.
///
- private readonly Stream innerStream;
+ private static readonly Func GetDataNoOp = () => 0;
///
- /// The compressed stream sitting over the top of the deframer
+ /// The inner raw memory stream
///
- private DeflateStream compressedStream;
+ private readonly Stream innerStream;
///
/// A value indicating whether this instance of the given entity has been disposed.
@@ -55,8 +55,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
///
/// Initializes a new instance of the class.
///
- /// The inner raw stream
- /// A delegate to get more data from the inner stream
+ /// The inner raw stream.
+ public ZlibInflateStream(Stream innerStream)
+ : this(innerStream, GetDataNoOp)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The inner raw stream.
+ /// A delegate to get more data from the inner stream.
public ZlibInflateStream(Stream innerStream, Func getData)
{
this.innerStream = innerStream;
@@ -76,31 +85,32 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
public override long Length => throw new NotSupportedException();
///
- public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
+ public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
///
/// Gets the compressed stream over the deframed inner stream
///
- public DeflateStream CompressedStream => this.compressedStream;
+ public DeflateStream CompressedStream { get; private set; }
///
/// Adds new bytes from a frame found in the original stream
///
/// blabla
- public void AllocateNewBytes(int bytes)
+ /// Whether the chunk to be inflated is a critical chunk.
+ /// The .
+ public bool AllocateNewBytes(int bytes, bool isCriticalChunk)
{
this.currentDataRemaining = bytes;
- if (this.compressedStream is null)
+ if (this.CompressedStream is null)
{
- this.InitializeInflateStream();
+ return this.InitializeInflateStream(isCriticalChunk);
}
+
+ return true;
}
///
- public override void Flush()
- {
- throw new NotSupportedException();
- }
+ public override void Flush() => throw new NotSupportedException();
///
public override int ReadByte()
@@ -182,10 +192,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
if (disposing)
{
// dispose managed resources
- if (this.compressedStream != null)
+ if (this.CompressedStream != null)
{
- this.compressedStream.Dispose();
- this.compressedStream = null;
+ this.CompressedStream.Dispose();
+ this.CompressedStream = null;
}
}
@@ -197,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.isDisposed = true;
}
- private void InitializeInflateStream()
+ private bool InitializeInflateStream(bool isCriticalChunk)
{
// Read the zlib header : http://tools.ietf.org/html/rfc1950
// CMF(Compression Method and flags)
@@ -215,7 +225,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.currentDataRemaining -= 2;
if (cmf == -1 || flag == -1)
{
- return;
+ return false;
}
if ((cmf & 0x0F) == 8)
@@ -225,14 +235,28 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
if (cinfo > 7)
{
- // Values of CINFO above 7 are not allowed in RFC1950.
- // CINFO is not defined in this specification for CM not equal to 8.
- throw new ImageFormatException($"Invalid window size for ZLIB header: cinfo={cinfo}");
+ if (isCriticalChunk)
+ {
+ // Values of CINFO above 7 are not allowed in RFC1950.
+ // 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;
+ }
}
}
else
{
- throw new ImageFormatException($"Bad method for ZLIB header: cmf={cmf}");
+ if (isCriticalChunk)
+ {
+ throw new ImageFormatException($"Bad method for ZLIB header: cmf={cmf}");
+ }
+ else
+ {
+ return false;
+ }
}
// The preset dictionary.
@@ -246,7 +270,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
// Initialize the deflate Stream.
- this.compressedStream = new DeflateStream(this, CompressionMode.Decompress, true);
+ this.CompressedStream = new DeflateStream(this, CompressionMode.Decompress, true);
+
+ return true;
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
index 2e9fd7481..91b1ef2c1 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
@@ -40,7 +40,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
TestImages.Png.GrayAlpha8Bit,
TestImages.Png.Gray1BitTrans,
TestImages.Png.Bad.ZlibOverflow,
- TestImages.Png.Bad.ZlibOverflow2
+ TestImages.Png.Bad.ZlibOverflow2,
+ TestImages.Png.Bad.ZlibZtxtBadHeader,
};
public static readonly string[] TestImages48Bpp =
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index e95ce0907..163d09bdd 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -90,6 +90,7 @@ namespace SixLabors.ImageSharp.Tests
public const string CorruptedChunk = "Png/big-corrupted-chunk.png";
public const string ZlibOverflow = "Png/zlib-overflow.png";
public const string ZlibOverflow2 = "Png/zlib-overflow2.png";
+ public const string ZlibZtxtBadHeader = "Png/zlib-ztxt-bad-header.png";
}
public static readonly string[] All =
diff --git a/tests/Images/Input/Png/zlib-ztxt-bad-header.png b/tests/Images/Input/Png/zlib-ztxt-bad-header.png
new file mode 100644
index 000000000..0eb37aab8
--- /dev/null
+++ b/tests/Images/Input/Png/zlib-ztxt-bad-header.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ce623255656921d491b5c389cd46931fbd6024575b87522c55d67a496dd761f0
+size 22781