Skip to content

Commit 87b9e60

Browse files
authored
Merge pull request github#18291 from paldepind/rust-data-flow-models
Rust: Data flow improvements to unlock flow in sqlx test
2 parents ef2215d + 049fab4 commit 87b9e60

File tree

21 files changed

+925
-591
lines changed

21 files changed

+925
-591
lines changed

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,11 @@ private class CapturedVariableContent extends Content, TCapturedVariableContent
712712
override string toString() { result = "captured " + v }
713713
}
714714

715+
/** A value referred to by a reference. */
716+
final class ReferenceContent extends Content, TReferenceContent {
717+
override string toString() { result = "&ref" }
718+
}
719+
715720
/**
716721
* An element in an array.
717722
*/
@@ -1051,6 +1056,13 @@ module RustDataFlow implements InputSig<Location> {
10511056
["crate::option::Option::Some", "crate::result::Result::Ok"]
10521057
)
10531058
or
1059+
exists(PrefixExprCfgNode deref |
1060+
c instanceof ReferenceContent and
1061+
deref.getOperatorName() = "*" and
1062+
node1.asExpr() = deref.getExpr() and
1063+
node2.asExpr() = deref
1064+
)
1065+
or
10541066
VariableCapture::readStep(node1, c, node2)
10551067
)
10561068
or
@@ -1128,6 +1140,12 @@ module RustDataFlow implements InputSig<Location> {
11281140
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = index.getBase()
11291141
)
11301142
or
1143+
exists(RefExprCfgNode ref |
1144+
c instanceof ReferenceContent and
1145+
node1.asExpr() = ref.getExpr() and
1146+
node2.asExpr() = ref
1147+
)
1148+
or
11311149
VariableCapture::storeStep(node1, c, node2)
11321150
}
11331151

@@ -1395,7 +1413,8 @@ private module Cached {
13951413
e =
13961414
[
13971415
any(IndexExprCfgNode i).getBase(), any(FieldExprCfgNode access).getExpr(),
1398-
any(TryExprCfgNode try).getExpr()
1416+
any(TryExprCfgNode try).getExpr(),
1417+
any(PrefixExprCfgNode pe | pe.getOperatorName() = "*").getExpr()
13991418
]
14001419
} or
14011420
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
@@ -1495,7 +1514,8 @@ private module Cached {
14951514
TStructFieldContent(StructCanonicalPath s, string field) {
14961515
field = s.getStruct().getFieldList().(RecordFieldList).getAField().getName().getText()
14971516
} or
1498-
TCapturedVariableContent(VariableCapture::CapturedVariable v)
1517+
TCapturedVariableContent(VariableCapture::CapturedVariable v) or
1518+
TReferenceContent()
14991519

15001520
cached
15011521
newtype TContentSet = TSingletonContentSet(Content c)

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

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,22 +88,16 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
8888
|
8989
va instanceof VariableReadAccess
9090
or
91+
// For immutable variables, we model a read when they are borrowed
92+
// (although the actual read happens later, if at all).
93+
va = any(RefExpr re).getExpr()
94+
or
9195
// Although compound assignments, like `x += y`, may in fact not read `x`,
9296
// it makes sense to treat them as such
9397
va = any(CompoundAssignmentExpr cae).getLhs()
9498
) and
9599
certain = true
96100
or
97-
// For immutable variables, we model a read when they are borrowed (although the
98-
// actual read happens later, if at all). This only affects the SSA liveness
99-
// analysis.
100-
exists(VariableAccess va |
101-
va = any(RefExpr re).getExpr() and
102-
va = bb.getNode(i).getAstNode() and
103-
v = va.getVariable() and
104-
certain = false
105-
)
106-
or
107101
capturedCallRead(_, bb, i, v) and certain = false
108102
or
109103
capturedExitRead(bb, i, v) and certain = false
@@ -146,7 +140,9 @@ private predicate adjacentDefReadExt(
146140

147141
/** Holds if `v` is read at index `i` in basic block `bb`. */
148142
private predicate variableReadActual(BasicBlock bb, int i, Variable v) {
149-
exists(VariableReadAccess read |
143+
exists(VariableAccess read |
144+
read instanceof VariableReadAccess or read = any(RefExpr re).getExpr()
145+
|
150146
read.getVariable() = v and
151147
read = bb.getNode(i).getAstNode()
152148
)

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ module RustTaintTracking implements InputSig<Location, RustDataFlow> {
5959
bindingset[node]
6060
predicate defaultImplicitTaintRead(Node::Node node, ContentSet cs) {
6161
exists(node) and
62-
cs.(SingletonContentSet).getContent() instanceof ArrayElementContent
62+
exists(Content c | c = cs.(SingletonContentSet).getContent() |
63+
c instanceof ArrayElementContent or
64+
c instanceof ReferenceContent
65+
)
6366
}
6467

6568
/**
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
extensions:
2+
- addsTo:
3+
pack: codeql/rust-all
4+
extensible: summaryModel
5+
data:
6+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "<crate::blocking::response::Response>::text", "Argument[self]", "ReturnValue.Variant[crate::result::Result::Ok(0)]", "taint", "manual"]

rust/ql/lib/codeql/rust/frameworks/stdlib/lang-core.model.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,13 @@ extensions:
33
pack: codeql/rust-all
44
extensible: summaryModel
55
data:
6+
# Option
67
- ["lang:core", "<crate::option::Option>::unwrap", "Argument[self].Variant[crate::option::Option::Some(0)]", "ReturnValue", "value", "manual"]
8+
- ["lang:core", "<crate::option::Option>::unwrap_or", "Argument[self].Variant[crate::option::Option::Some(0)]", "ReturnValue", "value", "manual"]
9+
- ["lang:core", "<crate::option::Option>::unwrap_or", "Argument[0]", "ReturnValue", "value", "manual"]
10+
# Result
11+
- ["lang:core", "<crate::result::Result>::unwrap", "Argument[self].Variant[crate::result::Result::Ok(0)]", "ReturnValue", "value", "manual"]
12+
- ["lang:core", "<crate::result::Result>::unwrap_or", "Argument[self].Variant[crate::result::Result::Ok(0)]", "ReturnValue", "value", "manual"]
13+
- ["lang:core", "<crate::result::Result>::unwrap_or", "Argument[0]", "ReturnValue", "value", "manual"]
14+
# String
15+
- ["lang:alloc", "<crate::string::String>::as_str", "Argument[self]", "ReturnValue", "taint", "manual"]

rust/ql/lib/utils/test/InlineFlowTest.qll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@ private import codeql.rust.dataflow.internal.TaintTrackingImpl
1212
private import codeql.rust.dataflow.internal.ModelsAsData as MaD
1313
private import internal.InlineExpectationsTestImpl as InlineExpectationsTestImpl
1414

15-
// Holds if the target expression of `call` is a path and the string representation of the path is `name`.
15+
/**
16+
* Holds if the target expression of `call` is a path and the string
17+
* representation of the path has `name` as a prefix.
18+
*/
19+
bindingset[name]
1620
private predicate callTargetName(CallExprCfgNode call, string name) {
17-
call.getFunction().(PathExprCfgNode).toString() = name
21+
call.getFunction().(PathExprCfgNode).toString().matches(name + "%")
1822
}
1923

2024
private module FlowTestImpl implements InputSig<Location, RustDataFlow> {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
identityLocalStep
2-
| main.rs:404:7:404:18 | phi(default_name) | Node steps to itself |
2+
| main.rs:394:7:394:18 | phi(default_name) | Node steps to itself |

0 commit comments

Comments
 (0)