https://github.com/llvmbot created 
https://github.com/llvm/llvm-project/pull/195869

Backport efb01c1

Requested by: @SLTozer

>From eb08fa05bcc3570874913f547c0719caa854244d Mon Sep 17 00:00:00 2001
From: Stephen Tozer <[email protected]>
Date: Fri, 1 May 2026 10:48:16 +0100
Subject: [PATCH] [Clang][Coroutines] Don't emit fake uses for coroutine
 parameters (#194690)

Fixes issue: https://github.com/llvm/llvm-project/issues/192351

The combination of coroutines with -fextend-variable-liveness has
resulted in use-after-free, caused by the fact that we insert fake uses
of coroutine parameters at the end of the coroutine. While this is fine
for normal functions, in coroutines these variables are stored in the
coroutine frame, which is freed before the end of the function; this
results in us loading from the deleted frame.

This patch fixes this by no longer emitting fake uses for most coroutine
parameters. Since coroutine parameters will be saved back to the frame
when we suspend, and currently may not be optimized out, fake uses are
not needed in this case, and so by not emitting them we avoid dealing
with the complexity of updating fake uses in the CoroSplit pass. The
exception to this is 'this', which is not saved to the frame.

(cherry picked from commit efb01c1bf558eaaf8ec64e1a54110584e827f21b)
---
 clang/lib/CodeGen/CGDecl.cpp                  |  9 +++-
 .../CodeGenCoroutines/coro-param-fake-use.cpp | 42 +++++++++++++++++++
 2 files changed, 49 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGenCoroutines/coro-param-fake-use.cpp

diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 8b1cd83af2396..aeecf0bac1b87 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -2829,8 +2829,13 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, 
ParamValue Arg,
       (CGM.getCodeGenOpts().getExtendVariableLiveness() ==
            CodeGenOptions::ExtendVariableLivenessKind::This &&
        &D == CXXABIThisDecl)) {
-    if (shouldExtendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl))
-      EHStack.pushCleanup<FakeUse>(NormalFakeUse, DeclPtr);
+    // We don't emit fake uses for coroutine parameters, other than `this`.
+    if (auto *FnDecl = dyn_cast_or_null<FunctionDecl>(CurCodeDecl);
+        &D == CXXABIThisDecl || !FnDecl ||
+        FnDecl->getBody()->getStmtClass() != Stmt::CoroutineBodyStmtClass) {
+      if (shouldExtendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl))
+        EHStack.pushCleanup<FakeUse>(NormalFakeUse, DeclPtr);
+    }
   }
 
   // Emit debug info for param declarations in non-thunk functions.
diff --git a/clang/test/CodeGenCoroutines/coro-param-fake-use.cpp 
b/clang/test/CodeGenCoroutines/coro-param-fake-use.cpp
new file mode 100644
index 0000000000000..0a0da815975d9
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-param-fake-use.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm 
-fextend-variable-liveness -o - %s -disable-llvm-passes -fexceptions | 
FileCheck %s
+
+// See issue #192351
+// Tests that parameters to a coroutine do not have fake uses inserted for them
+// when we enable -fextend-variable-liveness, except for `this`, which is not
+// stored in the coroutine frame.
+
+#include "Inputs/coroutine.h"
+
+struct task {
+    struct promise_type {
+        task get_return_object() noexcept { return {}; }
+        std::suspend_never initial_suspend() noexcept { return {}; }
+        std::suspend_never final_suspend() noexcept { return {}; }
+        void return_void() noexcept {}
+        void unhandled_exception() noexcept {}
+    };
+};
+
+class C {
+public:
+    C() {}
+
+    // CHECK-LABEL: void @_ZN1C1fEb(ptr noundef{{.*}} %this, i1 noundef{{.*}} 
%b)
+    task f(bool b) {
+        // CHECK: store ptr %this, ptr %[[THIS_ADDR:.+]]
+        // CHECK-NOT: llvm.fake.use
+
+        // CHECK:      coro.ret:
+        // CHECK-NEXT: call void @llvm.coro.end(
+        // CHECK-NEXT: %[[THIS_FAKE_USE:.+]] = load ptr, ptr %[[THIS_ADDR]]
+        // CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr 
%[[THIS_FAKE_USE]])
+        // CHECK-NEXT: ret void
+        if (b) {
+            co_await std::suspend_always{};
+        }
+    }
+};
+
+void foo() {
+    C().f(false);
+}

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

Reply via email to