Skip to content

Commit 97462a3

Browse files
committed
C++: Include more expressions in 'asExpr' in local expression flow.
1 parent d25a312 commit 97462a3

File tree

1 file changed

+60
-6
lines changed

1 file changed

+60
-6
lines changed

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

Lines changed: 60 additions & 6 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,11 +1613,64 @@ 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-
* dataflow step.
1616+
* Holds if `n` is an indirect operand of a `PointerArithmeticInstruction`, and
1617+
* `e` is the result of loading from the `PointerArithmeticInstruction`.
16171618
*/
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+
16181672
private predicate localStepFromNonExpr(Node n1, Node n2) {
1619-
not exists(n1.asExpr()) and
1673+
not exists(asExpr(n1)) and
16201674
localFlowStep(n1, n2)
16211675
}
16221676

@@ -1627,7 +1681,7 @@ private module ExprFlowCached {
16271681
pragma[nomagic]
16281682
private predicate localStepsToExpr(Node n1, Node n2, Expr e2) {
16291683
localStepFromNonExpr*(n1, n2) and
1630-
e2 = n2.asExpr()
1684+
e2 = asExpr(n2)
16311685
}
16321686

16331687
/**
@@ -1638,7 +1692,7 @@ private module ExprFlowCached {
16381692
exists(Node mid |
16391693
localFlowStep(n1, mid) and
16401694
localStepsToExpr(mid, n2, e2) and
1641-
e1 = n1.asExpr()
1695+
e1 = asExpr(n1)
16421696
)
16431697
}
16441698

0 commit comments

Comments
 (0)