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/Analysis/IssueHash.h" 10 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 11 #include "clang/StaticAnalyzer/Checkers/SValExplainer.h" 12 #include "clang/StaticAnalyzer/Checkers/Taint.h" 13 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 14 #include "clang/StaticAnalyzer/Core/Checker.h" 15 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 16 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 17 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" 18 #include "llvm/ADT/StringSwitch.h" 19 #include "llvm/Support/ScopedPrinter.h" 20 #include <optional> 21 22 using namespace clang; 23 using namespace ento; 24 25 namespace { 26 class ExprInspectionChecker 27 : public Checker<eval::Call, check::DeadSymbols, check::EndAnalysis> { 28 mutable std::unique_ptr<BugType> BT; 29 30 // These stats are per-analysis, not per-branch, hence they shouldn't 31 // stay inside the program state. 32 struct ReachedStat { 33 ExplodedNode *ExampleNode; 34 unsigned NumTimesReached; 35 }; 36 mutable llvm::DenseMap<const CallExpr *, ReachedStat> ReachedStats; 37 38 void analyzerEval(const CallExpr *CE, CheckerContext &C) const; 39 void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const; 40 void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const; 41 void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const; 42 void analyzerCrash(const CallExpr *CE, CheckerContext &C) const; 43 void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const; 44 void analyzerValue(const CallExpr *CE, CheckerContext &C) const; 45 void analyzerDumpSValType(const CallExpr *CE, CheckerContext &C) const; 46 void analyzerDump(const CallExpr *CE, CheckerContext &C) const; 47 void analyzerExplain(const CallExpr *CE, CheckerContext &C) const; 48 void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const; 49 void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const; 50 void analyzerDumpExtent(const CallExpr *CE, CheckerContext &C) const; 51 void analyzerDumpElementCount(const CallExpr *CE, CheckerContext &C) const; 52 void analyzerHashDump(const CallExpr *CE, CheckerContext &C) const; 53 void analyzerDenote(const CallExpr *CE, CheckerContext &C) const; 54 void analyzerExpress(const CallExpr *CE, CheckerContext &C) const; 55 void analyzerIsTainted(const CallExpr *CE, CheckerContext &C) const; 56 57 typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *, 58 CheckerContext &C) const; 59 60 // Optional parameter `ExprVal` for expression value to be marked interesting. 61 ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C, 62 Optional<SVal> ExprVal = std::nullopt) const; 63 ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N, 64 Optional<SVal> ExprVal = std::nullopt) const; 65 template <typename T> void printAndReport(CheckerContext &C, T What) const; 66 67 const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const; 68 const MemRegion *getArgRegion(const CallExpr *CE, CheckerContext &C) const; 69 70 public: 71 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 72 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 73 void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, 74 ExprEngine &Eng) const; 75 }; 76 } // namespace 77 78 REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef) 79 REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *) 80 81 bool ExprInspectionChecker::evalCall(const CallEvent &Call, 82 CheckerContext &C) const { 83 const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 84 if (!CE) 85 return false; 86 87 // These checks should have no effect on the surrounding environment 88 // (globals should not be invalidated, etc), hence the use of evalCall. 89 FnCheck Handler = 90 llvm::StringSwitch<FnCheck>(C.getCalleeName(CE)) 91 .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval) 92 .Case("clang_analyzer_checkInlined", 93 &ExprInspectionChecker::analyzerCheckInlined) 94 .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash) 95 .Case("clang_analyzer_warnIfReached", 96 &ExprInspectionChecker::analyzerWarnIfReached) 97 .Case("clang_analyzer_warnOnDeadSymbol", 98 &ExprInspectionChecker::analyzerWarnOnDeadSymbol) 99 .StartsWith("clang_analyzer_explain", 100 &ExprInspectionChecker::analyzerExplain) 101 .Case("clang_analyzer_dumpExtent", 102 &ExprInspectionChecker::analyzerDumpExtent) 103 .Case("clang_analyzer_dumpElementCount", 104 &ExprInspectionChecker::analyzerDumpElementCount) 105 .Case("clang_analyzer_value", &ExprInspectionChecker::analyzerValue) 106 .StartsWith("clang_analyzer_dumpSvalType", 107 &ExprInspectionChecker::analyzerDumpSValType) 108 .StartsWith("clang_analyzer_dump", 109 &ExprInspectionChecker::analyzerDump) 110 .Case("clang_analyzer_getExtent", 111 &ExprInspectionChecker::analyzerGetExtent) 112 .Case("clang_analyzer_printState", 113 &ExprInspectionChecker::analyzerPrintState) 114 .Case("clang_analyzer_numTimesReached", 115 &ExprInspectionChecker::analyzerNumTimesReached) 116 .Case("clang_analyzer_hashDump", 117 &ExprInspectionChecker::analyzerHashDump) 118 .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote) 119 .Case("clang_analyzer_express", // This also marks the argument as 120 // interesting. 121 &ExprInspectionChecker::analyzerExpress) 122 .StartsWith("clang_analyzer_isTainted", 123 &ExprInspectionChecker::analyzerIsTainted) 124 .Default(nullptr); 125 126 if (!Handler) 127 return false; 128 129 (this->*Handler)(CE, C); 130 return true; 131 } 132 133 static const char *getArgumentValueString(const CallExpr *CE, 134 CheckerContext &C) { 135 if (CE->getNumArgs() == 0) 136 return "Missing assertion argument"; 137 138 ExplodedNode *N = C.getPredecessor(); 139 const LocationContext *LC = N->getLocationContext(); 140 ProgramStateRef State = N->getState(); 141 142 const Expr *Assertion = CE->getArg(0); 143 SVal AssertionVal = State->getSVal(Assertion, LC); 144 145 if (AssertionVal.isUndef()) 146 return "UNDEFINED"; 147 148 ProgramStateRef StTrue, StFalse; 149 std::tie(StTrue, StFalse) = 150 State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>()); 151 152 if (StTrue) { 153 if (StFalse) 154 return "UNKNOWN"; 155 else 156 return "TRUE"; 157 } else { 158 if (StFalse) 159 return "FALSE"; 160 else 161 llvm_unreachable("Invalid constraint; neither true or false."); 162 } 163 } 164 165 ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, 166 CheckerContext &C, 167 Optional<SVal> ExprVal) const { 168 ExplodedNode *N = C.generateNonFatalErrorNode(); 169 reportBug(Msg, C.getBugReporter(), N, ExprVal); 170 return N; 171 } 172 173 ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, 174 BugReporter &BR, ExplodedNode *N, 175 Optional<SVal> ExprVal) const { 176 if (!N) 177 return nullptr; 178 179 if (!BT) 180 BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); 181 182 auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); 183 if (ExprVal) { 184 R->markInteresting(*ExprVal); 185 } 186 BR.emitReport(std::move(R)); 187 return N; 188 } 189 190 const Expr *ExprInspectionChecker::getArgExpr(const CallExpr *CE, 191 CheckerContext &C) const { 192 if (CE->getNumArgs() == 0) { 193 reportBug("Missing argument", C); 194 return nullptr; 195 } 196 return CE->getArg(0); 197 } 198 199 const MemRegion *ExprInspectionChecker::getArgRegion(const CallExpr *CE, 200 CheckerContext &C) const { 201 const Expr *Arg = getArgExpr(CE, C); 202 if (!Arg) 203 return nullptr; 204 205 const MemRegion *MR = C.getSVal(Arg).getAsRegion(); 206 if (!MR) { 207 reportBug("Cannot obtain the region", C); 208 return nullptr; 209 } 210 211 return MR; 212 } 213 214 void ExprInspectionChecker::analyzerEval(const CallExpr *CE, 215 CheckerContext &C) const { 216 const LocationContext *LC = C.getPredecessor()->getLocationContext(); 217 218 // A specific instantiation of an inlined function may have more constrained 219 // values than can generally be assumed. Skip the check. 220 if (LC->getStackFrame()->getParent() != nullptr) 221 return; 222 223 reportBug(getArgumentValueString(CE, C), C); 224 } 225 226 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE, 227 CheckerContext &C) const { 228 reportBug("REACHABLE", C); 229 } 230 231 void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE, 232 CheckerContext &C) const { 233 ++ReachedStats[CE].NumTimesReached; 234 if (!ReachedStats[CE].ExampleNode) { 235 // Later, in checkEndAnalysis, we'd throw a report against it. 236 ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode(); 237 } 238 } 239 240 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, 241 CheckerContext &C) const { 242 const LocationContext *LC = C.getPredecessor()->getLocationContext(); 243 244 // An inlined function could conceivably also be analyzed as a top-level 245 // function. We ignore this case and only emit a message (TRUE or FALSE) 246 // when we are analyzing it as an inlined function. This means that 247 // clang_analyzer_checkInlined(true) should always print TRUE, but 248 // clang_analyzer_checkInlined(false) should never actually print anything. 249 if (LC->getStackFrame()->getParent() == nullptr) 250 return; 251 252 reportBug(getArgumentValueString(CE, C), C); 253 } 254 255 void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, 256 CheckerContext &C) const { 257 const Expr *Arg = getArgExpr(CE, C); 258 if (!Arg) 259 return; 260 261 SVal V = C.getSVal(Arg); 262 SValExplainer Ex(C.getASTContext()); 263 reportBug(Ex.Visit(V), C); 264 } 265 266 static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, 267 const llvm::APSInt &I) { 268 Out << I.getBitWidth() << (I.isUnsigned() ? "u:" : "s:"); 269 Out << I; 270 } 271 272 static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, 273 SymbolRef Sym) { 274 C.getConstraintManager().printValue(Out, C.getState(), Sym); 275 } 276 277 static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, 278 SVal V) { 279 Out << V; 280 } 281 282 template <typename T> 283 void ExprInspectionChecker::printAndReport(CheckerContext &C, T What) const { 284 llvm::SmallString<64> Str; 285 llvm::raw_svector_ostream OS(Str); 286 printHelper(OS, C, What); 287 reportBug(OS.str(), C); 288 } 289 290 void ExprInspectionChecker::analyzerValue(const CallExpr *CE, 291 CheckerContext &C) const { 292 const Expr *Arg = getArgExpr(CE, C); 293 if (!Arg) 294 return; 295 296 SVal V = C.getSVal(Arg); 297 if (const SymbolRef Sym = V.getAsSymbol()) 298 printAndReport(C, Sym); 299 else if (const llvm::APSInt *I = V.getAsInteger()) 300 printAndReport(C, *I); 301 else 302 reportBug("n/a", C); 303 } 304 305 void ExprInspectionChecker::analyzerDumpSValType(const CallExpr *CE, 306 CheckerContext &C) const { 307 const Expr *Arg = getArgExpr(CE, C); 308 if (!Arg) 309 return; 310 311 QualType Ty = C.getSVal(Arg).getType(C.getASTContext()); 312 reportBug(Ty.getAsString(), C); 313 } 314 315 void ExprInspectionChecker::analyzerDump(const CallExpr *CE, 316 CheckerContext &C) const { 317 const Expr *Arg = getArgExpr(CE, C); 318 if (!Arg) 319 return; 320 321 SVal V = C.getSVal(Arg); 322 printAndReport(C, V); 323 } 324 325 void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, 326 CheckerContext &C) const { 327 const MemRegion *MR = getArgRegion(CE, C); 328 if (!MR) 329 return; 330 331 ProgramStateRef State = C.getState(); 332 DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, C.getSValBuilder()); 333 334 State = State->BindExpr(CE, C.getLocationContext(), Size); 335 C.addTransition(State); 336 } 337 338 void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE, 339 CheckerContext &C) const { 340 const MemRegion *MR = getArgRegion(CE, C); 341 if (!MR) 342 return; 343 344 DefinedOrUnknownSVal Size = 345 getDynamicExtent(C.getState(), MR, C.getSValBuilder()); 346 printAndReport(C, Size); 347 } 348 349 void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE, 350 CheckerContext &C) const { 351 const MemRegion *MR = getArgRegion(CE, C); 352 if (!MR) 353 return; 354 355 QualType ElementTy; 356 if (const auto *TVR = MR->getAs<TypedValueRegion>()) { 357 ElementTy = TVR->getValueType(); 358 } else { 359 ElementTy = MR->castAs<SymbolicRegion>()->getPointeeStaticType(); 360 } 361 362 assert(!ElementTy->isPointerType()); 363 364 DefinedOrUnknownSVal ElementCount = 365 getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy); 366 printAndReport(C, ElementCount); 367 } 368 369 void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE, 370 CheckerContext &C) const { 371 C.getState()->dump(); 372 } 373 374 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, 375 CheckerContext &C) const { 376 const Expr *Arg = getArgExpr(CE, C); 377 if (!Arg) 378 return; 379 380 SVal Val = C.getSVal(Arg); 381 SymbolRef Sym = Val.getAsSymbol(); 382 if (!Sym) 383 return; 384 385 ProgramStateRef State = C.getState(); 386 State = State->add<MarkedSymbols>(Sym); 387 C.addTransition(State); 388 } 389 390 void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper, 391 CheckerContext &C) const { 392 ProgramStateRef State = C.getState(); 393 const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>(); 394 ExplodedNode *N = C.getPredecessor(); 395 for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) { 396 SymbolRef Sym = *I; 397 if (!SymReaper.isDead(Sym)) 398 continue; 399 400 // The non-fatal error node should be the same for all reports. 401 if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C)) 402 N = BugNode; 403 State = State->remove<MarkedSymbols>(Sym); 404 } 405 406 for (auto I : State->get<DenotedSymbols>()) { 407 SymbolRef Sym = I.first; 408 if (!SymReaper.isLive(Sym)) 409 State = State->remove<DenotedSymbols>(Sym); 410 } 411 412 C.addTransition(State, N); 413 } 414 415 void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, 416 ExprEngine &Eng) const { 417 for (auto Item : ReachedStats) { 418 unsigned NumTimesReached = Item.second.NumTimesReached; 419 ExplodedNode *N = Item.second.ExampleNode; 420 421 reportBug(llvm::to_string(NumTimesReached), BR, N); 422 } 423 ReachedStats.clear(); 424 } 425 426 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, 427 CheckerContext &C) const { 428 LLVM_BUILTIN_TRAP; 429 } 430 431 void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE, 432 CheckerContext &C) const { 433 const LangOptions &Opts = C.getLangOpts(); 434 const SourceManager &SM = C.getSourceManager(); 435 FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM); 436 std::string HashContent = 437 getIssueString(FL, getCheckerName().getName(), "Category", 438 C.getLocationContext()->getDecl(), Opts); 439 440 reportBug(HashContent, C); 441 } 442 443 void ExprInspectionChecker::analyzerDenote(const CallExpr *CE, 444 CheckerContext &C) const { 445 if (CE->getNumArgs() < 2) { 446 reportBug("clang_analyzer_denote() requires a symbol and a string literal", 447 C); 448 return; 449 } 450 451 SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol(); 452 if (!Sym) { 453 reportBug("Not a symbol", C); 454 return; 455 } 456 457 const auto *E = dyn_cast<StringLiteral>(CE->getArg(1)->IgnoreParenCasts()); 458 if (!E) { 459 reportBug("Not a string literal", C); 460 return; 461 } 462 463 ProgramStateRef State = C.getState(); 464 465 C.addTransition(C.getState()->set<DenotedSymbols>(Sym, E)); 466 } 467 468 namespace { 469 class SymbolExpressor 470 : public SymExprVisitor<SymbolExpressor, Optional<std::string>> { 471 ProgramStateRef State; 472 473 public: 474 SymbolExpressor(ProgramStateRef State) : State(State) {} 475 476 Optional<std::string> lookup(const SymExpr *S) { 477 if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) { 478 const StringLiteral *SL = *SLPtr; 479 return std::string(SL->getBytes()); 480 } 481 return std::nullopt; 482 } 483 484 Optional<std::string> VisitSymExpr(const SymExpr *S) { return lookup(S); } 485 486 Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) { 487 if (Optional<std::string> Str = lookup(S)) 488 return Str; 489 if (Optional<std::string> Str = Visit(S->getLHS())) 490 return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " + 491 std::to_string(S->getRHS().getLimitedValue()) + 492 (S->getRHS().isUnsigned() ? "U" : "")) 493 .str(); 494 return std::nullopt; 495 } 496 497 Optional<std::string> VisitSymSymExpr(const SymSymExpr *S) { 498 if (Optional<std::string> Str = lookup(S)) 499 return Str; 500 if (Optional<std::string> Str1 = Visit(S->getLHS())) 501 if (Optional<std::string> Str2 = Visit(S->getRHS())) 502 return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + 503 " " + *Str2) 504 .str(); 505 return std::nullopt; 506 } 507 508 Optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) { 509 if (Optional<std::string> Str = lookup(S)) 510 return Str; 511 if (Optional<std::string> Str = Visit(S->getOperand())) 512 return (UnaryOperator::getOpcodeStr(S->getOpcode()) + *Str).str(); 513 return std::nullopt; 514 } 515 516 Optional<std::string> VisitSymbolCast(const SymbolCast *S) { 517 if (Optional<std::string> Str = lookup(S)) 518 return Str; 519 if (Optional<std::string> Str = Visit(S->getOperand())) 520 return (Twine("(") + S->getType().getAsString() + ")" + *Str).str(); 521 return std::nullopt; 522 } 523 }; 524 } // namespace 525 526 void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, 527 CheckerContext &C) const { 528 const Expr *Arg = getArgExpr(CE, C); 529 if (!Arg) 530 return; 531 532 SVal ArgVal = C.getSVal(CE->getArg(0)); 533 SymbolRef Sym = ArgVal.getAsSymbol(); 534 if (!Sym) { 535 reportBug("Not a symbol", C, ArgVal); 536 return; 537 } 538 539 SymbolExpressor V(C.getState()); 540 auto Str = V.Visit(Sym); 541 if (!Str) { 542 reportBug("Unable to express", C, ArgVal); 543 return; 544 } 545 546 reportBug(*Str, C, ArgVal); 547 } 548 549 void ExprInspectionChecker::analyzerIsTainted(const CallExpr *CE, 550 CheckerContext &C) const { 551 if (CE->getNumArgs() != 1) { 552 reportBug("clang_analyzer_isTainted() requires exactly one argument", C); 553 return; 554 } 555 const bool IsTainted = 556 taint::isTainted(C.getState(), CE->getArg(0), C.getLocationContext()); 557 reportBug(IsTainted ? "YES" : "NO", C); 558 } 559 560 void ento::registerExprInspectionChecker(CheckerManager &Mgr) { 561 Mgr.registerChecker<ExprInspectionChecker>(); 562 } 563 564 bool ento::shouldRegisterExprInspectionChecker(const CheckerManager &mgr) { 565 return true; 566 } 567