diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index ae998faebdd..528934f6de2 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -143,7 +143,6 @@ jobs: working-directory: ${{ env.BUILD_DIR }} env: BUILD_TYPE: ${{ inputs.build_type }} - SANITIZERS: ${{ inputs.sanitizers }} CMAKE_ARGS: ${{ inputs.cmake_args }} run: | cmake \ diff --git a/BUILD.md b/BUILD.md index cf0a685abdb..5479242f91a 100644 --- a/BUILD.md +++ b/BUILD.md @@ -530,16 +530,16 @@ stored inside the build directory, as either of: ## Sanitizers To build dependencies and xrpld with sanitizer instrumentation, set the -`SANITIZERS` environment variable (only once before running conan and cmake) and use the `sanitizers` profile in conan: +`SANITIZERS` environment variable when running `conan install` and use the `sanitizers` profile: ```bash export SANITIZERS=address,undefinedbehavior conan install .. --output-folder . --profile:all sanitizers --build missing --settings build_type=Debug - -cmake -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug -Dxrpld=ON -Dtests=ON .. ``` +You can then build and test as usual, with the generated `xrpld` binary containing the sanitizer instrumentation. When you run it, it will report any sanitizer errors it detects in the console output. + See [Sanitizers docs](./docs/build/sanitizers.md) for more details. ## Options diff --git a/cmake/XrplSanitizers.cmake b/cmake/XrplSanitizers.cmake index 22283812868..64f1841bfb9 100644 --- a/cmake/XrplSanitizers.cmake +++ b/cmake/XrplSanitizers.cmake @@ -1,140 +1,33 @@ #[===================================================================[ - Configure sanitizers based on environment variables. + Apply sanitizer flags built by the Conan profile. - This module reads the following environment variables: - - SANITIZERS: The sanitizers to enable. Possible values: - - "address" - - "address,undefinedbehavior" - - "thread" - - "thread,undefinedbehavior" - - "undefinedbehavior" + Parsing, validation, and flag construction are performed in conan/profiles/sanitizers. + This module reads the following CMake variables injected by the Conan toolchain via extra_variables: - The compiler type and platform are detected in CompilationEnv.cmake. - The sanitizer compile options are applied to the 'common' interface library - which is linked to all targets in the project. + - SANITIZERS: The active sanitizers (e.g. "address,undefinedbehavior"). + - SANITIZERS_COMPILER_FLAGS: Space-separated compiler flags. + - SANITIZERS_LINKER_FLAGS: Space-separated linker flags. - Internal flag variables set by this module: - - - SANITIZER_TYPES: List of sanitizer types to enable (e.g., "address", - "thread", "undefined"). And two more flags for undefined behavior sanitizer (e.g., "float-divide-by-zero", "unsigned-integer-overflow"). - This list is joined with commas and passed to -fsanitize=. - - - SANITIZERS_COMPILE_FLAGS: Compiler flags for sanitizer instrumentation. - Includes: - * -fno-omit-frame-pointer: Preserves frame pointers for stack traces - * -O1: Minimum optimization for reasonable performance - * -fsanitize=: Enables sanitizer instrumentation - * -fsanitize-ignorelist=: (Clang only) Compile-time ignorelist - * -mcmodel=large/medium: (GCC only) Code model for large binaries - * -Wno-stringop-overflow: (GCC only) Suppresses false positive warnings - * -Wno-tsan: (For GCC TSAN combination only) Suppresses atomic_thread_fence warnings - - - SANITIZERS_LINK_FLAGS: Linker flags for sanitizer runtime libraries. - Includes: - * -fsanitize=: Links sanitizer runtime libraries - * -mcmodel=large/medium: (GCC only) Matches compile-time code model - - - SANITIZERS_RELOCATION_FLAGS: (GCC only, x86_64 only) Code model flags for linking. - Used to handle large instrumented binaries on x86_64: - * -mcmodel=large: For AddressSanitizer (prevents relocation errors) - * -mcmodel=medium: For ThreadSanitizer (large model is incompatible) - On ARM64, these flags are omitted since GCC does not support - -mcmodel=large with -fPIC, and -mcmodel=medium does not exist. + The flags are applied to the 'common' interface library which is linked to all targets in the project. #]===================================================================] +include_guard(GLOBAL) include(CompilationEnv) -# Read environment variable -set(SANITIZERS "") -if(DEFINED ENV{SANITIZERS}) - set(SANITIZERS "$ENV{SANITIZERS}") -endif() - -# Set SANITIZERS_ENABLED flag for use in other modules -if(SANITIZERS MATCHES "address|thread|undefinedbehavior") - set(SANITIZERS_ENABLED TRUE) -else() +if(NOT DEFINED SANITIZERS) set(SANITIZERS_ENABLED FALSE) return() endif() +set(SANITIZERS_ENABLED TRUE) -# Sanitizers are not supported on Windows/MSVC -if(is_msvc) - message( - FATAL_ERROR - "Sanitizers are not supported on Windows/MSVC. " - "Please unset the SANITIZERS environment variable." - ) -endif() - -message(STATUS "Configuring sanitizers: ${SANITIZERS}") - -# Parse SANITIZERS value to determine which sanitizers to enable -set(enable_asan FALSE) -set(enable_tsan FALSE) -set(enable_ubsan FALSE) - -# Normalize SANITIZERS into a list -set(san_list "${SANITIZERS}") -string(REPLACE "," ";" san_list "${san_list}") -separate_arguments(san_list) - -foreach(san IN LISTS san_list) - if(san STREQUAL "address") - set(enable_asan TRUE) - elseif(san STREQUAL "thread") - set(enable_tsan TRUE) - elseif(san STREQUAL "undefinedbehavior") - set(enable_ubsan TRUE) - else() - message( - FATAL_ERROR - "Unsupported sanitizer type: ${san}" - "Supported: address, thread, undefinedbehavior and their combinations." - ) - endif() -endforeach() - -# Validate sanitizer compatibility -if(enable_asan AND enable_tsan) - message( - FATAL_ERROR - "AddressSanitizer and ThreadSanitizer are incompatible and cannot be enabled simultaneously. " - "Use 'address' or 'thread', optionally with 'undefinedbehavior'." - ) -endif() - -# Frame pointer is required for meaningful stack traces. Sanitizers recommend minimum of -O1 for reasonable performance -set(SANITIZERS_COMPILE_FLAGS "-fno-omit-frame-pointer" "-O1") - -# Build the sanitizer flags list -set(SANITIZER_TYPES) - -if(enable_asan) - list(APPEND SANITIZER_TYPES "address") -elseif(enable_tsan) - list(APPEND SANITIZER_TYPES "thread") -endif() +message(STATUS "=== Configuring Sanitizers ===") +message(STATUS " SANITIZERS: ${SANITIZERS}") +message(STATUS " Compile flags: ${SANITIZERS_COMPILER_FLAGS}") +message(STATUS " Link flags: ${SANITIZERS_LINKER_FLAGS}") -if(enable_ubsan) - # UB sanitizer flags - list(APPEND SANITIZER_TYPES "undefined" "float-divide-by-zero") - if(is_clang) - # Clang supports additional UB checks. More info here - # https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html - list(APPEND SANITIZER_TYPES "unsigned-integer-overflow") - endif() -endif() - -# Configure code model for GCC on amd64 Use large code model for ASAN to avoid relocation errors Use medium code model -# for TSAN (large is not compatible with TSAN) -set(SANITIZERS_RELOCATION_FLAGS) - -# Compiler-specific configuration +# GCC with sanitizers is incompatible with mold, gold, and lld linkers. +# Namely, the instrumented binary exceeds size limits imposed by these linkers. if(is_gcc) - # Disable mold, gold and lld linkers for GCC with sanitizers Use default linker (bfd/ld) which is more lenient with - # mixed code models This is needed since the size of instrumented binary exceeds the limits set by mold, lld and - # gold linkers set(use_mold OFF CACHE BOOL "Use mold linker" FORCE) set(use_gold OFF CACHE BOOL "Use gold linker" FORCE) set(use_lld OFF CACHE BOOL "Use lld linker" FORCE) @@ -142,82 +35,62 @@ if(is_gcc) STATUS " Disabled mold, gold, and lld linkers for GCC with sanitizers" ) +endif() - # Suppress false positive warnings in GCC with stringop-overflow - list(APPEND SANITIZERS_COMPILE_FLAGS "-Wno-stringop-overflow") - - if(is_amd64 AND enable_asan) - message(STATUS " Using large code model (-mcmodel=large)") - list(APPEND SANITIZERS_COMPILE_FLAGS "-mcmodel=large") - list(APPEND SANITIZERS_RELOCATION_FLAGS "-mcmodel=large") - elseif(enable_tsan) - # GCC doesn't support atomic_thread_fence with tsan. Suppress warnings. - list(APPEND SANITIZERS_COMPILE_FLAGS "-Wno-tsan") - if(is_amd64) - message(STATUS " Using medium code model (-mcmodel=medium)") - list(APPEND SANITIZERS_COMPILE_FLAGS "-mcmodel=medium") - list(APPEND SANITIZERS_RELOCATION_FLAGS "-mcmodel=medium") - endif() - endif() +# Flags arrive as space-separated strings; split into CMake lists before use +separate_arguments( + sanitizers_compiler_flags + UNIX_COMMAND + "${SANITIZERS_COMPILER_FLAGS}" +) +separate_arguments( + sanitizers_linker_flags + UNIX_COMMAND + "${SANITIZERS_LINKER_FLAGS}" +) - # Join sanitizer flags with commas for -fsanitize option - list(JOIN SANITIZER_TYPES "," SANITIZER_TYPES_STR) +target_compile_options( + common + INTERFACE + $<$:${sanitizers_compiler_flags}> + $<$:${sanitizers_compiler_flags}> +) +target_link_options(common INTERFACE ${sanitizers_linker_flags}) - # Add sanitizer to compile and link flags - list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}") - set(SANITIZERS_LINK_FLAGS - "${SANITIZERS_RELOCATION_FLAGS}" - "-fsanitize=${SANITIZER_TYPES_STR}" - ) -elseif(is_clang) - # Add ignorelist for Clang (GCC doesn't support this) Use CMAKE_SOURCE_DIR to get the path to the ignorelist - set(IGNORELIST_PATH +# This module appends -fsanitize-ignorelist= for Clang builds. +# The ignorelist path contains CMAKE_SOURCE_DIR, so it must be set here, rather than in the Conan profile. +# GCC does not support -fsanitize-ignorelist. +if(is_clang) + set(ignorelist_path "${CMAKE_SOURCE_DIR}/sanitizers/suppressions/sanitizer-ignorelist.txt" ) - if(NOT EXISTS "${IGNORELIST_PATH}") + if(NOT EXISTS "${ignorelist_path}") message( FATAL_ERROR - "Sanitizer ignorelist not found: ${IGNORELIST_PATH}" + "Sanitizer ignorelist not found: ${ignorelist_path}" ) endif() - - list( - APPEND SANITIZERS_COMPILE_FLAGS - "-fsanitize-ignorelist=${IGNORELIST_PATH}" + target_compile_options( + common + INTERFACE + $<$:-fsanitize-ignorelist=${ignorelist_path}> + $<$:-fsanitize-ignorelist=${ignorelist_path}> ) - message(STATUS " Using sanitizer ignorelist: ${IGNORELIST_PATH}") - - # Join sanitizer flags with commas for -fsanitize option - list(JOIN SANITIZER_TYPES "," SANITIZER_TYPES_STR) - - # Add sanitizer to compile and link flags - list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}") - set(SANITIZERS_LINK_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}") + message(STATUS " Ignorelist: ${ignorelist_path}") endif() -message(STATUS " Compile flags: ${SANITIZERS_COMPILE_FLAGS}") -message(STATUS " Link flags: ${SANITIZERS_LINK_FLAGS}") - -# Apply the sanitizer flags to the 'common' interface library This is the same library used by XrplCompiler.cmake -target_compile_options( - common - INTERFACE - $<$:${SANITIZERS_COMPILE_FLAGS}> - $<$:${SANITIZERS_COMPILE_FLAGS}> -) - -# Apply linker flags -target_link_options(common INTERFACE ${SANITIZERS_LINK_FLAGS}) - # Define SANITIZERS macro for BuildInfo.cpp set(sanitizers_list) -if(enable_asan) +if(SANITIZERS MATCHES "address") + set(enable_asan ON) list(APPEND sanitizers_list "ASAN") endif() -if(enable_tsan) +if(SANITIZERS MATCHES "thread") + set(enable_tsan ON) list(APPEND sanitizers_list "TSAN") endif() -if(enable_ubsan) +if(SANITIZERS MATCHES "undefinedbehavior") + set(enable_ubsan ON) list(APPEND sanitizers_list "UBSAN") endif() diff --git a/conan/profiles/ci b/conan/profiles/ci index c4c0898ad55..ae931870266 100644 --- a/conan/profiles/ci +++ b/conan/profiles/ci @@ -1 +1 @@ - include(sanitizers) +include(sanitizers) diff --git a/conan/profiles/sanitizers b/conan/profiles/sanitizers index 800e6eb48c8..4a05fda7341 100644 --- a/conan/profiles/sanitizers +++ b/conan/profiles/sanitizers @@ -3,96 +3,120 @@ include(default) {% set arch = detect_api.detect_arch() %} {% set sanitizers = os.getenv("SANITIZERS") %} -[conf] -{% if sanitizers %} - {% if compiler == "gcc" %} - {% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %} - {% set sanitizer_list = [] %} - {% set defines = [] %} - {% set model_code = "" %} - {% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1", "-Wno-stringop-overflow"] %} - - {% if "address" in sanitizers %} - {% set _ = sanitizer_list.append("address") %} - {% if arch == "x86_64" %} - {% set model_code = "-mcmodel=large" %} - {% endif %} - {% set _ = defines.append("BOOST_USE_ASAN")%} - {% set _ = defines.append("BOOST_USE_UCONTEXT")%} - {% elif "thread" in sanitizers %} - {% set _ = sanitizer_list.append("thread") %} - {% if arch == "x86_64" %} - {% set model_code = "-mcmodel=medium" %} - {% endif %} - {% set _ = extra_cxxflags.append("-Wno-tsan") %} - {% set _ = defines.append("BOOST_USE_TSAN")%} - {% set _ = defines.append("BOOST_USE_UCONTEXT")%} - {% endif %} - - {% if "undefinedbehavior" in sanitizers %} - {% set _ = sanitizer_list.append("undefined") %} - {% set _ = sanitizer_list.append("float-divide-by-zero") %} - {% endif %} - - {% set sanitizer_flags = "-fsanitize=" ~ ",".join(sanitizer_list) ~ " " ~ model_code %} - - tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}'] - tools.build:sharedlinkflags+=['{{sanitizer_flags}}'] - tools.build:exelinkflags+=['{{sanitizer_flags}}'] - tools.build:defines+={{defines}} - {% endif %} - {% elif compiler == "apple-clang" or compiler == "clang" %} - {% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %} - {% set sanitizer_list = [] %} - {% set defines = [] %} - {% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1"] %} - - {% if "address" in sanitizers %} - {% set _ = sanitizer_list.append("address") %} - {% set _ = defines.append("BOOST_USE_ASAN")%} - {% set _ = defines.append("BOOST_USE_UCONTEXT")%} - {% elif "thread" in sanitizers %} - {% set _ = sanitizer_list.append("thread") %} - {% set _ = defines.append("BOOST_USE_TSAN")%} - {% set _ = defines.append("BOOST_USE_UCONTEXT")%} - {% endif %} - - {% if "undefinedbehavior" in sanitizers %} - {% set _ = sanitizer_list.append("undefined") %} - {% set _ = sanitizer_list.append("float-divide-by-zero") %} - {% set _ = sanitizer_list.append("unsigned-integer-overflow") %} - {% endif %} - - {% set sanitizer_flags = "-fsanitize=" ~ ",".join(sanitizer_list) %} - - tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}'] - tools.build:sharedlinkflags+=['{{sanitizer_flags}}'] - tools.build:exelinkflags+=['{{sanitizer_flags}}'] - tools.build:defines+={{defines}} +{% if not sanitizers %} +{# Sanitizers not configured; no additional settings needed #} +{% else %} + +{% if compiler == "msvc" %} + {{ "Sanitizers are not supported on Windows/MSVC. Please unset the SANITIZERS environment variable." }} +{% endif %} + +{% set known_sanitizers = ["address", "thread", "undefinedbehavior"] %} +{% set provided_sanitizers = [] %} +{% for san in sanitizers.split(",") %} + {% set san = san.strip() %} + {% if san not in known_sanitizers %} + {{ "Unknown sanitizer in SANITIZERS: " ~ san }} + {% endif %} + {% set _ = provided_sanitizers.append(san) %} +{% endfor %} + +{% set enable_asan = "address" in provided_sanitizers %} +{% set enable_tsan = "thread" in provided_sanitizers %} +{% set enable_ubsan = "undefinedbehavior" in provided_sanitizers %} + +{% if enable_asan and enable_tsan %} + {{ "AddressSanitizer and ThreadSanitizer are incompatible and cannot be enabled simultaneously." }} +{% endif %} + +{% set sanitizer_types = [] %} +{% set defines = [] %} + +{% if enable_asan %} + {% set _ = sanitizer_types.append("address") %} + {% set _ = defines.append("BOOST_USE_ASAN") %} + {% set _ = defines.append("BOOST_USE_UCONTEXT") %} +{% elif enable_tsan %} + {% set _ = sanitizer_types.append("thread") %} + {% set _ = defines.append("BOOST_USE_TSAN") %} + {% set _ = defines.append("BOOST_USE_UCONTEXT") %} +{% endif %} + +{% if enable_ubsan %} + {% set _ = sanitizer_types.append("undefined") %} + {% set _ = sanitizer_types.append("float-divide-by-zero") %} + {# Clang supports additional UB checks beyond the GCC baseline #} + {% if compiler == "clang" or compiler == "apple-clang" %} + {% set _ = sanitizer_types.append("unsigned-integer-overflow") %} + {% endif %} +{% endif %} + +{# Frame pointer required for meaningful stack traces; -O1 for reasonable performance #} +{% set compile_flags = ["-fno-omit-frame-pointer", "-O1"] %} + +{% if compiler == "gcc" %} + {# Suppress false positive warnings with GCC #} + {% set _ = compile_flags.append("-Wno-stringop-overflow") %} + + {% set relocation_flags = [] %} + + {% if arch == "x86_64" and enable_asan %} + {# Large code model prevents relocation errors in instrumented ASAN binaries #} + {% set _ = compile_flags.append("-mcmodel=large") %} + {% set _ = relocation_flags.append("-mcmodel=large") %} + {% elif enable_tsan %} + {# GCC doesn't support atomic_thread_fence with TSAN; suppress warnings #} + {% set _ = compile_flags.append("-Wno-tsan") %} + {% if arch == "x86_64" %} + {# Medium code model for TSAN; large is incompatible #} + {% set _ = compile_flags.append("-mcmodel=medium") %} + {% set _ = relocation_flags.append("-mcmodel=medium") %} {% endif %} {% endif %} + + {% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %} + {% set _ = compile_flags.append(fsanitize) %} + {% set _ = relocation_flags.append(fsanitize) %} + + {% set sanitizer_compiler_flags = " ".join(compile_flags) %} + {% set sanitizer_linker_flags = " ".join(relocation_flags) %} +{% elif compiler == "clang" or compiler == "apple-clang" %} + {% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %} + {% set _ = compile_flags.append(fsanitize) %} + + {% set sanitizer_compiler_flags = " ".join(compile_flags) %} + {% set sanitizer_linker_flags = fsanitize %} {% endif %} +[conf] +tools.build:defines+={{defines}} +tools.build:cxxflags+=['{{sanitizer_compiler_flags}}'] +tools.build:sharedlinkflags+=['{{sanitizer_linker_flags}}'] +tools.build:exelinkflags+=['{{sanitizer_linker_flags}}'] + tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"] +# &: means "apply only to the consumer/root package" +&:tools.cmake.cmaketoolchain:extra_variables={"SANITIZERS": "{{sanitizers}}", "SANITIZERS_COMPILER_FLAGS": "{{sanitizer_compiler_flags}}", "SANITIZERS_LINKER_FLAGS": "{{sanitizer_linker_flags}}"} + [options] -{% if sanitizers %} - {% if "address" in sanitizers %} - # Build Boost.Context with ucontext backend (not fcontext) so that - # ASAN fiber-switching annotations (__sanitizer_start/finish_switch_fiber) - # are compiled into the library. fcontext (assembly) has no ASAN support. - # define=BOOST_USE_ASAN=1 is critical: it must be defined when building - # Boost.Context itself so the ucontext backend compiles in the ASAN annotations. - boost/*:extra_b2_flags=context-impl=ucontext address-sanitizer=on define=BOOST_USE_ASAN=1 - boost/*:without_context=False - # Boost stacktrace fails to build with some sanitizers - boost/*:without_stacktrace=True - {% elif "thread" in sanitizers %} - # Build Boost.Context with ucontext backend for TSAN. fcontext (assembly) - # has no TSAN annotations, so without this the BOOST_USE_TSAN/BOOST_USE_UCONTEXT - # defines in [conf] would be ineffective. - boost/*:extra_b2_flags=context-impl=ucontext thread-sanitizer=on define=BOOST_USE_TSAN=1 - boost/*:without_context=False - boost/*:without_stacktrace=True - {% endif %} +{% if enable_asan %} + # Build Boost.Context with ucontext backend (not fcontext) so that + # ASAN fiber-switching annotations (__sanitizer_start/finish_switch_fiber) + # are compiled into the library. fcontext (assembly) has no ASAN support. + # define=BOOST_USE_ASAN=1 is critical: it must be defined when building + # Boost.Context itself so the ucontext backend compiles in the ASAN annotations. + boost/*:extra_b2_flags=context-impl=ucontext address-sanitizer=on define=BOOST_USE_ASAN=1 + boost/*:without_context=False + # Boost stacktrace fails to build with some sanitizers + boost/*:without_stacktrace=True +{% elif enable_tsan %} + # Build Boost.Context with ucontext backend for TSAN. fcontext (assembly) + # has no TSAN annotations, so without this the BOOST_USE_TSAN/BOOST_USE_UCONTEXT + # defines in [conf] would be ineffective. + boost/*:extra_b2_flags=context-impl=ucontext thread-sanitizer=on define=BOOST_USE_TSAN=1 + boost/*:without_context=False + boost/*:without_stacktrace=True +{% endif %} + {% endif %} diff --git a/cspell.config.yaml b/cspell.config.yaml index e2c1bad46c4..8b8589a8c9b 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -71,6 +71,7 @@ words: - citardauq - clawback - clawbacks + - cmaketoolchain - coeffs - coldwallet - compr diff --git a/docs/build/sanitizers.md b/docs/build/sanitizers.md index e4949bcf868..6e7284fd062 100644 --- a/docs/build/sanitizers.md +++ b/docs/build/sanitizers.md @@ -1,15 +1,17 @@ # Sanitizer Configuration for Xrpld -This document explains how to properly configure and run sanitizers (AddressSanitizer, undefinedbehaviorSanitizer, ThreadSanitizer) with the xrpld project. +This document explains how to properly configure and run sanitizers (`AddressSanitizer`, `UndefinedBehaviorSanitizer`, `ThreadSanitizer`) with the xrpld project. Corresponding suppression files are located in the `sanitizers/suppressions` directory. +> [!CAUTION] +> Do not mix Address and Thread sanitizers - they are incompatible. +> Also, we don't yet support MSVC sanitizers, so this is only for Clang/GCC builds. + - [Sanitizer Configuration for Xrpld](#sanitizer-configuration-for-xrpld) - [Building with Sanitizers](#building-with-sanitizers) - [Summary](#summary) - [Build steps:](#build-steps) - [Install dependencies](#install-dependencies) - - [Call CMake](#call-cmake) - - [Build](#build) - [Running Tests with Sanitizers](#running-tests-with-sanitizers) - [AddressSanitizer (ASAN)](#addresssanitizer-asan) - [ThreadSanitizer (TSan)](#threadsanitizer-tsan) @@ -33,9 +35,13 @@ Corresponding suppression files are located in the `sanitizers/suppressions` dir Follow the same instructions as mentioned in [BUILD.md](../../BUILD.md) but with the following changes: 1. Make sure you have a clean build directory. -2. Set the `SANITIZERS` environment variable before calling conan install and cmake. Only set it once. Make sure both conan and cmake read the same values. +2. Set the `SANITIZERS` environment variable before calling `conan install`. Only set it once. Example: `export SANITIZERS=address,undefinedbehavior` -3. Optionally use `--profile:all sanitizers` with Conan to build dependencies with sanitizer instrumentation. [!NOTE]Building with sanitizer-instrumented dependencies is slower but produces fewer false positives. +3. Use `--profile:all sanitizers` with Conan to build dependencies with sanitizer instrumentation. + + > [!NOTE] + > Building with sanitizer-instrumented dependencies is slower but produces fewer false positives. + 4. Set `ASAN_OPTIONS`, `LSAN_OPTIONS`, `UBSAN_OPTIONS` and `TSAN_OPTIONS` environment variables to configure sanitizer behavior when running executables. [More details below](#running-tests-with-sanitizers). --- @@ -51,36 +57,13 @@ cd .build #### Install dependencies -The `SANITIZERS` environment variable is used by both Conan and CMake. +The `SANITIZERS` environment variable is used during `conan install` command. ```bash -export SANITIZERS=address,undefinedbehavior -# Standard build (without instrumenting dependencies) -conan install .. --output-folder . --build missing --settings build_type=Debug - -# Or with sanitizer-instrumented dependencies (takes longer but fewer false positives) -conan install .. --output-folder . --profile:all sanitizers --build missing --settings build_type=Debug +SANITIZERS=address,undefinedbehavior conan install .. --output-folder . --build missing --settings build_type=Debug --profile:all sanitizers ``` -[!CAUTION] -Do not mix Address and Thread sanitizers - they are incompatible. - -Since you already set the `SANITIZERS` environment variable when running Conan, same values will be read for the next part. - -#### Call CMake - -```bash -cmake .. -G Ninja \ - -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ - -DCMAKE_BUILD_TYPE=Debug \ - -Dtests=ON -Dxrpld=ON -``` - -#### Build - -```bash -cmake --build . --parallel 4 -``` +Proceed with the rest of the build instructions as mentioned in [BUILD.md](../../BUILD.md). ## Running Tests with Sanitizers