Skip to content

Commit 1e14fda

Browse files
committed
Rust: Data flow for consts and statics
1 parent 3e8a79c commit 1e14fda

9 files changed

Lines changed: 99 additions & 18 deletions

File tree

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,14 @@ final class CallableScope extends CfgScopeImpl, Callable {
4545
/** Holds if `scope` is exited when `last` finishes with completion `c`. */
4646
override predicate scopeLast(AstNode last, Completion c) { last(this.getBody(), last, c) }
4747
}
48+
49+
/**
50+
* A special scope used to represent the context in which `const`s and
51+
* `static`s are initialized. We do not actually compute a CFG for such
52+
* scopes.
53+
*/
54+
final class SourceFileScope extends CfgScopeImpl, SourceFile {
55+
override predicate scopeFirst(AstNode first) { none() }
56+
57+
override predicate scopeLast(AstNode last, Completion c) { none() }
58+
}

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,8 +653,19 @@ module RustDataFlowGen<RustDataFlowInputSig Input> implements InputSig<Location>
653653
*/
654654
predicate jumpStep(Node node1, Node node2) {
655655
FlowSummaryImpl::Private::Steps::summaryJumpStep(node1.(FlowSummaryNode).getSummaryNode(),
656-
node2.(FlowSummaryNode).getSummaryNode()) or
656+
node2.(FlowSummaryNode).getSummaryNode())
657+
or
657658
FlowSummaryImpl::Private::Steps::sourceJumpStep(node1.(FlowSummaryNode).getSummaryNode(), node2)
659+
or
660+
exists(Const c |
661+
node1.asExpr() = c.getBody() and
662+
node2.asExpr() = c.getAnAccess()
663+
)
664+
or
665+
exists(Static s |
666+
node1.asExpr() = s.getBody() and
667+
node2.asExpr() = s.getAnAccess()
668+
)
658669
}
659670

660671
pragma[nomagic]

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,37 @@ module Impl {
4848
)
4949
}
5050

