Skip to content

Commit 0fd1f19

Browse files
committed
Rust: Add support for MaD sources and sinks with access paths
1 parent 8efd870 commit 0fd1f19

File tree

11 files changed

+355
-43
lines changed

11 files changed

+355
-43
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/** Provides classes and predicates for defining flow sources. */
2+
3+
private import rust
4+
private import internal.FlowSummaryImpl as Impl
5+
private import internal.DataFlowImpl as DataFlowImpl
6+
7+
// import all instances below
8+
private module Sources {
9+
private import codeql.rust.Frameworks
10+
private import codeql.rust.dataflow.internal.ModelsAsData
11+
}
12+
13+
/** Provides the `Range` class used to define the extent of `FlowSource`. */
14+
module FlowSource {
15+
/** A flow source. */
16+
abstract class Range extends Impl::Public::SourceNode {
17+
bindingset[this]
18+
Range() { any() }
19+
20+
override predicate isSource(
21+
string output, string kind, Impl::Public::Provenance provenance, string model
22+
) {
23+
this.isSource(output, kind) and provenance = "manual" and model = ""
24+
}
25+
26+
/**
27+
* Holds is this element is a flow source of kind `kind`, where data
28+
* flows out as described by `output`.
29+
*/
30+
predicate isSource(string output, string kind) { none() }
31+
}
32+
}
33+
34+
final class FlowSource = FlowSource::Range;
35+
36+
predicate sourceNode = DataFlowImpl::sourceNode/2;

rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,5 @@ module SummarizedCallable {
5757
}
5858

5959
final class SummarizedCallable = SummarizedCallable::Range;
60+
61+
final class Provenance = Impl::Public::Provenance;

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

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -186,18 +186,37 @@ module Node {
186186
class FlowSummaryNode extends Node, TFlowSummaryNode {
187187
FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) }
188188

189-
/** Gets the summarized callable that this node belongs to. */
189+
/** Gets the summarized callable that this node belongs to, if any. */
190190
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() {
191191
result = this.getSummaryNode().getSummarizedCallable()
192192
}
193193

194-
override CfgScope getCfgScope() { none() }
194+
/** Gets the source node that this node belongs to, if any */
195+
FlowSummaryImpl::Public::SourceNode getSourceNode() {
196+
result = this.getSummaryNode().getSourceNode()
197+
}
198+
199+
/** Holds is this node is a source node of kind `kind`. */
200+
predicate isSource(string kind) {
201+
this.getSummaryNode().(FlowSummaryImpl::Private::SourceOutputNode).isEntry(kind)
202+
}
203+
204+
override CfgScope getCfgScope() {
205+
result = this.getSummaryNode().getSourceNode().getEnclosingCfgScope()
206+
}
195207

196208
override DataFlowCallable getEnclosingCallable() {
197209
result.asLibraryCallable() = this.getSummarizedCallable()
210+
or
211+
result.asCfgScope() = this.getSummaryNode().getSourceNode().getEnclosingCfgScope()
198212
}
199213

200-
override EmptyLocation getLocation() { any() }
214+
override Location getLocation() {
215+
exists(this.getSummarizedCallable()) and
216+
result instanceof EmptyLocation
217+
or
218+
result = this.getSourceNode().getLocation()
219+
}
201220

202221
override string toString() { result = this.getSummaryNode().toString() }
203222
}
@@ -526,13 +545,14 @@ private ExprCfgNode getALastEvalNode(ExprCfgNode e) {
526545
}
527546

528547
module LocalFlow {
529-
predicate flowSummaryLocalStep(
530-
Node::FlowSummaryNode nodeFrom, Node::FlowSummaryNode nodeTo,
531-
FlowSummaryImpl::Public::SummarizedCallable c, string model
532-
) {
533-
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.getSummaryNode(),
534-
nodeTo.getSummaryNode(), true, model) and
535-
c = nodeFrom.getSummarizedCallable()
548+
predicate flowSummaryLocalStep(Node::FlowSummaryNode nodeFrom, Node nodeTo, string model) {
549+
exists(FlowSummaryImpl::Public::SummarizedCallable c |
550+
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.getSummaryNode(),
551+
nodeTo.(Node::FlowSummaryNode).getSummaryNode(), true, model) and
552+
c = nodeFrom.getSummarizedCallable()
553+
)
554+
or
555+
FlowSummaryImpl::Private::localSourceNodeStep(nodeFrom, nodeTo, model)
536556
}
537557

538558
pragma[nomagic]
@@ -848,7 +868,7 @@ module RustDataFlow implements InputSig<Location> {
848868
predicate nodeIsHidden(Node node) {
849869
node instanceof Node::SsaNode
850870
or
851-
node instanceof Node::FlowSummaryNode
871+
node.(Node::FlowSummaryNode).getSummaryNode().isHidden()
852872
or
853873
node instanceof Node::CaptureNode
854874
or
@@ -864,6 +884,8 @@ module RustDataFlow implements InputSig<Location> {
864884
node.asExpr() = match.getScrutinee() or
865885
node.asExpr() = match.getArmPat(_)
866886
)
887+
or
888+
FlowSummaryImpl::Private::localSourceNodeStep(_, node, _)
867889
}
868890

869891
class DataFlowExpr = ExprCfgNode;
@@ -944,7 +966,7 @@ module RustDataFlow implements InputSig<Location> {
944966
) and
945967
model = ""
946968
or
947-
LocalFlow::flowSummaryLocalStep(nodeFrom, nodeTo, _, model)
969+
LocalFlow::flowSummaryLocalStep(nodeFrom, nodeTo, model)
948970
}
949971

