@@ -2154,6 +2154,41 @@ module ExceptionTypes {
21542154 /** Gets a string representation of this exception type. */
21552155 string toString ( ) { result = this .getName ( ) }
21562156
2157+ /** Holds if this exception type may be raised at control flow node `r`. */
2158+ predicate isRaisedAt ( ControlFlowNode r ) {
2159+ exists ( Expr raised |
2160+ raised = r .getNode ( ) .( Raise ) .getRaised ( ) and
2161+ this .getAUse ( ) .asExpr ( ) in [ raised , raised .( Call ) .getFunc ( ) ]
2162+ )
2163+ or
2164+ exists ( Function callee |
2165+ resolveCall ( r , callee , _) and
2166+ this .isRaisedIn ( callee )
2167+ )
2168+ }
2169+
2170+ /**
2171+ * Holds if this exception type may be raised in function `f`, either
2172+ * directly via `raise` statements or transitively through calls to other functions.
2173+ */
2174+ predicate isRaisedIn ( Function f ) { this .isRaisedAt ( any ( ControlFlowNode r | r .getScope ( ) = f ) ) }
2175+
2176+ /** Holds if this exception type is handled by the `except` clause at `handler`. */
2177+ predicate isHandledAt ( ExceptFlowNode handler ) {
2178+ exists ( ExceptStmt ex , Expr typeExpr | ex = handler .getNode ( ) |
2179+ (
2180+ typeExpr = ex .getType ( )
2181+ or
2182+ typeExpr = ex .getType ( ) .( Tuple ) .getAnElt ( )
2183+ ) and
2184+ this .getAUse ( ) .asExpr ( ) = typeExpr
2185+ )
2186+ or
2187+ // A bare `except:` handles everything
2188+ not exists ( handler .getNode ( ) .( ExceptStmt ) .getType ( ) ) and
2189+ this .( BuiltinExceptType ) .getName ( ) = "BaseException"
2190+ }
2191+
21572192 /**
21582193 * Holds if this element is at the specified location.
21592194 * The location spans column `startColumn` of line `startLine` to
@@ -2222,5 +2257,128 @@ module ExceptionTypes {
22222257 endColumn = 0
22232258 }
22242259 }
2260+
2261+ /**
2262+ * Holds if the exception edge from `r` to `handler` is unlikely because
2263+ * none of the exception types that `r` may raise are handled by `handler`.
2264+ */
2265+ predicate unlikelyExceptionEdge ( ControlFlowNode r , ExceptFlowNode handler ) {
2266+ handler = r .getAnExceptionalSuccessor ( ) and
2267+ // We can determine at least one raised type
2268+ exists ( ExceptType t | t .isRaisedAt ( r ) ) and
2269+ // But none of them are handled by this handler
2270+ not exists ( ExceptType raised , ExceptType handled |
2271+ raised .isRaisedAt ( r ) and
2272+ handled .isHandledAt ( handler ) and
2273+ raised .getADirectSuperclass * ( ) = handled
2274+ )
2275+ }
22252276}
22262277
2278+ /**
2279+ * Provides predicates for reasoning about the reachability of control flow nodes
2280+ * and basic blocks.
2281+ */
2282+ module Reachability {
2283+ private import semmle.python.ApiGraphs
2284+ import ExceptionTypes
2285+
2286+ /**
2287+ * Holds if `call` is a call to a function that is known to never return normally
2288+ * (e.g. `sys.exit()`, `os._exit()`, `os.abort()`).
2289+ */
2290+ predicate isCallToNeverReturningFunction ( CallNode call ) {
2291+ // Known never-returning builtins/stdlib functions via API graphs
2292+ call = API:: builtin ( "exit" ) .getACall ( ) .asCfgNode ( )
2293+ or
2294+ call = API:: builtin ( "quit" ) .getACall ( ) .asCfgNode ( )
2295+ or
2296+ call = API:: moduleImport ( "sys" ) .getMember ( "exit" ) .getACall ( ) .asCfgNode ( )
2297+ or
2298+ call = API:: moduleImport ( "os" ) .getMember ( "_exit" ) .getACall ( ) .asCfgNode ( )
2299+ or
2300+ call = API:: moduleImport ( "os" ) .getMember ( "abort" ) .getACall ( ) .asCfgNode ( )
2301+ or
2302+ // User-defined functions that only contain raise statements (no normal returns)
2303+ exists ( Function target |
2304+ resolveCall ( call , target , _) and
2305+ neverReturns ( target )
2306+ )
2307+ }
2308+
2309+ /**
2310+ * Holds if function `f` never returns normally, because every normal exit
2311+ * is dominated by a call to a never-returning function or an unconditional raise.
2312+ */
2313+ predicate neverReturns ( Function f ) {
2314+ exists ( f .getANormalExit ( ) ) and
2315+ forall ( BasicBlock exit | exit = f .getANormalExit ( ) .getBasicBlock ( ) |
2316+ exists ( BasicBlock raising |
2317+ raising .dominates ( exit ) and
2318+ (
2319+ isCallToNeverReturningFunction ( raising .getLastNode ( ) )
2320+ or
2321+ raising .getLastNode ( ) .getNode ( ) instanceof Raise
2322+ )
2323+ )
2324+ )
2325+ }
2326+
2327+ /**
2328+ * Holds if it is highly unlikely for control to flow from `node` to `succ`.
2329+ */
2330+ predicate unlikelySuccessor ( ControlFlowNode node , ControlFlowNode succ ) {
2331+ // Exceptional edge where the raised type doesn't match the handler
2332+ unlikelyExceptionEdge ( node , succ )
2333+ or
2334+ // Normal successor of a never-returning call
2335+ isCallToNeverReturningFunction ( node ) and
2336+ succ = node .getASuccessor ( ) and
2337+ not succ = node .getAnExceptionalSuccessor ( ) and
2338+ not succ .getNode ( ) instanceof Yield
2339+ }
2340+
2341+ private predicate startBbLikelyReachable ( BasicBlock b ) {
2342+ exists ( Scope s | s .getEntryNode ( ) = b .getNode ( _) )
2343+ or
2344+ exists ( BasicBlock pred |
2345+ pred = b .getAPredecessor ( ) and
2346+ endBbLikelyReachable ( pred ) and
2347+ not unlikelySuccessor ( pred .getLastNode ( ) , b )
2348+ )
2349+ }
2350+
2351+ private predicate endBbLikelyReachable ( BasicBlock b ) {
2352+ startBbLikelyReachable ( b ) and
2353+ not exists ( ControlFlowNode p , ControlFlowNode s |
2354+ unlikelySuccessor ( p , s ) and
2355+ p = b .getNode ( _) and
2356+ s = b .getNode ( _) and
2357+ not p = b .getLastNode ( )
2358+ )
2359+ }
2360+
2361+ /**
2362+ * Holds if basic block `b` is likely to be reachable from the entry of its
2363+ * enclosing scope.
2364+ */
2365+ predicate likelyReachable ( BasicBlock b ) { startBbLikelyReachable ( b ) }
2366+
2367+ /**
2368+ * Holds if it is unlikely that `node` can be reached during execution.
2369+ */
2370+ predicate unlikelyReachable ( ControlFlowNode node ) {
2371+ not startBbLikelyReachable ( node .getBasicBlock ( ) )
2372+ or
2373+ exists ( BasicBlock b |
2374+ startBbLikelyReachable ( b ) and
2375+ not endBbLikelyReachable ( b ) and
2376+ exists ( ControlFlowNode p , int i , int j |
2377+ unlikelySuccessor ( p , _) and
2378+ p = b .getNode ( i ) and
2379+ node = b .getNode ( j ) and
2380+ i < j
2381+ )
2382+ )
2383+ }
2384+ }
0 commit comments