https://github.com/kumarak updated https://github.com/llvm/llvm-project/pull/208850
>From a94f7b2287eb04da44ecea4fa48f18733c9c4779 Mon Sep 17 00:00:00 2001 From: AkshayK <[email protected]> Date: Fri, 10 Jul 2026 17:39:00 -0400 Subject: [PATCH 1/2] [CIR] Fix use-after-free when emitting ternary with a throw-expression arm VisitAbstractConditionalOperator saved a raw insertion point when an arm yielded no value, intending to patch in a cir.yield later. For a noreturn arm (e.g. throw), that insertion point refers to the empty dead-code block created after the throw's terminator - a block LexicalScope::cleanup() erases when emitBranch's scope exits. The deferred patch loop then restored the insertion point into the freed block, producing invalid IR ("cir.yield must be the last operation in the parent block") or a segfault, depending on heap reuse. Skip saving an insertion point in that case: the region is already terminated by cir.unreachable and needs no yield. The verifier and cir-flatten-cfg both accept unreachable-terminated ternary regions. The existing ternary-throw.cpp only covered glvalue conditionals, which take the LValue path; add the scalar-rvalue cases that reach this code, including both-arms-throw. --- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 6 ++ clang/test/CIR/CodeGen/ternary-throw.cpp | 89 ++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 645cca9fe16eb..8e1e955be9051 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -2929,6 +2929,12 @@ mlir::Value ScalarExprEmitter::VisitAbstractConditionalOperator( yieldTy = branch.getType(); cir::YieldOp::create(b, loc, branch); } else { + // A noreturn branch (e.g. throw) leaves the insertion point in a dead + // block that LexicalScope cleanup erases; no yield is needed and saving + // an insertion point into it would dangle. + mlir::Block *curBlock = b.getInsertionBlock(); + if (curBlock->empty() && !curBlock->isEntryBlock()) + return; // If LHS or RHS is a throw or void expression we need to patch // arms as to properly match yield types. insertPoints.push_back(b.saveInsertionPoint()); diff --git a/clang/test/CIR/CodeGen/ternary-throw.cpp b/clang/test/CIR/CodeGen/ternary-throw.cpp index fbafa9811d76e..8a342ee51308d 100644 --- a/clang/test/CIR/CodeGen/ternary-throw.cpp +++ b/clang/test/CIR/CodeGen/ternary-throw.cpp @@ -650,3 +650,92 @@ void test_agg_throw_false(bool flag) { // OGCG: unreachable // OGCG: [[END]]: // OGCG: ret void + +int test_scalar_throw_true(bool flag, int x) { + return flag ? throw 0 : x; +} + +// CIR-LABEL: cir.func {{.*}} @_Z22test_scalar_throw_truebi( +// CIR: %[[COND:.*]] = cir.load{{.*}} : !cir.ptr<!cir.bool>, !cir.bool +// CIR: %{{.*}} = cir.ternary(%[[COND]], true { +// CIR: %[[EXC:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i> +// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: }, false { +// CIR: %[[X:.*]] = cir.load{{.*}} : !cir.ptr<!s32i>, !s32i +// CIR: cir.yield %[[X]] : !s32i +// CIR: }) : (!cir.bool) -> !s32i + +// LLVM-LABEL: define{{.*}} i32 @_Z22test_scalar_throw_truebi( +// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[FALSE_BB]]: +// LLVM: %{{.*}} = load i32 +// LLVM: ret i32 + +// OGCG-LABEL: define{{.*}} i32 @_Z22test_scalar_throw_truebi( +// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[FALSE_BB]]: +// OGCG: ret i32 + +int test_scalar_throw_false(bool flag, int x) { + return flag ? x : throw 0; +} + +// CIR-LABEL: cir.func {{.*}} @_Z23test_scalar_throw_falsebi( +// CIR: %{{.*}} = cir.ternary(%{{.*}}, true { +// CIR: %[[X:.*]] = cir.load{{.*}} : !cir.ptr<!s32i>, !s32i +// CIR: cir.yield %[[X]] : !s32i +// CIR: }, false { +// CIR: %[[EXC:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i> +// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: }) : (!cir.bool) -> !s32i + +// LLVM-LABEL: define{{.*}} i32 @_Z23test_scalar_throw_falsebi( +// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[FALSE_BB]]: +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable + +// OGCG-LABEL: define{{.*}} i32 @_Z23test_scalar_throw_falsebi( +// OGCG: br i1 %{{.*}}, label %{{.*}}, label %[[FALSE_BB:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable + +void test_both_throw(bool flag) { + flag ? throw 1 : throw 2; +} + +// CIR-LABEL: cir.func {{.*}} @_Z15test_both_throwb( +// CIR: cir.ternary(%{{.*}}, true { +// CIR: cir.throw %{{.*}} : !cir.ptr<!s32i>, @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: }, false { +// CIR: cir.throw %{{.*}} : !cir.ptr<!s32i>, @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: }) + +// LLVM-LABEL: define{{.*}} void @_Z15test_both_throwb( +// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[FALSE_BB]]: +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable + +// OGCG-LABEL: define{{.*}} void @_Z15test_both_throwb( +// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[FALSE_BB]]: +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable >From 21334628c3b11d34bd9879902068c1d3f10627f9 Mon Sep 17 00:00:00 2001 From: AkshayK <[email protected]> Date: Mon, 13 Jul 2026 18:44:18 -0400 Subject: [PATCH 2/2] [CIR] Address review: terminate ternary regions after creation instead of saving insertion points --- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 66 ++++++++-------------- 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 8e1e955be9051..2d76d40e25dda 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -2906,8 +2906,6 @@ mlir::Value ScalarExprEmitter::VisitAbstractConditionalOperator( mlir::Value condV = cgf.emitOpOnBoolExpr(loc, condExpr); CIRGenFunction::ConditionalEvaluation eval(cgf); - SmallVector<mlir::OpBuilder::InsertPoint, 2> insertPoints{}; - mlir::Type yieldTy{}; auto emitBranch = [&](mlir::OpBuilder &b, mlir::Location loc, Expr *expr) { CIRGenFunction::LexicalScope lexScope{cgf, loc, b.getInsertionBlock()}; @@ -2925,55 +2923,35 @@ mlir::Value ScalarExprEmitter::VisitAbstractConditionalOperator( branchCleanups.forceCleanup({&branch}); } - if (branch) { - yieldTy = branch.getType(); + if (branch) cir::YieldOp::create(b, loc, branch); - } else { - // A noreturn branch (e.g. throw) leaves the insertion point in a dead - // block that LexicalScope cleanup erases; no yield is needed and saving - // an insertion point into it would dangle. - mlir::Block *curBlock = b.getInsertionBlock(); - if (curBlock->empty() && !curBlock->isEntryBlock()) - return; - // If LHS or RHS is a throw or void expression we need to patch - // arms as to properly match yield types. - insertPoints.push_back(b.saveInsertionPoint()); - } }; - mlir::Value result = cir::TernaryOp::create( - builder, loc, condV, - /*trueBuilder=*/ - [&](mlir::OpBuilder &b, mlir::Location loc) { - emitBranch(b, loc, lhsExpr); - }, - /*falseBuilder=*/ - [&](mlir::OpBuilder &b, mlir::Location loc) { - emitBranch(b, loc, rhsExpr); - }) - .getResult(); - - if (!insertPoints.empty()) { - // If both arms are void, so be it. - if (!yieldTy) - yieldTy = cgf.voidTy; - - // Insert required yields. - for (mlir::OpBuilder::InsertPoint &toInsert : insertPoints) { + cir::TernaryOp ternary = cir::TernaryOp::create( + builder, loc, condV, + /*trueBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + emitBranch(b, loc, lhsExpr); + }, + /*falseBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + emitBranch(b, loc, rhsExpr); + }); + + // Only a void arm can be left unterminated (a noreturn arm already ends + // in cir.unreachable); close it with an empty cir.yield. + for (mlir::Region *region : + {&ternary.getTrueRegion(), &ternary.getFalseRegion()}) { + mlir::Block &lastBlock = region->back(); + if (lastBlock.empty() || + !lastBlock.back().hasTrait<mlir::OpTrait::IsTerminator>()) { mlir::OpBuilder::InsertionGuard guard(builder); - builder.restoreInsertionPoint(toInsert); - - // Block does not return: build empty yield. - if (mlir::isa<cir::VoidType>(yieldTy)) { - cir::YieldOp::create(builder, loc); - } else { // Block returns: set null yield value. - mlir::Value op0 = builder.getNullValue(yieldTy, loc); - cir::YieldOp::create(builder, loc, op0); - } + builder.setInsertionPointToEnd(&lastBlock); + cir::YieldOp::create(builder, loc); } } - return result; + return ternary.getResult(); } mlir::Value CIRGenFunction::emitScalarPrePostIncDec(const UnaryOperator *e, _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
