Skip to content

Commit a7173e0

Browse files
authored
Merge pull request #20443 from hvitved/rust/ssa-adjust-write-note
Rust: Adjust SSA write node for (compound) assignments
2 parents 7670a2b + 7cac226 commit a7173e0

File tree

12 files changed

+3233
-3130
lines changed

12 files changed

+3233
-3130
lines changed

rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,36 @@ class ExitCfgNode = CfgImpl::ExitNode;
1616

1717
class AnnotatedExitCfgNode = CfgImpl::AnnotatedExitNode;
1818

19+
/** A variable access. */
20+
final class VariableAccessCfgNode extends PathExprBaseCfgNode {
21+
private VariableAccess a;
22+
23+
VariableAccessCfgNode() { a = this.getAstNode() }
24+
25+
/** Gets the underlying `VariableAccess`. */
26+
VariableAccess getAccess() { result = a }
27+
}
28+
29+
/** A variable write. */
30+
final class VariableWriteAccessCfgNode extends VariableAccessCfgNode {
31+
private VariableWriteAccess a;
32+
33+
VariableWriteAccessCfgNode() { a = this.getAstNode() }
34+
35+
/** Gets the underlying `VariableWriteAccess`. */
36+
VariableWriteAccess getAccess() { result = a }
37+
}
38+
39+
/** A variable read. */
40+
final class VariableReadAccessCfgNode extends VariableAccessCfgNode {
41+
private VariableReadAccess a;
42+
43+
VariableReadAccessCfgNode() { a = this.getAstNode() }
44+
45+
/** Gets the underlying `VariableReadAccess`. */
46+
VariableReadAccess getAccess() { result = a }
47+
}
48+
1949
/**
2050
* An assignment expression, for example
2151
*
@@ -24,12 +54,42 @@ class AnnotatedExitCfgNode = CfgImpl::AnnotatedExitNode;
2454
* ```
2555
*/
2656
final class AssignmentExprCfgNode extends BinaryExprCfgNode {
27-
AssignmentExpr a;
57+
AssignmentExprChildMapping a;
2858

2959
AssignmentExprCfgNode() { a = this.getBinaryExpr() }
3060

3161
/** Gets the underlying `AssignmentExpr`. */
3262
AssignmentExpr getAssignmentExpr() { result = a }
63+
64+
/**
65+
* Gets a write access that occurs in the left-hand side of this assignment expression.
66+
*/
67+
VariableWriteAccessCfgNode getAWriteAccess() {
68+
a = result.getAccess().getAssignmentExpr() and
69+
any(ChildMapping mapping).hasCfgChild(a, result.getAccess(), this, result)
70+
}
71+
}
72+
73+
/**
74+
* A compound assignment expression, for example:
75+
* ```rust
76+
* x += y;
77+
* ```
78+
*
79+
* Note that compound assignment expressions are syntatic sugar for
80+
* trait invocations, i.e., the above actually means
81+
*
82+
* ```rust
83+
* (&mut x).add_assign(y);
84+
* ```
85+
*/
86+
final class CompoundAssignmentExprCfgNode extends BinaryExprCfgNode {
87+
CompoundAssignmentExpr a;
88+
89+
CompoundAssignmentExprCfgNode() { a = this.getBinaryExpr() }
90+
91+
/** Gets the underlying `CompoundAssignmentExpr`. */
92+
CompoundAssignmentExpr getCompoundAssignmentExpr() { result = a }
3393
}
3494

