Best practices for safer C++ development
Safe coding practices has never been more important, and historically the C and C++ community has emphasized performance and backwards compatibility. This is now changing rapidly as safety concerns are being addressed vigorously by parties both inside, and outside, the standard C and C++ committees.
Modern C++ tool chains are significantly more secure than before, providing features such as:
- Static analyzers, implemented using path-sensitive, inter-procedural
analysis based on symbolic execution techniques, including:
- Thread safety analysis as a C++ language extension which warns about potential race conditions in code.
- Warnings emitted at compile time to help developers update their code to encapsulate and propagate the bounds information associated with pointers.
- Runtime assertions that eliminate undefined behavior as long as the coding convention is followed and the bounds information is therefore available and correct.
- SAL annotations used to enhance static code analysis.
- Runtime sanitizers like:
- AddressSanitizer which is a fast memory error detector consisting of a compiler
instrumentation module and a run-time library. It can detect bugs like:
- Out-of-bounds accesses to heap, stack and global variables.
- Use-after-free.
- Use-after-return.
- Use-after-scope.
- Double-free, invalid free.
- Memory leaks.
- ThreadSanitizer which detects data races.
- MemorySanitizer which detects uninitialized memory use such as:
- Uninitialized value used in a conditional branch.
- Uninitialized pointer used for memory accesses.
- Uninitialized value was passed or returned from a function call.
- Uninitialized data was passed to a system call.
- UndefinedBehaviorSanitizer which detects:
- Array subscript out of bounds, where the bounds can be statically determined.
- Bitwise shifts that are out of bounds for their data type.
- Dereferencing misaligned or null pointers.
- Signed integer overflow.
- Conversion to, from, or between floating-point types which would overflow the destination
- and more.
- AddressSanitizer which is a fast memory error detector consisting of a compiler
instrumentation module and a run-time library. It can detect bugs like:
None of the above helps if it is not used, and these features are being actively developed and enhanced, and the latest version of the C++ tool chains offers significant enhancements over the previous versions.
These features are generally something developers must actively enable for the code base they are working on, and this often requires a significant effort, as the static analyzers often favors modern C++ idioms and certain coding conventions, which is good as they are far less error prone. This means that the code base may need modification to get as much mileage out of the tools as possible. Still, this should be much cheaper than rewriting the entire code base in another programming language.
These tools cannot be proven to catch all potential errors in C and C++ code, but when used rigorously in conjunction with unit tests and integrations tests, they are able to catch most of the bugs they are designed to detect.