Skip to content

Commit 7c59a74

Browse files
authored
Merge pull request #184 from microsoft/fix-parameter-by-name-flow-3
PS: Fix the last remaining missing flows after AST prettification
2 parents b452339 + 38536a9 commit 7c59a74

File tree

19 files changed

+645
-306
lines changed

19 files changed

+645
-306
lines changed

powershell/ql/lib/semmle/code/powershell/ast/internal/ChildIndex.qll

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ newtype ChildIndex =
3333
} or
3434
ThisVar() or
3535
PipelineParamVar() or
36-
// PipelineByPropertNameVar(Raw::PipelineByPropertyNameParameter p) or
36+
PipelineByPropertyNameVar(Raw::PipelineByPropertyNameParameter p) or
3737
PipelineIteratorVar() or
3838
PipelineByPropertyNameIteratorVar(Raw::PipelineByPropertyNameParameter p) or
3939
RealVar(string name) { name = variableNameInScope(_, _) } or
40-
ProcessBlockPipelineVarReadAccess()
40+
ProcessBlockPipelineVarReadAccess() or
41+
ProcessBlockPipelineByPropertyNameVarReadAccess(string name) {
42+
name = any(Raw::PipelineByPropertyNameParameter p).getName()
43+
}
4144

4245
int synthPipelineParameterChildIndex(Raw::ScriptBlock sb) {
4346
// If there is a parameter block, but no pipeline parameter
@@ -340,3 +343,7 @@ ChildIndex whileStmtCond() { result = RawChildIndex(Raw::WhileStmtCond()) }
340343
ChildIndex whileStmtBody() { result = RawChildIndex(Raw::WhileStmtBody()) }
341344

342345
ChildIndex processBlockPipelineVarReadAccess() { result = ProcessBlockPipelineVarReadAccess() }
346+
347+
ChildIndex processBlockPipelineByPropertyNameVarReadAccess(string name) {
348+
result = ProcessBlockPipelineByPropertyNameVarReadAccess(name)
349+
}

powershell/ql/lib/semmle/code/powershell/ast/internal/NamedBlock.qll

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,20 @@ class ProcessBlock extends NamedBlock {
5858
synthChild(getRawAst(this), processBlockPipelineVarReadAccess(), result)
5959
}
6060

61+
PipelineByPropertyNameParameter getPipelineByPropertyNameParameter(string name) {
62+
result = scriptBlock.getAParameter() and
63+
result.getPropertyName() = name
64+
}
65+
6166
PipelineByPropertyNameParameter getAPipelineByPropertyNameParameter() {
62-
result = scriptBlock.getEnclosingFunction().getAParameter()
67+
result = this.getPipelineByPropertyNameParameter(_)
68+
}
69+
70+
VarReadAccess getPipelineByPropertyNameParameterAccess(string name) {
71+
synthChild(getRawAst(this), processBlockPipelineByPropertyNameVarReadAccess(name), result)
72+
}
73+
74+
VarReadAccess getAPipelineByPropertyNameParameterAccess() {
75+
result = this.getPipelineByPropertyNameParameterAccess(_)
6376
}
6477
}

powershell/ql/lib/semmle/code/powershell/ast/internal/Synthesis.qll

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ private module LiteralSynth {
615615

616616
/**
617617
* Holds if `va` is an access to the automatic variable named `name`.
618-
*
618+
*
619619
* Unlike `Raw::isAutomaticVariableAccess`, this predicate also checks for
620620
* shadowing.
621621
*/
@@ -770,6 +770,13 @@ private module IteratorAccessSynth {
770770
or
771771
va.getUserPath().toLowerCase() =
772772
pb.getScriptBlock().getParamBlock().getPipelineParameter().getName().toLowerCase()
773+
or
774+
va.getUserPath().toLowerCase() =
775+
pb.getScriptBlock()
776+
.getParamBlock()
777+
.getAPipelineByPropertyNameParameter()
778+
.getName()
779+
.toLowerCase()
773780
)
774781
}
775782

