45 / 163 · C++11 · 11 min
Memory safety diagnostics
Split memory errors into spatial out-of-bounds, temporal invalidation, missing initialization, deallocation-protocol errors, and concurrency races, then choose matching evidence. The crash site is often only the consequence; trace the first illegal access and the allocation and free stacks, and fix the root cause with explicit bounds and owning interfaces.
In this lesson
Classify first, then trace the first error
A spatial error is access beyond a legitimate object boundary; a temporal error is using an object before construction or after destruction; an initialization error is reading state that has not yet obtained a valid value. Deallocation-protocol errors include double free, freeing a stack address, and new/delete[] mismatch; a data race can make logic that is correct on a single thread fail.
Heap corruption may crash only on the next allocation, so the last malloc or container operation is not necessarily at fault. Keep a reproducing input, and prefer reading the detector report's first illegal read or write plus the related allocation and free stacks. Later errors may be only a chain reaction from the same damage.
Tools do not cover the same ground
ASan is good at finding many out-of-bounds, use-after-free, double-free, and invalid-free bugs; UBSan adds some undefined-behavior checks; MemorySanitizer targets uses of uninitialized values; thread races need an appropriate concurrency detector. Do not infer that other categories are already safe because one sanitizer reported nothing.
You can start from an ASan build with debug information, compiling and linking with -fsanitize=address -g -O1 -fno-omit-frame-pointer. Uninitialized issues need a separate MemorySanitizer setup and its required instrumented dependency environment. Dynamic tools cover only executed paths and also depend on platform and library instrumentation support; they are not a formal proof of correctness.
Fix input bounds and the ownership contract
The example uses vector::at to accept bounds checking: a legal index reads successfully, an index equal to size throws out_of_range, and the program catches and verifies that result instead of performing undefined subscript access. This suits paths whose bounds have not yet been proven by upper layers; if you use unchecked access, the valid range must be an explicit precondition.
Eliminating out-of-bounds usually requires correcting length calculations, loop termination conditions, or input validation; eliminating temporal errors usually requires adjusting ownership and borrow scope. Adding a null-pointer check cannot fix a non-null dangling pointer, and enlarging a buffer cannot fix a wrong length protocol. After the fix, rerun the same input and cover empty collections and values adjacent to the boundary.
Pitfalls
- Assertions may be disabled in some build modes; do not use assert in place of runtime checks that handle untrusted input.
- Undefined behavior is not guaranteed to crash immediately; output that happens to look correct once is not dependable behavior and is not suitable as a fixed expected output.
Run an example
Minimum C++11 · complete program · Download .cpp
#include <cassert>
#include <stdexcept>
#include <vector>
int main() {
std::vector<int> values{4, 8, 12};
assert(values.at(2) == 12);
bool rejected = false;
try {
(void)values.at(values.size());
} catch (const std::out_of_range&) {
rejected = true;
}
assert(rejected);
assert(values.size() == 3 && values.at(0) == 4);
}
Compile locally
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread memory-memory-errors.cpp -o example && ./exampleExpected result
Expected: exit 0, no output; every assert holds.
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
A report indicates a heap-use-after-free; the accessed pointer is non-null. Should you add a null check first, or trace the free stack? Give a repair direction.
Show a reference answer
First trace that object's allocation stack, free stack, and the path of this access, and determine which observer outlived the owner. A null check cannot recognize this kind of non-null dangling pointer. A fix can move the use before the free, let an asynchronous task hold a necessary owning copy, or obtain temporary ownership of a shared object with weak_ptr::lock. Then run the original reproduction and confirm the invalid path no longer occurs.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.