-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Specify sanitizers using IGN_SANITIZERS cmake variable #210
Changes from all commits
e6eea98
c80c29c
443e015
f169762
c1bc190
86f2091
05e5e6d
c55d115
6bb8683
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
# | ||
# Copyright (C) 2018-2022 by George Cave - [email protected] | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
# use this file except in compliance with the License. You may obtain a copy of | ||
# the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
# License for the specific language governing permissions and limitations under | ||
# the License. | ||
|
||
# Original code https://raw.githubusercontent.com/StableCoder/cmake-scripts/main/sanitizers.cmake | ||
|
||
include(CheckCXXSourceCompiles) | ||
|
||
set(IGN_SANITIZER "" | ||
CACHE STRING | ||
"Compile with a sanitizer. Options are: Address, Memory, MemoryWithOrigins, Undefined, Thread, Leak, 'Address;Undefined', CFI" | ||
) | ||
|
||
function(append value) | ||
foreach(variable ${ARGN}) | ||
set(${variable} | ||
"${${variable}} ${value}" | ||
PARENT_SCOPE) | ||
endforeach(variable) | ||
endfunction() | ||
|
||
function(append_quoteless value) | ||
foreach(variable ${ARGN}) | ||
set(${variable} | ||
${${variable}} ${value} | ||
PARENT_SCOPE) | ||
endforeach(variable) | ||
endfunction() | ||
|
||
function(test_san_flags return_var flags) | ||
set(QUIET_BACKUP ${CMAKE_REQUIRED_QUIET}) | ||
set(CMAKE_REQUIRED_QUIET TRUE) | ||
unset(${return_var} CACHE) | ||
set(FLAGS_BACKUP ${CMAKE_REQUIRED_FLAGS}) | ||
set(CMAKE_REQUIRED_FLAGS "${flags}") | ||
check_cxx_source_compiles("int main() { return 0; }" ${return_var}) | ||
set(CMAKE_REQUIRED_FLAGS "${FLAGS_BACKUP}") | ||
set(CMAKE_REQUIRED_QUIET "${QUIET_BACKUP}") | ||
endfunction() | ||
|
||
if(IGN_SANITIZER) | ||
append("-fno-omit-frame-pointer" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
|
||
unset(SANITIZER_SELECTED_FLAGS) | ||
|
||
if(UNIX) | ||
if(IGN_SANITIZER MATCHES "([Aa]ddress)") | ||
# Optional: -fno-optimize-sibling-calls -fsanitize-address-use-after-scope | ||
message(STATUS "Testing with Address sanitizer") | ||
set(SANITIZER_ADDR_FLAG "-fsanitize=address") | ||
test_san_flags(SANITIZER_ADDR_AVAILABLE ${SANITIZER_ADDR_FLAG}) | ||
if(SANITIZER_ADDR_AVAILABLE) | ||
message(STATUS " Building with Address sanitizer") | ||
append("${SANITIZER_ADDR_FLAG}" SANITIZER_SELECTED_FLAGS) | ||
|
||
if(AFL) | ||
append_quoteless(AFL_USE_ASAN=1 CMAKE_C_COMPILER_LAUNCHER | ||
CMAKE_CXX_COMPILER_LAUNCHER) | ||
endif() | ||
else() | ||
message( | ||
FATAL_ERROR | ||
"Address sanitizer not available for ${CMAKE_CXX_COMPILER}") | ||
endif() | ||
endif() | ||
|
||
if(IGN_SANITIZER MATCHES "([Mm]emory([Ww]ith[Oo]rigins)?)") | ||
# Optional: -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2 | ||
set(SANITIZER_MEM_FLAG "-fsanitize=memory") | ||
if(IGN_SANITIZER MATCHES "([Mm]emory[Ww]ith[Oo]rigins)") | ||
message(STATUS "Testing with MemoryWithOrigins sanitizer") | ||
append("-fsanitize-memory-track-origins" SANITIZER_MEM_FLAG) | ||
else() | ||
message(STATUS "Testing with Memory sanitizer") | ||
endif() | ||
test_san_flags(SANITIZER_MEM_AVAILABLE ${SANITIZER_MEM_FLAG}) | ||
if(SANITIZER_MEM_AVAILABLE) | ||
if(IGN_SANITIZER MATCHES "([Mm]emory[Ww]ith[Oo]rigins)") | ||
message(STATUS " Building with MemoryWithOrigins sanitizer") | ||
else() | ||
message(STATUS " Building with Memory sanitizer") | ||
endif() | ||
append("${SANITIZER_MEM_FLAG}" SANITIZER_SELECTED_FLAGS) | ||
|
||
if(AFL) | ||
append_quoteless(AFL_USE_MSAN=1 CMAKE_C_COMPILER_LAUNCHER | ||
CMAKE_CXX_COMPILER_LAUNCHER) | ||
endif() | ||
else() | ||
message( | ||
FATAL_ERROR | ||
"Memory [With Origins] sanitizer not available for ${CMAKE_CXX_COMPILER}" | ||
) | ||
endif() | ||
endif() | ||
|
||
if(IGN_SANITIZER MATCHES "([Uu]ndefined)") | ||
message(STATUS "Testing with Undefined Behaviour sanitizer") | ||
set(SANITIZER_UB_FLAG "-fsanitize=undefined") | ||
if(EXISTS "${BLACKLIST_FILE}") | ||
append("-fsanitize-blacklist=${BLACKLIST_FILE}" SANITIZER_UB_FLAG) | ||
endif() | ||
test_san_flags(SANITIZER_UB_AVAILABLE ${SANITIZER_UB_FLAG}) | ||
if(SANITIZER_UB_AVAILABLE) | ||
message(STATUS " Building with Undefined Behaviour sanitizer") | ||
append("${SANITIZER_UB_FLAG}" SANITIZER_SELECTED_FLAGS) | ||
|
||
if(AFL) | ||
append_quoteless(AFL_USE_UBSAN=1 CMAKE_C_COMPILER_LAUNCHER | ||
CMAKE_CXX_COMPILER_LAUNCHER) | ||
endif() | ||
else() | ||
message( | ||
FATAL_ERROR | ||
"Undefined Behaviour sanitizer not available for ${CMAKE_CXX_COMPILER}" | ||
) | ||
endif() | ||
endif() | ||
|
||
if(IGN_SANITIZER MATCHES "([Tt]hread)") | ||
message(STATUS "Testing with Thread sanitizer") | ||
set(SANITIZER_THREAD_FLAG "-fsanitize=thread") | ||
test_san_flags(SANITIZER_THREAD_AVAILABLE ${SANITIZER_THREAD_FLAG}) | ||
if(SANITIZER_THREAD_AVAILABLE) | ||
message(STATUS " Building with Thread sanitizer") | ||
append("${SANITIZER_THREAD_FLAG}" SANITIZER_SELECTED_FLAGS) | ||
|
||
if(AFL) | ||
append_quoteless(AFL_USE_TSAN=1 CMAKE_C_COMPILER_LAUNCHER | ||
CMAKE_CXX_COMPILER_LAUNCHER) | ||
endif() | ||
else() | ||
message( | ||
FATAL_ERROR "Thread sanitizer not available for ${CMAKE_CXX_COMPILER}" | ||
) | ||
endif() | ||
endif() | ||
|
||
if(IGN_SANITIZER MATCHES "([Ll]eak)") | ||
message(STATUS "Testing with Leak sanitizer") | ||
set(SANITIZER_LEAK_FLAG "-fsanitize=leak") | ||
test_san_flags(SANITIZER_LEAK_AVAILABLE ${SANITIZER_LEAK_FLAG}) | ||
if(SANITIZER_LEAK_AVAILABLE) | ||
message(STATUS " Building with Leak sanitizer") | ||
append("${SANITIZER_LEAK_FLAG}" SANITIZER_SELECTED_FLAGS) | ||
|
||
if(AFL) | ||
append_quoteless(AFL_USE_LSAN=1 CMAKE_C_COMPILER_LAUNCHER | ||
CMAKE_CXX_COMPILER_LAUNCHER) | ||
endif() | ||
else() | ||
message( | ||
FATAL_ERROR "Thread sanitizer not available for ${CMAKE_CXX_COMPILER}" | ||
) | ||
endif() | ||
endif() | ||
|
||
if(IGN_SANITIZER MATCHES "([Cc][Ff][Ii])") | ||
message(STATUS "Testing with Control Flow Integrity(CFI) sanitizer") | ||
set(SANITIZER_CFI_FLAG "-fsanitize=cfi") | ||
test_san_flags(SANITIZER_CFI_AVAILABLE ${SANITIZER_CFI_FLAG}) | ||
if(SANITIZER_CFI_AVAILABLE) | ||
message(STATUS " Building with Control Flow Integrity(CFI) sanitizer") | ||
append("${SANITIZER_LEAK_FLAG}" SANITIZER_SELECTED_FLAGS) | ||
|
||
if(AFL) | ||
append_quoteless(AFL_USE_CFISAN=1 CMAKE_C_COMPILER_LAUNCHER | ||
CMAKE_CXX_COMPILER_LAUNCHER) | ||
endif() | ||
else() | ||
message( | ||
FATAL_ERROR | ||
"Control Flow Integrity(CFI) sanitizer not available for ${CMAKE_CXX_COMPILER}" | ||
) | ||
endif() | ||
endif() | ||
|
||
message(STATUS "Sanitizer flags: ${SANITIZER_SELECTED_FLAGS}") | ||
test_san_flags(SANITIZER_SELECTED_COMPATIBLE ${SANITIZER_SELECTED_FLAGS}) | ||
if(SANITIZER_SELECTED_COMPATIBLE) | ||
message(STATUS " Building with ${SANITIZER_SELECTED_FLAGS}") | ||
append("${SANITIZER_SELECTED_FLAGS}" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
else() | ||
message( | ||
FATAL_ERROR | ||
" Sanitizer flags ${SANITIZER_SELECTED_FLAGS} are not compatible.") | ||
endif() | ||
elseif(MSVC) | ||
if(IGN_SANITIZER MATCHES "([Aa]ddress)") | ||
message(STATUS "Building with Address sanitizer") | ||
append("-fsanitize=address" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) | ||
|
||
if(AFL) | ||
append_quoteless(AFL_USE_ASAN=1 CMAKE_C_COMPILER_LAUNCHER | ||
CMAKE_CXX_COMPILER_LAUNCHER) | ||
endif() | ||
else() | ||
message( | ||
FATAL_ERROR | ||
"This sanitizer not yet supported in the MSVC environment: ${IGN_SANITIZER}" | ||
) | ||
endif() | ||
else() | ||
message(FATAL_ERROR "IGN_SANITIZER is not supported on this platform.") | ||
endif() | ||
|
||
endif() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) | ||
project(ignition-sanitizers VERSION 0.1.0) | ||
find_package(ignition-cmake2 REQUIRED) | ||
ign_configure_project() | ||
ign_configure_build(QUIT_IF_BUILD_ERRORS) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
ign_get_libsources_and_unittests(sources gtest_sources) | ||
ign_add_executable(asanfail ${sources}) | ||
add_test(asan asanfail) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright (C) 2018-2022 by George Cave - [email protected] | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
* use this file except in compliance with the License. You may obtain a copy of | ||
* the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
* License for the specific language governing permissions and limitations under | ||
* the License. | ||
* | ||
* Original code https://raw.githubusercontent.com/StableCoder/cmake-scripts/main/example/src/asan_fail.cpp | ||
*/ | ||
|
||
int main(int argc, char * argv[]) { | ||
int *array = new int[100]; | ||
delete[] array; | ||
return array[argc]; // BOOM | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -96,26 +96,10 @@ To change the build system type, set the CMake flag: | |
-GNinja | ||
``` | ||
|
||
### Address sanitizer (ASan) | ||
### Build sanitizers | ||
|
||
The `gcc` and `clang` compilers have a set of flags to generate instrumented builds for detecting memory leaks. | ||
|
||
By default, address sanitizer is *not used*. | ||
|
||
To enable address sanitizer, set all of the following flags: | ||
|
||
``` | ||
-DCMAKE_CXX_FLAGS="-fsanitize=address -fsanitize=leak -g" | ||
-DCMAKE_C_FLAGS="-fsanitize=address -fsanitize=leak -g" | ||
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address -fsanitize=leak" | ||
-DCMAKE_MODULE_LINKER_FLAGS="-fsanitize=address -fsanitize=leak" | ||
``` | ||
|
||
This will report if memory is leaked during execution of binaries or tests. | ||
|
||
More information about address santizier can be found in the [ASan documentation](https:/google/sanitizers/wiki/AddressSanitizer). | ||
|
||
Note: Address sanitizer may have an impact on the performance of execution. | ||
`IGN_SANITIZER` CMake parameter can be used with different compilers to support the detection of different problems in the code. | ||
[Check the documentation for `IGN_SANITIZER` flag](ign_cmake_sanitizers.md) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem we have is that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We've been using the term "tutorial" to refer to all kinds of documentation, guides, instructions, etc. So I wouldn't worry too much about it. It's good to give the document more visibility by placing it on the main page. In any case, note that we're not publishing tutorials for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks @azeey and @chapulina for the information. I added the entry in c55d115 |
||
|
||
### Using CCache | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Sanitizer Builds | ||
|
||
## Original source and Copyright | ||
|
||
The original work for these instructions is the project https:/StableCoder/cmake-scripts/ licensed under the Apache-2 with the following copyright: | ||
|
||
> Copyright (C) 2018-2022 by George Cave - [email protected] | ||
|
||
## Description | ||
|
||
Sanitizers are tools that perform checks during a program’s runtime and returns issues, and as such, along with unit testing, code coverage and static analysis, is another tool to add to the programmers toolbox. And of course, like the previous tools, are tragically simple to add into any project using CMake, allowing any project and developer to quickly and easily use. | ||
|
||
A quick rundown of the tools available, and what they do: | ||
- [LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html) detects memory leaks, or issues where memory is allocated and never deallocated, causing programs to slowly consume more and more memory, eventually leading to a crash. | ||
- [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) is a fast memory error detector. It is useful for detecting most issues dealing with memory, such as: | ||
- Out of bounds accesses to heap, stack, global | ||
- Use after free | ||
- Use after return | ||
- Use after scope | ||
- Double-free, invalid free | ||
- Memory leaks (using LeakSanitizer) | ||
- [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html) detects data races for multi-threaded code. | ||
- [UndefinedBehaviourSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) detects the use of various features of C/C++ that are explicitly listed as resulting in undefined behaviour. Most notably: | ||
- Using misaligned or null pointer. | ||
- Signed integer overflow | ||
- Conversion to, from, or between floating-point types which would overflow the destination | ||
- Division by zero | ||
- Unreachable code | ||
- [MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html) detects uninitialized reads. | ||
- [Control Flow Integrity](https://clang.llvm.org/docs/ControlFlowIntegrity.html) is designed to detect certain forms of undefined behaviour that can potentially allow attackers to subvert the program's control flow. | ||
|
||
These are used by declaring the `IGN_SANITIZER` CMake variable as string containing any of: | ||
- Address | ||
- Memory | ||
- MemoryWithOrigins | ||
- Undefined | ||
- Thread | ||
- Leak | ||
- CFI | ||
|
||
Multiple values are allowed, e.g. `-DIGN_SANITIZER=Address,Leak` but some sanitizers cannot be combined together, e.g.`-DIGN_SANITIZER=Address,Memory` will result in configuration error. The delimeter character is not required and `-DIGN_SANITIZER=AddressLeak` would work as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFL
is not being used here, so we can remove thisif
block. Here and elsewhere below.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. I'm in favor of leave it in place just in the case that someone (or even us in the future) is using that support now that we have it implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I'm okay with that, but I haven't tested it. Not sure if you've had a chance.