Skip to content

Commit 5126963

Browse files
committed
PS: Pipeline flow.
1 parent cc995b1 commit 5126963

File tree

3 files changed

+116
-11
lines changed

3 files changed

+116
-11
lines changed

powershell/ql/lib/semmle/code/powershell/Variable.qll

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ private newtype TVariable =
160160
not name = "this" and // This is modeled as a parameter
161161
exists(VarAccess va | va.getUserPath() = name and scope = va.getEnclosingScope())
162162
} or
163-
TParameter(ParameterImpl p)
163+
TParameter(ParameterImpl p) or
164+
TPipelineIteratorVariable(ProcessBlock pb)
164165

165166
private class AbstractVariable extends TVariable {
166167
abstract Location getLocation();
@@ -272,3 +273,23 @@ class Parameter extends AbstractLocalScopeVariable, TParameter {
272273
class PipelineParameter extends Parameter {
273274
PipelineParameter() { this.isPipeline() }
274275
}
276+
277+
/**
278+
* The variable that represents the value of a pipeline during a process block.
279+
*
280+
* That is, it is _not_ the pipeline variable, but the value that is obtained by reading
281+
* from the pipeline.
282+
*/
283+
class PipelineIteratorVariable extends AbstractLocalScopeVariable, TPipelineIteratorVariable {
284+
private ProcessBlock pb;
285+
286+
PipelineIteratorVariable() { this = TPipelineIteratorVariable(pb) }
287+
288+
override Location getLocation() { result = pb.getLocation() }
289+
290+
override string getName() { result = "pipeline iterator for " + pb.toString() }
291+
292+
final override Scope getDeclaringScope() { result = pb.getEnclosingScope() }
293+
294+
ProcessBlock getProcessBlock() { result = pb }
295+
}

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

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ module SsaFlow {
7676
or
7777
result.(Impl::ExprNode).getExpr() = n.asStmt()
7878
or
79+
result.(Impl::ExprNode).getExpr() = n.(ProcessNode).getProcessBlock()
80+
or
7981
result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr()
8082
or
8183
n = toParameterNode(result.(Impl::ParameterNode).getParameter())
@@ -148,7 +150,8 @@ private module Cached {
148150
} or
149151
TPreReturnNodeImpl(CfgNodes::AstCfgNode n, Boolean isArray) { isReturned(n) } or
150152
TImplicitWrapNode(CfgNodes::AstCfgNode n, Boolean shouldWrap) { isReturned(n) } or
151-
TReturnNodeImpl(CfgScope scope)
153+
TReturnNodeImpl(CfgScope scope) or
154+
TProcessNode(ProcessBlock process)
152155

153156
cached
154157
Location getLocation(NodeImpl n) { result = n.getLocationImpl() }
@@ -462,6 +465,9 @@ private module ParameterNodes {
462465
p.getName() = ns.getAName()
463466
)
464467
)
468+
or
469+
parameter.isPipeline() and
470+
pos.isPipeline()
465471
)
466472
}
467473

@@ -508,6 +514,26 @@ module ArgumentNodes {
508514
)
509515
}
510516
}
517+
518+
private predicate isPipelineInput(
519+
CfgNodes::StmtNodes::CmdBaseCfgNode input, CfgNodes::StmtNodes::CmdBaseCfgNode consumer
520+
) {
521+
exists(CfgNodes::StmtNodes::PipelineCfgNode pipeline, int i |
522+
input = pipeline.getComponent(i) and
523+
consumer = pipeline.getComponent(i + 1)
524+
)
525+
}
526+
527+
class PipelineArgumentNode extends ArgumentNode, StmtNode {
528+
CfgNodes::StmtNodes::CmdBaseCfgNode consumer;
529+
530+
PipelineArgumentNode() { isPipelineInput(this.getStmtNode(), consumer) }
531+
532+
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
533+
call.asCall() = consumer and
534+
pos.isPipeline()
535+
}
536+
}
511537
}
512538

513539
import ArgumentNodes
@@ -706,6 +732,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
706732
node2 = TImplicitWrapNode(cfgNode, true) and
707733
c.isSingleton(any(Content::KnownElementContent ec))
708734
)
735+
or
736+
c.isAnyElement() and
737+
exists(SsaImpl::DefinitionExt def |
738+
node1.(ProcessNode).getIteratorVariable() = def.getSourceVariable() and
739+
SsaImpl::firstRead(def, node2.asExpr())
740+
)
709741
}
710742

