Skip to content

Commit 7eb98c1

Browse files
committed
PS: Add an emptiness successor to get rid of CFG inconsistencies.
1 parent a4e1860 commit 7eb98c1

File tree

3 files changed

+41
-4
lines changed

3 files changed

+41
-4
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ module SuccessorTypes {
108108
class ExitSuccessor extends SuccessorType, CfgImpl::TExitSuccessor {
109109
final override string toString() { result = "exit" }
110110
}
111+
112+
class EmptinessSuccessor extends ConditionalSuccessor, CfgImpl::TEmptinessSuccessor {
113+
EmptinessSuccessor() { this = CfgImpl::TEmptinessSuccessor(value) }
114+
115+
/** Holds if this is an empty successor. */
116+
predicate isEmpty() { value = true }
117+
118+
override string toString() { if this.isEmpty() then result = "empty" else result = "non-empty" }
119+
}
111120
}
112121

113122
class Split = Splitting::Split;

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ private newtype TCompletion =
1919
TContinueCompletion() or
2020
TThrowCompletion() or
2121
TExitCompletion() or
22-
TMatchingCompletion(Boolean b)
22+
TMatchingCompletion(Boolean b) or
23+
TEmptinessCompletion(Boolean isEmpty)
2324

2425
private predicate commandThrows(Cmd c, boolean unconditional) {
2526
c.getNamedArgument("ErrorAction").(StringConstExpr).getValue().getValue() = "Stop" and
@@ -68,6 +69,9 @@ abstract class Completion extends TCompletion {
6869
not isMatchingConstant(n, _) and
6970
this = TMatchingCompletion(_)
7071
)
72+
or
73+
mustHaveEmptinessCompletion(n) and
74+
this = TEmptinessCompletion(_)
7175
}
7276

7377
private predicate isValidForSpecific(Ast n) { this.isValidForSpecific0(n) }
@@ -183,6 +187,12 @@ private predicate inMatchingContext(Ast n) {
183187
n = any(CatchClause cc).getACatchType()
184188
}
185189

190+
/**
191+
* Holds if a normal completion of `cfe` must be an emptiness completion. Thats is,
192+
* whether `cfe` determines whether to execute the body of a `foreach` statement.
193+
*/
194+
private predicate mustHaveEmptinessCompletion(Ast n) { n instanceof ForEachStmt }
195+
186196
/**
187197
* A completion that represents normal evaluation of a statement or an
188198
* expression.
@@ -296,3 +306,20 @@ class ExitCompletion extends Completion, TExitCompletion {
296306

297307
override string toString() { result = "exit" }
298308
}
309+
310+
/**
311+
* A completion that represents evaluation of an emptiness test, for example
312+
* a test in a `foreach` statement.
313+
*/
314+
class EmptinessCompletion extends ConditionalCompletion, TEmptinessCompletion {
315+
EmptinessCompletion() { this = TEmptinessCompletion(value) }
316+
317+
/** Holds if the emptiness test evaluates to `true`. */
318+
predicate isEmpty() { value = true }
319+
320+
EmptinessCompletion getDual() { result = TEmptinessCompletion(value.booleanNot()) }
321+
322+
override EmptinessSuccessor getAMatchingSuccessorType() { result.getValue() = value }
323+
324+
override string toString() { if this.isEmpty() then result = "empty" else result = "non-empty" }
325+
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ module Trees {
404404
final override predicate last(AstNode last, Completion c) {
405405
// Emptiness test exits with no more elements
406406
last = this and
407-
completionIsSimple(c)
407+
c.(EmptinessCompletion).isEmpty()
408408
or
409409
super.last(last, c)
410410
}
@@ -418,7 +418,7 @@ module Trees {
418418
// Emptiness test to variable declaration
419419
pred = this and
420420
first(super.getVarAccess(), succ) and
421-
completionIsSimple(c)
421+
c = any(EmptinessCompletion ec | not ec.isEmpty())
422422
or
423423
// Variable declaration to body
424424
last(super.getVarAccess(), pred, c) and
@@ -779,7 +779,8 @@ private module Cached {
779779
TContinueSuccessor() or
780780
TThrowSuccessor() or
781781
TExitSuccessor() or
782-
TMatchingSuccessor(Boolean b)
782+
TMatchingSuccessor(Boolean b) or
783+
TEmptinessSuccessor(Boolean b)
783784
}
784785

785786
import Cached

0 commit comments

Comments
 (0)