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