https://github.com/skadewdl3 created 
https://github.com/llvm/llvm-project/pull/219137

FIxes #213520.

`CodeGenModule::Structor::AssociatedData` is a raw `llvm::Constant*` used as 
the COMDAT key in `@llvm.global_ctors`. Since it isn't an IR used, a RAUW call 
from `GetOrCreateLLVMGlobal` can leave it dangling. Reading this pointer leads 
to a segfault down the line in `AsmPrinter`.

The error would not surface in GCC, but would do so in Clang 22.1.2. The bug is 
reproducible on trunk Clang with ASAN enabled. See 
[this](https://github.com/llvm/llvm-project/issues/213520#issuecomment-5314618072)
 and 
[this](https://github.com/llvm/llvm-project/issues/213520#issuecomment-5353945202)
  for more details.

The `-fopenmp` option causes emission of the global `g` to be deferred. It is 
emitted when `CodeGebModule::EmitDeferred` runs. In the reproducer, the 
deferred list emits globals as follows:
- Function `a`: Declaration for `g` is emitted with type `%T`.
- `g` (used in `a`):
  - `GetAddrOfGlobal` wants `%T`.
  - A subsequent call to `EmitGlobalVarDefinition` wants `{ i32 }`.
  - Type of `g` declaration is replacesd with `{ i32 }`, since initializer type 
takes precedence.
- Function `b`: `g` is already declared.
- `g` (used in `b`):
  - `GetAddrOfGlobal` wants `%T`, but `g` is defined with type `{ i32 }`.
  - `g` gets freed and a new `g` is built with type `%T`.
  - RAUW called to replace uses of the old `g` with the new one.
 
However, even after the RAUW, the COMDAT key (`Structor::AssociatedData`) is an 
`llvm::Constant*` - so it keeps pointing to the old (now freed) `g`. Hence, a 
heap use-after-free is detected by ASAN when the constructor list is being 
emitted in `EmitCtorList`.

This PR makes `Structor::AssociatedData` a `llvm::WeakTrackingVH`, so RAUW can 
fix it up too when it replaces the old `g` with the new one.

---

Assisted-by: Cursor
I used Cursor to figure out that the bug only occurred with a [specific version 
of 
Glibc](https://github.com/llvm/llvm-project/issues/213520#issuecomment-5314618072)
 which hadn't occurred to me. I also used Cursor to understand how Clang emits 
globals.

>From 24d33f9544f8cd3bf296af7cb315ed2baeba0006 Mon Sep 17 00:00:00 2001
From: Soham Karandikar <[email protected]>
Date: Thu, 27 Aug 2026 02:12:53 -0500
Subject: [PATCH] Made COMDAT key of a global structor be a WeakTrackingVH

---
 clang/lib/CodeGen/CodeGenModule.cpp           |  2 +-
 clang/lib/CodeGen/CodeGenModule.h             |  5 ++++-
 .../gh213520-global-ctor-comdat.cpp           | 22 +++++++++++++++++++
 3 files changed, 27 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/gh213520-global-ctor-comdat.cpp

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 348d5e579b9c3..72f7a61b89300 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2754,7 +2754,7 @@ void CodeGenModule::EmitCtorList(CtorList &Fns, const 
char *GlobalName) {
     Ctor.addInt(Int32Ty, I.Priority);
     Ctor.add(I.Initializer);
     if (I.AssociatedData)
-      Ctor.add(I.AssociatedData);
+      Ctor.add(cast<llvm::Constant>(I.AssociatedData));
     else
       Ctor.addNullPointer(PtrTy);
     Ctor.finishAndAddTo(Ctors);
diff --git a/clang/lib/CodeGen/CodeGenModule.h 
b/clang/lib/CodeGen/CodeGenModule.h
index 1f5ecf734c528..13b31d542e39a 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -349,7 +349,10 @@ class CodeGenModule : public CodeGenTypeCache {
     int Priority;
     unsigned LexOrder;
     llvm::Constant *Initializer;
-    llvm::Constant *AssociatedData;
+    // GetOrCreateLLVMGlobal can replace and erase this global before
+    // EmitCtorList runs, so it should be a WeakTrackinhVH for RAUW to fix it 
up
+    // properly.
+    llvm::WeakTrackingVH AssociatedData;
   };
 
   typedef std::vector<Structor> CtorList;
diff --git a/clang/test/CodeGenCXX/gh213520-global-ctor-comdat.cpp 
b/clang/test/CodeGenCXX/gh213520-global-ctor-comdat.cpp
new file mode 100644
index 0000000000000..782ccc4760ea5
--- /dev/null
+++ b/clang/test/CodeGenCXX/gh213520-global-ctor-comdat.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fopenmp \
+// RUN:     -emit-llvm -o - %s | FileCheck %s
+
+struct Base {
+  int x;
+  constexpr Base() : x(0) {}
+};
+
+struct T : Base {
+  ~T();
+};
+
+struct S;
+
+template <class Tag>
+inline T g;
+
+T *a() { return &g<S>; }
+T *b() { return &g<S>; }
+
+// The COMDAT key must be the surviving global, not null or a freed value.
+// CHECK: @llvm.global_ctors = appending global {{.*}} { i32 65535, ptr 
@__cxx_global_var_init{{.*}}, ptr @_Z1gI1SE }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to