From 8fbf6030ef005b728944176a859d18bfb979e14e Mon Sep 17 00:00:00 2001 From: Vyacheslav Egorov Date: Tue, 26 Aug 2025 01:01:14 -0700 Subject: [PATCH] [tsan] Improve suppression for tzset When running under TSAN disable inlining of LocalTime helper so we can write a suppression using its name. Fixes https://github.com/dart-lang/sdk/issues/61396 TEST=ci Cq-Include-Trybots: luci.dart.try:vm-tsan-linux-release-x64-try,vm-tsan-linux-release-arm64-try Change-Id: I9f07572b5b597343dbc61554c4f287eac630f047 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/446720 Commit-Queue: Slava Egorov Reviewed-by: Alexander Markov --- build/sanitizers/sanitizer_options.cc | 2 +- runtime/vm/os_linux.cc | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/build/sanitizers/sanitizer_options.cc b/build/sanitizers/sanitizer_options.cc index 14a06227e54..6c347210b4c 100644 --- a/build/sanitizers/sanitizer_options.cc +++ b/build/sanitizers/sanitizer_options.cc @@ -29,7 +29,7 @@ SANITIZER_HOOK_ATTRIBUTE const char* __tsan_default_suppressions() { # In some environments tzset_internal is not symbolized correctly so we # also suppress the closest caller which is properly symbolized. race:tzset_internal -race:dart::DN_HelperDateTime_timeZoneOffsetInSeconds +race:dart::LocalTime )"; } #endif // defined(USING_THREAD_SANITIZER) && defined(DART_HOST_OS_LINUX) diff --git a/runtime/vm/os_linux.cc b/runtime/vm/os_linux.cc index 8bdfda024d3..7fc8c422e3e 100644 --- a/runtime/vm/os_linux.cc +++ b/runtime/vm/os_linux.cc @@ -415,10 +415,33 @@ intptr_t OS::ProcessId() { return static_cast(getpid()); } +// TSAN detects data races inside tzset implementation because it does not +// understand low-level synchronization primitives used by libc. We would +// like to suppress these false positives, however writing a suppression +// targeting tzset is hard because +// +// 1. On our bots TSAN fails to properly symbolize +// libc symbols (meaning that we can't simply suppress tzset itself). +// 2. libc is compiled without frame-pointers so TzSet caller is missing +// from the stack trace. +// +// To work-around both issues we create a simple wrapper over TzSet with is +// not inlined to guarantee that LocalTime (the caller of this wrapper) +// appears in the stack trace and we can suppress false positive occurring +// inside it. +#if defined(USING_THREAD_SANITIZER) +DART_NOINLINE +#else +DART_FORCE_INLINE +#endif +static void TzSet() { + tzset(); +} + static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) { time_t seconds = static_cast(seconds_since_epoch); if (seconds != seconds_since_epoch) return false; - tzset(); // Not guaranteed by POSIX to be called by `localtime_r`. + TzSet(); // Not guaranteed by POSIX to be called by `localtime_r`. struct tm* error_code = localtime_r(&seconds, tm_result); return error_code != nullptr; }