diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs
index 79d7e3786..83a571083 100644
--- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs
+++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs
@@ -344,11 +344,11 @@ internal abstract class IconDecoderCore : ImageDecoderCore
long available = stream.Length - basePosition;
uint directorySize = (uint)(IconDir.Size + (this.fileHeader.Count * IconDirEntry.Size));
- // Offsets are relative to the icon resource and must not point into its directory or beyond its containing stream.
+ // Offsets are relative to the icon resource and must not point into its directory or at or beyond its containing stream.
if (entry.Reserved is not 0
|| entry.BytesInRes is 0
|| entry.ImageOffset < directorySize
- || entry.ImageOffset > available)
+ || entry.ImageOffset >= available)
{
throw new InvalidImageContentException("The icon directory contains an invalid image resource range.");
}
diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs
index 0329f0e4d..bdbf67461 100644
--- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs
@@ -341,6 +341,32 @@ public class IcoDecoderTests
Assert.Equal(entryCount - 1, info.FrameMetadataCollection.Count);
}
+ ///
+ /// Verifies that a non-empty resource cannot begin at the exclusive end of its containing icon stream.
+ ///
+ [Fact]
+ public void EntryAtEndOfStream_ThrowsInvalidResourceRange()
+ {
+ byte[] data = TestFile.Create(Bpp32Size1x1).Bytes.ToArray();
+ Assert.Equal(1, BinaryPrimitives.ReadUInt16LittleEndian(data.AsSpan(4)));
+
+ // ImageOffset is the final DWORD in the sole directory entry. The stream length is an exclusive boundary,
+ // so accepting this value would create an empty child stream despite the entry declaring non-empty data.
+ BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(IconDir.Size + 12), (uint)data.Length);
+
+ using MemoryStream decodeStream = new(data, false);
+ InvalidImageContentException decodeException = Assert.Throws(
+ () => IcoDecoder.Instance.Decode(DecoderOptions.Default, decodeStream));
+
+ Assert.Equal("The icon directory contains an invalid image resource range.", decodeException.Message);
+
+ using MemoryStream identifyStream = new(data, false);
+ InvalidImageContentException identifyException = Assert.Throws(
+ () => IcoDecoder.Instance.Identify(DecoderOptions.Default, identifyStream));
+
+ Assert.Equal("The icon directory contains an invalid image resource range.", identifyException.Message);
+ }
+
[Fact]
public void IcoFrameMetadata_ScalesZeroEncodingDimensionsFrom256()
{