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