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 std::optional<SVal> ExprVal = std::nullopt) const; 63 ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N, 64 std::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 * 166 ExprInspectionChecker::reportBug(llvm::StringRef Msg, CheckerContext &C, 167 std::optional<SVal> ExprVal) const { 168 ExplodedNode *N = C.generateNonFatalErrorNode(); 169 reportBug(Msg, C.getBugReporter(), N, ExprVal); 170 return N; 171 } 172 173 ExplodedNode * 174 ExprInspectionChecker::reportBug(llvm::StringRef Msg, BugReporter &BR, 175 ExplodedNode *N, 176 std::optional<SVal> ExprVal) const { 177 if (!N) 178 return nullptr; 179 180 if (!BT) 181 BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); 182 183 auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); 184 if (ExprVal) { 185 R->markInteresting(*ExprVal); 186 } 187 BR.emitReport(std::move(R)); 188 return N; 189 } 190 191 const Expr *ExprInspectionChecker::getArgExpr(const CallExpr *CE, 192 CheckerContext &C) const { 193 if (CE->getNumArgs() == 0) { 194 reportBug("Missing argument", C); 195 return nullptr; 196 } 197 return CE->getArg(0); 198 } 199 200 const MemRegion *ExprInspectionChecker::getArgRegion(const CallExpr *CE, 201 CheckerContext &C) const { 202 const Expr *Arg = getArgExpr(CE, C); 203 if (!Arg) 204 return nullptr; 205 206 const MemRegion *MR = C.getSVal(Arg).getAsRegion(); 207 if (!MR) { 208 reportBug("Cannot obtain the region", C); 209 return nullptr; 210 } 211 212 return MR; 213 } 214 215 void ExprInspectionChecker::analyzerEval(const CallExpr *CE, 216 CheckerContext &C) const { 217 const LocationContext *LC = C.getPredecessor()->getLocationContext(); 218 219 // A specific instantiation of an inlined function may have more constrained 220 // values than can generally be assumed. Skip the check. 221 if (LC->getStackFrame()->getParent() != nullptr) 222 return; 223 224 reportBug(getArgumentValueString(CE, C), C); 225 } 226 227 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE, 228 CheckerContext &C) const { 229 reportBug("REACHABLE", C); 230 } 231 232 void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE, 233 CheckerContext &C) const { 234 ++ReachedStats[CE].NumTimesReached; 235 if (!ReachedStats[CE].ExampleNode) { 236 // Later, in checkEndAnalysis, we'd throw a report against it. 237 ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode(); 238 } 239 } 240 241 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, 242 CheckerContext &C) const { 243 const LocationContext *LC = C.getPredecessor()->getLocationContext(); 244 245 // An inlined function could conceivably also be analyzed as a top-level 246 // function. We ignore this case and only emit a message (TRUE or FALSE) 247 // when we are analyzing it as an inlined function. This means that 248 // clang_analyzer_checkInlined(true) should always print TRUE, but 249 // clang_analyzer_checkInlined(false) should never actually print anything. 250 if (LC->getStackFrame()->getParent() == nullptr) 251 return; 252 253 reportBug(getArgumentValueString(CE, C), C); 254 } 255 256 void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, 257 CheckerContext &C) const { 258 const Expr *Arg = getArgExpr(CE, C); 259 if (!Arg) 260 return; 261 262 SVal V = C.getSVal(Arg); 263 SValExplainer Ex(C.getASTContext()); 264 reportBug(Ex.Visit(V), C); 265 } 266 267 static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, 268 const llvm::APSInt &I) { 269 Out << I.getBitWidth() << (I.isUnsigned() ? "u:" : "s:"); 270 Out << I; 271 } 272 273 static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, 274 SymbolRef Sym) { 275 C.getConstraintManager().printValue(Out, C.getState(), Sym); 276 } 277 278 static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, 279 SVal V) { 280 Out << V; 281 } 282 283 template <typename T> 284 void ExprInspectionChecker::printAndReport(CheckerContext &C, T What) const { 285 llvm::SmallString<64> Str; 286 llvm::raw_svector_ostream OS(Str); 287 printHelper(OS, C, What); 288 reportBug(OS.str(), C); 289 } 290 291 void ExprInspectionChecker::analyzerValue(const CallExpr *CE, 292 CheckerContext &C) const { 293 const Expr *Arg = getArgExpr(CE, C); 294 if (!Arg) 295 return; 296 297 SVal V = C.getSVal(Arg); 298 if (const SymbolRef Sym = V.getAsSymbol()) 299 printAndReport(C, Sym); 300 else if (const llvm::APSInt *I = V.getAsInteger()) 301 printAndReport(C, *I); 302 else 303 reportBug("n/a", C); 304 } 305 306 void ExprInspectionChecker::analyzerDumpSValType(const CallExpr *CE, 307 CheckerContext &C) const { 308 const Expr *Arg = getArgExpr(CE, C); 309 if (!Arg) 310 return; 311 312 QualType Ty = C.getSVal(Arg).getType(C.getASTContext()); 313 reportBug(Ty.getAsString(), C); 314 } 315 316 void ExprInspectionChecker::analyzerDump(const CallExpr *CE, 317 CheckerContext &C) const { 318 const Expr *Arg = getArgExpr(CE, C); 319 if (!Arg) 320 return; 321 322 SVal V = C.getSVal(Arg); 323 printAndReport(C, V); 324 } 325 326 void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, 327 CheckerContext &C) const { 328 const MemRegion *MR = getArgRegion(CE, C); 329 if (!MR) 330 return; 331 332 ProgramStateRef State = C.getState(); 333 DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, C.getSValBuilder()); 334 335 State = State->BindExpr(CE, C.getLocationContext(), Size); 336 C.addTransition(State); 337 } 338 339 void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE, 340 CheckerContext &C) const { 341 const MemRegion *MR = getArgRegion(CE, C); 342 if (!MR) 343 return; 344 345 DefinedOrUnknownSVal Size = 346 getDynamicExtent(C.getState(), MR, C.getSValBuilder()); 347 printAndReport(C, Size); 348 } 349 350 void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE, 351 CheckerContext &C) const { 352 const MemRegion *MR = getArgRegion(CE, C); 353 if (!MR) 354 return; 355 356 QualType ElementTy; 357 if (const auto *TVR = MR->getAs<TypedValueRegion>()) { 358 ElementTy = TVR->getValueType(); 359 } else { 360 ElementTy = MR->castAs<SymbolicRegion>()->getPointeeStaticType(); 361 } 362 363 assert(!ElementTy->isPointerType()); 364 365 DefinedOrUnknownSVal ElementCount = 366 getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy); 367 printAndReport(C, ElementCount); 368 } 369 370 void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE, 371 CheckerContext &C) const { 372 C.getState()->dump(); 373 } 374 375 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, 376 CheckerContext &C) const { 377 const Expr *Arg = getArgExpr(CE, C); 378 if (!Arg) 379 return; 380 381 SVal Val = C.getSVal(Arg); 382 SymbolRef Sym = Val.getAsSymbol(); 383 if (!Sym) 384 return; 385 386 ProgramStateRef State = C.getState(); 387 State = State->add<MarkedSymbols>(Sym); 388 C.addTransition(State); 389 } 390 391 void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper, 392 CheckerContext &C) const { 393 ProgramStateRef State = C.getState(); 394 const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>(); 395 ExplodedNode *N = C.getPredecessor(); 396 for (SymbolRef Sym : Syms) { 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, std::optional<std::string>> { 471 ProgramStateRef State; 472 473 public: 474 SymbolExpressor(ProgramStateRef State) : State(State) {} 475 476 std::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 std::optional<std::string> VisitSymExpr(const SymExpr *S) { 485 return lookup(S); 486 } 487 488 std::optional<std::string> VisitSymIntExpr(const SymIntExpr *S) { 489 if (std::optional<std::string> Str = lookup(S)) 490 return Str; 491 if (std::optional<std::string> Str = Visit(S->getLHS())) 492 return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " + 493 std::to_string(S->getRHS().getLimitedValue()) + 494 (S->getRHS().isUnsigned() ? "U" : "")) 495 .str(); 496 return std::nullopt; 497 } 498 499 std::optional<std::string> VisitSymSymExpr(const SymSymExpr *S) { 500 if (std::optional<std::string> Str = lookup(S)) 501 return Str; 502 if (std::optional<std::string> Str1 = Visit(S->getLHS())) 503 if (std::optional<std::string> Str2 = Visit(S->getRHS())) 504 return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + 505 " " + *Str2) 506 .str(); 507 return std::nullopt; 508 } 509 510 std::optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) { 511 if (std::optional<std::string> Str = lookup(S)) 512 return Str; 513 if (std::optional<std::string> Str = Visit(S->getOperand())) 514 return (UnaryOperator::getOpcodeStr(S->getOpcode()) + *Str).str(); 515 return std::nullopt; 516 } 517 518 std::optional<std::string> VisitSymbolCast(const SymbolCast *S) { 519 if (std::optional<std::string> Str = lookup(S)) 520 return Str; 521 if (std::optional<std::string> Str = Visit(S->getOperand())) 522 return (Twine("(") + S->getType().getAsString() + ")" + *Str).str(); 523 return std::nullopt; 524 } 525 }; 526 } // namespace 527 528 void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, 529 CheckerContext &C) const { 530 const Expr *Arg = getArgExpr(CE, C); 531 if (!Arg) 532 return; 533 534 SVal ArgVal = C.getSVal(CE->getArg(0)); 535 SymbolRef Sym = ArgVal.getAsSymbol(); 536 if (!Sym) { 537 reportBug("Not a symbol", C, ArgVal); 538 return; 539 } 540 541 SymbolExpressor V(C.getState()); 542 auto Str = V.Visit(Sym); 543 if (!Str) { 544 reportBug("Unable to express", C, ArgVal); 545 return; 546 } 547 548 reportBug(*Str, C, ArgVal); 549 } 550 551 void ExprInspectionChecker::analyzerIsTainted(const CallExpr *CE, 552 CheckerContext &C) const { 553 if (CE->getNumArgs() != 1) { 554 reportBug("clang_analyzer_isTainted() requires exactly one argument", C); 555 return; 556 } 557 const bool IsTainted = 558 taint::isTainted(C.getState(), CE->getArg(0), C.getLocationContext()); 559 reportBug(IsTainted ? "YES" : "NO", C); 560 } 561 562 void ento::registerExprInspectionChecker(CheckerManager &Mgr) { 563 Mgr.registerChecker<ExprInspectionChecker>(); 564 } 565 566 bool ento::shouldRegisterExprInspectionChecker(const CheckerManager &mgr) { 567 return true; 568 } 569