Skip to content

Commit

Permalink
Fix #1004
Browse files Browse the repository at this point in the history
  • Loading branch information
JimBobSquarePants committed Sep 8, 2019
1 parent ceff1cc commit dbc5c76
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 17 deletions.
38 changes: 30 additions & 8 deletions src/ImageSharp/Formats/Png/PngDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,18 @@ public Image<TPixel> Decode<TPixel>(Stream stream)
this.InitializeImage(metadata, out image);
}

using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk))
var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk);
try
{
deframeStream.AllocateNewBytes(chunk.Length);
deframeStream.AllocateNewBytes(chunk.Length, true);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetadata);
}
finally
{
// If an invalid Zlib stream is discovered the decoder will throw an exception
// due to the critical nature of the data chunk.
deframeStream.Dispose();
}

break;
case PngChunkType.Palette:
Expand Down Expand Up @@ -924,7 +931,11 @@ private void ReadCompressedTextChunk(PngMetadata metadata, ReadOnlySpan<byte> da
}

ReadOnlySpan<byte> 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));
}
}

/// <summary>
Expand Down Expand Up @@ -987,7 +998,11 @@ private void ReadInternationalTextChunk(PngMetadata metadata, ReadOnlySpan<byte>
if (compressionFlag == 1)
{
ReadOnlySpan<byte> 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
{
Expand All @@ -1001,13 +1016,19 @@ private void ReadInternationalTextChunk(PngMetadata metadata, ReadOnlySpan<byte>
/// </summary>
/// <param name="compressedData">Compressed text data bytes.</param>
/// <param name="encoding">The string encoding to use.</param>
/// <returns>A string.</returns>
private string UncompressTextData(ReadOnlySpan<byte> compressedData, Encoding encoding)
/// <param name="value">The uncompressed value.</param>
/// <returns>The <see cref="bool"/>.</returns>
private bool TryUncompressTextData(ReadOnlySpan<byte> compressedData, Encoding encoding, out string value)
{
using (var memoryStream = new MemoryStream(compressedData.ToArray()))
using (var inflateStream = new ZlibInflateStream(memoryStream, () => 0))
{
inflateStream.AllocateNewBytes(compressedData.Length);
if (!inflateStream.AllocateNewBytes(compressedData.Length, false))
{
value = null;
return false;
}

var uncompressedBytes = new List<byte>();

// Note: this uses the a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here.
Expand All @@ -1018,7 +1039,8 @@ private string UncompressTextData(ReadOnlySpan<byte> compressedData, Encoding en
bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
}

return encoding.GetString(uncompressedBytes.ToArray());
value = encoding.GetString(uncompressedBytes.ToArray());
return true;
}
}

Expand Down
36 changes: 28 additions & 8 deletions src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,17 @@ public ZlibInflateStream(Stream innerStream, Func<int> getData)
/// Adds new bytes from a frame found in the original stream
/// </summary>
/// <param name="bytes">blabla</param>
public void AllocateNewBytes(int bytes)
/// <param name="isCriticalChunk">Whether the chunk to be inflated is a critical chunk.</param>
/// <returns>The <see cref="bool"/>.</returns>
public bool AllocateNewBytes(int bytes, bool isCriticalChunk)
{
this.currentDataRemaining = bytes;
if (this.compressedStream is null)
{
this.InitializeInflateStream();
return this.InitializeInflateStream(isCriticalChunk);
}

return true;
}

/// <inheritdoc/>
Expand Down Expand Up @@ -197,7 +201,7 @@ protected override void Dispose(bool disposing)
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)
Expand All @@ -215,7 +219,7 @@ private void InitializeInflateStream()
this.currentDataRemaining -= 2;
if (cmf == -1 || flag == -1)
{
return;
return false;
}

if ((cmf & 0x0F) == 8)
Expand All @@ -225,14 +229,28 @@ private void InitializeInflateStream()

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.
Expand All @@ -247,6 +265,8 @@ private void InitializeInflateStream()

// Initialize the deflate Stream.
this.compressedStream = new DeflateStream(this, CompressionMode.Decompress, true);

return true;
}
}
}
3 changes: 2 additions & 1 deletion tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ public partial class PngDecoderTests
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 =
Expand Down
1 change: 1 addition & 0 deletions tests/ImageSharp.Tests/TestImages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public static class Bad
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 =
Expand Down
Binary file added tests/Images/Input/Png/zlib-ztxt-bad-header.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit dbc5c76

Please sign in to comment.