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 const BugType BT{this, "Checking analyzer assumptions", "debug"}; 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 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); 180 if (ExprVal) { 181 R->markInteresting(*ExprVal); 182 } 183 BR.emitReport(std::move(R)); 184 return N; 185 } 186 187 const Expr *ExprInspectionChecker::getArgExpr(const CallExpr *CE, 188 CheckerContext &C) const { 189 if (CE->getNumArgs() == 0) { 190 reportBug("Missing argument", C); 191 return nullptr; 192 } 193 return CE->getArg(0); 194 } 195 196 const MemRegion *ExprInspectionChecker::getArgRegion(const CallExpr *CE, 197 CheckerContext &C) const { 198 const Expr *Arg = getArgExpr(CE, C); 199 if (!Arg) 200 return nullptr; 201 202 const MemRegion *MR = C.getSVal(Arg).getAsRegion(); 203 if (!MR) { 204 reportBug("Cannot obtain the region", C); 205 return nullptr; 206 } 207 208 return MR; 209 } 210 211 void ExprInspectionChecker::analyzerEval(const CallExpr *CE, 212 CheckerContext &C) const { 213 const LocationContext *LC = C.getPredecessor()->getLocationContext(); 214 215 // A specific instantiation of an inlined function may have more constrained 216 // values than can generally be assumed. Skip the check. 217 if (LC->getStackFrame()->getParent() != nullptr) 218 return; 219 220 reportBug(getArgumentValueString(CE, C), C); 221 } 222 223 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE, 224 CheckerContext &C) const { 225 reportBug("REACHABLE", C); 226 } 227 228 void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE, 229 CheckerContext &C) const { 230 ++ReachedStats[CE].NumTimesReached; 231 if (!ReachedStats[CE].ExampleNode) { 232 // Later, in checkEndAnalysis, we'd throw a report against it. 233 ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode(); 234 } 235 } 236 237 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, 238 CheckerContext &C) const { 239 const LocationContext *LC = C.getPredecessor()->getLocationContext(); 240 241 // An inlined function could conceivably also be analyzed as a top-level 242 // function. We ignore this case and only emit a message (TRUE or FALSE) 243 // when we are analyzing it as an inlined function. This means that 244 // clang_analyzer_checkInlined(true) should always print TRUE, but 245 // clang_analyzer_checkInlined(false) should never actually print anything. 246 if (LC->getStackFrame()->getParent() == nullptr) 247 return; 248 249 reportBug(getArgumentValueString(CE, C), C); 250 } 251 252 void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, 253 CheckerContext &C) const { 254 const Expr *Arg = getArgExpr(CE, C); 255 if (!Arg) 256 return; 257 258 SVal V = C.getSVal(Arg); 259 SValExplainer Ex(C.getASTContext()); 260 reportBug(Ex.Visit(V), C); 261 } 262 263 static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, 264 const llvm::APSInt &I) { 265 Out << I.getBitWidth() << (I.isUnsigned() ? "u:" : "s:"); 266 Out << I; 267 } 268 269 static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, 270 SymbolRef Sym) { 271 C.getConstraintManager().printValue(Out, C.getState(), Sym); 272 } 273 274 static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, 275 SVal V) { 276 Out << V; 277 } 278 279 template <typename T> 280 void ExprInspectionChecker::printAndReport(CheckerContext &C, T What) const { 281 llvm::SmallString<64> Str; 282 llvm::raw_svector_ostream OS(Str); 283 printHelper(OS, C, What); 284 reportBug(OS.str(), C); 285 } 286 287 void ExprInspectionChecker::analyzerValue(const CallExpr *CE, 288 CheckerContext &C) const { 289 const Expr *Arg = getArgExpr(CE, C); 290 if (!Arg) 291 return; 292 293 SVal V = C.getSVal(Arg); 294 if (const SymbolRef Sym = V.getAsSymbol()) 295 printAndReport(C, Sym); 296 else if (const llvm::APSInt *I = V.getAsInteger()) 297 printAndReport(C, *I); 298 else 299 reportBug("n/a", C); 300 } 301 302 void ExprInspectionChecker::analyzerDumpSValType(const CallExpr *CE, 303 CheckerContext &C) const { 304 const Expr *Arg = getArgExpr(CE, C); 305 if (!Arg) 306 return; 307 308 QualType Ty = C.getSVal(Arg).getType(C.getASTContext()); 309 reportBug(Ty.getAsString(), C); 310 } 311 312 void ExprInspectionChecker::analyzerDump(const CallExpr *CE, 313 CheckerContext &C) const { 314 const Expr *Arg = getArgExpr(CE, C); 315 if (!Arg) 316 return; 317 318 SVal V = C.getSVal(Arg); 319 printAndReport(C, V); 320 } 321 322 void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, 323 CheckerContext &C) const { 324 const Expr *Arg = getArgExpr(CE, C); 325 if (!Arg) 326 return; 327 328 ProgramStateRef State = C.getState(); 329 SVal Size = getDynamicExtentWithOffset(State, C.getSVal(Arg)); 330 331 State = State->BindExpr(CE, C.getLocationContext(), Size); 332 C.addTransition(State); 333 } 334 335 void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE, 336 CheckerContext &C) const { 337 const Expr *Arg = getArgExpr(CE, C); 338 if (!Arg) 339 return; 340 341 ProgramStateRef State = C.getState(); 342 SVal Size = getDynamicExtentWithOffset(State, C.getSVal(Arg)); 343 printAndReport(C, Size); 344 } 345 346 void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE, 347 CheckerContext &C) const { 348 const MemRegion *MR = getArgRegion(CE, C); 349 if (!MR) 350 return; 351 352 QualType ElementTy; 353 if (const auto *TVR = MR->getAs<TypedValueRegion>()) { 354 ElementTy = TVR->getValueType(); 355 } else { 356 ElementTy = MR->castAs<SymbolicRegion>()->getPointeeStaticType(); 357 } 358 359 assert(!ElementTy->isPointerType()); 360 361 DefinedOrUnknownSVal ElementCount = getDynamicElementCountWithOffset( 362 C.getState(), C.getSVal(getArgExpr(CE, C)), ElementTy); 363 printAndReport(C, ElementCount); 364 } 365 366 void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE, 367 CheckerContext &C) const { 368 C.getState()->dump(); 369 } 370 371 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, 372 CheckerContext &C) const { 373 const Expr *Arg = getArgExpr(CE, C); 374 if (!Arg) 375 return; 376 377 SVal Val = C.getSVal(Arg); 378 SymbolRef Sym = Val.getAsSymbol(); 379 if (!Sym) 380 return; 381 382 ProgramStateRef State = C.getState(); 383 State = State->add<MarkedSymbols>(Sym); 384 C.addTransition(State); 385 } 386 387 void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper, 388 CheckerContext &C) const { 389 ProgramStateRef State = C.getState(); 390 const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>(); 391 ExplodedNode *N = C.getPredecessor(); 392 for (SymbolRef Sym : Syms) { 393 if (!SymReaper.isDead(Sym)) 394 continue; 395 396 // The non-fatal error node should be the same for all reports. 397 if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C)) 398 N = BugNode; 399 State = State->remove<MarkedSymbols>(Sym); 400 } 401 402 for (auto I : State->get<DenotedSymbols>()) { 403 SymbolRef Sym = I.first; 404 if (!SymReaper.isLive(Sym)) 405 State = State->remove<DenotedSymbols>(Sym); 406 } 407 408 C.addTransition(State, N); 409 } 410 411 void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, 412 ExprEngine &Eng) const { 413 for (auto Item : ReachedStats) { 414 unsigned NumTimesReached = Item.second.NumTimesReached; 415 ExplodedNode *N = Item.second.ExampleNode; 416 417 reportBug(llvm::to_string(NumTimesReached), BR, N); 418 } 419 ReachedStats.clear(); 420 } 421 422 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, 423 CheckerContext &C) const { 424 LLVM_BUILTIN_TRAP; 425 } 426 427 void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE, 428 CheckerContext &C) const { 429 const LangOptions &Opts = C.getLangOpts(); 430 const SourceManager &SM = C.getSourceManager(); 431 FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM); 432 std::string HashContent = 433 getIssueString(FL, getCheckerName().getName(), "Category", 434 C.getLocationContext()->getDecl(), Opts); 435 436 reportBug(HashContent, C); 437 } 438 439 void ExprInspectionChecker::analyzerDenote(const CallExpr *CE, 440 CheckerContext &C) const { 441 if (CE->getNumArgs() < 2) { 442 reportBug("clang_analyzer_denote() requires a symbol and a string literal", 443 C); 444 return; 445 } 446 447 SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol(); 448 if (!Sym) { 449 reportBug("Not a symbol", C); 450 return; 451 } 452 453 const auto *E = dyn_cast<StringLiteral>(CE->getArg(1)->IgnoreParenCasts()); 454 if (!E) { 455 reportBug("Not a string literal", C); 456 return; 457 } 458 459 ProgramStateRef State = C.getState(); 460 461 C.addTransition(C.getState()->set<DenotedSymbols>(Sym, E)); 462 } 463 464 namespace { 465 class SymbolExpressor 466 : public SymExprVisitor<SymbolExpressor, std::optional<std::string>> { 467 ProgramStateRef State; 468 469 public: 470 SymbolExpressor(ProgramStateRef State) : State(State) {} 471 472 std::optional<std::string> lookup(const SymExpr *S) { 473 if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) { 474 const StringLiteral *SL = *SLPtr; 475 return std::string(SL->getBytes()); 476 } 477 return std::nullopt; 478 } 479 480 std::optional<std::string> VisitSymExpr(const SymExpr *S) { 481 return lookup(S); 482 } 483 484 std::optional<std::string> VisitSymIntExpr(const SymIntExpr *S) { 485 if (std::optional<std::string> Str = lookup(S)) 486 return Str; 487 if (std::optional<std::string> Str = Visit(S->getLHS())) 488 return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " + 489 std::to_string(S->getRHS().getLimitedValue()) + 490 (S->getRHS().isUnsigned() ? "U" : "")) 491 .str(); 492 return std::nullopt; 493 } 494 495 std::optional<std::string> VisitSymSymExpr(const SymSymExpr *S) { 496 if (std::optional<std::string> Str = lookup(S)) 497 return Str; 498 if (std::optional<std::string> Str1 = Visit(S->getLHS())) 499 if (std::optional<std::string> Str2 = Visit(S->getRHS())) 500 return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + 501 " " + *Str2) 502 .str(); 503 return std::nullopt; 504 } 505 506 std::optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) { 507 if (std::optional<std::string> Str = lookup(S)) 508 return Str; 509 if (std::optional<std::string> Str = Visit(S->getOperand())) 510 return (UnaryOperator::getOpcodeStr(S->getOpcode()) + *Str).str(); 511 return std::nullopt; 512 } 513 514 std::optional<std::string> VisitSymbolCast(const SymbolCast *S) { 515 if (std::optional<std::string> Str = lookup(S)) 516 return Str; 517 if (std::optional<std::string> Str = Visit(S->getOperand())) 518 return (Twine("(") + S->getType().getAsString() + ")" + *Str).str(); 519 return std::nullopt; 520 } 521 }; 522 } // namespace 523 524 void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, 525 CheckerContext &C) const { 526 const Expr *Arg = getArgExpr(CE, C); 527 if (!Arg) 528 return; 529 530 SVal ArgVal = C.getSVal(CE->getArg(0)); 531 SymbolRef Sym = ArgVal.getAsSymbol(); 532 if (!Sym) { 533 reportBug("Not a symbol", C, ArgVal); 534 return; 535 } 536 537 SymbolExpressor V(C.getState()); 538 auto Str = V.Visit(Sym); 539 if (!Str) { 540 reportBug("Unable to express", C, ArgVal); 541 return; 542 } 543 544 reportBug(*Str, C, ArgVal); 545 } 546 547 void ExprInspectionChecker::analyzerIsTainted(const CallExpr *CE, 548 CheckerContext &C) const { 549 if (CE->getNumArgs() != 1) { 550 reportBug("clang_analyzer_isTainted() requires exactly one argument", C); 551 return; 552 } 553 const bool IsTainted = 554 taint::isTainted(C.getState(), CE->getArg(0), C.getLocationContext()); 555 reportBug(IsTainted ? "YES" : "NO", C); 556 } 557 558 void ento::registerExprInspectionChecker(CheckerManager &Mgr) { 559 Mgr.registerChecker<ExprInspectionChecker>(); 560 } 561 562 bool ento::shouldRegisterExprInspectionChecker(const CheckerManager &mgr) { 563 return true; 564 } 565