Skip to content

[mlir][Transforms] Fix use-after-free when accessing replaced block args #83646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions mlir/lib/Transforms/Utils/DialectConversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,13 @@ class IRRewrite {
/// Roll back the rewrite. Operations may be erased during rollback.
virtual void rollback() = 0;

/// Commit the rewrite. Operations may be unlinked from their blocks during
/// the commit phase, but they must not be erased yet. This is because
/// internal dialect conversion state (such as `mapping`) may still be using
/// them. Operations must be erased during cleanup.
/// Commit the rewrite. Operations/blocks may be unlinked during the commit
/// phase, but they must not be erased yet. This is because internal dialect
/// conversion state (such as `mapping`) may still be using them. Operations/
/// blocks must be erased during cleanup.
virtual void commit() {}

/// Cleanup operations. Cleanup is called after commit.
/// Cleanup operations/blocks. Cleanup is called after commit.
virtual void cleanup() {}

Kind getKind() const { return kind; }
Expand Down Expand Up @@ -282,9 +282,9 @@ class CreateBlockRewrite : public BlockRewrite {
};

/// Erasure of a block. Block erasures are partially reflected in the IR. Erased
/// blocks are immediately unlinked, but only erased when the rewrite is
/// committed. This makes it easier to rollback a block erasure: the block is
/// simply inserted into its original location.
/// blocks are immediately unlinked, but only erased during cleanup. This makes
/// it easier to rollback a block erasure: the block is simply inserted into its
/// original location.
class EraseBlockRewrite : public BlockRewrite {
public:
EraseBlockRewrite(ConversionPatternRewriterImpl &rewriterImpl, Block *block,
Expand All @@ -297,7 +297,8 @@ class EraseBlockRewrite : public BlockRewrite {
}

~EraseBlockRewrite() override {
assert(!block && "rewrite was neither rolled back nor committed");
assert(!block &&
"rewrite was neither rolled back nor committed/cleaned up");
}

void rollback() override {
Expand All @@ -312,7 +313,7 @@ class EraseBlockRewrite : public BlockRewrite {
block = nullptr;
}

void commit() override {
void cleanup() override {
// Erase the block.
assert(block && "expected block");
assert(block->empty() && "expected empty block");
Expand Down Expand Up @@ -440,6 +441,8 @@ class BlockTypeConversionRewrite : public BlockRewrite {

void commit() override;

void cleanup() override;

void rollback() override;

private:
Expand Down Expand Up @@ -986,7 +989,9 @@ void BlockTypeConversionRewrite::commit() {
rewriterImpl.mapping.lookupOrDefault(castValue, origArg.getType()));
}
}
}

void BlockTypeConversionRewrite::cleanup() {
assert(origBlock->empty() && "expected empty block");
origBlock->dropAllDefinedValueUses();
delete origBlock;
Expand Down Expand Up @@ -1484,6 +1489,11 @@ void ConversionPatternRewriterImpl::notifyBlockInserted(
Block *block, Region *previous, Region::iterator previousIt) {
assert(!wasOpReplaced(block->getParentOp()) &&
"attempting to insert into a region within a replaced/erased op");
LLVM_DEBUG({
logger.startLine() << "** Insert Block into : '"
<< block->getParentOp()->getName() << "'("
<< block->getParentOp() << ")\n";
});

if (!previous) {
// This is a newly created block.
Expand Down
12 changes: 12 additions & 0 deletions mlir/test/Transforms/test-legalizer.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -346,3 +346,15 @@ func.func @test_properties_rollback() {
{modify_inplace}
"test.return"() : () -> ()
}

// -----

// CHECK: func.func @use_of_replaced_bbarg(
// CHECK-SAME: %[[arg0:.*]]: f64)
// CHECK: "test.valid"(%[[arg0]])
func.func @use_of_replaced_bbarg(%arg0: i64) {
%0 = "test.op_with_region_fold"(%arg0) ({
"foo.op_with_region_terminator"() : () -> ()
}) : (i64) -> (i64)
"test.invalid"(%0) : (i64) -> ()
}
4 changes: 2 additions & 2 deletions mlir/test/lib/Dialect/Test/TestOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1270,8 +1270,8 @@ def TestOpWithRegionFoldNoMemoryEffect : TEST_Op<

// Op for testing folding of outer op with inner ops.
def TestOpWithRegionFold : TEST_Op<"op_with_region_fold"> {
let arguments = (ins I32:$operand);
let results = (outs I32);
let arguments = (ins AnyType:$operand);
let results = (outs AnyType);
let regions = (region SizedRegion<1>:$region);
let hasFolder = 1;
}
Expand Down