Sanitizers#
Runtime sanitizers catch memory errors, undefined behaviour, and data races at test time. This page covers adding sanitizer presets, the library requirements for each sanitizer, stacking rules, and how to run them.
For the variable reference, see LIBRA_SAN in
Variable reference.
Available sanitizers#
Value |
Name |
What it detects |
|---|---|---|
|
AddressSanitizer |
Heap/stack buffer overflows, use-after-free, use-after-return,
double-free. Requires |
|
UndefinedBehaviourSanitizer |
Signed integer overflow, null pointer dereference, misaligned access, invalid enum values, and more. |
|
ThreadSanitizer |
Data races between threads. Requires |
|
MemorySanitizer |
Reads from uninitialised memory. Clang only — GCC does not support MSAN. |
|
Stack sanitizer |
Aggressive stack checking beyond what ASAN provides. |
Stacking rules#
ASAN, UBSAN, and SSAN can be combined freely. TSAN and MSAN less so, depending on compiler.
1. Add sanitizer presets#
Add dedicated presets to CMakePresets.json rather than enabling
sanitizers in the debug preset. This keeps debug builds fast and
gives sanitizer runs their own build directories:
{
"configurePresets": [
{
"name": "asan",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"LIBRA_TESTS": "ON",
"LIBRA_SAN": "ASAN;UBSAN"
}
},
{
"name": "tsan",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"LIBRA_TESTS": "ON",
"LIBRA_SAN": "TSAN"
}
},
{
"name": "msan",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"LIBRA_TESTS": "ON",
"LIBRA_SAN": "MSAN"
}
}
],
"buildPresets": [
{ "name": "asan", "configurePreset": "asan" },
{ "name": "tsan", "configurePreset": "tsan" },
{ "name": "msan", "configurePreset": "msan" }
],
"testPresets": [
{
"name": "asan",
"configurePreset": "asan",
"output": { "outputOnFailure": true }
},
{
"name": "tsan",
"configurePreset": "tsan",
"output": { "outputOnFailure": true }
}
]
}
2. Install runtime libraries#
Some sanitizers require runtime libraries separate from the compiler.
GCC:
# Debian/Ubuntu
sudo apt-get install libasan8 libubsan1 libtsan2
# Fedora/RHEL
sudo dnf install libasan libubsan libtsan
Clang: ASAN, UBSAN, TSAN, and MSAN are bundled with the Clang installation — no separate install needed.
Note
The runtime library version must match the compiler version. If
libasan8 is not available for your GCC version, install the
package that matches: libasan6 for GCC 11, libasan8 for
GCC 13, etc.
3. Build and run tests#
clibra test --preset asan
clibra test --preset tsan
clibra test --preset msan # Clang only
cmake --preset asan
cmake --build --preset asan --target all-tests
ctest --preset asan --output-on-failure
4. Interpreting sanitizer output#
Sanitizer violations are printed to stderr before or after the test output. Each report includes a stack trace and the type of violation.
With ASAN:
==12345==ERROR: AddressSanitizer: heap-buffer-overflow on address ...
#0 0x... in my_function src/core.cpp:42
#1 0x... in main src/main.cpp:10
With TSAN:
WARNING: ThreadSanitizer: data race on 0x...
Write by thread T2:
#0 0x... in MyClass::write() src/myclass.cpp:55
Previous read by thread T1:
#0 0x... in MyClass::read() src/myclass.cpp:47
Set ASAN_OPTIONS, TSAN_OPTIONS, or UBSAN_OPTIONS in your
test preset’s environment field to control sanitizer behaviour:
"testPresets": [
{
"name": "asan",
"configurePreset": "asan",
"environment": {
"ASAN_OPTIONS": "halt_on_error=1:abort_on_error=1",
"UBSAN_OPTIONS": "halt_on_error=1:print_stacktrace=1"
}
}
]
Adding sanitizers to CI#
Add a sanitizer job to your pipeline after the basic test job. See CI setup for the full pipeline context:
# GitHub Actions
sanitizers:
name: Sanitizers (ASAN/UBSAN)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install
run: |
sudo apt-get update
sudo apt-get install -y cmake ninja-build gcc g++ libasan8 libubsan1
- name: Test with ASAN+UBSAN
run: |
cmake --preset asan
cmake --build --preset asan --target all-tests
ctest --preset asan --output-on-failure