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