https://github.com/BStott6 updated https://github.com/llvm/llvm-project/pull/166542
>From 8b4ff4f264b471e77da96b8161829a9208a316bb Mon Sep 17 00:00:00 2001 From: BStott <[email protected]> Date: Wed, 5 Nov 2025 12:08:56 +0000 Subject: [PATCH 1/4] [Clang] Warn when `std::atomic_thread_fence` is used with `fsanitize=thread` --- clang/include/clang/Basic/DiagnosticGroups.td | 2 ++ .../clang/Basic/DiagnosticSemaKinds.td | 3 ++ clang/include/clang/Sema/Sema.h | 2 ++ clang/lib/Sema/SemaChecking.cpp | 34 +++++++++++++++++++ clang/test/SemaCXX/warn-tsan-atomic-fence.cpp | 11 ++++++ 5 files changed, 52 insertions(+) create mode 100644 clang/test/SemaCXX/warn-tsan-atomic-fence.cpp diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 8aa3489a2a62b..489b9f5ec552b 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1769,3 +1769,5 @@ def ExplicitSpecializationStorageClass : DiagGroup<"explicit-specialization-stor // A warning for options that enable a feature that is not yet complete def ExperimentalOption : DiagGroup<"experimental-option">; + +def TSan : DiagGroup<"tsan">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4e369be0bbb92..928b52b3d41f0 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -112,6 +112,9 @@ def warn_max_unsigned_zero : Warning< "%select{a value and unsigned zero|unsigned zero and a value}0 " "is always equal to the other value">, InGroup<MaxUnsignedZero>; +def warn_atomic_thread_fence_with_tsan : Warning< + "`std::atomic_thread_fence` is not supported with `-fsanitize=thread`">, + InGroup<TSan>; def note_remove_max_call : Note< "remove call to max function and unsigned zero argument">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index c67ed99b1f49e..99e486179dd2e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3033,6 +3033,8 @@ class Sema final : public SemaBase { void CheckMaxUnsignedZero(const CallExpr *Call, const FunctionDecl *FDecl); + void CheckUseOfAtomicThreadFenceWithTSan(const CallExpr *Call, const FunctionDecl *FDecl); + /// Check for dangerous or invalid arguments to memset(). /// /// This issues warnings on known problematic, dangerous or unspecified diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index ad2c2e4a97bb9..13b61ea453f80 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -45,6 +45,7 @@ #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/NoSanitizeList.h" #include "clang/Basic/OpenCLOptions.h" #include "clang/Basic/OperatorKinds.h" #include "clang/Basic/PartialDiagnostic.h" @@ -4100,6 +4101,7 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, CheckAbsoluteValueFunction(TheCall, FDecl); CheckMaxUnsignedZero(TheCall, FDecl); CheckInfNaNFunction(TheCall, FDecl); + CheckUseOfAtomicThreadFenceWithTSan(TheCall, FDecl); if (getLangOpts().ObjC) ObjC().DiagnoseCStringFormatDirectiveInCFAPI(FDecl, Args, NumArgs); @@ -9822,6 +9824,38 @@ void Sema::CheckMaxUnsignedZero(const CallExpr *Call, << FixItHint::CreateRemoval(RemovalRange); } +//===--- CHECK: Warn on use of `std::atomic_thread_fence` with TSan. ------===// +void Sema::CheckUseOfAtomicThreadFenceWithTSan(const CallExpr *Call, + const FunctionDecl *FDecl) { + // Thread sanitizer currently does not support `std::atomic_thread_fence`, + // leading to false positive. Example issue: + // https://github.com/llvm/llvm-project/issues/52942 + + if (!Call || !FDecl) + return; + + if (!IsStdFunction(FDecl, "atomic_thread_fence")) + return; + + // See if TSan is enabled in this function + const auto EnabledTSanMask = + Context.getLangOpts().Sanitize.Mask & (SanitizerKind::Thread); + if (!EnabledTSanMask) + return; + + const auto &NoSanitizeList = Context.getNoSanitizeList(); + if (NoSanitizeList.containsLocation(EnabledTSanMask, + Call->getSourceRange().getBegin())) + // File is excluded + return; + if (NoSanitizeList.containsFunction(EnabledTSanMask, + FDecl->getQualifiedNameAsString())) + // Function is excluded + return; + + Diag(Call->getExprLoc(), diag::warn_atomic_thread_fence_with_tsan); +} + //===--- CHECK: Standard memory functions ---------------------------------===// /// Takes the expression passed to the size_t parameter of functions diff --git a/clang/test/SemaCXX/warn-tsan-atomic-fence.cpp b/clang/test/SemaCXX/warn-tsan-atomic-fence.cpp new file mode 100644 index 0000000000000..b8720260fe1c1 --- /dev/null +++ b/clang/test/SemaCXX/warn-tsan-atomic-fence.cpp @@ -0,0 +1,11 @@ +// RUN: %clang -std=c++17 %s 2>&1 | FileCheck %s --check-prefix=NO-TSAN --allow-empty +// RUN: %clang -std=c++17 -fsanitize=thread %s 2>&1 | FileCheck %s --check-prefix=WITH-TSAN + +// WITH-TSAN: `std::atomic_thread_fence` is not supported with `-fsanitize=thread` +// NO-TSAN-NOT: `std::atomic_thread_fence` is not supported with `-fsanitize=thread` + +#include <atomic> + +int main() { + std::atomic_thread_fence(std::memory_order::memory_order_relaxed); +} >From 25585490503c017ed2055765cff01c1b7b8482d1 Mon Sep 17 00:00:00 2001 From: BStott <[email protected]> Date: Thu, 6 Nov 2025 10:57:30 +0000 Subject: [PATCH 2/4] Implement suggestions, fix handling of ignored functions, handle nosanitize attributes --- clang/docs/ReleaseNotes.rst | 2 + clang/include/clang/Basic/DiagnosticGroups.td | 2 - .../clang/Basic/DiagnosticSemaKinds.td | 4 +- clang/lib/Sema/SemaChecking.cpp | 26 ++++++++--- clang/test/SemaCXX/warn-tsan-atomic-fence.cpp | 45 ++++++++++++++++--- 5 files changed, 62 insertions(+), 17 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 92fc9381a5868..d5a1a7fa9b5fc 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -349,6 +349,8 @@ Improvements to Clang's diagnostics - Fixed false positives in ``-Waddress-of-packed-member`` diagnostics when potential misaligned members get processed before they can get discarded. (#GH144729) +- Clang now emits a warning when `std::atomic_thread_fence` is used with `-fsanitize=thread` as this can + lead to false positives. (This can be disabled with ``-Wno-tsan``) - Clang now emits dignostic with correct message in case of assigning to const reference captured in lambda. (#GH105647) diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 489b9f5ec552b..8aa3489a2a62b 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1769,5 +1769,3 @@ def ExplicitSpecializationStorageClass : DiagGroup<"explicit-specialization-stor // A warning for options that enable a feature that is not yet complete def ExperimentalOption : DiagGroup<"experimental-option">; - -def TSan : DiagGroup<"tsan">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 928b52b3d41f0..c90b018f64f81 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -113,8 +113,8 @@ def warn_max_unsigned_zero : Warning< "is always equal to the other value">, InGroup<MaxUnsignedZero>; def warn_atomic_thread_fence_with_tsan : Warning< - "`std::atomic_thread_fence` is not supported with `-fsanitize=thread`">, - InGroup<TSan>; + "'std::atomic_thread_fence' is not supported with '-fsanitize=thread'">, + InGroup<DiagGroup<"tsan">>; def note_remove_max_call : Note< "remove call to max function and unsigned zero argument">; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 13b61ea453f80..8621e1db4e561 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -9828,7 +9828,7 @@ void Sema::CheckMaxUnsignedZero(const CallExpr *Call, void Sema::CheckUseOfAtomicThreadFenceWithTSan(const CallExpr *Call, const FunctionDecl *FDecl) { // Thread sanitizer currently does not support `std::atomic_thread_fence`, - // leading to false positive. Example issue: + // leading to false positives. Example issue: // https://github.com/llvm/llvm-project/issues/52942 if (!Call || !FDecl) @@ -9837,22 +9837,34 @@ void Sema::CheckUseOfAtomicThreadFenceWithTSan(const CallExpr *Call, if (!IsStdFunction(FDecl, "atomic_thread_fence")) return; - // See if TSan is enabled in this function + // Check that TSan is enabled in this context const auto EnabledTSanMask = Context.getLangOpts().Sanitize.Mask & (SanitizerKind::Thread); if (!EnabledTSanMask) return; + // Check that the file isn't in the no-sanitize list const auto &NoSanitizeList = Context.getNoSanitizeList(); if (NoSanitizeList.containsLocation(EnabledTSanMask, Call->getSourceRange().getBegin())) - // File is excluded - return; - if (NoSanitizeList.containsFunction(EnabledTSanMask, - FDecl->getQualifiedNameAsString())) - // Function is excluded return; + // Check that the calling function: + // - Does not have any attributes preventing TSan checking + // - Is not in the ignore list + if (const NamedDecl *Caller = getCurFunctionOrMethodDecl()) { + if (Caller->hasAttr<DisableSanitizerInstrumentationAttr>()) + return; + + for (const auto *Attr : Caller->specific_attrs<NoSanitizeAttr>()) + if (Attr->getMask() & SanitizerKind::Thread) + return; + + if (NoSanitizeList.containsFunction(EnabledTSanMask, + Caller->getQualifiedNameAsString())) + return; + } + Diag(Call->getExprLoc(), diag::warn_atomic_thread_fence_with_tsan); } diff --git a/clang/test/SemaCXX/warn-tsan-atomic-fence.cpp b/clang/test/SemaCXX/warn-tsan-atomic-fence.cpp index b8720260fe1c1..ae0afbed749d7 100644 --- a/clang/test/SemaCXX/warn-tsan-atomic-fence.cpp +++ b/clang/test/SemaCXX/warn-tsan-atomic-fence.cpp @@ -1,11 +1,44 @@ -// RUN: %clang -std=c++17 %s 2>&1 | FileCheck %s --check-prefix=NO-TSAN --allow-empty -// RUN: %clang -std=c++17 -fsanitize=thread %s 2>&1 | FileCheck %s --check-prefix=WITH-TSAN +// No warnings in regular compile +// RUN: %clang_cc1 -verify=no-tsan %s -// WITH-TSAN: `std::atomic_thread_fence` is not supported with `-fsanitize=thread` -// NO-TSAN-NOT: `std::atomic_thread_fence` is not supported with `-fsanitize=thread` +// Emits warning with `-fsanitize=thread` +// RUN: %clang_cc1 -verify=with-tsan -fsanitize=thread %s -#include <atomic> +// No warnings if `-Wno-tsan` is passed +// RUN: %clang_cc1 -verify=no-tsan -fsanitize=thread -Wno-tsan %s + +// Ignoring function +// RUN: echo "fun:main" > %t +// RUN: %clang_cc1 -verify=no-tsan -fsanitize=thread -fsanitize-ignorelist=%t %s + +// Ignoring source file +// RUN: echo "src:%s" > %t +// RUN: %clang_cc1 -verify=no-tsan -fsanitize=thread -fsanitize-ignorelist=%t %s + +// no-tsan-no-diagnostics + +namespace std { + enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, + }; + void atomic_thread_fence(memory_order) {} +}; + +__attribute__((no_sanitize("thread"))) +void ignore_1() { + std::atomic_thread_fence(std::memory_order_relaxed); +} + +__attribute__((no_sanitize_thread)) +void ignore_2() { + std::atomic_thread_fence(std::memory_order_relaxed); +} int main() { - std::atomic_thread_fence(std::memory_order::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_relaxed); // with-tsan-warning {{'std::atomic_thread_fence' is not supported with '-fsanitize=thread'}} } >From 30835da9bd16ade2eecf20866a418e18382837d2 Mon Sep 17 00:00:00 2001 From: BStott <[email protected]> Date: Thu, 6 Nov 2025 16:44:04 +0000 Subject: [PATCH 3/4] Fix formatting mistake --- clang/include/clang/Sema/Sema.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 99e486179dd2e..d78d912c16104 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3033,7 +3033,8 @@ class Sema final : public SemaBase { void CheckMaxUnsignedZero(const CallExpr *Call, const FunctionDecl *FDecl); - void CheckUseOfAtomicThreadFenceWithTSan(const CallExpr *Call, const FunctionDecl *FDecl); + void CheckUseOfAtomicThreadFenceWithTSan(const CallExpr *Call, + const FunctionDecl *FDecl); /// Check for dangerous or invalid arguments to memset(). /// >From 8a1b662160b455b10c1503dbc4cf154c947f4e02 Mon Sep 17 00:00:00 2001 From: BStott <[email protected]> Date: Fri, 7 Nov 2025 17:40:46 +0000 Subject: [PATCH 4/4] Improve test, account for lambda attributes, use mangled name to lookup in ignore list --- clang/lib/Sema/SemaChecking.cpp | 38 +++++++++++++------ clang/test/SemaCXX/warn-tsan-atomic-fence.cpp | 24 ++++++++---- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 8621e1db4e561..52ba36ab93642 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -29,6 +29,7 @@ #include "clang/AST/ExprObjC.h" #include "clang/AST/FormatString.h" #include "clang/AST/IgnoreExpr.h" +#include "clang/AST/Mangle.h" #include "clang/AST/NSAPI.h" #include "clang/AST/NonTrivialTypeVisitor.h" #include "clang/AST/OperationKinds.h" @@ -9849,21 +9850,36 @@ void Sema::CheckUseOfAtomicThreadFenceWithTSan(const CallExpr *Call, Call->getSourceRange().getBegin())) return; - // Check that the calling function: + std::unique_ptr<MangleContext> MC(Context.createMangleContext()); + + // Check that the calling function or lambda: // - Does not have any attributes preventing TSan checking // - Is not in the ignore list - if (const NamedDecl *Caller = getCurFunctionOrMethodDecl()) { - if (Caller->hasAttr<DisableSanitizerInstrumentationAttr>()) - return; + auto IsNotSanitized = [&](NamedDecl *Decl) { + const auto SpecificAttrs = Decl->specific_attrs<NoSanitizeAttr>(); + const auto IsNoSanitizeThreadAttr = [](NoSanitizeAttr *Attr) { + return static_cast<bool>(Attr->getMask() & SanitizerKind::Thread); + }; - for (const auto *Attr : Caller->specific_attrs<NoSanitizeAttr>()) - if (Attr->getMask() & SanitizerKind::Thread) - return; + // Get mangled name for ignorelist lookup + std::string MangledName; + if (MC->shouldMangleDeclName(Decl)) { + llvm::raw_string_ostream S = llvm::raw_string_ostream(MangledName); + MC->mangleName(Decl, S); + } else { + MangledName = Decl->getName(); + } - if (NoSanitizeList.containsFunction(EnabledTSanMask, - Caller->getQualifiedNameAsString())) - return; - } + return Decl && + (Decl->hasAttr<DisableSanitizerInstrumentationAttr>() || + std::any_of(SpecificAttrs.begin(), SpecificAttrs.end(), + IsNoSanitizeThreadAttr) || + NoSanitizeList.containsFunction(EnabledTSanMask, MangledName)); + }; + if (IsNotSanitized(getCurFunctionOrMethodDecl())) + return; + if (IsNotSanitized(getCurFunctionDecl(/*AllowLambdas*/ true))) + return; Diag(Call->getExprLoc(), diag::warn_atomic_thread_fence_with_tsan); } diff --git a/clang/test/SemaCXX/warn-tsan-atomic-fence.cpp b/clang/test/SemaCXX/warn-tsan-atomic-fence.cpp index ae0afbed749d7..a7db3958696db 100644 --- a/clang/test/SemaCXX/warn-tsan-atomic-fence.cpp +++ b/clang/test/SemaCXX/warn-tsan-atomic-fence.cpp @@ -7,8 +7,8 @@ // No warnings if `-Wno-tsan` is passed // RUN: %clang_cc1 -verify=no-tsan -fsanitize=thread -Wno-tsan %s -// Ignoring function -// RUN: echo "fun:main" > %t +// Ignoring func1 +// RUN: echo "fun:*func1*" > %t // RUN: %clang_cc1 -verify=no-tsan -fsanitize=thread -fsanitize-ignorelist=%t %s // Ignoring source file @@ -29,16 +29,26 @@ namespace std { void atomic_thread_fence(memory_order) {} }; +void func1() { // extern "C" to stop name mangling + std::atomic_thread_fence(std::memory_order_relaxed); // with-tsan-warning {{'std::atomic_thread_fence' is not supported with '-fsanitize=thread'}} + + auto lam = []() __attribute__((no_sanitize("thread"))) { + std::atomic_thread_fence(std::memory_order_relaxed); + }; +} + __attribute__((no_sanitize("thread"))) -void ignore_1() { +void func2() { std::atomic_thread_fence(std::memory_order_relaxed); + + auto lam = []() { + std::atomic_thread_fence(std::memory_order_relaxed); + }; } __attribute__((no_sanitize_thread)) -void ignore_2() { +void func3() { std::atomic_thread_fence(std::memory_order_relaxed); } -int main() { - std::atomic_thread_fence(std::memory_order_relaxed); // with-tsan-warning {{'std::atomic_thread_fence' is not supported with '-fsanitize=thread'}} -} +int main() {} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
