1//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8// 9// This defines NoReturnFunctionChecker, which evaluates functions that do not 10// return to the caller. 11// 12//===----------------------------------------------------------------------===// 13 14#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15#include "clang/AST/Attr.h" 16#include "clang/Analysis/SelectorExtras.h" 17#include "clang/StaticAnalyzer/Core/Checker.h" 18#include "clang/StaticAnalyzer/Core/CheckerManager.h" 19#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 20#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 21#include "llvm/ADT/StringSwitch.h" 22#include <cstdarg> 23 24using namespace clang; 25using namespace ento; 26 27namespace { 28 29class NoReturnFunctionChecker : public Checker< check::PostCall, 30 check::PostObjCMessage > { 31 mutable Selector HandleFailureInFunctionSel; 32 mutable Selector HandleFailureInMethodSel; 33public: 34 void checkPostCall(const CallEvent &CE, CheckerContext &C) const; 35 void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 36}; 37 38} 39 40void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE, 41 CheckerContext &C) const { 42 bool BuildSinks = false; 43 44 if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE.getDecl())) 45 BuildSinks = FD->hasAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn(); 46 47 if (const CallExpr *CExpr = dyn_cast_or_null<CallExpr>(CE.getOriginExpr()); 48 CExpr && !BuildSinks) { 49 if (const Expr *C = CExpr->getCallee()) 50 BuildSinks = getFunctionExtInfo(C->getType()).getNoReturn(); 51 } 52 53 if (!BuildSinks && CE.isGlobalCFunction()) { 54 if (const IdentifierInfo *II = CE.getCalleeIdentifier()) { 55 // HACK: Some functions are not marked noreturn, and don't return. 56 // Here are a few hardwired ones. If this takes too long, we can 57 // potentially cache these results. 58 BuildSinks 59 = llvm::StringSwitch<bool>(StringRef(II->getName())) 60 .Case("exit", true) 61 .Case("panic", true) 62 .Case("error", true) 63 .Case("Assert", true) 64 // FIXME: This is just a wrapper around throwing an exception. 65 // Eventually inter-procedural analysis should handle this easily. 66 .Case("ziperr", true) 67 .Case("assfail", true) 68 .Case("db_error", true) 69 .Case("__assert", true) 70 .Case("__assert2", true) 71 // For the purpose of static analysis, we do not care that 72 // this MSVC function will return if the user decides to continue. 73 .Case("_wassert", true) 74 .Case("__assert_rtn", true) 75 .Case("__assert_fail", true) 76 .Case("dtrace_assfail", true) 77 .Case("yy_fatal_error", true) 78 .Case("_XCAssertionFailureHandler", true) 79 .Case("_DTAssertionFailureHandler", true) 80 .Case("_TSAssertionFailureHandler", true) 81 .Default(false); 82 } 83 } 84 85 if (BuildSinks) 86 C.generateSink(C.getState(), C.getPredecessor()); 87} 88 89void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, 90 CheckerContext &C) const { 91 // Check if the method is annotated with analyzer_noreturn. 92 if (const ObjCMethodDecl *MD = Msg.getDecl()) { 93 MD = MD->getCanonicalDecl(); 94 if (MD->hasAttr<AnalyzerNoReturnAttr>()) { 95 C.generateSink(C.getState(), C.getPredecessor()); 96 return; 97 } 98 } 99 100 // HACK: This entire check is to handle two messages in the Cocoa frameworks: 101 // -[NSAssertionHandler 102 // handleFailureInMethod:object:file:lineNumber:description:] 103 // -[NSAssertionHandler 104 // handleFailureInFunction:file:lineNumber:description:] 105 // Eventually these should be annotated with __attribute__((noreturn)). 106 // Because ObjC messages use dynamic dispatch, it is not generally safe to 107 // assume certain methods can't return. In cases where it is definitely valid, 108 // see if you can mark the methods noreturn or analyzer_noreturn instead of 109 // adding more explicit checks to this method. 110 111 if (!Msg.isInstanceMessage()) 112 return; 113 114 const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface(); 115 if (!Receiver) 116 return; 117 if (!Receiver->getIdentifier()->isStr("NSAssertionHandler")) 118 return; 119 120 Selector Sel = Msg.getSelector(); 121 switch (Sel.getNumArgs()) { 122 default: 123 return; 124 case 4: 125 lazyInitKeywordSelector(HandleFailureInFunctionSel, C.getASTContext(), 126 "handleFailureInFunction", "file", "lineNumber", 127 "description"); 128 if (Sel != HandleFailureInFunctionSel) 129 return; 130 break; 131 case 5: 132 lazyInitKeywordSelector(HandleFailureInMethodSel, C.getASTContext(), 133 "handleFailureInMethod", "object", "file", 134 "lineNumber", "description"); 135 if (Sel != HandleFailureInMethodSel) 136 return; 137 break; 138 } 139 140 // If we got here, it's one of the messages we care about. 141 C.generateSink(C.getState(), C.getPredecessor()); 142} 143 144void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) { 145 mgr.registerChecker<NoReturnFunctionChecker>(); 146} 147 148bool ento::shouldRegisterNoReturnFunctionChecker(const CheckerManager &mgr) { 149 return true; 150} 151