Skip to content

Commit

Permalink
Add sanitizers with CMake
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex-PLACET committed Jun 26, 2024
1 parent 98cd268 commit 34b78be
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 23 deletions.
17 changes: 14 additions & 3 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

name: Linux
on:
workflow_dispatch:
Expand Down Expand Up @@ -42,10 +43,10 @@ jobs:

- name: Install LLVM and Clang
if: matrix.sys.compiler == 'clang'
uses: egor-tensin/setup-clang@v1
uses: KyleMayes/install-llvm-action@v2
with:
version: ${{matrix.sys.version}}
platform: x64
arch: x64

- name: Install the specified standard library for clang
if: matrix.sys.compiler == 'clang'
Expand All @@ -63,7 +64,7 @@ jobs:
cache-downloads: true

- name: Configure using CMake
run: cmake -G Ninja -Bbuild ${{matrix.sys.config-flags}} -DCMAKE_BUILD_TYPE:STRING=${{matrix.config.name}} -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX -DUSE_DATE_POLYFILL=${{matrix.sys.date-polyfill}} -DBUILD_TESTS=ON
run: cmake -G Ninja -Bbuild ${{matrix.sys.config-flags}} -DCMAKE_BUILD_TYPE:STRING=${{matrix.config.name}} -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX -DUSE_DATE_POLYFILL=${{matrix.sys.date-polyfill}} -DBUILD_TESTS=ON -DUSE_SANITIZER="address;undefined;leak"

- name: Install
working-directory: build
Expand All @@ -76,10 +77,20 @@ jobs:
- name: Run tests
working-directory: build
run: cmake --build . --config ${{matrix.config.name}} --target run_tests_with_junit_report
env:
ASAN_OPTIONS: log_path=${{github.workspace}}/asan.log:alloc_dealloc_mismatch=0:halt_on_error=0:handle_abort=0:exitcode=0
# UBSAN_OPTIONS: log_path=${{github.workspace}}/ubsan.log:halt_on_error=0:handle_abort=1:exitcode=0

- name: Upload test results
uses: actions/upload-artifact@v4
if: success() || failure()
with:
name: test_sparrow_lib_report_Linux_${{ matrix.sys.compiler }}_${{ matrix.sys.version }}_${{ matrix.sys.stdlib }}_${{ matrix.config.name }}_date-polyfill_${{ matrix.sys.date-polyfill}}
path: '**/test_sparrow_lib_report.xml'

- name: Upload ASAN log
if: always()
uses: actions/upload-artifact@v2
with:
name: asan-log-${{ matrix.sys.compiler }}-${{ matrix.sys.version }}-${{ matrix.config.name }}-${{ matrix.sys.date-polyfill }}
path: ${{github.workspace}}/asan.log
6 changes: 4 additions & 2 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
steps:

- name: Setup MSVC
if: matrix.sys.compiler == 'msvc' && matrix.build-system != 'Visual Studio 17 2022'
if: matrix.sys.compiler == 'msvc'
uses: ilammy/msvc-dev-cmd@v1

- name: Install LLVM and Clang
Expand Down Expand Up @@ -63,7 +63,9 @@ jobs:
ninja
- name: Configure using CMake
run: cmake -Bbuild -DCMAKE_BUILD_TYPE:STRING=${{matrix.config.name}} -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX -DBUILD_TESTS=ON -DUSE_DATE_POLYFILL=${{matrix.sys.date-polyfill}} -G "${{matrix.build-system}}"
run: cmake -Bbuild -DCMAKE_BUILD_TYPE:STRING=${{matrix.config.name}} -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX -DBUILD_TESTS=ON -DUSE_DATE_POLYFILL=${{matrix.sys.date-polyfill}} -G "${{matrix.build-system}}" ${{env.CMAKE_SANITIZER}}
env:
CMAKE_SANITIZER: ${{ (matrix.config.name == 'Debug' && matrix.sys.compiler == 'msvc') && '-DUSE_SANITIZER=address' || '' }}

- name: Install
working-directory: build
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ cmake_minimum_required(VERSION 3.27)
# so here we go.
# This will affects all following targets being defined.
set(CMAKE_CXX_EXTENSIONS OFF)
cmake_policy(SET CMP0091 NEW)

project(sparrow CXX)

Expand Down Expand Up @@ -73,6 +74,10 @@ if(ACTIVATE_LINTER)
include(cmake/clang-tidy.cmake)
endif()

# Sanitizers
# ==========
include(cmake/sanitizers.cmake)

