1# detect-sanitizer.cmake -- Detect supported compiler sanitizer flags
2# Licensed under the Zlib license, see LICENSE.md for details
3
4macro(add_common_sanitizer_flags)
5    if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
6        add_compile_options(-g3)
7    endif()
8    check_c_compiler_flag(-fno-omit-frame-pointer HAVE_NO_OMIT_FRAME_POINTER)
9    if(HAVE_NO_OMIT_FRAME_POINTER)
10        add_compile_options(-fno-omit-frame-pointer)
11        add_link_options(-fno-omit-frame-pointer)
12    endif()
13    check_c_compiler_flag(-fno-optimize-sibling-calls HAVE_NO_OPTIMIZE_SIBLING_CALLS)
14    if(HAVE_NO_OPTIMIZE_SIBLING_CALLS)
15        add_compile_options(-fno-optimize-sibling-calls)
16        add_link_options(-fno-optimize-sibling-calls)
17    endif()
18endmacro()
19
20macro(check_sanitizer_support known_checks supported_checks)
21    set(available_checks "")
22
23    # Build list of supported sanitizer flags by incrementally trying compilation with
24    # known sanitizer checks
25
26    foreach(check ${known_checks})
27        if(available_checks STREQUAL "")
28            set(compile_checks "${check}")
29        else()
30            set(compile_checks "${available_checks},${check}")
31        endif()
32
33        set(CMAKE_REQUIRED_FLAGS -fsanitize=${compile_checks})
34
35        check_c_source_compiles("int main() { return 0; }" HAVE_SANITIZER_${check}
36            FAIL_REGEX "not supported|unrecognized command|unknown option")
37
38        set(CMAKE_REQUIRED_FLAGS)
39
40        if(HAVE_SANITIZER_${check})
41            set(available_checks ${compile_checks})
42        endif()
43    endforeach()
44
45    set(${supported_checks} ${available_checks})
46endmacro()
47
48macro(add_address_sanitizer)
49    set(known_checks
50        address
51        pointer-compare
52        pointer-subtract
53        )
54
55    check_sanitizer_support("${known_checks}" supported_checks)
56    if(NOT ${supported_checks} STREQUAL "")
57        message(STATUS "Address sanitizer is enabled: ${supported_checks}")
58        add_compile_options(-fsanitize=${supported_checks})
59        add_link_options(-fsanitize=${supported_checks})
60        add_common_sanitizer_flags()
61    else()
62        message(STATUS "Address sanitizer is not supported")
63    endif()
64
65    if(CMAKE_CROSSCOMPILING_EMULATOR)
66        # Only check for leak sanitizer if not cross-compiling due to qemu crash
67        message(WARNING "Leak sanitizer is not supported when cross compiling")
68    else()
69        # Leak sanitizer requires address sanitizer
70        check_sanitizer_support("leak" supported_checks)
71        if(NOT ${supported_checks} STREQUAL "")
72            message(STATUS "Leak sanitizer is enabled: ${supported_checks}")
73            add_compile_options(-fsanitize=${supported_checks})
74            add_link_options(-fsanitize=${supported_checks})
75            add_common_sanitizer_flags()
76        else()
77            message(STATUS "Leak sanitizer is not supported")
78        endif()
79    endif()
80endmacro()
81
82macro(add_memory_sanitizer)
83    check_sanitizer_support("memory" supported_checks)
84    if(NOT ${supported_checks} STREQUAL "")
85        message(STATUS "Memory sanitizer is enabled: ${supported_checks}")
86        add_compile_options(-fsanitize=${supported_checks})
87        add_link_options(-fsanitize=${supported_checks})
88        add_common_sanitizer_flags()
89
90        check_c_compiler_flag(-fsanitize-memory-track-origins HAVE_MEMORY_TRACK_ORIGINS)
91        if(HAVE_MEMORY_TRACK_ORIGINS)
92            add_compile_options(-fsanitize-memory-track-origins)
93            add_link_options(-fsanitize-memory-track-origins)
94        endif()
95    else()
96        message(STATUS "Memory sanitizer is not supported")
97    endif()
98endmacro()
99
100macro(add_thread_sanitizer)
101    check_sanitizer_support("thread" supported_checks)
102    if(NOT ${supported_checks} STREQUAL "")
103        message(STATUS "Thread sanitizer is enabled: ${supported_checks}")
104        add_compile_options(-fsanitize=${supported_checks})
105        add_link_options(-fsanitize=${supported_checks})
106        add_common_sanitizer_flags()
107    else()
108        message(STATUS "Thread sanitizer is not supported")
109    endif()
110endmacro()
111
112macro(add_undefined_sanitizer)
113    set(known_checks
114        array-bounds
115        bool
116        bounds
117        builtin
118        enum
119        float-cast-overflow
120        float-divide-by-zero
121        function
122        integer-divide-by-zero
123        local-bounds
124        null
125        nonnull-attribute
126        pointer-overflow
127        return
128        returns-nonnull-attribute
129        shift
130        shift-base
131        shift-exponent
132        signed-integer-overflow
133        undefined
134        unsigned-integer-overflow
135        unsigned-shift-base
136        vla-bound
137        vptr
138        )
139
140    # Only check for alignment sanitizer flag if unaligned access is not supported
141    if(NOT WITH_UNALIGNED)
142        list(APPEND known_checks alignment)
143    endif()
144    # Object size sanitizer has no effect at -O0 and produces compiler warning if enabled
145    if(NOT CMAKE_C_FLAGS MATCHES "-O0")
146        list(APPEND known_checks object-size)
147    endif()
148
149    check_sanitizer_support("${known_checks}" supported_checks)
150
151    if(NOT ${supported_checks} STREQUAL "")
152        message(STATUS "Undefined behavior sanitizer is enabled: ${supported_checks}")
153        add_compile_options(-fsanitize=${supported_checks})
154        add_link_options(-fsanitize=${supported_checks})
155
156        # Group sanitizer flag -fsanitize=undefined will automatically add alignment, even if
157        # it is not in our sanitize flag list, so we need to explicitly disable alignment sanitizing.
158        if(WITH_UNALIGNED)
159            add_compile_options(-fno-sanitize=alignment)
160        endif()
161
162        add_common_sanitizer_flags()
163    else()
164        message(STATUS "Undefined behavior sanitizer is not supported")
165    endif()
166endmacro()
167