Skip to content

Commit 2f23bac

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

File tree

12 files changed

+721
-44
lines changed

12 files changed

+721
-44
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 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;
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: 59 additions & 12 deletions
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,17 @@ 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::localSourceNodeStep(nodeFrom, nodeTo, model)
569+
or
570+
FlowSummaryImpl::Private::localSinkNodeStep(nodeFrom, nodeTo, model)
536571
}
537572

538573
pragma[nomagic]
@@ -848,7 +883,7 @@ module RustDataFlow implements InputSig<Location> {
848883
predicate nodeIsHidden(Node node) {
849884
node instanceof Node::SsaNode
850885
or
851-
node instanceof Node::FlowSummaryNode
886+
node.(Node::FlowSummaryNode).getSummaryNode().isHidden()
852887
or
853888
node instanceof Node::CaptureNode
854889
or
@@ -864,6 +899,10 @@ module RustDataFlow implements InputSig<Location> {
864899
node.asExpr() = match.getScrutinee() or
865900
node.asExpr() = match.getArmPat(_)
866901
)
902+
or
903+
FlowSummaryImpl::Private::localSourceNodeStep(_, node, _)
904+
or
905+
FlowSummaryImpl::Private::localSinkNodeStep(node, _, _)
867906
}
868907

869908
class DataFlowExpr = ExprCfgNode;
@@ -944,7 +983,7 @@ module RustDataFlow implements InputSig<Location> {
944983
) and
945984
model = ""
946985
or
947-
LocalFlow::flowSummaryLocalStep(nodeFrom, nodeTo, _, model)
986+
LocalFlow::flowSummaryLocalStep(nodeFrom, nodeTo, model)
948987
}
949988

950989
/**
@@ -1499,6 +1538,14 @@ private module Cached {
14991538

15001539
cached
15011540
newtype TContentSet = TSingletonContentSet(Content c)
1541+
1542+
/** Holds if `n` is a flow source of kind `kind`. */
1543+
cached
1544+
predicate sourceNode(Node n, string kind) { n.(Node::FlowSummaryNode).isSource(kind) }
1545+
1546+
/** Holds if `n` is a flow sink of kind `kind`. */
1547+
cached
1548+
predicate sinkNode(Node n, string kind) { n.(Node::FlowSummaryNode).isSink(kind) }
15021549
}
15031550

15041551
import Cached

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

Lines changed: 64 additions & 0 deletions
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() }
@@ -92,6 +132,30 @@ module Private {
92132
import Impl::Private
93133

94134
module Steps = Impl::Private::Steps<StepsInput>;
135+
136+
private import codeql.rust.dataflow.FlowSource
137+
private import codeql.rust.dataflow.FlowSink
138+
139+
predicate localSourceNodeStep(Node::FlowSummaryNode nodeFrom, Node::ExprNode nodeTo, string model) {
140+
exists(SummaryComponent sc, FlowSource source |
141+
nodeFrom.getSummaryNode().(SourceOutputNode).isExit(source, sc, model) and
142+
sc = SummaryComponent::return(_) and
143+
nodeTo.asExpr().getExpr() = source.getCall()
144+
)
145+
}
146+
147+
predicate localSinkNodeStep(Node::ExprNode nodeFrom, Node::FlowSummaryNode nodeTo, string model) {
148+
exists(CallExprBase call, Expr arg, SummaryComponent sc, FlowSink sink, ParameterPosition pos |
149+
nodeFrom.asExpr().getExpr() = arg and
150+
nodeTo.getSummaryNode().(SinkInputNode).isEntry(sink, sc, model) and
151+
sc = SummaryComponent::argument(pos) and
152+
call = sink.getCall()
153+
|
154+
arg = call.getArgList().getArg(pos.getPosition())
155+
or
156+
arg = call.(MethodCallExpr).getReceiver() and pos.isSelf()
157+
)
158+
}
95159
}
96160

97161
module Public = Impl::Public;

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

Lines changed: 36 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.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

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(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)