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 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 31 public: 32 bool evalCall(const CallExpr *CE, CheckerContext &C) const; 33 }; 34 } 35 36 bool 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 55 static 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 87 void ExprInspectionChecker::analyzerEval(const CallExpr *CE, 88 CheckerContext &C) const { 89 const LocationContext *LC = C.getPredecessor()->getLocationContext(); 90 91 // A specific instantiation of an inlined function may have more constrained 92 // values than can generally be assumed. Skip the check. 93 if (LC->getCurrentStackFrame()->getParent() != nullptr) 94 return; 95 96 if (!BT) 97 BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); 98 99 ExplodedNode *N = C.generateNonFatalErrorNode(); 100 if (!N) 101 return; 102 C.emitReport( 103 llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N)); 104 } 105 106 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE, 107 CheckerContext &C) const { 108 109 if (!BT) 110 BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); 111 112 ExplodedNode *N = C.generateNonFatalErrorNode(); 113 if (!N) 114 return; 115 C.emitReport(llvm::make_unique<BugReport>(*BT, "REACHABLE", N)); 116 } 117 118 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, 119 CheckerContext &C) const { 120 const LocationContext *LC = C.getPredecessor()->getLocationContext(); 121 122 // An inlined function could conceivably also be analyzed as a top-level 123 // function. We ignore this case and only emit a message (TRUE or FALSE) 124 // when we are analyzing it as an inlined function. This means that 125 // clang_analyzer_checkInlined(true) should always print TRUE, but 126 // clang_analyzer_checkInlined(false) should never actually print anything. 127 if (LC->getCurrentStackFrame()->getParent() == nullptr) 128 return; 129 130 if (!BT) 131 BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); 132 133 ExplodedNode *N = C.generateNonFatalErrorNode(); 134 if (!N) 135 return; 136 C.emitReport( 137 llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N)); 138 } 139 140 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, 141 CheckerContext &C) const { 142 LLVM_BUILTIN_TRAP; 143 } 144 145 void ento::registerExprInspectionChecker(CheckerManager &Mgr) { 146 Mgr.registerChecker<ExprInspectionChecker>(); 147 } 148 149