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 "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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 void analyzerDenote(const CallExpr *CE, CheckerContext &C) const; 47 void analyzerExpress(const CallExpr *CE, CheckerContext &C) const; 48 49 typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *, 50 CheckerContext &C) const; 51 52 ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C) const; 53 ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, 54 ExplodedNode *N) const; 55 56 public: 57 bool evalCall(const CallExpr *CE, CheckerContext &C) const; 58 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 59 void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, 60 ExprEngine &Eng) const; 61 }; 62 } 63 64 REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef) 65 REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *) 66 67 bool ExprInspectionChecker::evalCall(const CallExpr *CE, 68 CheckerContext &C) const { 69 // These checks should have no effect on the surrounding environment 70 // (globals should not be invalidated, etc), hence the use of evalCall. 71 FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE)) 72 .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval) 73 .Case("clang_analyzer_checkInlined", 74 &ExprInspectionChecker::analyzerCheckInlined) 75 .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash) 76 .Case("clang_analyzer_warnIfReached", 77 &ExprInspectionChecker::analyzerWarnIfReached) 78 .Case("clang_analyzer_warnOnDeadSymbol", 79 &ExprInspectionChecker::analyzerWarnOnDeadSymbol) 80 .StartsWith("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain) 81 .StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump) 82 .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent) 83 .Case("clang_analyzer_printState", 84 &ExprInspectionChecker::analyzerPrintState) 85 .Case("clang_analyzer_numTimesReached", 86 &ExprInspectionChecker::analyzerNumTimesReached) 87 .Case("clang_analyzer_hashDump", &ExprInspectionChecker::analyzerHashDump) 88 .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote) 89 .Case("clang_analyzer_express", &ExprInspectionChecker::analyzerExpress) 90 .Default(nullptr); 91 92 if (!Handler) 93 return false; 94 95 (this->*Handler)(CE, C); 96 return true; 97 } 98 99 static const char *getArgumentValueString(const CallExpr *CE, 100 CheckerContext &C) { 101 if (CE->getNumArgs() == 0) 102 return "Missing assertion argument"; 103 104 ExplodedNode *N = C.getPredecessor(); 105 const LocationContext *LC = N->getLocationContext(); 106 ProgramStateRef State = N->getState(); 107 108 const Expr *Assertion = CE->getArg(0); 109 SVal AssertionVal = State->getSVal(Assertion, LC); 110 111 if (AssertionVal.isUndef()) 112 return "UNDEFINED"; 113 114 ProgramStateRef StTrue, StFalse; 115 std::tie(StTrue, StFalse) = 116 State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>()); 117 118 if (StTrue) { 119 if (StFalse) 120 return "UNKNOWN"; 121 else 122 return "TRUE"; 123 } else { 124 if (StFalse) 125 return "FALSE"; 126 else 127 llvm_unreachable("Invalid constraint; neither true or false."); 128 } 129 } 130 131 ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, 132 CheckerContext &C) const { 133 ExplodedNode *N = C.generateNonFatalErrorNode(); 134 reportBug(Msg, C.getBugReporter(), N); 135 return N; 136 } 137 138 ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, 139 BugReporter &BR, 140 ExplodedNode *N) const { 141 if (!N) 142 return nullptr; 143 144 if (!BT) 145 BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); 146 147 BR.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N)); 148 return N; 149 } 150 151 void ExprInspectionChecker::analyzerEval(const CallExpr *CE, 152 CheckerContext &C) const { 153 const LocationContext *LC = C.getPredecessor()->getLocationContext(); 154 155 // A specific instantiation of an inlined function may have more constrained 156 // values than can generally be assumed. Skip the check. 157 if (LC->getStackFrame()->getParent() != nullptr) 158 return; 159 160 reportBug(getArgumentValueString(CE, C), C); 161 } 162 163 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE, 164 CheckerContext &C) const { 165 reportBug("REACHABLE", C); 166 } 167 168 void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE, 169 CheckerContext &C) const { 170 ++ReachedStats[CE].NumTimesReached; 171 if (!ReachedStats[CE].ExampleNode) { 172 // Later, in checkEndAnalysis, we'd throw a report against it. 173 ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode(); 174 } 175 } 176 177 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, 178 CheckerContext &C) const { 179 const LocationContext *LC = C.getPredecessor()->getLocationContext(); 180 181 // An inlined function could conceivably also be analyzed as a top-level 182 // function. We ignore this case and only emit a message (TRUE or FALSE) 183 // when we are analyzing it as an inlined function. This means that 184 // clang_analyzer_checkInlined(true) should always print TRUE, but 185 // clang_analyzer_checkInlined(false) should never actually print anything. 186 if (LC->getStackFrame()->getParent() == nullptr) 187 return; 188 189 reportBug(getArgumentValueString(CE, C), C); 190 } 191 192 void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, 193 CheckerContext &C) const { 194 if (CE->getNumArgs() == 0) { 195 reportBug("Missing argument for explaining", C); 196 return; 197 } 198 199 SVal V = C.getSVal(CE->getArg(0)); 200 SValExplainer Ex(C.getASTContext()); 201 reportBug(Ex.Visit(V), C); 202 } 203 204 void ExprInspectionChecker::analyzerDump(const CallExpr *CE, 205 CheckerContext &C) const { 206 if (CE->getNumArgs() == 0) { 207 reportBug("Missing argument for dumping", C); 208 return; 209 } 210 211 SVal V = C.getSVal(CE->getArg(0)); 212 213 llvm::SmallString<32> Str; 214 llvm::raw_svector_ostream OS(Str); 215 V.dumpToStream(OS); 216 reportBug(OS.str(), C); 217 } 218 219 void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, 220 CheckerContext &C) const { 221 if (CE->getNumArgs() == 0) { 222 reportBug("Missing region for obtaining extent", C); 223 return; 224 } 225 226 auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion()); 227 if (!MR) { 228 reportBug("Obtaining extent of a non-region", C); 229 return; 230 } 231 232 ProgramStateRef State = C.getState(); 233 State = State->BindExpr(CE, C.getLocationContext(), 234 MR->getExtent(C.getSValBuilder())); 235 C.addTransition(State); 236 } 237 238 void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE, 239 CheckerContext &C) const { 240 C.getState()->dump(); 241 } 242 243 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, 244 CheckerContext &C) const { 245 if (CE->getNumArgs() == 0) 246 return; 247 SVal Val = C.getSVal(CE->getArg(0)); 248 SymbolRef Sym = Val.getAsSymbol(); 249 if (!Sym) 250 return; 251 252 ProgramStateRef State = C.getState(); 253 State = State->add<MarkedSymbols>(Sym); 254 C.addTransition(State); 255 } 256 257 void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper, 258 CheckerContext &C) const { 259 ProgramStateRef State = C.getState(); 260 const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>(); 261 ExplodedNode *N = C.getPredecessor(); 262 for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) { 263 SymbolRef Sym = *I; 264 if (!SymReaper.isDead(Sym)) 265 continue; 266 267 // The non-fatal error node should be the same for all reports. 268 if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C)) 269 N = BugNode; 270 State = State->remove<MarkedSymbols>(Sym); 271 } 272 273 for (auto I : State->get<DenotedSymbols>()) { 274 SymbolRef Sym = I.first; 275 if (!SymReaper.isLive(Sym)) 276 State = State->remove<DenotedSymbols>(Sym); 277 } 278 279 C.addTransition(State, N); 280 } 281 282 void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, 283 ExprEngine &Eng) const { 284 for (auto Item: ReachedStats) { 285 unsigned NumTimesReached = Item.second.NumTimesReached; 286 ExplodedNode *N = Item.second.ExampleNode; 287 288 reportBug(llvm::to_string(NumTimesReached), BR, N); 289 } 290 ReachedStats.clear(); 291 } 292 293 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, 294 CheckerContext &C) const { 295 LLVM_BUILTIN_TRAP; 296 } 297 298 void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE, 299 CheckerContext &C) const { 300 const LangOptions &Opts = C.getLangOpts(); 301 const SourceManager &SM = C.getSourceManager(); 302 FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM); 303 std::string HashContent = 304 GetIssueString(SM, FL, getCheckName().getName(), "Category", 305 C.getLocationContext()->getDecl(), Opts); 306 307 reportBug(HashContent, C); 308 } 309 310 void ExprInspectionChecker::analyzerDenote(const CallExpr *CE, 311 CheckerContext &C) const { 312 if (CE->getNumArgs() < 2) { 313 reportBug("clang_analyzer_denote() requires a symbol and a string literal", 314 C); 315 return; 316 } 317 318 SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol(); 319 if (!Sym) { 320 reportBug("Not a symbol", C); 321 return; 322 } 323 324 const auto *E = dyn_cast<StringLiteral>(CE->getArg(1)->IgnoreParenCasts()); 325 if (!E) { 326 reportBug("Not a string literal", C); 327 return; 328 } 329 330 ProgramStateRef State = C.getState(); 331 332 C.addTransition(C.getState()->set<DenotedSymbols>(Sym, E)); 333 } 334 335 namespace { 336 class SymbolExpressor 337 : public SymExprVisitor<SymbolExpressor, Optional<std::string>> { 338 ProgramStateRef State; 339 340 public: 341 SymbolExpressor(ProgramStateRef State) : State(State) {} 342 343 Optional<std::string> lookup(const SymExpr *S) { 344 if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) { 345 const StringLiteral *SL = *SLPtr; 346 return std::string(SL->getBytes()); 347 } 348 return None; 349 } 350 351 Optional<std::string> VisitSymExpr(const SymExpr *S) { 352 return lookup(S); 353 } 354 355 Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) { 356 if (Optional<std::string> Str = lookup(S)) 357 return Str; 358 if (Optional<std::string> Str = Visit(S->getLHS())) 359 return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " + 360 std::to_string(S->getRHS().getLimitedValue()) + 361 (S->getRHS().isUnsigned() ? "U" : "")) 362 .str(); 363 return None; 364 } 365 366 Optional<std::string> VisitSymSymExpr(const SymSymExpr *S) { 367 if (Optional<std::string> Str = lookup(S)) 368 return Str; 369 if (Optional<std::string> Str1 = Visit(S->getLHS())) 370 if (Optional<std::string> Str2 = Visit(S->getRHS())) 371 return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + 372 " " + *Str2).str(); 373 return None; 374 } 375 376 Optional<std::string> VisitSymbolCast(const SymbolCast *S) { 377 if (Optional<std::string> Str = lookup(S)) 378 return Str; 379 if (Optional<std::string> Str = Visit(S->getOperand())) 380 return (Twine("(") + S->getType().getAsString() + ")" + *Str).str(); 381 return None; 382 } 383 }; 384 } // namespace 385 386 void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, 387 CheckerContext &C) const { 388 if (CE->getNumArgs() == 0) { 389 reportBug("clang_analyzer_express() requires a symbol", C); 390 return; 391 } 392 393 SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol(); 394 if (!Sym) { 395 reportBug("Not a symbol", C); 396 return; 397 } 398 399 SymbolExpressor V(C.getState()); 400 auto Str = V.Visit(Sym); 401 if (!Str) { 402 reportBug("Unable to express", C); 403 return; 404 } 405 406 reportBug(*Str, C); 407 } 408 409 void ento::registerExprInspectionChecker(CheckerManager &Mgr) { 410 Mgr.registerChecker<ExprInspectionChecker>(); 411 } 412