From 42eac1f7afbe6ae2442feac2f22fa66c44186b77 Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Sat, 5 Oct 2013 22:16:39 -0400 Subject: [PATCH] Add modules for asan, msan, tsan and ubsan --- FindASan.cmake | 63 ++++++++++++++++++ FindMSan.cmake | 72 +++++++++++++++++++++ FindTSan.cmake | 77 ++++++++++++++++++++++ FindUBSan.cmake | 124 ++++++++++++++++++++++++++++++++++++ test_project/CMakeLists.txt | 12 ++++ test_project/test.cpp | 48 ++++++++++++++ 6 files changed, 396 insertions(+) create mode 100644 FindASan.cmake create mode 100644 FindMSan.cmake create mode 100644 FindTSan.cmake create mode 100644 FindUBSan.cmake create mode 100644 test_project/CMakeLists.txt create mode 100644 test_project/test.cpp diff --git a/FindASan.cmake b/FindASan.cmake new file mode 100644 index 0000000..99f4af2 --- /dev/null +++ b/FindASan.cmake @@ -0,0 +1,63 @@ +# +# The MIT License (MIT) +# +# Copyright (c) 2013 Matthew Arsenault +# +# 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. +# + +include(CheckCCompilerFlag) + +# 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) + +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) + +unset(CMAKE_REQUIRED_FLAGS) + +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() +endif() + +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) diff --git a/FindMSan.cmake b/FindMSan.cmake new file mode 100644 index 0000000..c8cd71d --- /dev/null +++ b/FindMSan.cmake @@ -0,0 +1,72 @@ +# +# The MIT License (MIT) +# +# Copyright (c) 2013 Matthew Arsenault +# +# 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 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 tsan +# 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) + +# 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) + +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) + +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() + +if(NOT MEMORY_SANITIZER_FLAG) + 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) diff --git a/FindTSan.cmake b/FindTSan.cmake new file mode 100644 index 0000000..283ae7f --- /dev/null +++ b/FindTSan.cmake @@ -0,0 +1,77 @@ +# +# The MIT License (MIT) +# +# Copyright (c) 2013 Matthew Arsenault +# +# 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 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) + + +# 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) + +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) + +# A special test that uses threads seems to not be necessary. tsan +# symbols are used even in just int main() { return 0; } + + +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() + +set(HAVE_THREAD_SANITIZER TRUE) + +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) diff --git a/FindUBSan.cmake b/FindUBSan.cmake new file mode 100644 index 0000000..453d9cf --- /dev/null +++ b/FindUBSan.cmake @@ -0,0 +1,124 @@ +# +# The MIT License (MIT) +# +# Copyright (c) 2013 Matthew Arsenault +# +# 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. +# + +# 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}") + +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; + } + }; + +/* 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])); + } + + delete b; + return 0; +} +" + HAVE_UNDEFINED_BEHAVIOR_SANITIZER) +unset(CMAKE_REQUIRED_FLAGS) + +if(NOT HAVE_UNDEFINED_BEHAVIOR_SANITIZER) + return() +endif() + + +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) + + diff --git a/test_project/CMakeLists.txt b/test_project/CMakeLists.txt new file mode 100644 index 0000000..835a055 --- /dev/null +++ b/test_project/CMakeLists.txt @@ -0,0 +1,12 @@ + +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 new file mode 100644 index 0000000..b79e7d1 --- /dev/null +++ b/test_project/test.cpp @@ -0,0 +1,48 @@ + +#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; +}