Skip to content

Commit 9abef45

Browse files
committed
Rust: Model Deref trait in type inference
1 parent 299497b commit 9abef45

File tree

16 files changed

+572
-259
lines changed

16 files changed

+572
-259
lines changed

rust/ql/lib/codeql/rust/internal/TypeInference.qll

Lines changed: 208 additions & 95 deletions
Large diffs are not rendered by default.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/** Provides classes for representing implicit dereferences. */
2+
3+
private import rust
4+
private import codeql.rust.internal.PathResolution
5+
private import codeql.rust.internal.Type
6+
private import codeql.rust.internal.TypeInference
7+
private import codeql.rust.internal.TypeMention
8+
private import codeql.rust.frameworks.stdlib.Stdlib
9+
private import codeql.rust.frameworks.stdlib.Builtins as Builtins
10+
private import codeql.util.UnboundList as UnboundListImpl
11+
12+
/** An `impl` block that implements the `Deref` trait. */
13+
class DerefImplItemNode extends ImplItemNode {
14+
DerefImplItemNode() { this.resolveTraitTy() instanceof DerefTrait }
15+
16+
/** Gets the `deref` function in this `Deref` impl block. */
17+
Function getDerefFunction() { result = this.getAssocItem("deref") }
18+
19+
private SelfParam getSelfParam() { result = this.getDerefFunction().getSelfParam() }
20+
21+
/**
22+
* Resolves the type at `path` of the `self` parameter inside the `deref` function,
23+
* stripped of the leading `&`.
24+
*/
25+
pragma[nomagic]
26+
Type resolveSelfParamTypeStrippedAt(TypePath path) {
27+
exists(TypePath path0 |
28+
result = getSelfParamTypeMention(this.getSelfParam()).resolveTypeAt(path0) and
29+
path0.isCons(getRefSharedTypeParameter(), path)
30+
)
31+
}
32+
33+
/**
34+
* Holds if the return type at `path` of the `deref` function, stripped of the
35+
* leading `&`, mentions type parameter `tp` at `path`.
36+
*/
37+
pragma[nomagic]
38+
predicate returnTypeStrippedMentionsTypeParameterAt(TypeParameter tp, TypePath path) {
39+
exists(TypePath path0 |
40+
tp = getReturnTypeMention(this.getDerefFunction()).resolveTypeAt(path0) and
41+
path0.isCons(getRefSharedTypeParameter(), path)
42+
)
43+
}
44+
}
45+
46+
private module UnboundListInput implements UnboundListImpl::InputSig<Location> {
47+
private import codeql.rust.elements.internal.generated.Raw
48+
private import codeql.rust.elements.internal.generated.Synth
49+
50+
private class DerefImplItemRaw extends Raw::Impl {
51+
DerefImplItemRaw() { this = Synth::convertAstNodeToRaw(any(DerefImplItemNode i)) }
52+
}
53+
54+
private predicate id(DerefImplItemRaw x, DerefImplItemRaw y) { x = y }
55+
56+
private predicate idOfRaw(DerefImplItemRaw x, int y) = equivalenceRelation(id/2)(x, y)
57+
58+
class Element = DerefImplItemNode;
59+
60+
int getId(Element e) { idOfRaw(Synth::convertAstNodeToRaw(e), result) }
61+
62+
string getElementString(Element e) { result = e.resolveSelfTy().getName() }
63+
}
64+
65+
private import UnboundListImpl::Make<Location, UnboundListInput>
66+
67+
/**
68+
* A sequence of `Deref` impl blocks representing a chain of implicit dereferences,
69+
* encoded as a string.
70+
*/
71+
class DerefChain = UnboundList;
72+
73+
/** Provides predicates for constructing `DerefChain`s. */
74+
module DerefChain = UnboundList;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
| main.rs:292:13:292:14 | * ... | main.rs:251:5:253:5 | fn deref |
8585
| main.rs:293:5:293:11 | sink(...) | main.rs:5:1:7:1 | fn sink |
8686
| main.rs:295:28:295:37 | source(...) | main.rs:1:1:3:1 | fn source |
87+
| main.rs:296:13:296:23 | a.min(...) | {EXTERNAL LOCATION} | fn min |
8788
| main.rs:297:5:297:11 | sink(...) | main.rs:5:1:7:1 | fn sink |
8889
| main.rs:319:28:319:36 | source(...) | main.rs:1:1:3:1 | fn source |
8990
| main.rs:321:30:321:54 | ...::take_self(...) | main.rs:309:5:311:5 | fn take_self |

