Skip to content

Commit f51a486

Browse files
committed
PS: CFG for try/catch and throw.
1 parent 4c59de4 commit f51a486

File tree

3 files changed

+98
-6
lines changed

3 files changed

+98
-6
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ class CatchClause extends @catch_clause, Ast {
1111

1212
TypeConstraint getACatchType() { result = this.getCatchType(_) }
1313

14+
int getNumberOfCatchTypes() { result = count(this.getCatchType(_)) }
15+
1416
predicate isCatchAll() { catch_clause(this, _, true) } // TODO: Should be equivalent to not exists(this.getACatchType())
1517

1618
TryStmt getTryStmt() { result.getACatchClause() = this }

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

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,32 @@ private newtype TCompletion =
1717
TReturnCompletion() or
1818
TBreakCompletion() or
1919
TContinueCompletion() or
20-
TRaiseCompletion() or
20+
TThrowCompletion() or
2121
TExitCompletion() or
2222
TMatchingCompletion(Boolean b)
2323

24+
private predicate commandThrows(Cmd c, boolean unconditional) {
25+
c.getNamedArgument("ErrorAction").(StringConstExpr).getValue().getValue() = "Stop" and
26+
if c.getName() = "Write-Error" then unconditional = true else unconditional = false
27+
}
28+
2429
pragma[noinline]
2530
private predicate completionIsValidForStmt(Ast n, Completion c) {
2631
n instanceof BreakStmt and
2732
c instanceof BreakCompletion
2833
or
2934
n instanceof ContinueStmt and
3035
c instanceof ContinueCompletion
36+
or
37+
n instanceof ThrowStmt and
38+
c instanceof ThrowCompletion
39+
or
40+
exists(boolean unconditional | commandThrows(n, unconditional) |
41+
c instanceof ThrowCompletion
42+
or
43+
unconditional = false and
44+
c instanceof SimpleCompletion
45+
)
3146
}
3247

3348
/** A completion of a statement or an expression. */
@@ -157,6 +172,8 @@ private predicate inBooleanContext(Ast n) {
157172
*/
158173
private predicate inMatchingContext(Ast n) {
159174
n = any(SwitchStmt switch).getAPattern()
175+
or
176+
n = any(CatchClause cc).getACatchType()
160177
}
161178

162179
/**
@@ -257,10 +274,10 @@ class ContinueCompletion extends Completion, TContinueCompletion {
257274
* A completion that represents evaluation of a statement or an
258275
* expression resulting in a thrown exception.
259276
*/
260-
class RaiseCompletion extends Completion, TRaiseCompletion {
261-
override RaiseSuccessor getAMatchingSuccessorType() { any() }
277+
class ThrowCompletion extends Completion, TThrowCompletion {
278+
override ThrowSuccessor getAMatchingSuccessorType() { any() }
262279

263-
override string toString() { result = "raise" }
280+
override string toString() { result = "throw" }
264281
}
265282

266283
/**

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

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ private module CfgInput implements CfgShared::InputSig<Location> {
5252
}
5353

5454
predicate isAbnormalExitType(SuccessorType t) {
55-
t instanceof Cfg::SuccessorTypes::RaiseSuccessor or
55+
t instanceof Cfg::SuccessorTypes::ThrowSuccessor or
5656
t instanceof Cfg::SuccessorTypes::ExitSuccessor
5757
}
5858
}
@@ -543,6 +543,79 @@ module Trees {
543543
override AstNode getChildNode(int i) { result = super.getElement(i) }
544544
}
545545

546+
class CatchClauseTree extends PreOrderTree instanceof CatchClause {
547+
final override predicate propagatesAbnormal(Ast child) { none() }
548+
549+
final override predicate last(AstNode last, Completion c) {
550+
last(super.getBody(), last, c)
551+
or
552+
// The last catch type failed to matchs
553+
last(super.getCatchType(super.getNumberOfCatchTypes() - 1), last, c) and
554+
c.(MatchCompletion).isNonMatch()
555+
}
556+
557+
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
558+
// Preorder: Flow from the catch clause to the first catch type to test,
559+
// or to the body if this is a catch all
560+
pred = this and
561+
completionIsSimple(c) and
562+
(
563+
first(super.getCatchType(0), succ)
564+
or
565+
super.isCatchAll() and
566+
first(super.getBody(), succ)
567+
)
568+
or
569+
// Flow from a catch type to the next catch type when it fails to match
570+
exists(int i, boolean match |
571+
last(super.getCatchType(i), pred, c) and
572+
match = c.(MatchCompletion).getValue()
573+
|
574+
match = true and
575+
first(super.getBody(), succ)
576+
or
577+
match = false and
578+
first(super.getCatchType(i + 1), succ)
579+
)
580+
}
581+
}
582+
583+
class TryStmtBlock extends PreOrderTree instanceof TryStmt {
584+
final override predicate propagatesAbnormal(AstNode child) { child = super.getFinally() }
585+
586+
final override predicate last(AstNode last, Completion c) {
587+
last(super.getFinally(), last, c)
588+
or
589+
not super.hasFinally() and
590+
(
591+
// Body exits without an exception
592+
last(super.getBody(), last, c) and
593+
completionIsNormal(c)
594+
or
595+
// In case there's an exception we exit by evaluating one of the catch clauses
596+
// Note that there will always be at least one catch clause if there is no `try`.
597+
last(super.getACatchClause(), last, c)
598+
)
599+
}
600+
601+
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
602+
// Preorder: The try/catch statment flows to the body
603+
pred = this and
604+
first(super.getBody(), succ) and
605+
completionIsSimple(c)
606+
or
607+
// Flow from the body to the finally when the body didn't throw
608+
last(super.getBody(), pred, c) and
609+
if c instanceof ThrowCompletion
610+
then first(super.getCatchClause(0), succ)
611+
else first(super.getFinally(), succ)
612+
}
613+
}
614+
615+
class ThrowStmtTree extends StandardPreOrderTree instanceof ThrowStmt {
616+
override AstNode getChildNode(int i) { i = 0 and result = super.getPipeline() }
617+
}
618+
546619
class ConstExprTree extends LeafTree instanceof ConstExpr { }
547620

548621
class CmdExprTree extends StandardPreOrderTree instanceof CmdExpr {
@@ -577,7 +650,7 @@ private module Cached {
577650
TReturnSuccessor() or
578651
TBreakSuccessor() or
579652
TContinueSuccessor() or
580-
TRaiseSuccessor() or
653+
TThrowSuccessor() or
581654
TExitSuccessor() or
582655
TMatchingSuccessor(Boolean b)
583656
}

0 commit comments

Comments
 (0)