Skip to content

Commit eef5022

Browse files
authored
Merge pull request #9014 from michaelnebel/csharp/dataflowcallablerefactor
C#: Dataflow callable refactoring.
2 parents a4dac9f + 23ee033 commit eef5022

File tree

16 files changed

+284
-106
lines changed

16 files changed

+284
-106
lines changed

csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,8 +515,10 @@ Element interpretElement(
515515
/**
516516
* Holds if `c` has a `generated` summary.
517517
*/
518-
predicate hasSummary(DataFlowCallable c, boolean generated) {
519-
summaryElement(c, _, _, _, generated)
518+
predicate hasSummary(Callable c, boolean generated) {
519+
exists(DataFlowCallable dc |
520+
dc.asSummarizedCallable() = c and summaryElement(dc, _, _, _, generated)
521+
)
520522
}
521523

522524
cached

csharp/ql/lib/semmle/code/csharp/dataflow/FlowSummary.qll

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/** Provides classes and predicates for defining flow summaries. */
22

33
import csharp
4+
private import dotnet
45
private import internal.FlowSummaryImpl as Impl
56
private import internal.DataFlowDispatch as DataFlowDispatch
67

@@ -113,7 +114,69 @@ module SummaryComponentStack {
113114
SummaryComponentStack jump(Callable c) { result = singleton(SummaryComponent::jump(c)) }
114115
}
115116

116-
class SummarizedCallable = Impl::Public::SummarizedCallable;
117+
/**
118+
* A class for synthesized callables given by a summary.
119+
*/
120+
abstract class SummarizedCallable extends DotNet::Callable {
121+
SummarizedCallable() { this.isUnboundDeclaration() }
122+
123+
/**
124+
* Holds if data may flow from `input` to `output` through this callable.
125+
*
126+
* `preservesValue` indicates whether this is a value-preserving step
127+
* or a taint-step.
128+
*
129+
* Input specifications are restricted to stacks that end with
130+
* `SummaryComponent::argument(_)`, preceded by zero or more
131+
* `SummaryComponent::return(_)` or `SummaryComponent::content(_)` components.
132+
*
133+
* Output specifications are restricted to stacks that end with
134+
* `SummaryComponent::return(_)` or `SummaryComponent::argument(_)`.
135+
*
136+
* Output stacks ending with `SummaryComponent::return(_)` can be preceded by zero
137+
* or more `SummaryComponent::content(_)` components.
138+
*
139+
* Output stacks ending with `SummaryComponent::argument(_)` can be preceded by an
140+
* optional `SummaryComponent::parameter(_)` component, which in turn can be preceded
141+
* by zero or more `SummaryComponent::content(_)` components.
142+
*/
143+
pragma[nomagic]
144+
predicate propagatesFlow(
145+
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
146+
) {
147+
none()
148+
}
149+
150+
/**
151+
* Holds if values stored inside `content` are cleared on objects passed as
152+
* arguments at position `pos` to this callable.
153+
*/
154+
pragma[nomagic]
155+
predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) { none() }
156+
157+
/**
158+
* Holds if the summary is auto generated.
159+
*/
160+
predicate isAutoGenerated() { none() }
161+
}
162+
163+
private class SummarizedCallableAdapter extends Impl::Public::SummarizedCallable {
164+
private SummarizedCallable sc;
165+
166+
SummarizedCallableAdapter() { this = DataFlowDispatch::TSummarizedCallable(sc) }
167+
168+
final override predicate propagatesFlow(
169+
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
170+
) {
171+
sc.propagatesFlow(input, output, preservesValue)
172+
}
173+
174+
final override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
175+
sc.clearsContent(pos, content)
176+
}
177+
178+
final override predicate isAutoGenerated() { sc.isAutoGenerated() }
179+
}
117180

