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