CWE-787 is the weakness where a program writes data past the end, or before the beginning, of the buffer it meant to write into. It keeps showing up in browsers, firmware, media libraries and system software.
It affects memory-unsafe languages, chiefly C, C++ and assembly, and shows up in ICS and OT code as well.
How it happens
Several routes lead to the same result.
- Missing or wrong length check. Code copies attacker-sized data into a fixed buffer.
- Integer errors. A size calculation wraps around, the allocation is too small, and the copy then uses the real size. The MITRE record cites CVE-2025-27363, where a font rendering library assigned a signed short to an unsigned long, causing integer wraparound and a buffer that was too small.
- Off-by-one and loop bounds. A loop uses less-than-or-equal where less-than was needed.
- Missing validation upstream. In CVE-2021-21220, insufficient input validation in a browser allowed heap corruption, and the record notes it was exploited in the wild per CISA KEV.
- Absent length checks on protocol data. CVE-2023-1017 was reference implementation code for a Trusted Platform Module that did not check data length, allowing a write of 2 bytes past the end of a buffer. Small overruns count.
A short vulnerable pattern:
void handle(const unsigned char *pkt, size_t len) {
unsigned char buf[64];
memcpy(buf, pkt, len); /* len is attacker controlled */
}
And the integer version:
uint16_t n = read_count(pkt); /* attacker sets 0xFFFF */
uint16_t total = n * 4; /* wraps, small allocation */
uint32_t *a = malloc(total);
for (size_t i = 0; i < n; i++) a[i] = read_item(pkt, i); /* writes far past a */
What an attacker gains
From the record:
- Integrity. Memory corruption, and in some cases modification of control data such as return addresses and function pointers, leading to execution of unauthorized code.
- Availability. Writes to invalid or unauthorized memory can crash the product.
- Unexpected state. Later writes can produce undefined results.
Because the attacker controls both the data and often the size, this class is a common route to remote code execution.
How to find it
MITRE says this weakness can often be detected with static analysis tools, and that many modern tools use data flow analysis to reduce false positives. It also lists dynamic approaches, including tools that interact with the running product and compile-time instrumentation that inserts runtime bounds checks.
A working routine:
- Run static analysis in CI and triage findings that involve copies, loops and arithmetic on sizes.
- Build test binaries with AddressSanitizer and run your tests and fuzzers against them.
- Fuzz every parser: image, font, archive, network protocol, file format.
- Track the libraries you depend on. A flaw in a decoder you only link against still ships in your product. The free in-browser dependency scan needs no login and keeps your file on your machine, and reachability analysis helps decide whether your code calls the vulnerable function.
How to fix it
MITRE's mitigations focus on prevention and on limiting damage.
- Choose safer languages for new code. Languages that manage memory, such as Java and Perl, are not subject to buffer overflows in the way C is. MITRE adds that Ada and C# typically offer protection which a programmer can disable, and that native code interfaces can reintroduce the problem.
- Use vetted libraries that offer safer string and buffer handling, while knowing they do not cover every overflow.
- Turn on compiler and platform protections, such as stack canaries, FORTIFY_SOURCE and the Visual Studio /GS flag. MITRE calls these defense in depth, since they detect only some overflows and the result is usually a crash.
- Check every length before every copy.
void handle(const unsigned char *pkt, size_t len) {
unsigned char buf[64];
if (len > sizeof(buf)) return; /* reject */
memcpy(buf, pkt, len);
}
- Check arithmetic before allocation. Test for wraparound before multiplying sizes.
if (n > SIZE_MAX / sizeof(uint32_t)) return -1;
uint32_t *a = malloc(n * sizeof(uint32_t));
- Verify the buffer size you pass. Be aware that functions such as strncpy do not guarantee termination when the destination size equals the source size.
Try it yourself
Static code scanning flags risky copy patterns before they ship. Step through this demo to see how findings are surfaced.
Click through it at your own pace, or open it full screen.
Quick check
Why is a 2 byte overrun still serious?
Memory next to a buffer often holds control data or other live variables. A small controlled write can be enough to change behavior.
Do compiler protections remove the weakness?
No. They detect some overflows and usually turn them into crashes. The bug is still there, so fix the bounds check.
How does integer overflow lead to an out-of-bounds write?
A size calculation wraps, so the program allocates a small buffer and then writes the full amount of data into it.