-
Notifications
You must be signed in to change notification settings - Fork 156
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support for building with sanitizers (#149)
* Add support for building with sanitizers * CI Enable building with ASAN+UBSAN sanitizers in Debug builds * Add directions to README * Better CI reporting and matrix for sanitizer runs
- Loading branch information
Showing
4 changed files
with
195 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
# Enable C/C++ sanitizers in your CMake project by `include`ing this file | ||
# somewhere in your CMakeLists.txt. | ||
# | ||
# Example: | ||
# | ||
# include("cmake/sanitizers.cmake") | ||
# process_sanitizer(MY_PROJECT) | ||
# | ||
# Example CMake configuration usage for `MY_PROJECT`: | ||
# | ||
# cmake -Bbuild-asan-ubsan -H. -DMY_PROJECT_USE_SANITIZER=Address,Undefined | ||
# | ||
# where "MY_PROJECT" can by any arbitrary text that will be prepended to some | ||
# expected CMake variables--see below. | ||
# | ||
# This file expects the following variables to be set during CMake | ||
# configuration, where the prefix is set by the caller of `process_sanitizer`: | ||
# | ||
# - *_USE_SANITIZER | ||
# - A string value that is one of the following: | ||
# - Address | ||
# - HWAddress | ||
# - Memory | ||
# - MemoryWithOrigins | ||
# - Undefined | ||
# - Thread | ||
# - DataFlow | ||
# - Leak | ||
# - Address,Undefined | ||
# | ||
# - *_OPTIMIZE_SANITIZED_BUILDS | ||
# - A boolean value to set whether a higher optimization is used in debug | ||
# builds | ||
# | ||
# - *_BLACKLIST_FILE | ||
# - A filepath to a sanitizer blacklist file. | ||
|
||
|
||
function(append value) | ||
foreach(variable ${ARGN}) | ||
set(${variable} | ||
"${${variable}} ${value}" | ||
PARENT_SCOPE) | ||
endforeach(variable) | ||
endfunction() | ||
|
||
|
||
macro(append_common_sanitizer_flags prefix) | ||
if (NOT MSVC) | ||
# Append -fno-omit-frame-pointer and turn on debug info to get better | ||
# stack traces. | ||
append("-fno-omit-frame-pointer" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
append("-fno-optimize-sibling-calls" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
append("-gline-tables-only" CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE | ||
CMAKE_C_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_MINSIZE_REL) | ||
# Use -O1 even in debug mode, otherwise sanitizers slowdown is too large. | ||
if (${prefix}_OPTIMIZE_SANITIZED_BUILDS) | ||
message(STATUS "Optimizing sanitized Debug build") | ||
append("-O1" CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG) | ||
endif() | ||
else() | ||
# Keep frame pointers around. | ||
append("/Oy-" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
# Always ask the linker to produce symbols with asan. | ||
append("/Zi" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
# Workaround for incompatible (warning-producing) default CMake flag | ||
# https://docs.microsoft.com/en-us/cpp/sanitizers/asan-known-issues | ||
# https://gitlab.kitware.com/cmake/cmake/-/issues/19084 | ||
string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") | ||
append("/debug" CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) | ||
endif() | ||
endmacro() | ||
|
||
|
||
# Main logic | ||
macro(process_sanitizer prefix) | ||
|
||
# Add options for the project to use sanitizers | ||
option(${prefix}_USE_SANITIZER "Enable building with sanitizer support. Options are: Address, HWAddress, Memory, MemoryWithOrigins, Undefined, Thread, DataFlow, Leak, 'Address,Undefined'" false) | ||
if (UNIX) | ||
option(${prefix}_OPTIMIZE_SANITIZED_BUILDS "Optimize builds that use sanitization" false) | ||
option(${prefix}_BLACKLIST_FILE "Path to blacklist file for sanitizers" "") | ||
option(${prefix}_USE_SANITIZE_COVERAGE "Set for libFuzzer-required instrumentation, no linking." false) | ||
endif() | ||
|
||
if (${prefix}_USE_SANITIZER) | ||
if(UNIX) | ||
|
||
if(${prefix}_USE_SANITIZER STREQUAL "Address") | ||
message(STATUS "Building with Address sanitizer") | ||
append_common_sanitizer_flags(${prefix}) | ||
append("-fsanitize=address" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
|
||
elseif(${prefix}_USE_SANITIZER STREQUAL "HWAddress") | ||
message(STATUS "Building with Address sanitizer") | ||
append_common_sanitizer_flags(${prefix}) | ||
append("-fsanitize=hwaddress" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
|
||
elseif(${prefix}_USE_SANITIZER MATCHES "Memory(WithOrigins)?") | ||
append_common_sanitizer_flags(${prefix}) | ||
append("-fsanitize=memory" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
if(${prefix}_USE_SANITIZER STREQUAL "MemoryWithOrigins") | ||
message(STATUS "Building with MemoryWithOrigins sanitizer") | ||
append("-fsanitize-memory-track-origins" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
else() | ||
message(STATUS "Building with Memory sanitizer") | ||
endif() | ||
|
||
elseif(${prefix}_USE_SANITIZER STREQUAL "Undefined") | ||
message(STATUS "Building with Undefined sanitizer") | ||
append_common_sanitizer_flags(${prefix}) | ||
append("-fsanitize=undefined" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
# Execution error on undefined detection. Could be optional to add this | ||
append("-fno-sanitize-recover=all" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
|
||
elseif(${prefix}_USE_SANITIZER STREQUAL "Thread") | ||
message(STATUS "Building with Thread sanitizer") | ||
append_common_sanitizer_flags(${prefix}) | ||
append("-fsanitize=thread" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
|
||
elseif(${prefix}_USE_SANITIZER STREQUAL "DataFlow") | ||
message(STATUS "Building with DataFlow sanitizer") | ||
append("-fsanitize=dataflow" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
|
||
elseif(${prefix}_USE_SANITIZER STREQUAL "Leak") | ||
message(STATUS "Building with Leak sanitizer") | ||
append_common_sanitizer_flags(${prefix}) | ||
append("-fsanitize=leak" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
|
||
elseif(${prefix}_USE_SANITIZER STREQUAL "Address,Undefined" | ||
OR ${prefix}_USE_SANITIZER STREQUAL "Undefined,Address") | ||
message(STATUS "Building with Address, Undefined sanitizers") | ||
append_common_sanitizer_flags(${prefix}) | ||
append("-fsanitize=address,undefined" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
# Execution error on undefined detection. Could be optional to add this | ||
append("-fno-sanitize-recover=all" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
|
||
else() | ||
message( | ||
FATAL_ERROR "Unsupported value of ${prefix}_USE_SANITIZER: '${${prefix}_USE_SANITIZER}'") | ||
endif() | ||
elseif(MSVC) | ||
if(${prefix}_USE_SANITIZER STREQUAL "Address") | ||
message(STATUS "Building with Address sanitizer") | ||
append_common_sanitizer_flags(${prefix}) | ||
append("/fsanitize=address" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
else() | ||
message(FATAL_ERROR "This sanitizer is not yet supported in the MSVC environment: '${${prefix}_USE_SANITIZER}'") | ||
endif() | ||
else() | ||
message(FATAL_ERROR "${prefix}_USE_SANITIZER is not supported on this platform.") | ||
endif() | ||
|
||
# If specified, use a blacklist file | ||
if (EXISTS "${${prefix}_BLACKLIST_FILE}") | ||
message(STATUS "Using sanitizer blacklist file: ${${prefix}_BLACKLIST_FILE}") | ||
append("-fsanitize-blacklist=${${prefix}_BLACKLIST_FILE}" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
endif() | ||
|
||
# Set a once non-default option for more detection | ||
if (${prefix}_USE_SANITIZER MATCHES "(Undefined,)?Address(,Undefined)?") | ||
if (UNIX) | ||
append("-fsanitize-address-use-after-scope" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
endif() | ||
endif() | ||
|
||
# Set for libFuzzer-required instrumentation, no linking. | ||
if (${prefix}_USE_SANITIZE_COVERAGE) | ||
message(STATUS "Setting up sanitizer for coverage support with 'fuzzer-no-link'") | ||
append("-fsanitize=fuzzer-no-link" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
endif() | ||
endif() | ||
endmacro() |