https://github.com/yasster updated 
https://github.com/llvm/llvm-project/pull/146796

>From 2ccf38469c28392597199e98515dd5a21cfc7c84 Mon Sep 17 00:00:00 2001
From: Yassine Missoum <mmiss...@microsoft.com>
Date: Tue, 1 Jul 2025 15:25:21 -0700
Subject: [PATCH 1/2] Fixed double finally block execution

---
 clang/lib/CodeGen/CGException.cpp             | 45 +++++++++++++++----
 .../test/CodeGen/seh-finally-double-execute.c | 34 ++++++++++++++
 2 files changed, 71 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/CodeGen/seh-finally-double-execute.c

diff --git a/clang/lib/CodeGen/CGException.cpp 
b/clang/lib/CodeGen/CGException.cpp
index ad138b9876e8c..ab4086716cc1c 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -1368,14 +1368,24 @@ namespace {
     llvm::FunctionCallee EndCatchFn;
     llvm::FunctionCallee RethrowFn;
     llvm::Value *SavedExnVar;
+    llvm::Value *FinallyExecutedFlag;
 
     PerformFinally(const Stmt *Body, llvm::Value *ForEHVar,
                    llvm::FunctionCallee EndCatchFn,
-                   llvm::FunctionCallee RethrowFn, llvm::Value *SavedExnVar)
+                   llvm::FunctionCallee RethrowFn, llvm::Value *SavedExnVar,
+                   llvm::Value *FinallyExecutedFlag)
         : Body(Body), ForEHVar(ForEHVar), EndCatchFn(EndCatchFn),
-          RethrowFn(RethrowFn), SavedExnVar(SavedExnVar) {}
+          RethrowFn(RethrowFn), SavedExnVar(SavedExnVar),
+          FinallyExecutedFlag(FinallyExecutedFlag) {}
 
     void Emit(CodeGenFunction &CGF, Flags flags) override {
+      // Only execute the finally block if it hasn't already run.
+      llvm::BasicBlock *RunFinallyBB = CGF.createBasicBlock("finally.run");
+      llvm::BasicBlock *SkipFinallyBB = CGF.createBasicBlock("finally.skip");
+      llvm::Value *AlreadyExecuted = 
CGF.Builder.CreateFlagLoad(FinallyExecutedFlag, "finally.executed");
+      CGF.Builder.CreateCondBr(AlreadyExecuted, SkipFinallyBB, RunFinallyBB);
+      CGF.EmitBlock(RunFinallyBB);
+      CGF.Builder.CreateFlagStore(true, FinallyExecutedFlag);
       // Enter a cleanup to call the end-catch function if one was provided.
       if (EndCatchFn)
         CGF.EHStack.pushCleanup<CallEndCatchForFinally>(NormalAndEHCleanup,
@@ -1429,6 +1439,7 @@ namespace {
       // Now make sure we actually have an insertion point or the
       // cleanup gods will hate us.
       CGF.EnsureInsertPoint();
+      CGF.EmitBlock(SkipFinallyBB);
     }
   };
 } // end anonymous namespace
@@ -1478,10 +1489,12 @@ void 
CodeGenFunction::FinallyInfo::enter(CodeGenFunction &CGF, const Stmt *body,
   ForEHVar = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(), "finally.for-eh");
   CGF.Builder.CreateFlagStore(false, ForEHVar);
 
-  // Enter a normal cleanup which will perform the @finally block.
+  // Allocate a flag to ensure the finally block is only executed once.
+  llvm::Value *FinallyExecutedFlag = 
CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(), "finally.executed");
+  CGF.Builder.CreateFlagStore(false, FinallyExecutedFlag);
   CGF.EHStack.pushCleanup<PerformFinally>(NormalCleanup, body,
                                           ForEHVar, endCatchFn,
-                                          rethrowFn, SavedExnVar);
+                                          rethrowFn, SavedExnVar, 
FinallyExecutedFlag);
 
   // Enter a catch-all scope.
   llvm::BasicBlock *catchBB = CGF.createBasicBlock("finally.catchall");