950972
/**
@@ -1499,6 +1521,10 @@ private module Cached {
14991521

15001522
cached
15011523
newtype TContentSet = TSingletonContentSet(Content c)
1524+
1525+
/** Holds if `n` is a flow source of kind `kind`. */
1526+
cached
1527+
predicate sourceNode(Node n, string kind) { n.(Node::FlowSummaryNode).isSource(kind) }
15021528
}
15031529

15041530
import Cached

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,26 @@ private import codeql.rust.dataflow.FlowSummary
1111
module Input implements InputSig<Location, RustDataFlow> {
1212
class SummarizedCallableBase = string;
1313

14+
abstract class SourceBase extends Expr {
15+
abstract CallExprBase getCall();
16+
}
17+
18+
private class CallExprSource extends SourceBase {
19+
private CallExpr call;
20+
21+
CallExprSource() { this = call.getFunction() }
22+
23+
override CallExpr getCall() { result = call }
24+
}
25+
26+
private class MethodCallExprSource extends SourceBase {
27+
private MethodCallExpr call;
28+
29+
MethodCallExprSource() { this = call }
30+
31+
override MethodCallExpr getCall() { result = call }
32+
}
33+
1434
RustDataFlow::ArgumentPosition callbackSelfParameterPosition() { none() }
1535

1636
ReturnKind getStandardReturnValueKind() { result = TNormalReturnKind() }
@@ -92,6 +112,16 @@ module Private {
92112
import Impl::Private
93113

94114
module Steps = Impl::Private::Steps<StepsInput>;
115+
116+
private import codeql.rust.dataflow.FlowSource
117+
118+
predicate localSourceNodeStep(Node::FlowSummaryNode nodeFrom, Node::ExprNode nodeTo, string model) {
119+
exists(SummaryComponent sc, FlowSource source |
120+
nodeFrom.getSummaryNode().(SourceOutputNode).isExit(source, sc, model) and
121+
sc = SummaryComponent::return(_) and
122+
nodeTo.asExpr().getExpr() = source.getCall()
123+
)
124+
}
95125
}
96126

97127
module Public = Impl::Public;

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747

4848
private import rust
4949
private import codeql.rust.dataflow.FlowSummary
50+
private import codeql.rust.dataflow.FlowSource
51+
private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprBaseImpl
5052

5153
/**
5254
* Holds if in a call to the function with canonical path `path`, defined in the
@@ -138,3 +140,28 @@ private class SummarizedCallableFromModel extends SummarizedCallable::Range {
138140
)
139141
}
140142
}
143+
144+
private class FlowSourceFromModel extends FlowSource::Range {
145+
private string crate;
146+
private string path;
147+
148+
FlowSourceFromModel() {
149+
sourceModel(crate, path, _, _, _, _) and
150+
exists(Resolvable r |
151+
r = CallExprBaseImpl::getCallResolvable(this.getCall()) and
152+
path = r.getResolvedPath()
153+
|
154+
crate = r.getResolvedCrateOrigin()
155+
or
156+
not r.hasResolvedCrateOrigin() and
157+
crate = ""
158+
)
159+
}
160+
161+
override predicate isSource(string output, string kind, Provenance provenance, string model) {
162+
exists(QlBuiltins::ExtensionId madId |
163+
sourceModel(crate, path, output, kind, provenance, madId) and
164+
model = "MaD:" + madId.toString()
165+
)
166+
}
167+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ private module FlowTestImpl implements InputSig<Location, RustDataFlow> {
2727
private string getSourceArgString(DataFlow::Node src) {
2828
defaultSource(src) and
2929
result = src.asExpr().(CallExprCfgNode).getArgument(0).toString()
30+
or
31+
sourceNode(src, _) and
32+
exists(CallExpr call |
33+
call = src.(Node::FlowSummaryNode).getSourceNode().getCall() and
34+
result = call.getArgList().getArg(0).toString()
35+
)
3036
}
3137

3238
bindingset[src, sink]

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,19 @@ fn test_set_tuple_element() {
175175
sink(t.1); // $ hasValueFlow=11
176176
}
177177

178+
// has a source model
179+
fn enum_source(i: i64) -> MyFieldEnum {
180+
MyFieldEnum::C { field_c: 0 }
181+
}
182+
183+
fn test_enum_source() {
184+
let s = enum_source(12);
185+
match s {
186+
MyFieldEnum::C { field_c: i } => sink(i),
187+
MyFieldEnum::D { field_d: i } => sink(i), // $ hasValueFlow=12
188+
}
189+
}
190+
178191
fn main() {
179192
test_identify();
180193
test_get_var_pos();
@@ -187,5 +200,6 @@ fn main() {
187200
test_set_array_element();
188201
test_get_tuple_element();
189202
test_set_tuple_element();
203+
test_enum_source();
190204
let dummy = Some(0); // ensure that the the `lang:core` crate is extracted
191205
}

0 commit comments

Comments
 (0)