Skip to content

Commit ca2b719

Browse files
committed
sinks
1 parent 6902cef commit ca2b719

File tree

10 files changed

+439
-73
lines changed

10 files changed

+439
-73
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;

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

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,26 +196,38 @@ module Node {
196196
result = this.getSummaryNode().getSourceNode()
197197
}
198198

199+
/** Gets the sink node that this node belongs to, if any */
200+
FlowSummaryImpl::Public::SinkNode getSinkNode() { result = this.getSummaryNode().getSinkNode() }
201+
199202
/** Holds is this node is a source node of kind `kind`. */
200203
predicate isSource(string kind) {
201204
this.getSummaryNode().(FlowSummaryImpl::Private::SourceOutputNode).isEntry(kind)
202205
}
203206

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+
204212
override CfgScope getCfgScope() {
205213
result = this.getSummaryNode().getSourceNode().getEnclosingCfgScope()
214+
or
215+
result = this.getSummaryNode().getSinkNode().getEnclosingCfgScope()
206216
}
207217

208218
override DataFlowCallable getEnclosingCallable() {
209219
result.asLibraryCallable() = this.getSummarizedCallable()
210220
or
211-
result.asCfgScope() = this.getSummaryNode().getSourceNode().getEnclosingCfgScope()
221+
result.asCfgScope() = this.getCfgScope()
212222
}
213223

214224
override Location getLocation() {
215225
exists(this.getSummarizedCallable()) and
216226
result instanceof EmptyLocation
217227
or
218228
result = this.getSourceNode().getLocation()
229+
or
230+
result = this.getSinkNode().getLocation()
219231
}
220232

221233
override string toString() { result = this.getSummaryNode().toString() }
@@ -545,14 +557,17 @@ private ExprCfgNode getALastEvalNode(ExprCfgNode e) {
545557
}
546558

547559
module LocalFlow {
548-
predicate flowSummaryLocalStep(Node::FlowSummaryNode nodeFrom, Node nodeTo, string model) {
560+
predicate flowSummaryLocalStep(Node nodeFrom, Node nodeTo, string model) {
549561
exists(FlowSummaryImpl::Public::SummarizedCallable c |
550-
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.getSummaryNode(),
551-
nodeTo.(Node::FlowSummaryNode).getSummaryNode(), true, model) and
552-
c = nodeFrom.getSummarizedCallable()
562+
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom
563+
.(Node::FlowSummaryNode)
564+
.getSummaryNode(), nodeTo.(Node::FlowSummaryNode).getSummaryNode(), true, model) and
565+
c = nodeFrom.(Node::FlowSummaryNode).getSummarizedCallable()
553566
)
554567
or
555568
FlowSummaryImpl::Private::localSourceNodeStep(nodeFrom, nodeTo, model)
569+
or
570+
FlowSummaryImpl::Private::localSinkNodeStep(nodeFrom, nodeTo, model)
556571
}
557572

558573
pragma[nomagic]
@@ -886,6 +901,8 @@ module RustDataFlow implements InputSig<Location> {
886901
)
887902
or
888903
FlowSummaryImpl::Private::localSourceNodeStep(_, node, _)
904+
or
905+
FlowSummaryImpl::Private::localSinkNodeStep(node, _, _)
889906
}
890907

891908
class DataFlowExpr = ExprCfgNode;
@@ -1525,6 +1542,10 @@ private module Cached {
15251542
/** Holds if `n` is a flow source of kind `kind`. */
15261543
cached
15271544
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) }
15281549
}
15291550