3595
/**

rust/ql/lib/codeql/rust/controlflow/internal/CfgNodes.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ class FormatArgsExprChildMapping extends ParentAstNode, CfgImpl::ExprTrees::Form
8181
override predicate relevantChild(AstNode child) { child = this.getChildNode(_) }
8282
}
8383

84+
class AssignmentExprChildMapping extends ParentAstNode, AssignmentExpr {
85+
override predicate relevantChild(AstNode child) {
86+
child.(VariableWriteAccess).getAssignmentExpr() = this
87+
}
88+
}
89+
8490
private class ChildMappingImpl extends ChildMapping {
8591
/** Gets a CFG node for `child`, where `child` is a relevant child node of `parent`. */
8692
private CfgNode getRelevantChildCfgNode(AstNode parent, AstNode child) {

rust/ql/lib/codeql/rust/dataflow/Ssa.qll

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,20 @@ module Ssa {
171171
private CfgNode write;
172172

173173
WriteDefinition() {
174-
exists(BasicBlock bb, int i, Variable v |
174+
exists(BasicBlock bb, int i, Variable v, CfgNode n |
175175
this.definesAt(v, bb, i) and
176-
SsaImpl::variableWriteActual(bb, i, v, write)
176+
SsaImpl::variableWriteActual(bb, i, v, n)
177+
|
178+
write.(VariableAccessCfgNode).getAccess().getVariable() = v and
179+
(
180+
write = n.(AssignmentExprCfgNode).getAWriteAccess()
181+
or
182+
write = n.(CompoundAssignmentExprCfgNode).getLhs()
183+
)
184+
or
185+
not n instanceof AssignmentExprCfgNode and
186+
not n instanceof CompoundAssignmentExprCfgNode and
187+
write = n
177188
)
178189
}
179190

rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ module LocalFlow {
263263
or
264264
// An edge from a pattern/expression to its corresponding SSA definition.
265265
nodeFrom.(AstCfgFlowNode).getCfgNode() =
266-
nodeTo.(SsaNode).asDefinition().(Ssa::WriteDefinition).getControlFlowNode()
266+
nodeTo.(SsaNode).asDefinition().(Ssa::WriteDefinition).getWriteAccess()
267267
or
268268
nodeFrom.(SourceParameterNode).getParameter().(ParamCfgNode).getPat() = nodeTo.asPat()
269269
or

rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,22 @@ private predicate isInUninitializedLet(Name name) {
1818
)
1919
}
2020

21-
/** Holds if `write` writes to variable `v`. */
22-
predicate variableWrite(AstNode write, Variable v) {
21+
/** Holds if `write` writes to variable `v` via `access`. */
22+
predicate variableWrite(AstNode write, AstNode access, Variable v) {
2323
exists(Name name |
2424
name = write and
25+
access = write and
2526
name = v.getName() and
2627
not isInUninitializedLet(name)
2728
)
2829
or
29-
exists(VariableAccess access |
30-
access = write and
31-
access.getVariable() = v
32-
|
33-
access instanceof VariableWriteAccess
30+
v = access.(VariableAccess).getVariable() and
31+
(
32+
write = access.(VariableWriteAccess).getAssignmentExpr()
3433
or
3534
// Although compound assignments, like `x += y`, may in fact not update `x`,
3635
// it makes sense to treat them as such
37-
access = any(CompoundAssignmentExpr cae).getLhs()
36+
access = write.(CompoundAssignmentExpr).getLhs()
3837
)
3938
}
4039

@@ -226,7 +225,7 @@ private module Cached {
226225
cached
227226
predicate variableWriteActual(BasicBlock bb, int i, Variable v, CfgNode write) {
228227
bb.getNode(i) = write and
229-
variableWrite(write.getAstNode(), v)
228+
variableWrite(write.getAstNode(), _, v)
230229
}
231230

232231
cached

rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -679,11 +679,11 @@ module Impl {
679679
}
680680

681681
/** Holds if `e` occurs in the LHS of an assignment or compound assignment. */
682-
private predicate assignmentExprDescendant(Expr e) {
683-
e = any(AssignmentExpr ae).getLhs()
682+
private predicate assignmentExprDescendant(AssignmentExpr ae, Expr e) {
683+
e = ae.getLhs()
684684
or
685685
exists(Expr mid |
686-
assignmentExprDescendant(mid) and
686+
assignmentExprDescendant(ae, mid) and
687687
getImmediateParentAdj(e) = mid and
688688
not mid instanceof DerefExpr and
689689
not mid instanceof FieldExpr and
@@ -693,11 +693,16 @@ module Impl {
693693

694694
/** A variable write. */
695695
class VariableWriteAccess extends VariableAccess {
696+
private AssignmentExpr ae;
697+
696698
cached
697699
VariableWriteAccess() {
698700
Cached::ref() and
699-
assignmentExprDescendant(this)
701+
assignmentExprDescendant(ae, this)
700702
}
703+
704+
/** Gets the assignment expression that has this write access in the left-hand side. */
705+
AssignmentExpr getAssignmentExpr() { result = ae }
701706
}
702707

703708
/** A variable read. */

rust/ql/src/queries/unusedentities/UnusedValue.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import UnusedVariable
1616

1717
from AstNode write, Ssa::Variable v
1818
where
19-
variableWrite(write, v) and
19+
variableWrite(_, write, v) and
2020
not v instanceof DiscardVariable and
2121
not write.isInMacroExpansion() and
2222
not isAllowableUnused(v) and
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
multipleCallTargets
2-
| main.rs:89:19:89:40 | ...::from(...) |
3-
| main.rs:111:19:111:40 | ...::from(...) |
2+
| main.rs:91:19:91:40 | ...::from(...) |
3+
| main.rs:113:19:113:40 | ...::from(...) |

0 commit comments

Comments
 (0)