rust/ql/test/library-tests/dataflow/sources/file/InlineFlow.expected

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ models
3535
| 34 | Summary: <_ as tokio::io::util::async_read_ext::AsyncReadExt>::read_to_string; Argument[self].Reference; Argument[0].Reference; taint |
3636
| 35 | Summary: <_ as tokio::io::util::async_read_ext::AsyncReadExt>::read_u8; Argument[self].Reference; ReturnValue.Future.Field[core::result::Result::Ok(0)]; taint |
3737
| 36 | Summary: <core::result::Result>::unwrap; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value |
38-
| 37 | Summary: <std::path::PathBuf>::as_path; Argument[self].Reference; ReturnValue.Reference; value |
38+
| 37 | Summary: <std::path::Path>::canonicalize; Argument[self].Reference.OptionalBarrier[normalize-path]; ReturnValue.Field[core::result::Result::Ok(0)]; taint |
39+
| 38 | Summary: <std::path::PathBuf>::as_path; Argument[self].Reference; ReturnValue.Reference; value |
3940
edges
4041
| test.rs:12:13:12:18 | buffer | test.rs:13:14:13:19 | buffer | provenance | |
4142
| test.rs:12:31:12:43 | ...::read | test.rs:12:31:12:55 | ...::read(...) [Ok] | provenance | Src:MaD:11 |
@@ -51,12 +52,15 @@ edges
5152
| test.rs:22:22:22:52 | TryExpr | test.rs:22:13:22:18 | buffer | provenance | |
5253
| test.rs:29:13:29:16 | path | test.rs:30:14:30:17 | path | provenance | |
5354
| test.rs:29:13:29:16 | path | test.rs:31:14:31:17 | path | provenance | |
55+
| test.rs:29:13:29:16 | path | test.rs:40:14:40:17 | path | provenance | |
5456
| test.rs:29:13:29:16 | path | test.rs:41:14:41:17 | path | provenance | |
5557
| test.rs:29:20:29:27 | e.path() | test.rs:29:13:29:16 | path | provenance | |
5658
| test.rs:29:22:29:25 | path | test.rs:29:20:29:27 | e.path() | provenance | Src:MaD:4 MaD:4 |
5759
| test.rs:30:14:30:17 | path | test.rs:30:14:30:25 | path.clone() | provenance | MaD:18 |
5860
| test.rs:31:14:31:17 | path | test.rs:31:14:31:25 | path.clone() | provenance | MaD:18 |
59-
| test.rs:31:14:31:25 | path.clone() | test.rs:31:14:31:35 | ... .as_path() | provenance | MaD:37 |
61+
| test.rs:31:14:31:25 | path.clone() | test.rs:31:14:31:35 | ... .as_path() | provenance | MaD:38 |
62+
| test.rs:40:14:40:17 | path | test.rs:40:14:40:32 | path.canonicalize() [Ok] | provenance | MaD:37 |
63+
| test.rs:40:14:40:32 | path.canonicalize() [Ok] | test.rs:40:14:40:41 | ... .unwrap() | provenance | MaD:36 |
6064
| test.rs:43:13:43:21 | file_name | test.rs:44:14:44:22 | file_name | provenance | |
6165
| test.rs:43:13:43:21 | file_name | test.rs:49:14:49:22 | file_name | provenance | |
6266
| test.rs:43:25:43:37 | e.file_name() | test.rs:43:13:43:21 | file_name | provenance | |
@@ -273,6 +277,9 @@ nodes
273277
| test.rs:31:14:31:17 | path | semmle.label | path |
274278
| test.rs:31:14:31:25 | path.clone() | semmle.label | path.clone() |
275279
| test.rs:31:14:31:35 | ... .as_path() | semmle.label | ... .as_path() |
280+
| test.rs:40:14:40:17 | path | semmle.label | path |
281+
| test.rs:40:14:40:32 | path.canonicalize() [Ok] | semmle.label | path.canonicalize() [Ok] |
282+
| test.rs:40:14:40:41 | ... .unwrap() | semmle.label | ... .unwrap() |
276283
| test.rs:41:14:41:17 | path | semmle.label | path |
277284
| test.rs:43:13:43:21 | file_name | semmle.label | file_name |
278285
| test.rs:43:25:43:37 | e.file_name() | semmle.label | e.file_name() |
@@ -492,6 +499,7 @@ testFailures
492499
| test.rs:23:14:23:19 | buffer | test.rs:22:22:22:39 | ...::read_to_string | test.rs:23:14:23:19 | buffer | $@ | test.rs:22:22:22:39 | ...::read_to_string | ...::read_to_string |
493500
| test.rs:30:14:30:25 | path.clone() | test.rs:29:22:29:25 | path | test.rs:30:14:30:25 | path.clone() | $@ | test.rs:29:22:29:25 | path | path |
494501
| test.rs:31:14:31:35 | ... .as_path() | test.rs:29:22:29:25 | path | test.rs:31:14:31:35 | ... .as_path() | $@ | test.rs:29:22:29:25 | path | path |
502+
| test.rs:40:14:40:41 | ... .unwrap() | test.rs:29:22:29:25 | path | test.rs:40:14:40:41 | ... .unwrap() | $@ | test.rs:29:22:29:25 | path | path |
495503
| test.rs:41:14:41:17 | path | test.rs:29:22:29:25 | path | test.rs:41:14:41:17 | path | $@ | test.rs:29:22:29:25 | path | path |
496504
| test.rs:44:14:44:30 | file_name.clone() | test.rs:43:27:43:35 | file_name | test.rs:44:14:44:30 | file_name.clone() | $@ | test.rs:43:27:43:35 | file_name | file_name |
497505
| test.rs:49:14:49:22 | file_name | test.rs:43:27:43:35 | file_name | test.rs:49:14:49:22 | file_name | $@ | test.rs:43:27:43:35 | file_name | file_name |

