Aussie AI Blog
100 C++ Memory Safety Techniques
-
7th March, 2025
-
by David Spuler, Ph.D.
100 C++ Memory Safety Techniques
As a response to Bjarne Stroustrup's call to action regarding "C++ attacks" related to memory safety [1], I decided to compile a list of the possible methods, and count them. It's disheartening to see ongoing knee-jerk reactions to C++ memory issues, being effectively just to ban it. The plan to replace C++ with another programming language, usually Rust, is not a good idea because:
- It's expensive,
- It's not necessary, and
- It's too slow.
It's much more expensive to hire new programmers and do a ground-up rewrite of your application, than it is to refactor the code with extra memory safety mitigations. Admittedly, your C++ programmers are already expecting to get fired because of AI, so go crazy if you really like firing people. I guess you could be kinder and ask your C++ programmers to retrain in Rust, but then what you really have is a bunch of newbie programmers writing your business applications.
And it's not necessary. The cost of upgrading C++ to better memory safety is much less. Even upcoming standards improvements, such as "Profiles" as espoused by Bjarne Stroustrup [2], will only require code changes similar to getting C++ to work with a very picky compiler (because Profiles are statically enforced by the compiler). Similarly, adding pragmatic memory safety approaches is similar to a code refactoring effort, not a rewrite. Furthermore, some approaches that you can do today involve the use of new builds, new compiler tools, and upgraded standard libraries, rather than code changes.
Which one is faster? The ground-up rewrite doesn't sound that fast to me. Many of the pragmatic code changes are a refactoring effort, where many existing techniques can be integrated into existing code bases in a day or so. Similarly, changes to the tools in build, compilers, static analyzers, and runtime checkers, are all very fast, with no code changes (except when they find bugs, haha!).
Here's the List So Far
Herewith, I provide a list of all the C++ memory safety risk mitigation methods of which I'm aware, in the categories of:
- Upcoming C++ language safety improvements (e.g., Profiles [2], Safe C++).
- Already-completed and ongoing C++ mitigation work (e.g., C++ Core Guidelines, hardening standard C++ libraries).
- Pragmatic memory safety coding approaches (e.g., safety wrapper functions).
Without further ado...
- Profiles [2] — supported by Bjarne Stroustrup, with C++ memory-safety enforceable by the compiler statically.
- Safe C++ — C++ language extensions with "
safe
" and "unsafe
" keywords. - TrapC
- FilC
- Mini-C
Existing C++ Safety Guidelines: - C++ Core Guidelines — from C++ standardization gurus Bjarne Stroustrup and Herb Sutter.
- SEI CERT C++ Secure Coding Guidelines
- SEI CERT C Secure Coding Guidelines
Big Quality Improvements (General Approaches): - Automate full test runs regularly — use CI/CD, or nightly builds when it gets too slow for CI/CD.
- Nightly build tests with sanitizers/runtime checkers — detect bugs as early as possible.
- Fuzzing — thrash your code with lots of weird stuff, long inputs, etc.
- Fuzzing with runtime sanitizers — it's slow, but worth it.
- AI code checking and debugging — it's already good, and will be great soon.
- Review "technical debt" — but fixing it is not usually as impactful as programmers think.
Build Improvements for Safety: - Run memory safety checkers and sanitizers regularly — e.g., nightly builds.
- Use multiple runtime memory safety checkers — Valgrind, ASan, MSVS, etc.
- Build a custom memory-safe coding style enforcer — e.g., even
grep
for a file of patterns containing the names of memory-unsafe functions works quite well. - Extra warnings — separate build path for running compilers enabled with extra picky warnings.
- Optimizer levels — separate build path for running code with different optimizer levels to shake out rare but insidious memory errors that only occur when optimized.
- Run builds on multiple platforms of your core platform-independent code (Windows, Linux, Mac) — more compiler warnings, more ways to thrash the code at runtime, more sanitizers (can spin up a cloud virtual machine for whatever platforms you need).
General Safe Coding Style Improvements: - Assertions
- Check return codes
- Validate incoming parameter values in functions
- Debug tracing macros (logging)
- Unit tests (can never have too many)
- Module-level tests
- Automated integration tests
- Exception handling — consider whether to use C-style return codes versus C++ try/catch exceptions.
- Painstaking work — adding reliability to code is endless small improvements,
not just throw an exception and you're done.
Specific Coding Improvements for C++ Memory Safety: - Use macro wrappers to ensure checking of return codes for common functions (both library and custom code)
— this is more general than
[nodiscard]
which guarantees only that the return code is assigned somewhere, but not that it's well handled, whereas a wrapper guarantees that failures are at least logged somewhere (and then hopefully properly handled by the caller). - Unreachable code marked with assertions or other handling.
- Not-yet-implemented code marked with assertions or other handling.
- Safety wrapper functions for common library or non-library functions — validate inputs, check for common usage errors, and check the return value for failure so it's never undetected.
- Detect uncommon "undefined behavior" in wrapper functions — e.g., overlapping memory blocks in
memcpy
, file read and write without intervening seek, etc. - Intercept fatal signals (e.g.
SIGSEGV
) with a handler that at least prints a nice message and ideally even a stacktrace — no, you can't really recover at this point, but you can provide extra debugging context for supportability; also watch out it doesn't get re-raised.
DIY Memory Safety Classes: - Safe smart buffer class (two-variable method) — add a second "buffer safety checker" object to watch an existing buffer.
- Safe smart buffer class (one-variable method) — replace simple buffer variables with a templated smart buffer object of the required size
Heap Memory Safety Methods for C++: - Macro interception of C-style memory primitives (malloc, calloc, free, strdup, etc.)
- Link-time interception of C++ new/delete memory primitives — it's been a standard feature of C++ for many years.
- Implement a randomized delayed deallocator — this blocks most Use-After-Free attack vectors.
DIY Heap Memory Safety Wrapper Libraries for C++: - Validate all memory primitives (e.g., detect free non-heap, double-deallocation, mixed C/C++ memory primitives, null pointers, overlapping memory blocks, and more.)
- Use canary values for a fast way to detect buffer overflows.
- Use "last-byte-null" canary values in string buffers.
- Use redzones to detect buffer overflows.
- Detect buffer underflows with canaries and redzones.
- Poison uninitialized or freed memory blocks
- Poison after the null byte in long buffers containing short strings.
- Implement a "never-free" approach to detect all Use-After-Free errors — probably only in testing, not production.
- Check memory block size parameters are not equal to
sizeof(char*)
— indicatingsizeof
used on a pointer or array parameter, then passed tomemset
or other functions. - Find memory block sizes in safety wrapper functions — each platform has its own way to find the
size of an allocated block using the address of the start of the block (but not from the middle of the block).
Stack Memory Safety Methods for C++: - Macro interception of C-style stack memory primitives (alloca, other variants, etc.)
- Use a runtime sanitizer that detects stack buffer overflows (i.e., not Valgrind).
- Use a stack overflow canary object as a local variable — the constructor sets it to a fixed value,
and the destructor checks the value hasn't changed, or calls
abort
if it has. Usevolatile
to prevent a smart optimizer removing it. - Use a semi-randomized stack overflow canary object — to further thwart stack buffer overflow attacks, don't just use a fixed value, but a semi-random value that changes with time.
- Use an obscured pair of numbers in a semi-randomized stack object — both values are stored in the stack object and can be overrwitten, so
even further you can either: (a) allocate heap memory for the expected value (effective but inefficient), or
(b) use some obscure formula to generate and validate the two values.
Compile-Time Memory Safety Methods for C++: - Warning-free compilation policy — fix all compiler warnings, even the "unused variable" ones, lest they hide serious bugs.
- Use
static_assert
— e.g., check thesizeof
for your types at compile-time (avoids portability glitches later).
Specific Coding Improvements for Non-Memory Safety: - Add loop counters to detect and prevent infinite loops
- Add
[nodiscard]
to your functions to detect thrown-away return codes. - Add a standardized stack trace reporting library — use
std::backtrace
, Boost or Gnu; useful for assertions, return code failures, memory failures, etc. - Add math function wrappers — e.g.,
cos(90)
is probably mistaking radians and degrees. - Add fopen/fclose file function wrappers — prevent double
fclose
errors, and other crashes. - Safe integer classes to detect overflow — a little slow for my taste.
Weird arithmetic problems: - Check for arithmetic overflow and underflow
- Check for floating-point overflow and underflow
- Beware floating-point denormalized values — tiny and weird.
- Two floating-point zeros — there is now a level negative-zero.
- Floating point has many different values for infinity and negative infinity.
- Order of evaluation errors on various binary operators.
- Order of evaluation errors on function call arguments.
- Floating-point runtime error checker tools — seems like these are only in research papers,
not used commercially yet.
Basic Standard C++ Coding Changes: - Use references not pointers — references are a longstanding compiler-enforced way to replace pointers, with zero extra runtime cost.
- Use "const correctness" — I'm not a fan of this, because it's a lot of busy work if the code doesn't already adhere to proper
const
usage, and fixing it rarely finds any real bugs.
Unsafe C-style Functions: - Change
sprintf
tosnprintf
— but beware using its return value. - Use safe versions of unsafe string primitives (e.g.,
strcpy
,strcat
) - Avoid or wrap
strncpy
— it's actually unsafe despite having a buffer size parameter because it can leave a string without a null byte. - Avoid
fflush(stdin)
— officially undefined behavior, although I've seen it used and it's simply a nop. - Avoid the old
strncat
function — Write your own safe version ofstrcat
instead. - Prefer
memmove
tomemcpy
— it handles overlapping ranges without failure. - Avoid
longjmp
andsetjmp
for exception handling — they're old, unsafe, and superceded by many other options. - Avoid
tmpfile
andmktemp
— they have a race condition that's a security risk. - Avoid
scanf
andsscanf
— they're just a hot mess!
Standard C++ Library Memory Safety Techniques: - Use
std::string
notchar*
orchar[]
. - Use
std::span
— safe view onto other array data. - Use
std::mdspan
— safe view onto multi-dimensional data. - Use standard data structure classes — don't write your own hash table ever again.
- Use standard C++ smart pointer types (e.g.
shared_ptr
,weak_ptr
, etc.)
Sanitizer Tools for Runtime C++ Memory Safety Checking: - Valgrind (free on Linux)
- ASan (address sanitizer, free)
- Various commercial runtime memory checkers
Compilers for C++ Memory Safety: - GCC warning options (
-Wall
is my favorite, and there's-Wextra
,-Wpointer-arith
, and various others) - CLANG warnings — enable more to check more.
- MSVS warnings — click on some checkboxes.
Linters for C++ Memory Safety: - Use compilers and extra warnings as linters
- Set up a separate "lint" build path (e.g., "
make lint
") - Use freeware linters
- Use commercial linters
Library Improvements for C++ Memory Safety: - C++ standard library hardening efforts
- libc++ hardening
- Debug versions of libc++ — with extra error checking, self-validation, and instrumentation.
C++ Safety Future Standardization Efforts:
Sigh. Okay, so I admit they're not all about memory safety, but some are more about software quality and non-memory undefined behaviors. On the other hand, I didn't list out every different warning to use for each compiler, every free and commercial lint checker, every different type of memory error, and so on.
References
- Thomas Claburn, Sun 2 Mar 2025, C++ creator calls for help to defend programming language from 'serious attacks': Bjarne Stroustrup wants standards body to respond to memory-safety push as Rust monsters lurk at the door, The Register, https://www.theregister.com/2025/03/02/c_creator_calls_for_action
- Bjarne Stroustrup, March 2025 (accessed), ``Profiles'' -- What we need, https://github.com/BjarneStroustrup/profiles
Blog Articles on C++ Memory Safety
Some of our other writings on this topic:
- DIY Memory Safety in C++
- Safe C++ Standard and Memory Safety Book
- DIY Preventive C++ Memory Safety
- Canary Values & Redzones for Memory-Safe C++
- User-After-Free Memory Errors in C++
- Array Bounds Violations and Memory Safe C++
- Poisoning Memory Blocks for Safer C++
- Uninitialized Memory Safety in C++
- DIY Preventive C++ Memory Safety
- CUDA C++ Floating Point Exceptions
- Memory Safe C++ Library Functions
- Smart Stack Buffers for Memory Safe C++
- Safe C++ Text Buffers with snprintf
Safe C++ Book
![]() |
The new Safe C++ coding book by David Spuler:
Get your copy from Amazon: Safe C++: Fixing Memory Safety Issues |
More AI Research Topics
Read more about: