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 "llvm/ADT/StringSwitch.h"
     15 
     16 using namespace clang;
     17 using namespace ento;
     18 
     19 namespace {
     20 class ExprInspectionChecker : public Checker< eval::Call > {
     21   mutable OwningPtr<BugType> BT;
     22 
     23   void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
     24   void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
     25   void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
     26 
     27   typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
     28                                                  CheckerContext &C) const;
     29 
     30 public:
     31   bool evalCall(const CallExpr *CE, CheckerContext &C) const;
     32 };
     33 }
     34 
     35 bool ExprInspectionChecker::evalCall(const CallExpr *CE,
     36                                      CheckerContext &C) const {
     37   // These checks should have no effect on the surrounding environment
     38   // (globals should not be invalidated, etc), hence the use of evalCall.
     39   FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
     40     .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
     41     .Case("clang_analyzer_checkInlined",
     42           &ExprInspectionChecker::analyzerCheckInlined)
     43     .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
     44     .Default(0);
     45 
     46   if (!Handler)
     47     return false;
     48 
     49   (this->*Handler)(CE, C);
     50   return true;
     51 }
     52 
     53 static const char *getArgumentValueString(const CallExpr *CE,
     54                                           CheckerContext &C) {
     55   if (CE->getNumArgs() == 0)
     56     return "Missing assertion argument";
     57 
     58   ExplodedNode *N = C.getPredecessor();
     59   const LocationContext *LC = N->getLocationContext();
     60   ProgramStateRef State = N->getState();
     61 
     62   const Expr *Assertion = CE->getArg(0);
     63   SVal AssertionVal = State->getSVal(Assertion, LC);
     64 
     65   if (AssertionVal.isUndef())
     66     return "UNDEFINED";
     67 
     68   ProgramStateRef StTrue, StFalse;
     69   llvm::tie(StTrue, StFalse) =
     70     State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
     71 
     72   if (StTrue) {
     73     if (StFalse)
     74       return "UNKNOWN";
     75     else
     76       return "TRUE";
     77   } else {
     78     if (StFalse)
     79       return "FALSE";
     80     else
     81       llvm_unreachable("Invalid constraint; neither true or false.");
     82   }
     83 }
     84 
     85 void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
     86                                          CheckerContext &C) const {
     87   ExplodedNode *N = C.getPredecessor();
     88   const LocationContext *LC = N->getLocationContext();
     89 
     90   // A specific instantiation of an inlined function may have more constrained
     91   // values than can generally be assumed. Skip the check.
     92   if (LC->getCurrentStackFrame()->getParent() != 0)
     93     return;
     94 
     95   if (!BT)
     96     BT.reset(new BugType("Checking analyzer assumptions", "debug"));
     97 
     98   BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
     99   C.emitReport(R);
    100 }
    101 
    102 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
    103                                                  CheckerContext &C) const {
    104   ExplodedNode *N = C.getPredecessor();
    105   const LocationContext *LC = N->getLocationContext();
    106 
    107   // An inlined function could conceivably also be analyzed as a top-level
    108   // function. We ignore this case and only emit a message (TRUE or FALSE)
    109   // when we are analyzing it as an inlined function. This means that
    110   // clang_analyzer_checkInlined(true) should always print TRUE, but
    111   // clang_analyzer_checkInlined(false) should never actually print anything.
    112   if (LC->getCurrentStackFrame()->getParent() == 0)
    113     return;
    114 
    115   if (!BT)
    116     BT.reset(new BugType("Checking analyzer assumptions", "debug"));
    117 
    118   BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
    119   C.emitReport(R);
    120 }
    121 
    122 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
    123                                           CheckerContext &C) const {
    124   LLVM_BUILTIN_TRAP;
    125 }
    126 
    127 void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
    128   Mgr.registerChecker<ExprInspectionChecker>();
    129 }
    130 
    131