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 check::EndAnalysis> { 23 mutable std::unique_ptr<BugType> BT; 24 25 // These stats are per-analysis, not per-branch, hence they shouldn't 26 // stay inside the program state. 27 struct ReachedStat { 28 ExplodedNode *ExampleNode; 29 unsigned NumTimesReached; 30 }; 31 mutable llvm::DenseMap<const CallExpr *, ReachedStat> ReachedStats; 32 33 void analyzerEval(const CallExpr *CE, CheckerContext &C) const; 34 void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const; 35 void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const; 36 void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const; 37 void analyzerCrash(const CallExpr *CE, CheckerContext &C) const; 38 void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const; 39 void analyzerDump(const CallExpr *CE, CheckerContext &C) const; 40 void analyzerExplain(const CallExpr *CE, CheckerContext &C) const; 41 void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const; 42 void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const; 43 44 typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *, 45 CheckerContext &C) const; 46 47 ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C) const; 48 ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, 49 ExplodedNode *N) const; 50 51 public: 52 bool evalCall(const CallExpr *CE, CheckerContext &C) const; 53 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 54 void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, 55 ExprEngine &Eng) const; 56 }; 57 } 58 59 REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef) 60 61 bool ExprInspectionChecker::evalCall(const CallExpr *CE, 62 CheckerContext &C) const { 63 // These checks should have no effect on the surrounding environment 64 // (globals should not be invalidated, etc), hence the use of evalCall. 65 FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE)) 66 .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval) 67 .Case("clang_analyzer_checkInlined", 68 &ExprInspectionChecker::analyzerCheckInlined) 69 .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash) 70 .Case("clang_analyzer_warnIfReached", 71 &ExprInspectionChecker::analyzerWarnIfReached) 72 .Case("clang_analyzer_warnOnDeadSymbol", 73 &ExprInspectionChecker::analyzerWarnOnDeadSymbol) 74 .Case("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain) 75 .Case("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump) 76 .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent) 77 .Case("clang_analyzer_printState", 78 &ExprInspectionChecker::analyzerPrintState) 79 .Case("clang_analyzer_numTimesReached", 80 &ExprInspectionChecker::analyzerNumTimesReached) 81 .Default(nullptr); 82 83 if (!Handler) 84 return false; 85 86 (this->*Handler)(CE, C); 87 return true; 88 } 89 90 static const char *getArgumentValueString(const CallExpr *CE, 91 CheckerContext &C) { 92 if (CE->getNumArgs() == 0) 93 return "Missing assertion argument"; 94 95 ExplodedNode *N = C.getPredecessor(); 96 const LocationContext *LC = N->getLocationContext(); 97 ProgramStateRef State = N->getState(); 98 99 const Expr *Assertion = CE->getArg(0); 100 SVal AssertionVal = State->getSVal(Assertion, LC); 101 102 if (AssertionVal.isUndef()) 103 return "UNDEFINED"; 104 105 ProgramStateRef StTrue, StFalse; 106 std::tie(StTrue, StFalse) = 107 State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>()); 108 109 if (StTrue) { 110 if (StFalse) 111 return "UNKNOWN"; 112 else 113 return "TRUE"; 114 } else { 115 if (StFalse) 116 return "FALSE"; 117 else 118 llvm_unreachable("Invalid constraint; neither true or false."); 119 } 120 } 121 122 ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, 123 CheckerContext &C) const { 124 ExplodedNode *N = C.generateNonFatalErrorNode(); 125 reportBug(Msg, C.getBugReporter(), N); 126 return N; 127 } 128 129 ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, 130 BugReporter &BR, 131 ExplodedNode *N) const { 132 if (!N) 133 return nullptr; 134 135 if (!BT) 136 BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); 137 138 BR.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N)); 139 return N; 140 } 141 142 void ExprInspectionChecker::analyzerEval(const CallExpr *CE, 143 CheckerContext &C) const { 144 const LocationContext *LC = C.getPredecessor()->getLocationContext(); 145 146 // A specific instantiation of an inlined function may have more constrained 147 // values than can generally be assumed. Skip the check. 148 if (LC->getCurrentStackFrame()->getParent() != nullptr) 149 return; 150 151 reportBug(getArgumentValueString(CE, C), C); 152 } 153 154 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE, 155 CheckerContext &C) const { 156 reportBug("REACHABLE", C); 157 } 158 159 void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE, 160 CheckerContext &C) const { 161 ++ReachedStats[CE].NumTimesReached; 162 if (!ReachedStats[CE].ExampleNode) { 163 // Later, in checkEndAnalysis, we'd throw a report against it. 164 ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode(); 165 } 166 } 167 168 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, 169 CheckerContext &C) const { 170 const LocationContext *LC = C.getPredecessor()->getLocationContext(); 171 172 // An inlined function could conceivably also be analyzed as a top-level 173 // function. We ignore this case and only emit a message (TRUE or FALSE) 174 // when we are analyzing it as an inlined function. This means that 175 // clang_analyzer_checkInlined(true) should always print TRUE, but 176 // clang_analyzer_checkInlined(false) should never actually print anything. 177 if (LC->getCurrentStackFrame()->getParent() == nullptr) 178 return; 179 180 reportBug(getArgumentValueString(CE, C), C); 181 } 182 183 void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, 184 CheckerContext &C) const { 185 if (CE->getNumArgs() == 0) { 186 reportBug("Missing argument for explaining", C); 187 return; 188 } 189 190 SVal V = C.getSVal(CE->getArg(0)); 191 SValExplainer Ex(C.getASTContext()); 192 reportBug(Ex.Visit(V), C); 193 } 194 195 void ExprInspectionChecker::analyzerDump(const CallExpr *CE, 196 CheckerContext &C) const { 197 if (CE->getNumArgs() == 0) { 198 reportBug("Missing argument for dumping", C); 199 return; 200 } 201 202 SVal V = C.getSVal(CE->getArg(0)); 203 204 llvm::SmallString<32> Str; 205 llvm::raw_svector_ostream OS(Str); 206 V.dumpToStream(OS); 207 reportBug(OS.str(), C); 208 } 209 210 void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, 211 CheckerContext &C) const { 212 if (CE->getNumArgs() == 0) { 213 reportBug("Missing region for obtaining extent", C); 214 return; 215 } 216 217 auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion()); 218 if (!MR) { 219 reportBug("Obtaining extent of a non-region", C); 220 return; 221 } 222 223 ProgramStateRef State = C.getState(); 224 State = State->BindExpr(CE, C.getLocationContext(), 225 MR->getExtent(C.getSValBuilder())); 226 C.addTransition(State); 227 } 228 229 void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE, 230 CheckerContext &C) const { 231 C.getState()->dump(); 232 } 233 234 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, 235 CheckerContext &C) const { 236 if (CE->getNumArgs() == 0) 237 return; 238 SVal Val = C.getSVal(CE->getArg(0)); 239 SymbolRef Sym = Val.getAsSymbol(); 240 if (!Sym) 241 return; 242 243 ProgramStateRef State = C.getState(); 244 State = State->add<MarkedSymbols>(Sym); 245 C.addTransition(State); 246 } 247 248 void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper, 249 CheckerContext &C) const { 250 ProgramStateRef State = C.getState(); 251 const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>(); 252 ExplodedNode *N = C.getPredecessor(); 253 for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) { 254 SymbolRef Sym = *I; 255 if (!SymReaper.isDead(Sym)) 256 continue; 257 258 // The non-fatal error node should be the same for all reports. 259 if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C)) 260 N = BugNode; 261 State = State->remove<MarkedSymbols>(Sym); 262 } 263 C.addTransition(State, N); 264 } 265 266 void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, 267 ExprEngine &Eng) const { 268 for (auto Item: ReachedStats) { 269 unsigned NumTimesReached = Item.second.NumTimesReached; 270 ExplodedNode *N = Item.second.ExampleNode; 271 272 reportBug(std::to_string(NumTimesReached), BR, N); 273 } 274 } 275 276 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, 277 CheckerContext &C) const { 278 LLVM_BUILTIN_TRAP; 279 } 280 281 void ento::registerExprInspectionChecker(CheckerManager &Mgr) { 282 Mgr.registerChecker<ExprInspectionChecker>(); 283 } 284 285