Skip to content

Commit 2a44dd2

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

File tree

12 files changed

+780
-44
lines changed

12 files changed

+780
-44
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/** Provides classes and predicates for defining flow sinks. */
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 Sinks {
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 `FlowSink`. */
14+
module FlowSink {
15+
/** A flow source. */
16+
abstract class Range extends Impl::Public::SinkNode {
17+
bindingset[this]
18+
Range() { any() }
19+
20+
override predicate isSink(
21+
string input, string kind, Impl::Public::Provenance provenance, string model
22+
) {
23+
this.isSink(input, kind) and provenance = "manual" and model = ""
24+
}
25+
26+
/**
27+
* Holds is this element is a flow sink of kind `kind`, where data
28+
* flows in as described by `input`.
29+
*/
30+
predicate isSink(string output, string kind) { none() }
31+
}
32+
}
33+
34+
final class FlowSink = FlowSink::Range;
35+
36+
predicate sinkNode = DataFlowImpl::sinkNode/2;
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

+2
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

+62-12
Original file line numberDiff line numberDiff line change
@@ -186,18 +186,49 @@ 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+
/** Gets the sink node that this node belongs to, if any */
200+
FlowSummaryImpl::Public::SinkNode getSinkNode() { result = this.getSummaryNode().getSinkNode() }
201+
202+
/** Holds is this node is a source node of kind `kind`. */
203+
predicate isSource(string kind) {
204+
this.getSummaryNode().(FlowSummaryImpl::Private::SourceOutputNode).isEntry(kind)
205+
}
206+
207+
/** Holds is this node is a sink node of kind `kind`. */
208+
predicate isSink(string kind) {
209+
this.getSummaryNode().(FlowSummaryImpl::Private::SinkInputNode).isExit(kind)
210+
}
211+
212+
override CfgScope getCfgScope() {
213+
result = this.getSummaryNode().getSourceNode().getEnclosingCfgScope()
214+
or
215+
result = this.getSummaryNode().getSinkNode().getEnclosingCfgScope()
216+
}
195217

196218
override DataFlowCallable getEnclosingCallable() {
197219
result.asLibraryCallable() = this.getSummarizedCallable()
220+
or
221+
result.asCfgScope() = this.getCfgScope()
198222
}
199223

200-
override EmptyLocation getLocation() { any() }
224+
override Location getLocation() {
225+
exists(this.getSummarizedCallable()) and
226+
result instanceof EmptyLocation
227+
or
228+
result = this.getSourceNode().getLocation()
229+
or
230+
result = this.getSinkNode().getLocation()
231+
}
201232

202233
override string toString() { result = this.getSummaryNode().toString() }
203234
}
@@ -526,13 +557,20 @@ private ExprCfgNode getALastEvalNode(ExprCfgNode e) {
526557
}
527558

528559
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()
560+
predicate flowSummaryLocalStep(Node nodeFrom, Node nodeTo, string model) {
561+
exists(FlowSummaryImpl::Public::SummarizedCallable c |
562+
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom
563+
.(Node::FlowSummaryNode)
564+
.getSummaryNode(), nodeTo.(Node::FlowSummaryNode).getSummaryNode(), true, model) and
565+
c = nodeFrom.(Node::FlowSummaryNode).getSummarizedCallable()
566+
)
567+
or
568+
FlowSummaryImpl::Private::Steps::sourceLocalStep(nodeFrom
569+
.(Node::FlowSummaryNode)
570+
.getSummaryNode(), nodeTo, model)
571+
or
572+
FlowSummaryImpl::Private::Steps::sinkLocalStep(nodeFrom,
573+
nodeTo.(Node::FlowSummaryNode).getSummaryNode(), model)
536574
}
537575

538576
pragma[nomagic]
@@ -848,7 +886,7 @@ module RustDataFlow implements InputSig<Location> {
848886
predicate nodeIsHidden(Node node) {
849887
node instanceof Node::SsaNode
850888
or
851-
node instanceof Node::FlowSummaryNode
889+
node.(Node::FlowSummaryNode).getSummaryNode().isHidden()
852890
or
853891
node instanceof Node::CaptureNode
854892
or
@@ -864,6 +902,10 @@ module RustDataFlow implements InputSig<Location> {
864902
node.asExpr() = match.getScrutinee() or
865903
node.asExpr() = match.getArmPat(_)
866904
)
905+
or
906+
FlowSummaryImpl::Private::Steps::sourceLocalStep(_, node, _)
907+
or
908+
FlowSummaryImpl::Private::Steps::sinkLocalStep(node, _, _)
867909
}
868910

869911
class DataFlowExpr = ExprCfgNode;
@@ -944,7 +986,7 @@ module RustDataFlow implements InputSig<Location> {
944986
) and
945987
model = ""
946988
or
947-
LocalFlow::flowSummaryLocalStep(nodeFrom, nodeTo, _, model)
989+
LocalFlow::flowSummaryLocalStep(nodeFrom, nodeTo, model)
948990
}
949991