rust/ql/test/library-tests/dataflow/sources/file/TaintSources.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
| test.rs:51:52:51:59 | read_dir | Flow source 'FileSource' of type file (DEFAULT). |
88
| test.rs:54:22:54:25 | path | Flow source 'FileSource' of type file (DEFAULT). |
99
| test.rs:55:27:55:35 | file_name | Flow source 'FileSource' of type file (DEFAULT). |
10+
| test.rs:57:56:57:63 | read_dir | Flow source 'FileSource' of type file (DEFAULT). |
11+
| test.rs:60:22:60:25 | path | Flow source 'FileSource' of type file (DEFAULT). |
12+
| test.rs:61:27:61:35 | file_name | Flow source 'FileSource' of type file (DEFAULT). |
1013
| test.rs:65:22:65:34 | ...::read_link | Flow source 'FileSource' of type file (DEFAULT). |
1114
| test.rs:74:31:74:45 | ...::read | Flow source 'FileSource' of type file (DEFAULT). |
1215
| test.rs:79:31:79:45 | ...::read | Flow source 'FileSource' of type file (DEFAULT). |

rust/ql/test/library-tests/dataflow/sources/file/test.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fn test_fs() -> Result<(), Box<dyn std::error::Error>> {
3737
sink(path.to_path_buf()); // $ MISSING: hasTaintFlow
3838
sink(path.file_name().unwrap()); // $ MISSING: hasTaintFlow
3939
sink(path.extension().unwrap()); // $ MISSING: hasTaintFlow
40-
sink(path.canonicalize().unwrap()); // $ MISSING: hasTaintFlow
40+
sink(path.canonicalize().unwrap()); // $ hasTaintFlow
4141
sink(path); // $ hasTaintFlow
4242

4343
let file_name = e.file_name(); // $ Alert[rust/summary/taint-sources]
@@ -54,11 +54,11 @@ fn test_fs() -> Result<(), Box<dyn std::error::Error>> {
5454
let path = e.path(); // $ Alert[rust/summary/taint-sources]
5555
let file_name = e.file_name(); // $ Alert[rust/summary/taint-sources]
5656
}
57-
for entry in std::path::PathBuf::from("directory").read_dir()? {
57+
for entry in std::path::PathBuf::from("directory").read_dir()? { // $ Alert[rust/summary/taint-sources]
5858
let e = entry?;
5959

60-
let path = e.path(); // $ MISSING: Alert[rust/summary/taint-sources]
61-
let file_name = e.file_name(); // $ MISSING: Alert[rust/summary/taint-sources]
60+
let path = e.path(); // $ Alert[rust/summary/taint-sources]
61+
let file_name = e.file_name(); // $ Alert[rust/summary/taint-sources]
6262
}
6363

6464
{

0 commit comments

Comments
 (0)