What's the idiomatic way of enabling UB sanitization in CMake?

1.7k Views Asked by At

I want to build a test program for a repository of mine with undefined behavior sanitization enabled (at least with GCC and perhaps clang). I know how to do this manually:

  • Add -fsanitize=undefined to the compilation flags
  • Add -lubsan to the linking flags
  • Make sure an appropriate version of libubsan is installed.

Now, in CMake, I would expect something like the following to work:

find_package(ubsan)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"   OR
    CMAKE_CXX_COMPILER_ID STREQUAL "Clang" )
    target_compile_options(my_test PRIVATE "-fsanitize=undefined")
endif()

target_link_libraries(ubsan::ubsan)

... but there is no such version (as of CMake 3.21.0-rc2). So, how should I go about it? Grab a FindUBSan.cmake from somewhere? Perhaps do something else?

PS - The question applies similarly to C

2

There are 2 best solutions below

6
Alex Reinking On

Everything needs to be compiled with the sanitizers enabled (mix and match generally isn't possible), and enabling the santizers is compiler-specific; therefore, it's a toolchain option.

In a toolchain file, include the following:

set(CMAKE_C_FLAGS_INIT "-fsanitize=undefined")
set(CMAKE_CXX_FLAGS_INIT "-fsanitize=undefined")

You can also set CMAKE_<LANG>_FLAGS at the command line to include -fsanitize=undefined.


Full example:

alex@alex-ubuntu:~/test$ tree
.
├── CMakeLists.txt
├── main.cpp
└── ubsan.cmake

0 directories, 3 files

alex@alex-ubuntu:~/test$ cat CMakeLists.txt 
cmake_minimum_required(VERSION 3.21)
project(test)

add_executable(main main.cpp)

alex@alex-ubuntu:~/test$ cat main.cpp 
int main () { return 0; }

alex@alex-ubuntu:~/test$ cat ubsan.cmake 
set(CMAKE_C_FLAGS_INIT "-fsanitize=undefined")
set(CMAKE_CXX_FLAGS_INIT "-fsanitize=undefined")

alex@alex-ubuntu:~/test$ cmake -G Ninja -S . -B build --toolchain ubsan.cmake 
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/alex/test/build

alex@alex-ubuntu:~/test$ cmake --build build/ -- -v
[1/2] /usr/bin/c++   -fsanitize=undefined -MD -MT CMakeFiles/main.dir/main.cpp.o -MF CMakeFiles/main.dir/main.cpp.o.d -o CMakeFiles/main.dir/main.cpp.o -c /home/alex/test/main.cpp
[2/2] : && /usr/bin/c++ -fsanitize=undefined  CMakeFiles/main.dir/main.cpp.o -o main   && :

alex@alex-ubuntu:~/test$ ldd build/main 
    linux-vdso.so.1 (0x00007ffc9dd75000)
    libubsan.so.1 => /lib/x86_64-linux-gnu/libubsan.so.1 (0x00007f79a4b52000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f79a4960000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f79a495a000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f79a4937000)
    libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f79a4755000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f79a4738000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f79a550a000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f79a45e9000)
2
KamilCuk On

What's the idiomatic way of enabling UB sanitization in CMake?

There is no "idiomatic way" - it's compiler specific. Do:

  • Add -fsanitize=undefined to the compilation flags
target_compile_options(my_test PUBLIC -fsanitize=undefined)
target_link_options(my_test PUBLIC -fsanitize=undefined)

You could check if the option is supported with CheckCXXCompilerFlag.