diff --git a/.bazelrc b/.bazelrc index aa622afbc..c5c5f1eb8 100644 --- a/.bazelrc +++ b/.bazelrc @@ -117,3 +117,17 @@ test:asan_ubsan_lsan --build_tests_only build:asan --config=asan_ubsan_lsan build:ubsan --config=asan_ubsan_lsan build:lsan --config=asan_ubsan_lsan + +# ThreadSanitizer (cannot be combined with ASan/LSan) +build:tsan --config=with_debug_symbols +build:tsan --copt=-fsanitize=thread +build:tsan --copt=-O1 +build:tsan --linkopt=-fsanitize=thread +# GCC TSan doesn't instrument atomic_thread_fence; suppress the resulting -Wtsan +# warning so it doesn't become a build error in external deps that use -Werror. +build:tsan --cxxopt=-Wno-tsan +build:tsan --platform_suffix=tsan +build:tsan --@score_cpp_policies//sanitizers/flags:sanitizer=tsan +test:tsan --run_under=//tools/sanitizers:tsan_setarch_wrapper +test:tsan --test_tag_filters=-no-tsan +test:tsan --build_tests_only diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 95533dc9b..1403893fa 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -30,7 +30,7 @@ jobs: strategy: fail-fast: false matrix: - config: [asan_ubsan_lsan] + config: [asan_ubsan_lsan, tsan] steps: - name: Checkout code uses: actions/checkout@v4.2.2 diff --git a/README.md b/README.md index 6cda8174e..e38f21602 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,12 @@ ASan + UBSan + LSan (recommended): bazel test --config=asan_ubsan_lsan --config=x86_64-linux //score/... //tests/... ``` +TSan: + +```sh +bazel test --config=tsan --config=x86_64-linux //score/... //tests/... +``` + To build all components with ``score::mw::log`` enabled, use this command: ```sh diff --git a/score/health_monitor/src/cpp/BUILD b/score/health_monitor/src/cpp/BUILD index b069aa2e8..4efca1283 100644 --- a/score/health_monitor/src/cpp/BUILD +++ b/score/health_monitor/src/cpp/BUILD @@ -101,6 +101,7 @@ cc_gtest_unit_test( }), tags = [ "no-asan", + "no-tsan", ], deps = [ "//score/health_monitor/src/cpp:health_monitoring_cc_stub_supervisor", diff --git a/score/health_monitor/src/rust/BUILD b/score/health_monitor/src/rust/BUILD index 202ec5ba3..498cf3bce 100644 --- a/score/health_monitor/src/rust/BUILD +++ b/score/health_monitor/src/rust/BUILD @@ -71,6 +71,7 @@ rust_test( }), tags = [ "no-asan", + "no-tsan", ], deps = ["@score_baselibs_rust//src/log/stdout_logger"], ) @@ -96,6 +97,7 @@ rust_test( tags = [ "loom", "no-asan", + "no-tsan", ], target_compatible_with = ["@platforms//os:linux"], deps = [ diff --git a/score/launch_manager/daemon/src/process_state_client/BUILD b/score/launch_manager/daemon/src/process_state_client/BUILD index a2c720fb6..2c576c844 100644 --- a/score/launch_manager/daemon/src/process_state_client/BUILD +++ b/score/launch_manager/daemon/src/process_state_client/BUILD @@ -75,6 +75,7 @@ cc_library( cc_test( name = "process_state_client_ut", srcs = ["process_state_client_ut.cpp"], + tags = ["no-tsan"], deps = [ ":process_state_client", "//score/launch_manager/daemon/src/process_state_client/details:process_state_receiver", diff --git a/tests/utils/bazel/integration.bzl b/tests/utils/bazel/integration.bzl index e8646d623..42a94930b 100644 --- a/tests/utils/bazel/integration.bzl +++ b/tests/utils/bazel/integration.bzl @@ -28,7 +28,7 @@ def integration_test(name, srcs, test_binaries, args = [], deps = [], data = [], }) # The test container does not ship the sanitizer runtime; daemon fails to start. - sanitizer_tags = ["no-asan"] + sanitizer_tags = ["no-asan", "no-tsan"] tags = kwargs.pop("tags", []) + sanitizer_tags py_itf_test( diff --git a/tools/sanitizers/BUILD b/tools/sanitizers/BUILD new file mode 100644 index 000000000..e588883d5 --- /dev/null +++ b/tools/sanitizers/BUILD @@ -0,0 +1,32 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load(":wrapper.bzl", "expand_executable_template") + +expand_executable_template( + name = "tsan_setarch_wrapper_sh", + out = "tsan_setarch_wrapper.sh", + template = "tsan_setarch_wrapper.sh.tpl", + wrapper = "@score_cpp_policies//sanitizers:wrapper", +) + +sh_binary( + name = "tsan_setarch_wrapper", + srcs = [":tsan_setarch_wrapper_sh"], + data = [ + "@bazel_tools//tools/bash/runfiles", + "@score_cpp_policies//sanitizers:wrapper", + ], + target_compatible_with = ["@platforms//os:linux"], + visibility = ["//visibility:public"], +) diff --git a/tools/sanitizers/tsan_setarch_wrapper.sh.tpl b/tools/sanitizers/tsan_setarch_wrapper.sh.tpl new file mode 100644 index 000000000..b279ab179 --- /dev/null +++ b/tools/sanitizers/tsan_setarch_wrapper.sh.tpl @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# --- begin runfiles.bash initialization v3 --- +set -uo pipefail +f=bazel_tools/tools/bash/runfiles/runfiles.bash +source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ + source "$0.runfiles/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + { echo >&2 "ERROR: cannot find $f"; exit 1; }; f= +# --- end runfiles.bash initialization v3 --- + +exec setarch "$(uname -m)" -R -- "$(rlocation %WRAPPER_RLOCATION%)" "$@" diff --git a/tools/sanitizers/wrapper.bzl b/tools/sanitizers/wrapper.bzl new file mode 100644 index 000000000..4ad9c300c --- /dev/null +++ b/tools/sanitizers/wrapper.bzl @@ -0,0 +1,41 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +"""Generates an executable script from a template, with the runfiles path of +another executable target baked in as a substitution.""" + +def _expand_executable_template_impl(ctx): + out = ctx.actions.declare_file(ctx.attr.out) + wrapper_label = ctx.attr.wrapper.label + ctx.actions.expand_template( + template = ctx.file.template, + output = out, + substitutions = { + "%WRAPPER_RLOCATION%": "{}/{}/{}".format( + wrapper_label.workspace_name, + wrapper_label.package, + wrapper_label.name, + ), + }, + is_executable = True, + ) + return [DefaultInfo(files = depset([out]))] + +expand_executable_template = rule( + implementation = _expand_executable_template_impl, + attrs = { + "out": attr.string(mandatory = True), + "template": attr.label(allow_single_file = True, mandatory = True), + "wrapper": attr.label(mandatory = True), + }, +)