C and C++ memory safety¶
Memory-safety failures include reading or writing outside a valid object and using an object after its lifetime ends. Depending on the reachable operation, impact can include crashes, disclosure, or corrupted program state. An unsafe-looking API name alone does not establish exploitability: inspect buffer sizes, input lengths, ownership, and reachable paths.
Trust boundary: a packet or file can provide bytes and a claimed size. It cannot establish the size or lifetime of the destination object. Bounds must be checked before access. SEI CERT's ARR30-C rule explains invalid pointer and subscript operations.
Unsafe example: an unchecked copy¶
This C fragment assumes source contains at least length readable bytes. A length greater than 63 also makes the terminator write invalid.
char label[64];
memcpy(label, source, length);
label[length] = '\0';
Safer example: keep capacity with the destination¶
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
bool copy_label(char *destination, size_t capacity,
const char *source, size_t length) {
if (destination == NULL || source == NULL || capacity == 0
|| length >= capacity) {
return false;
}
/* Caller guarantees source has length readable bytes and no overlap. */
memcpy(destination, source, length);
destination[length] = '\0';
return true;
}
The caller must supply the actual allocation capacity. The function cannot discover it from a pointer. The strict inequality reserves space for the terminator and rejects the input before writing. If overlapping copies are part of the contract, use an appropriate overlap-safe operation; if embedded NUL is disallowed by the text format, validate that separately.
C++: use ownership and length-aware interfaces¶
For a new C++20 byte-buffer interface, an owning container avoids a separate manual allocation and a span carries the source length.
#include <cstddef>
#include <span>
#include <stdexcept>
#include <vector>
std::vector<std::byte> copy_packet(std::span<const std::byte> source) {
if (source.size() > 4096) {
throw std::length_error("Packet too large");
}
return {source.begin(), source.end()};
}
A span does not own memory: its source must remain valid during the call. Prefer RAII and std::unique_ptr for exclusive ownership; document borrowed lifetimes and avoid retaining pointers into containers that may reallocate. These patterns reduce lifetime mistakes but do not establish that every C++ operation is safe. Handle allocation failures at an appropriate application boundary.
Regression test¶
For a 64-byte C destination, accept lengths 0 and 63 and reject 64 without changing the destination. Confirm the final byte is NUL for accepted text. For C++, accept a small byte span and reject 4097 bytes. Run these local tests under AddressSanitizer and UndefinedBehaviorSanitizer where available; test real ownership transitions separately because bounds tests do not prove the absence of use-after-free.
Related: integer overflow, format strings, and unsafe buffer allocation.
References: CWE-125 — out-of-bounds read, CWE-787 — out-of-bounds write, and CWE-416 — use after free.