Browse Source

Reject ICO entries at stream-end offset

Tightened icon frame bounds validation to treat `ImageOffset == available` as invalid, preventing non-empty resources from starting at the exclusive end of the icon stream. Added a regression test that mutates an ICO directory entry to this boundary value and verifies both `Decode` and `Identify` throw `InvalidImageContentException` with the expected message.
pull/3162/head
James Jackson-South 2 weeks ago
parent
commit
ed64ea7197
  1. 4
      src/ImageSharp/Formats/Icon/IconDecoderCore.cs
  2. 26
      tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs

4
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.");
}

26
tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs

@ -341,6 +341,32 @@ public class IcoDecoderTests
Assert.Equal(entryCount - 1, info.FrameMetadataCollection.Count);
}
/// <summary>
/// Verifies that a non-empty resource cannot begin at the exclusive end of its containing icon stream.
/// </summary>
[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<InvalidImageContentException>(
() => IcoDecoder.Instance.Decode<Rgba32>(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<InvalidImageContentException>(
() => IcoDecoder.Instance.Identify(DecoderOptions.Default, identifyStream));
Assert.Equal("The icon directory contains an invalid image resource range.", identifyException.Message);
}
[Fact]
public void IcoFrameMetadata_ScalesZeroEncodingDimensionsFrom256()
{

Loading…
Cancel
Save