Add CMake option to enable address sanitizers

Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
This commit is contained in:
Felix Weilbach 2021-04-19 16:08:00 +02:00 committed by Felix Weilbach (Rebase PR Action)
parent 31179131f2
commit e2d0481c64
6 changed files with 205 additions and 41 deletions

View file

@ -9,7 +9,7 @@ steps:
path: /drone/build
commands:
- cd /drone/build
- cmake -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DCMAKE_BUILD_TYPE=Debug -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DSANITIZE_ADDRESS=ON ../src
- cmake -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DCMAKE_BUILD_TYPE=Debug -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DECM_ENABLE_SANITIZERS=address ../src
- name: compile
image: nextcloudci/client-5.12:client-5.12-11
volumes:
@ -53,7 +53,7 @@ steps:
path: /drone/build
commands:
- cd /drone/build
- cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON DCMAKE_C_COMPILER=clang-10 -DCMAKE_CXX_COMPILER=clang++-10 -DCMAKE_BUILD_TYPE=Debug -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DSANITIZE_ADDRESS=ON ../src
- cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_C_COMPILER=clang-10 -DCMAKE_CXX_COMPILER=clang++-10 -DCMAKE_BUILD_TYPE=Debug -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DECM_ENABLE_SANITIZERS=address ../src
- name: compile
image: nextcloudci/client-5.12:client-5.12-11
volumes:

View file

@ -233,8 +233,6 @@ if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
include(SanitizerFlags)
# Handle Translations, pick all client_* files from trans directory.
file( GLOB TRANS_FILES ${CMAKE_SOURCE_DIR}/translations/client_*.ts)
set(TRANSLATIONS ${TRANS_FILES})

View file

