https://github.com/kumarak updated https://github.com/llvm/llvm-project/pull/208850
>From e426c6b57b7bf131036454bb64601a8b4fc5de32 Mon Sep 17 00:00:00 2001 From: AkshayK <[email protected]> Date: Fri, 10 Jul 2026 17:39:00 -0400 Subject: [PATCH] [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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
