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