Skip to content

Commit baba061

Browse files
committed
Rust: Model Deref trait in type inference
1 parent f62c2b5 commit baba061

File tree

14 files changed

+389
-186
lines changed

14 files changed

+389
-186
lines changed

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

Lines changed: 107 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,24 +1441,13 @@ private module MethodResolution {
14411441
* Same as `getACandidateReceiverTypeAt`, but without borrows.
14421442
*/
14431443
pragma[nomagic]
1444-
private Type getACandidateReceiverTypeAtNoBorrow(string derefChain, TypePath path) {
1444+
Type getACandidateReceiverTypeAtNoBorrow(string derefChain, TypePath path) {
14451445
result = this.getReceiverTypeAt(path) and
14461446
derefChain = ""
14471447
or
1448-
this.supportsAutoDerefAndBorrow() and
1449-
exists(TypePath path0, Type t0, string derefChain0 |
1450-
this.hasNoCompatibleTargetMutBorrow(derefChain0) and
1451-
t0 = this.getACandidateReceiverTypeAtNoBorrow(derefChain0, path0)
1452-
|
1453-
path0.isCons(getRefTypeParameter(), path) and
1454-
result = t0 and
1455-
derefChain = derefChain0 + ".ref"
1456-
or
1457-
path0.isEmpty() and
1458-
path = path0 and
1459-
t0 = getStringStruct() and
1460-
result = getStrStruct() and
1461-
derefChain = derefChain0 + ".str"
1448+
exists(ImplicitDeref::DerefImplItemNode impl, string derefChain0 |
1449+
result = ImplicitDeref::getDereferencedCandidateReceiverType(this, impl, derefChain0, path) and
1450+
derefChain = derefChain0 + "." + impl.getId()
14621451
)
14631452
}
14641453

@@ -1698,8 +1687,11 @@ private module MethodResolution {
16981687
}
16991688

17001689
predicate receiverHasImplicitDeref(AstNode receiver) {
1701-
exists(this.resolveCallTarget(_, ".ref", TNoBorrowKind())) and
1702-
receiver = this.getArg(any(ArgumentPosition pos | pos.isSelf()))
1690+
exists(ImplicitDeref::DerefImplItemNode impl |
1691+
impl.isRefImpl() and
1692+
exists(this.resolveCallTarget(_, "." + impl.getId(), TNoBorrowKind())) and
1693+
receiver = this.getArg(any(ArgumentPosition pos | pos.isSelf()))
1694+
)
17031695
}
17041696

17051697
predicate argumentHasImplicitBorrow(AstNode arg, BorrowKind borrow) {
@@ -1969,6 +1961,98 @@ private module MethodResolution {
19691961
Location getLocation() { result = mc_.getLocation() }
19701962
}
19711963

1964+
module ImplicitDeref {
1965+
private import codeql.rust.elements.internal.generated.Raw
1966+
private import codeql.rust.elements.internal.generated.Synth
1967+
1968+
/** An `impl` block that implements the `Deref` trait. */
1969+
class DerefImplItemNode extends ImplItemNode {
1970+
DerefImplItemNode() { this.resolveTraitTy() instanceof DerefTrait }
1971+
1972+
/**
1973+
* Holds if this `impl` block is the special `Deref` implementation for
1974+
* `&T` or `&mut T`:
1975+
*
1976+
* ```rust
1977+
* impl<T: ?Sized> const Deref for &T
1978+
* ```
1979+
*
1980+
* or
1981+
*
1982+
* ```rust
1983+
* impl<T: ?Sized> const Deref for &mut T
1984+
* ```
1985+
*/
1986+
predicate isRefImpl() { this.resolveSelfTyBuiltin() instanceof Builtins::RefType }
1987+
1988+
/** Gets an internal unique ID used to identify this block amongst all `Deref` impl blocks. */
1989+
int getId() { idOfRaw(Synth::convertAstNodeToRaw(this), result) }
1990+
}
1991+
1992+
private class DerefImplItemRaw extends Raw::Impl {
1993+
DerefImplItemRaw() { this = Synth::convertAstNodeToRaw(any(DerefImplItemNode i)) }
1994+
}
1995+
1996+
private predicate id(DerefImplItemRaw x, DerefImplItemRaw y) { x = y }
1997+
1998+
private predicate idOfRaw(DerefImplItemRaw x, int y) = equivalenceRelation(id/2)(x, y)
1999+
2000+
private newtype TMethodCallDerefCand =
2001+
MkMethodCallDerefCand(MethodCall mc, string derefChain) {
2002+
mc.supportsAutoDerefAndBorrow() and
2003+
mc.hasNoCompatibleTargetMutBorrow(derefChain) and
2004+
exists(mc.getACandidateReceiverTypeAtNoBorrow(derefChain, _))
2005+
}
2006+
2007+
private class MethodCallDerefCand extends MkMethodCallDerefCand {
2008+
MethodCall mc_;
2009+
string derefChain;
2010+
2011+
MethodCallDerefCand() { this = MkMethodCallDerefCand(mc_, derefChain) }
2012+
2013+
MethodCall getMethodCall() { result = mc_ }
2014+
2015+
Type getTypeAt(TypePath path) {
2016+
result = substituteLookupTraits(mc_.getACandidateReceiverTypeAtNoBorrow(derefChain, path)) and
2017+
not result = TNeverType() and
2018+
not result = TUnknownType()
2019+
}
2020+
2021+
string toString() { result = mc_.toString() + " [" + derefChain + "]" }
2022+
2023+
Location getLocation() { result = mc_.getLocation() }
2024+
}
2025+
2026+
private module MethodCallSatisfiesConstraintInput implements
2027+
SatisfiesConstraintInputSig<MethodCallDerefCand>
2028+
{
2029+
pragma[nomagic]
2030+
predicate relevantConstraint(MethodCallDerefCand mc, Type constraint) {
2031+
exists(mc) and
2032+
constraint.(TraitType).getTrait() instanceof DerefTrait
2033+
}
2034+
2035+
predicate useUniversalConditions() { none() }
2036+
}
2037+
2038+
pragma[nomagic]
2039+
private AssociatedTypeTypeParameter getDerefTargetTypeParameter() {
2040+
result.getTypeAlias() = any(DerefTrait ft).getTargetType()
2041+
}
2042+
2043+
pragma[nomagic]
2044+
Type getDereferencedCandidateReceiverType(
2045+
MethodCall mc, DerefImplItemNode impl, string derefChain, TypePath path
2046+
) {
2047+
exists(MethodCallDerefCand mcc, TypePath exprPath |
2048+
mcc = MkMethodCallDerefCand(mc, derefChain) and
2049+
SatisfiesConstraint<MethodCallDerefCand, MethodCallSatisfiesConstraintInput>::satisfiesConstraintType(mcc,
2050+
impl, _, exprPath, result) and
2051+
exprPath.isCons(getDerefTargetTypeParameter(), path)
2052+
)
2053+
}
2054+
}
2055+
19722056
private module ReceiverSatisfiesBlanketLikeConstraintInput implements
19732057
BlanketImplementation::SatisfiesBlanketConstraintInputSig<MethodCallCand>
19742058
{
@@ -2347,9 +2431,12 @@ private Type inferMethodCallType1(AstNode n, boolean isReturn, TypePath path) {
23472431
path = path0
23482432
or
23492433
// adjust for implicit deref
2350-
apos.isSelf() and
2351-
derefChainBorrow = ".ref;" and
2352-
path = TypePath::cons(getRefTypeParameter(), path0)
2434+
exists(MethodResolution::ImplicitDeref::DerefImplItemNode impl |
2435+
impl.isRefImpl() and
2436+
apos.isSelf() and
2437+
derefChainBorrow = "." + impl.getId() + ";" and
2438+
path = TypePath::cons(getRefTypeParameter(), path0)
2439+
)
23532440
or
23542441
// adjust for implicit borrow
23552442
apos.isSelf() and
@@ -3156,9 +3243,6 @@ private Type inferTryExprType(TryExpr te, TypePath path) {
31563243
pragma[nomagic]
31573244
private StructType getStrStruct() { result = TStruct(any(Builtins::Str s)) }
31583245

3159-
pragma[nomagic]
3160-
private StructType getStringStruct() { result = TStruct(any(StringStruct s)) }
3161-
31623246
pragma[nomagic]
31633247
private Type inferLiteralType(LiteralExpr le, TypePath path, boolean certain) {
31643248
path.isEmpty() and

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ models
3737
| 36 | Summary: <_ as tokio::io::util::async_read_ext::AsyncReadExt>::read_to_string; Argument[self].Reference; Argument[0].Reference; taint |
3838
| 37 | Summary: <_ as tokio::io::util::async_read_ext::AsyncReadExt>::read_u8; Argument[self].Reference; ReturnValue.Future.Field[core::result::Result::Ok(0)]; taint |
3939
| 38 | Summary: <core::result::Result>::unwrap; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value |
40-
| 39 | Summary: <std::path::PathBuf>::as_path; Argument[self]; ReturnValue; value |
40+
| 39 | Summary: <std::path::Path>::canonicalize; Argument[self].Reference.OptionalBarrier[normalize-path]; ReturnValue.Field[core::result::Result::Ok(0)]; taint |
41+
| 40 | Summary: <std::path::PathBuf>::as_path; Argument[self]; ReturnValue; value |
4142
edges
4243
| test.rs:12:13:12:18 | buffer | test.rs:13:14:13:19 | buffer | provenance | |
4344
| test.rs:12:31:12:43 | ...::read | test.rs:12:31:12:43 | ...::read [Ok] | provenance | Src:MaD:11 |
@@ -59,12 +60,15 @@ edges
5960
| test.rs:22:22:22:52 | TryExpr | test.rs:22:13:22:18 | buffer | provenance | |
6061
| test.rs:29:13:29:16 | path | test.rs:30:14:30:17 | path | provenance | |
6162
| test.rs:29:13:29:16 | path | test.rs:31:14:31:17 | path | provenance | |
63+
| test.rs:29:13:29:16 | path | test.rs:40:14:40:17 | path | provenance | |
6264
| test.rs:29:13:29:16 | path | test.rs:41:14:41:17 | path | provenance | |
6365
| test.rs:29:20:29:27 | e.path() | test.rs:29:13:29:16 | path | provenance | |
6466
| test.rs:29:22:29:25 | path | test.rs:29:20:29:27 | e.path() | provenance | Src:MaD:4 MaD:4 |
6567
| test.rs:30:14:30:17 | path | test.rs:30:14:30:25 | path.clone() | provenance | MaD:20 |
6668
| test.rs:31:14:31:17 | path | test.rs:31:14:31:25 | path.clone() | provenance | MaD:20 |
67-
| test.rs:31:14:31:25 | path.clone() | test.rs:31:14:31:35 | ... .as_path() | provenance | MaD:39 |
69+
| test.rs:31:14:31:25 | path.clone() | test.rs:31:14:31:35 | ... .as_path() | provenance | MaD:40 |
70+
| test.rs:40:14:40:17 | path | test.rs:40:14:40:32 | path.canonicalize() [Ok] | provenance | MaD:39 |
71+
| test.rs:40:14:40:32 | path.canonicalize() [Ok] | test.rs:40:14:40:41 | ... .unwrap() | provenance | MaD:38 |
6872
| test.rs:43:13:43:21 | file_name | test.rs:44:14:44:22 | file_name | provenance | |
6973
| test.rs:43:13:43:21 | file_name | test.rs:49:14:49:22 | file_name | provenance | |
7074
| test.rs:43:25:43:37 | e.file_name() | test.rs:43:13:43:21 | file_name | provenance | |
@@ -286,6 +290,9 @@ nodes
286290
| test.rs:31:14:31:17 | path | semmle.label | path |
287291
| test.rs:31:14:31:25 | path.clone() | semmle.label | path.clone() |
288292
| test.rs:31:14:31:35 | ... .as_path() | semmle.label | ... .as_path() |
293+
| test.rs:40:14:40:17 | path | semmle.label | path |
294+
| test.rs:40:14:40:32 | path.canonicalize() [Ok] | semmle.label | path.canonicalize() [Ok] |
295+
| test.rs:40:14:40:41 | ... .unwrap() | semmle.label | ... .unwrap() |
289296
| test.rs:41:14:41:17 | path | semmle.label | path |
290297
| test.rs:43:13:43:21 | file_name | semmle.label | file_name |
291298
| test.rs:43:25:43:37 | e.file_name() | semmle.label | e.file_name() |
@@ -507,6 +514,7 @@ testFailures
507514
| 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 |
508515
| 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 |
509516
| 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 |
517+
| 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 |
510518
| 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 |
511519
| 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 |
512520
| 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
@@ -10,6 +10,9 @@
1010
| test.rs:51:52:51:59 | read_dir | Flow source 'FileSource' of type file (DEFAULT). |
1111
| test.rs:54:22:54:25 | path | Flow source 'FileSource' of type file (DEFAULT). |
1212
| test.rs:55:27:55:35 | file_name | Flow source 'FileSource' of type file (DEFAULT). |
13+
| test.rs:57:56:57:63 | read_dir | Flow source 'FileSource' of type file (DEFAULT). |
14+
| test.rs:60:22:60:25 | path | Flow source 'FileSource' of type file (DEFAULT). |
15+
| test.rs:61:27:61:35 | file_name | Flow source 'FileSource' of type file (DEFAULT). |
1316
| test.rs:65:22:65:34 | ...::read_link | Flow source 'FileSource' of type file (DEFAULT). |
1417
| test.rs:74:31:74:45 | ...::read | Flow source 'FileSource' of type file (DEFAULT). |
1518
| 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)