Diagnosing and Fixing Use‑After‑Free UB in C++: A Practical Guide
When a C++ program crashes or corrupts data at random points, a dangling pointer or use‑after‑free is often the culprit. This guide walks through symptoms, root causes, systematic checks, and concrete fixes using smart pointers and ASan.
22 Aug 2025, 05:21 UTC

Recognizable Condition
In a running application you may see one of the following:
- Segmentation fault that occurs at seemingly random points.
- Data that changes unexpectedly after a function returns.
- No crash, but the program behaves inconsistently between builds.
These symptoms are classic signs of undefined behavior (UB) caused by a dangling pointer or use‑after‑free.
Root Cause
After an object is deallocated with delete (or free), any pointer that still references that memory is dangling. Accessing the memory again, whether for reading or writing, triggers UB. The program may crash immediately or corrupt data silently.
Diagnostic Table
| Symptom | Likely Root Cause | Immediate Check |
|---|---|---|
| Crash at a random offset in the stack trace | Heap corruption due to use‑after‑free | Run with AddressSanitizer (ASan) to see the exact memory access. |
| Variable value changes after a function call | Dangling pointer reuse | Search for delete calls that precede the write. |
| Program compiles but fails only in release mode | Optimizations exposing UB | Compile with -fsanitize=address in both debug and release. |
Ordered Checks
- Verify ownership patterns: Ensure that every
newhas a clear owner whose lifetime extends beyond any use of the raw pointer.- Look for
std::unique_ptrorstd::shared_ptrownership markers. - Check that no raw pointers escape the scope of the owning object.
- Look for
- Audit all
deletecalls:- After deallocating, set the pointer to
nullptr. - Use a macro like
#define SAFE_DELETE(p) { delete (p); (p) = nullptr; }to enforce this pattern.
- After deallocating, set the pointer to
- Search for raw pointer usage after deallocation:
- Static analysis tools (Clang Static Analyzer, cppcheck) can flag potential dangling references.
- Manually review code paths where a pointer might outlive its object.
Concrete Example
Below is a minimal snippet that demonstrates a dangling pointer:
// Bad code – use‑after‑free
class Widget {
public:
Widget(int v) : value(v) {}
int value;
};
Widget* create() {
Widget* w = new Widget(42);
delete w; // object freed
return w; // dangling pointer returned
}
int main() {
Widget* w = create();
std::cout << w->value << std::endl; // UB: reads freed memory
}
To fix it, replace the raw pointer with a smart pointer that owns the object until it is no longer needed:
// Fixed code – unique_ptr ensures lifetime
std::unique_ptr<Widget> create() {
return std::make_unique<Widget>(42);
}
int main() {
auto w = create();
std::cout << w->value << std::endl; // safe
}
Verification with AddressSanitizer
Compile the program with ASan to catch any remaining use‑after‑free:
# Compile (Linux, macOS)
g++ -std=c++20 -fsanitize=address -g -O1 -o test test.cpp
# Run
./test
# Expected output (no crash)
42
ASan will abort the program and print a stack trace if a dangling pointer is dereferenced. Run the same command on your full test suite to ensure the issue is resolved.
Escalation Criteria
If after applying the above fixes the application still reports memory corruption:
- Inspect custom memory allocators or third‑party libraries that may return pointers to freed memory.
- Verify that no external C libraries are leaking or reusing memory blocks.
- Use Valgrind Memcheck for a deeper analysis of heap usage patterns.
Limitations and Practical Checks
- Setting a pointer to
nullptrafterdeleteprevents accidental use but does not fix the logic that caused the dangling reference. - Overusing
std::shared_ptrcan create circular references and memory leaks; preferstd::unique_ptrwhen ownership is singular. - UB may not surface in debug builds because of compiler optimizations; always test with sanitizers in both debug and release configurations.
- To confirm the fix, run a full regression test suite with ASan enabled and verify that no
use‑after‑freewarnings appear.
Following this ordered checklist, replacing raw pointers with smart pointers, and validating with ASan will eliminate most dangling pointer and use‑after‑free bugs in contemporary C++ codebases.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.