summaryrefslogtreecommitdiff
path: root/cmake/detect-sanitizer.cmake
blob: b0a0236e83293092f0762f85b628e4b8b667a3fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# detect-sanitizer.cmake -- Detect supported compiler sanitizer flags
# Licensed under the Zlib license, see LICENSE.md for details

macro(check_sanitizer_support known_checks supported_checks)
    set(available_checks "")

    # Build list of supported sanitizer flags by incrementally trying compilation with
    # known sanitizer checks

    foreach(check ${known_checks})
        if(available_checks STREQUAL "")
            set(compile_checks "${check}")
        else()
            set(compile_checks "${available_checks},${check}")
        endif()

        set(CMAKE_REQUIRED_FLAGS "-fsanitize=${compile_checks}")

        check_c_source_compiles("int main() { return 0; }" HAS_SANITIZER_${check}
            FAIL_REGEX "not supported|unrecognized command|unknown option")

        set(CMAKE_REQUIRED_FLAGS)

        if(HAS_SANITIZER_${check})
            set(available_checks ${compile_checks})
        endif()
    endforeach()

    set(${supported_checks} ${available_checks})
endmacro()

macro(add_address_sanitizer)
    set(known_checks
        address
        pointer-compare
        pointer-subtract
        )

    check_sanitizer_support("${known_checks}" supported_checks)
    if(NOT ${supported_checks} STREQUAL "")
        message(STATUS "Address sanitizer is enabled: ${supported_checks}")
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=${supported_checks}")
    else()
        message(STATUS "Address sanitizer is not supported")
    endif()

    if(CMAKE_CROSSCOMPILING_EMULATOR)
        # Only check for leak sanitizer if not cross-compiling due to qemu crash
        message(WARNING "Leak sanitizer is not supported when cross compiling")
    else()
        # Leak sanitizer requires address sanitizer
        check_sanitizer_support("leak" supported_checks)
        if(NOT ${supported_checks} STREQUAL "")
            message(STATUS "Leak sanitizer is enabled: ${supported_checks}")
            set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=${supported_checks}")
        else()
            message(STATUS "Leak sanitizer is not supported")
        endif()
    endif()
endmacro()

macro(add_memory_sanitizer)
    check_sanitizer_support("memory" supported_checks)
    if(NOT ${supported_checks} STREQUAL "")
        message(STATUS "Memory sanitizer is enabled: ${supported_checks}")
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=${supported_checks}")
    else()
        message(STATUS "Memory sanitizer is not supported")
    endif()
endmacro()

macro(add_thread_sanitizer)
    check_sanitizer_support("thread" supported_checks)
    if(NOT ${supported_checks} STREQUAL "")
        message(STATUS "Thread sanitizer is enabled: ${supported_checks}")
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=${supported_checks}")
    else()
        message(STATUS "Thread sanitizer is not supported")
    endif()
endmacro()

macro(add_undefined_sanitizer)
    set(known_checks
        array-bounds
        bool
        bounds
        builtin
        enum
        float-cast-overflow
        float-divide-by-zero
        function
        integer-divide-by-zero
        local-bounds
        null
        nonnull-attribute
        pointer-overflow
        return
        returns-nonnull-attribute
        shift
        shift-base
        shift-exponent
        signed-integer-overflow
        undefined
        unsigned-integer-overflow
        unsigned-shift-base
        vla-bound
        vptr
        )

    # Only check for alignment sanitizer flag if unaligned access is not supported
    if(NOT UNALIGNED_OK)
        list(APPEND known_checks alignment)
    endif()
    # Object size sanitizer has no effect at -O0 and produces compiler warning if enabled
    if(NOT CMAKE_C_FLAGS MATCHES "-O0")
        list(APPEND known_checks object-size)
    endif()

    check_sanitizer_support("${known_checks}" supported_checks)

    if(NOT ${supported_checks} STREQUAL "")
        message(STATUS "Undefined behavior sanitizer is enabled: ${supported_checks}")
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=${supported_checks}")

        # Group sanitizer flag -fsanitize=undefined will automatically add alignment, even if
        # it is not in our sanitize flag list, so we need to explicitly disable alignment sanitizing.
        if(UNALIGNED_OK)
            set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=alignment")
        endif()
    else()
        message(STATUS "UNdefined behavior sanitizer is not supported")
    endif()
endmacro()