Wrap-around Error
Description
Wrap-around Error occurs when an integer value is incremented beyond its maximum representable value, causing it to "wrap around" to its minimum value (often zero or a large negative number). In unsigned integers, incrementing past the maximum wraps to zero. In signed integers, overflow behavior is undefined in C/C++ but typically wraps to the minimum negative value. Wrap-around errors are particularly dangerous when the affected integer is used for security-critical calculations such as buffer sizes, array indices, or loop counters, potentially leading to buffer overflows, infinite loops, or other memory corruption.
Risk
Wrap-around errors are highly exploitable because they can transform large values into small or negative values, bypassing security checks. A classic attack involves providing a large size value that wraps to a small allocation size, followed by operations that assume the original large size—resulting in massive buffer overflows. Wrap-around in loop counters can cause infinite loops or premature termination. These vulnerabilities have enabled remote code execution in countless applications, particularly those handling network protocols, file formats, or user-supplied size parameters.
Solution
Always validate integer values before arithmetic operations that could cause wrap-around. Use safe integer arithmetic libraries that detect overflow (SafeInt in C++, checked arithmetic in Rust). Check for maximum values before incrementing. Use larger integer types when possible (64-bit instead of 32-bit). Implement explicit overflow checks before size calculations used in memory allocation. Consider compiler flags that trap on overflow (-ftrapv in GCC). Avoid unsigned integers for values that will be subtracted, as underflow wraps to large positive values.
Common Consequences
| Impact | Details |
|---|---|
| Integrity | Scope: Memory Corruption Wrap-around in size calculations leads to undersized buffer allocations and subsequent overflows when the original value is used. |
| Availability | Scope: Denial of Service Wrap-around in loop counters causes infinite loops; wrap-around in array indices causes crashes. |
| Access Control | Scope: Code Execution Buffer overflows resulting from wrap-around errors can enable arbitrary code execution. |
Example Code + Solution Code
Vulnerable Code
#include <stdlib.h>
#include <string.h>
// VULNERABLE: Integer wrap-around in size calculation
void allocate_table(unsigned int num_entries) {
// If num_entries is large, multiplication wraps around
size_t size = num_entries * sizeof(int); // Wraps to small value
int *table = malloc(size); // Allocates tiny buffer
// Later code treats table as having num_entries elements
for (unsigned int i = 0; i < num_entries; i++) {
table[i] = 0; // Massive buffer overflow
}
}
// VULNERABLE: Increment wrap-around
void process_loop(unsigned char count) {
unsigned char i = 0;
// If count is 255 and incremented, wraps to 0
while (i <= count) {
process_item(i);
i++; // When i=255, i++ wraps to 0, loop never ends
}
}
// VULNERABLE: Addition overflow in bounds check
int safe_add_wrong(int a, int b, int *result) {
// This check is insufficient - overflow happens before comparison
if (a + b < a) { // Undefined behavior in signed overflow
return -1;
}
*result = a + b;
return 0;
}
Fixed Code
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <limits.h>
// SAFE: Check for overflow before multiplication
void *allocate_table_safe(size_t num_entries) {
// Check for multiplication overflow
if (num_entries > SIZE_MAX / sizeof(int)) {
return NULL; // Would overflow
}
size_t size = num_entries * sizeof(int);
int *table = malloc(size);
if (table) {
memset(table, 0, size); // Use calculated size
}
return table;
}
// SAFE: Avoid wrap-around in loop
void process_loop_safe(unsigned int count) {
for (unsigned int i = 0; i < count; i++) {
process_item(i);
}
// Handle the last item separately if needed
if (count > 0) {
process_item(count);
}
}
// SAFE: Check for overflow before arithmetic
int safe_add(int a, int b, int *result) {
// Check before performing addition
if (b > 0 && a > INT_MAX - b) {
return -1; // Would overflow
}
if (b < 0 && a < INT_MIN - b) {
return -1; // Would underflow
}
*result = a + b;
return 0;
}
// SAFE: Use size_t for sizes and check overflow
size_t safe_multiply(size_t a, size_t b) {
if (a != 0 && b > SIZE_MAX / a) {
return 0; // Overflow, return error indicator
}
return a * b;
}
Exploited in the Wild
OpenSSH Challenge-Response (OpenSSH, 2002)
CVE-2002-0639 was an integer overflow in OpenSSH's challenge-response authentication that allowed remote attackers to execute arbitrary code by exploiting wrap-around in a size calculation.
Windows Image Processing (Microsoft, Multiple)
Multiple integer overflow vulnerabilities in Windows GDI+ and image processing libraries have been exploited through malicious image files with crafted dimension values that cause wrap-around.
PNG Library Integer Overflow (libpng, 2015)
CVE-2015-8126 was an integer overflow in libpng that could lead to heap buffer overflows when processing PNG images with maliciously crafted chunk sizes.
Tools to test/exploit
-
UBSan — undefined behavior sanitizer that detects signed integer overflow.
-
SafeInt — C++ library for safe integer arithmetic with overflow detection.
-
AFL++ — fuzzer effective at discovering integer overflow conditions.
CVE Examples
-
CVE-2002-0639 — OpenSSH integer overflow in challenge-response authentication.
-
CVE-2015-8126 — libpng integer overflow leading to buffer overflow.
-
CVE-2021-21300 — Git integer overflow enabling RCE on case-insensitive filesystems.
References
-
MITRE. "CWE-128: Wrap-around Error." https://cwe.mitre.org/data/definitions/128.html
-
CERT. "INT30-C. Ensure that unsigned integer operations do not wrap." https://wiki.sei.cmu.edu/confluence/display/c/INT30-C