Skip to content

Commit 136769d

Browse files
authored
Merge pull request github#12507 from MathiasVP/fix-as-expr-performance-2
C++: Map some indirect nodes to expressions in `localExprFlowStep`
2 parents a505165 + 58c1518 commit 136769d

File tree

1 file changed

+69
-11
lines changed

1 file changed

+69
-11
lines changed

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,8 @@ private class IndirectOperandIndirectExprNode extends IndirectExprNodeBase, RawI
11421142
}
11431143

11441144
private class IndirectInstructionIndirectExprNode extends IndirectExprNodeBase,
1145-
RawIndirectInstruction {
1145+
RawIndirectInstruction
1146+
{
11461147
IndirectInstructionIndirectExprNode() { indirectExprNodeShouldBeIndirectInstruction(this, _) }
11471148

11481149
final override Expr getConvertedExpr(int index) {
@@ -1612,39 +1613,96 @@ predicate localInstructionFlow(Instruction e1, Instruction e2) {
16121613
cached
16131614
private module ExprFlowCached {
16141615
/**
1615-
* Holds if `n1.asExpr()` doesn't have a result and `n1` flows to `n2` in a single
1616+
* Holds if `n` is an indirect operand of a `PointerArithmeticInstruction`, and
1617+
* `e` is the result of loading from the `PointerArithmeticInstruction`.
1618+
*/
1619+
private predicate isIndirectBaseOfArrayAccess(IndirectOperand n, Expr e) {
1620+
exists(LoadInstruction load, PointerArithmeticInstruction pai |
1621+
pai = load.getSourceAddress() and
1622+
pai.getLeftOperand() = n.getOperand() and
1623+
n.getIndirectionIndex() = 1 and
1624+
e = load.getConvertedResultExpression()
1625+
)
1626+
}
1627+
1628+
/**
1629+
* Gets the expression associated with node `n`, if any.
1630+
*
1631+
* Unlike `n.asExpr()`, this predicate will also get the
1632+
* expression `*(x + i)` when `n` is the indirect node
1633+
* for `x`. This ensures that an assignment in a long chain
1634+
* of assignments in a macro expansion is properly mapped
1635+
* to the previous assignment. For example, in:
1636+
* ```cpp
1637+
* *x = source();
1638+
* use(x[0]);
1639+
* use(x[1]);
1640+
* ...
1641+
* use(x[i]);
1642+
* use(x[i+1]);
1643+
* ...
1644+
* use(x[N]);
1645+
* ```
1646+
* To see what the problem would be if `asExpr(n)` was replaced
1647+
* with `n.asExpr()`, consider the transitive closure over
1648+
* `localStepFromNonExpr` in `localStepsToExpr`. We start at `n2`
1649+
* for which `n.asExpr()` exists. For example, `n2` in the above
1650+
* example could be a `x[i]` in any of the `use(x[i])` above.
1651+
*
1652+
* We then step to a dataflow predecessor of `n2`. In the above
1653+
* code fragment, thats the indirect node corresponding to `x` in
1654+
* `x[i-1]`. Since this doesn't have a result for `Node::asExpr()`
1655+
* we continue with the recursion until we reach `*x = source()`
1656+
* which does have a result for `Node::asExpr()`.
1657+
*
1658+
* If `N` is very large this blows up.
1659+
*
1660+
* To fix this, we map the indirect node corresponding to `x` to
1661+
* in `x[i - 1]` to the `x[i - 1]` expression. This ensures that
1662+
* `x[i]` steps to the expression `x[i - 1]` without traversing the
1663+
* entire chain.
1664+
*/
1665+
private Expr asExpr(Node n) {
1666+
isIndirectBaseOfArrayAccess(n, result)
1667+
or
1668+
not isIndirectBaseOfArrayAccess(n, _) and
1669+
result = n.asExpr()
1670+
}
1671+
1672+
/**
1673+
* Holds if `asExpr(n1)` doesn't have a result and `n1` flows to `n2` in a single
16161674
* dataflow step.
16171675
*/
16181676
private predicate localStepFromNonExpr(Node n1, Node n2) {
1619-
not exists(n1.asExpr()) and
1677+
not exists(asExpr(n1)) and
16201678
localFlowStep(n1, n2)
16211679
}
16221680

16231681
/**
1624-
* Holds if `n1.asExpr()` doesn't have a result, `n2.asExpr() = e2` and
1625-
* `n2` is the first node reachable from `n1` such that `n2.asExpr()` exists.
1682+
* Holds if `asExpr(n1)` doesn't have a result, `asExpr(n2) = e2` and
1683+
* `n2` is the first node reachable from `n1` such that `asExpr(n2)` exists.
16261684
*/
16271685
pragma[nomagic]
16281686
private predicate localStepsToExpr(Node n1, Node n2, Expr e2) {
16291687
localStepFromNonExpr*(n1, n2) and
1630-
e2 = n2.asExpr()
1688+
e2 = asExpr(n2)
16311689
}
16321690

16331691
/**
1634-
* Holds if `n1.asExpr() = e1` and `n2.asExpr() = e2` and `n2` is the first node
1635-
* reachable from `n1` such that `n2.asExpr()` exists.
1692+
* Holds if `asExpr(n1) = e1` and `asExpr(n2) = e2` and `n2` is the first node
1693+
* reachable from `n1` such that `asExpr(n2)` exists.
16361694
*/
16371695
private predicate localExprFlowSingleExprStep(Node n1, Expr e1, Node n2, Expr e2) {
16381696
exists(Node mid |
16391697
localFlowStep(n1, mid) and
16401698
localStepsToExpr(mid, n2, e2) and
1641-
e1 = n1.asExpr()
1699+
e1 = asExpr(n1)
16421700
)
16431701
}
16441702

16451703
/**
1646-
* Holds if `n1.asExpr() = e1` and `e1 != e2` and `n2` is the first reachable node from
1647-
* `n1` such that `n2.asExpr() = e2`.
1704+
* Holds if `asExpr(n1) = e1` and `e1 != e2` and `n2` is the first reachable node from
1705+
* `n1` such that `asExpr(n2) = e2`.
16481706
*/
16491707
private predicate localExprFlowStepImpl(Node n1, Expr e1, Node n2, Expr e2) {
16501708
exists(Node n, Expr e | localExprFlowSingleExprStep(n1, e1, n, e) |

0 commit comments

Comments
 (0)