@@ -1724,10 +1737,18 @@ void CodeGenFunction::VolatilizeTryBlocks(
 namespace {
 struct PerformSEHFinally final : EHScopeStack::Cleanup {
   llvm::Function *OutlinedFinally;
-  PerformSEHFinally(llvm::Function *OutlinedFinally)
-      : OutlinedFinally(OutlinedFinally) {}
+  llvm::Value *FinallyExecutedFlag;
+  PerformSEHFinally(llvm::Function *OutlinedFinally, llvm::Value 
*FinallyExecutedFlag)
+      : OutlinedFinally(OutlinedFinally), 
FinallyExecutedFlag(FinallyExecutedFlag) {}
 
   void Emit(CodeGenFunction &CGF, Flags F) override {
+    // Only execute the finally block if it hasn't already run.
+    llvm::BasicBlock *RunFinallyBB = CGF.createBasicBlock("finally.run");
+    llvm::BasicBlock *SkipFinallyBB = CGF.createBasicBlock("finally.skip");
+    llvm::Value *AlreadyExecuted = 
CGF.Builder.CreateFlagLoad(FinallyExecutedFlag, "finally.executed");
+    CGF.Builder.CreateCondBr(AlreadyExecuted, SkipFinallyBB, RunFinallyBB);
+    CGF.EmitBlock(RunFinallyBB);
+    CGF.Builder.CreateFlagStore(true, FinallyExecutedFlag);
     ASTContext &Context = CGF.getContext();
     CodeGenModule &CGM = CGF.CGM;
 
@@ -1769,6 +1790,8 @@ struct PerformSEHFinally final : EHScopeStack::Cleanup {
 
     auto Callee = CGCallee::forDirect(OutlinedFinally);
     CGF.EmitCall(FnInfo, Callee, ReturnValueSlot(), Args);
+    
+    CGF.EmitBlock(SkipFinallyBB);
   }
 };
 } // end anonymous namespace
@@ -2164,7 +2187,10 @@ llvm::Value 
*CodeGenFunction::EmitSEHAbnormalTermination() {
 
 void CodeGenFunction::pushSEHCleanup(CleanupKind Kind,
                                      llvm::Function *FinallyFunc) {
-  EHStack.pushCleanup<PerformSEHFinally>(Kind, FinallyFunc);
+  // Allocate a flag to ensure the finally block is only executed once.
+  llvm::Value *FinallyExecutedFlag = CreateTempAlloca(Builder.getInt1Ty(), 
"finally.executed");
+  Builder.CreateFlagStore(false, FinallyExecutedFlag);
+  EHStack.pushCleanup<PerformSEHFinally>(Kind, FinallyFunc, 
FinallyExecutedFlag);
 }
 
 void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) {
@@ -2175,8 +2201,11 @@ void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt 
&S) {
     llvm::Function *FinallyFunc =
         HelperCGF.GenerateSEHFinallyFunction(*this, *Finally);
 
+    // Allocate a flag to ensure the finally block is only executed once.
+    llvm::Value *FinallyExecutedFlag = CreateTempAlloca(Builder.getInt1Ty(), 
"finally.executed");
+    Builder.CreateFlagStore(false, FinallyExecutedFlag);
     // Push a cleanup for __finally blocks.
-    EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup, FinallyFunc);
+    EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup, FinallyFunc, 
FinallyExecutedFlag);
     return;
   }
 
diff --git a/clang/test/CodeGen/seh-finally-double-execute.c 
b/clang/test/CodeGen/seh-finally-double-execute.c
new file mode 100644
index 0000000000000..0f2d417e0f4fb
--- /dev/null
+++ b/clang/test/CodeGen/seh-finally-double-execute.c
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -emit-llvm -O0 -fms-extensions 
-fexceptions -fcxx-exceptions -o - %s | FileCheck %s
+
+int freed = 0;
+void myfree(int *p) {
+  ++freed;
+}
+
+// CHECK-LABEL: define dso_local i32 @main(
+int main() {
+  int x = 0;
+  int *p = &x;
+  __try {
+    return 0;
+  } __finally {
+    myfree(p);
+  }
+}
+
+// Check that a guard flag is allocated to prevent double execution
+// CHECK: %finally.executed = alloca i1
+// CHECK: store i1 false, ptr %finally.executed
+
+// Check the main function has guard logic to prevent double execution
+// CHECK: %finally.executed{{.*}} = load i1, ptr %finally.executed
+// CHECK: br i1 %finally.executed{{.*}}, label %finally.skip, label 
%finally.run
+// CHECK: finally.run:
+// CHECK: store i1 true, ptr %finally.executed
+// CHECK: call void @"?fin$0@0@main@@"
+// CHECK: finally.skip:
+
+// Check the finally helper function is called only once
+// CHECK-LABEL: define internal void @"?fin$0@0@main@@"
+// CHECK: call void @myfree
+// CHECK-NOT: call void @myfree

