diff --git a/unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll index 2c60994c4cd5..019722484d1a 100644 --- a/unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll @@ -7,6 +7,7 @@ module; private import unified private import codeql.controlflow.ControlFlowGraph private import codeql.controlflow.SuccessorType +private import ControlFlowGraphPlugin private module Cfg0 = Make0; @@ -221,6 +222,8 @@ private module Ast implements AstSig { } } +private predicate mayThrow(AstNode ast) { any(ControlFlowGraphPlugin p).mayThrow(ast) } + private module Input implements InputSig1, InputSig2 { private import codeql.util.Void @@ -256,7 +259,10 @@ private module Input implements InputSig1, InputSig2 { predicate beginAbruptCompletion( AstNode ast, PreControlFlowNode n, AbruptCompletion c, boolean always ) { - none() + mayThrow(ast) and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false } predicate endAbruptCompletion(AstNode ast, PreControlFlowNode n, AbruptCompletion c) { none() } diff --git a/unified/ql/lib/codeql/unified/internal/ControlFlowGraphPlugin.qll b/unified/ql/lib/codeql/unified/internal/ControlFlowGraphPlugin.qll new file mode 100644 index 000000000000..ae1f5fa25ce5 --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/ControlFlowGraphPlugin.qll @@ -0,0 +1,10 @@ +private import unified +private import codeql.util.Unit + +private module Plugins { + private import ControlFlowGraphPluginSwift +} + +class ControlFlowGraphPlugin extends Unit { + predicate mayThrow(AstNode ast) { none() } +} diff --git a/unified/ql/lib/codeql/unified/internal/ControlFlowGraphPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/ControlFlowGraphPluginSwift.qll new file mode 100644 index 000000000000..fac0d2bacf1c --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/ControlFlowGraphPluginSwift.qll @@ -0,0 +1,16 @@ +private import unified +private import ControlFlowGraphPlugin + +private predicate inTry(AstNode ast) { + ast.(UnaryExpr).getOperator().(Token).getValue() = "try" + or + exists(AstNode parent | + parent = ast.getParent() and + inTry(ast.getParent()) and + not parent instanceof Callable + ) +} + +private class ControlFlowGraphPluginSwift extends ControlFlowGraphPlugin { + override predicate mayThrow(AstNode ast) { ast instanceof CallExpr and inTry(ast) } +} diff --git a/unified/ql/test/library-tests/controlflow/basicblock-slices.expected b/unified/ql/test/library-tests/controlflow/basicblock-slices.expected index 4cdaa2b10fde..c9d14ccd1af7 100644 --- a/unified/ql/test/library-tests/controlflow/basicblock-slices.expected +++ b/unified/ql/test/library-tests/controlflow/basicblock-slices.expected @@ -19,10 +19,22 @@ | 28 | cfg.swift:28:1:45:1 | FunctionDeclaration | 'FunctionDeclaration' | | 28 | cfg.swift:28:15:28:15 | x | 'x -^ Block' | | 29 | cfg.swift:29:3:43:3 | TryExpr | 'TryExpr -V Block' | -| 30 | cfg.swift:30:9:30:18 | mightThrow | 'mightThrow -> Argument -V 0 -^ CallExpr -^ try -^ UnaryExpr' | +| 30 | cfg.swift:30:5:30:24 | try | 'try -^ UnaryExpr' | +| 30 | cfg.swift:30:9:30:18 | mightThrow | 'mightThrow -> Argument -V 0 -^ CallExpr' | | 31 | cfg.swift:31:5:31:9 | print | 'print -> Argument -V "Did not throw." -^ CallExpr' | | 32 | cfg.swift:32:10:32:19 | mightThrow | 'mightThrow -> Argument -V 0 -^ CallExpr -^ try! -^ UnaryExpr' | | 33 | cfg.swift:33:5:33:9 | print | 'print -> Argument -V "Still did not throw." -^ CallExpr' | +| 35 | cfg.swift:35:5:37:3 | CatchClause | 'CatchClause -V MyError -^ MemberAccessExpr -> isZero -> Argument -V x -^ CallExpr -? MyError -^ MemberAccessExpr -^ ConditionalPattern -^ OrPattern' | +| 35 | cfg.swift:35:62:37:3 | Block | 'Block' | +| 36 | cfg.swift:36:12:36:12 | 0 | '0 -^ ReturnExpr' | +| 37 | cfg.swift:37:5:39:3 | CatchClause | 'CatchClause -V MyError -^ MemberAccessExpr -> Argument -V withParam -^ ExprPattern -^ CallExpr' | +| 37 | cfg.swift:37:41:39:3 | Block | 'Block' | +| 38 | cfg.swift:38:12:38:20 | withParam | 'withParam -^ ReturnExpr' | +| 39 | cfg.swift:39:5:41:3 | CatchClause | 'CatchClause -V ' | +| 39 | cfg.swift:39:22:41:3 | Block | 'Block' | +| 40 | cfg.swift:40:5:40:9 | print | 'print -> Argument -V "MyError" -^ CallExpr' | +| 41 | cfg.swift:41:5:43:3 | CatchClause | 'CatchClause -V Block' | +| 42 | cfg.swift:42:5:42:9 | print | 'print -> Argument -V Unknown error -> interpolation -V Argument -V error -^ CallExpr -> -^ StringInterpolationExpr -^ CallExpr' | | 44 | cfg.swift:44:10:44:10 | 0 | '0 -^ ReturnExpr' | | 47 | cfg.swift:47:1:51:1 | FunctionDeclaration | 'FunctionDeclaration' | | 47 | cfg.swift:47:21:47:21 | s | 's -^ Block' | @@ -311,7 +323,8 @@ | 396 | cfg.swift:396:1:404:1 | FunctionDeclaration | 'FunctionDeclaration' | | 396 | cfg.swift:396:21:396:21 | x | 'x -^ Block' | | 397 | cfg.swift:397:3:402:3 | TryExpr | 'TryExpr -V Block' | -| 398 | cfg.swift:398:9:398:18 | mightThrow | 'mightThrow -> Argument -V 0 -^ CallExpr -^ try -^ UnaryExpr' | +| 398 | cfg.swift:398:5:398:24 | try | 'try -^ UnaryExpr' | +| 398 | cfg.swift:398:9:398:18 | mightThrow | 'mightThrow -> Argument -V 0 -^ CallExpr' | | 399 | cfg.swift:399:5:399:9 | print | 'print -> Argument -V "Did not throw." -^ CallExpr' | | 400 | cfg.swift:400:10:400:19 | mightThrow | 'mightThrow -> Argument -V 0 -^ CallExpr -^ try! -^ UnaryExpr' | | 401 | cfg.swift:401:5:401:9 | print | 'print -> Argument -V "Still did not throw." -^ CallExpr' | diff --git a/unified/ql/test/library-tests/controlflow/cfg.expected b/unified/ql/test/library-tests/controlflow/cfg.expected index 675e2bbe0312..cf0b25228239 100644 --- a/unified/ql/test/library-tests/controlflow/cfg.expected +++ b/unified/ql/test/library-tests/controlflow/cfg.expected @@ -4,6 +4,17 @@ bbContinues | cfg.swift:525:78:525:78 | Block | 'Block goto Task(+2)' | | cfg.swift:526:9:526:20 | continuation | 'continuation goto Block(-1)' | bbStep +| cfg.swift:30:9:30:24 | CallExpr | 'CallExpr : exception -> CatchClause(+5)' | +| cfg.swift:30:9:30:24 | CallExpr | 'CallExpr : successor -> try(+0)' | +| cfg.swift:33:5:33:33 | CallExpr | 'CallExpr : successor -> 0(+11)' | +| cfg.swift:35:5:35:5 | OrPattern | 'OrPattern : match -> Block(+0)' | +| cfg.swift:35:5:35:5 | OrPattern | 'OrPattern : no-match -> CatchClause(+2)' | +| cfg.swift:37:11:37:39 | CallExpr | 'CallExpr : match -> Block(+0)' | +| cfg.swift:37:11:37:39 | CallExpr | 'CallExpr : no-match -> CatchClause(+2)' | +| cfg.swift:39:11:39:20 | | ' : match -> Block(+0)' | +| cfg.swift:39:11:39:20 | | ' : no-match -> CatchClause(+2)' | +| cfg.swift:40:5:40:20 | CallExpr | 'CallExpr : successor -> 0(+4)' | +| cfg.swift:42:5:42:35 | CallExpr | 'CallExpr : successor -> 0(+2)' | | cfg.swift:140:12:140:17 | BinaryExpr | 'BinaryExpr : empty -> SwitchExpr(+3)' | | cfg.swift:140:12:140:17 | BinaryExpr | 'BinaryExpr : non-empty -> _(+0)' | | cfg.swift:141:9:141:12 | Block | 'Block : successor -> SwitchExpr(+2)' | @@ -80,6 +91,7 @@ bbStep | cfg.swift:360:3:360:3 | WhileStmt | 'WhileStmt : successor -> x(+0)' | | cfg.swift:360:9:360:14 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | | cfg.swift:361:5:361:10 | CompoundAssignExpr | 'CompoundAssignExpr : successor -> x(-1)' | +| cfg.swift:398:9:398:24 | CallExpr | 'CallExpr : successor -> try(+0)' | | cfg.swift:500:6:500:28 | | ' : false -> IfExpr(+4)' | | cfg.swift:500:6:500:28 | | ' : true -> Block(+0)' | | cfg.swift:501:5:501:10 | CompoundAssignExpr | 'CompoundAssignExpr : successor -> IfExpr(+3)' | @@ -116,13 +128,6 @@ bbStep noCfg | cfg.swift:23:9:23:9 | x | | cfg.swift:24:5:24:42 | ThrowExpr | -| cfg.swift:35:11:35:17 | MyError | -| cfg.swift:36:5:36:12 | ReturnExpr | -| cfg.swift:37:11:37:17 | MyError | -| cfg.swift:38:5:38:20 | ReturnExpr | -| cfg.swift:39:11:39:20 | | -| cfg.swift:40:5:40:9 | print | -| cfg.swift:42:5:42:9 | print | | cfg.swift:47:42:47:47 | String | | cfg.swift:53:34:53:34 | _ | | cfg.swift:60:34:60:34 | _ | @@ -152,6 +157,7 @@ noCfg | cfg.swift:559:6:559:20 | autoclosureTest | nonSimple | cfg.swift:10:1:10:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V MyError -^ BaseType -V Error' | +| cfg.swift:35:5:35:5 | CatchClause | 'CatchClause -V MyError -^ MemberAccessExpr -> isZero -> Argument -V x -^ CallExpr -? MyError -^ MemberAccessExpr -^ ConditionalPattern -^ OrPattern' | | cfg.swift:209:5:209:5 | x | 'x -> x -? - -^ UnaryExpr -^ AssignExpr' | | cfg.swift:390:1:390:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V Derived -^ BaseType -V C' | | cfg.swift:527:13:527:16 | Task | 'Task -^ MemberAccessExpr -^ Argument -V FunctionExpr -^ CallExpr' | diff --git a/unified/ql/test/library-tests/controlflow/cfg.swift b/unified/ql/test/library-tests/controlflow/cfg.swift index 984983fc1b52..e8bb40e33f8e 100644 --- a/unified/ql/test/library-tests/controlflow/cfg.swift +++ b/unified/ql/test/library-tests/controlflow/cfg.swift @@ -27,19 +27,19 @@ func mightThrow(x : Int) throws -> Void { func tryCatch(x : Int) -> Int { do { - try mightThrow(x: 0) + try mightThrow(x: 0) // $ bbStep='CallExpr : exception -> CatchClause(+5)' bbStep='CallExpr : successor -> try(+0)' print("Did not throw.") try! mightThrow(x: 0) - print("Still did not throw.") + print("Still did not throw.") // $ bbStep='CallExpr : successor -> 0(+11)' - } catch MyError.error1 , MyError.error2 where isZero(x: x) { // $ noCfg - return 0 // $ noCfg - } catch MyError.error3(let withParam) { // $ noCfg - return withParam // $ noCfg - } catch is MyError { // $ noCfg - print("MyError") // $ noCfg + } catch MyError.error1 , MyError.error2 where isZero(x: x) { // $ bbStep='OrPattern : match -> Block(+0)' bbStep='OrPattern : no-match -> CatchClause(+2)' nonSimple='CatchClause -V MyError -^ MemberAccessExpr -> isZero -> Argument -V x -^ CallExpr -? MyError -^ MemberAccessExpr -^ ConditionalPattern -^ OrPattern' + return 0 + } catch MyError.error3(let withParam) { // $ bbStep='CallExpr : match -> Block(+0)' bbStep='CallExpr : no-match -> CatchClause(+2)' + return withParam + } catch is MyError { // $ bbStep=' : match -> Block(+0)' bbStep=' : no-match -> CatchClause(+2)' + print("MyError") // $ bbStep='CallExpr : successor -> 0(+4)' } catch { - print("Unknown error \(error)") // $ noCfg + print("Unknown error \(error)") // $ bbStep='CallExpr : successor -> 0(+2)' } return 0 } @@ -395,7 +395,7 @@ class Derived : C { // $ nonSimple='ClassLikeDeclaration -V Derived -^ BaseType func doWithoutCatch(x : Int) throws -> Int { do { - try mightThrow(x: 0) + try mightThrow(x: 0) // $ bbStep='CallExpr : successor -> try(+0)' print("Did not throw.") try! mightThrow(x: 0) print("Still did not throw.")