Possible integer overflow
| Vulnerability potential | High |
| DDoS potential | Medium |
The variable is likely to overflow in this operation
Impact
When an arithmetic operation produces a result that does not fit in the result
type, the value wraps (for unsigned types) or is undefined (for signed types in
C/C++). A computation that should have grown produces a small or negative number
instead. The damage depends on where the result flows: an overflowed size passed
to malloc allocates too little memory, an overflowed index escapes array
bounds, an overflowed loop counter never terminates, and an overflowed balance
or quota check passes when it should fail. Integer overflow is one of the most
common root causes behind heap overflows and the security bugs built on top of
them.
Vulnerability potential
This issue has a strong potential to become a vulnerability and is a classic exploitation primitive (CWE-190).
- Undersized allocation.
malloc(count * size)overflows, returns a tiny buffer, and the following copy loop writes past it — a heap overflow that often leads to remote code execution. - Bypassed bounds and quota checks. A length validated as
len + header <= maxpasses whenlen + headerwraps to a small value, letting an attacker smuggle an oversized payload through. - Signed overflow is undefined behavior. The compiler may assume it cannot
happen and remove a check such as
if (a + b < a), deleting the very overflow guard the programmer wrote. - Denial of service. A wrapped counter can produce an infinite loop or an absurd allocation request, exhausting CPU or memory.
Technical details
Machine integers are fixed-width, so every type has a finite range. When a result exceeds that range the high bits are lost.
Unsigned types
Unsigned arithmetic in C/C++ is defined to wrap modulo 2^N. UINT_MAX + 1
is 0, and 0u - 1 is UINT_MAX. This is well defined but rarely what the
programmer intended.
Signed types
Signed overflow is undefined behavior in C and C++. In practice two’s
complement hardware wraps (INT_MAX + 1 becomes INT_MIN), but the compiler is
entitled to assume overflow never occurs and optimize accordingly, which is why
relying on the wrap is dangerous.
Integer promotion and conversion
Operands narrower than int are promoted to int before the operation, so
uint16_t a, b; a * b is computed in int and can overflow int. Mixing
signed and unsigned operands triggers the “usual arithmetic conversions”: the
signed operand is converted to unsigned, so len - 1 with len == 0 becomes a
huge unsigned number. Truncating a wide result back into a narrow type
(size_t into int) loses the high bits silently.
Catching the issue
Sanitizers
-fsanitize=signed-integer-overflow (part of UBSan) traps signed overflow at
runtime. -fsanitize=unsigned-integer-overflow flags unsigned wraps too,
useful when wrapping is not intended (but expect noise from code that wraps on
purpose, such as hashes). -ftrapv makes signed overflow abort.
Safe arithmetic
Use the compiler builtins __builtin_add_overflow / __builtin_mul_overflow
(GCC and Clang) or, in C23, <stdckdint.h>’s ckd_add/ckd_mul, which return a
flag instead of a wrapped value. Validate before allocating: check count <=
SIZE_MAX / size before computing count * size.
Static analysis
Coverity, clang-tidy (bugprone-misplaced-widening-cast,
bugprone-implicit-widening-of-multiplication-result), PVS-Studio, and CodeQL
detect many overflow-prone multiplications and size computations.
Compiler warnings
-Wstrict-overflow, -Wconversion, and -Wsign-conversion surface risky
conversions and optimizations that depend on overflow not happening.
How to reproduce
Observe that the multiplication wraps, so malloc receives a tiny size while
the loop writes count elements — a heap overflow. Build with
-fsanitize=undefined to see the overflow reported.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main(void)
{
/* Attacker-controlled element count. */
uint32_t count = 0x40000001u; /* ~1 billion */
size_t bytes = count * 4u; /* overflows 32-bit math: wraps to 4 */
int *p = malloc(bytes); /* allocates 4 bytes, not ~4 GB */
printf("requested %u elements, allocated %zu bytes\n", count, bytes);
for (uint32_t i = 0; i < count; i++)
p[i] = 0; /* writes far past the allocation */
free(p);
return 0;
}