diff --git a/src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs b/src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs
index 8af0ac8ca7..cce4d16b84 100644
--- a/src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs
+++ b/src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs
@@ -46,6 +46,11 @@ public PngPhysical(uint x, uint y, byte unitSpecifier)
/// The parsed PhysicalChunkData.
public static PngPhysical Parse(ReadOnlySpan 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];
diff --git a/src/ImageSharp/Formats/Png/PngThrowHelper.cs b/src/ImageSharp/Formats/Png/PngThrowHelper.cs
index 8dc70e1d9a..80c51ef6e5 100644
--- a/src/ImageSharp/Formats/Png/PngThrowHelper.cs
+++ b/src/ImageSharp/Formats/Png/PngThrowHelper.cs
@@ -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.");
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs
index 8dfa987488..0b46e7f878 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs
@@ -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]
@@ -70,13 +70,28 @@ public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(uint chunkType
WriteChunk(memStream, chunkName);
WriteDataChunk(memStream);
- ImageFormatException exception =
+ InvalidImageContentException exception =
Assert.Throws(() => PngDecoder.Instance.Decode(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(() => Image.Load(stream));
+
+ Assert.Equal("pHYs chunk is too short", exception.Message);
+ }
+
private static string GetChunkTypeName(uint value)
{
byte[] data = new byte[4];