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