Integer Overflow to Buffer Overflow
Description
Integer Overflow to Buffer Overflow is a compound vulnerability where an integer overflow during memory allocation calculations results in allocating a buffer that is too small, which subsequently leads to a buffer overflow when the program attempts to use the undersized buffer. This typically occurs when calculating the size for memory allocation—multiplying a count by an element size, for example. When the calculation overflows, the result wraps around to a small value, causing malloc() or similar functions to allocate a tiny buffer. The code then treats this undersized buffer as if it had the originally intended capacity, writing far beyond its actual bounds.
Risk
Integer overflow to buffer overflow vulnerabilities are highly dangerous and frequently exploited. Attackers can provide large input values designed to trigger the overflow, gaining control over memory allocation sizes. The subsequent buffer overflow enables heap corruption, which can be leveraged for arbitrary code execution. These vulnerabilities often bypass other security checks because the computed size passes validation before the overflow occurs. The compound nature makes them harder to detect through code review. Real-world exploits have used this pattern for remote code execution in browsers, image processing libraries, and file parsers.
Solution
Validate all inputs that contribute to memory allocation calculations before performing arithmetic. Check for potential overflow before multiplication: if (a > SIZE_MAX / b) then overflow would occur. Use safe integer arithmetic libraries that detect overflow. Consider using calloc() instead of malloc() with multiplication, as some implementations check for overflow. Use compiler options like -ftrapv (GCC) to trap on signed overflow. Employ static analysis tools that detect integer overflow patterns. Apply defense in depth with AddressSanitizer or similar runtime checks during development.
Common Consequences
| Impact | Details |
|---|---|
| Integrity | Scope: Integrity Modify Application Data - Buffer overflow corrupts heap or stack data, potentially modifying security-critical information. |
| Availability | Scope: Availability DoS: Crash, Exit, or Restart - Memory corruption leads to crashes and application instability. |
| Confidentiality | Scope: Confidentiality, Integrity, Availability Execute Unauthorized Code or Commands - Heap-based buffer overflows can be exploited for arbitrary code execution. |
Example Code
Vulnerable Code
// Vulnerable: Integer overflow in allocation size calculation
#include <stdlib.h>
#include <string.h>
typedef struct {
int width;
int height;
unsigned char* pixels;
} Image;
// Vulnerable: Integer overflow before malloc
Image* vulnerable_create_image(int width, int height) {
Image* img = malloc(sizeof(Image));
if (!img) return NULL;
img->width = width;
img->height = height;
// Vulnerable: width * height * 4 can overflow
// If width=65536, height=65536: 65536 * 65536 * 4 overflows 32-bit
// Result might be a small number like 0 or 4
size_t size = width * height * 4; // Integer overflow!
img->pixels = malloc(size); // Allocates tiny buffer
// Later code writes full image data, causing buffer overflow
return img;
}
// Vulnerable: Array allocation with overflow
typedef struct {
char name[32];
int id;
} Record;
Record* vulnerable_load_records(int num_records) {
// Vulnerable: num_records * sizeof(Record) can overflow
// sizeof(Record) = 36, if num_records = 119304648
// 119304648 * 36 = 4,294,967,328 which wraps to small value on 32-bit
Record* records = malloc(num_records * sizeof(Record));
if (records == NULL) {
return NULL;
}
// Later: memcpy or loop writes to all num_records entries
// causing massive buffer overflow
for (int i = 0; i < num_records; i++) {
read_record(&records[i]); // Writes beyond allocated memory
}
return records;
}
// Vulnerable: String concatenation length calculation
char* vulnerable_concat(const char* str1, const char* str2) {
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
// Vulnerable: len1 + len2 + 1 can overflow if strings are huge
size_t total = len1 + len2 + 1; // Overflow wraps to small value
char* result = malloc(total); // Tiny allocation
if (result) {
strcpy(result, str1);
strcat(result, str2); // Buffer overflow!
}
return result;
}
// Vulnerable: Packet header specifies size
void vulnerable_process_packet(unsigned char* packet) {
// Attacker controls packet header
unsigned int element_count = *(unsigned int*)packet;
unsigned int element_size = *(unsigned int*)(packet + 4);
// Vulnerable: Multiplication overflow
unsigned int total_size = element_count * element_size; // Overflow!
char* buffer = malloc(total_size); // Small allocation
// Copy packet data into buffer - overflow
memcpy(buffer, packet + 8, element_count * element_size);
process_data(buffer);
free(buffer);
}
Fixed Code
// Fixed: Safe integer arithmetic with overflow checking
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <limits.h>
// Safe multiplication with overflow check
int safe_multiply(size_t a, size_t b, size_t* result) {
if (a > 0 && b > SIZE_MAX / a) {
return -1; // Would overflow
}
*result = a * b;
return 0;
}
// Safe addition with overflow check
int safe_add(size_t a, size_t b, size_t* result) {
if (a > SIZE_MAX - b) {
return -1; // Would overflow
}
*result = a + b;
return 0;
}
// Fixed: Image creation with overflow protection
typedef struct {
int width;
int height;
unsigned char* pixels;
} Image;
Image* secure_create_image(int width, int height) {
// Fixed: Validate inputs
if (width <= 0 || height <= 0) {
return NULL;
}
// Fixed: Check for overflow before allocation
size_t pixel_count;
if (safe_multiply((size_t)width, (size_t)height, &pixel_count) != 0) {
return NULL; // Overflow would occur
}
size_t size;
if (safe_multiply(pixel_count, 4, &size) != 0) {
return NULL; // Overflow would occur
}
// Additional sanity check
if (size > MAX_IMAGE_SIZE) {
return NULL; // Unreasonably large
}
Image* img = malloc(sizeof(Image));
if (!img) return NULL;
img->width = width;
img->height = height;
img->pixels = malloc(size);
if (!img->pixels) {
free(img);
return NULL;
}
return img;
}
// Fixed: Using calloc which may check for overflow
Record* secure_load_records(size_t num_records) {
// Fixed: Validate count
if (num_records == 0 || num_records > MAX_RECORDS) {
return NULL;
}
// Fixed: calloc() with separate count and size
// Many implementations check for overflow internally
Record* records = calloc(num_records, sizeof(Record));
if (records == NULL) {
return NULL;
}
for (size_t i = 0; i < num_records; i++) {
if (read_record(&records[i]) != 0) {
free(records);
return NULL;
}
}
return records;
}
// Fixed: String concatenation with overflow checking
char* secure_concat(const char* str1, const char* str2) {
if (str1 == NULL || str2 == NULL) {
return NULL;
}
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
// Fixed: Check for overflow
size_t total;
if (safe_add(len1, len2, &total) != 0) {
return NULL;
}
if (safe_add(total, 1, &total) != 0) {
return NULL;
}
char* result = malloc(total);
if (result) {
memcpy(result, str1, len1);
memcpy(result + len1, str2, len2 + 1); // Include null terminator
}
return result;
}
// Fixed: Packet processing with safe arithmetic
#define MAX_PACKET_SIZE (1024 * 1024) // 1MB max
int secure_process_packet(unsigned char* packet, size_t packet_len) {
if (packet_len < 8) {
return -1; // Packet too small for header
}
unsigned int element_count = *(unsigned int*)packet;
unsigned int element_size = *(unsigned int*)(packet + 4);
// Fixed: Check for overflow before multiplication
size_t total_size;
if (safe_multiply((size_t)element_count, (size_t)element_size,
&total_size) != 0) {
return -1;
}
// Fixed: Sanity check on size
if (total_size > MAX_PACKET_SIZE) {
return -1;
}
// Fixed: Verify packet contains enough data
if (packet_len < 8 + total_size) {
return -1;
}
char* buffer = malloc(total_size);
if (buffer == NULL) {
return -1;
}
memcpy(buffer, packet + 8, total_size);
process_data(buffer);
free(buffer);
return 0;
}
// Modern C11 approach using checked arithmetic (if available)
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#include <stdckdint.h> // C23 checked integer arithmetic
Image* modern_create_image(int width, int height) {
if (width <= 0 || height <= 0) return NULL;
size_t pixel_count, size;
// C23 checked multiplication
if (ckd_mul(&pixel_count, (size_t)width, (size_t)height)) {
return NULL; // Overflow
}
if (ckd_mul(&size, pixel_count, 4)) {
return NULL; // Overflow
}
Image* img = malloc(sizeof(Image));
if (!img) return NULL;
img->pixels = malloc(size);
if (!img->pixels) {
free(img);
return NULL;
}
img->width = width;
img->height = height;
return img;
}
#endif
CVE Examples
- CVE-2021-43537: Type casting error causing integer overflow leading to heap buffer overflow.
- CVE-2017-1000121: Unchecked user-controlled metadata values leading to integer overflow and buffer corruption.
References
- MITRE Corporation. "CWE-680: Integer Overflow to Buffer Overflow." https://cwe.mitre.org/data/definitions/680.html
- CERT C Coding Standard. "INT30-C. Ensure that unsigned integer operations do not wrap."
- CERT C Coding Standard. "INT32-C. Ensure that operations on signed integers do not result in overflow."