-
-
Notifications
You must be signed in to change notification settings - Fork 892
Description
Prerequisites
- I have written a descriptive issue title
- I have verified that I am running the latest version of ImageSharp
- I have verified if the problem exist in both
DEBUGandRELEASEmode - I have searched open and closed issues to ensure it has not already been reported
ImageSharp version
3.1.12
Other ImageSharp packages and versions
N/A — only SixLabors.ImageSharp
Environment (Operating system, version and so on)
Linux (WSL2, Ubuntu 22.04) and Windows 10
.NET Framework version
.NET 10.0
Description
A crafted BMP file (35 bytes) with a pixel data offset of 0x7FFFFFFF (2,147,483,647) causes an unhandled OutOfMemoryException in BmpDecoderCore.ReadImageHeaders. The decoder uses the untrusted bfOffBits field to compute a palette size without validating it against the actual file/stream length, leading to a ~2 GB allocation attempt from a tiny input.
This exception is not part of the ImageSharp exception hierarchy (ImageFormatException / InvalidImageContentException / UnknownImageFormatException), so applications following the documented error handling pattern cannot catch it.
Note: Issue #2696 addressed a related BMP memory exhaustion scenario via configurable memory allocation limits (fixed in v3.1.10). However, that fix does not cover this case — the OutOfMemoryException is thrown by the runtime itself ("Array dimensions exceeded supported range") before the allocator limits can take effect, because the requested array size exceeds the CLR's maximum array length.
Found by coverage-guided fuzzing with SharpFuzz + AFL++.
Steps to Reproduce
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
// Crafted BMP: pixel data offset = 0x7FFFFFFF, actual file = 35 bytes
var data = new byte[]
{
0x42, 0x4D, // "BM" signature
0x3A, 0x00, 0x00, 0x00, // file size: 58
0x00, 0x00, 0x00, 0x00, // reserved
0xFF, 0xFF, 0xFF, 0x7F, // pixel offset: 0x7FFFFFFF (2,147,483,647)
0x28, 0x00, 0x00, 0x00, // DIB header size: 40
0x01, 0x00, 0x00, 0x00, // width: 1
0x01, 0xFF, 0x00, 0x00, // height: 65281
0x01, 0x00, // color planes: 1
0x08, 0x00, // bits per pixel: 8
0x00, 0x00, 0x00, 0x00, // compression: RGB
0x00, 0x00, 0x00 // (truncated)
};
using var stream = new MemoryStream(data);
using var image = Image.Load<Rgba32>(stream); // throws OutOfMemoryExceptionStack trace:
System.OutOfMemoryException: Array dimensions exceeded supported range.
at SixLabors.ImageSharp.Formats.Bmp.BmpDecoderCore.ReadImageHeaders(
BufferedReadStream stream, Boolean& inverted, Byte[]& palette)
at SixLabors.ImageSharp.Formats.Bmp.BmpDecoderCore.Decode[TPixel](
BufferedReadStream stream, CancellationToken cancellationToken)
at SixLabors.ImageSharp.Formats.ImageDecoderCore.Decode[TPixel](...)
at SixLabors.ImageSharp.Image.Load[TPixel](Stream stream)
Root Cause & Suggested Fix
In BmpDecoderCore.ReadImageHeaders, the palette byte count is derived from the bfOffBits (pixel data offset) field in the BMP file header. When this field is 0x7FFFFFFF, the computed palette size exceeds the CLR maximum array length, causing the runtime to throw OutOfMemoryException before ImageSharp's memory allocator limits can intervene.
A suggested fix — validate bfOffBits against the actual stream length before using it to compute the palette size:
if (this.fileHeader.Offset > stream.Length)
{
BmpThrowHelper.ThrowInvalidImageContentException(
$"Pixel data offset {this.fileHeader.Offset} exceeds file size {stream.Length}.");
}This is consistent with how the BMP decoder already validates other header fields and would throw InvalidImageContentException, which callers are expected to handle.
Images
N/A — the reproduction is fully inline above (35-byte constructed BMP).