Skip to content

Commit 5151eb3

Browse files
committed
PS: Add dataflow for pipeline-by-property-name variables.
1 parent 86ec291 commit 5151eb3

File tree

2 files changed

+121
-85
lines changed

2 files changed

+121
-85
lines changed

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

Lines changed: 107 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ module SsaFlow {
6666
result = TThisParameterNode(p.asThis())
6767
or
6868
result = TPipelineParameterNode(p.asPipelineParameter())
69+
or
70+
result = TPipelineByPropertyNameParameterNode(p.asPipelineByPropertyNameParameter())
6971
}
7072

7173
/** Gets the SSA node corresponding to the PowerShell node `n`. */
@@ -83,6 +85,16 @@ module SsaFlow {
8385
.definesAt(pb.getPipelineIteratorVariable(), bb, i)
8486
)
8587
or
88+
exists(
89+
BasicBlock bb, int i, PipelineByPropertyNameParameter p, ProcessPropertyByNameNode pbNode
90+
|
91+
pbNode = n and
92+
pbNode.hasRead() and
93+
pbNode.getParameter() = p and
94+
bb.getNode(i) = pbNode.getProcessBlock() and
95+
result.(Impl::SsaDefinitionNode).getDefinition().definesAt(p.getIteratorVariable(), bb, i)
96+
)
97+
or
8698
result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr()
8799
or
88100
n = toParameterNode(result.(Impl::ParameterNode).getParameter())
@@ -145,13 +157,16 @@ module LocalFlow {
145157
nodeTo.(ReturnNodeImpl).getCfgScope() = scriptBlock.getAstNode()
146158
)
147159
or
148-
exists(CfgNodes::ExprNodes::PipelineArgumentCfgNode e | nodeFrom.asExpr() = e |
149-
// If we are not already tracking as element content
150-
nodeTo = TPrePipelineArgumentNode(e)
151-
or
152-
// If we are already tracking an element content
153-
nodeTo = TPipelineArgumentNode(e)
160+
nodeTo.(PreProcessPropertyByNameNode).getAccess() = nodeFrom.asExpr()
161+
or
162+
exists(PreProcessPropertyByNameNode pbNode |
163+
pbNode = nodeFrom and
164+
nodeTo = TProcessPropertyByNameNode(pbNode.getAccess().getVariable(), false)
154165
)
166+
or
167+
nodeTo.(PreProcessNode).getProcessBlock().getPipelineVariableAccess() = nodeFrom.asExpr()
168+
or
169+
nodeTo.(ProcessNode).getProcessBlock() = nodeFrom.(PreProcessNode).getProcessBlock()
155170
}
156171

