Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public PngPhysical(uint x, uint y, byte unitSpecifier)
/// <returns>The parsed PhysicalChunkData.</returns>
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 vResolution = BinaryPrimitives.ReadUInt32BigEndian(data.Slice(4, 4));
byte unit = data[8];
Expand Down
3 changes: 1 addition & 2 deletions src/ImageSharp/Formats/Png/PngThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Png;
internal static class PngThrowHelper
{
[DoesNotReturn]
public static void ThrowInvalidImageContentException(string errorMessage, Exception innerException)
=> throw new InvalidImageContentException(errorMessage, innerException);
public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage);

[DoesNotReturn]
public static void ThrowInvalidHeader() => throw new InvalidImageContentException("PNG Image must contain a header chunk and it must be located before any other chunks.");
Expand Down
59 changes: 37 additions & 22 deletions tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,40 +20,40 @@ public partial class PngDecoderTests
private static readonly byte[] Raw1X1PngIhdrAndpHYs =
[
// PNG Identifier
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,

// IHDR
0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02,
0x00, 0x00, 0x00,
// IHDR
0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02,
0x00, 0x00, 0x00,

// IHDR CRC
0x90, 0x77, 0x53, 0xDE,
// IHDR CRC
0x90, 0x77, 0x53, 0xDE,

// pHYS
0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00,
0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01,
// pHYS
0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00,
0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01,

// pHYS CRC
0xC7, 0x6F, 0xA8, 0x64
// pHYS CRC
0xC7, 0x6F, 0xA8, 0x64
];

// Contains the png marker, IDAT and IEND chunks of a 1x1 pixel 32bit png 1 a single black pixel.
private static readonly byte[] Raw1X1PngIdatAndIend =
[
// IDAT
0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x18,
0x57, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x04,
0x00, 0x01,
0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x18,
0x57, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x04,
0x00, 0x01,

// IDAT CRC
0x5C, 0xCD, 0xFF, 0x69,
// IDAT CRC
0x5C, 0xCD, 0xFF, 0x69,

// IEND
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44,
// IEND
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44,

// IEND CRC
0xAE, 0x42, 0x60, 0x82
// IEND CRC
0xAE, 0x42, 0x60, 0x82
];

[Theory]
Expand All @@ -70,13 +70,28 @@ public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(uint chunkType
WriteChunk(memStream, chunkName);
WriteDataChunk(memStream);

ImageFormatException exception =
InvalidImageContentException exception =
Assert.Throws<InvalidImageContentException>(() => PngDecoder.Instance.Decode<Rgb24>(DecoderOptions.Default, memStream));

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)
{
byte[] data = new byte[4];
Expand Down
Loading