>From 4c99b3f9dbdfa408169c4167138e64debae40233 Mon Sep 17 00:00:00 2001
From: Yassine Missoum <mmiss...@microsoft.com>
Date: Mon, 7 Jul 2025 10:58:17 -0700
Subject: [PATCH 2/2] Updated the test and the code to fix double finally issue

---
 clang/lib/CodeGen/CGException.cpp             |  13 +-
 .../test/CodeGen/seh-finally-double-execute.c | 130 ++++++++++++++----
 2 files changed, 113 insertions(+), 30 deletions(-)

diff --git a/clang/lib/CodeGen/CGException.cpp 
b/clang/lib/CodeGen/CGException.cpp
index ab4086716cc1c..f3cbb77d810a7 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -2201,11 +2201,8 @@ void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt 
&S) {
     llvm::Function *FinallyFunc =
         HelperCGF.GenerateSEHFinallyFunction(*this, *Finally);
 
-    // Allocate a flag to ensure the finally block is only executed once.
-    llvm::Value *FinallyExecutedFlag = CreateTempAlloca(Builder.getInt1Ty(), 
"finally.executed");
-    Builder.CreateFlagStore(false, FinallyExecutedFlag);
     // Push a cleanup for __finally blocks.
-    EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup, FinallyFunc, 
FinallyExecutedFlag);
+    pushSEHCleanup(NormalAndEHCleanup, FinallyFunc);
     return;
   }
 