# Dependencies
# ============

Expand Down
47 changes: 47 additions & 0 deletions cmake/sanitizers.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
set(AVALAIBLE_SANITIZERS "address;leak;memory;thread;undefined")
set(USE_SANITIZER "" CACHE STRING "Enable sanitizer(s). Options are: ${AVALAIBLE_SANITIZERS}. Case insensitive; multiple options delimited by comma or space possible.")
string(TOLOWER "${USE_SANITIZER}" USE_SANITIZER)

if((CMAKE_BUILD_TYPE IN_LIST "Debug;RelWithDebInfo") AND USE_SANITIZER)
message(FATAL_ERROR "❌ Sanitizer only supported in Debug and RelWithDebInfo build types.")
endif()

if(USE_SANITIZER)
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")

if(USE_SANITIZER MATCHES "address")
list(APPEND SANITIZER_COMPILE_OPTIONS /fsanitize=address /D_DISABLE_VECTOR_ANNOTATION /D_DISABLE_STRING_ANNOTATION)
else()
message(FATAL_ERROR "❌ Sanitizer not supported by MSVC: ${USE_SANITIZER}. It only supports 'address'.")
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
if(USE_SANITIZER MATCHES "address")
list(APPEND SANITIZER_COMPILE_OPTIONS /fsanitize=address /D_DISABLE_VECTOR_ANNOTATION /D_DISABLE_STRING_ANNOTATION)
list(APPEND SANITIZER_LINK_LIBRARIES clang_rt.asan_dynamic-x86_64 clang_rt.asan_dynamic_runtime_thunk-x86_64)
else()
message(FATAL_ERROR "❌ Sanitizer not supported by Clang-MSVC: ${USE_SANITIZER}. It only supports 'address'.")
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
foreach(sanitizer ${USE_SANITIZER})
if(NOT ${sanitizer} IN_LIST AVALAIBLE_SANITIZERS)
message(FATAL_ERROR "❌ Sanitizer not supported: ${sanitizer}. It should be one of: ${AVALAIBLE_SANITIZERS}.")
endif()
list(APPEND SANITIZER_COMPILE_OPTIONS -fsanitize=${sanitizer})
list(APPEND SANITIZER_LINK_OPTIONS -fsanitize=${sanitizer})
if (${sanitizer} MATCHES "memory")
list(APPEND SANITIZER_LINK_LIBRARIES -fsanitize-memory-track-origins -fPIE -pie)
list(APPEND SANITIZER_LINK_OPTIONS -fsanitize-memory-track-origins -fPIE -pie)
endif()
endforeach()
list(APPEND SANITIZER_COMPILE_OPTIONS -fno-omit-frame-pointer)
else()
message(FATAL_ERROR "❌ Sanitizer: Unsupported compiler: ${CMAKE_CXX_COMPILER_ID}")
endif()

list(REMOVE_DUPLICATES SANITIZER_COMPILE_OPTIONS)
list(REMOVE_DUPLICATES SANITIZER_LINK_OPTIONS)
list(REMOVE_DUPLICATES SANITIZER_LINK_LIBRARIES)

message(STATUS "🔍 Using sanitizer: ${USE_SANITIZER}")
endif()
1 change: 1 addition & 0 deletions ignorelist_asan.supp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src:*/float16_t.hpp
36 changes: 23 additions & 13 deletions include/sparrow/allocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ namespace sparrow
&& (std::same_as<A, std::allocator<T>>
|| std::same_as<A, std::pmr::polymorphic_allocator<T>>);

template <class... Ts>
struct overloaded : Ts...
{
using Ts::operator()...;
};
// Although not required in C++20, clang needs it to build the code below
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

