Skip to content

Commit 7476aea

Browse files
committed
Rust: Handle ref patterns in data flow
To do this we: * Let SSA writes target the name inside identifier patterns instead of the pattern itself * Include relevant names in the data flow graph * Add a store step from a identifier patterns with `ref` into the contained name. So we have an edge `ref a` -> `a` that stores in the reference content type.
1 parent 5da1425 commit 7476aea

File tree

20 files changed

+853
-221
lines changed

20 files changed

+853
-221
lines changed

rust/ql/.generated.list

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,7 @@ module PatternTrees {
662662
}
663663

664664
override predicate succ(AstNode pred, AstNode succ, Completion c) {
665-
// Edge from succesful subpattern to name
665+
// Edge from successful subpattern to name
666666
super.succ(pred, succ, c) and c.(MatchCompletion).succeeded()
667667
or
668668
// Edge from name to the identifier pattern itself

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

Lines changed: 83 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ module Ssa {
195195
)
196196
or
197197
exists(LetStmtCfgNode ls |
198-
ls.getPat() = write and
198+
ls.getPat().(IdentPatCfgNode).getName() = write and
199199
ls.getInitializer() = value
200200
)
201201
}

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,15 @@ module Node {
273273
override PatCfgNode asPat() { result = n }
274274
}
275275

