What happens
ImageDecoder's documented contract (ImGui.App/Images/ImageDecoder.cs:22-23, restated in CLAUDE.md) is that "anything unrecognised or malformed raises InvalidImageDataException naming what was found". JpegDecoder.Decode and TgaDecoder.Decode each declare only that exception.
Two header fields are used as array indices without validation.
JPEG progressive spectral selectors. ImGui.App/Images/JpegDecoder.cs:542-543 reads them straight out of the SOS header as raw bytes (0–255):
int spectralStart = header[1 + (scanComponentCount * 2)];
int spectralEnd = header[2 + (scanComponentCount * 2)];
and clamps them only for baseline (if (!progressive) at :549). ZigZag (:44) has 64 entries, and the progressive paths index it unbounded — :781 block[ZigZag[k]], :794 RefineCoefficient(ref bits, block, ZigZag[band], bit), :834 int index = ZigZag[k].
TGA colour-map entry size. ImGui.App/Images/TgaDecoder.cs:62 reads colorMapEntryBits and never validates it — note that pixelBits immediately below is validated at :80. Line 89 computes entryBytes = (colorMapEntryBits + 7) / 8 and line 99 hands a span of that length to ReadPixel, whose default: branch (:245-250) reads source[2] and source[3].
Failure scenario
Built ImGui.App and called the public ImageDecoder.Decode:
control Ss=1 Se=5 Ah=0 Al=0 : decoded 8x8
Ss=64 Se=64 Ah=1 Al=0 : System.IndexOutOfRangeException
Ss=64 Se=64 Ah=0 Al=0 : System.IndexOutOfRangeException
TGA control entrySize=24 : decoded 2x2
TGA entrySize=0 : System.IndexOutOfRangeException
TGA entrySize=12 : System.IndexOutOfRangeException
Inputs were minimal hand-built files: SOI/DQT/SOF2/DHT/SOS with Ss=Se=64; and an 18-byte TGA header with colorMapType=1, colorMapLength=2, colorMapEntryBits=0.
Why it matters
InvalidImageDataException is the exception type consumers are told to catch around texture loading. A truncated download or a corrupt icon takes down the caller instead of being reported as a bad image.
This matters more than usual here because the decoders are in-house specifically so the package carries no imaging dependency — the contract is the whole interface to them.
Missing coverage
tests/ImGui.App.Tests/Images/JpegDecoderTests.cs has two malformed-input tests (:107, :118) and neither reaches a scan header.
Suggested fix
- In
ReadScan, reject spectralStart > 63 || spectralEnd > 63 || spectralEnd < spectralStart — and, per the spec, spectralStart == 0 requiring spectralEnd == 0 — with a named InvalidImageDataException.
- In
TgaDecoder.Decode, validate colorMapEntryBits is 8 or 15 or 16 or 24 or 32 alongside the existing pixelBits check at :80.
What happens
ImageDecoder's documented contract (ImGui.App/Images/ImageDecoder.cs:22-23, restated in CLAUDE.md) is that "anything unrecognised or malformed raisesInvalidImageDataExceptionnaming what was found".JpegDecoder.DecodeandTgaDecoder.Decodeeach declare only that exception.Two header fields are used as array indices without validation.
JPEG progressive spectral selectors.
ImGui.App/Images/JpegDecoder.cs:542-543reads them straight out of the SOS header as raw bytes (0–255):and clamps them only for baseline (
if (!progressive)at:549).ZigZag(:44) has 64 entries, and the progressive paths index it unbounded —:781block[ZigZag[k]],:794RefineCoefficient(ref bits, block, ZigZag[band], bit),:834int index = ZigZag[k].TGA colour-map entry size.
ImGui.App/Images/TgaDecoder.cs:62readscolorMapEntryBitsand never validates it — note thatpixelBitsimmediately below is validated at:80. Line 89 computesentryBytes = (colorMapEntryBits + 7) / 8and line 99 hands a span of that length toReadPixel, whosedefault:branch (:245-250) readssource[2]andsource[3].Failure scenario
Built
ImGui.Appand called the publicImageDecoder.Decode:Inputs were minimal hand-built files: SOI/DQT/SOF2/DHT/SOS with
Ss=Se=64; and an 18-byte TGA header withcolorMapType=1, colorMapLength=2, colorMapEntryBits=0.Why it matters
InvalidImageDataExceptionis the exception type consumers are told to catch around texture loading. A truncated download or a corrupt icon takes down the caller instead of being reported as a bad image.This matters more than usual here because the decoders are in-house specifically so the package carries no imaging dependency — the contract is the whole interface to them.
Missing coverage
tests/ImGui.App.Tests/Images/JpegDecoderTests.cshas two malformed-input tests (:107,:118) and neither reaches a scan header.Suggested fix
ReadScan, rejectspectralStart > 63 || spectralEnd > 63 || spectralEnd < spectralStart— and, per the spec,spectralStart == 0requiringspectralEnd == 0— with a namedInvalidImageDataException.TgaDecoder.Decode, validatecolorMapEntryBits is 8 or 15 or 16 or 24 or 32alongside the existingpixelBitscheck at:80.