CWE-125, Out-of-bounds Read, is simple to state. The product reads data past the end, or before the beginning, of the intended buffer. Nothing is overwritten, so nothing looks broken. The program just hands back memory it was never meant to share.
MITRE lists memory-unsafe languages, C and C++, plus ICS and OT environments among the affected platforms. If you build network stacks, parsers, embedded devices, or anything that processes data from outside, you are in scope. If you only consume such software, you are in scope through your dependencies.
How it happens
A read goes out of bounds when the code trusts a length, an index or a terminator that it should have checked. MITRE's description of the consequences hints at the usual shape: code that reads a variable amount of data and assumes a sentinel exists to stop the read, such as a NUL terminator.
The most famous example is in the record. CVE-2014-0160, known as Heartbleed, is described by MITRE as a chain: the bug receives an inconsistent length parameter (CWE-130), enabling an out-of-bounds read (CWE-126) that returns memory which could include private cryptographic keys and other sensitive data.
The shape of that bug is easy to sketch. A request says "echo back N bytes" and supplies a payload. If the code believes N without comparing it to the real payload size, it copies N bytes starting at the payload and goes beyond it.
// Vulnerable: claimed_len comes from the message, not from reality
void echo(const uint8_t *msg, size_t msg_len, uint8_t *reply) {
uint16_t claimed_len = (msg[0] << 8) | msg[1];
const uint8_t *payload = msg + 2;
memcpy(reply, payload, claimed_len); // may read past msg + msg_len
}
The fix compares the claim to what actually arrived.
// Fixed: reject messages whose length field does not fit
int echo(const uint8_t *msg, size_t msg_len, uint8_t *reply, size_t reply_cap) {
if (msg_len < 2) return -1;
uint16_t claimed_len = (msg[0] << 8) | msg[1];
if ((size_t)claimed_len > msg_len - 2 || claimed_len > reply_cap) return -1;
memcpy(reply, msg + 2, claimed_len);
return claimed_len;
}
Other observed examples on MITRE's page show different roots. CVE-2023-1018 is reference code for a Trusted Platform Module that does not check data lengths and lets an attacker read 2 bytes past the end of a buffer. CVE-2020-11899 is an out-of-bounds read in an IP stack used in embedded systems, exploited in the wild per CISA KEV. CVE-2021-40985 is a buffer under-read in an HTML conversion package that allows a crash.
Reads can go in either direction. Past the end is a buffer over-read (CWE-126). Before the start is a buffer under-read (CWE-127).
What an attacker gains
MITRE lists four outcomes.
- Secrets. An attacker could get secret values such as cryptographic keys, personal data, memory addresses or other information that helps further attacks.
- Bypassing protections. Out-of-bounds memory can hold addresses that help defeat ASLR and similar mechanisms, which makes a separate code execution bug more reliable to exploit.
- Crashes. A read outside allowed memory can cause a segmentation fault, especially when the code reads a variable amount of data and assumes a sentinel will stop it.
- Other undefined results. The read can produce unexpected behavior that varies by context.
The quiet nature is the danger. A write error often crashes the program and gets noticed. A read error can run for months while leaking data with no visible symptom.
How to find it
MITRE names three methods.
- Fuzzing. It generates large numbers of diverse inputs and can reveal crashes and memory corruption even with random data.
- Static analysis. SAST can find some instances by modeling data flow in source or compiled code.
- Runtime checks. Compile with AddressSanitizer for C and C++. MITRE notes crafted inputs are still needed to reach the vulnerable code, so pair it with fuzzing.
A reliable practical test is to feed each parser inputs whose length fields lie: zero, one more than the data, the maximum value. Run those under ASan.
How to fix it
MITRE's record has no mitigation list, so these steps follow from the causes.
- Compare every length field to the actual amount of data received before using it.
- Track buffer sizes next to pointers, and check them before every read.
- Do not rely on terminators for external data. Carry explicit lengths.
- Check loop bounds in both directions.
- Zero or avoid reusing buffers that held secrets, so any stray read returns less.
- Run fuzzers under ASan in CI for every parser that touches untrusted input.
Find it in what you ship
Out-of-bounds reads usually reach you through a library: TLS, a TPM stack, an IP stack, a document converter. A list of the components you depend on is the first step. The demo below shows how an inventory of dependencies is built and matched against advisories.
Click through it at your own pace, or open it full screen.
You can also check a manifest right now with the free in-browser dependency scan. No login is needed, the file stays on your machine, and it covers up to 100 dependencies.
Quick check
Why can an out-of-bounds read be as serious as a write?
Reads can expose keys, personal data and memory addresses. The addresses can defeat protections like ASLR and make a separate code execution bug more reliable.
What was the root cause chain in Heartbleed according to MITRE?
An inconsistent length parameter (CWE-130) enabled an out-of-bounds read (CWE-126), returning memory that could include private cryptographic keys and other sensitive data.
Which test finds these bugs best?
Fuzzing with ASan enabled. Fuzzing supplies the odd inputs and ASan turns a silent over-read into a visible failure.