https://github.com/bcardosolopes updated https://github.com/llvm/llvm-project/pull/191949
>From 5e6e77ceb0e398722bebb81ddb42350fa88a00a1 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <[email protected]> Date: Mon, 13 Apr 2026 19:45:42 -0700 Subject: [PATCH 1/2] [CIR] Fix heap-use-after-free in IndirectBrOp lowering The previous code called op->getBlock()->eraseArgument(0) to remove a block argument when the poison attribute was set (unreachable block with no predecessors). This directly mutated the IR, freeing the BlockArgument while the MLIR dialect conversion framework still held references to it. When the framework later replayed changes in applyRewrites(), it dereferenced the freed BlockArgument, causing a heap-use-after-free detected by ASAN. Found by running check-clang-cir under ASAN (test: clang/test/CIR/CodeGen/label-values.c). The fix removes the eraseArgument call entirely. The MLIR conversion framework tracks block arguments and handles their lifecycle. A block with no predecessors naturally produces no PHI node in LLVM IR, so manual removal was unnecessary. Additional cleanup: - Use adaptor.getAddr() directly instead of creating an unnecessary BitcastOp (CIR ptr already converts to LLVM ptr). - Use adaptor.getSuccOperands() instead of op.getSuccOperands() to ensure successor operands go through type conversion. - Use replaceOpWithNewOp instead of separate create + replaceOp. --- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index c352ef91d1ee2..beaf06e577f02 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -4578,32 +4578,27 @@ mlir::LogicalResult CIRToLLVMIndirectBrOpLowering::matchAndRewrite( cir::IndirectBrOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { - llvm::SmallVector<mlir::Block *, 8> successors; - llvm::SmallVector<mlir::ValueRange, 8> succOperands; - bool poison = op.getPoison(); - for (mlir::Block *succ : op->getSuccessors()) - successors.push_back(succ); - - for (mlir::ValueRange operand : op.getSuccOperands()) { - succOperands.push_back(operand); - } - - auto llvmPtrType = mlir::LLVM::LLVMPointerType::get(rewriter.getContext()); - mlir::Value targetAddr; - if (!poison) { - targetAddr = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), - llvmPtrType, adaptor.getAddr()); - } else { + mlir::Value targetAddr = adaptor.getAddr(); + + // If the poison attribute is set, use llvm.mlir.poison as the address. + // This happens when the block has no predecessors and is essentially + // unreachable. Do NOT erase the block argument directly, as that violates + // the MLIR dialect conversion framework contract (the framework tracks block + // arguments and will clean them up). A block with no predecessors simply + // produces no PHI node. + if (op.getPoison()) { + auto llvmPtrType = mlir::LLVM::LLVMPointerType::get(rewriter.getContext()); targetAddr = mlir::LLVM::PoisonOp::create(rewriter, op->getLoc(), llvmPtrType); - // Remove the block argument to avoid generating an empty PHI during - // lowering. - op->getBlock()->eraseArgument(0); } - auto newOp = mlir::LLVM::IndirectBrOp::create( - rewriter, op.getLoc(), targetAddr, succOperands, successors); - rewriter.replaceOp(op, newOp); + // Get successor operands through the adaptor to ensure type conversion. + llvm::SmallVector<mlir::ValueRange> succOperands; + for (unsigned i = 0; i < op.getSuccessors().size(); ++i) + succOperands.push_back(adaptor.getSuccOperands()[i]); + + rewriter.replaceOpWithNewOp<mlir::LLVM::IndirectBrOp>( + op, targetAddr, succOperands, op.getSuccessors()); return mlir::success(); } >From 2751aefc29ee6e4ff948b35e845b4d66e17404f1 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <[email protected]> Date: Tue, 14 Apr 2026 10:42:40 -0700 Subject: [PATCH 2/2] [CIR] NFC: simplify IndirectBrOp successor operand materialization Replace the manual loop with direct SmallVector range construction. --- clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index beaf06e577f02..c2796ad8daa75 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -4592,13 +4592,8 @@ mlir::LogicalResult CIRToLLVMIndirectBrOpLowering::matchAndRewrite( mlir::LLVM::PoisonOp::create(rewriter, op->getLoc(), llvmPtrType); } - // Get successor operands through the adaptor to ensure type conversion. - llvm::SmallVector<mlir::ValueRange> succOperands; - for (unsigned i = 0; i < op.getSuccessors().size(); ++i) - succOperands.push_back(adaptor.getSuccOperands()[i]); - rewriter.replaceOpWithNewOp<mlir::LLVM::IndirectBrOp>( - op, targetAddr, succOperands, op.getSuccessors()); + op, targetAddr, adaptor.getSuccOperands(), op.getSuccessors()); return mlir::success(); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
