Access of Memory Location Before Start of Buffer
Description
Access of Memory Location Before Start of Buffer is a memory safety vulnerability where software reads or writes to a memory location that precedes the beginning of an allocated buffer. This typically occurs when a pointer or array index is decremented beyond the buffer's starting address, when pointer arithmetic produces a negative offset, or when a negative index is explicitly used. Also known as "buffer underread" or "buffer underwrite" depending on the operation, this vulnerability can expose sensitive data, corrupt memory, or enable code execution.
Risk
Accessing memory before a buffer's start is dangerous because that memory may contain sensitive data, security-critical values, or be completely unmapped. Reading before buffer start can expose sensitive information like passwords, keys, or memory layout details useful for exploitation. Writing before buffer start corrupts adjacent data structures, potentially including function pointers, heap metadata, or security flags. On the heap, underflow can corrupt allocator metadata, enabling heap exploitation techniques. The vulnerability is particularly dangerous because the accessed memory is often valid and won't immediately cause a crash, allowing silent corruption.
Solution
Implement bounds checking on all buffer accesses. Validate array indices are non-negative before use. Be especially careful with loops that decrement indices or pointers—ensure they stop at the buffer's start. Use signed integer types carefully and validate that values remain non-negative. Consider using safe container classes that perform automatic bounds checking. Use memory safety tools like AddressSanitizer during development and testing. Implement defensive checks when processing strings with only whitespace or empty content. Prefer using size_t for indices but validate against underflow before decrement.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Memory - Underreads expose sensitive data from memory before the buffer. |
| Integrity | Scope: Integrity Modify Memory - Underwrites corrupt data structures adjacent to the buffer. |
| Availability | Scope: Availability DoS: Crash - Memory corruption or access violations cause crashes. |
| Integrity | Scope: Integrity Execute Unauthorized Code - Corrupted function pointers or heap metadata enable code execution. |
Example Code
Vulnerable Code
// Vulnerable: Index decremented below zero
char* trimTrailingWhitespace(char *strMessage, int length) {
char *message = malloc(length + 1);
if (!message) return NULL;
memcpy(message, strMessage, length);
message[length] = '\0';
int index = length - 1;
// Vulnerable: If string is all whitespace, index goes negative
while (isspace(message[index])) {
message[index] = '\0';
index--;
// No check: index can become -1, -2, etc.
}
return message;
}
// Attack: Pass string " " (all spaces)
// index starts at 2, decrements to -1, accessing message[-1]
// Vulnerable: Negative array index from calculation
void vulnerable_process_data(int* data, int size, int offset) {
// Vulnerable: offset could be negative or larger than expected
for (int i = offset; i < size; i++) {
// If offset is negative, accesses before data start
data[i] = process(data[i]);
}
}
// Vulnerable: Signed/unsigned mismatch
void vulnerable_copy(char* dest, const char* src, int length) {
// Vulnerable: if length is negative, this underflows
for (int i = length - 1; i >= 0; i--) {
dest[i] = src[i];
// Compiler may optimize "i >= 0" away for unsigned types
}
}
// Vulnerable: Pointer arithmetic underflow
void vulnerable_parse_backward(char* buffer, size_t size) {
char* ptr = buffer + size - 1; // Point to last char
// Vulnerable: Decrements past buffer start
while (*ptr == ' ') {
ptr--;
// No check if ptr < buffer
}
*ptr = '\0'; // May write before buffer
}
// Vulnerable: Off-by-one in reverse iteration
void vulnerable_reverse(int* array, int length) {
// Vulnerable: Starting from length instead of length-1
for (int i = length; i > 0; i--) {
// First iteration accesses array[length] - past end!
// Then as i decrements, eventually tries to handle index 0 twice
process(array[i]);
}
}
// Vulnerable: User-controlled index without validation
typedef struct {
int values[10];
char name[32];
} Record;
void vulnerable_set_value(Record* rec, int index, int value) {
// Vulnerable: No bounds check on index
// Negative index accesses memory before values array
rec->values[index] = value;
}
// Attack: index = -1 overwrites memory before values[]
// This could modify fields of previous struct on heap
Fixed Code
// Fixed: Bounds checking on index decrement
char* fixed_trimTrailingWhitespace(char *strMessage, int length) {
if (length <= 0) {
char* empty = malloc(1);
if (empty) empty[0] = '\0';
return empty;
}
char *message = malloc(length + 1);
if (!message) return NULL;
memcpy(message, strMessage, length);
message[length] = '\0';
int index = length - 1;
// Fixed: Check index >= 0 before access
while (index >= 0 && isspace((unsigned char)message[index])) {
message[index] = '\0';
index--;
}
return message;
}
// Fixed: Validate offset before use
void fixed_process_data(int* data, int size, int offset) {
// Fixed: Validate offset is in valid range
if (offset < 0 || offset >= size) {
return; // Or handle error appropriately
}
for (int i = offset; i < size; i++) {
data[i] = process(data[i]);
}
}
// Fixed: Use size_t with underflow check
void fixed_copy(char* dest, const char* src, size_t length) {
if (length == 0) return;
// Fixed: Iterate forward to avoid underflow issues
for (size_t i = 0; i < length; i++) {
dest[i] = src[i];
}
}
// Fixed: Pointer bounds checking
void fixed_parse_backward(char* buffer, size_t size) {
if (size == 0) return;
char* ptr = buffer + size - 1;
char* start = buffer; // Remember start for bounds check
// Fixed: Check pointer against buffer start
while (ptr >= start && *ptr == ' ') {
ptr--;
}
// Only modify if within bounds
if (ptr >= start) {
*(ptr + 1) = '\0';
} else {
buffer[0] = '\0'; // Entire string was spaces
}
}
// Fixed: Correct loop bounds
void fixed_reverse(int* array, int length) {
if (length <= 0) return;
// Fixed: Start from length-1 (last valid index)
for (int i = length - 1; i >= 0; i--) {
process(array[i]);
}
}
// Fixed: Validate array index
typedef struct {
int values[10];
char name[32];
} Record;
bool fixed_set_value(Record* rec, int index, int value) {
// Fixed: Validate index is within bounds
if (index < 0 || index >= 10) {
return false; // Invalid index
}
rec->values[index] = value;
return true;
}
// Alternative: Use unsigned type and check upper bound only
bool fixed_set_value_v2(Record* rec, unsigned int index, int value) {
if (index >= 10) {
return false; // Unsigned can't be negative
}
rec->values[index] = value;
return true;
}
CVE Examples
- CVE-2002-2227: SSLv2 challenge value handling caused buffer underflow vulnerability.
- CVE-2007-4580: Buffer underflow from size/length inconsistency in file parsing.
- CVE-2007-1584: Underflow triggered by processing strings containing only whitespace.
- CVE-2006-4024: Negative parameter passed to memcpy causing buffer underflow.
References
- MITRE Corporation. "CWE-786: Access of Memory Location Before Start of Buffer." https://cwe.mitre.org/data/definitions/786.html
- CERT C Coding Standard. "ARR30-C. Do not form or use out-of-bounds pointers or array subscripts."
- CWE. "CWE-124: Buffer Underwrite ('Buffer Underflow')." Related weakness.