Introduction
-fsanitize-trap= is an Undefined Behavior Sanitizer (UBSan) flag that enables trapping UBSan, which causes programs built in this mode to execute a trap instruction if certain undefined behavior is observed. This is a “lighter weight” version of UBSan because it has no associated runtime (unlike userspace UBSan) that makes it suitable for deployment in environments that do not have (or do not want) a UBSan runtime.
A problem that currently exists with trapping UBSan is if the -fsanitize-trap=<sanitizer> flag is provided to the Clang driver but -fsanitize=<sanitizer> is not then this configuration of compiler flags silently does nothing.
Motivation
We do not think the current behavior of silently ignoring -fsanitize-trap= when it appears on its own is the right behavior. Typically the Clang driver warns about unused flags.
E.g.:
Passing linker arguments without initiating a linking phase ([5][6]):
$ clang -c foo.cpp -la
clang: warning: -la: 'linker' input unused [-Wunused-command-line-argument]
$ clang -c foo.cpp -rdynamic
clang: warning: argument unused during compilation: '-rdynamic' [-Wunused-command-line-argument]
Passing architecture specific flags with the wrong target ([7][8]):
$ clang --target=x86_64-apple-darwin -c foo.cpp -mthumb
clang: warning: argument unused during compilation: '-mthumb' [-Wunused-command-line-argument]
The current behavior aside from being at odds with the typical behavior of Clang makes it more likely a user of Clang will make a mistake by thinking they have UBSan instrumentation enabled when in fact, they don’t. There are internal reports inside of Apple that users of Clang found the current behavior confusing.
Proposed Change
We propose emitting a warning diagnostic when this occurs in order to prevent the accidental omission of the necessary flag to utilize -fsanitize-trap=<...> functionality. The warning would be on by default but will be placed in a warning group which allows it to be disabled for projects who wish to have the current behavior (i.e. no warning).
$ clang -fsanitize-trap=undefined test.c
clang: warning: -fsanitize-trap=undefined has no effect because the "undefined" sanitizer is disabled; consider passing "-fsanitize=undefined" to enable the sanitizer
$ clang -fsanitize=undefined -fsanitize-trap=undefined test.c
<no warning>
$ clang -fsanitize-trap=undefined -Wno-sanitize-trap-mismatch test.c
<no warning>
Projects relying on the current behavior
We have received feedback ([1][2][3]) that the current design is intentional and allows something like this.
Project(TestUBSan C)
cmake_minimum_required(VERSION 3.24)
# Enable UBSan at the top-level
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined")
option(UBSAN_TRAPS ON)
if(UBSAN_TRAPS)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-trap=undefined")
endif()
# Some targets use UBSan
add_executable(my_program_1 test1.c)
# We need to opt some targets out.
add_executable(my_program_2 test2.c)
target_compile_options(my_program_2 PRIVATE "-fno-sanitize=all")
Here
my_program_1builds with-fsanitize=undefined -fsanitize-trap=undefinedmy_program_2builds with-fsanitize=undefined -fsanitize-trap=undefined -fno-sanitize=all
Under this RFC when building my_program_2 a new warning will appear. Previously there would be no warning.
warning: -fsanitize-trap=undefined has no effect because the "undefined" sanitizer is disabled; consider passing "-fsanitize=undefined" to enable the sanitizer
We do not believe our RFC is at odds with build systems that want to do this style of UBSan opt out. Under the changes proposed by this RFC the project can continue to build with the new warnings appearing (as they are non-fatal) or they can hide the warnings by suppressing them.
target_compile_options(my_program_2 PRIVATE
"-fno-sanitize=all -Wno-sanitize-trap-mismatch")
It has been suggested that the current behavior is desirable and builds systems like the above justify this. While we have sympathy for this view we believe:
- The current behavior is at odds with how Clang normally handles unused driver flags
- Prioritizing the needs of a particular build system over the general usability of a Clang feature does not seem like the right trade-off. Especially because existing build systems can very easily adapt to this change.
Implementation
As part of my 2025 Google Summer of Code project I have implemented a sketch patch showing how this could be implemented. It turns out this was also implemented before in [4] but the effort stalled.
Unresolved problems
Object-size sanitizer has non-obvious behavior
At -O0, Clang intentionally disables the -fsanitize=object-size sanitizer even though the user may have enabled the sanitizer group, -fsanitize=undefined, which -fsanitize=object-size is a part of.
Example with the sketch patch implemented:
$ clang -O0 -fsanitize=undefined -fsanitize-trap=undefined foo.c
warning: -fsanitize-trap=object-size has no effect because the "object-size" sanitizer is disabled; consider passing "-fsanitize=object-size" to enable the sanitizer
$ clang -O1 -fsanitize=undefined -fsanitize-trap=undefined foo.c
<no warning>
Given that this behavior is intentional one possible fix here would be modify the warning to not warn about this particular case.
Alternatively we could require the user to pass -fno-sanitize-trap=object-size
E.g.:
$ clang -O0 -fsanitize=undefined -fsanitize-trap=undefined -fno-sanitize-trap=object-size foo.c
This solution doesn’t seem ideal because the most common case (using the “undefined” group) produces a “technically correct” but confusing warning.
Broken tests
No attempt has been made to make all the tests pass. Some currently don’t because they don’t expect to see the new warning. This can easily be resolved in a complete implementation of the patch. The tests have not been fixed in the sketch patch to make the patch easier to understand and to avoid spending effort before we know the direction that this RFC will take.
Path forward
We would like to get community consensus on the right path forward here. It’s clear there is disagreement hence we have put up this RFC to try to get official consensus.
CC: @delcypher, @Michael137 , @vitalybuka, @MaskRay, @thurston, @usama, @fmayer, @ilovepi, @jyknight, @petrhosek
External Links
[1] [clang][GSoC 2025] Usability Improvements for trapping Undefined Behavior Sanitizer - #5 by jyknight
[4] ⚙ D101505 [Driver][sanitizers] Warn about ignored -fsanitize-trap or -fsanitize-recover
[5] c++ - clang: warning: argument unused during compilation: '-rdynamic' - Stack Overflow
[6] Clang -Wunused-command-line-argument | MaskRay
[7] Better Firmware with LLVM/Clang | Interrupt - #5 by kisielk - Blog - Interrupt