[clang] [Clang] Warn when `std::atomic_thread_fence` is used with `fsanitize=thread` (PR #166542)
Benjamin Stott via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 6 02:58:00 PST 2025
https://github.com/BStott6 updated https://github.com/llvm/llvm-project/pull/166542
>From 8b4ff4f264b471e77da96b8161829a9208a316bb Mon Sep 17 00:00:00 2001
From: BStott <Benjamin.Stott at sony.com>
Date: Wed, 5 Nov 2025 12:08:56 +0000
Subject: [PATCH 1/2] [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 <Benjamin.Stott at sony.com>
Date: Thu, 6 Nov 2025 10:57:30 +0000
Subject: [PATCH 2/2] 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'}}
}
More information about the cfe-commits
mailing list