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