Skip to content

Commit 3821d74

Browse files
authored
[FIRRTL] Update LowerClasses to alter what PathInfo stores. (#7522)
PathInfo stores a pointer to an Operation, which was problematic because that Operation may be deleted in updateInstanceInModule. The test case added in this patch would lead to a use-after-free. This pointer was only really used for a few things, which can be handled differently to avoid needing to consider Operation lifetimes. One use was the operator bool implementation, to check if a PathInfo is empty. In the one place this was used, an equivalent check is to query the PathInfoTable, and check if the key was not in the table. Another use was adding the Operation's location in error messages and notes. We can safely store a Location directly for these messages. The final use was to do an isa check while determining the target kind. This is where the use in the use-after-free would manifest. For this, we do the isa check early, and store the result in a bool. In summary, we are able to simplify the data in PathInfo in order to avoid hanging on to an Operation pointer and needing to worry about its lifetime.
1 parent 3b9ceef commit 3821d74

File tree

2 files changed

+39
-18
lines changed

2 files changed

+39
-18
lines changed

lib/Dialect/FIRRTL/Transforms/LowerClasses.cpp

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,18 @@ static bool shouldCreateClassImpl(igraph::InstanceGraphNode *node) {
8181
/// to the targeted operation.
8282
struct PathInfo {
8383
PathInfo() = default;
84-
PathInfo(Operation *op, FlatSymbolRefAttr symRef,
84+
PathInfo(Location loc, bool canBeInstanceTarget, FlatSymbolRefAttr symRef,
8585
StringAttr altBasePathModule)
86-
: op(op), symRef(symRef), altBasePathModule(altBasePathModule) {
87-
assert(op && "op must not be null");
86+
: loc(loc), canBeInstanceTarget(canBeInstanceTarget), symRef(symRef),
87+
altBasePathModule(altBasePathModule) {
8888
assert(symRef && "symRef must not be null");
8989
}
9090

91-
operator bool() const { return op != nullptr; }
91+
/// The Location of the hardware component targeted by this path.
92+
std::optional<Location> loc = std::nullopt;
9293

93-
/// The hardware component targeted by this path.
94-
Operation *op = nullptr;
94+
/// Flag to indicate if the hardware component can be targeted as an instance.
95+
bool canBeInstanceTarget = false;
9596

9697
/// A reference to the hierarchical path targeting the op.
9798
FlatSymbolRefAttr symRef = nullptr;
@@ -584,17 +585,23 @@ LogicalResult PathTracker::updatePathInfoTable(PathInfoTable &pathInfoTable,
584585
auto [it, inserted] = pathInfoTable.table.try_emplace(entry.id);
585586
auto &pathInfo = it->second;
586587
if (!inserted) {
587-
auto diag =
588-
emitError(pathInfo.op->getLoc(), "duplicate identifier found");
588+
assert(pathInfo.loc.has_value() && "all PathInfo should have a Location");
589+
auto diag = emitError(pathInfo.loc.value(), "duplicate identifier found");
589590
diag.attachNote(entry.op->getLoc()) << "other identifier here";
590591
return failure();
591592
}
592593

593-
if (entry.pathAttr)
594-
pathInfo = {entry.op, cache.getRefFor(entry.pathAttr),
595-
entry.altBasePathModule};
596-
else
597-
pathInfo.op = entry.op;
594+
// Check if the op is targetable by an instance target. The op pointer may
595+
// be invalidated later, so this is the last time we want to access it here.
596+
bool canBeInstanceTarget = isa<InstanceOp, FModuleLike>(entry.op);
597+
598+
if (entry.pathAttr) {
599+
pathInfo = {entry.op->getLoc(), canBeInstanceTarget,
600+
cache.getRefFor(entry.pathAttr), entry.altBasePathModule};
601+
} else {
602+
pathInfo.loc = entry.op->getLoc();
603+
pathInfo.canBeInstanceTarget = canBeInstanceTarget;
604+
}
598605
}
599606
return success();
600607
}
@@ -1453,14 +1460,14 @@ struct PathOpConversion : public OpConversionPattern<firrtl::PathOp> {
14531460
ConversionPatternRewriter &rewriter) const override {
14541461
auto *context = op->getContext();
14551462
auto pathType = om::PathType::get(context);
1456-
auto pathInfo = pathInfoTable.table.lookup(op.getTarget());
1463+
auto pathInfoIt = pathInfoTable.table.find(op.getTarget());
14571464

14581465
// The 0'th argument is the base path by default.
14591466
auto basePath = op->getBlock()->getArgument(0);
14601467

14611468
// If the target was optimized away, then replace the path operation with
14621469
// a deleted path.
1463-
if (!pathInfo) {
1470+
if (pathInfoIt == pathInfoTable.table.end()) {
14641471
if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
14651472
return emitError(op.getLoc(), "DontTouch target was deleted");
14661473
if (op.getTargetKind() == firrtl::TargetKind::Instance)
@@ -1469,6 +1476,7 @@ struct PathOpConversion : public OpConversionPattern<firrtl::PathOp> {
14691476
return success();
14701477
}
14711478

1479+
auto pathInfo = pathInfoIt->second;
14721480
auto symbol = pathInfo.symRef;
14731481

14741482
// Convert the target kind to an OMIR target. Member references are updated
@@ -1482,15 +1490,15 @@ struct PathOpConversion : public OpConversionPattern<firrtl::PathOp> {
14821490
targetKind = om::TargetKind::Reference;
14831491
break;
14841492
case firrtl::TargetKind::Instance:
1485-
if (!isa<InstanceOp, FModuleLike>(pathInfo.op))
1493+
if (!pathInfo.canBeInstanceTarget)
14861494
return emitError(op.getLoc(), "invalid target for instance path")
1487-
.attachNote(pathInfo.op->getLoc())
1495+
.attachNote(pathInfo.loc)
14881496
<< "target not instance or module";
14891497
targetKind = om::TargetKind::Instance;
14901498
break;
14911499
case firrtl::TargetKind::MemberInstance:
14921500
case firrtl::TargetKind::MemberReference:
1493-
if (isa<InstanceOp, FModuleLike>(pathInfo.op))
1501+
if (pathInfo.canBeInstanceTarget)
14941502
targetKind = om::TargetKind::MemberInstance;
14951503
else
14961504
targetKind = om::TargetKind::MemberReference;

test/Dialect/FIRRTL/lower-classes.mlir

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,3 +458,16 @@ firrtl.circuit "OwningModulePrefix" {
458458
firrtl.path reference distinct[0]<>
459459
}
460460
}
461+
462+
// CHECK-LABEL: firrtl.circuit "PathTargetReplaced"
463+
firrtl.circuit "PathTargetReplaced" {
464+
// CHECK: hw.hierpath private [[NLA:@.+]] [@PathTargetReplaced::[[SYM:@.+]]]
465+
firrtl.module @PathTargetReplaced() {
466+
// CHECK: firrtl.instance replaced sym [[SYM]]
467+
firrtl.instance replaced {annotations = [{class = "circt.tracker", id = distinct[0]<>}]} @WillBeReplaced(out output: !firrtl.integer)
468+
// CHECK: om.path_create instance %basepath [[NLA]]
469+
%path = firrtl.path instance distinct[0]<>
470+
}
471+
firrtl.module private @WillBeReplaced(out %output: !firrtl.integer) {
472+
}
473+
}

0 commit comments

Comments
 (0)