CWE-786 covers reads and writes that land before the start of a buffer. It is the underflow cousin of the more familiar overflow. It affects memory-unsafe languages, mainly C and C++.
How it happens
MITRE describes the typical triggers: a pointer or index that is decremented past the first element, pointer arithmetic that lands before the buffer, or a negative index.
Examples from the record:
- CVE-2002-2227: an unchecked length of an SSLv2 challenge value led to a buffer underflow.
- CVE-2007-4580: a buffer underflow from a small size value used with a large buffer.
- CVE-2007-1584: an all-whitespace string caused a counter to be decremented before the buffer while the code looked for a non-whitespace character.
That last one is a pattern worth knowing. A loop that trims trailing whitespace walks backward from the end, and when the string is all spaces it never finds a stopping character:
char *end = s + strlen(s) - 1;
while (isspace((unsigned char)*end)) /* no lower bound check */
end--;
*(end + 1) = '\0';
For an empty or all-space string, end moves before s and the code reads and then writes outside the buffer.
What an attacker gains
The record lists three outcomes. An out-of-bounds read can expose sensitive information. Out-of-bounds access can corrupt memory and crash the program. And if the attacker controls the corrupted memory, code execution may be possible.
How to find it
MITRE recommends fuzzing, which produces many varied inputs and watches for crashes, and automated dynamic analysis that inserts runtime checks at compile time. Add these steps:
- Build tests with AddressSanitizer enabled.
- Feed empty, all-whitespace and extreme length inputs.
- Search code for loops that decrement an index or pointer and ask what stops them at zero.
- Treat signed values used as indexes with suspicion.
How to fix it
char *end = s + strlen(s);
while (end > s && isspace((unsigned char)end[-1]))
end--;
*end = '\0';
The fix is a lower-bound check in the loop condition. In general:
- Use unsigned types for sizes and validate that a computed index is at least zero before use.
- Check lengths against the buffer before pointer arithmetic.
- Prefer bounds-checked containers, such as std::vector with at(), or a memory-safe language for new parsing code.
- Keep compiler hardening and sanitizers on in test builds.
If you ship C or C++ libraries, outside code depends on yours. The free dependency scan can show whether the packages you ship carry known memory safety issues.
Quick check
How is an underflow different from an overflow?
An overflow goes past the end of the buffer. An underflow goes before the start. Both are out-of-bounds access.