@@ -2175,6 +2175,41 @@ module ExceptionTypes {
21752175 /** Gets a string representation of this exception type. */
21762176 string toString ( ) { result = this .getName ( ) }
21772177
2178+ /** Holds if this exception type may be raised at control flow node `r`. */
2179+ predicate isRaisedAt ( ControlFlowNode r ) {
2180+ exists ( Expr raised |
2181+ raised = r .getNode ( ) .( Raise ) .getRaised ( ) and
2182+ this .getAUse ( ) .asExpr ( ) in [ raised , raised .( Call ) .getFunc ( ) ]
2183+ )
2184+ or
2185+ exists ( Function callee |
2186+ resolveCall ( r , callee , _) and
2187+ this .isRaisedIn ( callee )
2188+ )
2189+ }
2190+
2191+ /**
2192+ * Holds if this exception type may be raised in function `f`, either
2193+ * directly via `raise` statements or transitively through calls to other functions.
2194+ */
2195+ predicate isRaisedIn ( Function f ) { this .isRaisedAt ( any ( ControlFlowNode r | r .getScope ( ) = f ) ) }
2196+
2197+ /** Holds if this exception type is handled by the `except` clause at `handler`. */
2198+ predicate isHandledAt ( ExceptFlowNode handler ) {
2199+ exists ( ExceptStmt ex , Expr typeExpr | ex = handler .getNode ( ) |
2200+ (
2201+ typeExpr = ex .getType ( )
2202+ or
2203+ typeExpr = ex .getType ( ) .( Tuple ) .getAnElt ( )
2204+ ) and
2205+ this .getAUse ( ) .asExpr ( ) = typeExpr
2206+ )
2207+ or
2208+ // A bare `except:` handles everything
2209+ not exists ( handler .getNode ( ) .( ExceptStmt ) .getType ( ) ) and
2210+ this .( BuiltinExceptType ) .getName ( ) = "BaseException"
2211+ }
2212+
21782213 /**
21792214 * Holds if this element is at the specified location.
21802215 * The location spans column `startColumn` of line `startLine` to
@@ -2243,5 +2278,128 @@ module ExceptionTypes {
22432278 endColumn = 0
22442279 }
22452280 }
2281+
2282+ /**
2283+ * Holds if the exception edge from `r` to `handler` is unlikely because
2284+ * none of the exception types that `r` may raise are handled by `handler`.
2285+ */
2286+ predicate unlikelyExceptionEdge ( ControlFlowNode r , ExceptFlowNode handler ) {
2287+ handler = r .getAnExceptionalSuccessor ( ) and
2288+ // We can determine at least one raised type
2289+ exists ( ExceptType t | t .isRaisedAt ( r ) ) and
2290+ // But none of them are handled by this handler
2291+ not exists ( ExceptType raised , ExceptType handled |
2292+ raised .isRaisedAt ( r ) and
2293+ handled .isHandledAt ( handler ) and
2294+ raised .getADirectSuperclass * ( ) = handled
2295+ )
2296+ }
22462297}
22472298
2299+ /**
2300+ * Provides predicates for reasoning about the reachability of control flow nodes
2301+ * and basic blocks.
2302+ */
2303+ module Reachability {
2304+ private import semmle.python.ApiGraphs
2305+ import ExceptionTypes
2306+
2307+ /**
2308+ * Holds if `call` is a call to a function that is known to never return normally
2309+ * (e.g. `sys.exit()`, `os._exit()`, `os.abort()`).
2310+ */
2311+ predicate isCallToNeverReturningFunction ( CallNode call ) {
2312+ // Known never-returning builtins/stdlib functions via API graphs
2313+ call = API:: builtin ( "exit" ) .getACall ( ) .asCfgNode ( )
2314+ or
2315+ call = API:: builtin ( "quit" ) .getACall ( ) .asCfgNode ( )
2316+ or
2317+ call = API:: moduleImport ( "sys" ) .getMember ( "exit" ) .getACall ( ) .asCfgNode ( )
2318+ or
2319+ call = API:: moduleImport ( "os" ) .getMember ( "_exit" ) .getACall ( ) .asCfgNode ( )
2320+ or
2321+ call = API:: moduleImport ( "os" ) .getMember ( "abort" ) .getACall ( ) .asCfgNode ( )
2322+ or
2323+ // User-defined functions that only contain raise statements (no normal returns)
2324+ exists ( Function target |
2325+ resolveCall ( call , target , _) and
2326+ neverReturns ( target )
2327+ )
2328+ }
2329+
2330+ /**
2331+ * Holds if function `f` never returns normally, because every normal exit
2332+ * is dominated by a call to a never-returning function or an unconditional raise.
2333+ */
2334+ predicate neverReturns ( Function f ) {
2335+ exists ( f .getANormalExit ( ) ) and
2336+ forall ( BasicBlock exit | exit = f .getANormalExit ( ) .getBasicBlock ( ) |
2337+ exists ( BasicBlock raising |
2338+ raising .dominates ( exit ) and
2339+ (
2340+ isCallToNeverReturningFunction ( raising .getLastNode ( ) )
2341+ or
2342+ raising .getLastNode ( ) .getNode ( ) instanceof Raise
2343+ )
2344+ )
2345+ )
2346+ }
2347+
2348+ /**
2349+ * Holds if it is highly unlikely for control to flow from `node` to `succ`.
2350+ */
2351+ predicate unlikelySuccessor ( ControlFlowNode node , ControlFlowNode succ ) {
2352+ // Exceptional edge where the raised type doesn't match the handler
2353+ unlikelyExceptionEdge ( node , succ )
2354+ or
2355+ // Normal successor of a never-returning call
2356+ isCallToNeverReturningFunction ( node ) and
2357+ succ = node .getASuccessor ( ) and
2358+ not succ = node .getAnExceptionalSuccessor ( ) and
2359+ not succ .getNode ( ) instanceof Yield
2360+ }
2361+
2362+ private predicate startBbLikelyReachable ( BasicBlock b ) {
2363+ exists ( Scope s | s .getEntryNode ( ) = b .getNode ( _) )
2364+ or
2365+ exists ( BasicBlock pred |
2366+ pred = b .getAPredecessor ( ) and
2367+ endBbLikelyReachable ( pred ) and
2368+ not unlikelySuccessor ( pred .getLastNode ( ) , b )
2369+ )
2370+ }
2371+
2372+ private predicate endBbLikelyReachable ( BasicBlock b ) {
2373+ startBbLikelyReachable ( b ) and
2374+ not exists ( ControlFlowNode p , ControlFlowNode s |
2375+ unlikelySuccessor ( p , s ) and
2376+ p = b .getNode ( _) and
2377+ s = b .getNode ( _) and
2378+ not p = b .getLastNode ( )
2379+ )
2380+ }
2381+
2382+ /**
2383+ * Holds if basic block `b` is likely to be reachable from the entry of its
2384+ * enclosing scope.
2385+ */
2386+ predicate likelyReachable ( BasicBlock b ) { startBbLikelyReachable ( b ) }
2387+
2388+ /**
2389+ * Holds if it is unlikely that `node` can be reached during execution.
2390+ */
2391+ predicate unlikelyReachable ( ControlFlowNode node ) {
2392+ not startBbLikelyReachable ( node .getBasicBlock ( ) )
2393+ or
2394+ exists ( BasicBlock b |
2395+ startBbLikelyReachable ( b ) and
2396+ not endBbLikelyReachable ( b ) and
2397+ exists ( ControlFlowNode p , int i , int j |
2398+ unlikelySuccessor ( p , _) and
2399+ p = b .getNode ( i ) and
2400+ node = b .getNode ( j ) and
2401+ i < j
2402+ )
2403+ )
2404+ }
2405+ }
0 commit comments