276+
/** A data flow node that corresponds to a name node in the CFG. */
277+
final class NameNode extends AstCfgFlowNode, TNameNode {
278+
override NameCfgNode n;
279+
280+
NameNode() { this = TNameNode(n) }
281+
282+
NameCfgNode asName() { result = n }
283+
}
284+
276285
/**
277286
* The value of a parameter at function entry, viewed as a node in a data
278287
* flow graph.
@@ -584,11 +593,23 @@ module LocalFlow {
584593
predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
585594
nodeFrom.getCfgNode() = getALastEvalNode(nodeTo.getCfgNode())
586595
or
596+
// An edge from the right-hand side of a let statement to the left-hand side.
587597
exists(LetStmtCfgNode s |
588598
nodeFrom.getCfgNode() = s.getInitializer() and
589599
nodeTo.getCfgNode() = s.getPat()
590600
)
591601
or
602+
exists(IdentPatCfgNode p |
603+
not p.isRef() and
604+
nodeFrom.getCfgNode() = p and
605+
nodeTo.getCfgNode() = p.getName()
606+
)
607+
or
608+
exists(SelfParamCfgNode self |
609+
nodeFrom.getCfgNode() = self and
610+
nodeTo.getCfgNode() = self.getName()
611+
)
612+
or
592613
// An edge from a pattern/expression to its corresponding SSA definition.
593614
nodeFrom.(Node::AstCfgFlowNode).getCfgNode() =
594615
nodeTo.(Node::SsaNode).getDefinitionExt().(Ssa::WriteDefinition).getControlFlowNode()
@@ -1266,6 +1287,14 @@ module RustDataFlow implements InputSig<Location> {
12661287
node2.asExpr().(ArrayListExprCfgNode).getAnExpr()
12671288
]
12681289
or
1290+
// Store from a `ref` identifier pattern into the contained name.
1291+
exists(IdentPatCfgNode p |
1292+
c instanceof ReferenceContent and
1293+
p.isRef() and
1294+
node1.asPat() = p and
1295+
node2.(Node::NameNode).asName() = p.getName()
1296+
)
1297+
or
12691298
fieldAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c)
12701299
or
12711300
referenceAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c)
@@ -1560,6 +1589,7 @@ private module Cached {
15601589
TExprNode(ExprCfgNode n) { Stages::DataFlowStage::ref() } or
15611590
TSourceParameterNode(ParamBaseCfgNode p) or
15621591
TPatNode(PatCfgNode p) or
1592+
TNameNode(NameCfgNode n) { n.getName() = any(Variable v).getName() } or
15631593
TExprPostUpdateNode(ExprCfgNode e) {
15641594
isArgumentForCall(e, _, _) or
15651595
lambdaCallExpr(_, _, e) or

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,25 @@ private import Cfg
77
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl as ControlFlowGraphImpl
88
private import codeql.ssa.Ssa as SsaImplCommon
99

10-
/** Holds if `v` is introduced like `let v : i64;`. */
11-
private predicate isUnitializedLet(IdentPat pat, Variable v) {
12-
pat = v.getPat() and
10+
/**
11+
* Holds if `name` occurs in the left-hand side of an uninitialized let
12+
* statement such as in `let name : i64;`.
13+
*/
14+
private predicate isInUninitializedLet(Name name) {
1315
exists(LetStmt let |
14-
let = v.getLetStmt() and
16+
let.getPat().(IdentPat).getName() = name and
1517
not let.hasInitializer()
1618
)
1719
}
1820

1921
/** Holds if `write` writes to variable `v`. */
2022
predicate variableWrite(AstNode write, Variable v) {
21-
exists(IdentPat pat |
22-
pat = write and
23-
pat = v.getPat() and
24-
not isUnitializedLet(pat, v)
23+
exists(Name name |
24+
name = write and
25+
name = v.getName() and
26+
not isInUninitializedLet(name)
2527
)
2628
or
27-
exists(SelfParam self | self = write and self = v.getSelfParam())
28-
or
2929
exists(VariableAccess access |
3030
access = write and
3131
access.getVariable() = v

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

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -74,58 +74,66 @@ module Impl {
7474
* where `definingNode` is the entire `Either::Left(x) | Either::Right(x)`
7575
* pattern.
7676
*/
77-
private predicate variableDecl(AstNode definingNode, AstNode p, string name) {
78-
p =
79-
any(SelfParam sp |
80-
definingNode = sp.getName() and
81-
name = sp.getName().getText() and
77+
private predicate variableDecl(AstNode definingNode, Name name, string text) {
78+
text = name.getText() and
79+
(
80+
exists(SelfParam sp |
81+
name = sp.getName() and
82+
definingNode = name and
8283
// exclude self parameters from functions without a body as these are
8384
// trait method declarations without implementations
8485
not exists(Function f | not f.hasBody() and f.getParamList().getSelfParam() = sp)
8586
)
86-
or
87-
p =
88-
any(IdentPat pat |
87+
or
88+
exists(IdentPat pat |
89+
name = pat.getName() and
8990
(
9091
definingNode = getOutermostEnclosingOrPat(pat)
9192
or
92-
not exists(getOutermostEnclosingOrPat(pat)) and definingNode = pat.getName()
93+
not exists(getOutermostEnclosingOrPat(pat)) and definingNode = name
9394
) and
94-
name = pat.getName().getText() and
9595
// exclude for now anything starting with an uppercase character, which may be a reference to
9696
// an enum constant (e.g. `None`). This excludes static and constant variables (UPPERCASE),
9797
// which we don't appear to recognize yet anyway. This also assumes programmers follow the
9898
// naming guidelines, which they generally do, but they're not enforced.
99-
not name.charAt(0).isUppercase() and
99+
not text.charAt(0).isUppercase() and
100100
// exclude parameters from functions without a body as these are trait method declarations
101101
// without implementations
102102
not exists(Function f | not f.hasBody() and f.getParamList().getAParam().getPat() = pat) and
103103
// exclude parameters from function pointer types (e.g. `x` in `fn(x: i32) -> i32`)
104104
not exists(FnPtrTypeRepr fp | fp.getParamList().getParam(_).getPat() = pat)
105105
)
106+
)
106107
}
107108

108109
/** A variable. */
109110
class Variable extends MkVariable {
110111
private AstNode definingNode;
111-
private string name;
112+
private string text;
112113

113-
Variable() { this = MkVariable(definingNode, name) }
114+
Variable() { this = MkVariable(definingNode, text) }
114115

115-
/** Gets the name of this variable. */
116-
string getName() { result = name }
116+
/** Gets the name of this variable as a string. */
117+
string getText() { result = text }
117118

118119
/** Gets the location of this variable. */
119120
Location getLocation() { result = definingNode.getLocation() }
120121

121122
/** Gets a textual representation of this variable. */
122-
string toString() { result = this.getName() }
123+
string toString() { result = this.getText() }
123124

124125
/** Gets an access to this variable. */
125126
VariableAccess getAnAccess() { result.getVariable() = this }
126127

127-
/** Gets the `self` parameter that declares this variable, if one exists. */
128-
SelfParam getSelfParam() { variableDecl(definingNode, result, name) }
128+
/**
129+
* Get the name of this variable.
130+
*
131+
* Normally, the name is unique, except when introduced in an or pattern.
132+
*/
133+
Name getName() { variableDecl(definingNode, result, text) }
134+
135+
/** Gets the `self` parameter that declares this variable, if any. */
136+
SelfParam getSelfParam() { result.getName() = this.getName() }
129137

130138
/**
131139
* Gets the pattern that declares this variable, if any.
@@ -138,7 +146,7 @@ module Impl {
138146
* }
139147
* ```
140148
*/
141-
IdentPat getPat() { variableDecl(definingNode, result, name) }
149+
IdentPat getPat() { result.getName() = this.getName() }
142150

143151
/** Gets the enclosing CFG scope for this variable declaration. */
144152
CfgScope getEnclosingCfgScope() { result = definingNode.getEnclosingCfgScope() }
@@ -204,6 +212,10 @@ module Impl {
204212
/** Gets the immediately enclosing variable scope of `n`. */
205213
private VariableScope getEnclosingScope(AstNode n) { result = getAnAncestorInVariableScope(n) }
206214

215+
/**
216+
* Get all the pattern ancestors of this variable up to an including the
217+
* root of the pattern.
218+
*/
207219
private Pat getAVariablePatAncestor(Variable v) {
208220
result = v.getPat()
209221
or
@@ -322,7 +334,7 @@ module Impl {
322334
* all nodes nester under `scope`, is `ord`.
323335
*/
324336
private predicate variableDeclInScope(Variable v, VariableScope scope, string name, int ord) {
325-
name = v.getName() and
337+
name = v.getText() and
326338
(
327339
parameterDeclInScope(v, scope) and
328340
ord = getPreOrderNumbering(scope, scope)

0 commit comments

Comments
 (0)