/*
* Type erasure class for allocators. This allows to use any kind of allocator
* (standard, polymorphic) without having to expose it as a template parameter.
Expand Down Expand Up @@ -149,15 +158,6 @@ namespace sparrow
return std::forward<A>(alloc);
}

template <class... Ts>
struct overloaded : Ts...
{
using Ts::operator()...;
};
// Although not required in C++20, clang needs it to build the code below
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

storage_type copy_storage(const storage_type& rhs) const
{
return std::visit(
Expand Down Expand Up @@ -226,18 +226,28 @@ namespace sparrow
}

template <class T>
void any_allocator<T>::deallocate(T* p, std::size_t n)
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
__declspec(no_sanitize_address)
#else
# if defined(__has_feature)
# if __has_feature(address_sanitizer)
__attribute__((no_sanitize("address")))
# endif
# endif
#endif
void
any_allocator<T>::deallocate(T* p, std::size_t n)
{
return visit_storage(
[n, p](auto& allocator)
{
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmismatched-new-delete"
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmismatched-new-delete"
#endif
return allocator.deallocate(p, n);
#if defined(__GNUC__)
#pragma GCC diagnostic pop
# pragma GCC diagnostic pop
#endif
}
);
Expand Down
2 changes: 2 additions & 0 deletions include/sparrow/data_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include "sparrow/fixed_size_layout.hpp"
#include "sparrow/variable_size_binary_layout.hpp"

#include <span>

namespace sparrow
{

Expand Down
2 changes: 1 addition & 1 deletion include/sparrow/details/3rdparty/float16_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ namespace half
const std::uint32_t f_m_rounded = ( f_m + f_m_round_offset );
const std::uint32_t f_m_denorm_sa = ( one - f_e_half_bias );
const std::uint32_t f_m_with_hidden = ( f_m_rounded | f_m_hidden_bit );
const std::uint32_t f_m_denorm = ( f_m_with_hidden >> f_m_denorm_sa );
const std::uint32_t f_m_denorm = f_m_denorm_sa < 32 ? ( f_m_with_hidden >> f_m_denorm_sa ) : 0;
const std::uint32_t h_m_denorm = ( f_m_denorm >> f_h_m_pos_offset );
const std::uint32_t f_m_rounded_overflow = ( f_m_rounded & f_m_hidden_bit );
const std::uint32_t m_nan = ( f_m >> f_h_m_pos_offset );
Expand Down
2 changes: 1 addition & 1 deletion include/sparrow/dynamic_bitset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ namespace sparrow
const size_type extra_bits = count_extra_bits();
if (extra_bits > 0)
{
m_buffer.data()[old_block_count - 1] |= (value << extra_bits);
m_buffer.data()[old_block_count - 1] |= static_cast<block_type>(value << extra_bits);
}
}

Expand Down
24 changes: 21 additions & 3 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ set(SPARROW_TESTS_SOURCES
)
set(test_target "test_sparrow_lib")
add_executable(${test_target} ${SPARROW_TESTS_SOURCES})
target_link_libraries(${test_target} PRIVATE sparrow doctest::doctest)
target_link_libraries(${test_target}
PRIVATE
sparrow
doctest::doctest
$<$<BOOL:USE_SANITIZER>:${SANITIZER_LINK_LIBRARIES}>)

include(doctest)
doctest_discover_tests(${test_target})
Expand Down Expand Up @@ -113,6 +117,9 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "C
-Wshadow # warn the user if a variable declaration shadows one from a parent context
-Wsign-conversion # warn on sign conversions
-Wunused # warn on anything being unused
$<$<CXX_COMPILER_ID:GNU>:-Wno-maybe-uninitialized>
$<$<CXX_COMPILER_ID:GNU>:-Wno-array-bounds>
$<$<CXX_COMPILER_ID:GNU>:-Wno-stringop-overread>
$<$<CXX_COMPILER_ID:GNU>:-Wduplicated-branches> # warn if if / else branches have duplicated code
$<$<CXX_COMPILER_ID:GNU>:-Wduplicated-cond> # warn if if / else chain has duplicated conditions
$<$<CXX_COMPILER_ID:GNU>:-Wlogical-op> # warn about logical operations being used where bitwise were probably wanted
Expand All @@ -121,10 +128,21 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "C
)
endif()

target_compile_options(${test_target} PRIVATE ${compiles_options})
target_compile_options(${test_target}
PRIVATE
${compiles_options}
$<$<BOOL:USE_SANITIZER>:${SANITIZER_COMPILE_OPTIONS}>)

target_link_options(${test_target}
PRIVATE
$<$<BOOL:USE_SANITIZER>:${SANITIZER_LINK_OPTIONS}>)

# We do not use non-standard C++
set_target_properties(${test_target} PROPERTIES CMAKE_CXX_EXTENSIONS OFF)
set_target_properties(${test_target}
PROPERTIES
CMAKE_CXX_EXTENSIONS OFF
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
# ENVIRONMENT "ASAN_OPTIONS=log_path=${CMAKE_SOURCE_DIR}/log_file.txt")
target_compile_features(${test_target} PRIVATE cxx_std_20)

add_custom_target(run_tests
Expand Down

0 comments on commit 34b78be

Please sign in to comment.