diff --git a/.github/workflows/code_sanitizers.yml b/.github/workflows/code_sanitizers.yml index 438f8fd051..d98587aa11 100644 --- a/.github/workflows/code_sanitizers.yml +++ b/.github/workflows/code_sanitizers.yml @@ -4,23 +4,29 @@ on: [push, pull_request, workflow_dispatch] jobs: linux_sanitizers: - name: Linux [${{ matrix.SANITIZER }}] [${{ matrix.BACKEND }} | ${{ matrix.COMPILER }}] + name: Linux [${{ matrix.SANITIZER.name }}] [${{ matrix.BACKEND }} | ${{ matrix.COMPILER }}] ${{ matrix.SANITIZER.ignore_errors && ' (Errors Ignored)' || '' }} timeout-minutes: 120 strategy: fail-fast: false matrix: BACKEND: [lmdb, rocksdb] COMPILER: [clang] - SANITIZER: [ASAN, ASAN_INT, TSAN] + SANITIZER: + - { name: UBSAN, ignore_errors: false } + - { name: ASAN, ignore_errors: true } + - { name: ASAN_INT, ignore_errors: true } + - { name: TSAN, ignore_errors: true } runs-on: ubuntu-22.04 env: COMPILER: ${{ matrix.COMPILER }} BACKEND: ${{ matrix.BACKEND }} - SANITIZER: ${{ matrix.SANITIZER }} + SANITIZER: ${{ matrix.SANITIZER.name }} + IGNORE_ERRORS: ${{ matrix.SANITIZER.ignore_errors }} TEST_USE_ROCKSDB: ${{ matrix.BACKEND == 'rocksdb' && '1' || '0' }} DEADLINE_SCALE_FACTOR: ${{ matrix.BACKEND == 'rocksdb' && '2' || '1' }} - ASAN_OPTIONS: log_exe_name=1:log_path=sanitizer_report + ASAN_OPTIONS: log_exe_name=1:log_path=sanitizer_report:suppressions=../asan_suppressions TSAN_OPTIONS: log_exe_name=1:log_path=sanitizer_report:suppressions=../tsan_suppressions + UBSAN_OPTIONS: log_exe_name=1:log_path=sanitizer_report:print_stacktrace=1 if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository steps: - name: Checkout @@ -49,6 +55,6 @@ jobs: - name: Reports if: steps.build.outcome == 'success' && (success() || failure()) - continue-on-error: true + continue-on-error: ${{ env.IGNORE_ERRORS == 'true' }} run: ../ci/tests/show-sanitizer-reports.sh working-directory: build diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b3d8b1d65..b211ccc432 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,9 +146,6 @@ if(${NANO_ASIO_HANDLER_TRACKING} GREATER 0) -DBOOST_ASIO_ENABLE_HANDLER_TRACKING) endif() -option(NANO_ASAN_INT "Enable ASan+UBSan+Integer overflow" OFF) -option(NANO_ASAN "Enable ASan+UBSan" OFF) -option(NANO_TSAN "Enable TSan" OFF) option(NANO_SIMD_OPTIMIZATIONS "Enable CPU-specific SIMD optimizations (SSE/AVX or NEON, e.g.)" OFF) option( @@ -216,49 +213,99 @@ endif() set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(USING_ASAN (NANO_ASAN OR RAIBLOCKS_ASAN)) -set(USING_ASAN_INT (NANO_ASAN_INT OR RAIBLOCKS_ASAN_INT)) -set(USING_TSAN (NANO_TSAN OR RAIBLOCKS_TSAN)) - find_package(Threads) -if(WIN32) - find_library(PSAPI Psapi) - add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600 -DMINIUPNP_STATICLIB - -D_CRT_SECURE_NO_WARNINGS -DNOGDI /EHsc) +# Sanitizers +option(NANO_ASAN "Enable ASan" OFF) +if(NANO_ASAN) + if(MSVC) + message(FATAL_ERROR "ASan is not supported on MSVC") + endif() + + message(STATUS "Using Asan") + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) - if(${USING_TSAN} - OR ${USING_ASAN} - OR ${USING_ASAN_INT}) - message(WARNING "Cannot use TSAN or ASAN on Windows, sanitizers ignored") + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options( + "-fsanitize-ignorelist=${PROJECT_SOURCE_DIR}/sanitize_ignorelist_asan") + add_link_options( + "-fsanitize-ignorelist=${PROJECT_SOURCE_DIR}/sanitize_ignorelist_asan") endif() -else() - if(NANO_WARN_TO_ERR) - add_compile_options(-Werror -Wno-deprecated-declarations) + add_definitions(-DED25519_NO_INLINE_ASM) +endif() + +option(NANO_ASAN_INT "Enable ASan+Integer overflow" OFF) +if(NANO_ASAN_INT) + if(MSVC) + message(FATAL_ERROR "ASan+Integer overflow is not supported on MSVC") endif() - if((${USING_TSAN} AND ${USING_ASAN}) OR (${USING_TSAN} AND ${USING_ASAN_INT})) - message(WARNING "Cannot use TSAN/ASAN together, defaulting to ASAN") + message(STATUS "Using ASan+Integer overflow") + add_compile_options(-fsanitize=address,integer) + add_link_options(-fsanitize=address,integer) + + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options( + "-fsanitize-ignorelist=${PROJECT_SOURCE_DIR}/sanitize_ignorelist_asan") + add_link_options( + "-fsanitize-ignorelist=${PROJECT_SOURCE_DIR}/sanitize_ignorelist_asan") endif() - if(${USING_ASAN} OR ${USING_ASAN_INT}) - if(${USING_ASAN_INT}) - add_compile_options(-fsanitize=address,undefined,integer) - else() - add_compile_options(-fsanitize=address,undefined) - endif() - add_definitions(-DED25519_NO_INLINE_ASM) - add_definitions(-DROCKSDB_UBSAN_RUN) - elseif(${USING_TSAN}) - add_compile_options(-fsanitize=thread) - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_compile_options( - "-fsanitize-blacklist=${PROJECT_SOURCE_DIR}/tsan_clang_blacklist") - endif() - add_definitions(-DED25519_NO_INLINE_ASM) + add_definitions(-DED25519_NO_INLINE_ASM) +endif() + +option(NANO_UBSAN "Enable UBSan" OFF) +if(NANO_UBSAN) + if(MSVC) + message(FATAL_ERROR "UBSan is not supported on MSVC") + endif() + + message(STATUS "Using UBSan") + add_compile_options(-fsanitize=undefined) + add_link_options(-fsanitize=undefined) + + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options( + "-fsanitize-ignorelist=${PROJECT_SOURCE_DIR}/sanitize_ignorelist_ubsan") + add_link_options( + "-fsanitize-ignorelist=${PROJECT_SOURCE_DIR}/sanitize_ignorelist_ubsan") endif() + add_definitions(-DED25519_NO_INLINE_ASM) + add_definitions(-DROCKSDB_UBSAN_RUN) +endif() + +option(NANO_TSAN "Enable TSan" OFF) +if(NANO_TSAN) + if(MSVC) + message(FATAL_ERROR "TSan is not supported on MSVC") + endif() + + message(STATUS "Using TSan") + add_compile_options(-fsanitize=thread) + add_link_options(-fsanitize=thread) + + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options( + "-fsanitize-ignorelist=${PROJECT_SOURCE_DIR}/sanitize_ignorelist_tsan") + add_link_options( + "-fsanitize-ignorelist=${PROJECT_SOURCE_DIR}/sanitize_ignorelist_tsan") + endif() + + add_definitions(-DED25519_NO_INLINE_ASM) +endif() + +if(NANO_WARN_TO_ERR) + add_compile_options(-Werror -Wno-deprecated-declarations) +endif() + +if(WIN32) + find_library(PSAPI Psapi) + add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600 -DMINIUPNP_STATICLIB + -D_CRT_SECURE_NO_WARNINGS -DNOGDI /EHsc) +else() if(NANO_FUZZER_TEST) add_compile_options(-fsanitize=fuzzer-no-link -fno-omit-frame-pointer) add_definitions(-DNANO_FUZZER_TEST) @@ -320,20 +367,6 @@ else() set(PLATFORM_LINK_FLAGS "-static-libgcc -static-libstdc++") endif() - if(${USING_ASAN_INT}) - set(PLATFORM_LINK_FLAGS - "${PLATFORM_LINK_FLAGS} -fsanitize=address,undefined,integer") - elseif(${USING_ASAN}) - set(PLATFORM_LINK_FLAGS - "${PLATFORM_LINK_FLAGS} -fsanitize=address,undefined") - elseif(${USING_TSAN}) - set(PLATFORM_LINK_FLAGS "${PLATFORM_LINK_FLAGS} -fsanitize=thread") - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(PLATFORM_LINK_FLAGS - "${PLATFORM_LINK_FLAGS} -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/tsan_clang_blacklist" - ) - endif() - endif() if(NANO_FUZZER_TEST) set(PLATFORM_LINK_FLAGS "${PLATFORM_LINK_FLAGS} -fsanitize=fuzzer-no-link") endif() diff --git a/asan_suppressions b/asan_suppressions new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ci/build.sh b/ci/build.sh index 62192e7e7d..7c25a724b5 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -1,5 +1,6 @@ #!/bin/bash set -euox pipefail +shopt -s nocasematch # Enable case-insensitive matching BUILD_TARGET="" if [[ ${1:-} ]]; then @@ -35,6 +36,9 @@ if [[ ${SANITIZER:-} ]]; then TSAN) CMAKE_SANITIZER="-DNANO_TSAN=ON" ;; + UBSAN) + CMAKE_SANITIZER="-DNANO_UBSAN=ON" + ;; *) echo "Unknown sanitizer: '${SANITIZER}'" exit 1 diff --git a/ci/prepare/linux/prepare-clang.sh b/ci/prepare/linux/prepare-clang.sh index 16636b0b3f..ce35402e2e 100755 --- a/ci/prepare/linux/prepare-clang.sh +++ b/ci/prepare/linux/prepare-clang.sh @@ -1,5 +1,8 @@ #!/bin/bash -set -euo pipefail +set -euox pipefail + +# Clang installer dependencies +DEBIAN_FRONTEND=noninteractive apt-get install -yqq lsb-release software-properties-common gnupg CLANG_VERSION=16 @@ -12,4 +15,4 @@ update-alternatives --install /usr/bin/lldb lldb /usr/bin/lldb-$CLANG_VERSION 10 # Workaround to get a path that can be easily passed into cmake for BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE # See https://www.boost.org/doc/libs/1_70_0/doc/html/stacktrace/configuration_and_build.html#stacktrace.configuration_and_build.f3 -backtrace_file=$(find /usr/lib/gcc/ -name 'backtrace.h' | head -n 1) && test -f $backtrace_file && ln -s $backtrace_file /tmp/backtrace.h \ No newline at end of file +backtrace_file=$(find /usr/lib/gcc/ -name 'backtrace.h' | head -n 1) && test -f $backtrace_file && ln -s $backtrace_file /tmp/backtrace.h diff --git a/ci/prepare/linux/prepare-gcc.sh b/ci/prepare/linux/prepare-gcc.sh index 2a9b97a5a3..d06a2644c3 100755 --- a/ci/prepare/linux/prepare-gcc.sh +++ b/ci/prepare/linux/prepare-gcc.sh @@ -1,2 +1,2 @@ #!/bin/bash -set -euo pipefail \ No newline at end of file +set -euox pipefail \ No newline at end of file diff --git a/ci/prepare/linux/prepare.sh b/ci/prepare/linux/prepare.sh index 2ed1a8f545..f63da28e8d 100755 --- a/ci/prepare/linux/prepare.sh +++ b/ci/prepare/linux/prepare.sh @@ -6,11 +6,12 @@ COMPILER=${COMPILER:-gcc} echo "Compiler: '${COMPILER}'" # Common dependencies needed for building & testing -apt-get update -qq +DEBIAN_FRONTEND=noninteractive apt-get update -qq DEBIAN_FRONTEND=noninteractive apt-get install -yqq \ build-essential \ g++ \ +curl \ wget \ python3 \ zlib1g-dev \ diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index 044813fd69..cb0e1974cb 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -75,7 +75,7 @@ std::shared_ptr nano::test::system::add_node (nano::node_config cons (*j)->network.merge_peer ((*i)->network.endpoint ()); { - auto ec = poll_until_true (3s, [&node1, &node2, starting_size_1, starting_size_2] () { + auto ec = poll_until_true (5s, [&node1, &node2, starting_size_1, starting_size_2] () { auto size_1 = node1->network.size (); auto size_2 = node2->network.size (); return size_1 > starting_size_1 && size_2 > starting_size_2; @@ -87,7 +87,7 @@ std::shared_ptr nano::test::system::add_node (nano::node_config cons { { // Wait for initial connection finish - auto ec = poll_until_true (3s, [&node1, &node2, starting_realtime_1, starting_realtime_2] () { + auto ec = poll_until_true (5s, [&node1, &node2, starting_realtime_1, starting_realtime_2] () { auto realtime_1 = node1->tcp_listener.realtime_count.load (); auto realtime_2 = node2->tcp_listener.realtime_count.load (); return realtime_1 > starting_realtime_1 && realtime_2 > starting_realtime_2; @@ -96,7 +96,7 @@ std::shared_ptr nano::test::system::add_node (nano::node_config cons } { // Wait for keepalive message exchange - auto ec = poll_until_true (3s, [&node1, &node2, starting_keepalives_1, starting_keepalives_2] () { + auto ec = poll_until_true (5s, [&node1, &node2, starting_keepalives_1, starting_keepalives_2] () { auto keepalives_1 = node1->stats.count (stat::type::message, stat::detail::keepalive, stat::dir::in); auto keepalives_2 = node2->stats.count (stat::type::message, stat::detail::keepalive, stat::dir::in); return keepalives_1 > starting_keepalives_1 && keepalives_2 > starting_keepalives_2; @@ -108,7 +108,7 @@ std::shared_ptr nano::test::system::add_node (nano::node_config cons { // Ensure no bootstrap initiators are in progress - auto ec = poll_until_true (3s, [this, &begin] () { + auto ec = poll_until_true (5s, [this, &begin] () { return std::all_of (begin, nodes.end (), [] (std::shared_ptr const & node_a) { return !node_a->bootstrap_initiator.in_progress (); }); }); debug_assert (!ec); @@ -116,7 +116,7 @@ std::shared_ptr nano::test::system::add_node (nano::node_config cons } else { - auto ec = poll_until_true (3s, [&node] () { + auto ec = poll_until_true (5s, [&node] () { return !node->bootstrap_initiator.in_progress (); }); debug_assert (!ec); diff --git a/sanitize_ignorelist b/sanitize_ignorelist new file mode 100644 index 0000000000..88c388537a --- /dev/null +++ b/sanitize_ignorelist @@ -0,0 +1,4 @@ +src:*crypto/ed25519* +src:*crypto/blake2* + +src:*submodules/lmdb* \ No newline at end of file diff --git a/sanitize_ignorelist_asan b/sanitize_ignorelist_asan new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sanitize_ignorelist_tsan b/sanitize_ignorelist_tsan new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sanitize_ignorelist_ubsan b/sanitize_ignorelist_ubsan new file mode 100644 index 0000000000..e69de29bb2