[clang] [WinEH] Fix crash, object unwinding in the except block (PR #172287)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Dec 17 08:34:35 PST 2025
https://github.com/GkvJwa updated https://github.com/llvm/llvm-project/pull/172287
>From 2e34318cd3fbffedbd231239d9f99a0907442970 Mon Sep 17 00:00:00 2001
From: GkvJwa <gkvjwa at gmail.com>
Date: Thu, 18 Dec 2025 00:30:02 +0800
Subject: [PATCH 1/3] Add test and skip Borland
---
clang/lib/Sema/SemaStmt.cpp | 70 +++++++++++++++++++++++++++
clang/test/SemaCXX/exceptions-seh.cpp | 30 ++++++++++++
2 files changed, 100 insertions(+)
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 1b1643250d05e..c562d15b0338c 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -4515,6 +4515,57 @@ void Sema::DiagnoseExceptionUse(SourceLocation Loc, bool IsTry) {
targetDiag(Loc, diag::err_exceptions_disabled) << (IsTry ? "try" : "throw");
}
+// Walk the statement subtree and return the first statement that
+// contains a non-trivial C++ object that would require destruction at
+// scope exit, or nullptr if none was found.
+static const Stmt *findNonTrivialObject(Sema &S, const Stmt *Node) {
+ (void)S;
+ if (!Node)
+ return nullptr;
+
+ // Check for declarations of local variables with non-trivial destructors.
+ if (const DeclStmt *DS = dyn_cast<DeclStmt>(Node)) {
+ for (const Decl *D : DS->decls()) {
+ if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
+ QualType T = VD->getType();
+ if (const RecordType *RT = T->getAs<RecordType>()) {
+ if (const CXXRecordDecl *RD = RT->getAsCXXRecordDecl()) {
+ if (RD->hasDefinition() && !RD->hasTrivialDestructor())
+ return DS;
+ }
+ }
+ }
+ }
+ }
+
+ // Check for expressions that materialize temporaries or otherwise
+ // produce prvalue/xvalue C++ objects that will require destruction.
+ if (const Expr *E = dyn_cast<Expr>(Node)) {
+ // A throw-expression creates an exception object as part of throwing and
+ // doesn't create a temporary that lives until the end of the handler
+ // scope; ignore it when checking for non-trivial temporaries.
+ if (isa<CXXThrowExpr>(E))
+ return nullptr;
+
+ QualType T = E->getType();
+ if (T->isRecordType() && E->getValueKind() != VK_LValue) {
+ if (const RecordType *RT = T->getAs<RecordType>()) {
+ if (const CXXRecordDecl *RD = RT->getAsCXXRecordDecl()) {
+ if (RD->hasDefinition() && !RD->hasTrivialDestructor())
+ return E;
+ }
+ }
+ }
+ }
+
+ // Recurse into children.
+ for (const Stmt *Child : Node->children())
+ if (const Stmt *SWith = findNonTrivialObject(S, Child))
+ return SWith;
+
+ return nullptr;
+}
+
StmtResult Sema::ActOnSEHTryBlock(bool IsCXXTry, SourceLocation TryLoc,
Stmt *TryBlock, Stmt *Handler) {
assert(TryBlock && Handler);
@@ -4535,6 +4586,17 @@ StmtResult Sema::ActOnSEHTryBlock(bool IsCXXTry, SourceLocation TryLoc,
FSI->setHasSEHTry(TryLoc);
+ // Disallow non-trivial C++ objects in an SEH __try block as well. If the
+ // try block contains temporaries or local objects with non-trivial
+ // destructors, emit the same diagnostic and fail parsing the try. Skip
+ // this diagnostic when Borland extensions are enabled.
+ if (!getLangOpts().Borland) {
+ if (const Stmt *Offending = findNonTrivialObject(*this, TryBlock)) {
+ return StmtError(
+ Diag(Offending->getBeginLoc(), diag::err_seh_try_outside_functions));
+ }
+ }
+
// Reject __try in Obj-C methods, blocks, and captured decls, since we don't
// track if they use SEH.
DeclContext *DC = CurContext;
@@ -4562,6 +4624,14 @@ StmtResult Sema::ActOnSEHExceptBlock(SourceLocation Loc, Expr *FilterExpr,
Diag(FilterExpr->getExprLoc(), diag::err_filter_expression_integral)
<< FTy);
}
+ // Disallow non-trivial C++ objects in an SEH __except handler. Skip
+ // this diagnostic when Borland extensions are enabled.
+ if (!getLangOpts().Borland) {
+ if (const Stmt *Offending = findNonTrivialObject(*this, Block)) {
+ return StmtError(
+ Diag(Offending->getBeginLoc(), diag::err_seh_try_outside_functions));
+ }
+ }
return SEHExceptStmt::Create(Context, Loc, FilterExpr, Block);
}
diff --git a/clang/test/SemaCXX/exceptions-seh.cpp b/clang/test/SemaCXX/exceptions-seh.cpp
index 02bb786160dcf..0a008aeb5721e 100644
--- a/clang/test/SemaCXX/exceptions-seh.cpp
+++ b/clang/test/SemaCXX/exceptions-seh.cpp
@@ -126,3 +126,33 @@ void instantiate_dependent_filter() {
dependent_filter<int>();
dependent_filter<NotInteger>(); // expected-note {{requested here}}
}
+
+int puts(const char *);
+class CheckError {
+public:
+ static CheckError Check(const char* msg);
+
+ ~CheckError();
+};
+
+int foo__try(const int* f) {
+ int e;
+ __try {
+ CheckError::Check("null pointer"); // expected-error{{cannot use SEH '__try' in blocks}}
+ e = *f;
+ } __except (1) {
+ puts("Caught a C-based exception.");
+ }
+ return e;
+}
+
+int foo__except(const int* f) {
+ int e;
+ __try {
+ puts("null pointer");
+ e = *f;
+ } __except (1) {
+ CheckError::Check("Caught a C-based exception."); // expected-error{{cannot use SEH '__try' in blocks}}
+ }
+ return e;
+}
\ No newline at end of file
>From 7c12341708516032754e2f5a68518e1efaeb1654 Mon Sep 17 00:00:00 2001
From: GkvJwa <gkvjwa at gmail.com>
Date: Thu, 18 Dec 2025 00:33:23 +0800
Subject: [PATCH 2/3] Add throw
---
clang/test/SemaCXX/exceptions-seh.cpp | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/clang/test/SemaCXX/exceptions-seh.cpp b/clang/test/SemaCXX/exceptions-seh.cpp
index 0a008aeb5721e..5c6222ab63551 100644
--- a/clang/test/SemaCXX/exceptions-seh.cpp
+++ b/clang/test/SemaCXX/exceptions-seh.cpp
@@ -155,4 +155,16 @@ int foo__except(const int* f) {
CheckError::Check("Caught a C-based exception."); // expected-error{{cannot use SEH '__try' in blocks}}
}
return e;
+}
+
+// has throw should work
+int foo__except_with_throw(const int* f) {
+ int e;
+ __try {
+ puts("null pointer");
+ e = *f;
+ } __except (1) {
+ throw(CheckError::Check("Caught a C-based exception."));
+ }
+ return e;
}
\ No newline at end of file
>From ac93f64132b69e974983c9d5a5a741807d02afca Mon Sep 17 00:00:00 2001
From: GkvJwa <gkvjwa at gmail.com>
Date: Thu, 18 Dec 2025 00:34:19 +0800
Subject: [PATCH 3/3] Add space
---
clang/test/SemaCXX/exceptions-seh.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/SemaCXX/exceptions-seh.cpp b/clang/test/SemaCXX/exceptions-seh.cpp
index 5c6222ab63551..bdc369ff767da 100644
--- a/clang/test/SemaCXX/exceptions-seh.cpp
+++ b/clang/test/SemaCXX/exceptions-seh.cpp
@@ -167,4 +167,4 @@ int foo__except_with_throw(const int* f) {
throw(CheckError::Check("Caught a C-based exception."));
}
return e;
-}
\ No newline at end of file
+}
More information about the cfe-commits
mailing list