The C standard says nothing about what happens when a signed integer overflows. The compiler is free to assume it never does. The CPU, meanwhile, wraps silently: INT_MAX + 1 becomes INT_MIN, no error, no signal, no smoke. The result is a vulnerability class that has caused over 30% of historical memory-corruption CVEs.
By itself, an integer overflow is rarely the exploit. It's the amplifier. The bug that takes a length check, makes it pass when it shouldn't, and then hands the bypassed length to a memcpy or malloc. Buffer overflows go in this door.
The shape of the bug
A classic image-parser function. Three lines, three integer arithmetic ops, one bug:
Attacker supplies width = 0x10000 and height = 0x10000. The product is 0x100000000, which on 32-bit size_t overflows to 0. Multiplied by 4: still 0. malloc(0) returns a tiny valid pointer (or NULL). fread then writes (width * height * 4) bytes — computed correctly somewhere — into that tiny buffer. Heap corruption.
Four flavors
1. Signed wraparound
Adding 1 to INT_MAX doesn't give you INT_MAX + 1. It gives you INT_MIN — the most negative possible value — because the sign bit flipped. Length comparisons that look correct become wrong:
The check thinks "is this length within the allowed maximum?". The attacker passes a negative length. It is, after all, less than MAX_LEN. The check passes. memcpy takes a size_t — unsigned — so -1 becomes ~0, the largest representable size.
2. Unsigned wraparound (multiplication)
The example above. Two large numbers multiply to something larger than the type can hold. The high bits silently drop. The result lies about how big the allocation needs to be.
3. Signed-to-unsigned confusion
A signed length variable is passed to a function expecting an unsigned size. Negative becomes huge. The attacker chooses the negative input; the function trusts the unsigned interpretation.
4. Truncation
Assigning a 64-bit value to a 32-bit type drops the high 32 bits. The low 32 might compare equal to a "reasonable" value while the actual file is much larger.
Famous incidents
| Year | Bug | Why it mattered |
|---|---|---|
| 1996 | Ping-of-death (Windows IP stack) | Fragmented IP packets > 65,535 bytes overflowed an unsigned 16-bit length, crashing Windows machines worldwide. |
| 2002 | OpenSSH challenge-response | Integer overflow in challenge size led to heap overflow. Remote pre-auth. |
| 2014 | Heartbleed (CVE-2014-0160) | A length field controlled by the attacker, no overflow check, exposed memory. (More missing bounds check than classical overflow, but same vulnerability family.) |
| 2018 | WebP libwebp (CVE-2018-25032) | Integer overflow in zlib via libwebp. Eventually patched but found in many embedded systems. |
| 2023 | libwebp CVE-2023-4863 | Heap buffer overflow originating from an integer mishandling. Exploited in the wild against Chrome; emergency patches at Apple, Google, Microsoft within 72 hours. |
Defenses
1. Check before you multiply
Anywhere you compute a * b for a buffer size, verify it didn't wrap. Modern compilers provide checked-arithmetic intrinsics:
2. Use unsigned for sizes
size_t for byte counts, lengths, indexes. Never store a length in a signed int. The compiler will warn at every mismatch — honor the warnings.
3. Enable sanitizers in CI
-fsanitize=undefined,integer in Clang catches signed overflow at runtime in tests. UBSan logs them; fuzzers find them. This isn't a production defense (sanitizers slow code dramatically), it's a development-time tripwire.
4. Move to memory-safe languages where you can
Rust, Go, C#, Swift — all check arithmetic at compile/runtime. Rust in debug mode panics on overflow; in release mode it wraps but you have checked_mul, saturating_mul, and wrapping_mul as explicit choices. The decision becomes deliberate.
Integer overflow is the bug class that enables other bug classes. It bypasses length checks, miscomputes allocations, and feeds bad values to memory-copying functions that do the actual damage.
In C/C++, every line that multiplies two attacker-controlled values is a potential overflow. Compiler builtins (__builtin_*_overflow) make the check cheap. Sanitizers catch what you missed. And every memory-safe language reduces this attack surface to nearly zero. The investment in safer arithmetic pays for itself in the buffer overflows you no longer have.
References
Formatted in APA 7.
- MITRE. (2024). CWE-190: Integer overflow or wraparound. Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/190.html
- MITRE. (2024). CWE-191: Integer underflow (wrap or wraparound). Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/191.html
- Cybersecurity and Infrastructure Security Agency. (2023). The case for memory safe roadmaps. CISA. https://www.cisa.gov/resources-tools/resources/case-memory-safe-roadmaps
- Seacord, R. (2013). Secure coding in C and C++ (2nd ed.). Addison-Wesley.
- National Institute of Standards and Technology. (2023). CVE-2023-4863 detail (libwebp). National Vulnerability Database. https://nvd.nist.gov/vuln/detail/CVE-2023-4863