NoReturnFunctionChecker.cpp revision 226633
1295958Ssephe//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===// 2298446Ssephe// 3295958Ssephe// The LLVM Compiler Infrastructure 4295958Ssephe// 5295958Ssephe// This file is distributed under the University of Illinois Open Source 6295958Ssephe// License. See LICENSE.TXT for details. 7295958Ssephe// 8295958Ssephe//===----------------------------------------------------------------------===// 9295958Ssephe// 10295958Ssephe// This defines NoReturnFunctionChecker, which evaluates functions that do not 11295958Ssephe// return to the caller. 12295958Ssephe// 13295958Ssephe//===----------------------------------------------------------------------===// 14295958Ssephe 15295958Ssephe#include "ClangSACheckers.h" 16295958Ssephe#include "clang/StaticAnalyzer/Core/Checker.h" 17295958Ssephe#include "clang/StaticAnalyzer/Core/CheckerManager.h" 18295958Ssephe#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19295958Ssephe#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" 20295958Ssephe#include "llvm/ADT/StringSwitch.h" 21295958Ssephe#include <cstdarg> 22295958Ssephe 23295958Ssepheusing namespace clang; 24295958Ssepheusing namespace ento; 25295958Ssephe 26295958Ssephenamespace { 27295958Ssephe 28295958Ssepheclass NoReturnFunctionChecker : public Checker< check::PostStmt<CallExpr>, 29295958Ssephe check::PostObjCMessage > { 30295958Ssephepublic: 31295958Ssephe void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; 32295958Ssephe void checkPostObjCMessage(const ObjCMessage &msg, CheckerContext &C) const; 33295958Ssephe}; 34295958Ssephe 35295958Ssephe} 36295958Ssephe 37295958Ssephevoid NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE, 38302880Ssephe CheckerContext &C) const { 39303067Ssephe const ProgramState *state = C.getState(); 40295958Ssephe const Expr *Callee = CE->getCallee(); 41302698Ssephe 42295958Ssephe bool BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); 43295958Ssephe 44302802Ssephe if (!BuildSinks) { 45295958Ssephe SVal L = state->getSVal(Callee); 46295958Ssephe const FunctionDecl *FD = L.getAsFunctionDecl(); 47295958Ssephe if (!FD) 48295958Ssephe return; 49295958Ssephe 50295958Ssephe if (FD->getAttr<AnalyzerNoReturnAttr>()) 51295958Ssephe BuildSinks = true; 52295958Ssephe else if (const IdentifierInfo *II = FD->getIdentifier()) { 53295958Ssephe // HACK: Some functions are not marked noreturn, and don't return. 54295958Ssephe // Here are a few hardwired ones. If this takes too long, we can 55295958Ssephe // potentially cache these results. 56302885Ssephe BuildSinks 57295958Ssephe = llvm::StringSwitch<bool>(StringRef(II->getName())) 58295958Ssephe .Case("exit", true) 59295958Ssephe .Case("panic", true) 60295958Ssephe .Case("error", true) 61295958Ssephe .Case("Assert", true) 62295958Ssephe // FIXME: This is just a wrapper around throwing an exception. 63295958Ssephe // Eventually inter-procedural analysis should handle this easily. 64295958Ssephe .Case("ziperr", true) 65297793Spfg .Case("assfail", true) 66302702Ssephe .Case("db_error", true) 67295958Ssephe .Case("__assert", true) 68302885Ssephe .Case("__assert_rtn", true) 69302885Ssephe .Case("__assert_fail", true) 70302885Ssephe .Case("dtrace_assfail", true) 71302885Ssephe .Case("yy_fatal_error", true) 72295958Ssephe .Case("_XCAssertionFailureHandler", true) 73295958Ssephe .Case("_DTAssertionFailureHandler", true) 74295958Ssephe .Case("_TSAssertionFailureHandler", true) 75295958Ssephe .Default(false); 76295958Ssephe } 77295958Ssephe } 78295958Ssephe 79295958Ssephe if (BuildSinks) 80295958Ssephe C.generateSink(); 81295958Ssephe} 82295958Ssephe 83295958Ssephestatic bool END_WITH_NULL isMultiArgSelector(const Selector *Sel, ...) { 84295958Ssephe va_list argp; 85295958Ssephe va_start(argp, Sel); 86295958Ssephe 87295958Ssephe unsigned Slot = 0; 88295958Ssephe const char *Arg; 89295958Ssephe while ((Arg = va_arg(argp, const char *))) { 90295958Ssephe if (!Sel->getNameForSlot(Slot).equals(Arg)) 91295958Ssephe break; // still need to va_end! 92295958Ssephe ++Slot; 93302882Ssephe } 94302882Ssephe 95295958Ssephe va_end(argp); 96295958Ssephe 97295958Ssephe // We only succeeded if we made it to the end of the argument list. 98295958Ssephe return (Arg == NULL); 99295958Ssephe} 100295958Ssephe 101297220Ssephevoid NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMessage &Msg, 102297220Ssephe CheckerContext &C) const { 103297220Ssephe // HACK: This entire check is to handle two messages in the Cocoa frameworks: 104302698Ssephe // -[NSAssertionHandler 105295958Ssephe // handleFailureInMethod:object:file:lineNumber:description:] 106295958Ssephe // -[NSAssertionHandler 107295958Ssephe // handleFailureInFunction:file:lineNumber:description:] 108295958Ssephe // Eventually these should be annotated with __attribute__((noreturn)). 109295958Ssephe // Because ObjC messages use dynamic dispatch, it is not generally safe to 110295958Ssephe // assume certain methods can't return. In cases where it is definitely valid, 111295958Ssephe // see if you can mark the methods noreturn or analyzer_noreturn instead of 112295958Ssephe // adding more explicit checks to this method. 113295958Ssephe 114295958Ssephe if (!Msg.isInstanceMessage()) 115295958Ssephe return; 116295958Ssephe 117295958Ssephe const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface(); 118295958Ssephe if (!Receiver) 119295958Ssephe return; 120295958Ssephe if (!Receiver->getIdentifier()->isStr("NSAssertionHandler")) 121295958Ssephe return; 122295958Ssephe 123295958Ssephe Selector Sel = Msg.getSelector(); 124295958Ssephe switch (Sel.getNumArgs()) { 125295958Ssephe default: 126295958Ssephe return; 127295958Ssephe case 4: 128295958Ssephe if (!isMultiArgSelector(&Sel, "handleFailureInFunction", "file", 129295958Ssephe "lineNumber", "description", NULL)) 130295958Ssephe return; 131295958Ssephe break; 132295958Ssephe case 5: 133295958Ssephe if (!isMultiArgSelector(&Sel, "handleFailureInMethod", "object", "file", 134295958Ssephe "lineNumber", "description", NULL)) 135295958Ssephe return; 136 break; 137 } 138 139 // If we got here, it's one of the messages we care about. 140 C.generateSink(); 141} 142 143 144void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) { 145 mgr.registerChecker<NoReturnFunctionChecker>(); 146} 147