1# Support for building with UndefinedBehaviorSanitizer (ubsan) -
2# https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
3#
4# Usage:
5# The variable CB_UNDEFINEDSANITIZER is used to enable UBSAN, which
6# accepts the following values:
7#   0: Disabled.
8#   1: Global - All targets will have UBSan enabled on them.
9#   2: Specific - Only targets which explicitly enable UBSan, via the
10#      add_sanitizers() macro will have UBSan enabled.
11
12INCLUDE(CheckCCompilerFlag)
13INCLUDE(CheckCXXCompilerFlag)
14INCLUDE(CMakePushCheckState)
15
16OPTION(CB_UNDEFINEDSANITIZER "Enable UndefinedBehaviorSanitizer memory error detector."
17       0)
18
19IF (CB_UNDEFINEDSANITIZER GREATER 0)
20
21    CMAKE_PUSH_CHECK_STATE(RESET)
22    SET(CMAKE_REQUIRED_FLAGS "-fsanitize=undefined") # Also needs to be a link flag for test to pass
23    CHECK_C_COMPILER_FLAG("-fsanitize=undefined" HAVE_FLAG_SANITIZE_UNDEFINED_C)
24    CHECK_CXX_COMPILER_FLAG("-fsanitize=undefined" HAVE_FLAG_SANITIZE_UNDEFINED_CXX)
25    CMAKE_POP_CHECK_STATE()
26
27    IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
28      # Clang requires an external symbolizer program.
29      FIND_PROGRAM(LLVM_SYMBOLIZER
30                   NAMES llvm-symbolizer
31                         llvm-symbolizer-3.8
32                         llvm-symbolizer-3.7
33                         llvm-symbolizer-3.6)
34
35      IF(NOT LLVM_SYMBOLIZER)
36        MESSAGE(WARNING "UndefinedBehaviorSanitizer failed to locate an llvm-symbolizer program. Stack traces may lack symbols.")
37      ENDIF()
38    ENDIF()
39
40    IF(HAVE_FLAG_SANITIZE_UNDEFINED_C AND HAVE_FLAG_SANITIZE_UNDEFINED_CXX)
41        # Have UndefinedBehaviorSanitizer for C & C++; enable as per the user's selection.
42
43        # UBSan makes heavy use of RTTI to verify the type of objects
44        # match the pointer/reference they are accessed through at
45        # runtime. This requires typeinfo for essentially all types
46        # involved in static_cast<> / reinterpret_cast<>. To simplify
47        # providing this; change the default symbol visiibility to
48        # default (all symbols visible).
49        SET(UNDEFINED_SANITIZER_FLAG "-fsanitize=undefined -fno-sanitize=alignment -fvisibility=default")
50
51        # Configure CTest's MemCheck mode.
52        SET(MEMORYCHECK_TYPE UndefinedBehaviorSanitizer)
53
54        if(CB_UNDEFINEDSANITIZER EQUAL 1)
55            # Enable globally
56
57            # Need -fno-omit-frame-pointer to allow the backtraces to be symbolified.
58            SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${UNDEFINED_SANITIZER_FLAG} -fno-omit-frame-pointer")
59            SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${UNDEFINED_SANITIZER_FLAG} -fno-omit-frame-pointer")
60            SET(CMAKE_CGO_LDFLAGS "${CMAKE_CGO_LDFLAGS} ${UNDEFINED_SANITIZER_FLAG}")
61	    ADD_DEFINITIONS(-DUNDEFINED_SANITIZER)
62	endif()
63
64        MESSAGE(STATUS "UndefinedBehaviorSanitizer enabled (mode ${CB_UNDEFINEDSANITIZER})")
65    ELSE()
66        MESSAGE(FATAL_ERROR "CB_UNDEFINEDSANITIZER enabled but compiler doesn't support UBSan - cannot continue.")
67    ENDIF()
68ENDIF()
69
70# Enable UBSAN for specific target. No-op if
71# CB_UNDEFINEDSANITIZER is not set to 2 (target-specific mode).
72# Typically used via add_sanitizers()
73function(add_sanitize_undefined TARGET)
74    if (NOT CB_UNDEFINEDSANITIZER EQUAL 2)
75        return()
76    endif ()
77
78    set_property(TARGET ${TARGET} APPEND_STRING
79        PROPERTY COMPILE_FLAGS " ${UNDEFINED_SANITIZER_FLAG} -fno-omit-frame-pointer")
80    set_property(TARGET ${TARGET} APPEND_STRING
81        PROPERTY LINK_FLAGS " ${UNDEFINED_SANITIZER_FLAG}")
82endfunction()
83