Improper Restriction of Operations within the Bounds of a Memory Buffer
Description
Improper Restriction of Operations within the Bounds of a Memory Buffer occurs when software performs operations on a memory buffer but can read from or write to a memory location that is outside the intended boundary of the buffer. This is the parent category for many specific buffer error types including buffer overflows (CWE-120), buffer underflows, out-of-bounds reads (CWE-125), and out-of-bounds writes (CWE-787). These vulnerabilities occur when array indexes, pointer arithmetic, or size calculations allow access beyond allocated memory regions.
Risk
Memory buffer errors are consistently among the most exploited vulnerability classes. Out-of-bounds writes can overwrite adjacent memory including return addresses, function pointers, and security-critical data. Out-of-bounds reads can leak sensitive information including cryptographic keys and memory layout (defeating ASLR). Modern mitigations (ASLR, DEP, stack canaries) have made exploitation harder but not impossible. Browser exploits, kernel vulnerabilities, and firmware attacks frequently exploit buffer errors.
Solution
Use memory-safe languages (Rust, Go, Java, Python) when possible. In C/C++, use bounds-checking functions and safe string libraries. Validate all array indices and buffer sizes. Use compiler protections (stack canaries, ASLR, DEP). Employ static and dynamic analysis tools. Use AddressSanitizer during development. Consider using safe containers (std::vector, std::string) in C++. Implement defense in depth with multiple layers of protection.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Information Disclosure Out-of-bounds reads can expose sensitive data from adjacent memory regions. |
| Integrity | Scope: Code Execution Out-of-bounds writes can corrupt code pointers enabling arbitrary code execution. |
| Availability | Scope: Denial of Service Buffer errors typically cause crashes and can be used for DoS attacks. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: No bounds checking on array access
void process_data(int *data, int index, int value) {
// No validation of index
data[index] = value; // Out-of-bounds write if index invalid
}
// VULNERABLE: Buffer overflow in string copy
void copy_user_input(char *input) {
char buffer[64];
strcpy(buffer, input); // Overflow if input > 63 chars
}
// VULNERABLE: Off-by-one in loop
void fill_array(int *arr, int size) {
// Bug: <= instead of <
for (int i = 0; i <= size; i++) {
arr[i] = 0; // Writes one element past end
}
}
// VULNERABLE: Integer overflow in size calculation
void allocate_matrix(unsigned int rows, unsigned int cols) {
// Multiplication can overflow
unsigned int size = rows * cols * sizeof(int);
int *matrix = malloc(size); // May allocate undersized buffer
// Subsequent access causes overflow
}
// VULNERABLE: Out-of-bounds read
char get_character(char *str, int index) {
// No bounds checking
return str[index]; // Can read past string bounds
}
// VULNERABLE: Pointer arithmetic without bounds
void process_packet(unsigned char *packet, size_t length) {
unsigned char *ptr = packet;
unsigned char *end = packet + length;
while (*ptr != 0) { // May read past packet end
process_byte(*ptr);
ptr++;
}
}
// VULNERABLE: Heap buffer overflow
void resize_buffer(Buffer *buf, size_t new_size) {
// If new_size > allocated size, writes overflow
buf->size = new_size;
memset(buf->data + buf->used, 0, new_size - buf->used);
}
// VULNERABLE: Stack buffer read overflow
void log_data(char *data, size_t len) {
char buffer[256];
// If len > 256, copies too much, then reads past buffer
memcpy(buffer, data, len);
printf("Data: %s\n", buffer); // May read past if no null terminator
}
// VULNERABLE: C++ with raw arrays
class DataStore {
int data[100];
public:
int& get(size_t index) {
return data[index]; // No bounds check
}
void set(size_t index, int value) {
data[index] = value; // No bounds check
}
};
// VULNERABLE: Iterator invalidation
void remove_items(std::vector<int>& vec) {
for (auto it = vec.begin(); it != vec.end(); ++it) {
if (*it < 0) {
vec.erase(it); // Invalidates iterator!
// Continuing to use it is undefined behavior
}
}
}
Fixed Code
// SAFE: Bounds checking on array access
bool process_data_safe(int *data, size_t data_size, size_t index, int value) {
if (index >= data_size) {
return false; // Out of bounds
}
data[index] = value;
return true;
}
// SAFE: Bounds-checked string copy
void copy_user_input_safe(const char *input) {
char buffer[64];
// strncpy limits copy length
strncpy(buffer, input, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0'; // Ensure null termination
}
// SAFE: Correct loop bounds
void fill_array_safe(int *arr, size_t size) {
for (size_t i = 0; i < size; i++) {
arr[i] = 0;
}
}
// SAFE: Integer overflow check
int *allocate_matrix_safe(size_t rows, size_t cols) {
// Check for overflow
if (rows > 0 && cols > SIZE_MAX / rows) {
return NULL;
}
size_t count = rows * cols;
if (count > SIZE_MAX / sizeof(int)) {
return NULL;
}
size_t size = count * sizeof(int);
return malloc(size);
}
// SAFE: Bounds-checked character access
bool get_character_safe(const char *str, size_t str_len, size_t index, char *out) {
if (index >= str_len) {
return false;
}
*out = str[index];
return true;
}
// SAFE: Pointer arithmetic with bounds checking
void process_packet_safe(unsigned char *packet, size_t length) {
if (length == 0) return;
unsigned char *ptr = packet;
unsigned char *end = packet + length;
while (ptr < end && *ptr != 0) {
process_byte(*ptr);
ptr++;
}
}
// SAFE: Validated buffer resize
bool resize_buffer_safe(Buffer *buf, size_t new_size) {
if (new_size > buf->allocated) {
// Need to reallocate
void *new_data = realloc(buf->data, new_size);
if (!new_data) return false;
buf->data = new_data;
buf->allocated = new_size;
}
if (new_size > buf->used) {
memset(buf->data + buf->used, 0, new_size - buf->used);
}
buf->size = new_size;
return true;
}
// SAFE: Bounds-checked logging
void log_data_safe(const char *data, size_t len) {
char buffer[256];
size_t copy_len = (len < sizeof(buffer) - 1) ? len : sizeof(buffer) - 1;
memcpy(buffer, data, copy_len);
buffer[copy_len] = '\0'; // Always null terminate
printf("Data: %s\n", buffer);
}
// SAFE: Use std::array with bounds checking
#include <array>
#include <stdexcept>
class DataStoreSafe {
std::array<int, 100> data;
public:
int& get(size_t index) {
return data.at(index); // Throws std::out_of_range
}
void set(size_t index, int value) {
data.at(index) = value; // Throws std::out_of_range
}
size_t size() const { return data.size(); }
};
// SAFE: Use std::vector with at()
void process_vector_safe(std::vector<int>& vec, size_t index) {
try {
int value = vec.at(index); // Bounds checked
process(value);
} catch (const std::out_of_range& e) {
handle_error("Index out of range");
}
}
// SAFE: Proper iterator handling
void remove_items_safe(std::vector<int>& vec) {
// Use erase-remove idiom
vec.erase(
std::remove_if(vec.begin(), vec.end(),
[](int x) { return x < 0; }),
vec.end()
);
}
// Alternative: careful iterator update
void remove_items_safe_alt(std::vector<int>& vec) {
for (auto it = vec.begin(); it != vec.end(); /* no increment */) {
if (*it < 0) {
it = vec.erase(it); // erase returns next valid iterator
} else {
++it;
}
}
}
// Rust: Memory safety by default
fn process_data_rust(data: &mut [i32], index: usize, value: i32) -> Result<(), &'static str> {
// Bounds checking is automatic
if let Some(elem) = data.get_mut(index) {
*elem = value;
Ok(())
} else {
Err("Index out of bounds")
}
}
// Rust: Safe iteration
fn remove_negatives(vec: &mut Vec<i32>) {
vec.retain(|&x| x >= 0); // Safe removal
}
Exploited in the Wild
Heartbleed (2014)
CVE-2014-0160 was an out-of-bounds read in OpenSSL that exposed up to 64KB of server memory per request, potentially including private keys and user data.
EternalBlue (2017)
CVE-2017-0144 was a buffer overflow in Windows SMBv1 that was exploited by WannaCry and NotPetya ransomware, causing billions in damages worldwide.
Chrome V8 Buffer Overflows
Numerous buffer overflow vulnerabilities in Chrome's V8 JavaScript engine have been exploited in the wild for browser-based attacks.
Tools to test/exploit
-
AddressSanitizer — runtime memory error detection.
-
Valgrind — memory error and leak detection.
-
AFL++ — fuzzing to find buffer errors.
-
Coverity — static analysis for buffer issues.
CVE Examples
-
CVE-2014-0160 — Heartbleed buffer over-read.
-
CVE-2017-0144 — EternalBlue SMB buffer overflow.
-
CVE-2021-44228 — Log4Shell (related memory issues).
References
-
MITRE. "CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer." https://cwe.mitre.org/data/definitions/119.html
-
CERT. "ARR30-C: Do not form or use out-of-bounds pointers or array subscripts." https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts