brintos

brintos / llvm-project-archived public Read only

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