Heuristic:Duckdb Duckdb Sanitizer Configuration
| Knowledge Sources | |
|---|---|
| Domains | Debugging, Build_System |
| Last Updated | 2026-02-07 12:00 GMT |
Overview
Address Sanitizer (ASAN) and Thread Sanitizer (TSAN) are mutually exclusive in DuckDB; enabling both silently disables ASAN. M1 MacBooks need vptr sanitizer workaround with older Clang.
Description
DuckDB enables AddressSanitizer and UndefinedBehaviorSanitizer by default in debug builds. However, ASAN and TSAN cannot coexist (a GCC/Clang limitation), and UBSAN also conflicts with TSAN. The build system handles these conflicts automatically but silently, which can lead to confusion. Additionally, the vptr sanitizer produces false positives on Apple M1 (ARM64) machines with Clang versions below 14.0.
Usage
Apply this heuristic when debugging memory issues (use ASAN), debugging race conditions (use TSAN), or when encountering false positive sanitizer reports on M1 MacBooks. Understanding the mutual exclusivity prevents wasted debugging time with the wrong sanitizer enabled.
The Insight (Rule of Thumb)
- Rule 1: ASAN and TSAN are mutually exclusive. If both are enabled, ASAN is silently disabled.
- Action: Use `THREADSAN=1 DISABLE_SANITIZER=1 make debug` for thread sanitizer.
- Action: Use `make debug` (default) for address sanitizer.
- Rule 2: UBSAN and TSAN are mutually exclusive. If both are enabled, UBSAN is silently disabled.
- Rule 3: On Apple M1 (ARM64) with Clang < 14.0, the vptr sanitizer produces false positives.
- Action: Set `DISABLE_VPTR_SANITIZER=1 make debug` to suppress.
- Note: Clang >= 14.0 on M1 does not need this workaround (the bug was fixed).
- Rule 4: Use `FORCE_SANITIZER=1` to enable sanitizers even in release builds for performance-aware testing.
Reasoning
ASAN and TSAN instrument memory differently and their runtime implementations conflict. The DuckDB build system prioritizes TSAN over ASAN when both are enabled, based on the logic that thread bugs are usually what you're specifically looking for when you enable TSAN.
Code evidence from `CMakeLists.txt:182-188`:
if(${ENABLE_THREAD_SANITIZER})
if(${ENABLE_SANITIZER})
message(
WARNING
"Both thread and address sanitizers are enabled. This is not supported.
The address sanitizer will be disabled, and we will run with only the
thread sanitizer.")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDUCKDB_THREAD_SANITIZER")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
M1 vptr workaround from `CMakeLists.txt:199-210`:
if (NOT ${DISABLE_VPTR_SANITIZER})
if(APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64")
if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_GREATER 14.0)
message(
WARNING
"Not disabling vptr sanitizer on M1 Macbook - set
DISABLE_VPTR_SANITIZER manually if you run into issues
with false positives in the sanitizer")
else()
set(DISABLE_VPTR_SANITIZER TRUE)
endif()
endif()
endif()
Makefile flags from `Makefile:43-57`:
ifeq (${DISABLE_SANITIZER}, 1)
DISABLE_SANITIZER_FLAG=-DENABLE_SANITIZER=FALSE -DENABLE_UBSAN=0
endif
ifeq (${DISABLE_VPTR_SANITIZER}, 1)
DISABLE_SANITIZER_FLAG:=${DISABLE_SANITIZER_FLAG} -DDISABLE_VPTR_SANITIZER=1
endif
ifeq (${THREADSAN}, 1)
DISABLE_SANITIZER_FLAG:=${DISABLE_SANITIZER_FLAG} -DENABLE_THREAD_SANITIZER=1
endif