diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index bbbfb8e358..7fa7e84d53 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -312,6 +312,8 @@ jobs: [ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$((BUILD_NPROC - 2)) # Sanitizer builds are memory hungry; run the tests single-threaded [ "$SANITIZERS_ON" = "true" ] && BUILD_NPROC=1 + # Never drop below one job (small runners could go to 0 or negative above) + [ "$BUILD_NPROC" -ge 1 ] || BUILD_NPROC=1 # The resolver/preload workaround is only correct for the ASan build: # a regular build doesn't hit the __dn_expand interceptor bug, and must diff --git a/docs/build/sanitizers.md b/docs/build/sanitizers.md index 6e7284fd06..92778fffc0 100644 --- a/docs/build/sanitizers.md +++ b/docs/build/sanitizers.md @@ -97,6 +97,11 @@ export TSAN_OPTIONS="include=sanitizers/suppressions/runtime-tsan-options.txt:su ./xrpld --unittest --unittest-jobs=5 ``` +`runtime-tsan-options.txt` sets `halt_on_error=true`, so the process aborts on +the first unsuppressed report. To collect all reports in one run instead (e.g. +when triaging locally), append `:halt_on_error=false` to `TSAN_OPTIONS`; the +exit code is still non-zero (66) if any report was produced. + More details [here](https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual). ### LeakSanitizer (LSan) diff --git a/sanitizers/suppressions/runtime-tsan-options.txt b/sanitizers/suppressions/runtime-tsan-options.txt index aafe997ea4..aee06c7d80 100644 --- a/sanitizers/suppressions/runtime-tsan-options.txt +++ b/sanitizers/suppressions/runtime-tsan-options.txt @@ -1,3 +1,3 @@ -halt_on_error=false +halt_on_error=true verbosity=1 second_deadlock_stack=1 diff --git a/sanitizers/suppressions/tsan.supp b/sanitizers/suppressions/tsan.supp index df2e200349..47e94dc214 100644 --- a/sanitizers/suppressions/tsan.supp +++ b/sanitizers/suppressions/tsan.supp @@ -3,24 +3,22 @@ # Races in rippled's own code should be fixed, not suppressed. # Boost ASIO / Boost Context false positives -# These are internal to Boost's reactor, fiber context switching, and memory management. +# These are internal to Boost's reactor and the fiber context switching used +# by boost::asio::spawn (server Spawn.h, test yield_to.h). +# NOTE: a suppression fires if ANY frame in the report matches, so these +# path patterns can also hide rippled races that merely pass through asio +# (e.g. handlers running on an io_context). Narrow them to specific frames +# once TSAN runs cleanly. race:boost/asio/ race:boost/context/ -race:boost/asio/executor.hpp -race:boost::asio -race:boost::context::fiber::resume -race:boost::asio::detail::spawned_fiber_thread -race:boost::asio::detail::spawned_fiber_thread::suspend_with -race:boost::asio::detail::spawned_fiber_thread::destroy race:__cxxabiv1::manage_exception_state -race:__tsan_memcpy # TSAN's own syscall interceptors and libc internals # These are false positives from TSAN's fd tracking in glibc. # TODO: Tighten these suppressions once TSAN runs cleanly — they may mask # real fd-lifetime bugs in rippled's networking code. race:crtstuff.c -race:pipe +race:__pipe race:epoll_ctl race:epoll_create race:closedir @@ -33,7 +31,6 @@ race:eventfd # C++ standard library internals race:ostreambuf_iterator race:basic_ostream -called_from_lib:libclang_rt # Deadlock false positives in Boost and threading primitives deadlock:pthread_create @@ -71,9 +68,7 @@ deadlock:xrpl::doLedgerAccept deadlock:beast::PropertyStream::Source # Signal/crash suppressions for GCC TSAN instrumentation issues -signal:std::__cxx11::basic_stringbuf signal:basic_stringbuf signal:basic_ostringstream signal:boost::asio::aligned_new signal:boost::asio::detail::memory -signal:execute_native_thread_routine