950992
/**
@@ -1499,6 +1541,14 @@ private module Cached {
14991541

15001542
cached
15011543
newtype TContentSet = TSingletonContentSet(Content c)
1544+
1545+
/** Holds if `n` is a flow source of kind `kind`. */
1546+
cached
1547+
predicate sourceNode(Node n, string kind) { n.(Node::FlowSummaryNode).isSource(kind) }
1548+
1549+
/** Holds if `n` is a flow sink of kind `kind`. */
1550+
cached
1551+
predicate sinkNode(Node n, string kind) { n.(Node::FlowSummaryNode).isSink(kind) }
15021552
}
15031553

15041554
import Cached

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

+60
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,48 @@ private import codeql.rust.dataflow.internal.DataFlowImpl
99
private import codeql.rust.dataflow.FlowSummary
1010

1111
module Input implements InputSig<Location, RustDataFlow> {
12+
private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprBaseImpl
13+
1214
class SummarizedCallableBase = string;
1315

16+
abstract private class SourceSinkBase extends AstNode {
17+
/** Gets the associated call. */
18+
abstract CallExprBase getCall();
19+
20+
/** Holds if the associated call resolves to `crate, path`. */
21+
final predicate callResolvesTo(string crate, string path) {
22+
exists(Resolvable r |
23+
r = CallExprBaseImpl::getCallResolvable(this.getCall()) and
24+
path = r.getResolvedPath()
25+
|
26+
crate = r.getResolvedCrateOrigin()
27+
or
28+
not r.hasResolvedCrateOrigin() and
29+
crate = ""
30+
)
31+
}
32+
}
33+
34+
abstract class SourceBase extends SourceSinkBase { }
35+
36+
abstract class SinkBase extends SourceSinkBase { }
37+
38+
private class CallExprFunction extends SourceBase, SinkBase {
39+
private CallExpr call;
40+
41+
CallExprFunction() { this = call.getFunction() }
42+
43+
override CallExpr getCall() { result = call }
44+
}
45+
46+
private class MethodCallExprNameRef extends SourceBase, SinkBase {
47+
private MethodCallExpr call;
48+
49+
MethodCallExprNameRef() { this = call.getNameRef() }
50+
51+
override MethodCallExpr getCall() { result = call }
52+
}
53+
1454
RustDataFlow::ArgumentPosition callbackSelfParameterPosition() { none() }
1555

1656
ReturnKind getStandardReturnValueKind() { result = TNormalReturnKind() }
@@ -86,12 +126,32 @@ private module StepsInput implements Impl::Private::StepsInputSig {
86126
DataFlowCall getACall(Public::SummarizedCallable sc) {
87127
result.asCallBaseExprCfgNode().getCallExprBase() = sc.(LibraryCallable).getACall()
88128
}
129+
130+
Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) {
131+
sc = Impl::Private::SummaryComponent::return(_) and
132+
result.asExpr().getExpr() = source.getCall()
133+
}
134+
135+
Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) {
136+
exists(CallExprBase call, Expr arg, ParameterPosition pos |
137+
result.asExpr().getExpr() = arg and
138+
sc = Impl::Private::SummaryComponent::argument(pos) and
139+
call = sink.getCall()
140+
|
141+
arg = call.getArgList().getArg(pos.getPosition())
142+
or
143+
arg = call.(MethodCallExpr).getReceiver() and pos.isSelf()
144+
)
145+
}
89146
}
90147

91148
module Private {
92149
import Impl::Private
93150

94151
module Steps = Impl::Private::Steps<StepsInput>;
152+
153+
private import codeql.rust.dataflow.FlowSource
154+
private import codeql.rust.dataflow.FlowSink
95155
}
96156

97157
module Public = Impl::Public;

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

+36
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.dataflow.FlowSink
5052

5153
/**
5254
* Holds if in a call to the function with canonical path `path`, defined in the
@@ -138,3 +140,37 @@ 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+
this.callResolvesTo(crate, path)
151+
}
152+
153+
override predicate isSource(string output, string kind, Provenance provenance, string model) {
154+
exists(QlBuiltins::ExtensionId madId |
155+
sourceModel(crate, path, output, kind, provenance, madId) and
156+
model = "MaD:" + madId.toString()
157+
)
158+
}
159+
}
160+
161+
private class FlowSinkFromModel extends FlowSink::Range {
162+
private string crate;
163+
private string path;
164+
165+
FlowSinkFromModel() {
166+
sinkModel(crate, path, _, _, _, _) and
167+
this.callResolvesTo(crate, path)
168+
}
169+
170+
override predicate isSink(string input, string kind, Provenance provenance, string model) {
171+
exists(QlBuiltins::ExtensionId madId |
172+
sinkModel(crate, path, input, kind, provenance, madId) and
173+
model = "MaD:" + madId.toString()
174+
)
175+
}
176+
}

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

+6
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(CallExprBase call |
33+
call = src.(Node::FlowSummaryNode).getSourceNode().getCall() and
34+
result = call.getArgList().getArg(0).toString()
35+
)
3036
}
3137

3238
bindingset[src, sink]

0 commit comments

Comments
 (0)