From 8de892a7623aa8a09ba2333b624c8d2eb98325df Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 5 Sep 2026 01:20:44 +1000 Subject: [PATCH] Validate ICC CLUT storage before allocation --- .../ICC/DataReader/IccDataReader.Lut.cs | 48 +++++++++++-------- .../ICC/DataReader/IccDataReaderLutTests.cs | 10 ++++ 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs index 700e43f972..da2f37efd3 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs @@ -72,13 +72,7 @@ internal sealed partial class IccDataReader /// The read CLUT8. public IccClut ReadClut8(int inChannelCount, int outChannelCount, byte[] gridPointCount) { - int length = 0; - for (int i = 0; i < inChannelCount; i++) - { - length += (int)Math.Pow(gridPointCount[i], inChannelCount); - } - - length /= inChannelCount; + int length = this.GetClutLength(inChannelCount, outChannelCount, gridPointCount, 1); const float Max = byte.MaxValue; @@ -105,13 +99,7 @@ internal sealed partial class IccDataReader public IccClut ReadClut16(int inChannelCount, int outChannelCount, byte[] gridPointCount) { int start = this.currentIndex; - int length = 0; - for (int i = 0; i < inChannelCount; i++) - { - length += (int)Math.Pow(gridPointCount[i], inChannelCount); - } - - length /= inChannelCount; + int length = this.GetClutLength(inChannelCount, outChannelCount, gridPointCount, 2); const float Max = ushort.MaxValue; @@ -139,13 +127,7 @@ internal sealed partial class IccDataReader public IccClut ReadClutF32(int inChCount, int outChCount, byte[] gridPointCount) { int start = this.currentIndex; - int length = 0; - for (int i = 0; i < inChCount; i++) - { - length += (int)Math.Pow(gridPointCount[i], inChCount); - } - - length /= inChCount; + int length = this.GetClutLength(inChCount, outChCount, gridPointCount, 4); float[] values = new float[length * outChCount]; int offset = 0; @@ -160,4 +142,28 @@ internal sealed partial class IccDataReader this.currentIndex = start + (length * outChCount * 4); return new IccClut(values, gridPointCount, IccClutDataType.Float, outChCount); } + + private int GetClutLength(int inputChannelCount, int outputChannelCount, byte[] gridPointCount, int bytesPerValue) + { + int length = 1; + for (int i = 0; i < inputChannelCount; i++) + { + int gridPoints = gridPointCount[i]; + if (gridPoints == 0 || length > int.MaxValue / gridPoints) + { + throw new InvalidIccProfileException("Invalid CLUT dimensions."); + } + + length *= gridPoints; + } + + long valueCount = (long)length * outputChannelCount; + long byteCount = valueCount * bytesPerValue; + if (valueCount > int.MaxValue || byteCount > this.data.Length - this.currentIndex) + { + throw new InvalidIccProfileException("The CLUT data is shorter than its declared dimensions."); + } + + return length; + } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs index a686d44872..055756e96b 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs @@ -53,6 +53,16 @@ public class IccDataReaderLutTests Assert.Equal(expected, output); } + [Fact] + public void ReadClut_WithOversizedDimensions_ThrowsInvalidIccProfileException() + { + byte[] gridPointCount = Enumerable.Repeat((byte)3, 15).ToArray(); + + Assert.Throws(() => CreateReader(new byte[8]).ReadClut8(15, 15, gridPointCount)); + Assert.Throws(() => CreateReader(new byte[8]).ReadClut16(15, 15, gridPointCount)); + Assert.Throws(() => CreateReader(new byte[8]).ReadClutF32(15, 15, gridPointCount)); + } + [Theory] [MemberData(nameof(IccTestDataLut.Lut8TestData), MemberType = typeof(IccTestDataLut))] internal void ReadLut8(byte[] data, IccLut expected)