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