From be852be4744a8eb2981d8167e4c99fa039cc21a4 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 9 Mar 2026 17:29:43 +0100 Subject: [PATCH 1/3] Add check for span length in parse png physical chunk --- src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs | 5 +++++ src/ImageSharp/Formats/Png/PngThrowHelper.cs | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) 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."); From 93b9345ab878fdfbbbbd5272730d116937dd3a46 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 9 Mar 2026 17:39:40 +0100 Subject: [PATCH 2/3] Add test case for #3078 --- .../Formats/Png/PngDecoderTests.Chunks.cs | 58 ++++++++++++------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index 8dfa987488..c31fa5831c 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -6,6 +6,7 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; +using static SixLabors.ImageSharp.Tests.Memory.TestStructs; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Png; @@ -20,40 +21,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] @@ -77,6 +78,21 @@ public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(uint chunkType } } + // 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]; From 60c556cd2a096288bcff95172105eb5d55d56ca0 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 9 Mar 2026 17:59:40 +0100 Subject: [PATCH 3/3] Remove not used using, change ImageFormatException to InvalidImageContentException --- tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index c31fa5831c..0b46e7f878 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -6,7 +6,6 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; -using static SixLabors.ImageSharp.Tests.Memory.TestStructs; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Png; @@ -71,7 +70,7 @@ 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);