51-
/** Gets the CFG scope that encloses this node, if any. */
52-
cached
53-
CfgScope getEnclosingCfgScope() {
51+
pragma[nomagic]
52+
private predicate isInConstOrStaticBody(File f) {
53+
exists(AstNode p |
54+
this.getParentNode*() = p and
55+
f = p.getFile()
56+
|
57+
p = any(Const c).getBody()
58+
or
59+
p = any(Static s).getBody()
60+
)
61+
}
62+
63+
pragma[nomagic]
64+
private CfgScope getEnclosingCfgScopeImpl() {
65+
not this.isInConstOrStaticBody(_) and
5466
exists(AstNode p | p = this.getParentNode() |
5567
result = p
5668
or
5769
not p instanceof CfgScope and
58-
result = p.getEnclosingCfgScope()
70+
result = p.getEnclosingCfgScopeImpl()
5971
)
6072
}
6173

74+
/** Gets the CFG scope that encloses this node, if any. */
75+
cached
76+
CfgScope getEnclosingCfgScope() {
77+
result = this.getEnclosingCfgScopeImpl()
78+
or
79+
this.isInConstOrStaticBody(result.(SourceFile).getFile())
80+
}
81+
6282
/** Holds if this node is inside a CFG scope. */
6383
predicate hasEnclosingCfgScope() { exists(this.getEnclosingCfgScope()) }
6484

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ module Impl {
2525
* ```
2626
*/
2727
class Const extends Generated::Const {
28+
/** Gets an access to this constant item. */
29+
ConstAccess getAnAccess() { this = result.getConst() }
30+
2831
override string toStringImpl() { result = "const " + this.getName().getText() }
2932
}
3033

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ module Impl {
2424
* ```
2525
*/
2626
class Static extends Generated::Static {
27+
/** Gets an access to this static item. */
28+
StaticAccess getAnAccess() { this = result.getStatic() }
29+
2730
override string toStringImpl() { result = "static " + this.getName().getText() }
2831
}
2932

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ edges
222222
| main.rs:415:18:415:38 | i.get_double_number() | main.rs:415:13:415:14 | n4 | provenance | |
223223
| main.rs:418:13:418:14 | n5 | main.rs:419:14:419:15 | n5 | provenance | |
224224
| main.rs:418:18:418:41 | ...::get_default(...) | main.rs:418:13:418:14 | n5 | provenance | |
225+
| main.rs:426:30:426:39 | source(...) | main.rs:430:35:430:45 | CONST_VALUE | provenance | |
226+
| main.rs:427:32:427:41 | source(...) | main.rs:432:28:432:39 | STATIC_VALUE | provenance | |
227+
| main.rs:430:35:430:45 | CONST_VALUE | main.rs:431:14:431:25 | CONST_VALUE2 | provenance | |
228+
| main.rs:432:13:432:24 | static_value | main.rs:433:14:433:25 | static_value | provenance | |
229+
| main.rs:432:28:432:39 | STATIC_VALUE | main.rs:432:13:432:24 | static_value | provenance | |
225230
nodes
226231
| main.rs:12:28:14:1 | { ... } | semmle.label | { ... } |
227232
| main.rs:13:5:13:13 | source(...) | semmle.label | source(...) |
@@ -464,6 +469,13 @@ nodes
464469
| main.rs:418:13:418:14 | n5 | semmle.label | n5 |
465470
| main.rs:418:18:418:41 | ...::get_default(...) | semmle.label | ...::get_default(...) |
466471
| main.rs:419:14:419:15 | n5 | semmle.label | n5 |
472+
| main.rs:426:30:426:39 | source(...) | semmle.label | source(...) |
473+
| main.rs:427:32:427:41 | source(...) | semmle.label | source(...) |
474+
| main.rs:430:35:430:45 | CONST_VALUE | semmle.label | CONST_VALUE |
475+
| main.rs:431:14:431:25 | CONST_VALUE2 | semmle.label | CONST_VALUE2 |
476+
| main.rs:432:13:432:24 | static_value | semmle.label | static_value |
477+
| main.rs:432:28:432:39 | STATIC_VALUE | semmle.label | STATIC_VALUE |
478+
| main.rs:433:14:433:25 | static_value | semmle.label | static_value |
467479
subpaths
468480
| main.rs:38:16:38:24 | source(...) | main.rs:26:28:26:33 | ...: i64 | main.rs:26:17:26:25 | SelfParam [Return] [&ref, MyStruct] | main.rs:38:5:38:5 | [post] a [MyStruct] |
469481
| main.rs:39:10:39:10 | a [MyStruct] | main.rs:30:17:30:21 | SelfParam [&ref, MyStruct] | main.rs:30:31:32:5 | { ... } | main.rs:39:10:39:21 | a.get_data() |
@@ -525,3 +537,5 @@ testFailures
525537
| main.rs:412:14:412:15 | n3 | main.rs:371:13:371:21 | source(...) | main.rs:412:14:412:15 | n3 | $@ | main.rs:371:13:371:21 | source(...) | source(...) |
526538
| main.rs:416:14:416:15 | n4 | main.rs:391:13:391:22 | source(...) | main.rs:416:14:416:15 | n4 | $@ | main.rs:391:13:391:22 | source(...) | source(...) |
527539
| main.rs:419:14:419:15 | n5 | main.rs:395:13:395:21 | source(...) | main.rs:419:14:419:15 | n5 | $@ | main.rs:395:13:395:21 | source(...) | source(...) |
540+
| main.rs:431:14:431:25 | CONST_VALUE2 | main.rs:426:30:426:39 | source(...) | main.rs:431:14:431:25 | CONST_VALUE2 | $@ | main.rs:426:30:426:39 | source(...) | source(...) |
541+
| main.rs:433:14:433:25 | static_value | main.rs:427:32:427:41 | source(...) | main.rs:433:14:433:25 | static_value | $@ | main.rs:427:32:427:41 | source(...) | source(...) |

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
fn source(i: i64) -> i64 {
1+
const fn source(i: i64) -> i64 {
22
1000 + i
33
}
44

@@ -420,6 +420,20 @@ mod not_trait_dispatch {
420420
}
421421
}
422422

423+
mod const_static {
424+
use super::{sink, source};
425+
426+
const CONST_VALUE: i64 = source(42);
427+
static STATIC_VALUE: i64 = source(43);
428+
429+
fn test_const_static() {
430+
const CONST_VALUE2: i64 = CONST_VALUE;
431+
sink(CONST_VALUE2); // $ hasValueFlow=42
432+
let static_value = STATIC_VALUE;
433+
sink(static_value); // $ hasValueFlow=43
434+
}
435+
}
436+
423437
fn main() {
424438
data_out_of_call();
425439
data_out_of_call_side_effect1();

rust/ql/test/library-tests/dataflow/global/viableCallable.expected

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,18 @@
128128
| main.rs:416:9:416:16 | sink(...) | main.rs:5:1:7:1 | fn sink |
129129
| main.rs:418:18:418:41 | ...::get_default(...) | main.rs:394:9:396:9 | fn get_default |
130130
| main.rs:419:9:419:16 | sink(...) | main.rs:5:1:7:1 | fn sink |
131-
| main.rs:424:5:424:22 | data_out_of_call(...) | main.rs:16:1:19:1 | fn data_out_of_call |
132-
| main.rs:425:5:425:35 | data_out_of_call_side_effect1(...) | main.rs:35:1:40:1 | fn data_out_of_call_side_effect1 |
133-
| main.rs:426:5:426:35 | data_out_of_call_side_effect2(...) | main.rs:42:1:50:1 | fn data_out_of_call_side_effect2 |
134-
| main.rs:427:5:427:21 | data_in_to_call(...) | main.rs:56:1:59:1 | fn data_in_to_call |
135-
| main.rs:428:5:428:23 | data_through_call(...) | main.rs:65:1:69:1 | fn data_through_call |
136-
| main.rs:429:5:429:34 | data_through_nested_function(...) | main.rs:79:1:88:1 | fn data_through_nested_function |
137-
| main.rs:431:5:431:24 | data_out_of_method(...) | main.rs:152:1:162:1 | fn data_out_of_method |
138-
| main.rs:432:5:432:28 | data_in_to_method_call(...) | main.rs:169:1:179:1 | fn data_in_to_method_call |
139-
| main.rs:433:5:433:25 | data_through_method(...) | main.rs:187:1:199:1 | fn data_through_method |
140-
| main.rs:435:5:435:31 | test_operator_overloading(...) | main.rs:256:1:298:1 | fn test_operator_overloading |
141-
| main.rs:436:5:436:22 | test_async_await(...) | main.rs:353:1:358:1 | fn test_async_await |
131+
| main.rs:426:30:426:39 | source(...) | main.rs:1:1:3:1 | fn source |
132+
| main.rs:427:32:427:41 | source(...) | main.rs:1:1:3:1 | fn source |
133+
| main.rs:431:9:431:26 | sink(...) | main.rs:5:1:7:1 | fn sink |
134+
| main.rs:433:9:433:26 | sink(...) | main.rs:5:1:7:1 | fn sink |
135+
| main.rs:438:5:438:22 | data_out_of_call(...) | main.rs:16:1:19:1 | fn data_out_of_call |
136+
| main.rs:439:5:439:35 | data_out_of_call_side_effect1(...) | main.rs:35:1:40:1 | fn data_out_of_call_side_effect1 |
137+
| main.rs:440:5:440:35 | data_out_of_call_side_effect2(...) | main.rs:42:1:50:1 | fn data_out_of_call_side_effect2 |
138+
| main.rs:441:5:441:21 | data_in_to_call(...) | main.rs:56:1:59:1 | fn data_in_to_call |
139+
| main.rs:442:5:442:23 | data_through_call(...) | main.rs:65:1:69:1 | fn data_through_call |
140+
| main.rs:443:5:443:34 | data_through_nested_function(...) | main.rs:79:1:88:1 | fn data_through_nested_function |
141+
| main.rs:445:5:445:24 | data_out_of_method(...) | main.rs:152:1:162:1 | fn data_out_of_method |
142+
| main.rs:446:5:446:28 | data_in_to_method_call(...) | main.rs:169:1:179:1 | fn data_in_to_method_call |
143+
| main.rs:447:5:447:25 | data_through_method(...) | main.rs:187:1:199:1 | fn data_through_method |
144+
| main.rs:449:5:449:31 | test_operator_overloading(...) | main.rs:256:1:298:1 | fn test_operator_overloading |
145+
| main.rs:450:5:450:22 | test_async_await(...) | main.rs:353:1:358:1 | fn test_async_await |
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import codeql.rust.dataflow.internal.DataFlowImpl
22

33
query predicate viableCallable(DataFlowCall call, DataFlowCallable callee) {
4-
RustDataFlow::viableCallable(call) = callee
4+
RustDataFlow::viableCallable(call) = callee and
5+
(call.asCall().fromSource() or call.isImplicitDerefCall(_, _, _, _))
56
}

0 commit comments

Comments
 (0)