Sanitizers¶
Sanitizers are runtime checkers built into compilers such as GCC and Clang. They instrument compiled code to report memory errors, undefined behavior and data races as they happen. They only find bugs in code that actually runs, so run as much of CPython as possible, for example the full test suite or a fuzzer.
Note
This page describes the main branch. Sanitizer support changes
quickly, so the details here may not apply to older branches.
Available sanitizers¶
configure has an option for each supported sanitizer. It adds the
necessary compiler and linker flags.
Sanitizer |
Detects |
|
Compilers |
|---|---|---|---|
AddressSanitizer (ASan) |
Out-of-bounds accesses, use-after-free, leaks |
GCC, Clang |
|
HWAddressSanitizer (HWASan) |
Like ASan, with hardware pointer tagging |
GCC, Clang (AArch64; experimental on x86-64) |
|
MemorySanitizer (MSan) |
Reads of uninitialized memory |
Clang |
|
UndefinedBehaviorSanitizer (UBSan) |
Signed overflow, misaligned access, invalid shifts, null dereference |
GCC, Clang |
|
ThreadSanitizer (TSan) |
Data races |
GCC, Clang |
UBSan can be combined with any of the others. ASan, HWASan, MSan and TSan are mutually exclusive.
Building and testing¶
Pick a compiler if needed, then configure with the option you want:
export CC=clang CXX=clang++
./configure --with-pydebug --with-address-sanitizer
make -j $(nproc)
make test
export CC=clang CXX=clang++
./configure --with-pydebug --with-address-sanitizer
make -j$(sysctl -n hw.logicalcpu)
make test
make itself may fail with a sanitizer report, since the just-built
interpreter runs during later stages of the build.
The sanitizer jobs in CPython’s CI, configured in .github/workflows/reusable-san.yml, are a good reference for a working set of options for each sanitizer. Notes:
MSan: reports false positives for memory initialized by libraries not built with MSan. Build dependencies with MSan or disable the extension modules that use them in
Modules/Setup.local, as CI does. Additionally, avoid--with-pydebug, since the debug allocator hooks hide uninitialized reads. You can use--with-assertionsinstead.TSan: most useful on the free-threaded build. A full test run is slow, so use the curated lists in Lib/test/libregrtest/tsan.py:
./python -m test --tsan ./python -m test --tsan-parallel --parallel-threads=4
HWASan: on x86-64 the aliasing mode is unsafe for programs that
fork(), including much of the test suite.
Each sanitizer reads runtime options from ASAN_OPTIONS,
HWASAN_OPTIONS, MSAN_OPTIONS, UBSAN_OPTIONS or TSAN_OPTIONS.
CI uses detect_leaks=0 (the test suite is not leak-clean),
allocator_may_return_null=1 (for out-of-memory tests), handle_segv=0
(leave crashes to faulthandler), halt_on_error=1 for UBSan
and log_path= to collect reports from subprocesses.
./python -m test prints the active sanitizers and these variables in its
header, which helps reproduce CI failures locally.
Suppressing findings¶
CPython keeps the runtime suppression files used by CI in the repository:
Load one with the suppressions= option:
export UBSAN_OPTIONS="suppressions=$PWD/Tools/ubsan/suppressions.txt"
When adding a suppression, include a comment referencing a GitHub issue that describes how to reproduce the problem and includes the sanitizer output, so the suppression can be removed once the issue is fixed. The file formats are described in the UBSan and TSan documentation.
Analyzing the output¶
A report names the failed check and the source location, and ASan, MSan and TSan add a stack trace:
==24064==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x619000004020 at pc 0x4ed4b2 bp 0x7fff80fff010 sp 0x7fff80fff008
READ of size 4 at 0x619000004020 thread T0
#0 0x4ed4b1 in PyObject_Free Objects/obmalloc.c:987
#1 0x7a2141 in code_dealloc Objects/codeobject.c:359
...
Traces are symbolized automatically via llvm-symbolizer (Clang) or
addr2line (GCC). If the tool is not on your PATH, set
ASAN_SYMBOLIZER_PATH (or the equivalent for other sanitizers) to its
location. In the CPython CI, reports are printed at the end of the job log and
attached as san_log.* artifacts.