Author: Weibo He
Date: 2026-08-10T11:59:40+08:00
New Revision: ab40e2bc72b39ec1009962100089be67a66d09f2

URL: 
https://github.com/llvm/llvm-project/commit/ab40e2bc72b39ec1009962100089be67a66d09f2
DIFF: 
https://github.com/llvm/llvm-project/commit/ab40e2bc72b39ec1009962100089be67a66d09f2.diff

LOG: [clang][CodeGen] Never collect return value alloca into coroutine frame 
(#213580)

The coroutine return value must not reside within the coroutine frame;
otherwise, a heap-use-after-free error will occur, as the frame is
destroyed before the return is completed.

In the front end, emit `coro_outside_frame` metadata for the
return-value alloca so that it does not accidentally enter the frame.

Close #49843

Added: 
    clang/test/CodeGenCoroutines/coro-gro5.cpp

Modified: 
    clang/lib/CodeGen/CGCoroutine.cpp
    clang/test/CodeGenCoroutines/coro-gro.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CGCoroutine.cpp 
b/clang/lib/CodeGen/CGCoroutine.cpp
index bf896f1338ab4..2f162519139e0 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -880,6 +880,9 @@ struct GetReturnObjectManager {
     Builder.CreateCondBr(InRamp, ConvBB, AfterConvBB);
 
     CGF.EmitBlock(ConvBB);
+    if (auto *AI = 
dyn_cast<llvm::AllocaInst>(CGF.ReturnValue.getBasePointer()))
+      AI->setMetadata(llvm::LLVMContext::MD_coro_outside_frame,
+                      llvm::MDNode::get(CGF.getLLVMContext(), {}));
     CGF.EmitAnyExprToMem(S.getReturnValue(), CGF.ReturnValue,
                          S.getReturnValue()->getType().getQualifiers(),
                          /*IsInit*/ true);

diff  --git a/clang/test/CodeGenCoroutines/coro-gro.cpp 
b/clang/test/CodeGenCoroutines/coro-gro.cpp
index e9f79e10db7ae..c4c93a9457010 100644
--- a/clang/test/CodeGenCoroutines/coro-gro.cpp
+++ b/clang/test/CodeGenCoroutines/coro-gro.cpp
@@ -28,10 +28,10 @@ void doSomething() noexcept;
 
 // CHECK: define{{.*}} i32 @_Z1fv(
 int f() {
-  // CHECK: %[[RetVal:.+]] = alloca i32
+  // CHECK: %[[RetVal:.+]] = alloca i32, align 4, !coro.outside.frame 
![[OutFrameMetadata:.+]]
   // CHECK-NEXT: %[[GroActive:.+]] = alloca i1
   // CHECK-NEXT: %[[Promise:.+]] = alloca 
%"struct.std::coroutine_traits<int>::promise_type", align 1
-  // CHECK-NEXT: %[[CoroGro:.+]] = alloca %struct.GroType, {{.*}} 
!coro.outside.frame ![[OutFrameMetadata:.+]]
+  // CHECK-NEXT: %[[CoroGro:.+]] = alloca %struct.GroType, {{.*}} 
!coro.outside.frame ![[OutFrameMetadata]]
 
   // CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64()
   // CHECK-NEXT: call noalias noundef nonnull ptr @_Znwm(i64 noundef %[[Size]])

diff  --git a/clang/test/CodeGenCoroutines/coro-gro5.cpp 
b/clang/test/CodeGenCoroutines/coro-gro5.cpp
new file mode 100644
index 0000000000000..7c275b826cbc3
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-gro5.cpp
@@ -0,0 +1,35 @@
+// Test that return value alloca does not enter the coro frame
+// Regression test for GH49843
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -fexceptions 
-fcxx-exceptions -emit-llvm -o - %s | FileCheck %s
+
+#include "Inputs/coroutine.h"
+
+struct tag { char data[8]; }; // `tag` can be any type. It could be empty, or 
an int, or anything. 
+
+struct expected {
+  char data; // No issues if this member isn't here.
+
+  expected(tag) : data() {}
+
+  struct promise_type {
+    tag get_return_object() { return {}; } // No issues if we return an 
`expected` instead. 
+    std::suspend_never initial_suspend() { return {}; }
+    std::suspend_never final_suspend() noexcept { return {}; }
+    tag return_value(tag) { return tag{}; }
+    void unhandled_exception() {}
+  };
+};
+
+// CHECK-LABEL: define {{.*}} i8 @_Z2f1v()
+expected f1() {
+  // CHECK: %[[Retval:.+]] = alloca %struct.expected, align 1, 
!coro.outside.frame
+
+  // %Retval are captured
+  // CHECK: gro.conv:
+  // CHECK: invoke void @_ZN8expectedC1E3tag(ptr {{.*}} %[[Retval]], i64 
{{.*}})
+
+  // CHECK: %[[GEP:.+]] = getelementptr {{.*}} %struct.expected, ptr 
%[[Retval]], i32 0, i32 0
+  // CHECK-NEXT: %[[Val:.+]] = load i8, ptr %[[GEP]], align 1
+  // CHECK-NEXT: ret i8 %[[Val]]
+  co_return {};
+}


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

Reply via email to