157172
predicate flowSummaryLocalStep(
@@ -179,12 +194,6 @@ module VariableCapture {
179194
// TODO
180195
}
181196

182-
private predicate isProcessPropertyByNameNode(
183-
PipelineByPropertyNameIteratorVariable iter, ProcessBlock pb
184-
) {
185-
pb = iter.getProcessBlock()
186-
}
187-
188197
/** A collection of cached types and predicates to be evaluated in the same stage. */
189198
cached
190199
private module Cached {
@@ -198,8 +207,6 @@ private module Cached {
198207
TThisParameterNode(Method m) or
199208
TPipelineByPropertyNameParameterNode(PipelineByPropertyNameParameter p) or
200209
TPipelineParameterNode(PipelineParameter p) or
201-
TPrePipelineArgumentNode(CfgNodes::ExprNodes::PipelineArgumentCfgNode n) or
202-
TPipelineArgumentNode(CfgNodes::ExprNodes::PipelineArgumentCfgNode n) or
203210
TExprPostUpdateNode(CfgNodes::ExprCfgNode n) {
204211
n instanceof CfgNodes::ExprNodes::ArgumentCfgNode
205212
or
@@ -220,9 +227,13 @@ private module Cached {
220227
blockMayReturnMultipleValues(scriptBlock)
221228
} or
222229
TReturnNodeImpl(CfgScope scope) or
230+
TPreProcessNode(CfgNodes::ProcessBlockCfgNode process) or
223231
TProcessNode(CfgNodes::ProcessBlockCfgNode process) or
224-
TProcessPropertyByNameNode(PipelineByPropertyNameIteratorVariable iter) {
225-
isProcessPropertyByNameNode(iter, _)
232+
TPreProcessPropertyByNameNode(CfgNodes::ExprNodes::VarReadAccessCfgNode va) {
233+
any(CfgNodes::ProcessBlockCfgNode pb).getAPipelineByPropertyNameParameterAccess() = va
234+
} or
235+
TProcessPropertyByNameNode(PipelineByPropertyNameParameter p, Boolean hasRead) {
236+
p.getDeclaringScope() = any(ProcessBlock pb).getScriptBlock()
226237
} or
227238
TScriptBlockNode(ScriptBlock scriptBlock) or
228239
TForbiddenRecursionGuard() {
@@ -642,10 +653,10 @@ private module ParameterNodes {
642653

643654
PipelineByPropertyNameParameterNode() { this = TPipelineByPropertyNameParameterNode(parameter) }
644655

645-
override PipelineParameter getParameter() { result = parameter }
656+
override PipelineByPropertyNameParameter getParameter() { result = parameter }
646657

647658
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
648-
pos.isPipeline() and // what about when it is applied as a normal parameter?
659+
pos.isPipeline() and
649660
c.asCfgScope() = parameter.getEnclosingScope()
650661
}
651662

@@ -709,22 +720,6 @@ abstract class ArgumentNode extends Node {
709720
final DataFlowCall getCall() { this.argumentOf(result, _) }
710721
}
711722

712-
class PrePipelineArgumentNodeImpl extends NodeImpl, TPrePipelineArgumentNode {
713-
CfgNodes::ExprNodes::PipelineArgumentCfgNode e;
714-
715-
PrePipelineArgumentNodeImpl() { this = TPrePipelineArgumentNode(e) }
716-
717-
final override CfgScope getCfgScope() { result = e.getScope() }
718-
719-
final override Location getLocationImpl() { result = e.getLocation() }
720-
721-
final override string toStringImpl() { result = "[pre pipeline] " + e.toString() }
722-
723-
final override predicate nodeIsHidden() { any() }
724-
725-
CfgNodes::ExprNodes::PipelineArgumentCfgNode getPipelineArgument() { result = e }
726-
}
727-
728723
module ArgumentNodes {
729724
class ExplicitArgumentNode extends ArgumentNode {
730725
CfgNodes::ExprNodes::ArgumentCfgNode arg;
@@ -768,31 +763,23 @@ module ArgumentNodes {
768763
}
769764
}
770765

771-
class PipelineArgumentNodeImpl extends NodeImpl, TPipelineArgumentNode {
772-
CfgNodes::ExprNodes::PipelineArgumentCfgNode e;
773-
774-
PipelineArgumentNodeImpl() { this = TPipelineArgumentNode(e) }
775-
776-
final override CfgScope getCfgScope() { result = e.getScope() }
777-
778-
final override Location getLocationImpl() { result = e.getLocation() }
779-
780-
final override string toStringImpl() { result = e.toString() }
781-
782-
final override predicate nodeIsHidden() { none() }
766+
class PipelineArgumentNode extends ArgumentNode instanceof ExprNode {
767+
PipelineArgumentNode() {
768+
this.getExprNode() instanceof CfgNodes::ExprNodes::PipelineArgumentCfgNode
769+
}
783770

784-
CfgNodes::ExprNodes::PipelineArgumentCfgNode getPipelineArgument() { result = e }
785-
}
771+
CfgNodes::ExprNodes::PipelineArgumentCfgNode getPipelineArgument() {
772+
result = super.getExprNode()
773+
}
786774

787-
class PipelineArgumentNode extends ArgumentNode instanceof PipelineArgumentNodeImpl {
788775
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
789776
this.sourceArgumentOf(call.asCall(), pos)
790777
}
791778

792779
override predicate sourceArgumentOf(
793780
CfgNodes::ExprNodes::CallExprCfgNode call, ArgumentPosition pos
794781
) {
795-
call = super.getPipelineArgument().getCall() and
782+
call = this.getPipelineArgument().getCall() and
796783
pos.isPipeline()
797784
}
798785
}
@@ -981,9 +968,9 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
981968
)
982969
or
983970
c.isUnknownPositionalContent() and
984-
exists(CfgNodes::ExprNodes::PipelineArgumentCfgNode arg |
985-
node1 = TPrePipelineArgumentNode(arg) and
986-
node2 = TPipelineArgumentNode(arg)
971+
exists(CfgNodes::ProcessBlockCfgNode process |
972+
node1 = TPreProcessNode(process) and
973+
node2 = TProcessNode(process)
987974
)
988975
or
989976
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
@@ -1021,27 +1008,24 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
10211008
c.isSingleton(any(Content::KnownElementContent ec | exists(ec.getIndex().asInt())))
10221009
)
10231010
or
1024-
c.isAnyElement() and
1011+
c.isAnyPositional() and
10251012
exists(CfgNodes::ProcessBlockCfgNode processBlock |
10261013
processBlock.getPipelineVariableAccess() = node1.asExpr() and
10271014
node2 = TProcessNode(processBlock)
10281015
)
10291016
or
1030-
exists(
1031-
Content::KnownElementContent ec, PipelineByPropertyNameParameter p, SsaImpl::DefinitionExt def
1032-
|
1033-
c.isSingleton(ec) and
1034-
p.getIteratorVariable() = node1.(ProcessPropertyByNameNode).getVariable() and
1035-
p.getName() = ec.getIndex().asString() and
1036-
def.getSourceVariable() = p.getIteratorVariable() and
1037-
SsaImpl::firstRead(def, node2.asExpr())
1017+
c.isAnyPositional() and
1018+
exists(CfgNodes::ProcessBlockCfgNode pb, CfgNodes::ExprNodes::VarReadAccessCfgNode va |
1019+
va = pb.getAPipelineByPropertyNameParameterAccess() and
1020+
node1.asExpr() = va and
1021+
node2 = TProcessPropertyByNameNode(va.getVariable(), false)
10381022
)
10391023
or
1040-
exists(Content::KnownElementContent ec, SsaImpl::DefinitionExt def |
1041-
c.isSingleton(ec) and
1042-
node1.(PipelineByPropertyNameParameterNode).getPropertyName() = ec.getIndex().asString() and
1043-
def = SsaImpl::getParameterDef(node1.(PipelineByPropertyNameParameterNode).getParameter()) and
1044-
SsaImpl::firstRead(def, node2.asExpr())
1024+
exists(PipelineByPropertyNameParameter p, Content::KnownElementContent ec |
1025+
c.isKnownOrUnknownElement(ec) and
1026+
ec.getIndex().asString() = p.getPropertyName() and
1027+
node1 = TProcessPropertyByNameNode(p, false) and
1028+
node2 = TProcessPropertyByNameNode(p, true)
10451029
)
10461030
or
10471031
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
@@ -1062,8 +1046,11 @@ predicate clearsContent(Node n, ContentSet c) {
10621046
n = TPreReturnNodeImpl(_, false) and
10631047
c.isAnyElement()
10641048
or
1065-
n instanceof PrePipelineArgumentNodeImpl and
1066-
c.isAnyPositional()
1049+
c.isAnyPositional() and
1050+
n instanceof PreProcessPropertyByNameNode
1051+
or
1052+
c.isAnyPositional() and
1053+
n instanceof PreProcessNode
10671054
}
10681055

10691056
/**
@@ -1078,9 +1065,6 @@ predicate expectsContent(Node n, ContentSet c) {
10781065
or
10791066
n = TImplicitWrapNode(_, false) and
10801067
c.isSingleton(any(Content::UnknownElementContent ec))
1081-
or
1082-
n instanceof PipelineArgumentNode and
1083-
c.isAnyPositional()
10841068
}
10851069

10861070
class DataFlowType extends TDataFlowType {
@@ -1210,6 +1194,22 @@ private class ReturnNodeImpl extends TReturnNodeImpl, NodeImpl {
12101194
override predicate nodeIsHidden() { any() }
12111195
}
12121196

1197+
private class PreProcessNode extends TPreProcessNode, NodeImpl {
1198+
CfgNodes::ProcessBlockCfgNode process;
1199+
1200+
PreProcessNode() { this = TPreProcessNode(process) }
1201+
1202+
override CfgScope getCfgScope() { result = process.getScope() }
1203+
1204+
override Location getLocationImpl() { result = process.getLocation() }
1205+
1206+
override string toStringImpl() { result = "pre-process node for " + process.toString() }
1207+
1208+
override predicate nodeIsHidden() { any() }
1209+
1210+
CfgNodes::ProcessBlockCfgNode getProcessBlock() { result = process }
1211+
}
1212+
12131213
private class ProcessNode extends TProcessNode, NodeImpl {
12141214
CfgNodes::ProcessBlockCfgNode process;
12151215

@@ -1230,26 +1230,53 @@ private class ProcessNode extends TProcessNode, NodeImpl {
12301230
CfgNodes::ProcessBlockCfgNode getProcessBlock() { result = process }
12311231
}
12321232

1233-
private class ProcessPropertyByNameNode extends TProcessPropertyByNameNode, NodeImpl {
1234-
private PipelineByPropertyNameIteratorVariable iter;
1233+
private class PreProcessPropertyByNameNode extends TPreProcessPropertyByNameNode, NodeImpl {
1234+
private CfgNodes::ExprNodes::VarReadAccessCfgNode va;
12351235

1236-
ProcessPropertyByNameNode() { this = TProcessPropertyByNameNode(iter) }
1236+
PreProcessPropertyByNameNode() { this = TPreProcessPropertyByNameNode(va) }
12371237

1238-
PipelineByPropertyNameIteratorVariable getVariable() { result = iter }
1238+
CfgNodes::ExprNodes::VarReadAccessCfgNode getAccess() { result = va }
12391239

1240-
override CfgScope getCfgScope() { result = iter.getDeclaringScope() }
1240+
override CfgScope getCfgScope() { result = va.getScope() }
12411241

1242-
override Location getLocationImpl() { result = iter.getLocation() }
1242+
override Location getLocationImpl() { result = this.getProcessBlock().getLocation() }
12431243

1244-
override string toStringImpl() { result = "process node for " + iter.toString() }
1244+
override string toStringImpl() { result = "pre-process node for " + va.toString() }
12451245

12461246
override predicate nodeIsHidden() { any() }
12471247

12481248
CfgNodes::ProcessBlockCfgNode getProcessBlock() {
1249-
isProcessPropertyByNameNode(iter, result.getAstNode())
1249+
result.getAPipelineByPropertyNameParameterAccess() = va
12501250
}
12511251
}
12521252

1253+
private class ProcessPropertyByNameNode extends TProcessPropertyByNameNode, NodeImpl {
1254+
private PipelineByPropertyNameParameter p;
1255+
private boolean hasRead;
1256+
1257+
ProcessPropertyByNameNode() { this = TProcessPropertyByNameNode(p, hasRead) }
1258+
1259+
PipelineByPropertyNameParameter getParameter() { result = p }
1260+
1261+
override CfgScope getCfgScope() { result = p.getDeclaringScope() }
1262+
1263+
override Location getLocationImpl() { result = this.getProcessBlock().getLocation() }
1264+
1265+
override string toStringImpl() {
1266+
hasRead = false and
1267+
result = "process node for " + p.toString()
1268+
or
1269+
hasRead = true and
1270+
result = "[has read] process node for " + p.toString()
1271+
}
1272+
1273+
override predicate nodeIsHidden() { any() }
1274+
1275+
CfgNodes::ProcessBlockCfgNode getProcessBlock() { result.getScope().getAParameter() = p }
1276+
1277+
predicate hasRead() { hasRead = true }
1278+
}
1279+
12531280
class ScriptBlockNode extends TScriptBlockNode, NodeImpl {
12541281
private ScriptBlock scriptBlock;
12551282

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

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
3131
or
3232
variableWriteActual(bb, i, v, _)
3333
or
34-
exists(ProcessBlockCfgNode processBlock |
35-
bb.getNode(i) = processBlock and
34+
exists(ProcessBlockCfgNode processBlock | bb.getNode(i) = processBlock |
3635
processBlock.getPipelineIteratorVariable() = v
36+
or
37+
processBlock.getAPipelineBypropertyNameIteratorVariable() = v
3738
)
3839
or
3940
parameterWrite(bb, i, v)
@@ -301,7 +302,8 @@ class NormalParameter extends Parameter {
301302
private newtype TParameterExt =
302303
TNormalParameter(NormalParameter p) or
303304
TThisMethodParameter(Method m) or
304-
TPipelineParameter(PipelineParameter p)
305+
TPipelineParameter(PipelineParameter p) or
306+
TPipelineByPropertyNameParameter(PipelineByPropertyNameParameter p)
305307

306308
/** A normal parameter or an implicit `this` parameter. */
307309
class ParameterExt extends TParameterExt {
@@ -311,27 +313,34 @@ class ParameterExt extends TParameterExt {
311313

312314
PipelineParameter asPipelineParameter() { this = TPipelineParameter(result) }
313315

316+
PipelineByPropertyNameParameter asPipelineByPropertyNameParameter() {
317+
this = TPipelineByPropertyNameParameter(result)
318+
}
319+
314320
predicate isInitializedBy(WriteDefinition def) {
315321
def = getParameterDef(this.asParameter())
316322
or
317323
def = getParameterDef(this.asPipelineParameter())
318324
or
325+
def = getParameterDef(this.asPipelineByPropertyNameParameter())
326+
or
319327
def.(Ssa::ThisDefinition).getSourceVariable().getDeclaringScope() = this.asThis().getBody()
320328
}
321329

322330
string toString() {
323331
result =
324332
[
325333
this.asParameter().toString(), this.asThis().toString(),
326-
this.asPipelineParameter().toString()
334+
this.asPipelineParameter().toString(), this.asPipelineByPropertyNameParameter().toString()
327335
]
328336
}
329337

330338
Location getLocation() {
331339
result =
332340
[
333341
this.asParameter().getLocation(), this.asThis().getLocation(),
334-
this.asPipelineParameter().getLocation()
342+
this.asPipelineParameter().getLocation(),
343+
this.asPipelineByPropertyNameParameter().getLocation()
335344
]
336345
}
337346
}

0 commit comments

Comments
 (0)