118181
private predicate recordConstructorFlow(Constructor c, int i, Property p) {
119182
c = any(RecordType r).getAMember() and

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,6 @@ private import semmle.code.csharp.dispatch.RuntimeCallable
1212
private import semmle.code.csharp.frameworks.system.Collections
1313
private import semmle.code.csharp.frameworks.system.collections.Generic
1414

15-
private predicate summarizedCallable(DataFlowCallable c) {
16-
c instanceof FlowSummary::SummarizedCallable
17-
or
18-
FlowSummaryImpl::Private::summaryReturnNode(_, TJumpReturnKind(c, _))
19-
or
20-
c = interpretElement(_, _, _, _, _, _)
21-
}
22-
2315
/**
2416
* Gets a source declaration of callable `c` that has a body or has
2517
* a flow summary.
@@ -29,9 +21,6 @@ private predicate summarizedCallable(DataFlowCallable c) {
2921
*/
3022
DotNet::Callable getCallableForDataFlow(DotNet::Callable c) {
3123
exists(DotNet::Callable unboundDecl | unboundDecl = c.getUnboundDeclaration() |
32-
summarizedCallable(unboundDecl) and
33-
result = unboundDecl
34-
or
3524
result.hasBody() and
3625
if unboundDecl.getFile().fromSource()
3726
then
@@ -81,17 +70,27 @@ newtype TReturnKind =
8170
v = def.getSourceVariable().getAssignable()
8271
)
8372
} or
84-
TJumpReturnKind(DataFlowCallable target, ReturnKind rk) {
85-
rk instanceof NormalReturnKind and
73+
TJumpReturnKind(Callable target, ReturnKind rk) {
74+
target.isUnboundDeclaration() and
8675
(
87-
target instanceof Constructor or
88-
not target.getReturnType() instanceof VoidType
76+
rk instanceof NormalReturnKind and
77+
(
78+
target instanceof Constructor or
79+
not target.getReturnType() instanceof VoidType
80+
)
81+
or
82+
exists(target.getParameter(rk.(OutRefReturnKind).getPosition()))
8983
)
90-
or
91-
exists(target.getParameter(rk.(OutRefReturnKind).getPosition()))
9284
}
9385

9486
private module Cached {
87+
cached
88+
newtype TDataFlowCallable =
89+
TDotNetCallable(DotNet::Callable c) {
90+
c.isUnboundDeclaration() and not c instanceof FlowSummary::SummarizedCallable
91+
} or
92+
TSummarizedCallable(FlowSummary::SummarizedCallable c)
93+
9594
cached
9695
newtype TDataFlowCall =
9796
TNonDelegateCall(ControlFlow::Nodes::ElementNode cfn, DispatchCall dc) {
@@ -108,7 +107,7 @@ private module Cached {
108107
// No need to include calls that are compiled from source
109108
not call.getImplementation().getMethod().compiledFromSource()
110109
} or
111-
TSummaryCall(FlowSummary::SummarizedCallable c, Node receiver) {
110+
TSummaryCall(FlowSummaryImpl::Public::SummarizedCallable c, Node receiver) {
112111
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
113112
}
114113

@@ -144,7 +143,7 @@ private module DispatchImpl {
144143
* call is a delegate call, or if the qualifier accesses a parameter of
145144
* the enclosing callable `c` (including the implicit `this` parameter).
146145
*/
147-
predicate mayBenefitFromCallContext(NonDelegateDataFlowCall call, Callable c) {
146+
predicate mayBenefitFromCallContext(NonDelegateDataFlowCall call, DataFlowCallable c) {
148147
c = call.getEnclosingCallable() and
149148
call.getDispatchCall().mayBenefitFromCallContext()
150149
}
@@ -154,7 +153,7 @@ private module DispatchImpl {
154153
* restricted to those `call`s for which a context might make a difference.
155154
*/
156155
DataFlowCallable viableImplInCallContext(NonDelegateDataFlowCall call, DataFlowCall ctx) {
157-
result =
156+
result.getUnderlyingCallable() =
158157
call.getDispatchCall()
159158
.getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall).getDispatchCall())
160159
.getUnboundDeclaration()
@@ -233,22 +232,38 @@ class ImplicitCapturedReturnKind extends ReturnKind, TImplicitCapturedReturnKind
233232
* one API entry point and out of another.
234233
*/
235234
class JumpReturnKind extends ReturnKind, TJumpReturnKind {
236-
private DataFlowCallable target;
235+
private Callable target;
237236
private ReturnKind rk;
238237

239238
JumpReturnKind() { this = TJumpReturnKind(target, rk) }
240239

241240
/** Gets the target of the jump. */
242-
DataFlowCallable getTarget() { result = target }
241+
Callable getTarget() { result = target }
243242

244243
/** Gets the return kind of the target. */
245244
ReturnKind getTargetReturnKind() { result = rk }
246245

247246
override string toString() { result = "jump to " + target }
248247
}
249248

250-
class DataFlowCallable extends DotNet::Callable {
251-
DataFlowCallable() { this.isUnboundDeclaration() }
249+
/** A callable used for data flow. */
250+
class DataFlowCallable extends TDataFlowCallable {
251+
/** Get the underlying source code callable, if any. */
252+
DotNet::Callable asCallable() { this = TDotNetCallable(result) }
253+
254+
/** Get the underlying summarized callable, if any. */
255+
FlowSummary::SummarizedCallable asSummarizedCallable() { this = TSummarizedCallable(result) }
256+
257+
/** Get the underlying callable. */
258+
DotNet::Callable getUnderlyingCallable() {
259+
result = this.asCallable() or result = this.asSummarizedCallable()
260+
}
261+
262+
/** Gets a textual representation of this dataflow callable. */
263+
string toString() { result = this.getUnderlyingCallable().toString() }
264+
265+
/** Get the location of this dataflow callable. */
266+
Location getLocation() { result = this.getUnderlyingCallable().getLocation() }
252267
}
253268

254269
/** A call relevant for data flow. */
@@ -306,18 +321,32 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
306321
DispatchCall getDispatchCall() { result = dc }
307322

308323
override DataFlowCallable getARuntimeTarget() {
309-
result = getCallableForDataFlow(dc.getADynamicTarget())
324+
result.asCallable() = getCallableForDataFlow(dc.getADynamicTarget())
325+
or
326+
exists(Callable c, boolean static |
327+
result.asSummarizedCallable() = c and
328+
c = this.getATarget(static)
329+
|
330+
static = false
331+
or
332+
static = true and not c instanceof RuntimeCallable
333+
)
334+
}
335+
336+
/** Gets a static or dynamic target of this call. */
337+
Callable getATarget(boolean static) {
338+
result = dc.getADynamicTarget().getUnboundDeclaration() and static = false
310339
or
311-
result = dc.getAStaticTarget().getUnboundDeclaration() and
312-
summarizedCallable(result) and
313-
not result instanceof RuntimeCallable
340+
result = dc.getAStaticTarget().getUnboundDeclaration() and static = true
314341
}
315342

316343
override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
317344

318345
override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }
319346

320-
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
347+
override DataFlowCallable getEnclosingCallable() {
348+
result.getUnderlyingCallable() = cfn.getEnclosingCallable()
349+
}
321350

322351
override string toString() { result = cfn.toString() }
323352

@@ -345,7 +374,9 @@ class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDe
345374

346375
override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }
347376

348-
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
377+
override DataFlowCallable getEnclosingCallable() {
378+
result.getUnderlyingCallable() = cfn.getEnclosingCallable()
379+
}
349380

350381
override string toString() { result = cfn.toString() }
351382

@@ -363,13 +394,15 @@ class TransitiveCapturedDataFlowCall extends DataFlowCall, TTransitiveCapturedCa
363394

364395
TransitiveCapturedDataFlowCall() { this = TTransitiveCapturedCall(cfn, target) }
365396

366-
override DataFlowCallable getARuntimeTarget() { result = target }
397+
override DataFlowCallable getARuntimeTarget() { result.getUnderlyingCallable() = target }
367398

368399
override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
369400

370401
override DataFlow::ExprNode getNode() { none() }
371402

372-
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
403+
override DataFlowCallable getEnclosingCallable() {
404+
result.getUnderlyingCallable() = cfn.getEnclosingCallable()
405+
}
373406

374407
override string toString() { result = "[transitive] " + cfn.toString() }
375408

@@ -384,14 +417,16 @@ class CilDataFlowCall extends DataFlowCall, TCilCall {
384417

385418
override DataFlowCallable getARuntimeTarget() {
386419
// There is no dispatch library for CIL, so do not consider overrides for now
387-
result = getCallableForDataFlow(call.getTarget())
420+
result.getUnderlyingCallable() = getCallableForDataFlow(call.getTarget())
388421
}
389422

390423
override ControlFlow::Nodes::ElementNode getControlFlowNode() { none() }
391424

392425
override DataFlow::ExprNode getNode() { result.getExpr() = call }
393426

394-
override DataFlowCallable getEnclosingCallable() { result = call.getEnclosingCallable() }
427+
override DataFlowCallable getEnclosingCallable() {
428+
result.getUnderlyingCallable() = call.getEnclosingCallable()
429+
}
395430

396431
override string toString() { result = call.toString() }
397432

@@ -406,7 +441,7 @@ class CilDataFlowCall extends DataFlowCall, TCilCall {
406441
* the method `Select`.
407442
*/
408443
class SummaryCall extends DelegateDataFlowCall, TSummaryCall {
409-
private FlowSummary::SummarizedCallable c;
444+
private FlowSummaryImpl::Public::SummarizedCallable c;
410445
private Node receiver;
411446

412447
SummaryCall() { this = TSummaryCall(c, receiver) }

0 commit comments

Comments
 (0)