15301551
import Cached

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +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 {
14+
abstract class SourceBase extends AstNode {
1515
abstract CallExprBase getCall();
1616
}
1717

18-
private class CallExprSource extends SourceBase {
18+
abstract class SinkBase extends AstNode {
19+
abstract CallExprBase getCall();
20+
}
21+
22+
private class CallExprFunction extends SourceBase, SinkBase {
1923
private CallExpr call;
2024

21-
CallExprSource() { this = call.getFunction() }
25+
CallExprFunction() { this = call.getFunction() }
2226

2327
override CallExpr getCall() { result = call }
2428
}
2529

26-
private class MethodCallExprSource extends SourceBase {
30+
private class MethodCallExprNameRef extends SourceBase, SinkBase {
2731
private MethodCallExpr call;
2832

29-
MethodCallExprSource() { this = call }
33+
MethodCallExprNameRef() { this = call.getNameRef() }
3034

3135
override MethodCallExpr getCall() { result = call }
3236
}
@@ -114,6 +118,7 @@ module Private {
114118
module Steps = Impl::Private::Steps<StepsInput>;
115119

116120
private import codeql.rust.dataflow.FlowSource
121+
private import codeql.rust.dataflow.FlowSink
117122

118123
predicate localSourceNodeStep(Node::FlowSummaryNode nodeFrom, Node::ExprNode nodeTo, string model) {
119124
exists(SummaryComponent sc, FlowSource source |
@@ -122,6 +127,19 @@ module Private {
122127
nodeTo.asExpr().getExpr() = source.getCall()
123128
)
124129
}
130+
131+
predicate localSinkNodeStep(Node::ExprNode nodeFrom, Node::FlowSummaryNode nodeTo, string model) {
132+
exists(CallExprBase call, Expr arg, SummaryComponent sc, FlowSink sink, ParameterPosition pos |
133+
nodeFrom.asExpr().getExpr() = arg and
134+
nodeTo.getSummaryNode().(SinkInputNode).isEntry(sink, sc, model) and
135+
sc = SummaryComponent::argument(pos) and
136+
call = sink.getCall()
137+
|
138+
arg = call.getArgList().getArg(pos.getPosition())
139+
or
140+
arg = call.(MethodCallExpr).getReceiver() and pos.isSelf()
141+
)
142+
}
125143
}
126144

127145
module Public = Impl::Public;

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
private import rust
4949
private import codeql.rust.dataflow.FlowSummary
5050
private import codeql.rust.dataflow.FlowSource
51+
private import codeql.rust.dataflow.FlowSink
5152
private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprBaseImpl
5253

5354
/**
@@ -165,3 +166,28 @@ private class FlowSourceFromModel extends FlowSource::Range {
165166
)
166167
}
167168
}
169+
170+
private class FlowSinkFromModel extends FlowSink::Range {
171+
private string crate;
172+
private string path;
173+
174+
FlowSinkFromModel() {
175+
sinkModel(crate, path, _, _, _, _) and
176+
exists(Resolvable r |
177+
r = CallExprBaseImpl::getCallResolvable(this.getCall()) and
178+
path = r.getResolvedPath()
179+
|
180+
crate = r.getResolvedCrateOrigin()
181+
or
182+
not r.hasResolvedCrateOrigin() and
183+
crate = ""
184+
)
185+
}
186+
187+
override predicate isSink(string input, string kind, Provenance provenance, string model) {
188+
exists(QlBuiltins::ExtensionId madId |
189+
sinkModel(crate, path, input, kind, provenance, madId) and
190+
model = "MaD:" + madId.toString()
191+
)
192+
}
193+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ private module FlowTestImpl implements InputSig<Location, RustDataFlow> {
2929
result = src.asExpr().(CallExprCfgNode).getArgument(0).toString()
3030
or
3131
sourceNode(src, _) and
32-
exists(CallExpr call |
32+
exists(CallExprBase call |
3333
call = src.(Node::FlowSummaryNode).getSourceNode().getCall() and
3434
result = call.getArgList().getArg(0).toString()
3535
)

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

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

178+
impl MyFieldEnum {
179+
// has a source model
180+
fn source(&self, i: i64) -> MyFieldEnum {
181+
MyFieldEnum::C { field_c: 0 }
182+
}
183+
184+
// has a sink model
185+
fn sink(self) {}
186+
}
187+
178188
// has a source model
179189
fn enum_source(i: i64) -> MyFieldEnum {
180190
MyFieldEnum::C { field_c: 0 }
@@ -188,6 +198,30 @@ fn test_enum_source() {
188198
}
189199
}
190200

201+
fn test_enum_method_source() {
202+
let e = MyFieldEnum::D { field_d: 0 };
203+
let s = e.source(13);
204+
match s {
205+
MyFieldEnum::C { field_c: i } => sink(i), // $ hasValueFlow=13
206+
MyFieldEnum::D { field_d: i } => sink(i),
207+
}
208+
}
209+
210+
// has a sink model
211+
fn enum_sink(e: MyFieldEnum) {}
212+
213+
fn test_enum_sink() {
214+
let s = source(14);
215+
enum_sink(MyFieldEnum::C { field_c: s }); // $ hasValueFlow=14
216+
enum_sink(MyFieldEnum::D { field_d: s });
217+
}
218+
219+
fn test_enum_method_sink() {
220+
let s = source(15);
221+
let e = MyFieldEnum::D { field_d: s };
222+
e.sink(); // $ hasValueFlow=15
223+
}
224+
191225
fn main() {
192226
test_identify();
193227
test_get_var_pos();
@@ -201,5 +235,8 @@ fn main() {
201235
test_get_tuple_element();
202236
test_set_tuple_element();
203237
test_enum_source();
238+
test_enum_method_source();
239+
test_enum_sink();
240+
test_enum_method_sink();
204241
let dummy = Some(0); // ensure that the the `lang:core` crate is extracted
205242
}

0 commit comments

Comments
 (0)