Home | History | Annotate | Download | only in Checkers
      1 //==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "ClangSACheckers.h"
     11 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
     12 #include "clang/StaticAnalyzer/Core/Checker.h"
     13 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
     14 #include "clang/StaticAnalyzer/Checkers/SValExplainer.h"
     15 #include "llvm/ADT/StringSwitch.h"
     16 
     17 using namespace clang;
     18 using namespace ento;
     19 
     20 namespace {
     21 class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols> {
     22   mutable std::unique_ptr<BugType> BT;
     23 
     24   void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
     25   void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
     26   void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
     27   void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
     28   void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
     29   void analyzerExplain(const CallExpr *CE, CheckerContext &C) const;
     30   void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const;
     31 
     32   typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
     33                                                  CheckerContext &C) const;
     34 
     35   void reportBug(llvm::StringRef Msg, CheckerContext &C) const;
     36 
     37 public:
     38   bool evalCall(const CallExpr *CE, CheckerContext &C) const;
     39   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
     40 };
     41 }
     42 
     43 REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef)
     44 
     45 bool ExprInspectionChecker::evalCall(const CallExpr *CE,
     46                                      CheckerContext &C) const {
     47   // These checks should have no effect on the surrounding environment
     48   // (globals should not be invalidated, etc), hence the use of evalCall.
     49   FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
     50     .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
     51     .Case("clang_analyzer_checkInlined",
     52           &ExprInspectionChecker::analyzerCheckInlined)
     53     .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
     54     .Case("clang_analyzer_warnIfReached",
     55           &ExprInspectionChecker::analyzerWarnIfReached)
     56     .Case("clang_analyzer_warnOnDeadSymbol",
     57           &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
     58     .Case("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain)
     59     .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent)
     60     .Default(nullptr);
     61 
     62   if (!Handler)
     63     return false;
     64 
     65   (this->*Handler)(CE, C);
     66   return true;
     67 }
     68 
     69 static const char *getArgumentValueString(const CallExpr *CE,
     70                                           CheckerContext &C) {
     71   if (CE->getNumArgs() == 0)
     72     return "Missing assertion argument";
     73 
     74   ExplodedNode *N = C.getPredecessor();
     75   const LocationContext *LC = N->getLocationContext();
     76   ProgramStateRef State = N->getState();
     77 
     78   const Expr *Assertion = CE->getArg(0);
     79   SVal AssertionVal = State->getSVal(Assertion, LC);
     80 
     81   if (AssertionVal.isUndef())
     82     return "UNDEFINED";
     83 
     84   ProgramStateRef StTrue, StFalse;
     85   std::tie(StTrue, StFalse) =
     86     State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
     87 
     88   if (StTrue) {
     89     if (StFalse)
     90       return "UNKNOWN";
     91     else
     92       return "TRUE";
     93   } else {
     94     if (StFalse)
     95       return "FALSE";
     96     else
     97       llvm_unreachable("Invalid constraint; neither true or false.");
     98   }
     99 }
    100 
    101 void ExprInspectionChecker::reportBug(llvm::StringRef Msg,
    102                                       CheckerContext &C) const {
    103   if (!BT)
    104     BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
    105 
    106   ExplodedNode *N = C.generateNonFatalErrorNode();
    107   if (!N)
    108     return;
    109 
    110   C.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N));
    111 }
    112 
    113 void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
    114                                          CheckerContext &C) const {
    115   const LocationContext *LC = C.getPredecessor()->getLocationContext();
    116 
    117   // A specific instantiation of an inlined function may have more constrained
    118   // values than can generally be assumed. Skip the check.
    119   if (LC->getCurrentStackFrame()->getParent() != nullptr)
    120     return;
    121 
    122   reportBug(getArgumentValueString(CE, C), C);
    123 }
    124 
    125 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
    126                                                   CheckerContext &C) const {
    127   reportBug("REACHABLE", C);
    128 }
    129 
    130 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
    131                                                  CheckerContext &C) const {
    132   const LocationContext *LC = C.getPredecessor()->getLocationContext();
    133 
    134   // An inlined function could conceivably also be analyzed as a top-level
    135   // function. We ignore this case and only emit a message (TRUE or FALSE)
    136   // when we are analyzing it as an inlined function. This means that
    137   // clang_analyzer_checkInlined(true) should always print TRUE, but
    138   // clang_analyzer_checkInlined(false) should never actually print anything.
    139   if (LC->getCurrentStackFrame()->getParent() == nullptr)
    140     return;
    141 
    142   reportBug(getArgumentValueString(CE, C), C);
    143 }
    144 
    145 void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
    146                                             CheckerContext &C) const {
    147   if (CE->getNumArgs() == 0)
    148     reportBug("Missing argument for explaining", C);
    149 
    150   SVal V = C.getSVal(CE->getArg(0));
    151   SValExplainer Ex(C.getASTContext());
    152   reportBug(Ex.Visit(V), C);
    153 }
    154 
    155 void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
    156                                               CheckerContext &C) const {
    157   if (CE->getNumArgs() == 0)
    158     reportBug("Missing region for obtaining extent", C);
    159 
    160   auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion());
    161   if (!MR)
    162     reportBug("Obtaining extent of a non-region", C);
    163 
    164   ProgramStateRef State = C.getState();
    165   State = State->BindExpr(CE, C.getLocationContext(),
    166                           MR->getExtent(C.getSValBuilder()));
    167   C.addTransition(State);
    168 }
    169 
    170 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
    171                                                      CheckerContext &C) const {
    172   if (CE->getNumArgs() == 0)
    173     return;
    174   SVal Val = C.getSVal(CE->getArg(0));
    175   SymbolRef Sym = Val.getAsSymbol();
    176   if (!Sym)
    177     return;
    178 
    179   ProgramStateRef State = C.getState();
    180   State = State->add<MarkedSymbols>(Sym);
    181   C.addTransition(State);
    182 }
    183 
    184 void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
    185                                              CheckerContext &C) const {
    186   ProgramStateRef State = C.getState();
    187   const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
    188   for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
    189     SymbolRef Sym = *I;
    190     if (!SymReaper.isDead(Sym))
    191       continue;
    192 
    193     reportBug("SYMBOL DEAD", C);
    194     State = State->remove<MarkedSymbols>(Sym);
    195   }
    196   C.addTransition(State);
    197 }
    198 
    199 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
    200                                           CheckerContext &C) const {
    201   LLVM_BUILTIN_TRAP;
    202 }
    203 
    204 void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
    205   Mgr.registerChecker<ExprInspectionChecker>();
    206 }
    207 
    208