Browse Source

Merge pull request #3080 from SixLabors/bp/fixIssue3078

Add check for span length in parse PNG pHYs chunk
pull/3089/head
James Jackson-South 2 months ago
committed by GitHub
parent
commit
65dd8bd587
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 5
      src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs
  2. 3
      src/ImageSharp/Formats/Png/PngThrowHelper.cs
  3. 59
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs

5
src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs

@ -46,6 +46,11 @@ internal readonly struct PngPhysical
/// <returns>The parsed PhysicalChunkData.</returns> /// <returns>The parsed PhysicalChunkData.</returns>
public static PngPhysical Parse(ReadOnlySpan<byte> data) public static PngPhysical Parse(ReadOnlySpan<byte> data)
{ {
if (data.Length < 9)
{
PngThrowHelper.ThrowInvalidImageContentException("pHYs chunk is too short");
}
uint hResolution = BinaryPrimitives.ReadUInt32BigEndian(data[..4]); uint hResolution = BinaryPrimitives.ReadUInt32BigEndian(data[..4]);
uint vResolution = BinaryPrimitives.ReadUInt32BigEndian(data.Slice(4, 4)); uint vResolution = BinaryPrimitives.ReadUInt32BigEndian(data.Slice(4, 4));
byte unit = data[8]; byte unit = data[8];

3
src/ImageSharp/Formats/Png/PngThrowHelper.cs

@ -9,8 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Png;
internal static class PngThrowHelper internal static class PngThrowHelper
{ {
[DoesNotReturn] [DoesNotReturn]
public static void ThrowInvalidImageContentException(string errorMessage, Exception innerException) public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage);
=> throw new InvalidImageContentException(errorMessage, innerException);
[DoesNotReturn] [DoesNotReturn]
public static void ThrowInvalidHeader() => throw new InvalidImageContentException("PNG Image must contain a header chunk and it must be located before any other chunks."); public static void ThrowInvalidHeader() => throw new InvalidImageContentException("PNG Image must contain a header chunk and it must be located before any other chunks.");

59
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs

@ -20,40 +20,40 @@ public partial class PngDecoderTests
private static readonly byte[] Raw1X1PngIhdrAndpHYs = private static readonly byte[] Raw1X1PngIhdrAndpHYs =
[ [
// PNG Identifier // PNG Identifier
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
// IHDR // IHDR
0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// IHDR CRC // IHDR CRC
0x90, 0x77, 0x53, 0xDE, 0x90, 0x77, 0x53, 0xDE,
// pHYS // pHYS
0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00,
0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01,
// pHYS CRC // pHYS CRC
0xC7, 0x6F, 0xA8, 0x64 0xC7, 0x6F, 0xA8, 0x64
]; ];
// Contains the png marker, IDAT and IEND chunks of a 1x1 pixel 32bit png 1 a single black pixel. // Contains the png marker, IDAT and IEND chunks of a 1x1 pixel 32bit png 1 a single black pixel.
private static readonly byte[] Raw1X1PngIdatAndIend = private static readonly byte[] Raw1X1PngIdatAndIend =
[ [
// IDAT // IDAT
0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x18,
0x57, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x04, 0x57, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x04,
0x00, 0x01, 0x00, 0x01,
// IDAT CRC // IDAT CRC
0x5C, 0xCD, 0xFF, 0x69, 0x5C, 0xCD, 0xFF, 0x69,
// IEND // IEND
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44,
// IEND CRC // IEND CRC
0xAE, 0x42, 0x60, 0x82 0xAE, 0x42, 0x60, 0x82
]; ];
[Theory] [Theory]
@ -70,13 +70,28 @@ public partial class PngDecoderTests
WriteChunk(memStream, chunkName); WriteChunk(memStream, chunkName);
WriteDataChunk(memStream); WriteDataChunk(memStream);
ImageFormatException exception = InvalidImageContentException exception =
Assert.Throws<InvalidImageContentException>(() => PngDecoder.Instance.Decode<Rgb24>(DecoderOptions.Default, memStream)); Assert.Throws<InvalidImageContentException>(() => PngDecoder.Instance.Decode<Rgb24>(DecoderOptions.Default, memStream));
Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message); Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message);
} }
} }
// https://github.com/SixLabors/ImageSharp/issues/3078
[Fact]
public void Decode_TruncatedPhysChunk_ExceptionIsThrown()
{
// 24 bytes — PNG signature + truncated pHYs chunk
byte[] payload = Convert.FromHexString(
"89504e470d0a1a0a3030303070485973" +
"3030303030303030");
using MemoryStream stream = new(payload);
InvalidImageContentException exception = Assert.Throws<InvalidImageContentException>(() => Image.Load<Rgba32>(stream));
Assert.Equal("pHYs chunk is too short", exception.Message);
}
private static string GetChunkTypeName(uint value) private static string GetChunkTypeName(uint value)
{ {
byte[] data = new byte[4]; byte[] data = new byte[4];

Loading…
Cancel
Save