Buffer Access Using Size of Source Buffer
Description
Buffer Access Using Size of Source Buffer is a specific memory safety vulnerability where software incorrectly uses the size of a source buffer when determining how many bytes to copy to a destination buffer. This is a common coding mistake: developers use sizeof(source) or the source's length when the destination buffer is smaller, causing data to be written beyond the destination's boundaries. When the source buffer is larger than the destination, this mismatch results in a buffer overflow that can corrupt adjacent memory, crash the application, or enable arbitrary code execution.
Risk
This vulnerability represents a particularly dangerous class of buffer overflow because the error pattern is subtle and easily overlooked during code review. The code may appear correct at first glance—it uses a size limit—but the wrong buffer is being measured. The consequences are severe: successful exploitation can lead to denial of service through crashes, information disclosure through memory corruption, or complete system compromise through code execution. Modern exploit techniques like Return-Oriented Programming (ROP) can leverage these overflows even with protections like DEP/NX enabled.
Solution
Always use the destination buffer's size in copy operations. This is a fundamental rule: strncpy(dest, src, sizeof(dest)-1), not sizeof(src). Review all buffer copy operations to verify the correct buffer is being sized. Use static analysis tools configured to detect this specific pattern. Consider using safer alternatives like snprintf(), strlcpy() (BSD), or StringCchCopy() (Windows) that require specifying the destination size explicitly. In C++, prefer std::string or std::vector which manage their own memory. Enable compiler warnings and treat them as errors. Deploy defense-in-depth measures including ASLR, stack canaries, and DEP.
Common Consequences
| Impact | Details |
|---|---|
| Availability | Scope: Availability Denial of Service - Buffer overflows typically cause crashes or hangs, disrupting service availability. |
| Integrity, Confidentiality, Availability | Scope: Integrity, Confidentiality, Availability Execute Unauthorized Code - Attackers can exploit overflows to execute arbitrary code outside the program's security policy. |
| Access Control | Scope: Access Control Bypass Protection Mechanism - Successful code execution can compromise or disable other security mechanisms. |
Example Code
Vulnerable Code
// Vulnerable: Uses sizeof(source) instead of sizeof(dest)
void vulnerable_copy(void) {
char source[21] = "the character string";
char dest[12];
// Vulnerable: sizeof(source) is 21, but dest is only 12 bytes
strncpy(dest, source, sizeof(source) - 1);
// Writes 20 bytes into 12-byte buffer - overflow!
}
// Vulnerable: Uses source length in memcpy
void vulnerable_memcpy(const char* source) {
char dest[32];
size_t source_len = strlen(source);
// Vulnerable: Uses source length, not destination size
memcpy(dest, source, source_len + 1); // +1 for null
// If source is longer than 32 bytes, overflow occurs
}
// Vulnerable: Passes source size to function
void vulnerable_wrapper(void) {
char large_buffer[256];
char small_buffer[32];
get_data(large_buffer, sizeof(large_buffer));
// Vulnerable: Passes wrong size
process_data(small_buffer, large_buffer, sizeof(large_buffer));
}
void process_data(char* dest, const char* src, size_t len) {
// Uses the passed length, which is source size
strncpy(dest, src, len);
}
// Vulnerable: Uses source array dimension
void vulnerable_array_copy(void) {
char src_array[100];
char dst_array[50];
get_input(src_array, sizeof(src_array));
// Vulnerable: Uses dimension of source array
for (int i = 0; i < sizeof(src_array); i++) {
dst_array[i] = src_array[i]; // Overflow when i >= 50
}
}
// Vulnerable: C++ with C-style arrays
void vulnerableCppCopy(const char* input) {
char buffer[64];
size_t input_size = strlen(input) + 1;
// Vulnerable: Uses input size
std::memcpy(buffer, input, input_size);
}
// Vulnerable: Struct member copy with wrong size
struct SmallStruct {
char data[16];
};
struct LargeStruct {
char data[128];
};
void vulnerable_struct_copy(struct LargeStruct* large) {
struct SmallStruct small;
// Vulnerable: Uses size of source struct's member
strncpy(small.data, large->data, sizeof(large->data));
// Writes 128 bytes into 16-byte member
}
Fixed Code
// Fixed: Uses sizeof(dest)
void fixed_copy(void) {
char source[21] = "the character string";
char dest[12];
// Fixed: Use destination size
strncpy(dest, source, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0'; // Ensure null termination
}
// Fixed: Validates against destination size
void fixed_memcpy(const char* source) {
char dest[32];
size_t source_len = strlen(source);
// Fixed: Use minimum of source length and destination size
size_t copy_len = (source_len < sizeof(dest) - 1) ?
source_len : sizeof(dest) - 1;
memcpy(dest, source, copy_len);
dest[copy_len] = '\0';
}
// Fixed: Pass destination size explicitly
void fixed_wrapper(void) {
char large_buffer[256];
char small_buffer[32];
get_data(large_buffer, sizeof(large_buffer));
// Fixed: Pass destination size
process_data_safe(small_buffer, sizeof(small_buffer),
large_buffer, strlen(large_buffer));
}
void process_data_safe(char* dest, size_t dest_size,
const char* src, size_t src_len) {
// Use destination size for limit
size_t copy_len = (src_len < dest_size - 1) ?
src_len : dest_size - 1;
strncpy(dest, src, copy_len);
dest[copy_len] = '\0';
}
// Fixed: Use destination array dimension
void fixed_array_copy(void) {
char src_array[100];
char dst_array[50];
get_input(src_array, sizeof(src_array));
// Fixed: Use dimension of destination array
for (size_t i = 0; i < sizeof(dst_array) - 1 && src_array[i]; i++) {
dst_array[i] = src_array[i];
}
dst_array[sizeof(dst_array) - 1] = '\0';
}
// Fixed: Use C++ string class
#include <string>
void fixedCppCopy(const char* input) {
// Fixed: std::string manages its own memory safely
std::string buffer(input);
// Or if fixed-size buffer needed:
char fixed_buffer[64];
std::string temp(input);
if (temp.length() >= sizeof(fixed_buffer)) {
temp.resize(sizeof(fixed_buffer) - 1);
}
std::strcpy(fixed_buffer, temp.c_str());
}
// Fixed: Struct member copy with correct size
struct SmallStruct {
char data[16];
};
struct LargeStruct {
char data[128];
};
void fixed_struct_copy(struct LargeStruct* large) {
struct SmallStruct small;
// Fixed: Use size of destination struct's member
strncpy(small.data, large->data, sizeof(small.data) - 1);
small.data[sizeof(small.data) - 1] = '\0';
}
// Best practice: Use safe string functions
#include <stdio.h>
void best_practice_copy(const char* source) {
char dest[32];
// snprintf is safe: always uses destination size
snprintf(dest, sizeof(dest), "%s", source);
// Automatically truncates and null-terminates
}
Related CWEs
- CWE-805: Buffer Access with Incorrect Length Value (parent)
- CWE-119: Improper Restriction of Operations within Bounds of Memory Buffer (grandparent)
- CWE-120: Buffer Copy without Checking Size of Input (related)
- CWE-787: Out-of-bounds Write (related)
References
- MITRE Corporation. "CWE-806: Buffer Access Using Size of Source Buffer." https://cwe.mitre.org/data/definitions/806.html
- CERT C Secure Coding Standard. "STR31-C. Guarantee that storage for strings has sufficient space."
- Microsoft. "Safe String Functions." https://docs.microsoft.com/en-us/windows/win32/menurc/strsafe-ovw