From 4fd453301a2d18fc6c0cc6f2bdf0285da859895d Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Tue, 7 Jul 2015 00:31:35 +0200 Subject: [PATCH 01/25] Moved CMake files and added a simple testcase --- CMakeLists.txt | 51 ++++++++++++++++++++++ FindASan.cmake => cmake/FindASan.cmake | 0 FindMSan.cmake => cmake/FindMSan.cmake | 0 FindTSan.cmake => cmake/FindTSan.cmake | 0 FindUBSan.cmake => cmake/FindUBSan.cmake | 0 test_project/CMakeLists.txt | 12 ------ test_project/test.cpp | 48 --------------------- tests/CMakeLists.txt | 54 ++++++++++++++++++++++++ tests/asan_test.cpp | 39 +++++++++++++++++ 9 files changed, 144 insertions(+), 60 deletions(-) create mode 100644 CMakeLists.txt rename FindASan.cmake => cmake/FindASan.cmake (100%) rename FindMSan.cmake => cmake/FindMSan.cmake (100%) rename FindTSan.cmake => cmake/FindTSan.cmake (100%) rename FindUBSan.cmake => cmake/FindUBSan.cmake (100%) delete mode 100644 test_project/CMakeLists.txt delete mode 100644 test_project/test.cpp create mode 100644 tests/CMakeLists.txt create mode 100644 tests/asan_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d249a4d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +# This file is part of CMake-sanitizers. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# +# Copyright (c) +# 2013-2015 Matt Arsenault +# 2015 RWTH Aachen University, Federal Republic of Germany +# + + +# +# project information +# + +# minimum required cmake version +cmake_minimum_required(VERSION 2.8) + +# project name +project("CMake-sanitizers") + + + +# +# cmake configuration +# +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) + + + +# +# add tests +# +enable_testing() +add_subdirectory(tests) diff --git a/FindASan.cmake b/cmake/FindASan.cmake similarity index 100% rename from FindASan.cmake rename to cmake/FindASan.cmake diff --git a/FindMSan.cmake b/cmake/FindMSan.cmake similarity index 100% rename from FindMSan.cmake rename to cmake/FindMSan.cmake diff --git a/FindTSan.cmake b/cmake/FindTSan.cmake similarity index 100% rename from FindTSan.cmake rename to cmake/FindTSan.cmake diff --git a/FindUBSan.cmake b/cmake/FindUBSan.cmake similarity index 100% rename from FindUBSan.cmake rename to cmake/FindUBSan.cmake diff --git a/test_project/CMakeLists.txt b/test_project/CMakeLists.txt deleted file mode 100644 index 835a055..0000000 --- a/test_project/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ - -cmake_minimum_required(VERSION 2.8) - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/..") - - -include(FindUBSan) -include(FindTSan) -include(FindASan) -include(FindMSan) - -add_executable(test test.cpp) diff --git a/test_project/test.cpp b/test_project/test.cpp deleted file mode 100644 index b79e7d1..0000000 --- a/test_project/test.cpp +++ /dev/null @@ -1,48 +0,0 @@ - -#include -#include -#include - -class BarB -{ -public: - float y; - /* Include something that uses a virtual function. The symbols - that are broken on current OS X libc++ involve this */ - virtual int arst(int o) - { - return 4 + o; - } -}; - -static void print_array(const int* a) -{ - for (int i = 0; i < 4; ++i) - { - std::cout << a[i] << ", "; - } - - std::cout << '\n'; -} - -/* Just include something that ubsan will need to check */ -int main(int argc, const char* argv[]) -{ - BarB* b = new BarB(); - if (argc > 1) - { - int uninitialized[4]; - //int* uninitialized = new int[4]; - print_array(uninitialized); - //delete[] uninitialized; - - int x = atoi(argv[1]); - std::cout << (4 / x) << '\n'; - - fputs(argv[x], stdout); - std::cout << b->arst(x) << '\n'; - } - - delete b; - return 0; -} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..bf0649a --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,54 @@ +# This file is part of CMake-sanitizers. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# +# Copyright (c) +# 2013-2015 Matt Arsenault +# 2015 RWTH Aachen University, Federal Republic of Germany +# + +# Function to add testcases. +function(add_testcase TESTNAME SOURCEFILES) + # remove ${TESTNAME} from ${ARGV} to use ${ARGV} as ${SOURCEFILES} + list(REMOVE_AT ARGV 0) + + # add a new executable + add_executable(${TESTNAME} ${ARGV}) + + # add a testcase for executable + add_test(${TESTNAME} ${TESTNAME}) +endfunction(add_testcase) + + + +# +# search for sanitizers +# +find_package(ASan) +find_package(MSan) +find_package(TSan) +find_package(UBSan) + + + +# +# add testcases +# +add_testcase("asan_test_cpp" asan_test.cpp) diff --git a/tests/asan_test.cpp b/tests/asan_test.cpp new file mode 100644 index 0000000..4b276de --- /dev/null +++ b/tests/asan_test.cpp @@ -0,0 +1,39 @@ +/* This file is part of CMake-sanitizers. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Copyright (c) + * 2013-2015 Matt Arsenault + * 2015 RWTH Aachen University, Federal Republic of Germany + */ + + +int +main(int argc, char **argv) +{ + // Allocate a new array and delete it. + int *array = new int[argc]; + delete[] array; + + /* Access element of the deleted array. This will cause an memory error with + * address sanitizer. + */ + return array[argc]; +} From 41e635c9ca1d569b625f0693d252ff5e9b253a68 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Thu, 19 Nov 2015 14:11:10 +0100 Subject: [PATCH 02/25] Restructured code of FindASan.cmake --- cmake/FindASan.cmake | 148 +++++++++++++++++++++++++++++-------------- 1 file changed, 99 insertions(+), 49 deletions(-) diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index 064b5b6..4ea61e5 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -1,4 +1,3 @@ -# # The MIT License (MIT) # # Copyright (c) 2013 Matthew Arsenault @@ -10,65 +9,116 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# This module tests if address sanitizer is supported by the compiler, -# and creates a ASan build type (i.e. set CMAKE_BUILD_TYPE=ASan to use -# it). This sets the following variables: -# -# CMAKE_C_FLAGS_ASAN - Flags to use for C with asan -# CMAKE_CXX_FLAGS_ASAN - Flags to use for C++ with asan -# HAVE_ADDRESS_SANITIZER - True or false if the ASan build type is available +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. -include(CheckCCompilerFlag) +# This module tests if address sanitizer is supported by the compiler. The +# necessary flags for compiler and linker will be stored in variables. ASan can +# be enabled for all targets with CMake build type "ASan", individual targets +# can enable ASan with the saitize_address() function. -# Set -Werror to catch "argument unused during compilation" warnings -set(CMAKE_REQUIRED_FLAGS "-Werror -faddress-sanitizer") # Also needs to be a link flag for test to pass -check_c_compiler_flag("-faddress-sanitizer" HAVE_FLAG_ADDRESS_SANITIZER) +option(SANITIZE_ADDRESS "Selects wheter Address Sanitizer will be enabled for + individual targets" Off) -set(CMAKE_REQUIRED_FLAGS "-Werror -fsanitize=address") # Also needs to be a link flag for test to pass -check_c_compiler_flag("-fsanitize=address" HAVE_FLAG_SANITIZE_ADDRESS) +set(ASAN_FLAG_CANDIDATES + # Clang 3.2+ use this version + "-fsanitize=address" -unset(CMAKE_REQUIRED_FLAGS) + # Older deprecated flag for ASan + "-faddress-sanitizer" +) -if(HAVE_FLAG_SANITIZE_ADDRESS) - # Clang 3.2+ use this version - set(ADDRESS_SANITIZER_FLAG "-fsanitize=address") -elseif(HAVE_FLAG_ADDRESS_SANITIZER) - # Older deprecated flag for ASan - set(ADDRESS_SANITIZER_FLAG "-faddress-sanitizer") -endif() -if(NOT ADDRESS_SANITIZER_FLAG) - return() -else(NOT ADDRESS_SANITIZER_FLAG) - set(HAVE_ADDRESS_SANITIZER FALSE) +set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) +set(CMAKE_REQUIRED_QUIET ${ASan_FIND_QUIETLY}) + +set(_ASAN_REQUIRED_VARS) +foreach (LANG C CXX) + if (NOT CMAKE_${LANG}_COMPILER_LOADED) + continue() + endif() + + list(APPEND _ASAN_REQUIRED_VARS ASAN_${LANG}_FLAGS) + + # If flags for this compiler were already found, do not try to find them + # again. + if (ASAN_${LANG}_FLAGS) + continue() + endif () + + foreach (FLAG ${ASAN_FLAG_CANDIDATES}) + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Try Address sanitizer ${LANG} flag = [${FLAG}]") + endif() + + set(CMAKE_REQUIRED_FLAGS "${FLAG}") + unset(ASAN_FLAG_DETECTED CACHE) + + if (${LANG} STREQUAL "C") + include(CheckCCompilerFlag) + check_c_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) + + elseif (${LANG} STREQUAL "CXX") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) + endif() + + if (ASAN_FLAG_DETECTED) + set(ASAN_${LANG}_FLAGS "${FLAG}" + CACHE STRING "${LANG} compiler flags for Address sanitizer") + break() + endif () + endforeach() +endforeach () + +set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE}) + + +if (_ASAN_REQUIRED_VARS) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(ASan REQUIRED_VARS ${_ASAN_REQUIRED_VARS}) + mark_as_advanced(${_ASAN_REQUIRED_VARS}) + unset(_ASAN_REQUIRED_VARS) +else() + message(SEND_ERROR "FindASan requires C or CXX language to be enabled") endif() -set(HAVE_ADDRESS_SANITIZER TRUE) - -set(CMAKE_C_FLAGS_ASAN "-O1 -g ${ADDRESS_SANITIZER_FLAG} -fno-omit-frame-pointer -fno-optimize-sibling-calls" - CACHE STRING "Flags used by the C compiler during ASan builds." - FORCE) -set(CMAKE_CXX_FLAGS_ASAN "-O1 -g ${ADDRESS_SANITIZER_FLAG} -fno-omit-frame-pointer -fno-optimize-sibling-calls" - CACHE STRING "Flags used by the C++ compiler during ASan builds." - FORCE) -set(CMAKE_EXE_LINKER_FLAGS_ASAN "${ADDRESS_SANITIZER_FLAG}" - CACHE STRING "Flags used for linking binaries during ASan builds." - FORCE) -set(CMAKE_SHARED_LINKER_FLAGS_ASAN "${ADDRESS_SANITIZER_FLAG}" - CACHE STRING "Flags used by the shared libraries linker during ASan builds." - FORCE) -mark_as_advanced(CMAKE_C_FLAGS_ASAN - CMAKE_CXX_FLAGS_ASAN - CMAKE_EXE_LINKER_FLAGS_ASAN - CMAKE_SHARED_LINKER_FLAGS_ASAN) + +# add build target ASan +if (ASan_FOUND) + set(CMAKE_C_FLAGS_ASAN "${ASAN_C_FLAGS}" CACHE + STRING "Flags used by the C compiler during ASan builds.") + set(CMAKE_CXX_FLAGS_ASAN "${ASAN_CXX_FLAGS}" CACHE + STRING "Flags used by the C++ compiler during ASan builds.") + set(CMAKE_EXE_LINKER_FLAGS_ASAN "${ASAN_C_FLAGS}" CACHE + STRING "Flags used for linking binaries during ASan builds.") + set(CMAKE_SHARED_LINKER_FLAGS_ASAN "${ASAN_C_FLAGS}" CACHE + STRING "Flags used by the shared libraries linker during ASan builds.") + set(CMAKE_MODULE_LINKER_FLAGS_ASAN "${ASAN_C_FLAGS}" CACHE + STRING "Flags used by the module libraries linker during ASan builds.") + mark_as_advanced(CMAKE_C_FLAGS_ASAN + CMAKE_CXX_FLAGS_ASAN + CMAKE_EXE_LINKER_FLAGS_ASAN + CMAKE_SHARED_LINKER_FLAGS_ASAN + CMAKE_MODULE_LINKER_FLAGS_ASAN) +endif () + + +function (sanitize_address TARGET) + if (NOT SANITIZE_ADDRESS) + return() + endif () + + set_property(TARGET ${TARGET} APPEND_STRING PROPERTY + COMPILE_FLAGS " ${ASAN_C_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING PROPERTY + LINK_FLAGS " ${ASAN_C_FLAGS}") +endfunction () From e39082050c167551392051b26b0c187c60cc6e4a Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Thu, 19 Nov 2015 16:38:53 +0100 Subject: [PATCH 03/25] Removed continue statement in FindASan.cmake Older CMake versions are not capable of continue in foreach functions. --- cmake/FindASan.cmake | 64 +++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index 4ea61e5..ea66275 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -42,41 +42,37 @@ set(CMAKE_REQUIRED_QUIET ${ASan_FIND_QUIETLY}) set(_ASAN_REQUIRED_VARS) foreach (LANG C CXX) - if (NOT CMAKE_${LANG}_COMPILER_LOADED) - continue() - endif() - - list(APPEND _ASAN_REQUIRED_VARS ASAN_${LANG}_FLAGS) - - # If flags for this compiler were already found, do not try to find them - # again. - if (ASAN_${LANG}_FLAGS) - continue() - endif () - - foreach (FLAG ${ASAN_FLAG_CANDIDATES}) - if(NOT CMAKE_REQUIRED_QUIET) - message(STATUS "Try Address sanitizer ${LANG} flag = [${FLAG}]") - endif() - - set(CMAKE_REQUIRED_FLAGS "${FLAG}") - unset(ASAN_FLAG_DETECTED CACHE) - - if (${LANG} STREQUAL "C") - include(CheckCCompilerFlag) - check_c_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "CXX") - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) - endif() - - if (ASAN_FLAG_DETECTED) - set(ASAN_${LANG}_FLAGS "${FLAG}" - CACHE STRING "${LANG} compiler flags for Address sanitizer") - break() + if (CMAKE_${LANG}_COMPILER_LOADED) + list(APPEND _ASAN_REQUIRED_VARS ASAN_${LANG}_FLAGS) + + # If flags for this compiler were already found, do not try to find them + # again. + if (NOT ASAN_${LANG}_FLAGS) + foreach (FLAG ${ASAN_FLAG_CANDIDATES}) + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Try Address sanitizer ${LANG} flag = [${FLAG}]") + endif() + + set(CMAKE_REQUIRED_FLAGS "${FLAG}") + unset(ASAN_FLAG_DETECTED CACHE) + + if (${LANG} STREQUAL "C") + include(CheckCCompilerFlag) + check_c_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) + + elseif (${LANG} STREQUAL "CXX") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) + endif() + + if (ASAN_FLAG_DETECTED) + set(ASAN_${LANG}_FLAGS "${FLAG}" + CACHE STRING "${LANG} compiler flags for Address sanitizer") + break() + endif () + endforeach() endif () - endforeach() + endif () endforeach () set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE}) From c29aa33f119c9452e01d5f433bb2df8eaf124037 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Thu, 28 Jan 2016 17:31:31 +0100 Subject: [PATCH 04/25] Check ASan support for all enabled compilers. Instead of fix checking for C and CXX compilers, all enabled languages will be tested now. --- cmake/FindASan.cmake | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index ea66275..248a77a 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -41,7 +41,9 @@ set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) set(CMAKE_REQUIRED_QUIET ${ASan_FIND_QUIETLY}) set(_ASAN_REQUIRED_VARS) -foreach (LANG C CXX) + +get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) +foreach (LANG ${ENABLED_LANGUAGES}) if (CMAKE_${LANG}_COMPILER_LOADED) list(APPEND _ASAN_REQUIRED_VARS ASAN_${LANG}_FLAGS) @@ -63,6 +65,10 @@ foreach (LANG C CXX) elseif (${LANG} STREQUAL "CXX") include(CheckCXXCompilerFlag) check_cxx_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) + + elseif (${LANG} STREQUAL "Fortran") + include(CheckFortranCompilerFlag) + check_fortran_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) endif() if (ASAN_FLAG_DETECTED) @@ -84,25 +90,26 @@ if (_ASAN_REQUIRED_VARS) mark_as_advanced(${_ASAN_REQUIRED_VARS}) unset(_ASAN_REQUIRED_VARS) else() - message(SEND_ERROR "FindASan requires C or CXX language to be enabled") + message(SEND_ERROR "FindASan requires C, CXX or Fortran language to be enabled") endif() # add build target ASan if (ASan_FOUND) - set(CMAKE_C_FLAGS_ASAN "${ASAN_C_FLAGS}" CACHE - STRING "Flags used by the C compiler during ASan builds.") - set(CMAKE_CXX_FLAGS_ASAN "${ASAN_CXX_FLAGS}" CACHE - STRING "Flags used by the C++ compiler during ASan builds.") + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + foreach (LANG ${ENABLED_LANGUAGES}) + set(CMAKE_${LANG}_FLAGS_ASAN "${ASAN_${LANG}_FLAGS}" CACHE + STRING "Flags used by the ${LANG} compiler during ASan builds.") + mark_as_advanced(CMAKE_${LANG}_FLAGS_ASAN) + endforeach () + set(CMAKE_EXE_LINKER_FLAGS_ASAN "${ASAN_C_FLAGS}" CACHE STRING "Flags used for linking binaries during ASan builds.") set(CMAKE_SHARED_LINKER_FLAGS_ASAN "${ASAN_C_FLAGS}" CACHE STRING "Flags used by the shared libraries linker during ASan builds.") set(CMAKE_MODULE_LINKER_FLAGS_ASAN "${ASAN_C_FLAGS}" CACHE STRING "Flags used by the module libraries linker during ASan builds.") - mark_as_advanced(CMAKE_C_FLAGS_ASAN - CMAKE_CXX_FLAGS_ASAN - CMAKE_EXE_LINKER_FLAGS_ASAN + mark_as_advanced(CMAKE_EXE_LINKER_FLAGS_ASAN CMAKE_SHARED_LINKER_FLAGS_ASAN CMAKE_MODULE_LINKER_FLAGS_ASAN) endif () From f62b5858ac29d093bc70bbb0f53c26063123c1d9 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Thu, 28 Jan 2016 17:41:24 +0100 Subject: [PATCH 05/25] Compile flags will now be set per-source file. Thus C can be mixed with Fortran and the objects can be build with different compilers, not both may support AddressSanitizer. --- cmake/FindASan.cmake | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index 248a77a..2654f2d 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -115,13 +115,29 @@ if (ASan_FOUND) endif () + function (sanitize_address TARGET) - if (NOT SANITIZE_ADDRESS) + if (NOT SANITIZE_ADDRESS OR NOT ASan_FOUND) return() endif () - set_property(TARGET ${TARGET} APPEND_STRING PROPERTY - COMPILE_FLAGS " ${ASAN_C_FLAGS}") + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + get_target_property(SOURCE_FILES ${TARGET} SOURCES) + foreach (SOURCE_FILE ${SOURCE_FILES}) + foreach (LANG ${ENABLED_LANGUAGES}) + get_filename_component(FILE_EXT "${SOURCE_FILE}" EXT) + string(TOLOWER "${FILE_EXT}" FILE_EXT) + string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) + list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) + if (NOT ${TEMP} EQUAL -1) + if (DEFINED ASAN_${LANG}_FLAGS}) + set_property(SOURCE ${SOURCE_FILE} APPEND_STRING PROPERTY + COMPILE_FLAGS " ${ASAN_${LANG}_FLAGS}") + endif () + endif () + endforeach() + endforeach (SOURCE_FILE) + set_property(TARGET ${TARGET} APPEND_STRING PROPERTY LINK_FLAGS " ${ASAN_C_FLAGS}") endfunction () From d81be396789e65222b301f6c4e87f22406725969 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Thu, 28 Jan 2016 19:17:07 +0100 Subject: [PATCH 06/25] Bugfix in FindASan.cmake. --- cmake/FindASan.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index 2654f2d..bfc5354 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -117,7 +117,7 @@ endif () function (sanitize_address TARGET) - if (NOT SANITIZE_ADDRESS OR NOT ASan_FOUND) + if (NOT SANITIZE_ADDRESS) return() endif () From c96a15b46c38e3c961dd95c8dfdc3d51b60d78fa Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Mon, 15 Feb 2016 05:13:36 +0100 Subject: [PATCH 07/25] Bugfix in FindASan.cmake --- cmake/FindASan.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index bfc5354..7de9ebd 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -130,7 +130,7 @@ function (sanitize_address TARGET) string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) if (NOT ${TEMP} EQUAL -1) - if (DEFINED ASAN_${LANG}_FLAGS}) + if (DEFINED ASAN_${LANG}_FLAGS) set_property(SOURCE ${SOURCE_FILE} APPEND_STRING PROPERTY COMPILE_FLAGS " ${ASAN_${LANG}_FLAGS}") endif () From 584f137e709d32c376403b32580605db2b6df00c Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Tue, 8 Mar 2016 03:49:25 +0100 Subject: [PATCH 08/25] Reworked FindASan.cmake. * removed build type ASAN. Targets should define ASan usage by sanitize_address function * compiler flags will be searched by compiler instead of per language * FindASan warns, if a target can't be sanitized because of incompatible compilers * added some helper functions --- cmake/FindASan.cmake | 144 ++++++++++++++++------------------- cmake/sanitize-helpers.cmake | 66 ++++++++++++++++ 2 files changed, 130 insertions(+), 80 deletions(-) create mode 100644 cmake/sanitize-helpers.cmake diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index 7de9ebd..d31d37a 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -40,80 +40,62 @@ set(ASAN_FLAG_CANDIDATES set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) set(CMAKE_REQUIRED_QUIET ${ASan_FIND_QUIETLY}) -set(_ASAN_REQUIRED_VARS) - get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) foreach (LANG ${ENABLED_LANGUAGES}) - if (CMAKE_${LANG}_COMPILER_LOADED) - list(APPEND _ASAN_REQUIRED_VARS ASAN_${LANG}_FLAGS) - - # If flags for this compiler were already found, do not try to find them - # again. - if (NOT ASAN_${LANG}_FLAGS) - foreach (FLAG ${ASAN_FLAG_CANDIDATES}) - if(NOT CMAKE_REQUIRED_QUIET) - message(STATUS "Try Address sanitizer ${LANG} flag = [${FLAG}]") - endif() - - set(CMAKE_REQUIRED_FLAGS "${FLAG}") - unset(ASAN_FLAG_DETECTED CACHE) - - if (${LANG} STREQUAL "C") - include(CheckCCompilerFlag) - check_c_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "CXX") - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "Fortran") - include(CheckFortranCompilerFlag) + # Sanitizer flags are not dependend on language, but the used compiler. So + # instead of searching flags foreach language, search flags foreach compiler + # used. + set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) + if (NOT ASAN_${COMPILER}_FLAGS) + foreach (FLAG ${ASAN_FLAG_CANDIDATES}) + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS + "Try ${COMPILER} AddressSanitizer flag = [${FLAG}]") + endif() + + set(CMAKE_REQUIRED_FLAGS "${FLAG}") + unset(ASAN_FLAG_DETECTED CACHE) + + if (${LANG} STREQUAL "C") + include(CheckCCompilerFlag) + check_c_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) + + elseif (${LANG} STREQUAL "CXX") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) + + elseif (${LANG} STREQUAL "Fortran") + # CheckFortranCompilerFlag was introduced in CMake 3.x. To be + # compatible with older Cmake versions, we will check if this + # module is present before we use it. Otherwise we will define + # Fortran coverage support as not available. + include(CheckFortranCompilerFlag OPTIONAL + RESULT_VARIABLE INCLUDED) + if (INCLUDED) check_fortran_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) - endif() - - if (ASAN_FLAG_DETECTED) - set(ASAN_${LANG}_FLAGS "${FLAG}" - CACHE STRING "${LANG} compiler flags for Address sanitizer") - break() + elseif (NOT CMAKE_REQUIRED_QUIET) + message("-- Performing Test ASAN_FLAG_DETECTED") + message("-- Performing Test ASAN_FLAG_DETECTED - Failed " + "(Check not supported)") endif () - endforeach() - endif () + endif() + + if (ASAN_FLAG_DETECTED) + set(ASAN_${COMPILER}_FLAGS "${FLAG}" + CACHE STRING "${LANG} compiler flags for AddressSanitizer.") + mark_as_advanced(ASAN_${COMPILER}_FLAGS) + break() + endif () + endforeach () endif () endforeach () set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE}) -if (_ASAN_REQUIRED_VARS) - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(ASan REQUIRED_VARS ${_ASAN_REQUIRED_VARS}) - mark_as_advanced(${_ASAN_REQUIRED_VARS}) - unset(_ASAN_REQUIRED_VARS) -else() - message(SEND_ERROR "FindASan requires C, CXX or Fortran language to be enabled") -endif() - - -# add build target ASan -if (ASan_FOUND) - get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) - foreach (LANG ${ENABLED_LANGUAGES}) - set(CMAKE_${LANG}_FLAGS_ASAN "${ASAN_${LANG}_FLAGS}" CACHE - STRING "Flags used by the ${LANG} compiler during ASan builds.") - mark_as_advanced(CMAKE_${LANG}_FLAGS_ASAN) - endforeach () - set(CMAKE_EXE_LINKER_FLAGS_ASAN "${ASAN_C_FLAGS}" CACHE - STRING "Flags used for linking binaries during ASan builds.") - set(CMAKE_SHARED_LINKER_FLAGS_ASAN "${ASAN_C_FLAGS}" CACHE - STRING "Flags used by the shared libraries linker during ASan builds.") - set(CMAKE_MODULE_LINKER_FLAGS_ASAN "${ASAN_C_FLAGS}" CACHE - STRING "Flags used by the module libraries linker during ASan builds.") - mark_as_advanced(CMAKE_EXE_LINKER_FLAGS_ASAN - CMAKE_SHARED_LINKER_FLAGS_ASAN - CMAKE_MODULE_LINKER_FLAGS_ASAN) -endif () +include(sanitize-helpers) function (sanitize_address TARGET) @@ -121,23 +103,25 @@ function (sanitize_address TARGET) return() endif () - get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) - get_target_property(SOURCE_FILES ${TARGET} SOURCES) - foreach (SOURCE_FILE ${SOURCE_FILES}) - foreach (LANG ${ENABLED_LANGUAGES}) - get_filename_component(FILE_EXT "${SOURCE_FILE}" EXT) - string(TOLOWER "${FILE_EXT}" FILE_EXT) - string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) - list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) - if (NOT ${TEMP} EQUAL -1) - if (DEFINED ASAN_${LANG}_FLAGS) - set_property(SOURCE ${SOURCE_FILE} APPEND_STRING PROPERTY - COMPILE_FLAGS " ${ASAN_${LANG}_FLAGS}") - endif () - endif () - endforeach() - endforeach (SOURCE_FILE) + # Get list of compilers used by target and check, if target can be checked + # by sanitizer. + sanitizer_target_compilers(${TARGET} TARGET_COMPILER) + list(LENGTH TARGET_COMPILER NUM_COMPILERS) + if (NUM_COMPILERS GREATER 1) + message(AUTHOR_WARNING "AddressSanitizer disabled for target ${TARGET} " + "because it will be compiled by different compilers.") + return() + + elseif ((NUM_COMPILERS EQUAL 0) OR + (NOT DEFINED "ASAN_${TARGET_COMPILER}_FLAGS")) + message(AUTHOR_WARNING "AddressSanitizer disabled for target ${TARGET} " + "because there is no sanitizer available for target sources.") + return() + endif() - set_property(TARGET ${TARGET} APPEND_STRING PROPERTY - LINK_FLAGS " ${ASAN_C_FLAGS}") + # Set compile- and link-flags for target. + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY COMPILE_FLAGS " ${ASAN_${TARGET_COMPILER}_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY LINK_FLAGS " ${ASAN_${TARGET_COMPILER}_FLAGS}") endfunction () diff --git a/cmake/sanitize-helpers.cmake b/cmake/sanitize-helpers.cmake new file mode 100644 index 0000000..f163588 --- /dev/null +++ b/cmake/sanitize-helpers.cmake @@ -0,0 +1,66 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# Helper function to get the language of a source file. +function (sanitizer_lang_of_source FILE RETURN_VAR) + get_filename_component(FILE_EXT "${FILE}" EXT) + string(TOLOWER "${FILE_EXT}" FILE_EXT) + string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) + + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + foreach (LANG ${ENABLED_LANGUAGES}) + list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) + if (NOT ${TEMP} EQUAL -1) + set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) + return() + endif () + endforeach() + + set(${RETURN_VAR} "" PARENT_SCOPE) +endfunction () + + +# Helper function to get compilers used by a target. +function (sanitizer_target_compilers TARGET RETURN_VAR) + # Check if all sources for target use the same compiler. If a target uses + # e.g. C and Fortran mixed and uses different compilers (e.g. clang and + # gfortran) this can trigger huge problems, because different compilers may + # use different implementations for sanitizers. + set(BUFFER "") + get_target_property(TSOURCES ${TARGET} SOURCES) + foreach (FILE ${TSOURCES}) + # If expression was found, FILE is a generator-expression for an object + # library. Object libraries will be ignored. + string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) + if ("${_file}" STREQUAL "") + sanitizer_lang_of_source(${FILE} LANG) + if (LANG) + list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID}) + endif () + endif () + endforeach () + + list(REMOVE_DUPLICATES BUFFER) + set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) +endfunction () From 88e75c6b037a9e3fb05487150fd9ea0cee9db2b0 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Tue, 8 Mar 2016 04:18:39 +0100 Subject: [PATCH 09/25] Enabled debug symbols for ASan. --- cmake/FindASan.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index d31d37a..f0d7c07 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -30,10 +30,10 @@ option(SANITIZE_ADDRESS "Selects wheter Address Sanitizer will be enabled for set(ASAN_FLAG_CANDIDATES # Clang 3.2+ use this version - "-fsanitize=address" + "-g -O0 -fsanitize=address" # Older deprecated flag for ASan - "-faddress-sanitizer" + "-g -O0 -faddress-sanitizer" ) From 925961bb5e72b1644a29b536d0a87173955936f4 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Tue, 8 Mar 2016 04:42:43 +0100 Subject: [PATCH 10/25] Bugfix in CMake cache docs. --- cmake/FindASan.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index f0d7c07..2de9ab4 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -82,7 +82,7 @@ foreach (LANG ${ENABLED_LANGUAGES}) if (ASAN_FLAG_DETECTED) set(ASAN_${COMPILER}_FLAGS "${FLAG}" - CACHE STRING "${LANG} compiler flags for AddressSanitizer.") + CACHE STRING "${COMPILER} flags for AddressSanitizer.") mark_as_advanced(ASAN_${COMPILER}_FLAGS) break() endif () From 8f2015a4d3d917c5bd1a84d51364c33ba0d15ea9 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Wed, 6 Apr 2016 01:17:20 +0200 Subject: [PATCH 11/25] Changed copyright notice. --- LICENSE | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 6a9eda6..2520efd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,8 @@ The MIT License (MIT) -Copyright (c) 2013 Matt Arsenault +Copyright (c) + 2013 Matthew Arsenault + 2015-2016 RWTH Aachen University, Federal Republic of Germany Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From b492553f61cb500d1dc1734934c65b6605bb2369 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Wed, 6 Apr 2016 18:41:24 +0200 Subject: [PATCH 12/25] New central module FindSanitizers.cmake. * Added a new module FindSanitizers with new function add_sanitizers to combine all sanitizers in one function instead of adding each one by one. * Code of FindASan.cmake was outdourced into helper functions, so that the code may be used by other sanitizer modules, too. * AddressSanitizer will be used with -O1 now to get a better performance. --- cmake/FindASan.cmake | 101 ++++------------------------------- cmake/FindSanitizers.cmake | 50 +++++++++++++++++ cmake/sanitize-helpers.cmake | 83 ++++++++++++++++++++++++++++ tests/CMakeLists.txt | 6 +-- 4 files changed, 145 insertions(+), 95 deletions(-) create mode 100644 cmake/FindSanitizers.cmake diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index 2de9ab4..5c95a0b 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -1,6 +1,8 @@ # The MIT License (MIT) # -# Copyright (c) 2013 Matthew Arsenault +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,108 +22,25 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -# This module tests if address sanitizer is supported by the compiler. The -# necessary flags for compiler and linker will be stored in variables. ASan can -# be enabled for all targets with CMake build type "ASan", individual targets -# can enable ASan with the saitize_address() function. +option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) -option(SANITIZE_ADDRESS "Selects wheter Address Sanitizer will be enabled for - individual targets" Off) - -set(ASAN_FLAG_CANDIDATES +set(FLAG_CANDIDATES # Clang 3.2+ use this version - "-g -O0 -fsanitize=address" + "-g -O1 -fsanitize=address" # Older deprecated flag for ASan - "-g -O0 -faddress-sanitizer" + "-g -O1 -faddress-sanitizer" ) -set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) -set(CMAKE_REQUIRED_QUIET ${ASan_FIND_QUIETLY}) - -get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) -foreach (LANG ${ENABLED_LANGUAGES}) - # Sanitizer flags are not dependend on language, but the used compiler. So - # instead of searching flags foreach language, search flags foreach compiler - # used. - set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) - if (NOT ASAN_${COMPILER}_FLAGS) - foreach (FLAG ${ASAN_FLAG_CANDIDATES}) - if(NOT CMAKE_REQUIRED_QUIET) - message(STATUS - "Try ${COMPILER} AddressSanitizer flag = [${FLAG}]") - endif() - - set(CMAKE_REQUIRED_FLAGS "${FLAG}") - unset(ASAN_FLAG_DETECTED CACHE) - - if (${LANG} STREQUAL "C") - include(CheckCCompilerFlag) - check_c_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "CXX") - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "Fortran") - # CheckFortranCompilerFlag was introduced in CMake 3.x. To be - # compatible with older Cmake versions, we will check if this - # module is present before we use it. Otherwise we will define - # Fortran coverage support as not available. - include(CheckFortranCompilerFlag OPTIONAL - RESULT_VARIABLE INCLUDED) - if (INCLUDED) - check_fortran_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) - elseif (NOT CMAKE_REQUIRED_QUIET) - message("-- Performing Test ASAN_FLAG_DETECTED") - message("-- Performing Test ASAN_FLAG_DETECTED - Failed " - "(Check not supported)") - endif () - endif() - - if (ASAN_FLAG_DETECTED) - set(ASAN_${COMPILER}_FLAGS "${FLAG}" - CACHE STRING "${COMPILER} flags for AddressSanitizer.") - mark_as_advanced(ASAN_${COMPILER}_FLAGS) - break() - endif () - endforeach () - endif () -endforeach () - -set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE}) - - - - include(sanitize-helpers) +sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" "ASan") -function (sanitize_address TARGET) +function (add_sanitize_address TARGET) if (NOT SANITIZE_ADDRESS) return() endif () - # Get list of compilers used by target and check, if target can be checked - # by sanitizer. - sanitizer_target_compilers(${TARGET} TARGET_COMPILER) - list(LENGTH TARGET_COMPILER NUM_COMPILERS) - if (NUM_COMPILERS GREATER 1) - message(AUTHOR_WARNING "AddressSanitizer disabled for target ${TARGET} " - "because it will be compiled by different compilers.") - return() - - elseif ((NUM_COMPILERS EQUAL 0) OR - (NOT DEFINED "ASAN_${TARGET_COMPILER}_FLAGS")) - message(AUTHOR_WARNING "AddressSanitizer disabled for target ${TARGET} " - "because there is no sanitizer available for target sources.") - return() - endif() - - # Set compile- and link-flags for target. - set_property(TARGET ${TARGET} APPEND_STRING - PROPERTY COMPILE_FLAGS " ${ASAN_${TARGET_COMPILER}_FLAGS}") - set_property(TARGET ${TARGET} APPEND_STRING - PROPERTY LINK_FLAGS " ${ASAN_${TARGET_COMPILER}_FLAGS}") + saitizer_add_flags(${TARGET} "AddressSanitizer" "ASan") endfunction () diff --git a/cmake/FindSanitizers.cmake b/cmake/FindSanitizers.cmake new file mode 100644 index 0000000..272bb2d --- /dev/null +++ b/cmake/FindSanitizers.cmake @@ -0,0 +1,50 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +# The following options will enable the desired sanitizers. +option(SANITIZE "Enable all available sanitizers for sanitized targets." OFF) + +# If option SANITIZE is enabled, enable all available sanitizers. +if (SANITIZE) + set(SANITIZE_ADDRESS ON CACHE BOOL + "Enable AddressSanitizer for sanitized targets." FORCE) +endif (SANITIZE) + + + + +set(FIND_QUIETLY_FLAG "") +if (DEFINED Sanitizers_FIND_QUIETLY) + set(FIND_QUIETLY_FLAG "QUIET") +endif () + +find_package(ASan ${FIND_QUIETLY_FLAG}) + + + + +function(add_sanitizers TARGET) + add_sanitize_address(${TARGET}) +endfunction(add_sanitizers) diff --git a/cmake/sanitize-helpers.cmake b/cmake/sanitize-helpers.cmake index f163588..63586f1 100644 --- a/cmake/sanitize-helpers.cmake +++ b/cmake/sanitize-helpers.cmake @@ -64,3 +64,86 @@ function (sanitizer_target_compilers TARGET RETURN_VAR) list(REMOVE_DUPLICATES BUFFER) set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) endfunction () + + +# Helper function to test compiler flags. +function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) + set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) + + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + foreach (LANG ${ENABLED_LANGUAGES}) + # Sanitizer flags are not dependend on language, but the used compiler. + # So instead of searching flags foreach language, search flags foreach + # compiler used. + set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) + if (NOT ${PREFIX}_${COMPILER}_FLAGS) + foreach (FLAG ${FLAG_CANDIDATES}) + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") + endif() + + set(CMAKE_REQUIRED_FLAGS "${FLAG}") + unset(${PREFIX}_FLAG_DETECTED CACHE) + + if (${LANG} STREQUAL "C") + include(CheckCCompilerFlag) + check_c_compiler_flag("${FLAG}" ${PREFIX}_FLAG_DETECTED) + + elseif (${LANG} STREQUAL "CXX") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("${FLAG}" ${PREFIX}_FLAG_DETECTED) + + elseif (${LANG} STREQUAL "Fortran") + # CheckFortranCompilerFlag was introduced in CMake 3.x. To + # be compatible with older Cmake versions, we will check if + # this module is present before we use it. Otherwise we will + # define Fortran coverage support as not available. + include(CheckFortranCompilerFlag OPTIONAL + RESULT_VARIABLE INCLUDED) + if (INCLUDED) + check_fortran_compiler_flag("${FLAG}" + ASAN_FLAG_DETECTED) + elseif (NOT CMAKE_REQUIRED_QUIET) + message(STATUS + "Performing Test ${PREFIX}_FLAG_DETECTED") + message(STATUS "Performing Test ${PREFIX}_FLAG_DETECTED" + " - Failed (Check not supported)") + endif () + endif() + + if (${PREFIX}_FLAG_DETECTED) + set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING + "${NAME} flags for ${COMPILER} compiler.") + mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) + break() + endif () + endforeach () + endif () + endforeach () +endfunction () + + +# Helper to assign sanitizer flags for TARGET. +function (saitizer_add_flags TARGET NAME PREFIX) + # Get list of compilers used by target and check, if target can be checked + # by sanitizer. + sanitizer_target_compilers(${TARGET} TARGET_COMPILER) + list(LENGTH TARGET_COMPILER NUM_COMPILERS) + if (NUM_COMPILERS GREATER 1) + message(AUTHOR_WARNING "${NAME} disabled for target ${TARGET} because " + "it will be compiled by different compilers.") + return() + + elseif ((NUM_COMPILERS EQUAL 0) OR + (NOT DEFINED "${PREFIX}_${TARGET_COMPILER}_FLAGS")) + message(WARNING "${NAME} disabled for target ${TARGET} because there is" + " no sanitizer available for target sources.") + return() + endif() + + # Set compile- and link-flags for target. + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") +endfunction () diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bf0649a..6ffb38f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -31,6 +31,7 @@ function(add_testcase TESTNAME SOURCEFILES) # add a new executable add_executable(${TESTNAME} ${ARGV}) + add_sanitizers(${TESTNAME}) # add a testcase for executable add_test(${TESTNAME} ${TESTNAME}) @@ -41,10 +42,7 @@ endfunction(add_testcase) # # search for sanitizers # -find_package(ASan) -find_package(MSan) -find_package(TSan) -find_package(UBSan) +find_package(Sanitizers) From 03db9d9383a640b2d0b312a30ab0f093872d33cb Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Wed, 6 Apr 2016 19:15:25 +0200 Subject: [PATCH 13/25] Restructured all sanitizer modules. --- cmake/FindMSan.cmake | 71 ++++++--------------- cmake/FindSanitizers.cmake | 12 ++++ cmake/FindTSan.cmake | 73 ++++++---------------- cmake/FindUBSan.cmake | 122 +++++++------------------------------ 4 files changed, 71 insertions(+), 207 deletions(-) diff --git a/cmake/FindMSan.cmake b/cmake/FindMSan.cmake index 2a7a2a8..ac9a290 100644 --- a/cmake/FindMSan.cmake +++ b/cmake/FindMSan.cmake @@ -1,7 +1,8 @@ -# # The MIT License (MIT) # -# Copyright (c) 2013 Matthew Arsenault +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,66 +11,32 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# - -# This module tests if memory sanitizer is supported by the compiler, -# and creates a MSan build type (i.e. set CMAKE_BUILD_TYPE=MSan to use -# it). This sets the following variables: -# -# CMAKE_C_FLAGS_MSAN - Flags to use for C with msan -# CMAKE_CXX_FLAGS_MSAN - Flags to use for C++ with msan -# HAVE_MEMORY_SANITIZER - True or false if the MSan build type is available - -include(CheckCCompilerFlag) +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. -# Set -Werror to catch "argument unused during compilation" warnings -set(CMAKE_REQUIRED_FLAGS "-Werror -fmemory-sanitizer") # Also needs to be a link flag for test to pass -check_c_compiler_flag("-fmemory-sanitizer" HAVE_FLAG_MEMORY_SANITIZER) +option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off) -set(CMAKE_REQUIRED_FLAGS "-Werror -fsanitize=memory") # Also needs to be a link flag for test to pass -check_c_compiler_flag("-fsanitize=memory" HAVE_FLAG_SANITIZE_MEMORY) +set(FLAG_CANDIDATES + "-g -O1 -fsanitize=memory" +) -unset(CMAKE_REQUIRED_FLAGS) -if(HAVE_FLAG_SANITIZE_MEMORY) - # Clang 3.2+ use this version - set(MEMORY_SANITIZER_FLAG "-fsanitize=memory") -elseif(HAVE_FLAG_MEMORY_SANITIZER) - # Older deprecated flag for MSan - set(MEMORY_SANITIZER_FLAG "-fmemory-sanitizer") -endif() +include(sanitize-helpers) -if(NOT MEMORY_SANITIZER_FLAG) - return() -else(NOT MEMORY_SANITIZER_FLAG) - set(HAVE_MEMORY_SANITIZER TRUE) -endif() +sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" "MSan") -set(HAVE_MEMORY_SANITIZER TRUE) +function (add_sanitize_memory TARGET) + if (NOT SANITIZE_THREAD) + return() + endif () -set(CMAKE_C_FLAGS_MSAN "-O1 -g ${MEMORY_SANITIZER_FLAG} -fno-omit-frame-pointer -fno-optimize-sibling-calls" - CACHE STRING "Flags used by the C compiler during MSan builds." - FORCE) -set(CMAKE_CXX_FLAGS_MSAN "-O1 -g ${MEMORY_SANITIZER_FLAG} -fno-omit-frame-pointer -fno-optimize-sibling-calls" - CACHE STRING "Flags used by the C++ compiler during MSan builds." - FORCE) -set(CMAKE_EXE_LINKER_FLAGS_MSAN "${MEMORY_SANITIZER_FLAG}" - CACHE STRING "Flags used for linking binaries during MSan builds." - FORCE) -set(CMAKE_SHARED_LINKER_FLAGS_MSAN "${MEMORY_SANITIZER_FLAG}" - CACHE STRING "Flags used by the shared libraries linker during MSan builds." - FORCE) -mark_as_advanced(CMAKE_C_FLAGS_MSAN - CMAKE_CXX_FLAGS_MSAN - CMAKE_EXE_LINKER_FLAGS_MSAN - CMAKE_SHARED_LINKER_FLAGS_MSAN) + saitizer_add_flags(${TARGET} "MemorySanitizer" "MSan") +endfunction () diff --git a/cmake/FindSanitizers.cmake b/cmake/FindSanitizers.cmake index 272bb2d..e92221c 100644 --- a/cmake/FindSanitizers.cmake +++ b/cmake/FindSanitizers.cmake @@ -30,6 +30,12 @@ option(SANITIZE "Enable all available sanitizers for sanitized targets." OFF) if (SANITIZE) set(SANITIZE_ADDRESS ON CACHE BOOL "Enable AddressSanitizer for sanitized targets." FORCE) + set(SANITIZE_THREAD ON CACHE BOOL + "Enable ThreadSanitizer for sanitized targets." FORCE) + set(SANITIZE_MEMORY ON CACHE BOOL + "Enable MemorySanitizer for sanitized targets." FORCE) + set(SANITIZE_UNDEFINED ON CACHE BOOL + "Enable UndefinedBehaviorSanitizer for sanitized targets." FORCE) endif (SANITIZE) @@ -41,10 +47,16 @@ if (DEFINED Sanitizers_FIND_QUIETLY) endif () find_package(ASan ${FIND_QUIETLY_FLAG}) +find_package(TSan ${FIND_QUIETLY_FLAG}) +find_package(MSan ${FIND_QUIETLY_FLAG}) +find_package(UBSan ${FIND_QUIETLY_FLAG}) function(add_sanitizers TARGET) add_sanitize_address(${TARGET}) + add_sanitize_thread(${TARGET}) + add_sanitize_memory(${TARGET}) + add_sanitize_undefined(${TARGET}) endfunction(add_sanitizers) diff --git a/cmake/FindTSan.cmake b/cmake/FindTSan.cmake index 283ae7f..b933642 100644 --- a/cmake/FindTSan.cmake +++ b/cmake/FindTSan.cmake @@ -1,7 +1,8 @@ -# # The MIT License (MIT) # -# Copyright (c) 2013 Matthew Arsenault +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,68 +11,32 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# - -# This module tests if thread sanitizer is supported by the compiler, -# and creates a TSan build type (i.e. set CMAKE_BUILD_TYPE=TSan to use -# it). This sets the following variables: -# -# CMAKE_C_FLAGS_TSAN - Flags to use for C with tsan -# CMAKE_CXX_FLAGS_TSAN - Flags to use for C++ with tsan -# HAVE_THREAD_SANITIZER - True or false if the TSan build type is available - -include(CheckCCompilerFlag) - +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. -# Set -Werror to catch "argument unused during compilation" warnings -set(CMAKE_REQUIRED_FLAGS "-Werror -fthread-sanitizer") # Also needs to be a link flag for test to pass -check_c_compiler_flag("-fthread-sanitizer" HAVE_FLAG_THREAD_SANITIZER) +option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off) -set(CMAKE_REQUIRED_FLAGS "-Werror -fsanitize=thread") # Also needs to be a link flag for test to pass -check_c_compiler_flag("-fsanitize=thread" HAVE_FLAG_SANITIZE_THREAD) -unset(CMAKE_REQUIRED_FLAGS) +set(FLAG_CANDIDATES + "-g -O1 -fsanitize=thread" +) -# A special test that uses threads seems to not be necessary. tsan -# symbols are used even in just int main() { return 0; } +include(sanitize-helpers) -if(HAVE_FLAG_SANITIZE_THREAD) - # Clang 3.2+ use this version - set(THREAD_SANITIZER_FLAG "-fsanitize=thread") -elseif(HAVE_FLAG_THREAD_SANITIZER) - # Older deprecated flag for TSan - set(THREAD_SANITIZER_FLAG_FLAG "-fthread-sanitizer") -else() - set(HAVE_THREAD_SANITIZER FALSE) - return() -endif() +sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" "TSan") -set(HAVE_THREAD_SANITIZER TRUE) +function (add_sanitize_thread TARGET) + if (NOT SANITIZE_THREAD) + return() + endif () -set(CMAKE_C_FLAGS_TSAN "-O1 -g ${THREAD_SANITIZER_FLAG} -fno-omit-frame-pointer" - CACHE STRING "Flags used by the C compiler during TSan builds." - FORCE - ) -set(CMAKE_CXX_FLAGS_TSAN "-O1 -g ${THREAD_SANITIZER_FLAG} -fno-omit-frame-pointer" - CACHE STRING "Flags used by the C++ compiler during TSan builds." - FORCE) -set(CMAKE_EXE_LINKER_FLAGS_TSAN "${THREAD_SANITIZER_FLAG}" - CACHE STRING "Flags used for linking binaries during TSan builds." - FORCE) -set(CMAKE_SHARED_LINKER_FLAGS_TSAN "${THREAD_SANITIZER_FLAG}" - CACHE STRING "Flags used by the shared libraries linker during TSan builds." - FORCE) -mark_as_advanced(CMAKE_C_FLAGS_TSAN - CMAKE_CXX_FLAGS_TSAN - CMAKE_EXE_LINKER_FLAGS_TSAN - CMAKE_SHARED_LINKER_FLAGS_TSAN) + saitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan") +endfunction () diff --git a/cmake/FindUBSan.cmake b/cmake/FindUBSan.cmake index a31c68f..f520f36 100644 --- a/cmake/FindUBSan.cmake +++ b/cmake/FindUBSan.cmake @@ -1,7 +1,8 @@ -# # The MIT License (MIT) # -# Copyright (c) 2013 Matthew Arsenault +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,115 +11,34 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# - -# Check if the compiler supports a working ubsan. Provides a UBSan -# build type, which is essentially Debug + ubsan. The flag can be used -# independently to compose it with other build types or sanitizers. -# -# Sets these variables: -# -# HAVE_UNDEFINED_BEHAVIOR_SANITIZER - True or false if the UBSan is available -# UNDEFINED_BEHAVIOR_SANITIZER_FLAG - Flag to add to compiler to use ubsan if supported -# -# CMAKE_C_FLAGS_UBSAN - Flags to use for C with ubsan -# CMAKE_CXX_FLAGS_UBSAN - Flags to use for C++ with ubsan -## -# - - -include(CheckCXXCompilerFlag) -include(CheckCXXSourceRuns) - -# Set -Werror to catch "argument unused during compilation" warnings -set(CMAKE_REQUIRED_FLAGS "-Werror") -check_cxx_compiler_flag("-fsanitize=undefined" HAVE_FLAG_SANITIZE_UNDEFINED) -check_cxx_compiler_flag("-fcatch-undefined-behavior" HAVE_FLAG_CATCH_UNDEFINED_BEHAVIOR) -if(HAVE_FLAG_SANITIZE_UNDEFINED) - set(UNDEFINED_BEHAVIOR_SANITIZER_FLAG "-fsanitize=undefined") -elseif(HAVE_FLAG_CATCH_UNDEFINED_BEHAVIOR) - set(UNDEFINED_BEHAVIOR_SANITIZER_FLAG "-fcatch-undefined-behavior") -else() - set(HAVE_UNDEFINED_BEHAVIOR_SANITIZER FALSE) - return() -endif() -unset(CMAKE_REQUIRED_FLAGS) - - -# It isn't sufficient to check if the flag works since the -# check_c_compiler_flag test doesn't link the output. -# -# Most clang packages ship broken packages (the autotools build -# produces a broken package which doesn't include the ubsan -# compiler-rt, so check that it actually works with a linked program -# before trying to use it -set(CMAKE_REQUIRED_FLAGS "${UNDEFINED_BEHAVIOR_SANITIZER_FLAG} -Wno-error=delete-non-virtual-dtor") - -check_cxx_source_runs( -" -#include -#include -#include - -class BarB -{ - public: - float y; - /* Include something that uses a virtual function. The symbols - that are broken on current OS X libc++ involve this */ - virtual int arst(int o) - { - return 4 + o; - } - }; +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. -/* Just include something that ubsan will need to check */ -int main(int argc, const char* argv[]) -{ - BarB* b = new BarB(); - if (argc > 1) - { - fputs(argv[atoi(argv[1])], stdout); - std::cout << b->arst(atoi(argv[1])); - } +option(SANITIZE_UNDEFINED + "Enable UndefinedBehaviorSanitizer for sanitized targets." Off) - delete b; - return 0; -} -" - HAVE_UNDEFINED_BEHAVIOR_SANITIZER) -unset(CMAKE_REQUIRED_FLAGS) +set(FLAG_CANDIDATES + "-g -O1 -fsanitize=undefined" +) -if(NOT HAVE_UNDEFINED_BEHAVIOR_SANITIZER) - return() -endif() +include(sanitize-helpers) -set(CMAKE_C_FLAGS_UBSAN "-O0 -g ${UNDEFINED_BEHAVIOR_SANITIZER_FLAG} -fno-omit-frame-pointer" - CACHE STRING "Flags used by the C compiler during UBSan builds." - FORCE) -set(CMAKE_CXX_FLAGS_UBSAN "-O0 -g ${UNDEFINED_BEHAVIOR_SANITIZER_FLAG} -fno-omit-frame-pointer" - CACHE STRING "Flags used by the C++ compiler during UBSan builds." - FORCE) -set(CMAKE_EXE_LINKER_FLAGS_UBSAN "${UNDEFINED_BEHAVIOR_SANITIZER_FLAG}" - CACHE STRING "Flags used for linking binaries during UBSan builds." - FORCE) -set(CMAKE_SHARED_LINKER_FLAGS_UBSAN "${UNDEFINED_BEHAVIOR_SANITIZER_FLAG}" - CACHE STRING "Flags used by the shared libraries linker during UBSan builds." - FORCE) -mark_as_advanced(CMAKE_C_FLAGS_UBSAN - CMAKE_CXX_FLAGS_UBSAN - CMAKE_EXE_LINKER_FLAGS_UBSAN - CMAKE_SHARED_LINKER_FLAGS_UBSAN) +sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "UndefinedBehaviorSanitizer" + "UBSan") +function (add_sanitize_undefined TARGET) + if (NOT SANITIZE_UNDEFINED) + return() + endif () + saitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan") +endfunction () From 4d96db375f74e9da97428007e9de12ee150f4808 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Wed, 6 Apr 2016 19:23:24 +0200 Subject: [PATCH 14/25] Check if ThreadSanitizer is used on x86_64 Linux. According to the ThreadSanitizer documentation, it is only compatible with 64bit Linux at the moment. --- cmake/FindTSan.cmake | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/cmake/FindTSan.cmake b/cmake/FindTSan.cmake index b933642..c9b8a9c 100644 --- a/cmake/FindTSan.cmake +++ b/cmake/FindTSan.cmake @@ -31,7 +31,22 @@ set(FLAG_CANDIDATES include(sanitize-helpers) -sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" "TSan") +if (SANITIZE_THREAD) + if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " + "ThreadSanitizer is supported for Linux systems only.") + set(SANITIZE_THREAD Off CACHE BOOL + "Enable ThreadSanitizer for sanitized targets." FORCE) + elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) + message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " + "ThreadSanitizer is supported for 64bit systems only.") + set(SANITIZE_THREAD Off CACHE BOOL + "Enable ThreadSanitizer for sanitized targets." FORCE) + else () + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" + "TSan") + endif () +endif () function (add_sanitize_thread TARGET) if (NOT SANITIZE_THREAD) From de1a13418344989e9dc25fd3b49e61627e1389e8 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Wed, 6 Apr 2016 19:32:45 +0200 Subject: [PATCH 15/25] Check if MemorySanitizer is used on x86_64 Linux. According to the MemorySanitizer documentation, it is only compatible with 64bit Linux at the moment. --- cmake/FindMSan.cmake | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/cmake/FindMSan.cmake b/cmake/FindMSan.cmake index ac9a290..bdc2c2c 100644 --- a/cmake/FindMSan.cmake +++ b/cmake/FindMSan.cmake @@ -31,10 +31,25 @@ set(FLAG_CANDIDATES include(sanitize-helpers) -sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" "MSan") +if (SANITIZE_MEMORY) + if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(WARNING "MemorySanitizer disabled for target ${TARGET} because " + "MemorySanitizer is supported for Linux systems only.") + set(SANITIZE_MEMORY Off CACHE BOOL + "Enable MemorySanitizer for sanitized targets." FORCE) + elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) + message(WARNING "MemorySanitizer disabled for target ${TARGET} because " + "MemorySanitizer is supported for 64bit systems only.") + set(SANITIZE_MEMORY Off CACHE BOOL + "Enable MemorySanitizer for sanitized targets." FORCE) + else () + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" + "MSan") + endif () +endif () function (add_sanitize_memory TARGET) - if (NOT SANITIZE_THREAD) + if (NOT SANITIZE_MEMORY) return() endif () From 6975d64d6b0b5bbce2df1de4b63282cbf1a2aae3 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Wed, 6 Apr 2016 19:39:47 +0200 Subject: [PATCH 16/25] Flags will be searched only once per compiler. If a compiler does not support sanitizing, it will not be checked a second time. --- cmake/sanitize-helpers.cmake | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cmake/sanitize-helpers.cmake b/cmake/sanitize-helpers.cmake index 63586f1..af0b456 100644 --- a/cmake/sanitize-helpers.cmake +++ b/cmake/sanitize-helpers.cmake @@ -76,7 +76,7 @@ function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) # So instead of searching flags foreach language, search flags foreach # compiler used. set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) - if (NOT ${PREFIX}_${COMPILER}_FLAGS) + if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS) foreach (FLAG ${FLAG_CANDIDATES}) if(NOT CMAKE_REQUIRED_QUIET) message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") @@ -118,6 +118,12 @@ function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) break() endif () endforeach () + + if (NOT ${PREFIX}_FLAG_DETECTED) + set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING + "${NAME} flags for ${COMPILER} compiler.") + mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) + endif () endif () endforeach () endfunction () @@ -135,7 +141,7 @@ function (saitizer_add_flags TARGET NAME PREFIX) return() elseif ((NUM_COMPILERS EQUAL 0) OR - (NOT DEFINED "${PREFIX}_${TARGET_COMPILER}_FLAGS")) + ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "")) message(WARNING "${NAME} disabled for target ${TARGET} because there is" " no sanitizer available for target sources.") return() From f566e2b9ecada218caf99ad6639dae801236bce4 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Tue, 12 Apr 2016 21:04:21 +0200 Subject: [PATCH 17/25] Added checks to avoid bad sanitizer combinations. Some sanitizers are not compatible with other sanitizers. Added some checks to avoid these combinations at configuration time. --- cmake/FindASan.cmake | 11 ++++++++++- cmake/FindSanitizers.cmake | 19 ------------------- cmake/FindTSan.cmake | 7 +++++++ cmake/FindUBSan.cmake | 6 ++++-- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index 5c95a0b..fa07674 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -33,9 +33,18 @@ set(FLAG_CANDIDATES ) +if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY)) + message(FATAL_ERROR "AddressSanitizer is not compatible with " + "ThreadSanitizer or MemorySanitizer.") +endif () + + include(sanitize-helpers) -sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" "ASan") +if (SANITIZE_ADDRESS) + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" + "ASan") +endif () function (add_sanitize_address TARGET) if (NOT SANITIZE_ADDRESS) diff --git a/cmake/FindSanitizers.cmake b/cmake/FindSanitizers.cmake index e92221c..f6db13e 100644 --- a/cmake/FindSanitizers.cmake +++ b/cmake/FindSanitizers.cmake @@ -22,25 +22,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. - -# The following options will enable the desired sanitizers. -option(SANITIZE "Enable all available sanitizers for sanitized targets." OFF) - -# If option SANITIZE is enabled, enable all available sanitizers. -if (SANITIZE) - set(SANITIZE_ADDRESS ON CACHE BOOL - "Enable AddressSanitizer for sanitized targets." FORCE) - set(SANITIZE_THREAD ON CACHE BOOL - "Enable ThreadSanitizer for sanitized targets." FORCE) - set(SANITIZE_MEMORY ON CACHE BOOL - "Enable MemorySanitizer for sanitized targets." FORCE) - set(SANITIZE_UNDEFINED ON CACHE BOOL - "Enable UndefinedBehaviorSanitizer for sanitized targets." FORCE) -endif (SANITIZE) - - - - set(FIND_QUIETLY_FLAG "") if (DEFINED Sanitizers_FIND_QUIETLY) set(FIND_QUIETLY_FLAG "QUIET") diff --git a/cmake/FindTSan.cmake b/cmake/FindTSan.cmake index c9b8a9c..81e07b0 100644 --- a/cmake/FindTSan.cmake +++ b/cmake/FindTSan.cmake @@ -29,6 +29,13 @@ set(FLAG_CANDIDATES ) +# ThreadSanitizer is not compatible with MemorySanitizer. +if (SANITIZE_THREAD AND SANITIZE_MEMORY) + message(FATAL_ERROR "ThreadSanitizer is not compatible with " + "MemorySanitizer.") +endif () + + include(sanitize-helpers) if (SANITIZE_THREAD) diff --git a/cmake/FindUBSan.cmake b/cmake/FindUBSan.cmake index f520f36..5ae0b54 100644 --- a/cmake/FindUBSan.cmake +++ b/cmake/FindUBSan.cmake @@ -32,8 +32,10 @@ set(FLAG_CANDIDATES include(sanitize-helpers) -sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "UndefinedBehaviorSanitizer" - "UBSan") +if (SANITIZE_UNDEFINED) + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" + "UndefinedBehaviorSanitizer" "UBSan") +endif () function (add_sanitize_undefined TARGET) if (NOT SANITIZE_UNDEFINED) From 1cefaef5071b142d909c9f9880f2b97487d7f2ee Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Fri, 15 Apr 2016 14:25:58 +0200 Subject: [PATCH 18/25] Added function to set sanitizer blacklist. --- cmake/FindSanitizers.cmake | 10 ++++++++++ cmake/sanitize-helpers.cmake | 2 ++ 2 files changed, 12 insertions(+) diff --git a/cmake/FindSanitizers.cmake b/cmake/FindSanitizers.cmake index f6db13e..de3ea17 100644 --- a/cmake/FindSanitizers.cmake +++ b/cmake/FindSanitizers.cmake @@ -35,6 +35,16 @@ find_package(UBSan ${FIND_QUIETLY_FLAG}) +function(sanitizer_add_blacklist_file FILE) + if(NOT IS_ABSOLUTE ${FILE}) + set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}") + endif() + get_filename_component(FILE "${FILE}" REALPATH) + + sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}" + "SanitizerBlacklist" "SanBlist") +endfunction() + function(add_sanitizers TARGET) add_sanitize_address(${TARGET}) add_sanitize_thread(${TARGET}) diff --git a/cmake/sanitize-helpers.cmake b/cmake/sanitize-helpers.cmake index af0b456..b61281a 100644 --- a/cmake/sanitize-helpers.cmake +++ b/cmake/sanitize-helpers.cmake @@ -150,6 +150,8 @@ function (saitizer_add_flags TARGET NAME PREFIX) # Set compile- and link-flags for target. set_property(TARGET ${TARGET} APPEND_STRING PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}") set_property(TARGET ${TARGET} APPEND_STRING PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") endfunction () From 1a68d6aefe4d64c1255622d7f415406721bbe411 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Fri, 15 Apr 2016 14:31:51 +0200 Subject: [PATCH 19/25] Little bugfixes. --- cmake/sanitize-helpers.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/sanitize-helpers.cmake b/cmake/sanitize-helpers.cmake index b61281a..1afe1f2 100644 --- a/cmake/sanitize-helpers.cmake +++ b/cmake/sanitize-helpers.cmake @@ -102,7 +102,7 @@ function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) RESULT_VARIABLE INCLUDED) if (INCLUDED) check_fortran_compiler_flag("${FLAG}" - ASAN_FLAG_DETECTED) + ${PREFIX}_FLAG_DETECTED) elseif (NOT CMAKE_REQUIRED_QUIET) message(STATUS "Performing Test ${PREFIX}_FLAG_DETECTED") @@ -136,8 +136,8 @@ function (saitizer_add_flags TARGET NAME PREFIX) sanitizer_target_compilers(${TARGET} TARGET_COMPILER) list(LENGTH TARGET_COMPILER NUM_COMPILERS) if (NUM_COMPILERS GREATER 1) - message(AUTHOR_WARNING "${NAME} disabled for target ${TARGET} because " - "it will be compiled by different compilers.") + message(WARNING "${NAME} disabled for target ${TARGET} because it will " + "be compiled by different compilers.") return() elseif ((NUM_COMPILERS EQUAL 0) OR From 6d56fe50e7731115b949bb70fb186d8dec781f44 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Tue, 19 Apr 2016 18:37:43 +0200 Subject: [PATCH 20/25] Removed optimization flags. Due optimizing the code is optional for using the sanitizers, the user should set an optimisation level by his choice on his own. --- cmake/FindASan.cmake | 4 ++-- cmake/FindMSan.cmake | 2 +- cmake/FindTSan.cmake | 2 +- cmake/FindUBSan.cmake | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index fa07674..c9c55e6 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -26,10 +26,10 @@ option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) set(FLAG_CANDIDATES # Clang 3.2+ use this version - "-g -O1 -fsanitize=address" + "-g -fsanitize=address" # Older deprecated flag for ASan - "-g -O1 -faddress-sanitizer" + "-g -faddress-sanitizer" ) diff --git a/cmake/FindMSan.cmake b/cmake/FindMSan.cmake index bdc2c2c..3b0a4ad 100644 --- a/cmake/FindMSan.cmake +++ b/cmake/FindMSan.cmake @@ -25,7 +25,7 @@ option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off) set(FLAG_CANDIDATES - "-g -O1 -fsanitize=memory" + "-g -fsanitize=memory" ) diff --git a/cmake/FindTSan.cmake b/cmake/FindTSan.cmake index 81e07b0..0e80f29 100644 --- a/cmake/FindTSan.cmake +++ b/cmake/FindTSan.cmake @@ -25,7 +25,7 @@ option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off) set(FLAG_CANDIDATES - "-g -O1 -fsanitize=thread" + "-g -fsanitize=thread" ) diff --git a/cmake/FindUBSan.cmake b/cmake/FindUBSan.cmake index 5ae0b54..6948674 100644 --- a/cmake/FindUBSan.cmake +++ b/cmake/FindUBSan.cmake @@ -26,7 +26,7 @@ option(SANITIZE_UNDEFINED "Enable UndefinedBehaviorSanitizer for sanitized targets." Off) set(FLAG_CANDIDATES - "-g -O1 -fsanitize=undefined" + "-g -fsanitize=undefined" ) From 179e77d612cf30d7e88bd208b0fac4e8d1f02cc4 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Thu, 12 May 2016 19:59:28 +0200 Subject: [PATCH 21/25] Added option for static linking gcc sanitizers. If you like to preload a sanitized library in front of an application, it will fail, because it is not the first in the library list anymore. This behaviour is gcc specific - clang will static link the sanitizers instead. To get the same for gcc, the new SANITIZE_LINK_STATIC flag will link the sanitizers static in gcc environments. Note: The preloaded executable must be sanitized, too! Otherwise ASan will print an error message! --- cmake/FindASan.cmake | 3 +- cmake/FindSanitizers.cmake | 7 ++++ cmake/sanitize-helpers.cmake | 66 ++++++++++++++++++++++-------------- 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index c9c55e6..7605df1 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -25,7 +25,8 @@ option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) set(FLAG_CANDIDATES - # Clang 3.2+ use this version + # Clang 3.2+ use this version. The no-omit-frame-pointer option is optional. + "-g -sanitize=address -fno-omit-frame-pointer" "-g -fsanitize=address" # Older deprecated flag for ASan diff --git a/cmake/FindSanitizers.cmake b/cmake/FindSanitizers.cmake index de3ea17..2be9d4a 100644 --- a/cmake/FindSanitizers.cmake +++ b/cmake/FindSanitizers.cmake @@ -22,6 +22,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +# If any of the used compiler is a GNU compiler, add a second option to static +# link against the sanitizers. +option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off) + + + + set(FIND_QUIETLY_FLAG "") if (DEFINED Sanitizers_FIND_QUIETLY) set(FIND_QUIETLY_FLAG "QUIET") diff --git a/cmake/sanitize-helpers.cmake b/cmake/sanitize-helpers.cmake index 1afe1f2..7ea90cc 100644 --- a/cmake/sanitize-helpers.cmake +++ b/cmake/sanitize-helpers.cmake @@ -66,6 +66,33 @@ function (sanitizer_target_compilers TARGET RETURN_VAR) endfunction () +# Helper function to check compiler flags for language compiler. +function (sanitizer_check_compiler_flag FLAG LANG VARIABLE) + if (${LANG} STREQUAL "C") + include(CheckCCompilerFlag) + check_c_compiler_flag("${FLAG}" ${VARIABLE}) + + elseif (${LANG} STREQUAL "CXX") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("${FLAG}" ${VARIABLE}) + + elseif (${LANG} STREQUAL "Fortran") + # CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible + # with older Cmake versions, we will check if this module is present + # before we use it. Otherwise we will define Fortran coverage support as + # not available. + include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED) + if (INCLUDED) + check_fortran_compiler_flag("${FLAG}" ${VARIABLE}) + elseif (NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VARIABLE}") + message(STATUS "Performing Test ${VARIABLE}" + " - Failed (Check not supported)") + endif () + endif() +endfunction () + + # Helper function to test compiler flags. function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) @@ -84,34 +111,23 @@ function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) set(CMAKE_REQUIRED_FLAGS "${FLAG}") unset(${PREFIX}_FLAG_DETECTED CACHE) + sanitizer_check_compiler_flag("${FLAG}" ${LANG} + ${PREFIX}_FLAG_DETECTED) - if (${LANG} STREQUAL "C") - include(CheckCCompilerFlag) - check_c_compiler_flag("${FLAG}" ${PREFIX}_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "CXX") - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag("${FLAG}" ${PREFIX}_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "Fortran") - # CheckFortranCompilerFlag was introduced in CMake 3.x. To - # be compatible with older Cmake versions, we will check if - # this module is present before we use it. Otherwise we will - # define Fortran coverage support as not available. - include(CheckFortranCompilerFlag OPTIONAL - RESULT_VARIABLE INCLUDED) - if (INCLUDED) - check_fortran_compiler_flag("${FLAG}" - ${PREFIX}_FLAG_DETECTED) - elseif (NOT CMAKE_REQUIRED_QUIET) - message(STATUS - "Performing Test ${PREFIX}_FLAG_DETECTED") - message(STATUS "Performing Test ${PREFIX}_FLAG_DETECTED" - " - Failed (Check not supported)") + if (${PREFIX}_FLAG_DETECTED) + # If compiler is a GNU compiler, search for static flag, if + # SANITIZE_LINK_STATIC is enabled. + if (SANITIZE_LINK_STATIC) + string(TOLOWER ${PREFIX} PREFIX_lower) + sanitizer_check_compiler_flag( + "-static-lib${PREFIX_lower}" ${LANG} + ${PREFIX}_STATIC_FLAG_DETECTED) + + if (${PREFIX}_STATIC_FLAG_DETECTED) + set(FLAG "-static-lib${PREFIX_lower} ${FLAG}") + endif () endif () - endif() - if (${PREFIX}_FLAG_DETECTED) set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING "${NAME} flags for ${COMPILER} compiler.") mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) From 8d8c0f7a4e26f8d9cdb23850f9f86f6c85a16d91 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Fri, 13 May 2016 14:59:11 +0200 Subject: [PATCH 22/25] Bugfixes. --- cmake/FindASan.cmake | 2 +- cmake/sanitize-helpers.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index 7605df1..734a960 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -26,7 +26,7 @@ option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) set(FLAG_CANDIDATES # Clang 3.2+ use this version. The no-omit-frame-pointer option is optional. - "-g -sanitize=address -fno-omit-frame-pointer" + "-g -fsanitize=address -fno-omit-frame-pointer" "-g -fsanitize=address" # Older deprecated flag for ASan diff --git a/cmake/sanitize-helpers.cmake b/cmake/sanitize-helpers.cmake index 7ea90cc..88da66e 100644 --- a/cmake/sanitize-helpers.cmake +++ b/cmake/sanitize-helpers.cmake @@ -117,7 +117,7 @@ function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) if (${PREFIX}_FLAG_DETECTED) # If compiler is a GNU compiler, search for static flag, if # SANITIZE_LINK_STATIC is enabled. - if (SANITIZE_LINK_STATIC) + if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU")) string(TOLOWER ${PREFIX} PREFIX_lower) sanitizer_check_compiler_flag( "-static-lib${PREFIX_lower}" ${LANG} From 8fe13da3a5e0bbacbfb238b48c2ced5337b1b5f6 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Fri, 13 May 2016 14:59:30 +0200 Subject: [PATCH 23/25] Added asan-wrapper. In some special cases static linking the sanitizers won't work, and you have to preload libasan. To find the right libasan depending on the application to be executed, a wrapper script was added. The location of the wrapper script can be accessed via ASan_WRAPPER variable. --- cmake/FindASan.cmake | 3 +++ cmake/asan-wrapper | 55 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100755 cmake/asan-wrapper diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index 734a960..fcebb43 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -45,6 +45,9 @@ include(sanitize-helpers) if (SANITIZE_ADDRESS) sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" "ASan") + + find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH}) + mark_as_advanced(ASan_WRAPPER) endif () function (add_sanitize_address TARGET) diff --git a/cmake/asan-wrapper b/cmake/asan-wrapper new file mode 100755 index 0000000..5d54103 --- /dev/null +++ b/cmake/asan-wrapper @@ -0,0 +1,55 @@ +#!/bin/sh + +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# This script is a wrapper for AddressSanitizer. In some special cases you need +# to preload AddressSanitizer to avoid error messages - e.g. if you're +# preloading another library to your application. At the moment this script will +# only do something, if we're running on a Linux platform. OSX might not be +# affected. + + +# Exit immediately, if platform is not Linux. +if [ "$(uname)" != "Linux" ] +then + exec $@ +fi + + +# Get the used libasan of the application ($1). If a libasan was found, it will +# be prepended to LD_PRELOAD. +libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1) +if [ -n "$libasan" ] +then + if [ -n "$LD_PRELOAD" ] + then + export LD_PRELOAD="$libasan:$LD_PRELOAD" + else + export LD_PRELOAD="$libasan" + fi +fi + +# Execute the application. +exec $@ From 5bfe8a8e511296fb0743ecb0420642e9ef5e2285 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Tue, 14 Jun 2016 01:12:11 +0200 Subject: [PATCH 24/25] Support multiple targets in add_sanitizers. --- cmake/FindSanitizers.cmake | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cmake/FindSanitizers.cmake b/cmake/FindSanitizers.cmake index 2be9d4a..627ee24 100644 --- a/cmake/FindSanitizers.cmake +++ b/cmake/FindSanitizers.cmake @@ -52,9 +52,11 @@ function(sanitizer_add_blacklist_file FILE) "SanitizerBlacklist" "SanBlist") endfunction() -function(add_sanitizers TARGET) - add_sanitize_address(${TARGET}) - add_sanitize_thread(${TARGET}) - add_sanitize_memory(${TARGET}) - add_sanitize_undefined(${TARGET}) +function(add_sanitizers ...) + foreach (TARGET ${ARGV}) + add_sanitize_address(${TARGET}) + add_sanitize_thread(${TARGET}) + add_sanitize_memory(${TARGET}) + add_sanitize_undefined(${TARGET}) + endforeach () endfunction(add_sanitizers) From 4f5410511ac900ce89b5b727f38ab4fcec9eb1e7 Mon Sep 17 00:00:00 2001 From: Alexander Haase Date: Tue, 14 Jun 2016 01:39:30 +0200 Subject: [PATCH 25/25] Updated README file. --- README.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dee9ef6..4c8f580 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,73 @@ -sanitizers-cmake -================ +# CMake-sanitizers -CMake modules to help use sanitizers + [![](https://img.shields.io/github/issues-raw/RWTH-ELP/CMake-sanitizers.svg?style=flat-square)](https://github.com/RWTH-ELP/CMake-sanitizers/issues) +[![MIT](http://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE) + +CMake module to enable sanitizers for binary targets. + + +## Include into your project + +To use [FindSanitizers.cmake](cmake/FindSanitizers.cmake), simply add this repository as git submodule into your own repository +```Shell +mkdir externals +git submodule add git://github.com/RWTH-ELP/CMake-sanitizers.git externals/CMake-sanitizers +``` +and adding ```externals/cmake-sanitizers/cmake``` to your ```CMAKE_MODULE_PATH``` +```CMake +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-sanitizers/cmake" ${CMAKE_MODULE_PATH}) +``` + +If you don't use git or dislike submodules you can copy the files in [cmake directory](cmake) into your repository. *Be careful and keep updates in mind!* + +Now you can simply run ```find_package``` in your CMake files: +```CMake +find_package(Sanitizers) +``` + + +## Usage + +You can enable the sanitizers with ``SANITIZE_ADDRESS``, ``SANITIZE_MEMORY``, ``SANITIZE_THREAD`` or ``SANITIZE_UNDEFINED`` options in your CMake configuration. You can do this by passing e.g. ``-DSANITIZE_ADDRESS=On`` on your command line or with your graphical interface. + +If sanitizers are supported by your compiler, the specified targets will be build with sanitizer support. If your compiler has no sanitizing capabilities (I asume intel compiler doesn't) you'll get a warning but CMake will continue processing and sanitizing will simply just be ignored. + +#### Compiler issues + +Different compilers may be using different implementations for sanitizers. If you'll try to sanitize targets with C and Fortran code but don't use gcc & gfortran but clang & gfortran, this will cause linking problems. To avoid this, such problems will be detected and sanitizing will be disabled for these targets. + +Even C only targets may cause problems in certain situations. Some problems have been seen with AddressSanitizer for preloading or dynamic linking. In such cases you may try the ``SANITIZE_LINK_STATIC`` to link sanitizers for gcc static. + + + +## Build targets with sanitizer support + +To enable sanitizer support you simply have to add ``add_sanitizers()`` after defining your target. To provide a sanitizer blacklist file you can use the ``add_sanitizer_blacklist()`` function: +```CMake +find_package(Sanitizers) + +add_sanitizer_blacklist("blacklist.txt") + +add_executable(some_exe foo.c bar.c) +add_sanitizers(some_exe) + +add_library(some_lib foo.c bar.c) +add_sanitizers(some_lib) +``` + +## Run your application + +The sanitizers check your program, while it's running. In some situations (e.g. LD_PRELOAD your target) it might be required to preload the used AddressSanitizer library first. In this case you may use the ``asan-wrapper`` script defined in ``ASan_WRAPPER`` variable to execute your application with ``${ASan_WRAPPER} myexe arg1 ...``. + + +## Contribute + +Anyone is welcome to contribute. Simply fork this repository, make your changes **in an own branch** and create a pull-request for your change. Please do only one change per pull-request. + +You found a bug? Please fill out an [issue](https://github.com/RWTH-ELP/CMake-sanitizers/issues) and include any data to reproduce the bug. + + +#### Contributors + +* [Matt Arsenault](https://github.com/arsenm) +* [Alexander Haase](https://github.com/alehaa)