CWE-122
AllowedHeap-based Buffer Overflow
Abstraction: Variant · Status: Draft
A heap overflow condition is a buffer overflow, where the buffer that can be overwritten is allocated in the heap portion of memory, generally meaning that the buffer was allocated using a routine such as malloc().
4096 vulnerabilities reference this CWE, most recent first.
GHSA-MW67-65X5-H28P
Vulnerability from github – Published: 2023-07-06 19:24 – Updated: 2024-04-04 05:35A bug affects the Linux kernel’s ksmbd NTLMv2 authentication and is known to crash the OS immediately in Linux-based systems.
{
"affected": [],
"aliases": [
"CVE-2023-0210"
],
"database_specific": {
"cwe_ids": [
"CWE-122",
"CWE-787"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-03-27T22:15:00Z",
"severity": "HIGH"
},
"details": "A bug affects the Linux kernel\u2019s ksmbd NTLMv2 authentication and is known to crash the OS immediately in Linux-based systems.",
"id": "GHSA-mw67-65x5-h28p",
"modified": "2024-04-04T05:35:19Z",
"published": "2023-07-06T19:24:12Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-0210"
},
{
"type": "WEB",
"url": "https://github.com/cifsd-team/ksmbd/commit/8824b7af409f51f1316e92e9887c2fd48c0b26d6"
},
{
"type": "WEB",
"url": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit"
},
{
"type": "WEB",
"url": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=797805d81baa814f76cf7bdab35f86408a79d707"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20230517-0002"
},
{
"type": "WEB",
"url": "https://securityonline.info/cve-2023-0210-flaw-in-linux-kernel-allows-unauthenticated-remote-dos-attacks"
},
{
"type": "WEB",
"url": "https://www.openwall.com/lists/oss-security/2023/01/04/1"
},
{
"type": "WEB",
"url": "https://www.openwall.com/lists/oss-security/2023/01/11/1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-MW8J-QVR9-2JPG
Vulnerability from github – Published: 2026-05-12 18:30 – Updated: 2026-05-12 18:30Access of resource using incompatible type ('type confusion') in Microsoft Office Word allows an unauthorized attacker to execute code locally.
{
"affected": [],
"aliases": [
"CVE-2026-40364"
],
"database_specific": {
"cwe_ids": [
"CWE-122"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-05-12T18:17:15Z",
"severity": "HIGH"
},
"details": "Access of resource using incompatible type (\u0027type confusion\u0027) in Microsoft Office Word allows an unauthorized attacker to execute code locally.",
"id": "GHSA-mw8j-qvr9-2jpg",
"modified": "2026-05-12T18:30:44Z",
"published": "2026-05-12T18:30:44Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-40364"
},
{
"type": "WEB",
"url": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-40364"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-MWQ3-XJFP-CJG3
Vulnerability from github – Published: 2025-01-14 18:32 – Updated: 2025-01-14 18:32Windows Telephony Service Remote Code Execution Vulnerability
{
"affected": [],
"aliases": [
"CVE-2025-21303"
],
"database_specific": {
"cwe_ids": [
"CWE-122"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-01-14T18:15:53Z",
"severity": "HIGH"
},
"details": "Windows Telephony Service Remote Code Execution Vulnerability",
"id": "GHSA-mwq3-xjfp-cjg3",
"modified": "2025-01-14T18:32:04Z",
"published": "2025-01-14T18:32:04Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-21303"
},
{
"type": "WEB",
"url": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-21303"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-MWQ4-J679-7FRP
Vulnerability from github – Published: 2026-02-16 21:30 – Updated: 2026-02-17 15:31Crypt::URandom versions from 0.41 before 0.55 for Perl is vulnerable to a heap buffer overflow in the XS function crypt_urandom_getrandom().
The function does not validate that the length parameter is non-negative. If a negative value (e.g. -1) is supplied, the expression length + 1u causes an integer wraparound, resulting in a zero-byte allocation. The subsequent call to getrandom(data, length, GRND_NONBLOCK) passes the original negative value, which is implicitly converted to a large unsigned value (typically SIZE_MAX). This can result in writes beyond the allocated buffer, leading to heap memory corruption and application crash (denial of service).
In common usage, the length argument is typically hardcoded by the caller, which reduces the likelihood of attacker-controlled exploitation. Applications that pass untrusted input to this parameter may be affected.
{
"affected": [],
"aliases": [
"CVE-2026-2474"
],
"database_specific": {
"cwe_ids": [
"CWE-122"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-02-16T21:22:18Z",
"severity": "HIGH"
},
"details": "Crypt::URandom versions from 0.41 before 0.55 for Perl is vulnerable to a heap buffer overflow in the XS function crypt_urandom_getrandom().\n\nThe function does not validate that the length parameter is non-negative. If a negative value (e.g. -1) is supplied, the expression length + 1u causes an integer wraparound, resulting in a zero-byte allocation. The subsequent call to getrandom(data, length, GRND_NONBLOCK) passes the original negative value, which is implicitly converted to a large unsigned value (typically SIZE_MAX). This can result in writes beyond the allocated buffer, leading to heap memory corruption and application crash (denial of service).\n\nIn common usage, the length argument is typically hardcoded by the caller, which reduces the likelihood of attacker-controlled exploitation. Applications that pass untrusted input to this parameter may be affected.",
"id": "GHSA-mwq4-j679-7frp",
"modified": "2026-02-17T15:31:35Z",
"published": "2026-02-16T21:30:14Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-2474"
},
{
"type": "WEB",
"url": "https://metacpan.org/release/DDICK/Crypt-URandom-0.54/source/URandom.xs#L35-79"
},
{
"type": "WEB",
"url": "https://metacpan.org/release/DDICK/Crypt-URandom-0.55/source/Changes"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-MWR4-M2R6-RMH7
Vulnerability from github – Published: 2024-02-16 21:31 – Updated: 2024-08-15 21:31In convertYUV420Planar16ToY410 of ColorConverter.cpp, there is a possible out of bounds write due to a heap buffer overflow. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is not needed for exploitation.
{
"affected": [],
"aliases": [
"CVE-2024-0018"
],
"database_specific": {
"cwe_ids": [
"CWE-122",
"CWE-787"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-02-16T20:15:47Z",
"severity": "HIGH"
},
"details": "In convertYUV420Planar16ToY410 of ColorConverter.cpp, there is a possible out of bounds write due to a heap buffer overflow. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is not needed for exploitation.",
"id": "GHSA-mwr4-m2r6-rmh7",
"modified": "2024-08-15T21:31:18Z",
"published": "2024-02-16T21:31:31Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-0018"
},
{
"type": "WEB",
"url": "https://android.googlesource.com/platform/frameworks/av/+/bf6406041919f67219fd1829438dda28845d4c23"
},
{
"type": "WEB",
"url": "https://source.android.com/security/bulletin/2024-01-01"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-MWXW-JP9C-R89R
Vulnerability from github – Published: 2024-04-09 18:30 – Updated: 2024-04-09 18:30Microsoft ODBC Driver for SQL Server Remote Code Execution Vulnerability
{
"affected": [],
"aliases": [
"CVE-2024-28932"
],
"database_specific": {
"cwe_ids": [
"CWE-122"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-04-09T17:15:54Z",
"severity": "HIGH"
},
"details": "Microsoft ODBC Driver for SQL Server Remote Code Execution Vulnerability",
"id": "GHSA-mwxw-jp9c-r89r",
"modified": "2024-04-09T18:30:27Z",
"published": "2024-04-09T18:30:27Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-28932"
},
{
"type": "WEB",
"url": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-28932"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-MX33-QPW6-4VP3
Vulnerability from github – Published: 2022-07-16 00:00 – Updated: 2022-07-16 00:00Adobe InCopy versions 17.2 (and earlier) and 16.4.1 (and earlier) are affected by a Heap-based Buffer Overflow vulnerability that could result in arbitrary code execution in the context of the current user. Exploitation of this issue requires user interaction in that a victim must open a malicious file.
{
"affected": [],
"aliases": [
"CVE-2022-34249"
],
"database_specific": {
"cwe_ids": [
"CWE-122"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-07-15T16:15:00Z",
"severity": "HIGH"
},
"details": "Adobe InCopy versions 17.2 (and earlier) and 16.4.1 (and earlier) are affected by a Heap-based Buffer Overflow vulnerability that could result in arbitrary code execution in the context of the current user. Exploitation of this issue requires user interaction in that a victim must open a malicious file.",
"id": "GHSA-mx33-qpw6-4vp3",
"modified": "2022-07-16T00:00:29Z",
"published": "2022-07-16T00:00:29Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-34249"
},
{
"type": "WEB",
"url": "https://helpx.adobe.com/security/products/incopy/apsb22-29.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-MX4J-FJQX-F8QJ
Vulnerability from github – Published: 2026-04-09 21:31 – Updated: 2026-04-29 18:31Heap buffer overflow in CertFromX509 via AuthorityKeyIdentifier size confusion. A heap buffer overflow occurs when converting an X.509 certificate internally due to incorrect size handling of the AuthorityKeyIdentifier extension.
{
"affected": [],
"aliases": [
"CVE-2026-5447"
],
"database_specific": {
"cwe_ids": [
"CWE-122"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-04-09T21:16:13Z",
"severity": "MODERATE"
},
"details": "Heap buffer overflow in CertFromX509 via AuthorityKeyIdentifier size confusion. A heap buffer overflow occurs when converting an X.509 certificate internally due to incorrect size handling of the AuthorityKeyIdentifier extension.",
"id": "GHSA-mx4j-fjqx-f8qj",
"modified": "2026-04-29T18:31:31Z",
"published": "2026-04-09T21:31:31Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-5447"
},
{
"type": "WEB",
"url": "https://github.com/wolfSSL/wolfssl/pull/10112"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-MXVV-97WH-CFMM
Vulnerability from github – Published: 2025-08-26 16:07 – Updated: 2025-11-03 21:34Summary
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.bmpworkers. -
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,
WriteBMPImageincoders/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)
bytes_per_line = 4 * ((image->columns * bmp_info.bits_per_pixel + 31) / 32);
Per-row base and 24-bpp loop (writer)
q = pixels + ((ssize_t)image->rows - y - 1) * (ssize_t)bytes_per_line;
for (x = 0; x < (ssize_t)image->columns; x++) {
*q++ = B(...); *q++ = G(...); *q++ = R(...); // writes 3 * width bytes
}
Allocation (writer)
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:
-
Stage-1 multiply+add in
(width * 24 + 31)
Overflow iffwidth > ⌊(0xFFFFFFFF − 31) / 24⌋ = 178,956,969
→ at width ≥ 178,956,970 the numerator wraps small before/32, producing a tinybytes_per_line. -
Stage-2 final ×4 after the division
Letq = (width * 24 + 31) / 32. Final×4overflows iffq > 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→ commit3fcd081c0278427f... -
Configure 32-bit Q8 non-HDRI with ASan/UBSan (summary):
./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:
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):
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):
#!/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 WidthOrHeightExceedsLimit/ResourceLimitError when values exceed safe bounds.
Concretely:
- Validate width and bits_per_pixel before the stride formula to ensure (width*bpp + 31) cannot overflow a size_t.
- Compute row_bytes for the chosen bpp and assert row_bytes <= bytes_per_line.
- Bound rows * stride before allocating and ensure biSizeImage (DIB 32-bit) cannot overflow.
A full suggested guarded implementation is provided in Appendix A — Full patch (for maintainers).
Regression Tests to Include (PR-friendly)
-
32-bit overflow repros (with ASan):
-
rows=1,width ≥ 178,956,970,bpp=24→ now cleanly errors. -
rows=2, same bound → no row overlap; clean error.
-
-
64-bit sanity: Medium images (e.g.,
8192×4096, 24-bpp) round-trip; header’sbiSizeImage = rows * bytes_per_line. -
Packed bpp (1/4/8): Validate
row_bytes = (width*bpp+7)/8(guarded), 4-pad, and payload ≤ stride holds.
Attachments (private BMP_Package)
Provided with report: README.md, poc_ppm_generator.py, repro_commands.sh, full_asan_bmp_crash.txt, appendix_a_patch_block.c. (Private gist link with package provided separately.)
Disclosure & Coordination
-
Reporter: Lumina Mescuwa
-
Tested on: i686 Linux container (details in Repro)
-
Timeline: August 19th, 2025
Appendices
Appendix A — Patch block tailored to bmp.c
Where this hooks in (current code):
-
Stride is computed here:
bytes_per_line=4*((image->columns*bmp_info.bits_per_pixel+31)/32); -
Header uses
bmp_info.image_size=(unsigned int) (bytes_per_line*image->rows); -
Allocation uses
AcquireVirtualMemory(image->rows, MagickMax(bytes_per_line, image->columns+256UL)*sizeof(*pixels)); -
24-bpp row loop writes pixels then zero-pads up to
bytes_per_line(so the per-row slot size matters):for (x=3L*(ssize_t)image->columns; x < (ssize_t)bytes_per_line; x++) *q++=0x00;
Suggested Patch (minimal surface, guards + invariant)
I recommend this in place of the existing bytes_per_line assignment and the subsequent bmp_info.image_size / allocation block. Keep your macros and local variables as-is.
/* --- PATCH BEGIN: guarded stride, per-row invariant, and product checks --- */
/* 1) Guard the original stride arithmetic (preserve behavior, add checks). */
if (bmp_info.bits_per_pixel == 0 ||
(size_t)image->columns > (SIZE_MAX - 31) / (size_t)bmp_info.bits_per_pixel)
ThrowWriterException(ImageError, "WidthOrHeightExceedsLimit");
size_t _tmp = (size_t)image->columns * (size_t)bmp_info.bits_per_pixel + 31;
/* Divide first; then check the final ×4 won't overflow. */
_tmp /= 32;
if (_tmp > (SIZE_MAX / 4))
ThrowWriterException(ImageError, "WidthOrHeightExceedsLimit");
bytes_per_line = 4 * _tmp; /* same formula as before, now checked */
/* 2) Compute the actual data bytes written per row for the chosen bpp. */
size_t row_bytes;
if (bmp_info.bits_per_pixel == 1 || bmp_info.bits_per_pixel == 4 || bmp_info.bits_per_pixel == 8) {
/* packed: ceil(width*bpp/8) */
if ((size_t)image->columns > (SIZE_MAX - 7) / (size_t)bmp_info.bits_per_pixel)
ThrowWriterException(ImageError, "WidthOrHeightExceedsLimit");
row_bytes = (((size_t)image->columns * (size_t)bmp_info.bits_per_pixel) + 7) >> 3;
} else {
/* 16/24/32 bpp: (bpp/8) * width */
size_t bpp_bytes = (size_t)bmp_info.bits_per_pixel / 8;
if (bpp_bytes == 0 || (size_t)image->columns > SIZE_MAX / bpp_bytes)
ThrowWriterException(ImageError, "WidthOrHeightExceedsLimit");
row_bytes = bpp_bytes * (size_t)image->columns;
}
/* 3) Per-row safety invariant: the payload must fit the stride. */
if (row_bytes > bytes_per_line)
ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
/* 4) Guard header size and allocation products. */
if ((size_t)image->rows == 0)
ThrowWriterException(ImageError, "WidthOrHeightExceedsLimit");
/* biSizeImage = rows * bytes_per_line (DIB field is 32-bit) */
if (bytes_per_line > 0xFFFFFFFFu / (size_t)image->rows)
ThrowWriterException(ImageError, "WidthOrHeightExceedsLimit");
bmp_info.image_size = (unsigned int)(bytes_per_line * (size_t)image->rows);
/* Allocation count = rows * stride_used, with existing MagickMax policy. */
size_t _stride = MagickMax(bytes_per_line, (size_t)image->columns + 256UL);
if (_stride > SIZE_MAX / (size_t)image->rows)
ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
pixel_info = AcquireVirtualMemory((size_t)image->rows, _stride * sizeof(*pixels));
if (pixel_info == (MemoryInfo *) NULL)
ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
pixels = (unsigned char *) GetVirtualMemoryBlob(pixel_info);
/* Optional: keep zeroing aligned to computed header size. */
(void) memset(pixels, 0, (size_t) bmp_info.image_size);
/* --- PATCH END --- */
Why this is the right spot?
-
It replaces the unguarded stride line you currently have, without changing the algorithm (still
4*((W*bpp+31)/32)). -
It fixes the header (
biSizeImage) to be a checked product, instead of a potentially wrapped multiplication. -
It guards allocation where you presently allocate
rows × MagickMax(bytes_per_line, columns+256). -
The invariant
row_bytes ≤ bytes_per_lineensures your 24-bpp emission loop (writes 3 bytes/pixel, then pads tobytes_per_line) can never exceed the per-row slot the code relies on.
Notes
-
Behavior preserved: The stride value for normal images is unchanged; only pathological integer states are rejected.
-
Header consistency:
biSizeImage = rows * bytes_per_lineremains true by construction, but now cannot overflow a 32-bit DIB field. -
Defensive alignment: If you prefer, you can compute
bytes_per_lineas((row_bytes + 3) & ~3U); it’s equivalent and may read clearer, but I kept the original formula with guards to minimize diff.
A slightly larger “helpers” variant (with safe_mul_size / safe_add_size utilities) also comes to mind, but the block above is the tightest patch that closes the 32-bit IOF→OOB class without touching unrelated code paths.
Appendix B — Arithmetic Worked Example (W=178,957,200)
-
(24W + 31) mod 2^32 = 5535 -
bytes_per_line = 4 * (5535/32) = 688 -
row_bytes (24-bpp) = 536,871,600 -
Allocation via
MagickMax = 178,957,456→ immediate row 0 out-of-bounds.
Appendix C — Raw ASan Log (trimmed)
=================================================================
==49178==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6eaac490
WRITE of size 1 at 0x6eaac490 thread T0
#0 0xed2788 in WriteBMPImage coders/bmp.c:2309
#1 0x13da32c in WriteImage MagickCore/constitute.c:1342
#2 0x13dc657 in WriteImages MagickCore/constitute.c:1564
0x6eaac490 is located 0 bytes to the right of 178957456-byte region
allocated by thread T0 here:
#0 0x408e30ab in __interceptor_posix_memalign
#1 0xd03305 in AcquireVirtualMemory MagickCore/memory.c:747
#2 0xecd597 in WriteBMPImage coders/bmp.c:2092
{
"affected": [
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-AnyCPU"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.8.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-HDRI-AnyCPU"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.8.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-HDRI-x86"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.8.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q16-x86"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.8.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q8-AnyCPU"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.8.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "NuGet",
"name": "Magick.NET-Q8-x86"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "14.8.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-57803"
],
"database_specific": {
"cwe_ids": [
"CWE-122",
"CWE-190"
],
"github_reviewed": true,
"github_reviewed_at": "2025-08-26T16:07:27Z",
"nvd_published_at": "2025-08-26T18:15:47Z",
"severity": "HIGH"
},
"details": "## Summary\n\nA 32-bit integer overflow in the BMP encoder\u2019s scanline-stride computation collapses\u00a0`bytes_per_line`\u00a0(stride) to a tiny value while the per-row writer still emits\u00a0`3 \u00d7 width`\u00a0bytes 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.\n\n- **Impact:**\u00a0Attacker-controlled heap out-of-bounds (OOB) write during conversion\u00a0**to BMP**.\n \n- **Surface:**\u00a0Typical upload \u2192 normalize/thumbnail \u2192\u00a0`magick ... out.bmp`\u00a0workers.\n \n- **32-bit:**\u00a0**Vulnerable**\u00a0(reproduced with ASan).\n \n- **64-bit:**\u00a0Safe from this specific integer overflow (IOF) by arithmetic, but still add product/size guards.\n \n- **Proposed severity:**\u00a0**Critical 9.8**\u00a0(CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).\n \n\n---\n\n## Scope \u0026 Affected Builds\n\n- **Project:**\u00a0ImageMagick (BMP writer path,\u00a0`WriteBMPImage`\u00a0in\u00a0`coders/bmp.c`).\n \n- **Commit under test:**\u00a0`3fcd081c0278427fc0e8ac40ef75c0a1537792f7`\n \n- **Version string from the run:**\u00a0`ImageMagick 7.1.2-0 Q8 i686 9bde76f1d:20250712`\n \n- **Architecture:**\u00a032-bit i686 (**`sizeof(size_t) == 4`**) with ASan/UBSan.\n \n- **Note on other versions:**\u00a0Any release/branch with the same stride arithmetic and row loop is likely affected on 32-bit.\n \n\n---\n\n## Root Cause (with code anchors)\n\n### Stride computation (writer)\n\n```c\nbytes_per_line = 4 * ((image-\u003ecolumns * bmp_info.bits_per_pixel + 31) / 32);\n```\n\n### Per-row base and 24-bpp loop (writer)\n\n```c\nq = pixels + ((ssize_t)image-\u003erows - y - 1) * (ssize_t)bytes_per_line;\nfor (x = 0; x \u003c (ssize_t)image-\u003ecolumns; x++) {\n *q++ = B(...); *q++ = G(...); *q++ = R(...); // writes 3 * width bytes\n}\n```\n\n### Allocation (writer)\n\n```c\npixel_info = AcquireVirtualMemory(image-\u003erows,\n MagickMax(bytes_per_line, image-\u003ecolumns + 256UL) * sizeof(*pixels));\npixels = (unsigned char *) GetVirtualMemoryBlob(pixel_info);\n```\n\n### Dimension \u201ccaps\u201d (insufficient)\n\nThe writer rejects dimensions that don\u2019t round-trip through\u00a0`signed int`, but both overflow thresholds below are\u00a0**\u2264 INT_MAX**\u00a0on 32-bit, so the caps\u00a0**do not prevent**\u00a0the bug.\n\n---\n\n## Integer-Overflow Analysis (32-bit\u00a0`size_t`)\n\nStride formula for 24-bpp:\n\n```\nbytes_per_line = 4 * ((width * 24 + 31) / 32)\n```\n\nThere are\u00a0**two independent overflow hazards**\u00a0on 32-bit:\n\n1. **Stage-1 multiply+add**\u00a0in\u00a0`(width * 24 + 31)` \n Overflow iff\u00a0`width \u003e \u230a(0xFFFFFFFF \u2212 31) / 24\u230b = 178,956,969` \n \u2192 at\u00a0**width \u2265 178,956,970**\u00a0the numerator wraps small before\u00a0`/32`, producing a\u00a0**tiny**\u00a0`bytes_per_line`.\n \n2. **Stage-2 final \u00d74**\u00a0after the division \n Let\u00a0`q = (width * 24 + 31) / 32`. Final\u00a0`\u00d74`\u00a0overflows iff\u00a0`q \u003e 0x3FFFFFFF`. \n Solving gives\u00a0**width \u2265 1,431,655,765 (0x55555555)**.\n \n\nBoth thresholds are\u00a0**below**\u00a0`INT_MAX`\u00a0(\u22482.147e9), so \u201cint caps\u201d don\u2019t help.\n\n**Mismatch predicate (guaranteed OOB when overflowed):** \nPer-row write for 24-bpp is\u00a0`row_bytes = 3*width`. Safety requires\u00a0`row_bytes \u2264 bytes_per_line`. \nUnder either overflow,\u00a0`bytes_per_line`\u00a0collapses \u2192\u00a0`3*width \u003e bytes_per_line`\u00a0holds \u2192\u00a0**OOB-write**.\n\n---\n\n## Concrete Demonstration\n\nChosen width:\u00a0**`W = 178,957,200`**\u00a0(just over Stage-1 bound)\n\n- Stage-1:\u00a0`24*W + 31 = 4,294,972,831 \u2261 0x0000159F (mod 2^32)`\u00a0\u2192\u00a0**5535**\n \n- Divide by 32:\u00a0`5535 / 32 = 172`\n \n- Multiply by 4:\u00a0`bytes_per_line = 172 * 4 = **688** bytes`\u00a0\u2190 tiny stride\n \n- Per-row data (24-bpp):\u00a0`row_bytes = 3*W = **536,871,600** bytes`\n \n- Allocation used:\u00a0`MagickMax(688, W+256) = **178,957,456** bytes`\n \n- **Immediate OOB**: first row writes ~536MB into a 178MB region, starting at a base advanced by only 688 bytes.\n \n---\n\n## Observed Result (ASan excerpt)\n\n```\nERROR: AddressSanitizer: heap-buffer-overflow on address 0x6eaac490\nWRITE of size 1 in WriteBMPImage coders/bmp.c:2309\n...\nallocated by:\n AcquireVirtualMemory MagickCore/memory.c:747\n WriteBMPImage coders/bmp.c:2092\n```\n\n- Binary:\u00a0**ELF 32-bit i386**, Q8, non-HDRI\n \n- Resources set to permit execution of the writer path (defense-in-depth limits relaxed for repro)\n \n\n---\n\n## Exploitability \u0026 Risk\n\n- **Primitive:**\u00a0Large, contiguous, attacker-controlled heap overwrite beginning at the scanline slot.\n \n- **Control:**\u00a0Overwrite bytes are sourced from attacker-supplied pixels (e.g., crafted input image to be converted to BMP).\n \n- **Likely deployment:**\u00a0Server-side, non-interactive conversion pipelines (UI:N).\n \n- **Outcome:**\u00a0At minimum, deterministic crash (DoS). On many 32-bit allocators, well-understood heap shaping can escalate to\u00a0**RCE**.\n \n\n**Note on 64-bit:**\u00a0Without integer overflow,\u00a0`bytes_per_line = 4 * ceil((3*width)/4) \u2265 3*width`, so the mismatch doesn\u2019t arise. Still add product/size checks to prevent DoS and future refactors.\n\n---\n\n## Reproduction (copy-paste triager script)\n\n**Test Environment:**\n\n- `docker run -it --rm --platform linux/386 debian:11 bash`\n \n- Install deps:\u00a0`apt-get update \u0026\u0026 apt-get install -y build-essential git autoconf automake libtool pkg-config python3`\n \n- Clone \u0026 checkout: ImageMagick\u00a0`7.1.2-0`\u00a0\u2192 commit\u00a0`3fcd081c0278427f...`\n \n- Configure 32-bit Q8 non-HDRI with ASan/UBSan (summary):\n\n```bash\n./configure \\\n --host=i686-pc-linux-gnu \\\n --build=x86_64-pc-linux-gnu \\\n --disable-dependency-tracking \\\n --disable-silent-rules \\\n --disable-shared \\\n --disable-openmp \\\n --disable-docs \\\n --without-x \\\n --without-perl \\\n --without-magick-plus-plus \\\n --without-lqr \\\n --without-zstd \\\n --without-tiff \\\n --with-quantum-depth=8 \\\n --disable-hdri \\\n CFLAGS=\"-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined\" \\\n CXXFLAGS=\"-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined\" \\\n LDFLAGS=\"-fsanitize=address,undefined\"\n\nmake -j\"$(nproc)\"\n```\n- Runtime limits to exercise writer:\n\n```bash\nexport MAGICK_WIDTH_LIMIT=200000000\nexport MAGICK_HEIGHT_LIMIT=200000000\nexport MAGICK_TEMPORARY_PATH=/tmp\nexport TMPDIR=/tmp\nexport ASAN_OPTIONS=\"detect_leaks=0:malloc_context_size=20:alloc_dealloc_mismatch=0\"\n```\n\n**One-liner trigger (no input file):**\n\n```bash\nW=178957200\n./utilities/magick \\\n -limit width 200000000 -limit height 200000000 \\\n -limit memory 268435456 -limit map 0 -limit disk 200000000000 \\\n -limit thread 1 \\\n -size ${W}x1 xc:black -type TrueColor -define bmp:format=bmp3 BMP3:/dev/null\n```\n\n**Expected:**\u00a0ASan heap-buffer-overflow in\u00a0`WriteBMPImage` (will be provided in a private gist link).\n\n**Alternate PoC (raw PPM generator):**\n\n```python\n#!/usr/bin/env python3\nW, H, MAXV = 180_000_000, 1, 255 \n# W \u003e 178,956,969\nwith open(\"huge.ppm\", \"wb\") as f:\n f.write(f\"P6\\n{W} {H}\\n{MAXV}\\n\".encode(\"ascii\"))\n chunk = (b\"\\x41\\x42\\x43\") * (1024*1024)\n remaining = 3 * W\n while remaining:\n n = min(remaining, len(chunk))\n f.write(chunk[:n]); remaining -= n\n# Then: magick huge.ppm out.bmp\n```\n\n---\n\n## Proposed Severity\n\n- **Primary vector (server auto-convert):**\u00a0`AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H`\u00a0\u2192\u00a0**9.8 Critical**\n \n- **If strictly CLI/manual conversion:**\u00a0`UI:R`\u00a0\u2192\u00a0**8.8 High**\n \n\n---\n\n## Maintainer Pushbacks \u2014 Pre-empted\n\n- **\u201cMagickMax makes allocation large.\u201d**\u00a0The row\u00a0**base**\u00a0advances by\u00a0**overflowed\u00a0`bytes_per_line`**, causing row overlap and eventual region exit regardless of total allocation size.\n \n- **\u201cWe\u2019re 64-bit only.\u201d**\u00a0Code is still incorrect for 32-bit consumers/cross-compiles; also add product guards on 64-bit for correctness/DoS.\n \n- **\u201cResource policy blocks large images.\u201d**\u00a0That\u2019s environment-dependent defense-in-depth; arithmetic must be correct.\n \n---\n\n## Remediation (Summary)\n\nAdd 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 WidthOrHeightExceedsLimit/ResourceLimitError when values exceed safe bounds.\n\nConcretely:\n\n- Validate width and bits_per_pixel before the stride formula to ensure (width*bpp + 31) cannot overflow a size_t.\n- Compute row_bytes for the chosen bpp and assert row_bytes \u003c= bytes_per_line.\n- Bound rows * stride before allocating and ensure biSizeImage (DIB 32-bit) cannot overflow.\n\nA full suggested guarded implementation is provided in Appendix A \u2014 Full patch (for maintainers).\n\n---\n\n## Regression Tests to Include (PR-friendly)\n\n1. **32-bit overflow repros**\u00a0(with ASan):\n \n - `rows=1`,\u00a0`width \u2265 178,956,970`,\u00a0`bpp=24`\u00a0\u2192 now cleanly errors.\n \n - `rows=2`, same bound \u2192 no row overlap; clean error.\n \n2. **64-bit sanity:**\u00a0Medium images (e.g.,\u00a0`8192\u00d74096`, 24-bpp) round-trip; header\u2019s\u00a0`biSizeImage = rows * bytes_per_line`.\n \n3. **Packed bpp (1/4/8):**\u00a0Validate\u00a0`row_bytes = (width*bpp+7)/8`\u00a0(guarded), 4-pad, and\u00a0**payload \u2264 stride**\u00a0holds.\n\n---\n\n## Attachments (private BMP_Package) \nProvided with report: README.md, poc_ppm_generator.py, repro_commands.sh, full_asan_bmp_crash.txt, appendix_a_patch_block.c. (Private gist link with package provided separately.)\n\n---\n\n## Disclosure \u0026 Coordination\n\n- **Reporter:**\u00a0Lumina Mescuwa\n \n- **Tested on:**\u00a0i686 Linux container (details in Repro)\n \n- **Timeline:**\u00a0August 19th, 2025\n \n\n---\n\n## Appendices\n\n### Appendix A \u2014 Patch block tailored to\u00a0 `bmp.c`\n\n**Where this hooks in (current code):**\n\n- Stride is computed here:\u00a0`bytes_per_line=4*((image-\u003ecolumns*bmp_info.bits_per_pixel+31)/32);`\n \n- Header uses\u00a0`bmp_info.image_size=(unsigned int) (bytes_per_line*image-\u003erows);`\n \n- Allocation uses\u00a0`AcquireVirtualMemory(image-\u003erows, MagickMax(bytes_per_line, image-\u003ecolumns+256UL)*sizeof(*pixels));`\n \n- 24-bpp row loop writes pixels then zero-pads up to\u00a0`bytes_per_line`\u00a0(so the per-row slot size matters):\u00a0`for (x=3L*(ssize_t)image-\u003ecolumns; x \u003c (ssize_t)bytes_per_line; x++) *q++=0x00;`\n \n\n---\n\n## Suggested Patch (minimal surface, guards + invariant)\n\nI recommend this\u00a0**in place of**\u00a0the existing\u00a0`bytes_per_line`\u00a0assignment and the subsequent\u00a0`bmp_info.image_size`\u00a0/ allocation block. Keep your macros and local variables as-is.\n\n```c\n/* --- PATCH BEGIN: guarded stride, per-row invariant, and product checks --- */\n\n/* 1) Guard the original stride arithmetic (preserve behavior, add checks). */\nif (bmp_info.bits_per_pixel == 0 ||\n (size_t)image-\u003ecolumns \u003e (SIZE_MAX - 31) / (size_t)bmp_info.bits_per_pixel)\n ThrowWriterException(ImageError, \"WidthOrHeightExceedsLimit\");\n\nsize_t _tmp = (size_t)image-\u003ecolumns * (size_t)bmp_info.bits_per_pixel + 31;\n/* Divide first; then check the final \u00d74 won\u0027t overflow. */\n_tmp /= 32;\nif (_tmp \u003e (SIZE_MAX / 4))\n ThrowWriterException(ImageError, \"WidthOrHeightExceedsLimit\");\n\nbytes_per_line = 4 * _tmp; /* same formula as before, now checked */\n\n/* 2) Compute the actual data bytes written per row for the chosen bpp. */\nsize_t row_bytes;\nif (bmp_info.bits_per_pixel == 1 || bmp_info.bits_per_pixel == 4 || bmp_info.bits_per_pixel == 8) {\n /* packed: ceil(width*bpp/8) */\n if ((size_t)image-\u003ecolumns \u003e (SIZE_MAX - 7) / (size_t)bmp_info.bits_per_pixel)\n ThrowWriterException(ImageError, \"WidthOrHeightExceedsLimit\");\n row_bytes = (((size_t)image-\u003ecolumns * (size_t)bmp_info.bits_per_pixel) + 7) \u003e\u003e 3;\n} else {\n /* 16/24/32 bpp: (bpp/8) * width */\n size_t bpp_bytes = (size_t)bmp_info.bits_per_pixel / 8;\n if (bpp_bytes == 0 || (size_t)image-\u003ecolumns \u003e SIZE_MAX / bpp_bytes)\n ThrowWriterException(ImageError, \"WidthOrHeightExceedsLimit\");\n row_bytes = bpp_bytes * (size_t)image-\u003ecolumns;\n}\n\n/* 3) Per-row safety invariant: the payload must fit the stride. */\nif (row_bytes \u003e bytes_per_line)\n ThrowWriterException(ResourceLimitError, \"MemoryAllocationFailed\");\n\n/* 4) Guard header size and allocation products. */\nif ((size_t)image-\u003erows == 0)\n ThrowWriterException(ImageError, \"WidthOrHeightExceedsLimit\");\n\n/* biSizeImage = rows * bytes_per_line (DIB field is 32-bit) */\nif (bytes_per_line \u003e 0xFFFFFFFFu / (size_t)image-\u003erows)\n ThrowWriterException(ImageError, \"WidthOrHeightExceedsLimit\");\nbmp_info.image_size = (unsigned int)(bytes_per_line * (size_t)image-\u003erows);\n\n/* Allocation count = rows * stride_used, with existing MagickMax policy. */\nsize_t _stride = MagickMax(bytes_per_line, (size_t)image-\u003ecolumns + 256UL);\nif (_stride \u003e SIZE_MAX / (size_t)image-\u003erows)\n ThrowWriterException(ResourceLimitError, \"MemoryAllocationFailed\");\n\npixel_info = AcquireVirtualMemory((size_t)image-\u003erows, _stride * sizeof(*pixels));\nif (pixel_info == (MemoryInfo *) NULL)\n ThrowWriterException(ResourceLimitError, \"MemoryAllocationFailed\");\npixels = (unsigned char *) GetVirtualMemoryBlob(pixel_info);\n\n/* Optional: keep zeroing aligned to computed header size. */\n(void) memset(pixels, 0, (size_t) bmp_info.image_size);\n\n/* --- PATCH END --- */\n```\n\n### Why this is the right spot?\n\n- It\u00a0**replaces**\u00a0the unguarded stride line you currently have, without changing the algorithm (still\u00a0`4*((W*bpp+31)/32)`).\u00a0\n \n- It\u00a0**fixes the header**\u00a0(`biSizeImage`) to be a checked product, instead of a potentially wrapped multiplication.\u00a0\n \n- It\u00a0**guards allocation**\u00a0where you presently allocate\u00a0`rows \u00d7 MagickMax(bytes_per_line, columns+256)`.\u00a0\n \n- The invariant\u00a0`row_bytes \u2264 bytes_per_line`\u00a0ensures your 24-bpp emission loop (writes 3 bytes/pixel, then pads to\u00a0`bytes_per_line`) can never exceed the per-row slot the code relies on.\u00a0\n \n\n---\n\n## Notes\n\n- **Behavior preserved**: The stride value for normal images is unchanged; only pathological integer states are rejected.\u00a0\n \n- **Header consistency**:\u00a0`biSizeImage = rows * bytes_per_line`\u00a0remains true by construction, but now cannot overflow a 32-bit DIB field.\u00a0\n \n- **Defensive alignment**: If you prefer, you can compute\u00a0`bytes_per_line`\u00a0as\u00a0`((row_bytes + 3) \u0026 ~3U)`; it\u2019s equivalent and may read clearer, but I kept the original formula with guards to minimize diff.\n \n\nA slightly larger \u201chelpers\u201d variant (with\u00a0`safe_mul_size`\u00a0/\u00a0`safe_add_size`\u00a0utilities) also comes to mind, but the block above is the tightest patch that closes the 32-bit IOF\u2192OOB class without touching unrelated code paths.\n\n\n\n### Appendix B \u2014 Arithmetic Worked Example (W=178,957,200)\n\n- `(24W + 31) mod 2^32 = 5535`\n \n- `bytes_per_line = 4 * (5535/32) = 688`\n \n- `row_bytes (24-bpp) = 536,871,600`\n \n- Allocation via\u00a0`MagickMax = 178,957,456`\u00a0\u2192 immediate row 0 out-of-bounds.\n \n\n### Appendix C \u2014 Raw ASan Log (trimmed)\n\n```\n=================================================================\n==49178==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6eaac490\nWRITE of size 1 at 0x6eaac490 thread T0\n #0 0xed2788 in WriteBMPImage coders/bmp.c:2309\n #1 0x13da32c in WriteImage MagickCore/constitute.c:1342\n #2 0x13dc657 in WriteImages MagickCore/constitute.c:1564\n0x6eaac490 is located 0 bytes to the right of 178957456-byte region\nallocated by thread T0 here:\n #0 0x408e30ab in __interceptor_posix_memalign\n #1 0xd03305 in AcquireVirtualMemory MagickCore/memory.c:747\n #2 0xecd597 in WriteBMPImage coders/bmp.c:2092\n```",
"id": "GHSA-mxvv-97wh-cfmm",
"modified": "2025-11-03T21:34:24Z",
"published": "2025-08-26T16:07:27Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-mxvv-97wh-cfmm"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-57803"
},
{
"type": "WEB",
"url": "https://github.com/ImageMagick/ImageMagick/commit/2c55221f4d38193adcb51056c14cf238fbcc35d7"
},
{
"type": "PACKAGE",
"url": "https://github.com/ImageMagick/ImageMagick"
},
{
"type": "WEB",
"url": "https://github.com/dlemstra/Magick.NET/releases/tag/14.8.1"
},
{
"type": "WEB",
"url": "https://lists.debian.org/debian-lts-announce/2025/09/msg00012.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "ImageMagick (WriteBMPImage): 32-bit integer overflow when writing BMP scanline stride \u2192 heap buffer overflow"
}
GHSA-P22W-WQ6P-G85G
Vulnerability from github – Published: 2026-06-25 09:31 – Updated: 2026-06-26 03:31If NSD is configured as secondary for a zone, the primary of that zone can crash NSD with an AXFR containing a DNS message with a special crafted SVCB RR with an rdata size of 65512, that let's an (uint16_t) variable that is used to allocate space needed for the RR wrap (because total size > 65535), causing a heap overflow. The attacker can perform a controlled (RCE class) head write of up to 65509 bytes
{
"affected": [],
"aliases": [
"CVE-2026-12244"
],
"database_specific": {
"cwe_ids": [
"CWE-122"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-25T07:16:44Z",
"severity": "HIGH"
},
"details": "If NSD is configured as secondary for a zone, the primary of that zone can crash NSD with an AXFR containing a DNS message with a special crafted SVCB RR with an rdata size of 65512, that let\u0027s an (uint16_t) variable that is used to allocate space needed for the RR wrap (because total size \u003e 65535), causing a heap overflow. The attacker can perform a controlled (RCE class) head write of up to 65509 bytes",
"id": "GHSA-p22w-wq6p-g85g",
"modified": "2026-06-26T03:31:28Z",
"published": "2026-06-25T09:31:17Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-12244"
},
{
"type": "WEB",
"url": "https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-12244.txt"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
Mitigation
Pre-design: Use a language or compiler that performs automatic bounds checking.
Mitigation
Use an abstraction library to abstract away risky APIs. Not a complete solution.
Mitigation MIT-10
Strategy: Environment Hardening
- Use automatic buffer overflow detection mechanisms that are offered by certain compilers or compiler extensions. Examples include: the Microsoft Visual Studio /GS flag, Fedora/Red Hat FORTIFY_SOURCE GCC flag, StackGuard, and ProPolice, which provide various mechanisms including canary-based detection and range/index checking.
- D3-SFCV (Stack Frame Canary Validation) from D3FEND [REF-1334] discusses canary-based detection in detail.
Mitigation MIT-11
Strategy: Environment Hardening
- Run or compile the software using features or extensions that randomly arrange the positions of a program's executable and libraries in memory. Because this makes the addresses unpredictable, it can prevent an attacker from reliably jumping to exploitable code.
- Examples include Address Space Layout Randomization (ASLR) [REF-58] [REF-60] and Position-Independent Executables (PIE) [REF-64]. Imported modules may be similarly realigned if their default memory addresses conflict with other modules, in a process known as "rebasing" (for Windows) and "prelinking" (for Linux) [REF-1332] using randomly generated addresses. ASLR for libraries cannot be used in conjunction with prelink since it would require relocating the libraries at run-time, defeating the whole purpose of prelinking.
- For more information on these techniques see D3-SAOR (Segment Address Offset Randomization) from D3FEND [REF-1335].
Mitigation
Implement and perform bounds checking on input.
Mitigation
Strategy: Libraries or Frameworks
Do not use dangerous functions such as gets. Look for their safe equivalent, which checks for the boundary.
Mitigation
Use OS-level preventative functionality. This is not a complete solution, but it provides some defense in depth.
CAPEC-92: Forced Integer Overflow
This attack forces an integer variable to go out of range. The integer variable is often used as an offset such as size of memory allocation or similarly. The attacker would typically control the value of such variable and try to get it out of range. For instance the integer in question is incremented past the maximum possible value, it may wrap to become a very small, or negative number, therefore providing a very incorrect value which can lead to unexpected behavior. At worst the attacker can execute arbitrary code.