mirror of
https://github.com/QuasarApp/pe-parse.git
synced 2025-04-26 04:14:32 +00:00
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
This commit is contained in:
parent
401743fd4b
commit
4286f109b0
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@ -44,6 +44,10 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'true'
|
||||
- name: Enable ASan+UBSan Sanitizers
|
||||
if: matrix.build-type == 'Debug'
|
||||
run: |
|
||||
echo "SANITIZER_FLAG=-DPEPARSE_USE_SANITIZER=Address,Undefined" >> $GITHUB_ENV
|
||||
- name: build
|
||||
env:
|
||||
CC: ${{ matrix.compiler.CC }}
|
||||
@ -55,6 +59,7 @@ jobs:
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \
|
||||
-DBUILD_SHARED_LIBS=${{ matrix.build-shared }} \
|
||||
-DPEPARSE_ENABLE_TESTING=ON \
|
||||
${SANITIZER_FLAG} \
|
||||
..
|
||||
cmake --build .
|
||||
- name: test
|
||||
|
14
README.md
14
README.md
@ -94,6 +94,20 @@ You can build the (catch2-based) tests by adding `-DPEPARSE_ENABLE_TESTING=ON` d
|
||||
|
||||
To run the full test suite with the [Corkami test suite](https://github.com/corkami/pocs/tree/master/PE), you must clone the submodule with `git submodule update --init`.
|
||||
|
||||
## Building with Sanitizers
|
||||
|
||||
If you are familiar with C++ sanitizers and any specific development environment requirements for them (compiler, instrumented standard library, etc.), you can choose to compile with any of the following sanitizers: `Address`, `HWAddress`, `Undefined`, `Memory`, `MemoryWithOrigins`, `Leak`, `Address,Undefined`.
|
||||
|
||||
For example, to compile with both `Address` and `Undefined` sanitizers, use the following (recommended for development and testing, and tested in CI):
|
||||
|
||||
```bash
|
||||
mkdir build-san
|
||||
cd build-san
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DPEPARSE_ENABLE_TESTING=ON -DPEPARSE_USE_SANITIZER=Address,Undefined ..
|
||||
cmake --build .
|
||||
```
|
||||
|
||||
## Using the library
|
||||
|
||||
Once the library is installed, linking to it is easy! Add the following lines in your CMake project:
|
||||
|
@ -2,6 +2,9 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
include("cmake/sanitizers.cmake")
|
||||
process_sanitizer(PEPARSE)
|
||||
|
||||
if (MSVC)
|
||||
list(APPEND DEFAULT_CXX_FLAGS /W4 /analyze)
|
||||
|
||||
|
173
cmake/sanitizers.cmake
Normal file
173
cmake/sanitizers.cmake
Normal file
@ -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()
|
Loading…
x
Reference in New Issue
Block a user