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