711743
/**
@@ -731,6 +763,9 @@ predicate expectsContent(Node n, ContentSet c) {
731763
or
732764
n = TImplicitWrapNode(_, false) and
733765
c.isSingleton(any(Content::UnknownElementContent ec))
766+
or
767+
n instanceof ProcessNode and
768+
c.isAnyElement()
734769
}
735770

736771
class DataFlowType extends TDataFlowType {
@@ -850,6 +885,24 @@ private class ReturnNodeImpl extends TReturnNodeImpl, NodeImpl {
850885
override predicate nodeIsHidden() { any() }
851886
}
852887

888+
private class ProcessNode extends TProcessNode, NodeImpl {
889+
ProcessBlock process;
890+
891+
ProcessNode() { this = TProcessNode(process) }
892+
893+
override CfgScope getCfgScope() { result = process.getEnclosingScope() }
894+
895+
override Location getLocationImpl() { result = process.getLocation() }
896+
897+
override string toStringImpl() { result = process.toString() }
898+
899+
override predicate nodeIsHidden() { any() }
900+
901+
PipelineIteratorVariable getIteratorVariable() { result.getProcessBlock() = process }
902+
903+
CfgNodes::ProcessBlockCfgNode getProcessBlock() { result.getAstNode() = process }
904+
}
905+
853906
/** A node that performs a type cast. */
854907
class CastNode extends Node {
855908
CastNode() { none() }

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

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,18 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
3434
parameterWrite(bb, i, v)
3535
or
3636
variableWriteActual(bb, i, v, _)
37+
or
38+
pipelineIteratorWrite(bb, i, v)
3739
) and
3840
certain = true
3941
}
4042

4143
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
42-
variableReadActual(bb, i, v) and
44+
(
45+
variableReadActual(bb, i, v)
46+
or
47+
pipelineRead(bb, i, v)
48+
) and
4349
certain = true
4450
}
4551
}
@@ -62,6 +68,13 @@ predicate uninitializedWrite(Cfg::EntryBasicBlock bb, int i, LocalVariable v) {
6268
i = -1
6369
}
6470

71+
predicate pipelineIteratorWrite(Cfg::BasicBlock bb, int i, PipelineIteratorVariable v) {
72+
exists(ProcessBlockCfgNode process |
73+
process = bb.getNode(i) and
74+
v.getProcessBlock() = process.getAstNode()
75+
)
76+
}
77+
6578
/**
6679
* Gets index of `p` in a version of the enclosing function where the parameter
6780
* list is reversed.
@@ -101,11 +114,28 @@ predicate parameterWrite(Cfg::EntryBasicBlock bb, int i, Parameter p) {
101114
)
102115
}
103116

117+
private PipelineIteratorVariable getPipelineIterator(PipelineParameter pipelineParam) {
118+
result.getProcessBlock().getScriptBlock() = pipelineParam.getDeclaringScope()
119+
}
120+
121+
private predicate isPipelineIteratorVarAccess(VarAccessCfgNode va) {
122+
va.getVariable() instanceof PipelineParameter and
123+
va.getAstNode().getParent*() instanceof ProcessBlock
124+
}
125+
104126
/** Holds if `v` is read at index `i` in basic block `bb`. */
105-
private predicate variableReadActual(Cfg::BasicBlock bb, int i, LocalScopeVariable v) {
106-
exists(VarReadAccess read |
107-
read.getVariable() = v and
108-
read = bb.getNode(i).getAstNode()
127+
private predicate variableReadActual(Cfg::BasicBlock bb, int i, SsaInput::SourceVariable v) {
128+
exists(VarReadAccessCfgNode read, SsaInput::SourceVariable w |
129+
read.getVariable() = w and read = bb.getNode(i)
130+
|
131+
if isPipelineIteratorVarAccess(read) then v = getPipelineIterator(w) else v = w
132+
)
133+
}
134+
135+
private predicate pipelineRead(Cfg::BasicBlock bb, int i, SsaInput::SourceVariable v) {
136+
exists(ProcessBlockCfgNode process |
137+
process = bb.getNode(i) and
138+
v = process.getPipelineParameter()
109139
)
110140
}
111141

@@ -195,11 +225,12 @@ private module Cached {
195225
*/
196226
cached
197227
predicate variableWriteActual(
198-
Cfg::BasicBlock bb, int i, LocalScopeVariable v, VarWriteAccessCfgNode write
228+
Cfg::BasicBlock bb, int i, SsaInput::SourceVariable v, VarWriteAccessCfgNode write
199229
) {
200-
exists(Cfg::CfgNode n |
201-
write.getVariable() = v and
202-
n = bb.getNode(i)
230+
exists(Cfg::CfgNode n, SsaInput::SourceVariable w |
231+
write.getVariable() = w and
232+
n = bb.getNode(i) and
233+
if isPipelineIteratorVarAccess(write) then v = getPipelineIterator(w) else v = w
203234
|
204235
write.isExplicitWrite(n)
205236
or

0 commit comments

Comments
 (0)