@@ -2238,7 +2235,13 @@ void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt 
&S) {
 void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S) {
   // Just pop the cleanup if it's a __finally block.
   if (S.getFinallyHandler()) {
-    PopCleanupBlock();
+    // In most cases, the cleanup is active, but if the __try contains a
+    // noreturn call, the block will be terminated and the cleanup will be
+    // inactive.
+    if (HaveInsertPoint())
+      PopCleanupBlock();
+    else
+      EHStack.popCleanup();
     return;
   }
 
diff --git a/clang/test/CodeGen/seh-finally-double-execute.c 
b/clang/test/CodeGen/seh-finally-double-execute.c
index 0f2d417e0f4fb..5924993ca78a5 100644
--- a/clang/test/CodeGen/seh-finally-double-execute.c
+++ b/clang/test/CodeGen/seh-finally-double-execute.c
@@ -1,34 +1,114 @@
-// RUN: %clang_cc1 -triple x86_64-windows-msvc -emit-llvm -O0 -fms-extensions 
-fexceptions -fcxx-exceptions -o - %s | FileCheck %s
+// RUN: %clang_cc1 -x c -triple x86_64-windows-msvc -emit-llvm -O0 
-fms-extensions -fexceptions -o - %s | FileCheck %s
 
+// Global state to track resource cleanup
 int freed = 0;
-void myfree(int *p) {
-  ++freed;
+void* allocated_buffer = 0;
+
+// External functions that prevent optimization
+extern void external_operation(void);
+extern int external_condition(void);
+extern void external_cleanup(void*);
+
+// Declare SEH exception functions
+void RaiseException(unsigned long code, unsigned long flags, unsigned long 
argc, void* argv);
+
+// Simulate complex resource allocation/cleanup
+void* allocate_buffer(int size) {
+  // Simulate allocation that could fail
+  if (external_condition()) {
+    allocated_buffer = (void*)0x12345678;  // Mock allocation
+    return allocated_buffer;
+  }
+  return 0;
 }
 
-// CHECK-LABEL: define dso_local i32 @main(
-int main() {
-  int x = 0;
-  int *p = &x;
+void free_buffer(void* buffer) {
+  if (buffer && freed == 0) {
+    freed = 1;
+    allocated_buffer = 0;
+    external_cleanup(buffer);  // External cleanup prevents inlining
+  }
+}
+
+
+int complex_operation_with_finally(int operation_type) {
+  void* buffer = 0;
+  int result = 0;
+  
   __try {
-    return 0;
+    // Multiple operations that could throw exceptions
+    buffer = allocate_buffer(1024);
+    if (!buffer) {
+      result = -1;
+      __leave;  // Early exit - finally should still run
+    }
+    
+    // Simulate complex operations that could throw
+    external_operation();  // Could throw
+    
+    if (operation_type == 1) {
+      external_operation();  // Another potential throw point
+    }
+    
+    result = 0;  // Success
   } __finally {
-    myfree(p);
+    // Critical cleanup that must run exactly once
+    if (buffer) {
+      free_buffer(buffer);
+    }
+  }
+  
+  // Exception raised after finally block has already executed
+  // This is the pattern that causes double execution in resource cleanup
+  if (operation_type == 2) {
+    RaiseException(0xC0000005, 0, 0, 0);
   }
+  
+  return result;
 }
 
-// Check that a guard flag is allocated to prevent double execution
-// CHECK: %finally.executed = alloca i1
-// CHECK: store i1 false, ptr %finally.executed
-
-// Check the main function has guard logic to prevent double execution
-// CHECK: %finally.executed{{.*}} = load i1, ptr %finally.executed
-// CHECK: br i1 %finally.executed{{.*}}, label %finally.skip, label 
%finally.run
-// CHECK: finally.run:
-// CHECK: store i1 true, ptr %finally.executed
-// CHECK: call void @"?fin$0@0@main@@"
-// CHECK: finally.skip:
-
-// Check the finally helper function is called only once
-// CHECK-LABEL: define internal void @"?fin$0@0@main@@"
-// CHECK: call void @myfree
-// CHECK-NOT: call void @myfree
+// CHECK: define dso_local i32 @complex_operation_with_finally(i32 noundef 
%operation_type)
+// CHECK:   %finally.executed = alloca i1, align 1
+// CHECK:   store i1 false, ptr %finally.executed, align 1
+
+// Normal path: check if finally already ran.
+// CHECK-LABEL: __try.__leave:
+// CHECK:   %[[finally_executed:.+]] = load i1, ptr %finally.executed, align 1
+// CHECK:   br i1 %[[finally_executed]], label %finally.skip, label 
%finally.run
+
+// Normal path: run finally and set flag.
+// CHECK-LABEL: finally.run:
+// CHECK:   store i1 true, ptr %finally.executed, align 1
+// CHECK:   call void @"?fin$0@0@complex_operation_with_finally@@"(i8 noundef
+// CHECK:   br label %finally.skip
+
+// Normal path: skip finally.
+// CHECK-LABEL: finally.skip:
+// CHECK:   %[[cmp:.+]] = icmp eq i32 {{.+}}, 2
+// CHECK:   br i1 %[[cmp]], label %if.then10, label %if.end11
+
+// Exception path: check if finally already ran.
+// CHECK-LABEL: ehcleanup:
+// CHECK:   %[[finally_executed_eh:.+]] = load i1, ptr %finally.executed, 
align 1
+// CHECK:   br i1 %[[finally_executed_eh]], label %finally.skip8, label 
%finally.run7
+
+// Exception path: run finally and set flag.
+// CHECK-LABEL: finally.run7:
+// CHECK:   store i1 true, ptr %finally.executed, align 1
+// CHECK:   call void @"?fin$0@0@complex_operation_with_finally@@"(i8 noundef 1
+// CHECK:   br label %finally.skip8
+
+// Exception path: skip finally.
+// CHECK-LABEL: finally.skip8:
+// CHECK:   cleanupret from {{.+}} unwind to caller
+
+// CHECK: define internal void @"?fin$0@0@complex_operation_with_finally@@"(i8 
noundef %abnormal_termination, ptr noundef %frame_pointer)
+// CHECK:   call void @free_buffer(ptr noundef
+
+// CHECK-LABEL: @main
+int main() {
+  // This tests that the finally is not executed twice when an exception
+  // is raised after the finally has already run.
+  int result = complex_operation_with_finally(2);
+  return result;
+}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to