@@ -2199,6 +2199,41 @@ module ExceptionTypes {
21992199 /** Gets a string representation of this exception type. */
22002200 string toString ( ) { result = this .getName ( ) }
22012201
2202+ /** Holds if this exception type may be raised at control flow node `r`. */
2203+ predicate isRaisedAt ( ControlFlowNode r ) {
2204+ exists ( Expr raised |
2205+ raised = r .getNode ( ) .( Raise ) .getRaised ( ) and
2206+ this .getAUse ( ) .asExpr ( ) in [ raised , raised .( Call ) .getFunc ( ) ]
2207+ )
2208+ or
2209+ exists ( Function callee |
2210+ resolveCall ( r , callee , _) and
2211+ this .isRaisedIn ( callee )
2212+ )
2213+ }
2214+
2215+ /**
2216+ * Holds if this exception type may be raised in function `f`, either
2217+ * directly via `raise` statements or transitively through calls to other functions.
2218+ */
2219+ predicate isRaisedIn ( Function f ) { this .isRaisedAt ( any ( ControlFlowNode r | r .getScope ( ) = f ) ) }
2220+
2221+ /** Holds if this exception type is handled by the `except` clause at `handler`. */
2222+ predicate isHandledAt ( ExceptFlowNode handler ) {
2223+ exists ( ExceptStmt ex , Expr typeExpr | ex = handler .getNode ( ) |
2224+ (
2225+ typeExpr = ex .getType ( )
2226+ or
2227+ typeExpr = ex .getType ( ) .( Tuple ) .getAnElt ( )
2228+ ) and
2229+ this .getAUse ( ) .asExpr ( ) = typeExpr
2230+ )
2231+ or
2232+ // A bare `except:` handles everything
2233+ not exists ( handler .getNode ( ) .( ExceptStmt ) .getType ( ) ) and
2234+ this .( BuiltinExceptType ) .getName ( ) = "BaseException"
2235+ }
2236+
22022237 /**
22032238 * Holds if this element is at the specified location.
22042239 * The location spans column `startColumn` of line `startLine` to
@@ -2267,5 +2302,128 @@ module ExceptionTypes {
22672302 endColumn = 0
22682303 }
22692304 }
2305+
2306+ /**
2307+ * Holds if the exception edge from `r` to `handler` is unlikely because
2308+ * none of the exception types that `r` may raise are handled by `handler`.
2309+ */
2310+ predicate unlikelyExceptionEdge ( ControlFlowNode r , ExceptFlowNode handler ) {
2311+ handler = r .getAnExceptionalSuccessor ( ) and
2312+ // We can determine at least one raised type
2313+ exists ( ExceptType t | t .isRaisedAt ( r ) ) and
2314+ // But none of them are handled by this handler
2315+ not exists ( ExceptType raised , ExceptType handled |
2316+ raised .isRaisedAt ( r ) and
2317+ handled .isHandledAt ( handler ) and
2318+ raised .getADirectSuperclass * ( ) = handled
2319+ )
2320+ }
22702321}
22712322
2323+ /**
2324+ * Provides predicates for reasoning about the reachability of control flow nodes
2325+ * and basic blocks.
2326+ */
2327+ module Reachability {
2328+ private import semmle.python.ApiGraphs
2329+ import ExceptionTypes
2330+
2331+ /**
2332+ * Holds if `call` is a call to a function that is known to never return normally
2333+ * (e.g. `sys.exit()`, `os._exit()`, `os.abort()`).
2334+ */
2335+ predicate isCallToNeverReturningFunction ( CallNode call ) {
2336+ // Known never-returning builtins/stdlib functions via API graphs
2337+ call = API:: builtin ( "exit" ) .getACall ( ) .asCfgNode ( )
2338+ or
2339+ call = API:: builtin ( "quit" ) .getACall ( ) .asCfgNode ( )
2340+ or
2341+ call = API:: moduleImport ( "sys" ) .getMember ( "exit" ) .getACall ( ) .asCfgNode ( )
2342+ or
2343+ call = API:: moduleImport ( "os" ) .getMember ( "_exit" ) .getACall ( ) .asCfgNode ( )
2344+ or
2345+ call = API:: moduleImport ( "os" ) .getMember ( "abort" ) .getACall ( ) .asCfgNode ( )
2346+ or
2347+ // User-defined functions that only contain raise statements (no normal returns)
2348+ exists ( Function target |
2349+ resolveCall ( call , target , _) and
2350+ neverReturns ( target )
2351+ )
2352+ }
2353+
2354+ /**
2355+ * Holds if function `f` never returns normally, because every normal exit
2356+ * is dominated by a call to a never-returning function or an unconditional raise.
2357+ */
2358+ predicate neverReturns ( Function f ) {
2359+ exists ( f .getANormalExit ( ) ) and
2360+ forall ( BasicBlock exit | exit = f .getANormalExit ( ) .getBasicBlock ( ) |
2361+ exists ( BasicBlock raising |
2362+ raising .dominates ( exit ) and
2363+ (
2364+ isCallToNeverReturningFunction ( raising .getLastNode ( ) )
2365+ or
2366+ raising .getLastNode ( ) .getNode ( ) instanceof Raise
2367+ )
2368+ )
2369+ )
2370+ }
2371+
2372+ /**
2373+ * Holds if it is highly unlikely for control to flow from `node` to `succ`.
2374+ */
2375+ predicate unlikelySuccessor ( ControlFlowNode node , ControlFlowNode succ ) {
2376+ // Exceptional edge where the raised type doesn't match the handler
2377+ unlikelyExceptionEdge ( node , succ )
2378+ or
2379+ // Normal successor of a never-returning call
2380+ isCallToNeverReturningFunction ( node ) and
2381+ succ = node .getASuccessor ( ) and
2382+ not succ = node .getAnExceptionalSuccessor ( ) and
2383+ not succ .getNode ( ) instanceof Yield
2384+ }
2385+
2386+ private predicate startBbLikelyReachable ( BasicBlock b ) {
2387+ exists ( Scope s | s .getEntryNode ( ) = b .getNode ( _) )
2388+ or
2389+ exists ( BasicBlock pred |
2390+ pred = b .getAPredecessor ( ) and
2391+ endBbLikelyReachable ( pred ) and
2392+ not unlikelySuccessor ( pred .getLastNode ( ) , b )
2393+ )
2394+ }
2395+
2396+ private predicate endBbLikelyReachable ( BasicBlock b ) {
2397+ startBbLikelyReachable ( b ) and
2398+ not exists ( ControlFlowNode p , ControlFlowNode s |
2399+ unlikelySuccessor ( p , s ) and
2400+ p = b .getNode ( _) and
2401+ s = b .getNode ( _) and
2402+ not p = b .getLastNode ( )
2403+ )
2404+ }
2405+
2406+ /**
2407+ * Holds if basic block `b` is likely to be reachable from the entry of its
2408+ * enclosing scope.
2409+ */
2410+ predicate likelyReachable ( BasicBlock b ) { startBbLikelyReachable ( b ) }
2411+
2412+ /**
2413+ * Holds if it is unlikely that `node` can be reached during execution.
2414+ */
2415+ predicate unlikelyReachable ( ControlFlowNode node ) {
2416+ not startBbLikelyReachable ( node .getBasicBlock ( ) )
2417+ or
2418+ exists ( BasicBlock b |
2419+ startBbLikelyReachable ( b ) and
2420+ not endBbLikelyReachable ( b ) and
2421+ exists ( ControlFlowNode p , int i , int j |
2422+ unlikelySuccessor ( p , _) and
2423+ p = b .getNode ( i ) and
2424+ node = b .getNode ( j ) and
2425+ i < j
2426+ )
2427+ )
2428+ }
2429+ }
0 commit comments