CWE-190: Integer Overflow or Wraparound
Overview
Integer overflow occurs when an arithmetic operation produces a result larger than the maximum value its integer type can hold, causing the value to wrap around to a negative or small positive number. Wraparound in a size, index, or bounds-check calculation turns a value that looked large enough into one that's too small, or a value meant to be rejected into one that passes.
Relationship to Other CWEs
- CWE-190 (this page) - an arithmetic operation whose result exceeds its type's maximum and wraps
- CWE-682 (Incorrect Calculation) - the parent this page is ChildOf. No page here
- CWE-680 (Integer Overflow to Buffer Overflow) - this page's child, for the specific chain where a wrapped size feeds directly into a memory-copy operation. No page here
- CWE-191 (Integer Underflow) - the mirror-image case, where a result falls below the type's minimum rather than above its maximum. MITRE's own guidance distinguishes the two explicitly; a scan finding described as "wraparound" can be either direction, so confirm which boundary the calculation actually crosses
- CWE-192 (Integer Coercion Error) and its siblings CWE-195, CWE-196 and CWE-197 - the conversion weaknesses under CWE-681 (Incorrect Conversion between Numeric Types), covering signed/unsigned and truncation conversions. Their root cause is an unsafe type conversion producing an unexpected value, where this page is an arithmetic operation exceeding its type's range
The two families chain together constantly - a coercion produces a value that then overflows, or an overflowing calculation is then truncated by a narrowing conversion - but they are distinct weaknesses. MITRE lists no formal ChildOf/ParentOf relationship between CWE-190 and either CWE-191 or the CWE-681 family: the connection is a "commonly confused with" one, not a hierarchy.
Risk
High: Integer overflows cause buffer overflows when the wrapped value sizes an allocation or a copy, security-check bypass when it feeds a comparison or a permission count, and infinite loops. This is a classic root cause behind buffer-sizing and length-calculation vulnerabilities.
Remediation Steps
Core Principle: Validate operand ranges before arithmetic, or use an operation that detects overflow itself, rather than trusting the result of unchecked arithmetic.
Trace the Data Path
- Source: where the operands come from - user input, a file or network field, a prior calculation.
- Sink: what the result is used for - a memory allocation size, an array index, a bounds check, a security-relevant count or limit.
- Missing controls: whether the operands are validated against a safe range before the arithmetic runs, and whether the arithmetic itself can detect its own overflow.
Use Overflow-Checked Arithmetic (Primary Defense)
- Use the platform's built-in overflow-checked operation where one exists (a compiler builtin, a language-level checked-arithmetic method, or checked-arithmetic mode) instead of a plain
+/-/*. - Where no checked operation exists, verify the operands are within safe range before the operation - for example, confirm
a == 0 or b <= MAX / abefore computinga * b, not after. That form has two preconditions and both have to be met: the zero case must be excluded explicitly or the check divides by zero, and both operands must already be known non-negative. On signed operands it is not a valid check -a = -1, b = INT_MINsatisfiesb <= MAX / aand the product still overflows, measured on JDK 26. Reject negatives first, or use a checked-arithmetic primitive, which has no such precondition. - Use an arbitrary-precision or wider integer type for calculations whose legitimate range can exceed the native type, then validate the result against a practical application-level limit before using it.
Validate Operands and Results (Defense in Depth)
- Reject unreasonably large operands early, before they reach arithmetic - a length field of several gigabytes is rarely legitimate regardless of whether the arithmetic on it would technically succeed.
- After the arithmetic, re-check that the result is within the range the calling code actually expects, not just "did not overflow."
- Bounds-check before indexing: verify
index < size(andindex >= 0) immediately before the access, not based on an earlier calculation that might itself have wrapped.
Test the Fix
- Test with boundary values (max value, max value minus one, min value, zero, negative one where applicable).
- Test with values specifically chosen to overflow the operation under test.
- Verify the overflow-checked path actually rejects the overflow case - test the failure path, not just that normal-sized inputs still work.
- Re-scan with the security scanner to confirm the finding is resolved.
Common Vulnerable Patterns
A wrapped product used as an allocation size
// VULNERABLE - unchecked multiplication used to size an allocation
total = count * itemSize
allocate(total) // wraps to a small value; caller then writes count*itemSize bytes
Why this is vulnerable: the multiply wraps silently - no exception, no status flag, no warning - so total is simply not the number that was asked for, and the allocation succeeds at the wrong size. Everything downstream behaves as though the request had been honoured, and the writes that follow are bounded by the number the caller still believes rather than by the buffer that actually exists.
Checking the result afterwards does not close it. A sign test catches only the wraps that land in the negative half; those landing on a small positive value, or exactly on zero, pass it while being just as wrong. The information needed to tell a wrapped value from a genuine one was destroyed by the multiply itself, so the check has to happen before or during the arithmetic - bounds on the operands, or a checked-arithmetic operation that reports the overflow instead of discarding it.
An overflow check that can itself overflow
// VULNERABLE - the check depends on the overflow it is testing for
if (destSize + srcSize < destSize): // sound where the wrap is defined; in C/C++ signed
reject() // arithmetic it is undefined, so a compiler may assume
copy(dest, src, srcSize) // it cannot happen and drop the check entirely
Why this is vulnerable: this idiom works wherever the wrap is defined: unsigned types in any language, and signed int/long in Java or unchecked C#, where a sum smaller than one of its operands really does prove wraparound happened. What breaks it is C and C++ signed arithmetic, where overflow is undefined behaviour rather than a modular wrap - a compiler may reason that destSize + srcSize never wraps, conclude the comparison is always false, and remove the branch. The guard is gone from the binary while the source still shows it.
Being defined is not the same as being a good check, which is why the pattern is here rather than under Secure Patterns. It still computes the wrapped value before testing it, so every later use of that variable is holding a wrong number; it only detects a wrap of the sum and says nothing about whether srcSize fits in destSize; and it does not generalise - the same shape written for a multiply, or for a subtraction, does not work at all.
In C and C++ that makes this the harder version to review, because whether the line is a working check or no check at all depends on the declared types of its operands - and those are often a typedef, a platform-dependent alias, or an inferred type, none of which answer the question where the check is written. A checked-arithmetic primitive, or a pre-check phrased so the dangerous operation never runs (srcSize > limit - destSize, after confirming 0 <= destSize <= limit), says the same thing without depending on the answer. The lower bound belongs in that check for the same reason it belongs in the division form above: a negative destSize makes limit - destSize the overflowing operation.
Secure Patterns
// SECURE - detect overflow by division before multiplying
if count < 0 or itemSize < 0: // the division check below is only valid for
reject() // non-negative operands - see Why this works
if itemSize != 0 and count > MAX_VALUE / itemSize:
reject() // would overflow
total = count * itemSize
if total > REASONABLE_LIMIT: // sanity cap, independent of overflow
reject()
allocate(total)
// SECURE - checked-arithmetic operation that signals overflow instead of wrapping
result, overflowed = checkedAdd(destSize, srcSize)
if overflowed or result > MAX_BUFFER:
reject()
Why this works: Checking for overflow before performing the operation means the check itself cannot be defeated by the same wraparound it's meant to catch, and it fails closed instead of silently producing a wrapped value that later code trusts.
The negative-operand rejection is what makes the division form sound, not a defensive extra. MAX_VALUE / itemSize compares against the wrong bound once either operand can be negative: with count = -1 and itemSize = INT_MIN the test passes and the product overflows anyway. Unsigned types get this for free, which is why the C page's size_t version needs no such line; anywhere the operands are signed, the check is incomplete without it. The checked-arithmetic form in the second example has no precondition at all and is the better default where the platform offers one.
Common Pitfalls
- Writing the overflow check as a post-hoc comparison on plain arithmetic:
if (a + b < a)detects wraparound after the fact only where the wrap is defined - any unsigned type, or Java and unchecked C# signed arithmetic. On a signed type in C or C++ the check itself performs undefined behaviour, and the compiler may delete it. Even where it is defined it is the weaker option, because it inspects a result that is already wrong; use a pre-check or a checked-arithmetic primitive instead. - Adding an overflow check without an application-level sanity cap: a value that mathematically doesn't overflow the integer type can still be an unreasonable allocation size (megabytes to gigabytes) - overflow-safety and "reasonable size" are two separate checks, not one.
- Enabling overflow detection only in development/CI builds: compiler sanitizers and checked-arithmetic debug modes catch the bug during testing, but if the equivalent checked code path isn't also shipped to production, the release build is exactly as vulnerable as before.
- Validating one operand's range but not the product/sum of several: each individual field can pass a per-field range check while their combination still overflows - validate the calculation's result, not just each input in isolation.
Language-Specific Guidance
- C -
SIZE_MAX/division pre-checks,__builtin_*_overflow, UBSan - Java -
Math.addExact/Math.multiplyExact,BigInteger, Bean Validation - Python - arbitrary-precision
int, NumPy/FFI boundary overflow, practical size limits