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