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 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 104 void 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 115 void 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 135 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, 136 CheckerContext &C) const { 137 LLVM_BUILTIN_TRAP; 138 } 139 140 void ento::registerExprInspectionChecker(CheckerManager &Mgr) { 141 Mgr.registerChecker<ExprInspectionChecker>(); 142 } 143 144