Skip to content
Memory safety Reviewed 2026-09-12

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.