Skip to content

Commit 9da26a8

Browse files
committed
Rust: Avoid location based variable analysis
1 parent 422bec1 commit 9da26a8

File tree

7 files changed

+151
-43
lines changed

7 files changed

+151
-43
lines changed

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

+100-35
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ module Impl {
165165

166166
/**
167167
* A path expression that may access a local variable. These are paths that
168-
* only consists of a simple name (i.e., without generic arguments,
168+
* only consist of a simple name (i.e., without generic arguments,
169169
* qualifiers, etc.).
170170
*/
171171
private class VariableAccessCand extends PathExprBase {
@@ -231,80 +231,151 @@ module Impl {
231231
)
232232
}
233233

234+
private Element getImmediateChildMin(Element e, int index) {
235+
// A child may have multiple positions for different accessors,
236+
// so always use the first
237+
result = getImmediateChildAndAccessor(e, index, _) and
238+
index = min(int i | result = getImmediateChildAndAccessor(e, i, _) | i)
239+
}
240+
241+
private Element getImmediateChild(Element e, int index) {
242+
result = rank[index + 1](Element res, int i | res = getImmediateChildMin(e, i) | res order by i)
243+
}
244+
245+
pragma[nomagic]
246+
private Element getImmediateLastChild(Element e) {
247+
exists(int last |
248+
result = getImmediateChild(e, last) and
249+
not exists(getImmediateChild(e, last + 1))
250+
)
251+
}
252+
253+
/**
254+
* Gets the pre-order numbering of `n`, where the immediately enclosing
255+
* variable scope of `n` is `scope`.
256+
*/
257+
pragma[nomagic]
258+
private int getPreOrderNumbering(VariableScope scope, Element n) {
259+
n = scope and
260+
result = 0
261+
or
262+
exists(Element parent |
263+
not parent instanceof VariableScope
264+
or
265+
parent = scope
266+
|
267+
// first child of a previously numbered node
268+
result = getPreOrderNumbering(scope, parent) + 1 and
269+
n = getImmediateChild(parent, 0)
270+
or
271+
// non-first child of a previously numbered node
272+
exists(Element child, int i |
273+
result = getLastPreOrderNumbering(scope, child) + 1 and
274+
child = getImmediateChild(parent, i) and
275+
n = getImmediateChild(parent, i + 1)
276+
)
277+
)
278+
}
279+
280+
/**
281+
* Gets the pre-order numbering of the _last_ node nested under `n`, where the
282+
* immediately enclosing variable scope of `n` (and the last node) is `scope`.
283+
*/
284+
pragma[nomagic]
285+
private int getLastPreOrderNumbering(VariableScope scope, Element n) {
286+
exists(Element leaf |
287+
result = getPreOrderNumbering(scope, leaf) and
288+
leaf != scope and
289+
(
290+
not exists(getImmediateChild(leaf, _))
291+
or
292+
leaf instanceof VariableScope
293+
)
294+
|
295+
n = leaf
296+
or
297+
getImmediateLastChild(n) = leaf and
298+
not n instanceof VariableScope
299+
)
300+
or
301+
exists(Element mid |
302+
mid = getImmediateLastChild(n) and
303+
result = getLastPreOrderNumbering(scope, mid) and
304+
not mid instanceof VariableScope and
305+
not n instanceof VariableScope
306+
)
307+
}
308+
234309
/**
235310
* Holds if `v` is named `name` and is declared inside variable scope
236-
* `scope`, and `v` is bound starting from `(line, column)`.
311+
* `scope`. The pre-order numbering of the binding site of `v`, amongst
312+
* all nodes nester under `scope`, is `ord`.
237313
*/
238-
private predicate variableDeclInScope(
239-
Variable v, VariableScope scope, string name, int line, int column
240-
) {
314+
private predicate variableDeclInScope(Variable v, VariableScope scope, string name, int ord) {
241315
name = v.getName() and
242316
(
243317
parameterDeclInScope(v, scope) and
244-
scope.getLocation().hasLocationFileInfo(_, line, column, _, _)
318+
ord = getPreOrderNumbering(scope, scope)
245319
or
246320
exists(Pat pat | pat = getAVariablePatAncestor(v) |
247321
scope =
248322
any(MatchArmScope arm |
249323
arm.getPat() = pat and
250-
arm.getLocation().hasLocationFileInfo(_, line, column, _, _)
324+
ord = getPreOrderNumbering(scope, arm)
251325
)
252326
or
253327
exists(LetStmt let |
254328
let.getPat() = pat and
255329
scope = getEnclosingScope(let) and
256330
// for `let` statements, variables are bound _after_ the statement, i.e.
257331
// not in the RHS
258-
let.getLocation().hasLocationFileInfo(_, _, _, line, column)
332+
ord = getLastPreOrderNumbering(scope, let) + 1
259333
)
260334
or
261335
exists(IfExpr ie, LetExpr let |
262336
let.getPat() = pat and
263337
ie.getCondition() = let and
264338
scope = ie.getThen() and
265-
scope.getLocation().hasLocationFileInfo(_, line, column, _, _)
339+
ord = getPreOrderNumbering(scope, scope)
266340
)
267341
or
268342
exists(ForExpr fe |
269343
fe.getPat() = pat and
270344
scope = fe.getLoopBody() and
271-
scope.getLocation().hasLocationFileInfo(_, line, column, _, _)
345+
ord = getPreOrderNumbering(scope, scope)
272346
)
273347
or
274348
exists(WhileExpr we, LetExpr let |
275349
let.getPat() = pat and
276350
we.getCondition() = let and
277351
scope = we.getLoopBody() and
278-
scope.getLocation().hasLocationFileInfo(_, line, column, _, _)
352+
ord = getPreOrderNumbering(scope, scope)
279353
)
280354
)
281355
)
282356
}
283357

284358
/**
285-
* Holds if `cand` may access a variable named `name` at
286-
* `(startline, startcolumn, endline, endcolumn)` in the variable scope
287-
* `scope`.
359+
* Holds if `cand` may access a variable named `name` at pre-order number `ord`
360+
* in the variable scope `scope`.
288361
*
289362
* `nestLevel` is the number of nested scopes that need to be traversed
290363
* to reach `scope` from `cand`.
291364
*/
292365
private predicate variableAccessCandInScope(
293-
VariableAccessCand cand, VariableScope scope, string name, int nestLevel, int startline,
294-
int startcolumn, int endline, int endcolumn
366+
VariableAccessCand cand, VariableScope scope, string name, int nestLevel, int ord
295367
) {
296368
name = cand.getName() and
297369
scope = [cand.(VariableScope), getEnclosingScope(cand)] and
298-
cand.getLocation().hasLocationFileInfo(_, startline, startcolumn, endline, endcolumn) and
370+
ord = getPreOrderNumbering(scope, cand) and
299371
nestLevel = 0
300372
or
301373
exists(VariableScope inner |
302-
variableAccessCandInScope(cand, inner, name, nestLevel - 1, _, _, _, _) and
374+
variableAccessCandInScope(cand, inner, name, nestLevel - 1, _) and
303375
scope = getEnclosingScope(inner) and
304-
// Use the location of the inner scope as the location of the access, instead of the
305-
// actual access location. This allows us to collapse multiple accesses in inner
306-
// scopes to a single entity
307-
inner.getLocation().hasLocationFileInfo(_, startline, startcolumn, endline, endcolumn)
376+
// Use the pre-order number of the inner scope as the number of the access. This allows
377+
// us to collapse multiple accesses in inner scopes to a single entity
378+
ord = getPreOrderNumbering(scope, inner)
308379
)
309380
}
310381

@@ -375,15 +446,10 @@ module Impl {
375446
}
376447

377448
pragma[nomagic]
378-
predicate rankBy(
379-
string name, VariableScope scope, int startline, int startcolumn, int endline, int endcolumn
380-
) {
381-
variableDeclInScope(this.asVariable(), scope, name, startline, startcolumn) and
382-
endline = -1 and
383-
endcolumn = -1
449+
predicate rankBy(string name, VariableScope scope, int ord) {
450+
variableDeclInScope(this.asVariable(), scope, name, ord)
384451
or
385-
variableAccessCandInScope(this.asVariableAccessCand(), scope, name, _, startline, startcolumn,
386-
endline, endcolumn)
452+
variableAccessCandInScope(this.asVariableAccessCand(), scope, name, _, ord)
387453
}
388454
}
389455

@@ -396,11 +462,10 @@ module Impl {
396462

397463
int getRank(VariableScope scope, string name, VariableOrAccessCand v) {
398464
v =
399-
rank[result](VariableOrAccessCand v0, int startline, int startcolumn, int endline,
400-
int endcolumn |
401-
v0.rankBy(name, scope, startline, startcolumn, endline, endcolumn)
465+
rank[result](VariableOrAccessCand v0, int ord |
466+
v0.rankBy(name, scope, ord)
402467
|
403-
v0 order by startline, startcolumn, endline, endcolumn
468+
v0 order by ord
404469
)
405470
}
406471
}
@@ -440,7 +505,7 @@ module Impl {
440505
exists(int rnk |
441506
variableReachesRank(scope, name, v, rnk) and
442507
rnk = rankVariableOrAccess(scope, name, TVariableOrAccessCandVariableAccessCand(cand)) and
443-
variableAccessCandInScope(cand, scope, name, nestLevel, _, _, _, _)
508+
variableAccessCandInScope(cand, scope, name, nestLevel, _)
444509
)
445510
}
446511

rust/ql/test/library-tests/dataflow/strings/inline-taint-flow.expected

+30
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
models
22
| 1 | Summary: lang:alloc; <crate::string::String>::as_str; Argument[self]; ReturnValue; taint |
33
| 2 | Summary: lang:alloc; crate::fmt::format; Argument[0]; ReturnValue; taint |
4+
| 3 | Summary: lang:core; crate::hint::must_use; Argument[0]; ReturnValue; value |
45
edges
56
| main.rs:26:9:26:9 | s | main.rs:27:19:27:25 | s[...] | provenance | |
67
| main.rs:26:13:26:22 | source(...) | main.rs:26:9:26:9 | s | provenance | |
@@ -27,6 +28,19 @@ edges
2728
| main.rs:77:9:77:18 | formatted3 | main.rs:78:10:78:19 | formatted3 | provenance | |
2829
| main.rs:77:22:77:75 | ...::format(...) | main.rs:77:9:77:18 | formatted3 | provenance | |
2930
| main.rs:77:34:77:74 | MacroExpr | main.rs:77:22:77:75 | ...::format(...) | provenance | MaD:2 |
31+
| main.rs:82:9:82:10 | s1 | main.rs:86:18:86:25 | MacroExpr | provenance | |
32+
| main.rs:82:9:82:10 | s1 | main.rs:87:18:87:32 | MacroExpr | provenance | |
33+
| main.rs:82:14:82:23 | source(...) | main.rs:82:9:82:10 | s1 | provenance | |
34+
| main.rs:86:10:86:26 | res | main.rs:86:18:86:25 | { ... } | provenance | |
35+
| main.rs:86:18:86:25 | ...::format(...) | main.rs:86:10:86:26 | res | provenance | |
36+
| main.rs:86:18:86:25 | ...::must_use(...) | main.rs:86:10:86:26 | MacroExpr | provenance | |
37+
| main.rs:86:18:86:25 | MacroExpr | main.rs:86:18:86:25 | ...::format(...) | provenance | MaD:2 |
38+
| main.rs:86:18:86:25 | { ... } | main.rs:86:18:86:25 | ...::must_use(...) | provenance | MaD:3 |
39+
| main.rs:87:10:87:33 | res | main.rs:87:18:87:32 | { ... } | provenance | |
40+
| main.rs:87:18:87:32 | ...::format(...) | main.rs:87:10:87:33 | res | provenance | |
41+
| main.rs:87:18:87:32 | ...::must_use(...) | main.rs:87:10:87:33 | MacroExpr | provenance | |
42+
| main.rs:87:18:87:32 | MacroExpr | main.rs:87:18:87:32 | ...::format(...) | provenance | MaD:2 |
43+
| main.rs:87:18:87:32 | { ... } | main.rs:87:18:87:32 | ...::must_use(...) | provenance | MaD:3 |
3044
nodes
3145
| main.rs:26:9:26:9 | s | semmle.label | s |
3246
| main.rs:26:13:26:22 | source(...) | semmle.label | source(...) |
@@ -58,6 +72,20 @@ nodes
5872
| main.rs:77:22:77:75 | ...::format(...) | semmle.label | ...::format(...) |
5973
| main.rs:77:34:77:74 | MacroExpr | semmle.label | MacroExpr |
6074
| main.rs:78:10:78:19 | formatted3 | semmle.label | formatted3 |
75+
| main.rs:82:9:82:10 | s1 | semmle.label | s1 |
76+
| main.rs:82:14:82:23 | source(...) | semmle.label | source(...) |
77+
| main.rs:86:10:86:26 | MacroExpr | semmle.label | MacroExpr |
78+
| main.rs:86:10:86:26 | res | semmle.label | res |
79+
| main.rs:86:18:86:25 | ...::format(...) | semmle.label | ...::format(...) |
80+
| main.rs:86:18:86:25 | ...::must_use(...) | semmle.label | ...::must_use(...) |
81+
| main.rs:86:18:86:25 | MacroExpr | semmle.label | MacroExpr |
82+
| main.rs:86:18:86:25 | { ... } | semmle.label | { ... } |
83+
| main.rs:87:10:87:33 | MacroExpr | semmle.label | MacroExpr |
84+
| main.rs:87:10:87:33 | res | semmle.label | res |
85+
| main.rs:87:18:87:32 | ...::format(...) | semmle.label | ...::format(...) |
86+
| main.rs:87:18:87:32 | ...::must_use(...) | semmle.label | ...::must_use(...) |
87+
| main.rs:87:18:87:32 | MacroExpr | semmle.label | MacroExpr |
88+
| main.rs:87:18:87:32 | { ... } | semmle.label | { ... } |
6189
subpaths
6290
testFailures
6391
#select
@@ -67,3 +95,5 @@ testFailures
6795
| main.rs:71:10:71:19 | formatted1 | main.rs:68:13:68:22 | source(...) | main.rs:71:10:71:19 | formatted1 | $@ | main.rs:68:13:68:22 | source(...) | source(...) |
6896
| main.rs:74:10:74:19 | formatted2 | main.rs:68:13:68:22 | source(...) | main.rs:74:10:74:19 | formatted2 | $@ | main.rs:68:13:68:22 | source(...) | source(...) |
6997
| main.rs:78:10:78:19 | formatted3 | main.rs:76:17:76:32 | source_usize(...) | main.rs:78:10:78:19 | formatted3 | $@ | main.rs:76:17:76:32 | source_usize(...) | source_usize(...) |
98+
| main.rs:86:10:86:26 | MacroExpr | main.rs:82:14:82:23 | source(...) | main.rs:86:10:86:26 | MacroExpr | $@ | main.rs:82:14:82:23 | source(...) | source(...) |
99+
| main.rs:87:10:87:33 | MacroExpr | main.rs:82:14:82:23 | source(...) | main.rs:87:10:87:33 | MacroExpr | $@ | main.rs:82:14:82:23 | source(...) | source(...) |

rust/ql/test/library-tests/dataflow/strings/main.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ fn format_macro() {
8383
let s2 = "2";
8484
let s3 = "3";
8585

86-
sink(format!("{}", s1)); // $ MISSING: hasTaintFlow=34
87-
sink(format!("{s1} and {s3}")); // $ MISSING: hasTaintFlow=34
86+
sink(format!("{}", s1)); // $ hasTaintFlow=34
87+
sink(format!("{s1} and {s3}")); // $ hasTaintFlow=34
8888
sink(format!("{s2} and {s3}"));
8989
}
9090

rust/ql/test/library-tests/variables/Ssa.expected

+5
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ definition
146146
| variables.rs:523:9:523:9 | z | variables.rs:523:9:523:9 | z |
147147
| variables.rs:532:10:532:18 | SelfParam | variables.rs:532:15:532:18 | self |
148148
| variables.rs:554:9:554:22 | var_from_macro | variables.rs:554:9:554:22 | var_from_macro |
149+
| variables.rs:555:9:555:25 | var_in_macro | variables.rs:555:9:555:25 | var_in_macro |
149150
read
150151
| variables.rs:3:14:3:14 | s | variables.rs:3:14:3:14 | s | variables.rs:4:20:4:20 | s |
151152
| variables.rs:7:14:7:14 | i | variables.rs:7:14:7:14 | i | variables.rs:8:20:8:20 | i |
@@ -284,6 +285,7 @@ read
284285
| variables.rs:523:9:523:9 | z | variables.rs:523:9:523:9 | z | variables.rs:524:20:524:20 | z |
285286
| variables.rs:532:10:532:18 | SelfParam | variables.rs:532:15:532:18 | self | variables.rs:533:6:533:9 | self |
286287
| variables.rs:554:9:554:22 | var_from_macro | variables.rs:554:9:554:22 | var_from_macro | variables.rs:556:15:556:28 | var_from_macro |
288+
| variables.rs:555:9:555:25 | var_in_macro | variables.rs:555:9:555:25 | var_in_macro | variables.rs:555:9:555:25 | var_in_macro |
287289
firstRead
288290
| variables.rs:3:14:3:14 | s | variables.rs:3:14:3:14 | s | variables.rs:4:20:4:20 | s |
289291
| variables.rs:7:14:7:14 | i | variables.rs:7:14:7:14 | i | variables.rs:8:20:8:20 | i |
@@ -395,6 +397,7 @@ firstRead
395397
| variables.rs:523:9:523:9 | z | variables.rs:523:9:523:9 | z | variables.rs:524:20:524:20 | z |
396398
| variables.rs:532:10:532:18 | SelfParam | variables.rs:532:15:532:18 | self | variables.rs:533:6:533:9 | self |
397399
| variables.rs:554:9:554:22 | var_from_macro | variables.rs:554:9:554:22 | var_from_macro | variables.rs:556:15:556:28 | var_from_macro |
400+
| variables.rs:555:9:555:25 | var_in_macro | variables.rs:555:9:555:25 | var_in_macro | variables.rs:555:9:555:25 | var_in_macro |
398401
lastRead
399402
| variables.rs:3:14:3:14 | s | variables.rs:3:14:3:14 | s | variables.rs:4:20:4:20 | s |
400403
| variables.rs:7:14:7:14 | i | variables.rs:7:14:7:14 | i | variables.rs:8:20:8:20 | i |
@@ -507,6 +510,7 @@ lastRead
507510
| variables.rs:523:9:523:9 | z | variables.rs:523:9:523:9 | z | variables.rs:524:20:524:20 | z |
508511
| variables.rs:532:10:532:18 | SelfParam | variables.rs:532:15:532:18 | self | variables.rs:533:6:533:9 | self |
509512
| variables.rs:554:9:554:22 | var_from_macro | variables.rs:554:9:554:22 | var_from_macro | variables.rs:556:15:556:28 | var_from_macro |
513+
| variables.rs:555:9:555:25 | var_in_macro | variables.rs:555:9:555:25 | var_in_macro | variables.rs:555:9:555:25 | var_in_macro |
510514
adjacentReads
511515
| variables.rs:35:9:35:10 | x3 | variables.rs:35:9:35:10 | x3 | variables.rs:36:15:36:16 | x3 | variables.rs:38:9:38:10 | x3 |
512516
| variables.rs:43:9:43:10 | x4 | variables.rs:43:9:43:10 | x4 | variables.rs:44:15:44:16 | x4 | variables.rs:49:15:49:16 | x4 |
@@ -659,3 +663,4 @@ assigns
659663
| variables.rs:519:9:519:9 | x | variables.rs:519:13:519:14 | 16 |
660664
| variables.rs:523:9:523:9 | z | variables.rs:523:13:523:14 | 17 |
661665
| variables.rs:554:9:554:22 | var_from_macro | variables.rs:555:9:555:25 | MacroExpr |
666+
| variables.rs:555:9:555:25 | var_in_macro | variables.rs:555:23:555:24 | 37 |

rust/ql/test/library-tests/variables/variables.expected

+2
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ variableAccess
276276
| variables.rs:533:6:533:9 | self | variables.rs:532:15:532:18 | self |
277277
| variables.rs:539:3:539:3 | a | variables.rs:538:11:538:11 | a |
278278
| variables.rs:541:13:541:13 | a | variables.rs:538:11:538:11 | a |
279+
| variables.rs:555:9:555:25 | var_in_macro | variables.rs:555:9:555:25 | var_in_macro |
279280
| variables.rs:556:15:556:28 | var_from_macro | variables.rs:554:9:554:22 | var_from_macro |
280281
variableWriteAccess
281282
| variables.rs:23:5:23:6 | x2 | variables.rs:21:13:21:14 | x2 |
@@ -436,6 +437,7 @@ variableReadAccess
436437
| variables.rs:533:6:533:9 | self | variables.rs:532:15:532:18 | self |
437438
| variables.rs:539:3:539:3 | a | variables.rs:538:11:538:11 | a |
438439
| variables.rs:541:13:541:13 | a | variables.rs:538:11:538:11 | a |
440+
| variables.rs:555:9:555:25 | var_in_macro | variables.rs:555:9:555:25 | var_in_macro |
439441
| variables.rs:556:15:556:28 | var_from_macro | variables.rs:554:9:554:22 | var_from_macro |
440442
variableInitializer
441443
| variables.rs:16:9:16:10 | x1 | variables.rs:16:14:16:16 | "a" |

rust/ql/test/library-tests/variables/variables.ql

+11-5
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ query predicate capturedAccess(VariableAccess va) { va.isCapture() }
1818
module VariableAccessTest implements TestSig {
1919
string getARelevantTag() { result = ["", "write_", "read_"] + "access" }
2020

21-
private predicate declAt(Variable v, string filepath, int line) {
22-
v.getLocation().hasLocationInfo(filepath, _, _, line, _)
21+
private predicate declAt(Variable v, string filepath, int line, boolean inMacro) {
22+
v.getLocation().hasLocationInfo(filepath, _, _, line, _) and
23+
if v.getPat().isInMacroExpansion() then inMacro = true else inMacro = false
2324
}
2425

2526
private predicate commmentAt(string text, string filepath, int line) {
@@ -30,10 +31,15 @@ module VariableAccessTest implements TestSig {
3031
}
3132

3233
private predicate decl(Variable v, string value) {
33-
exists(string filepath, int line | declAt(v, filepath, line) |
34-
commmentAt(value, filepath, line)
34+
exists(string filepath, int line, boolean inMacro | declAt(v, filepath, line, inMacro) |
35+
commmentAt(value, filepath, line) and
36+
inMacro = false
3537
or
36-
not commmentAt(_, filepath, line) and
38+
(
39+
not commmentAt(_, filepath, line)
40+
or
41+
inMacro = true
42+
) and
3743
value = v.getName()
3844
)
3945
}

rust/ql/test/library-tests/variables/variables.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ macro_rules! let_in_macro {
552552

553553
fn macro_invocation() {
554554
let var_from_macro =
555-
let_in_macro!(37); // $ MISSING: read_access=var_in_macro
555+
let_in_macro!(37); // $ read_access=var_in_macro
556556
print_i64(var_from_macro); // $ read_access=var_from_macro
557557
}
558558

0 commit comments

Comments
 (0)