llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-codegen

Author: Soham Karandikar (skadewdl3)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/219137.diff


3 Files Affected:

- (modified) clang/lib/CodeGen/CodeGenModule.cpp (+1-1) 
- (modified) clang/lib/CodeGen/CodeGenModule.h (+4-1) 
- (added) clang/test/CodeGenCXX/gh213520-global-ctor-comdat.cpp (+22) 


``````````diff
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 }

``````````

</details>


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

Reply via email to