1218887Sdim//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===//
2218887Sdim//
3218887Sdim//                     The LLVM Compiler Infrastructure
4218887Sdim//
5218887Sdim// This file is distributed under the University of Illinois Open Source
6218887Sdim// License. See LICENSE.TXT for details.
7218887Sdim//
8218887Sdim//===----------------------------------------------------------------------===//
9218887Sdim//
10218887Sdim// This defines NoReturnFunctionChecker, which evaluates functions that do not
11218887Sdim// return to the caller.
12218887Sdim//
13218887Sdim//===----------------------------------------------------------------------===//
14218887Sdim
15221345Sdim#include "ClangSACheckers.h"
16249423Sdim#include "clang/AST/Attr.h"
17221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
18221345Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
19239462Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
20221345Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21218887Sdim#include "llvm/ADT/StringSwitch.h"
22226633Sdim#include <cstdarg>
23218887Sdim
24218887Sdimusing namespace clang;
25218887Sdimusing namespace ento;
26218887Sdim
27218887Sdimnamespace {
28218887Sdim
29263508Sdimclass NoReturnFunctionChecker : public Checker< check::PostCall,
30226633Sdim                                                check::PostObjCMessage > {
31218887Sdimpublic:
32263508Sdim  void checkPostCall(const CallEvent &CE, CheckerContext &C) const;
33239462Sdim  void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
34218887Sdim};
35218887Sdim
36218887Sdim}
37218887Sdim
38263508Sdimvoid NoReturnFunctionChecker::checkPostCall(const CallEvent &CE,
39221345Sdim                                            CheckerContext &C) const {
40234353Sdim  ProgramStateRef state = C.getState();
41263508Sdim  bool BuildSinks = false;
42218887Sdim
43263508Sdim  if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE.getDecl()))
44263508Sdim    BuildSinks = FD->getAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn();
45218887Sdim
46263508Sdim  const Expr *Callee = CE.getOriginExpr();
47263508Sdim  if (!BuildSinks && Callee)
48263508Sdim    BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn();
49218887Sdim
50263508Sdim  if (!BuildSinks && CE.isGlobalCFunction()) {
51263508Sdim    if (const IdentifierInfo *II = CE.getCalleeIdentifier()) {
52218887Sdim      // HACK: Some functions are not marked noreturn, and don't return.
53218887Sdim      //  Here are a few hardwired ones.  If this takes too long, we can
54218887Sdim      //  potentially cache these results.
55218887Sdim      BuildSinks
56226633Sdim        = llvm::StringSwitch<bool>(StringRef(II->getName()))
57218887Sdim            .Case("exit", true)
58218887Sdim            .Case("panic", true)
59218887Sdim            .Case("error", true)
60218887Sdim            .Case("Assert", true)
61218887Sdim            // FIXME: This is just a wrapper around throwing an exception.
62218887Sdim            //  Eventually inter-procedural analysis should handle this easily.
63218887Sdim            .Case("ziperr", true)
64218887Sdim            .Case("assfail", true)
65218887Sdim            .Case("db_error", true)
66218887Sdim            .Case("__assert", true)
67263508Sdim            // For the purpose of static analysis, we do not care that
68263508Sdim            //  this MSVC function will return if the user decides to continue.
69263508Sdim            .Case("_wassert", true)
70218887Sdim            .Case("__assert_rtn", true)
71218887Sdim            .Case("__assert_fail", true)
72218887Sdim            .Case("dtrace_assfail", true)
73218887Sdim            .Case("yy_fatal_error", true)
74218887Sdim            .Case("_XCAssertionFailureHandler", true)
75218887Sdim            .Case("_DTAssertionFailureHandler", true)
76218887Sdim            .Case("_TSAssertionFailureHandler", true)
77218887Sdim            .Default(false);
78218887Sdim    }
79218887Sdim  }
80218887Sdim
81218887Sdim  if (BuildSinks)
82226633Sdim    C.generateSink();
83218887Sdim}
84221345Sdim
85226633Sdimstatic bool END_WITH_NULL isMultiArgSelector(const Selector *Sel, ...) {
86226633Sdim  va_list argp;
87226633Sdim  va_start(argp, Sel);
88226633Sdim
89226633Sdim  unsigned Slot = 0;
90226633Sdim  const char *Arg;
91226633Sdim  while ((Arg = va_arg(argp, const char *))) {
92226633Sdim    if (!Sel->getNameForSlot(Slot).equals(Arg))
93226633Sdim      break; // still need to va_end!
94226633Sdim    ++Slot;
95226633Sdim  }
96226633Sdim
97226633Sdim  va_end(argp);
98226633Sdim
99226633Sdim  // We only succeeded if we made it to the end of the argument list.
100226633Sdim  return (Arg == NULL);
101226633Sdim}
102226633Sdim
103239462Sdimvoid NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
104226633Sdim                                                   CheckerContext &C) const {
105249423Sdim  // Check if the method is annotated with analyzer_noreturn.
106249423Sdim  if (const ObjCMethodDecl *MD = Msg.getDecl()) {
107249423Sdim    MD = MD->getCanonicalDecl();
108249423Sdim    if (MD->hasAttr<AnalyzerNoReturnAttr>()) {
109249423Sdim      C.generateSink();
110249423Sdim      return;
111249423Sdim    }
112249423Sdim  }
113249423Sdim
114226633Sdim  // HACK: This entire check is to handle two messages in the Cocoa frameworks:
115226633Sdim  // -[NSAssertionHandler
116226633Sdim  //    handleFailureInMethod:object:file:lineNumber:description:]
117226633Sdim  // -[NSAssertionHandler
118226633Sdim  //    handleFailureInFunction:file:lineNumber:description:]
119226633Sdim  // Eventually these should be annotated with __attribute__((noreturn)).
120226633Sdim  // Because ObjC messages use dynamic dispatch, it is not generally safe to
121226633Sdim  // assume certain methods can't return. In cases where it is definitely valid,
122226633Sdim  // see if you can mark the methods noreturn or analyzer_noreturn instead of
123226633Sdim  // adding more explicit checks to this method.
124226633Sdim
125226633Sdim  if (!Msg.isInstanceMessage())
126226633Sdim    return;
127226633Sdim
128226633Sdim  const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface();
129226633Sdim  if (!Receiver)
130226633Sdim    return;
131226633Sdim  if (!Receiver->getIdentifier()->isStr("NSAssertionHandler"))
132226633Sdim    return;
133226633Sdim
134226633Sdim  Selector Sel = Msg.getSelector();
135226633Sdim  switch (Sel.getNumArgs()) {
136226633Sdim  default:
137226633Sdim    return;
138226633Sdim  case 4:
139226633Sdim    if (!isMultiArgSelector(&Sel, "handleFailureInFunction", "file",
140226633Sdim                            "lineNumber", "description", NULL))
141226633Sdim      return;
142226633Sdim    break;
143226633Sdim  case 5:
144226633Sdim    if (!isMultiArgSelector(&Sel, "handleFailureInMethod", "object", "file",
145226633Sdim                            "lineNumber", "description", NULL))
146226633Sdim      return;
147226633Sdim    break;
148226633Sdim  }
149226633Sdim
150226633Sdim  // If we got here, it's one of the messages we care about.
151226633Sdim  C.generateSink();
152226633Sdim}
153226633Sdim
154226633Sdim
155221345Sdimvoid ento::registerNoReturnFunctionChecker(CheckerManager &mgr) {
156221345Sdim  mgr.registerChecker<NoReturnFunctionChecker>();
157221345Sdim}
158