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

Reply via email to