As we have seen, we did not need to change anything in the code in order to get this kind of tripwire feature for buggy code. It basically came for free, just by appending some compiler flags to the command line when compiling the program.
This feature is implemented by sanitizers. A sanitizer usually consists of an additional compiler module and a runtime library. When sanitizers are activated, the compiler will add additional information and code to the binary, which results from our program. At runtime, the sanitizer libraries that are then linked into the program binary can, for example, replace the malloc and free functions in order to analyze how the program deals with the memory it acquires.
Sanitizers can detect different kinds of bugs. Just to list a few valuable examples:
- Out-of-bounds: This triggers whenever we access an array, vector, or anything similar outside its legitimate memory range.
- Use-after-free: This triggers if we reference heap memory after it was already freed (which we did in this section).
- Integer overflow: This triggers if an integer variable overflows by calculating with values that do not fit into the variable. For signed integers, the arithmetic wraparound is undefined behavior.
- Pointer alignment: Some architectures cannot access memory if it has a weird alignment in memory.
There are many more such bugs that sanitizers can detect.
It is not feasible to always activate all available sanitizers because they make the program slower. However, it is good style to always activate sanitizers in your unit tests and integration tests.