ASVS 5.0 V1.4.2 requires that you use sign checks, range checks and input validation so that arithmetic on attacker influenced numbers cannot overflow or wrap. It is a Level 2 requirement, so it applies to most applications that handle sensitive data.
Why it matters
An integer overflow rarely crashes anything on its own. The damage comes next. In C and C++, a wrapped size calculation leads to an undersized allocation and a heap overflow when the data is copied in. In Java, Go and C#, a wrapped value can turn a quantity of 2147483647 plus one into a large negative number, which then bypasses a price check or a limit.
Memory safe languages remove the memory corruption but not the logic flaw. Negative quantities, wrapped counters and truncated casts still produce wrong money, wrong access decisions and wrong loop bounds.
How to implement it
Validate the range of every number that comes from outside, then use arithmetic that detects overflow instead of assuming it will not happen.
In C, check before multiplying:
#include <stdint.h>
#include <stdlib.h>
void *alloc_array(size_t count, size_t elem_size) {
if (elem_size != 0 && count > SIZE_MAX / elem_size) {
return NULL; /* would overflow */
}
return malloc(count * elem_size);
}
In Java, use the exact methods, which throw instead of wrapping:
int total = Math.multiplyExact(quantity, unitPriceCents);
In Rust, use checked_mul and handle None. In every language, set minimum and maximum bounds at the API boundary, for example a quantity between 1 and 1000, and reject the rest. Watch for narrowing casts, such as a 64 bit value stored in a 32 bit field, and for signed values used where an unsigned size is expected.
How to test it
Feed boundary values to every numeric input: 0, 1, -1, 2147483647, 2147483648, 4294967295, 4294967296 and the maximum of the type you use. Check that the application rejects them or returns a controlled error, and that no stored value changes sign.
For native code, build with sanitizers. With clang, -fsanitize=undefined,integer reports overflow at runtime, and a fuzzer such as libFuzzer will explore the boundaries for you.
Where overflows slip in
Be most careful with sizes, counts, offsets and money. A request that says "read 4 gigabytes starting at offset 4 gigabytes minus 1" tests every addition in your parser. Pay attention to conversions between signed and unsigned types, and between 64 bit and 32 bit values. A length read from the network as a signed integer can be negative, and a negative value cast to size_t becomes enormous.
In languages with arbitrary precision integers, such as Python, the overflow moves to the boundary: a database column, a protocol field, or a native extension that expects a fixed width. Validate the range there.
Common mistakes
- Checking the result after the overflow has happened instead of the operands before it.
- Validating a number as a string length but not as a value.
- Trusting that the database column type will reject out of range values. Some configurations truncate silently.
- Using a signed type for sizes and forgetting that negative values pass a simple upper bound check.
Quick check
Why is the check count times size below limit a broken overflow check?
The multiplication happens first, so it can already have wrapped to a small number that passes the comparison. Divide the limit by one operand and compare against the other instead.