[clang] Fix some static initialization race-conditions (PR #181367)

Richard Dzenis via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 13 06:41:33 PST 2026


https://github.com/RIscRIpt created https://github.com/llvm/llvm-project/pull/181367

These problems were found when I was investigating why using different CompilerInstances in different threads causes crashes. I didn't find the root cause of my problem, but I spotted these potential problems.

See individual commits.

>From b13d48cd827f2eed3fdc3c1eef7d0495a6f8bd70 Mon Sep 17 00:00:00 2001
From: Richard Dzenis <richard at dzenis.dev>
Date: Fri, 13 Feb 2026 15:46:24 +0200
Subject: [PATCH 1/4] [clang][StaticAnalyzer] Fix static init in findKnownClass

Prior to this patch two threads running in findKnownClass could result
in a race condition.
---
 .../Checkers/BasicObjCFoundationChecks.cpp       | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index e682c4ef80896..0c48d77679226 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -70,16 +70,12 @@ enum FoundationClass {
 
 static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID,
                                       bool IncludeSuperclasses = true) {
-  static llvm::StringMap<FoundationClass> Classes;
-  if (Classes.empty()) {
-    Classes["NSArray"] = FC_NSArray;
-    Classes["NSDictionary"] = FC_NSDictionary;
-    Classes["NSEnumerator"] = FC_NSEnumerator;
-    Classes["NSNull"] = FC_NSNull;
-    Classes["NSOrderedSet"] = FC_NSOrderedSet;
-    Classes["NSSet"] = FC_NSSet;
-    Classes["NSString"] = FC_NSString;
-  }
+  static llvm::StringMap<FoundationClass> Classes{
+      {"NSArray", FC_NSArray},           {"NSDictionary", FC_NSDictionary},
+      {"NSEnumerator", FC_NSEnumerator}, {"NSNull", FC_NSNull},
+      {"NSOrderedSet", FC_NSOrderedSet}, {"NSSet", FC_NSSet},
+      {"NSString", FC_NSString},
+  };
 
   // FIXME: Should we cache this at all?
   FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());

>From 53df8baa67bc7e4dffb8232d4fe936e5d312720f Mon Sep 17 00:00:00 2001
From: Richard Dzenis <richard at dzenis.dev>
Date: Fri, 13 Feb 2026 15:51:41 +0200
Subject: [PATCH 2/4] [clang][CodeGen] Correctly restore diagnostic handler in
 HandleTranslationUnit

Prior to this patch an early exit from HandleTranslationUnit could
result in not restoring previous diagnostic handler.
---
 clang/lib/CodeGen/CodeGenAction.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index a5ef4ac9d361d..29dcabd1b0971 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -248,6 +248,8 @@ void BackendConsumer::HandleTranslationUnit(ASTContext &C) {
   LLVMContext &Ctx = getModule()->getContext();
   std::unique_ptr<DiagnosticHandler> OldDiagnosticHandler =
     Ctx.getDiagnosticHandler();
+  llvm::scope_exit RestoreDiagnosticHandler(
+      [&]() { Ctx.setDiagnosticHandler(std::move(OldDiagnosticHandler)); });
   Ctx.setDiagnosticHandler(std::make_unique<ClangDiagnosticHandler>(
       CodeGenOpts, this));
 
@@ -311,8 +313,6 @@ void BackendConsumer::HandleTranslationUnit(ASTContext &C) {
                     C.getTargetInfo().getDataLayoutString(), getModule(),
                     Action, FS, std::move(AsmOutStream), this);
 
-  Ctx.setDiagnosticHandler(std::move(OldDiagnosticHandler));
-
   if (OptRecordFile)
     OptRecordFile->keep();
 }

>From af2c8d2a30d3e6484a565743c054996e083b9870 Mon Sep 17 00:00:00 2001
From: Richard Dzenis <richard at dzenis.dev>
Date: Thu, 12 Feb 2026 20:07:16 +0200
Subject: [PATCH 3/4] [clang][Stmt] Fix StmtClassNameTable array initialization

Prior to this patch there was a race condition when two threads check
`if (Initialized)` at the same time.
---
 clang/lib/AST/Stmt.cpp | 43 +++++++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 22 deletions(-)

diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp
index 5b745dd3c43f5..5715546d31dbe 100644
--- a/clang/lib/AST/Stmt.cpp
+++ b/clang/lib/AST/Stmt.cpp
@@ -57,26 +57,19 @@ using namespace clang;
 #define ABSTRACT_STMT(STMT)
 #include "clang/AST/StmtNodes.inc"
 
-static struct StmtClassNameTable {
+struct StmtClassNameTable {
   const char *Name;
   unsigned Counter;
   unsigned Size;
-} StmtClassInfo[Stmt::lastStmtConstant+1];
+};
 
 static StmtClassNameTable &getStmtInfoTableEntry(Stmt::StmtClass E) {
-  static bool Initialized = false;
-  if (Initialized)
-    return StmtClassInfo[E];
-
-  // Initialize the table on the first use.
-  Initialized = true;
+  static StmtClassNameTable stmtClassInfo[Stmt::lastStmtConstant + 1] = {
 #define ABSTRACT_STMT(STMT)
-#define STMT(CLASS, PARENT) \
-  StmtClassInfo[(unsigned)Stmt::CLASS##Class].Name = #CLASS;    \
-  StmtClassInfo[(unsigned)Stmt::CLASS##Class].Size = sizeof(CLASS);
+#define STMT(CLASS, PARENT) {#CLASS, 0, sizeof(CLASS)},
 #include "clang/AST/StmtNodes.inc"
-
-  return StmtClassInfo[E];
+  };
+  return stmtClassInfo[E];
 }
 
 void *Stmt::operator new(size_t bytes, const ASTContext& C,
@@ -85,7 +78,7 @@ void *Stmt::operator new(size_t bytes, const ASTContext& C,
 }
 
 const char *Stmt::getStmtClassName() const {
-  return getStmtInfoTableEntry((StmtClass) StmtBits.sClass).Name;
+  return getStmtInfoTableEntry(static_cast<StmtClass>(StmtBits.sClass)).Name;
 }
 
 // Check that no statement / expression class is polymorphic. LLVM style RTTI
@@ -113,19 +106,25 @@ void Stmt::PrintStats() {
   unsigned sum = 0;
   llvm::errs() << "\n*** Stmt/Expr Stats:\n";
   for (int i = 0; i != Stmt::lastStmtConstant+1; i++) {
-    if (StmtClassInfo[i].Name == nullptr) continue;
-    sum += StmtClassInfo[i].Counter;
+    StmtClassNameTable &entry =
+        getStmtInfoTableEntry(static_cast<Stmt::StmtClass>(i));
+    if (entry.Name == nullptr)
+      continue;
+    sum += entry.Counter;
   }
   llvm::errs() << "  " << sum << " stmts/exprs total.\n";
   sum = 0;
   for (int i = 0; i != Stmt::lastStmtConstant+1; i++) {
-    if (StmtClassInfo[i].Name == nullptr) continue;
-    if (StmtClassInfo[i].Counter == 0) continue;
-    llvm::errs() << "    " << StmtClassInfo[i].Counter << " "
-                 << StmtClassInfo[i].Name << ", " << StmtClassInfo[i].Size
-                 << " each (" << StmtClassInfo[i].Counter*StmtClassInfo[i].Size
+    StmtClassNameTable &entry =
+        getStmtInfoTableEntry(static_cast<Stmt::StmtClass>(i));
+    if (entry.Name == nullptr)
+      continue;
+    if (entry.Counter == 0)
+      continue;
+    llvm::errs() << "    " << entry.Counter << " " << entry.Name << ", "
+                 << entry.Size << " each (" << entry.Counter * entry.Size
                  << " bytes)\n";
-    sum += StmtClassInfo[i].Counter*StmtClassInfo[i].Size;
+    sum += entry.Counter * entry.Size;
   }
 
   llvm::errs() << "Total bytes = " << sum << "\n";

>From 139a461214436e61187dbe2df4ad6d48b4fcd264 Mon Sep 17 00:00:00 2001
From: Richard Dzenis <richard at dzenis.dev>
Date: Thu, 12 Feb 2026 20:27:30 +0200
Subject: [PATCH 4/4] [clang][ParsedAttrInfo] Fix race condition in
 getAttributePluginInstances

Prior to this patch two threads could enter under if (empty()) check.
---
 clang/lib/Basic/ParsedAttrInfo.cpp | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Basic/ParsedAttrInfo.cpp b/clang/lib/Basic/ParsedAttrInfo.cpp
index 16fa314b642b9..d5b17b34b6e3a 100644
--- a/clang/lib/Basic/ParsedAttrInfo.cpp
+++ b/clang/lib/Basic/ParsedAttrInfo.cpp
@@ -20,13 +20,16 @@ using namespace clang;
 
 LLVM_INSTANTIATE_REGISTRY(ParsedAttrInfoRegistry)
 
+static std::list<std::unique_ptr<ParsedAttrInfo>> instantiateEntries() {
+  std::list<std::unique_ptr<ParsedAttrInfo>> Instances;
+  for (const auto &It : ParsedAttrInfoRegistry::entries())
+    Instances.emplace_back(It.instantiate());
+  return Instances;
+}
+
 const std::list<std::unique_ptr<ParsedAttrInfo>> &
 clang::getAttributePluginInstances() {
-  static llvm::ManagedStatic<std::list<std::unique_ptr<ParsedAttrInfo>>>
-      PluginAttrInstances;
-  if (PluginAttrInstances->empty())
-    for (const auto &It : ParsedAttrInfoRegistry::entries())
-      PluginAttrInstances->emplace_back(It.instantiate());
-
-  return *PluginAttrInstances;
+  static std::list<std::unique_ptr<ParsedAttrInfo>> Instances =
+      instantiateEntries();
+  return Instances;
 }



More information about the cfe-commits mailing list