brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.1 KiB · 42d5815 Raw
327 lines · cpp
1//=== InnerPointerChecker.cpp -------------------------------------*- 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// This file defines a check that marks a raw pointer to a C++ container's10// inner buffer released when the object is destroyed. This information can11// be used by MallocChecker to detect use-after-free problems.12//13//===----------------------------------------------------------------------===//14 15#include "AllocationState.h"16#include "InterCheckerAPI.h"17#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"18#include "clang/StaticAnalyzer/Core/Checker.h"19#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"20#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"21#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"22 23using namespace clang;24using namespace ento;25 26// Associate container objects with a set of raw pointer symbols.27REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(PtrSet, SymbolRef)28REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet)29 30 31namespace {32 33class InnerPointerChecker34    : public Checker<check::DeadSymbols, check::PostCall> {35 36  CallDescriptionSet InvalidatingMemberFunctions{37      CallDescription(CDM::CXXMethod, {"std", "basic_string", "append"}),38      CallDescription(CDM::CXXMethod, {"std", "basic_string", "assign"}),39      CallDescription(CDM::CXXMethod, {"std", "basic_string", "clear"}),40      CallDescription(CDM::CXXMethod, {"std", "basic_string", "erase"}),41      CallDescription(CDM::CXXMethod, {"std", "basic_string", "insert"}),42      CallDescription(CDM::CXXMethod, {"std", "basic_string", "pop_back"}),43      CallDescription(CDM::CXXMethod, {"std", "basic_string", "push_back"}),44      CallDescription(CDM::CXXMethod, {"std", "basic_string", "replace"}),45      CallDescription(CDM::CXXMethod, {"std", "basic_string", "reserve"}),46      CallDescription(CDM::CXXMethod, {"std", "basic_string", "resize"}),47      CallDescription(CDM::CXXMethod, {"std", "basic_string", "shrink_to_fit"}),48      CallDescription(CDM::CXXMethod, {"std", "basic_string", "swap"})};49 50  CallDescriptionSet AddressofFunctions{51      CallDescription(CDM::SimpleFunc, {"std", "addressof"}),52      CallDescription(CDM::SimpleFunc, {"std", "__addressof"})};53 54  CallDescriptionSet InnerPointerAccessFunctions{55      CallDescription(CDM::CXXMethod, {"std", "basic_string", "c_str"}),56      CallDescription(CDM::SimpleFunc, {"std", "data"}, 1),57      CallDescription(CDM::CXXMethod, {"std", "basic_string", "data"})};58 59public:60  class InnerPointerBRVisitor : public BugReporterVisitor {61    SymbolRef PtrToBuf;62 63  public:64    InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {}65 66    static void *getTag() {67      static int Tag = 0;68      return &Tag;69    }70 71    void Profile(llvm::FoldingSetNodeID &ID) const override {72      ID.AddPointer(getTag());73    }74 75    PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,76                                     BugReporterContext &BRC,77                                     PathSensitiveBugReport &BR) override;78 79    // FIXME: Scan the map once in the visitor's constructor and do a direct80    // lookup by region.81    bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {82      RawPtrMapTy Map = State->get<RawPtrMap>();83      for (const auto &Entry : Map) {84        if (Entry.second.contains(Sym))85          return true;86      }87      return false;88    }89  };90 91  /// Check whether the called member function potentially invalidates92  /// pointers referring to the container object's inner buffer.93  bool isInvalidatingMemberFunction(const CallEvent &Call) const;94 95  /// Mark pointer symbols associated with the given memory region released96  /// in the program state.97  void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State,98                              const MemRegion *ObjRegion,99                              CheckerContext &C) const;100 101  /// Standard library functions that take a non-const `basic_string` argument by102  /// reference may invalidate its inner pointers. Check for these cases and103  /// mark the pointers released.104  void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State,105                              CheckerContext &C) const;106 107  /// Record the connection between raw pointers referring to a container108  /// object's inner buffer and the object's memory region in the program state.109  /// Mark potentially invalidated pointers released.110  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;111 112  /// Clean up the program state map.113  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;114};115 116} // end anonymous namespace117 118bool InnerPointerChecker::isInvalidatingMemberFunction(119        const CallEvent &Call) const {120  if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) {121    OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator();122    if (Opc == OO_Equal || Opc == OO_PlusEqual)123      return true;124    return false;125  }126  return isa<CXXDestructorCall>(Call) ||127         InvalidatingMemberFunctions.contains(Call);128}129 130void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,131                                                 ProgramStateRef State,132                                                 const MemRegion *MR,133                                                 CheckerContext &C) const {134  if (const PtrSet *PS = State->get<RawPtrMap>(MR)) {135    const Expr *Origin = Call.getOriginExpr();136    for (const auto Symbol : *PS) {137      // NOTE: `Origin` may be null, and will be stored so in the symbol's138      // `RefState` in MallocChecker's `RegionState` program state map.139      State = allocation_state::markReleased(State, Symbol, Origin);140    }141    State = State->remove<RawPtrMap>(MR);142    C.addTransition(State);143    return;144  }145}146 147void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,148                                                 ProgramStateRef State,149                                                 CheckerContext &C) const {150  if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) {151    const FunctionDecl *FD = FC->getDecl();152    if (!FD || !FD->isInStdNamespace())153      return;154 155    for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) {156      QualType ParamTy = FD->getParamDecl(I)->getType();157      if (!ParamTy->isReferenceType() ||158          ParamTy->getPointeeType().isConstQualified())159        continue;160 161      // In case of member operator calls, `this` is counted as an162      // argument but not as a parameter.163      bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC);164      unsigned ArgI = isaMemberOpCall ? I+1 : I;165 166      SVal Arg = FC->getArgSVal(ArgI);167      const auto *ArgRegion =168          dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion());169      if (!ArgRegion)170        continue;171 172      // std::addressof functions accepts a non-const reference as an argument,173      // but doesn't modify it.174      if (AddressofFunctions.contains(Call))175        continue;176 177      markPtrSymbolsReleased(Call, State, ArgRegion, C);178    }179  }180}181 182// [string.require]183//184// "References, pointers, and iterators referring to the elements of a185// basic_string sequence may be invalidated by the following uses of that186// basic_string object:187//188// -- As an argument to any standard library function taking a reference189// to non-const basic_string as an argument. For example, as an argument to190// non-member functions swap(), operator>>(), and getline(), or as an argument191// to basic_string::swap().192//193// -- Calling non-const member functions, except operator[], at, front, back,194// begin, rbegin, end, and rend."195 196void InnerPointerChecker::checkPostCall(const CallEvent &Call,197                                        CheckerContext &C) const {198  ProgramStateRef State = C.getState();199 200  // TODO: Do we need these to be typed?201  const TypedValueRegion *ObjRegion = nullptr;202 203  if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) {204    ObjRegion = dyn_cast_or_null<TypedValueRegion>(205        ICall->getCXXThisVal().getAsRegion());206 207    // Check [string.require] / second point.208    if (isInvalidatingMemberFunction(Call)) {209      markPtrSymbolsReleased(Call, State, ObjRegion, C);210      return;211    }212  }213 214  if (InnerPointerAccessFunctions.contains(Call)) {215 216    if (isa<SimpleFunctionCall>(Call)) {217      // NOTE: As of now, we only have one free access function: std::data.218      //       If we add more functions like this in the list, hardcoded219      //       argument index should be changed.220      ObjRegion =221          dyn_cast_or_null<TypedValueRegion>(Call.getArgSVal(0).getAsRegion());222    }223 224    if (!ObjRegion)225      return;226 227    SVal RawPtr = Call.getReturnValue();228    if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {229      // Start tracking this raw pointer by adding it to the set of symbols230      // associated with this container object in the program state map.231 232      PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();233      const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion);234      PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet();235      assert(C.wasInlined || !Set.contains(Sym));236      Set = F.add(Set, Sym);237 238      State = State->set<RawPtrMap>(ObjRegion, Set);239      C.addTransition(State);240    }241 242    return;243  }244 245  // Check [string.require] / first point.246  checkFunctionArguments(Call, State, C);247}248 249void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper,250                                           CheckerContext &C) const {251  ProgramStateRef State = C.getState();252  PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();253  RawPtrMapTy RPM = State->get<RawPtrMap>();254  for (const auto &Entry : RPM) {255    if (!SymReaper.isLiveRegion(Entry.first)) {256      // Due to incomplete destructor support, some dead regions might257      // remain in the program state map. Clean them up.258      State = State->remove<RawPtrMap>(Entry.first);259    }260    if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) {261      PtrSet CleanedUpSet = *OldSet;262      for (const auto Symbol : Entry.second) {263        if (!SymReaper.isLive(Symbol))264          CleanedUpSet = F.remove(CleanedUpSet, Symbol);265      }266      State = CleanedUpSet.isEmpty()267                  ? State->remove<RawPtrMap>(Entry.first)268                  : State->set<RawPtrMap>(Entry.first, CleanedUpSet);269    }270  }271  C.addTransition(State);272}273 274namespace clang {275namespace ento {276namespace allocation_state {277 278std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) {279  return std::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym);280}281 282const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) {283  RawPtrMapTy Map = State->get<RawPtrMap>();284  for (const auto &Entry : Map) {285    if (Entry.second.contains(Sym)) {286      return Entry.first;287    }288  }289  return nullptr;290}291 292} // end namespace allocation_state293} // end namespace ento294} // end namespace clang295 296PathDiagnosticPieceRef InnerPointerChecker::InnerPointerBRVisitor::VisitNode(297    const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {298  if (!isSymbolTracked(N->getState(), PtrToBuf) ||299      isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf))300    return nullptr;301 302  const Stmt *S = N->getStmtForDiagnostics();303  if (!S)304    return nullptr;305 306  const MemRegion *ObjRegion =307      allocation_state::getContainerObjRegion(N->getState(), PtrToBuf);308  const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);309  QualType ObjTy = TypedRegion->getValueType();310 311  SmallString<256> Buf;312  llvm::raw_svector_ostream OS(Buf);313  OS << "Pointer to inner buffer of '" << ObjTy << "' obtained here";314  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),315                             N->getLocationContext());316  return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);317}318 319void ento::registerInnerPointerChecker(CheckerManager &Mgr) {320  registerInnerPointerCheckerAux(Mgr);321  Mgr.registerChecker<InnerPointerChecker>();322}323 324bool ento::shouldRegisterInnerPointerChecker(const CheckerManager &mgr) {325  return true;326}327