@ -0,0 +1,169 @@
# SPDX-FileCopyrightText: 2014 Mathieu Tarral <mathieu.tarral@gmail.com>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
ECMEnableSanitizers
-------------------
Enable compiler sanitizer flags.
The following sanitizers are supported:
- Address Sanitizer
- Memory Sanitizer
- Thread Sanitizer
- Leak Sanitizer
- Undefined Behaviour Sanitizer
All of them are implemented in Clang, depending on your version, and
there is an work in progress in GCC, where some of them are currently
implemented.
This module will check your current compiler version to see if it
supports the sanitizers that you want to enable
Usage
=====
Simply add::
include(ECMEnableSanitizers)
to your ``CMakeLists.txt``. Note that this module is included in
KDECompilerSettings, so projects using that module do not need to also
include this one.
The sanitizers are not enabled by default. Instead, you must set
``ECM_ENABLE_SANITIZERS`` (either in your ``CMakeLists.txt`` or on the
command line) to a semicolon-separated list of sanitizers you wish to enable.
The options are:
- address
- memory
- thread
- leak
- undefined
- fuzzer
The sanitizers "address", "memory" and "thread" are mutually exclusive. You
cannot enable two of them in the same build.
"leak" requires the "address" sanitizer.
.. note::
To reduce the overhead induced by the instrumentation of the sanitizers, it
is advised to enable compiler optimizations (``-O1`` or higher).
Example
=======
This is an example of usage::
mkdir build
cd build
cmake -DECM_ENABLE_SANITIZERS='address;leak;undefined' ..
.. note::
Most of the sanitizers will require Clang. To enable it, use::
-DCMAKE_CXX_COMPILER=clang++
Since 1.3.0.
#]=======================================================================]
# MACRO check_compiler_version
#-----------------------------
macro (check_compiler_version gcc_required_version clang_required_version msvc_required_version)
if (
(
CMAKE_CXX_COMPILER_ID MATCHES "GNU"
AND
CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${gcc_required_version}
)
OR
(
CMAKE_CXX_COMPILER_ID MATCHES "Clang"
AND
CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${clang_required_version}
)
OR
(
CMAKE_CXX_COMPILER_ID MATCHES "MSVC"
AND
CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${msvc_required_version}
)
)
# error !
message(FATAL_ERROR "You ask to enable the sanitizer ${CUR_SANITIZER},
but your compiler ${CMAKE_CXX_COMPILER_ID} version ${CMAKE_CXX_COMPILER_VERSION}
does not support it !
You should use at least GCC ${gcc_required_version}, Clang ${clang_required_version}
or MSVC ${msvc_required_version}
(99.99 means not implemented yet)")
endif ()
endmacro ()
# MACRO check_compiler_support
#------------------------------
macro (enable_sanitizer_flags sanitize_option)
if (${sanitize_option} MATCHES "address")
check_compiler_version("4.8" "3.1" "19.28")
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(XSAN_COMPILE_FLAGS "-fsanitize=address")
else()
set(XSAN_COMPILE_FLAGS "-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls")
set(XSAN_LINKER_FLAGS "-fsanitize=address")
endif()
elseif (${sanitize_option} MATCHES "thread")
check_compiler_version("4.8" "3.1" "99.99")
set(XSAN_COMPILE_FLAGS "-fsanitize=thread")
set(XSAN_LINKER_FLAGS "tsan")
elseif (${sanitize_option} MATCHES "memory")
check_compiler_version("99.99" "3.1" "99.99")
set(XSAN_COMPILE_FLAGS "-fsanitize=memory")
elseif (${sanitize_option} MATCHES "leak")
check_compiler_version("4.9" "3.4" "99.99")
set(XSAN_COMPILE_FLAGS "-fsanitize=leak")
set(XSAN_LINKER_FLAGS "lsan")
elseif (${sanitize_option} MATCHES "undefined")
check_compiler_version("4.9" "3.1" "99.99")
set(XSAN_COMPILE_FLAGS "-fsanitize=undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls")
elseif (${sanitize_option} MATCHES "fuzzer")
check_compiler_version("99.99" "6.0" "99.99")
set(XSAN_COMPILE_FLAGS "-fsanitize=fuzzer")
else ()
message(FATAL_ERROR "Compiler sanitizer option \"${sanitize_option}\" not supported.")
endif ()
endmacro ()
if (ECM_ENABLE_SANITIZERS)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
# for each element of the ECM_ENABLE_SANITIZERS list
foreach ( CUR_SANITIZER ${ECM_ENABLE_SANITIZERS} )
# lowercase filter
string(TOLOWER ${CUR_SANITIZER} CUR_SANITIZER)
# check option and enable appropriate flags
enable_sanitizer_flags ( ${CUR_SANITIZER} )
# TODO: GCC will not link pthread library if enabled ASan
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${XSAN_COMPILE_FLAGS}" )
link_libraries(${XSAN_LINKER_FLAGS})
endif()
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${XSAN_COMPILE_FLAGS}" )
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
link_libraries(${XSAN_LINKER_FLAGS})
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
string(REPLACE "-Wl,--no-undefined" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
string(REPLACE "-Wl,--no-undefined" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}")
link_libraries(${XSAN_LINKER_FLAGS})
endif ()
endforeach()
else()
message(STATUS "Tried to enable sanitizers (-DECM_ENABLE_SANITIZERS=${ECM_ENABLE_SANITIZERS}), \
but compiler (${CMAKE_CXX_COMPILER_ID}) does not have sanitizer support")
endif()
endif()

View file

@ -1,37 +0,0 @@
# Enable address sanitizer (gcc/clang only)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
set(SANITIZERS)
set(SANITIZER_EXTRA_FLAGS " -g")
macro(add_sanitizer_option variable flag help)
option(${variable} "Enable ${help}" OFF)
if(${variable})
list(APPEND SANITIZERS ${flag})
string(REPLACE ";" " " optional_args "${ARGN}")
if(optional_args)
string(APPEND SANITIZER_EXTRA_FLAGS " ${optional_args}")
endif()
endif()
mark_as_advanced(${variable})
endmacro()
add_sanitizer_option(SANITIZE_ADDRESS "address"
"AddressSanitizer (detects memory violations, buffer overflows, memory leaks)")
add_sanitizer_option(SANITIZE_LEAK "leak"
"standalone LeakSanitizer (detects memory leaks only)")
add_sanitizer_option(SANITIZE_MEMORY "memory"
"MemorySanitizer (detects reads in uninitialized memory)")
add_sanitizer_option(SANITIZE_UNDEFINED "undefined"
"UndefinedBehaviorSanitizer (detects undefined behavior)"
"-fno-sanitize=vptr")
add_sanitizer_option(SANITIZE_THREAD "thread"
"ThreadSanitizer (detects data races)")
if(SANITIZERS)
string(REPLACE ";" "," SANITIZER_FLAGS "${SANITIZERS}")
set(SANITIZER_FLAGS "-fsanitize=${SANITIZER_FLAGS}${SANITIZER_EXTRA_FLAGS}")
string(APPEND CMAKE_CXX_FLAGS " ${SANITIZER_FLAGS}")
string(APPEND CMAKE_C_FLAGS " ${SANITIZER_FLAGS}")
string(APPEND CMAKE_EXE_LINKER_FLAGS " ${SANITIZER_FLAGS}")
endif()
endif()

View file

@ -373,6 +373,38 @@ The following are known cmake parameters:
* ``BUILD_WITH_QT4=ON``: Builds using Qt4 (even if Qt5 is found).
* ``CMAKE_INSTALL_PREFIX=path``: Set an install prefix. This is mandatory on Mac OS
Address Sanitizer
=================
You can enable the address sanitizer to detect memory corruptions and other mistakes.
The are the following sanitizers are available:
* Address Sanitizer
* Leak anitizer
* Memory sanitizer
* Undefined sanitizer
* Threads sanitizer
You can enable one or more sanitizers through CMake. For example, to
enable the address and the undefined sanitizer, execute CMake like
``cmake .. -D ECM_ENABLE_SANITIZERS="address;undefined"``.
Keep in mind that not all combinations of sanitizers work together, and on some
platforms, not all types of sanitizers are available. For example, on Windows there is
currently only the address sanitizer available. If you are on Windows, you need to
make sure that the linker can find the sanitizer dlls at runtime. If you installed
Visual Studio in the standard location, you could find them in
**C:/ProgramFiles (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/Llvm/x64/lib/clang/10.0.0/lib/windows**.
Make sure you add this location to your path. You may also need to
`upgrade your Visual Studio version <https://docs.microsoft.com/en-us/cpp/sanitizers/asan?view=msvc-160#install-the-addresssanitizer>`_.
.. note:: If you use Visual Studio on Windows, you can enable the
sanitizer if you click on **Manage Configurations**, scroll
down to the section **CMake Command Arguments** and enter then
``-D ECM_ENABLE_SANITIZERS="address"`` in the text input field below.
After that, click on **Save and generate CMake cache to load variables**
right above the table.
.. _CMake: http://www.cmake.org/download
.. _CSync: http://www.csync.org
.. _Client Download Page: https://nextcloud.com/install/#install-clients

View file

@ -2,6 +2,8 @@
if(NOT TOKEN_AUTH_ONLY)
endif()
include(ECMEnableSanitizers)
set(synclib_NAME ${APPLICATION_EXECUTABLE}sync)
find_package(Qt5 5.12 COMPONENTS Core Network Xml Concurrent WebEngineWidgets WebEngine REQUIRED)