@@ -867,12 +874,19 @@ private module PipelineAccess {
867874
pipelineVar = TVariableSynth(pb.getScriptBlock(), PipelineParamVar()) and
868875
child = SynthChild(VarAccessSynthKind(pipelineVar))
869876
)
877+
or
878+
exists(PipelineByPropertyNameVariable pipelineVar, Raw::PipelineByPropertyNameParameter p |
879+
i = processBlockPipelineByPropertyNameVarReadAccess(p.getName()) and
880+
getResultAst(p) = pipelineVar and
881+
child = SynthChild(VarAccessSynthKind(pipelineVar))
882+
)
870883
)
871884
}
872885

873886
final override Location getLocation(Ast n) {
874887
exists(ProcessBlock pb |
875-
pb.getPipelineParameterAccess() = n and
888+
pb.getPipelineParameterAccess() = n or pb.getAPipelineByPropertyNameParameterAccess() = n
889+
|
876890
result = pb.getLocation()
877891
)
878892
}
@@ -881,6 +895,23 @@ private module PipelineAccess {
881895
exists(ProcessBlock pb |
882896
pb.getPipelineParameterAccess() = va and
883897
v = pb.getPipelineParameter()
898+
or
899+
exists(string name |
900+
pb.getPipelineByPropertyNameParameterAccess(name) = va and
901+
v = pb.getPipelineByPropertyNameParameter(name)
902+
)
903+
)
904+
}
905+
}
906+
}
907+
908+
private module ImplicitAssignmentInForEach {
909+
private class ForEachAssignment extends Synthesis {
910+
override predicate implicitAssignment(Raw::Ast dest, string name) {
911+
exists(Raw::ForEachStmt forEach, Raw::VarAccess va |
912+
va = forEach.getVarAccess() and
913+
va = dest and
914+
va.getUserPath() = name
884915
)
885916
}
886917
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ module Private {
4545

4646
class ParameterImpl extends VariableSynth {
4747
ParameterImpl() {
48-
i instanceof FunParam or i instanceof PipelineParamVar or i instanceof ThisVar
48+
i instanceof FunParam or
49+
i instanceof PipelineParamVar or
50+
i instanceof ThisVar
4951
}
5052
}
5153

@@ -59,6 +61,14 @@ module Private {
5961
ScriptBlock getScriptBlock() { this = TVariableSynth(getRawAst(result), _) }
6062
}
6163

64+
class PipelineByPropertyNameVariableImpl extends ParameterImpl {
65+
PipelineByPropertyNameVariableImpl() {
66+
getRawAst(this) instanceof Raw::PipelineByPropertyNameParameter
67+
}
68+
69+
ScriptBlock getScriptBlock() { this = TVariableSynth(getRawAst(result), _) }
70+
}
71+
6272
class PipelineIteratorVariableImpl extends VariableSynth {
6373
override PipelineIteratorVar i;
6474

@@ -171,6 +181,11 @@ module Public {
171181
ScriptBlock getScriptBlock() { result = super.getScriptBlock() }
172182
}
173183

184+
class PipelineByPropertyNameVariable extends Variable instanceof PipelineByPropertyNameVariableImpl
185+
{
186+
ScriptBlock getScriptBlock() { result = super.getScriptBlock() }
187+
}
188+
174189
class PipelineIteratorVariable extends Variable instanceof PipelineIteratorVariableImpl {
175190
ProcessBlock getProcessBlock() { result = super.getProcessBlock() }
176191
}

powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ private class ProcessBlockChildMapping extends NamedBlockChildMapping, ProcessBl
277277
super.relevantChild(child)
278278
or
279279
child = super.getPipelineParameterAccess()
280+
or
281+
child = super.getAPipelineByPropertyNameParameterAccess()
280282
}
281283
}
282284

@@ -300,6 +302,23 @@ class ProcessBlockCfgNode extends NamedBlockCfgNode {
300302
PipelineIteratorVariable getPipelineIteratorVariable() {
301303
result.getProcessBlock().getScriptBlock() = this.getScriptBlock().getAstNode()
302304
}
305+
306+
PipelineByPropertyNameIteratorVariable getPipelineBypropertyNameIteratorVariable(string name) {
307+
result.getPropertyName() = name and
308+
result.getProcessBlock().getScriptBlock() = this.getScriptBlock().getAstNode()
309+
}
310+
311+
PipelineByPropertyNameIteratorVariable getAPipelineBypropertyNameIteratorVariable() {
312+
result = this.getPipelineBypropertyNameIteratorVariable(_)
313+
}
314+
315+
ExprNodes::VarReadAccessCfgNode getPipelineByPropertyNameParameterAccess(string name) {
316+
block.hasCfgChild(block.getPipelineByPropertyNameParameterAccess(name), this, result)
317+
}
318+
319+
ExprNodes::VarReadAccessCfgNode getAPipelineByPropertyNameParameterAccess() {
320+
result = this.getPipelineByPropertyNameParameterAccess(_)
321+
}
303322
}
304323

305324
private class CatchClauseChildMapping extends NonExprChildMapping, CatchClause {

powershell/ql/lib/semmle/code/powershell/controlflow/internal/Completion.qll

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,11 @@ private predicate inMatchingContext(Ast n) {
188188
* Holds if a normal completion of `cfe` must be an emptiness completion. Thats is,
189189
* whether `cfe` determines whether to execute the body of a `foreach` statement.
190190
*/
191-
private predicate mustHaveEmptinessCompletion(Ast n) { n instanceof ForEachStmt }
191+
private predicate mustHaveEmptinessCompletion(Ast n) {
192+
n instanceof ForEachStmt
193+
or
194+
any(CfgImpl::Trees::ProcessBlockTree pbtree).lastEmptinessCheck(n)
195+
}
192196

193197
/**
194198
* A completion that represents normal evaluation of a statement or an

powershell/ql/lib/semmle/code/powershell/controlflow/internal/ControlFlowGraphImpl.qll

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,7 @@ module Trees {
221221
or
222222
last(super.getProcessBlock(), pred, c) and
223223
completionIsNormal(c) and
224-
(
225-
// If we process multiple items we will loop back to the process block
226-
first(super.getProcessBlock(), succ)
227-
or
228-
// Once we're done process all items we will go to the end block
229-
first(super.getEndBlock(), succ)
230-
)
224+
first(super.getEndBlock(), succ)
231225
}
232226

233227
final override predicate propagatesAbnormal(AstNode child) {
@@ -292,25 +286,17 @@ module Trees {
292286
final override predicate succEntry(Ast n, Completion c) { n = this and completionIsSimple(c) }
293287
}
294288

295-
abstract class NamedBlockTreeBase extends ControlFlowTree instanceof NamedBlock {
296-
final override predicate last(Ast last, Completion c) {
289+
abstract class NamedBlockTreeBase extends PreOrderTree instanceof NamedBlock {
290+
override predicate last(Ast last, Completion c) {
297291
exists(int i | last(super.getStmt(i), last, c) |
298292
completionIsNormal(c) and
299293
not exists(super.getStmt(i + 1))
300294
or
301295
not completionIsNormal(c)
302296
)
303-
or
304-
not exists(super.getAStmt()) and
305-
completionIsSimple(c) and
306-
last = this
307297
}
308298

309299
override predicate succ(Ast pred, Ast succ, Completion c) {
310-
pred = this and
311-
completionIsSimple(c) and
312-
first(super.getStmt(0), succ)
313-
or
314300
exists(int i |
315301
last(super.getStmt(i), pred, c) and
316302
completionIsNormal(c) and
@@ -324,20 +310,85 @@ module Trees {
324310
class NamedBlockTree extends NamedBlockTreeBase instanceof NamedBlock {
325311
NamedBlockTree() { not this instanceof ProcessBlock }
326312

327-
final override predicate first(Ast first) { first = this }
313+
final override predicate last(Ast last, Completion c) {
314+
super.last(last, c)
315+
or
316+
not exists(super.getAStmt()) and
317+
completionIsSimple(c) and
318+
last = this
319+
}
320+
321+
final override predicate succ(Ast pred, Ast succ, Completion c) {
322+
pred = this and
323+
completionIsSimple(c) and
324+
first(super.getStmt(0), succ)
325+
or
326+
super.succ(pred, succ, c)
327+
}
328328

329329
final override predicate propagatesAbnormal(Ast child) { super.propagatesAbnormal(child) }
330330
}
331331

332+
private VarAccess getRankedPipelineByPropertyNameVariable(ProcessBlock pb, int i) {
333+
result =
334+
rank[i + 1](string name | | pb.getPipelineByPropertyNameParameterAccess(name) order by name)
335+
}
336+
332337
class ProcessBlockTree extends NamedBlockTreeBase instanceof ProcessBlock {
333-
final override predicate first(Ast first) { first = super.getPipelineParameterAccess() }
338+
predicate lastEmptinessCheck(AstNode last) {
339+
last = super.getPipelineParameterAccess() and
340+
not exists(super.getAPipelineByPropertyNameParameterAccess())
341+
or
342+
exists(int i |
343+
last = getRankedPipelineByPropertyNameVariable(this, i) and
344+
not exists(getRankedPipelineByPropertyNameVariable(this, i + 1))
345+
)
346+
}
347+
348+
private predicate succEmptinessCheck(AstNode pred, AstNode succ, Completion c) {
349+
last(super.getPipelineParameterAccess(), pred, c) and
350+
first(getRankedPipelineByPropertyNameVariable(this, 0), succ)
351+
or
352+
exists(int i |
353+
last(getRankedPipelineByPropertyNameVariable(this, i), pred, c) and
354+
first(getRankedPipelineByPropertyNameVariable(this, i + 1), succ)
355+
)
356+
}
357+
358+
private predicate firstEmptinessCheck(AstNode first) {
359+
first(super.getPipelineParameterAccess(), first)
360+
}
361+
362+
final override predicate last(AstNode last, Completion c) {
363+
// Emptiness test exits with no more elements
364+
this.lastEmptinessCheck(last) and
365+
c.(EmptinessCompletion).isEmpty()
366+
or
367+
super.last(last, c)
368+
}
334369

335370
final override predicate succ(Ast pred, Ast succ, Completion c) {
336-
this.first(pred) and
337-
completionIsSimple(c) and
338-
succ = this
371+
// Evaluate the pipeline access
372+
pred = this and
373+
this.firstEmptinessCheck(succ) and
374+
completionIsSimple(c)
375+
or
376+
this.succEmptinessCheck(pred, succ, c)
377+
or
378+
this.lastEmptinessCheck(pred) and
379+
c = any(EmptinessCompletion ec | not ec.isEmpty()) and
380+
first(super.getStmt(0), succ)
339381
or
340382
super.succ(pred, succ, c)
383+
or
384+
// Body to emptiness test
385+
exists(Ast last0 |
386+
super.last(last0, _) and
387+
last(last0, pred, c) and
388+
// TODO: I don't think this correctly models the semantics inside process blocks
389+
c.continuesLoop()
390+
) and
391+
this.firstEmptinessCheck(succ)
341392
}
342393

343394
final override predicate propagatesAbnormal(Ast child) { super.propagatesAbnormal(child) }

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ module Ssa {
158158
not exists(this.getSplitString()) and
159159
prefix = ""
160160
|
161-
result = prefix + "phi"
161+
result = prefix + "phi (" + this.getSourceVariable() + ")"
162162
)
163163
}
164164

0 commit comments

Comments
 (0)