Skip to content

Commit 5852fe4

Browse files
committed
PS: Add a concept of 'source call' vs. 'library call' to avoid non-monotonic recursion in the next commits.
1 parent cfde677 commit 5852fe4

File tree

6 files changed

+106
-35
lines changed

6 files changed

+106
-35
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import powershell
44
private import semmle.code.powershell.controlflow.Cfg
55
private import semmle.code.powershell.typetracking.TypeTracking
6-
import semmle.code.powershell.dataflow.DataFlow
6+
private import semmle.code.powershell.dataflow.DataFlow
77
private import internal.FlowSummaryImpl as Impl
88
private import internal.DataFlowDispatch
99
private import internal.DataFlowImplCommon as DataFlowImplCommon
@@ -55,5 +55,7 @@ abstract class SimpleSummarizedCallable extends SummarizedCallable {
5555
bindingset[this]
5656
SimpleSummarizedCallable() { c.getName() = this }
5757

58-
final override MethodCall getACall() { result = c }
58+
final override Call getACall() { result = c }
59+
60+
final override Call getACallSimple() { result = c }
5961
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ abstract class LibraryCallable extends string {
4040

4141
/** Gets a call to this library callable. */
4242
Call getACall() { none() }
43+
44+
/** Same as `getACall()` except this does not depend on the call graph or API graph. */
45+
Call getACallSimple() { none() }
46+
}
47+
48+
/** A callable defined in library code, which should be taken into account in type tracking. */
49+
abstract class LibraryCallableToIncludeInTypeTracking extends LibraryCallable {
50+
bindingset[this]
51+
LibraryCallableToIncludeInTypeTracking() { exists(this) }
4352
}
4453

4554
/**
@@ -230,6 +239,14 @@ class AdditionalCallTarget extends Unit {
230239
abstract DataFlowCallable viableTarget(CfgNodes::CallCfgNode call);
231240
}
232241

242+
/** Holds if `call` may resolve to the returned summarized library method. */
243+
DataFlowCallable viableLibraryCallable(DataFlowCall call) {
244+
exists(LibraryCallable callable |
245+
result = TLibraryCallable(callable) and
246+
call.asCall().getAstNode() = [callable.getACall(), callable.getACallSimple()]
247+
)
248+
}
249+
233250
cached
234251
private module Cached {
235252
cached

powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,13 @@ private module ParameterNodes {
479479
abstract Parameter getParameter();
480480

481481
abstract predicate isParameterOf(DataFlowCallable c, ParameterPosition pos);
482+
483+
final predicate isSourceParameterOf(CfgScope c, ParameterPosition pos) {
484+
exists(DataFlowCallable callable |
485+
this.isParameterOf(callable, pos) and
486+
c = callable.asCfgScope()
487+
)
488+
}
482489
}
483490

484491
/**
@@ -590,6 +597,8 @@ abstract class ArgumentNode extends Node {
590597
/** Holds if this argument occurs at the given position in the given call. */
591598
abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos);
592599

600+
abstract predicate sourceArgumentOf(CfgNodes::CallCfgNode call, ArgumentPosition pos);
601+
593602
/** Gets the call in which this node is an argument. */
594603
final DataFlowCall getCall() { this.argumentOf(result, _) }
595604
}
@@ -601,13 +610,17 @@ module ArgumentNodes {
601610
ExplicitArgumentNode() { this.asExpr() = arg }
602611

603612
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
604-
arg.getCall() = call.asCall() and
613+
this.sourceArgumentOf(call.asCall(), pos)
614+
}
615+
616+
override predicate sourceArgumentOf(CfgNodes::CallCfgNode call, ArgumentPosition pos) {
617+
arg.getCall() = call and
605618
(
606619
pos.isKeyword(arg.getName())
607620
or
608621
exists(NamedSet ns, int i |
609622
i = arg.getPosition() and
610-
ns.getAnExactBindingCall() = call.asCall() and
623+
ns.getAnExactBindingCall() = call and
611624
pos.isPositional(i, ns)
612625
)
613626
or
@@ -632,7 +645,11 @@ module ArgumentNodes {
632645
PipelineArgumentNode() { isPipelineInput(this.getStmtNode(), consumer) }
633646

634647
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
635-
call.asCall() = consumer and
648+
this.sourceArgumentOf(call.asCall(), pos)
649+
}
650+
651+
override predicate sourceArgumentOf(CfgNodes::CallCfgNode call, ArgumentPosition pos) {
652+
call = consumer and
636653
pos.isPipeline()
637654
}
638655
}
@@ -648,6 +665,8 @@ module ArgumentNodes {
648665
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
649666
call.(SummaryCall).getReceiver() = receiver and pos = pos_
650667
}
668+
669+
override predicate sourceArgumentOf(CfgNodes::CallCfgNode call, ArgumentPosition pos) { none() }
651670
}
652671
}
653672

powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
private import powershell
22
private import DataFlowDispatch
33
private import DataFlowPrivate
4+
private import semmle.code.powershell.typetracking.internal.TypeTrackingImpl
45
private import semmle.code.powershell.Cfg
56

67
/**
@@ -13,6 +14,8 @@ class Node extends TNode {
1314

1415
CfgNodes::StmtCfgNode asStmt() { result = this.(StmtNode).getStmtNode() }
1516

17+
ScriptBlock asCallable() { result = this.(CallableNode).asCallableAstNode() }
18+
1619
/** Gets the parameter corresponding to this node, if any. */
1720
Parameter asParameter() { result = this.(ParameterNode).getParameter() }
1821

@@ -27,6 +30,12 @@ class Node extends TNode {
2730
*/
2831
Node getAPredecessor() { localFlowStep(result, this) }
2932

33+
/**
34+
* Gets a local source node from which data may flow to this node in zero or
35+
* more local data-flow steps.
36+
*/
37+
LocalSourceNode getALocalSource() { result.flowsTo(this) }
38+
3039
/**
3140
* Gets a data flow node to which data may flow from this node in one local step.
3241
*/
@@ -115,9 +124,18 @@ class PostUpdateNode extends Node {
115124

116125
cached
117126
private module Cached {
127+
cached
128+
predicate hasMethodCall(LocalSourceNode source, CallNode call, string name) {
129+
source.flowsTo(call.getQualifier()) and
130+
call.getName() = name
131+
}
132+
118133
cached
119134
CfgScope getCfgScope(NodeImpl node) { result = node.getCfgScope() }
120135

136+
cached
137+
ReturnNode getAReturnNode(ScriptBlock scriptBlock) { getCfgScope(result) = scriptBlock }
138+
121139
cached
122140
Parameter getParameter(ParameterNodeImpl param) { result = param.getParameter() }
123141

@@ -126,6 +144,11 @@ private module Cached {
126144
param.isParameterOf(c, result)
127145
}
128146

147+
cached
148+
ParameterPosition getSourceParameterPosition(ParameterNodeImpl param, ScriptBlock c) {
149+
param.isSourceParameterOf(c, result)
150+
}
151+
129152
cached
130153
Node getPreUpdateNode(PostUpdateNodeImpl node) { result = node.getPreUpdateNode() }
131154

powershell/ql/lib/semmle/code/powershell/dataflow/internal/FlowSummaryImpl.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ private import Make<Location, DataFlowImplSpecific::PowershellDataFlow, Input> a
111111
private module StepsInput implements Impl::Private::StepsInputSig {
112112
DataFlowCall getACall(Public::SummarizedCallable sc) {
113113
result.asCall().getAstNode() = sc.(LibraryCallable).getACall()
114+
or
115+
result.asCall().getAstNode() = sc.(LibraryCallable).getACallSimple()
114116
}
115117
}
116118

powershell/ql/lib/semmle/code/powershell/typetracking/internal/TypeTrackingImpl.qll

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,28 @@ private import semmle.code.powershell.dataflow.internal.DataFlowDispatch as Data
1313
private import semmle.code.powershell.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
1414
private import codeql.util.Unit
1515

16+
pragma[noinline]
17+
private predicate sourceArgumentPositionMatch(
18+
CallCfgNode call, DataFlowPrivate::ArgumentNode arg, DataFlowDispatch::ParameterPosition ppos
19+
) {
20+
exists(DataFlowDispatch::ArgumentPosition apos |
21+
arg.sourceArgumentOf(call, apos) and
22+
DataFlowDispatch::parameterMatch(ppos, apos)
23+
)
24+
}
25+
1626
pragma[noinline]
1727
private predicate argumentPositionMatch(
1828
DataFlowDispatch::DataFlowCall call, DataFlowPrivate::ArgumentNode arg,
1929
DataFlowDispatch::ParameterPosition ppos
2030
) {
31+
sourceArgumentPositionMatch(call.asCall(), arg, ppos)
32+
or
2133
exists(DataFlowDispatch::ArgumentPosition apos |
34+
DataFlowDispatch::parameterMatch(ppos, apos) and
2235
arg.argumentOf(call, apos) and
23-
DataFlowDispatch::parameterMatch(ppos, apos)
36+
call.getEnclosingCallable().asLibraryCallable() instanceof
37+
DataFlowDispatch::LibraryCallableToIncludeInTypeTracking
2438
)
2539
}
2640

@@ -31,6 +45,12 @@ private predicate viableParam(
3145
) {
3246
exists(DataFlowDispatch::DataFlowCallable callable |
3347
DataFlowDispatch::getTarget(call) = callable.asCfgScope()
48+
or
49+
call.asCall().getAstNode() =
50+
callable
51+
.asLibraryCallable()
52+
.(DataFlowDispatch::LibraryCallableToIncludeInTypeTracking)
53+
.getACallSimple()
3454
|
3555
p.isParameterOf(callable, ppos)
3656
)
@@ -103,45 +123,33 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
103123

104124
// Relating nodes to summaries
105125
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) {
106-
// exists(
107-
// DataFlowDispatch::ParameterPosition pos, DataFlowPrivate::ArgumentNode n,
108-
// DataFlowDispatch::DataFlowCall dfCall
109-
// |
110-
// arg = FlowSummaryImpl::Private::SummaryComponent::argument(pos) and
111-
// // TODO: Make this look more like Ruby when we know why it should be like Ruby
112-
// dfCall.asCall() = call.(DataFlow::AstNode).getCfgNode() and
113-
// argumentPositionMatch(dfCall, n, pos)
114-
// |
115-
// isPostUpdate = false and result = n
116-
// or
117-
// isPostUpdate = true and result.(DataFlowPublic::PostUpdateNode).getPreUpdateNode() = n
118-
// )
119-
none()
126+
exists(DataFlowDispatch::ParameterPosition pos, DataFlowPrivate::ArgumentNode n |
127+
arg = FlowSummaryImpl::Private::SummaryComponent::argument(pos) and
128+
sourceArgumentPositionMatch(call.asExpr(), n, pos)
129+
|
130+
isPostUpdate = false and result = n
131+
or
132+
isPostUpdate = true and result.(DataFlowPublic::PostUpdateNode).getPreUpdateNode() = n
133+
)
120134
}
121135

122136
Node parameterOf(Node callable, SummaryComponent param) {
123-
// exists(DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos |
124-
// param = FlowSummaryImpl::Private::SummaryComponent::parameter(apos) and
125-
// DataFlowDispatch::parameterMatch(ppos, apos) and
126-
// result
127-
// .(DataFlowPrivate::ParameterNodeImpl)
128-
// .isSourceParameterOf(callable, ppos)
129-
// )
130-
// TODO
131-
none()
137+
exists(DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos |
138+
param = FlowSummaryImpl::Private::SummaryComponent::parameter(apos) and
139+
DataFlowDispatch::parameterMatch(ppos, apos) and
140+
result.(DataFlowPrivate::ParameterNodeImpl).isSourceParameterOf(callable.asCallable(), ppos)
141+
)
132142
}
133143

134144
Node returnOf(Node callable, SummaryComponent return) {
135-
// return = FlowSummaryImpl::Private::SummaryComponent::return() and
136-
// result.(DataFlowPrivate::ReturnNode).(DataFlowPrivate::NodeImpl).getCfgScope() =
137-
// callable.asExpr().getExpr()
138-
// TODO
139-
none()
145+
return = FlowSummaryImpl::Private::SummaryComponent::return() and
146+
result.(DataFlowPrivate::ReturnNode).(DataFlowPrivate::NodeImpl).getCfgScope() =
147+
callable.asCallable()
140148
}
141149

142150
// Relating callables to nodes
143151
Node callTo(SummarizedCallable callable) {
144-
result.asExpr().getExpr() = callable.(FlowSummary::SummarizedCallable).getACall()
152+
result.asExpr().getExpr() = callable.(FlowSummary::SummarizedCallable).getACallSimple()
145153
}
146154
}
147155

0 commit comments

Comments
 (0)