Backed out previous changes to Broken and Ast.
The AstVar for __Vtask_wrap__1__Vfuncout was being deleted during V3Const’s constant-folding of the dead if (has && obj != null) block (has is constant 0), but its corresponding AstVarScope (which lives on the scope’s varsp() list, not under the deleted subtree) was left behind, causing both the ASAN UAF and the --debug “Broken link” on AstVarScope::m_varp. In src/V3Const.cpp, when visit(AstNodeIf*) folds a constant-condition if, and when visit(AstExprStmt*) drops the statement side, we now collect any AstVar declarations under the soon-to-be-deleted subtree using foreachAndNext, and unlink/delete the matching AstVarScopes from the current AstScope::varsp() list before pushDeletep(nodep). Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
This commit is contained in:
parent
e0eb99e8ae
commit
e4e8d04b13
|
|
@ -951,20 +951,8 @@ void AstNode::deleteTreeIter() {
|
|||
if (nodep->m_op3p) nodep->m_op3p->deleteTreeIter();
|
||||
if (nodep->m_op4p) nodep->m_op4p->deleteTreeIter();
|
||||
nodep->m_nextp = nullptr;
|
||||
|
||||
bool skipDelete = false;
|
||||
if (VN_IS(nodep, Var) && nodep->m_backp) {
|
||||
// If we are deleting a Var that is still linked to a parent (or list),
|
||||
// it implies we are deleting the parent/list.
|
||||
// In this case, we must NOT delete the Var yet, because there might be
|
||||
// AstVarScopes pointing to it (which are not children of the Var).
|
||||
// We leave the Var unlinked but allocated. V3Dead will later find it
|
||||
// (via AstVarScope) and delete it properly (at which point backp will be null).
|
||||
skipDelete = true;
|
||||
}
|
||||
|
||||
nodep->m_backp = nullptr;
|
||||
if (!skipDelete) { nodep->deleteNode(); }
|
||||
nodep->deleteNode();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -158,10 +158,6 @@ class BrokenCheckVisitor final : public VNVisitorConst {
|
|||
std::map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
|
||||
// Local variables declared in the scope of the current statement
|
||||
std::vector<std::unordered_set<const AstVar*>> m_localsStack;
|
||||
// Number of write references encountered
|
||||
size_t m_nWriteRefs = 0;
|
||||
// Number of function calls encountered
|
||||
size_t m_nCalls = 0;
|
||||
|
||||
// STATE - for current visit position (use VL_RESTORER)
|
||||
const AstCFunc* m_cfuncp = nullptr; // Current CFunc, if any
|
||||
|
|
@ -229,17 +225,7 @@ private:
|
|||
}
|
||||
// VISITORS
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
processEnter(nodep);
|
||||
iterateConst(nodep->rhsp());
|
||||
const size_t nWriteRefs = m_nWriteRefs;
|
||||
const size_t nCalls = m_nCalls;
|
||||
iterateConst(nodep->lhsp());
|
||||
// TODO: Enable this when #6756 is fixed
|
||||
// Only check if there are no calls on the LHS, as calls might return an LValue
|
||||
if (false && v3Global.assertDTypesResolved() && m_nCalls == nCalls) {
|
||||
UASSERT_OBJ(m_nWriteRefs > nWriteRefs, nodep, "No write refs on LHS of assignment");
|
||||
}
|
||||
processExit(nodep);
|
||||
processAndIterate(nodep);
|
||||
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && VN_IS(nodep->lhsp(), NodeVarRef)
|
||||
&& !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
|
||||
nodep, "Assignment LHS is not an lvalue");
|
||||
|
|
@ -284,19 +270,6 @@ private:
|
|||
}
|
||||
}
|
||||
}
|
||||
if (nodep->access().isWriteOrRW()) ++m_nWriteRefs;
|
||||
}
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
++m_nCalls;
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstCMethodHard* nodep) override {
|
||||
++m_nCalls;
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
++m_nCalls;
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
|
||||
|
|
@ -388,11 +361,6 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
|
|||
UASSERT_OBJ(nodep->brokenState() != brokenCntCurrent, nodep,
|
||||
"AstNode is already in tree at another location");
|
||||
if (nodep->maybePointedTo()) s_linkableTable.addLinkable(nodep);
|
||||
// Some cross-links point at nodes not reachable via op1-op4/nextp.
|
||||
// AstVarScope::m_varp is one such link; ensure targets are considered linkable.
|
||||
if (AstVarScope* const vscp = VN_CAST(nodep, VarScope)) {
|
||||
if (AstNode* const varp = vscp->varp()) s_linkableTable.addLinkable(varp);
|
||||
}
|
||||
nodep->brokenState(brokenCntCurrent);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3031,6 +3031,21 @@ class ConstVisitor final : public VNVisitor {
|
|||
void visit(AstExprStmt* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (!AstNode::afterCommentp(nodep->stmtsp())) {
|
||||
if (nodep->stmtsp()) {
|
||||
AstScope* const scopep = VN_CAST(const_cast<AstNode*>(m_scopep), Scope);
|
||||
if (scopep) {
|
||||
std::unordered_set<AstVar*> varps;
|
||||
nodep->stmtsp()->foreachAndNext([&](AstVar* varp) { varps.insert(varp); });
|
||||
if (!varps.empty()) {
|
||||
for (AstVarScope *vscp = scopep->varsp(), *nextp; vscp; vscp = nextp) {
|
||||
nextp = VN_AS(vscp->nextp(), VarScope);
|
||||
if (varps.find(vscp->varp()) != varps.end()) {
|
||||
VL_DO_DANGLING(pushDeletep(vscp->unlinkFrBack()), vscp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
UINFO(8, "ExprStmt(...) " << nodep << " " << nodep->resultp());
|
||||
nodep->replaceWith(nodep->resultp()->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
|
@ -3394,22 +3409,47 @@ class ConstVisitor final : public VNVisitor {
|
|||
iterateChildren(nodep);
|
||||
if (m_doNConst) {
|
||||
if (const AstConst* const constp = VN_CAST(nodep->condp(), Const)) {
|
||||
auto deleteVarScopesUnder = [&](AstNode* subtreep) {
|
||||
if (!subtreep) return;
|
||||
AstScope* const scopep = VN_CAST(const_cast<AstNode*>(m_scopep), Scope);
|
||||
if (!scopep) return;
|
||||
std::unordered_set<AstVar*> varps;
|
||||
subtreep->foreachAndNext([&](AstVar* varp) { varps.insert(varp); });
|
||||
if (varps.empty()) return;
|
||||
for (AstVarScope *vscp = scopep->varsp(), *nextp; vscp; vscp = nextp) {
|
||||
nextp = VN_AS(vscp->nextp(), VarScope);
|
||||
if (varps.find(vscp->varp()) != varps.end()) {
|
||||
VL_DO_DANGLING(pushDeletep(vscp->unlinkFrBack()), vscp);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AstNode* keepp = nullptr;
|
||||
AstNode* delp = nullptr;
|
||||
if (constp->isZero()) {
|
||||
UINFO(4, "IF(0,{any},{x}) => {x}: " << nodep);
|
||||
keepp = nodep->elsesp();
|
||||
delp = nodep->thensp();
|
||||
} else if (!m_doV || constp->isNeqZero()) { // Might be X in Verilog
|
||||
UINFO(4, "IF(!0,{x},{any}) => {x}: " << nodep);
|
||||
keepp = nodep->thensp();
|
||||
delp = nodep->elsesp();
|
||||
} else {
|
||||
UINFO(4, "IF condition is X, retaining: " << nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we delete a branch that contains variable declarations, also delete the
|
||||
// corresponding varscopes so we don't leave dangling AstVarScope::m_varp pointers.
|
||||
deleteVarScopesUnder(delp);
|
||||
|
||||
if (keepp) {
|
||||
keepp->unlinkFrBackWithNext();
|
||||
nodep->replaceWith(keepp);
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
deleteVarScopesUnder(nodep->thensp());
|
||||
deleteVarScopesUnder(nodep->elsesp());
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue