Memory Safety & Fuzzing

Heap Buffer Overflow: From AddressSanitizer Output to CVE

Published ·10 min read·Vorthix Research Team

A heap buffer overflow (CWE-122) is a memory-safety bug where a program reads or writes past the end (or before the start) of a heap-allocated buffer. A write overflow corrupts whatever memory is adjacent to the buffer — often allocator bookkeeping data or another object entirely — while a read overflow discloses adjacent heap contents. Both are triggered by an index, length, or pointer computation that isn't validated against the buffer's actual allocated size before use.

How Heap Buffer Overflows Happen

Nearly every heap buffer overflow traces back to the same root shape: a write index or length is computed from a value the function trusts — a caller-supplied parameter, a value parsed from untrusted input, or the result of arithmetic that can overflow — and that computed value is used to write into a buffer without being checked against the buffer's actual allocated size first. The bug is rarely in the write itself; it's in the missing validation step immediately before it.

Reading an AddressSanitizer Heap-Buffer-Overflow Report

AddressSanitizer's report is structured to answer three questions immediately: what happened, where, and against what allocation. The first block states the access type and size — for example, "WRITE of size 1024" — and the faulting address, followed by the stack trace of the offending access. The second block identifies the nearby heap allocation: its size, and the stack trace of where it was allocated. Critically, ASan states whether the faulting address falls before or after that allocation's bounds — "0 bytes after an 8192-byte region" points directly at an off-by-one or a sizing mismatch, while a large offset usually indicates an index computed from the wrong unit (elements instead of bytes, or vice versa).

Case Study: GHSA-8f95-v3jq-cj86 in pymonocypher

GHSA-8f95-v3jq-cj86 is a heap buffer overflow in argon2i_32(), the Argon2i key-derivation function in the Monocypher cryptographic library, reached through exactly this shape. The function computes a block index from a caller-supplied nb_blocks cost parameter and writes a 1024-byte block directly to blocks[index]inside the caller's work area — without validating that index is within the bounds of the memory the caller actually allocated. A 21-line reproducer allocates a work area sized for 8 blocks, then supplies a cost configuration implying 64 blocks; AddressSanitizer catches the resulting out-of-bounds write immediately, reporting a WRITE of size 1024 zero bytes past an 8192-byte allocation — the off-by-a-full-region signature of a caller/callee sizing contract that was never enforced in code. The finding was confirmed in under two hours and patched within fifteen. Full code, the fixed version, and the complete AddressSanitizer output are in the GHSA-8f95-v3jq-cj86 writeup.

From Report to Fix

The fix for this class of bug is almost always the same shape: validate the computed index or size against the actual allocation before using it, and fail explicitly — reject the input or abort — rather than trusting the caller's arithmetic. In the pymonocypher case, the patched argon2i_32() clamps nb_blocks to the Argon2 minimum segment size and adds an explicit bound check before every write, returning instead of writing past the allocation. This is a narrower, more defensive contract than the original — the function no longer assumes the caller sized the buffer correctly; it verifies.

Finding Heap Buffer Overflows Systematically

Coverage-guided fuzzing is the highest-yield technique for this bug class, because heap buffer overflows are triggered by specific numeric edge cases — an index one past the boundary, a size calculation that overflows a fixed-width integer — that a fuzzer's mutation strategy finds far faster than manual code review. Running the fuzzer against a sanitizer-instrumented build is not optional: without ASan, most heap buffer overflow writes silently corrupt adjacent memory without crashing immediately, and the program may run for a long time before an unrelated, confusing crash surfaces far from the actual root cause.

Frequently Asked Questions

What is a heap buffer overflow?

A heap buffer overflow (CWE-122) occurs when a program writes or reads past the boundary of a heap-allocated buffer. A write overflow corrupts adjacent heap memory — often allocator metadata or another object’s data — while a read overflow can leak adjacent heap contents back to the caller or attacker.

How do I read an AddressSanitizer heap-buffer-overflow report?

The report states the access type (READ or WRITE) and size, the faulting address, and a stack trace of where the access occurred. Below that, it shows the allocation this address is near — its size and the stack trace of where it was allocated — and whether the faulting address is before or after that allocation’s boundary, which tells you immediately whether the bug is an off-by-one, a missing bounds check, or a sizing mismatch.

What causes heap buffer overflows in practice?

The most common causes are: an index or length computed from untrusted input without an upper-bound check, an off-by-one in a loop boundary, a size calculation that overflows before the allocation (so less memory is allocated than the code assumes), and a caller/callee mismatch where a function trusts a size parameter the caller computed incorrectly.

Are heap buffer overflows still common in 2026?

Yes, particularly in C and C++ libraries handling untrusted input: parsers, codecs, and cryptographic primitives that perform manual pointer arithmetic over caller-provided buffers. Memory-safe languages have reduced the overall rate at the application layer, but the infrastructure layer — the libraries those applications link against — remains predominantly C and C++.

Real-World Case Studies

Related Reading