xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
10b57cec5SDimitry Andric //=== InnerPointerChecker.cpp -------------------------------------*- C++ -*--//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines a check that marks a raw pointer to a C++ container's
100b57cec5SDimitry Andric // inner buffer released when the object is destroyed. This information can
110b57cec5SDimitry Andric // be used by MallocChecker to detect use-after-free problems.
120b57cec5SDimitry Andric //
130b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric #include "AllocationState.h"
160b57cec5SDimitry Andric #include "InterCheckerAPI.h"
17349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
21349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric using namespace clang;
260b57cec5SDimitry Andric using namespace ento;
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric // Associate container objects with a set of raw pointer symbols.
290b57cec5SDimitry Andric REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(PtrSet, SymbolRef)
300b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet)
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric namespace {
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric class InnerPointerChecker
360b57cec5SDimitry Andric     : public Checker<check::DeadSymbols, check::PostCall> {
370b57cec5SDimitry Andric 
38*81ad6265SDimitry Andric   CallDescription AppendFn, AssignFn, AddressofFn, AddressofFn_, ClearFn,
39*81ad6265SDimitry Andric       CStrFn, DataFn, DataMemberFn, EraseFn, InsertFn, PopBackFn, PushBackFn,
40*81ad6265SDimitry Andric       ReplaceFn, ReserveFn, ResizeFn, ShrinkToFitFn, SwapFn;
410b57cec5SDimitry Andric 
420b57cec5SDimitry Andric public:
430b57cec5SDimitry Andric   class InnerPointerBRVisitor : public BugReporterVisitor {
440b57cec5SDimitry Andric     SymbolRef PtrToBuf;
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric   public:
470b57cec5SDimitry Andric     InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {}
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric     static void *getTag() {
500b57cec5SDimitry Andric       static int Tag = 0;
510b57cec5SDimitry Andric       return &Tag;
520b57cec5SDimitry Andric     }
530b57cec5SDimitry Andric 
540b57cec5SDimitry Andric     void Profile(llvm::FoldingSetNodeID &ID) const override {
550b57cec5SDimitry Andric       ID.AddPointer(getTag());
560b57cec5SDimitry Andric     }
570b57cec5SDimitry Andric 
58a7dea167SDimitry Andric     virtual PathDiagnosticPieceRef
59a7dea167SDimitry Andric     VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
60a7dea167SDimitry Andric               PathSensitiveBugReport &BR) override;
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric     // FIXME: Scan the map once in the visitor's constructor and do a direct
630b57cec5SDimitry Andric     // lookup by region.
640b57cec5SDimitry Andric     bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
650b57cec5SDimitry Andric       RawPtrMapTy Map = State->get<RawPtrMap>();
66480093f4SDimitry Andric       for (const auto &Entry : Map) {
670b57cec5SDimitry Andric         if (Entry.second.contains(Sym))
680b57cec5SDimitry Andric           return true;
690b57cec5SDimitry Andric       }
700b57cec5SDimitry Andric       return false;
710b57cec5SDimitry Andric     }
720b57cec5SDimitry Andric   };
730b57cec5SDimitry Andric 
740b57cec5SDimitry Andric   InnerPointerChecker()
750b57cec5SDimitry Andric       : AppendFn({"std", "basic_string", "append"}),
760b57cec5SDimitry Andric         AssignFn({"std", "basic_string", "assign"}),
77*81ad6265SDimitry Andric         AddressofFn({"std", "addressof"}), AddressofFn_({"std", "__addressof"}),
780b57cec5SDimitry Andric         ClearFn({"std", "basic_string", "clear"}),
79fe6060f1SDimitry Andric         CStrFn({"std", "basic_string", "c_str"}), DataFn({"std", "data"}, 1),
80fe6060f1SDimitry Andric         DataMemberFn({"std", "basic_string", "data"}),
810b57cec5SDimitry Andric         EraseFn({"std", "basic_string", "erase"}),
820b57cec5SDimitry Andric         InsertFn({"std", "basic_string", "insert"}),
830b57cec5SDimitry Andric         PopBackFn({"std", "basic_string", "pop_back"}),
840b57cec5SDimitry Andric         PushBackFn({"std", "basic_string", "push_back"}),
850b57cec5SDimitry Andric         ReplaceFn({"std", "basic_string", "replace"}),
860b57cec5SDimitry Andric         ReserveFn({"std", "basic_string", "reserve"}),
870b57cec5SDimitry Andric         ResizeFn({"std", "basic_string", "resize"}),
880b57cec5SDimitry Andric         ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}),
890b57cec5SDimitry Andric         SwapFn({"std", "basic_string", "swap"}) {}
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric   /// Check whether the called member function potentially invalidates
920b57cec5SDimitry Andric   /// pointers referring to the container object's inner buffer.
930b57cec5SDimitry Andric   bool isInvalidatingMemberFunction(const CallEvent &Call) const;
940b57cec5SDimitry Andric 
95fe6060f1SDimitry Andric   /// Check whether the called function returns a raw inner pointer.
96fe6060f1SDimitry Andric   bool isInnerPointerAccessFunction(const CallEvent &Call) const;
97fe6060f1SDimitry Andric 
980b57cec5SDimitry Andric   /// Mark pointer symbols associated with the given memory region released
990b57cec5SDimitry Andric   /// in the program state.
1000b57cec5SDimitry Andric   void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State,
1010b57cec5SDimitry Andric                               const MemRegion *ObjRegion,
1020b57cec5SDimitry Andric                               CheckerContext &C) const;
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric   /// Standard library functions that take a non-const `basic_string` argument by
1050b57cec5SDimitry Andric   /// reference may invalidate its inner pointers. Check for these cases and
1060b57cec5SDimitry Andric   /// mark the pointers released.
1070b57cec5SDimitry Andric   void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State,
1080b57cec5SDimitry Andric                               CheckerContext &C) const;
1090b57cec5SDimitry Andric 
1100b57cec5SDimitry Andric   /// Record the connection between raw pointers referring to a container
1110b57cec5SDimitry Andric   /// object's inner buffer and the object's memory region in the program state.
1120b57cec5SDimitry Andric   /// Mark potentially invalidated pointers released.
1130b57cec5SDimitry Andric   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   /// Clean up the program state map.
1160b57cec5SDimitry Andric   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
1170b57cec5SDimitry Andric };
1180b57cec5SDimitry Andric 
1190b57cec5SDimitry Andric } // end anonymous namespace
1200b57cec5SDimitry Andric 
1210b57cec5SDimitry Andric bool InnerPointerChecker::isInvalidatingMemberFunction(
1220b57cec5SDimitry Andric         const CallEvent &Call) const {
1230b57cec5SDimitry Andric   if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) {
1240b57cec5SDimitry Andric     OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator();
1250b57cec5SDimitry Andric     if (Opc == OO_Equal || Opc == OO_PlusEqual)
1260b57cec5SDimitry Andric       return true;
1270b57cec5SDimitry Andric     return false;
1280b57cec5SDimitry Andric   }
129349cc55cSDimitry Andric   return isa<CXXDestructorCall>(Call) ||
130349cc55cSDimitry Andric          matchesAny(Call, AppendFn, AssignFn, ClearFn, EraseFn, InsertFn,
131349cc55cSDimitry Andric                     PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn,
132349cc55cSDimitry Andric                     ShrinkToFitFn, SwapFn);
1330b57cec5SDimitry Andric }
1340b57cec5SDimitry Andric 
135fe6060f1SDimitry Andric bool InnerPointerChecker::isInnerPointerAccessFunction(
136fe6060f1SDimitry Andric     const CallEvent &Call) const {
137349cc55cSDimitry Andric   return matchesAny(Call, CStrFn, DataFn, DataMemberFn);
138fe6060f1SDimitry Andric }
139fe6060f1SDimitry Andric 
1400b57cec5SDimitry Andric void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
1410b57cec5SDimitry Andric                                                  ProgramStateRef State,
1420b57cec5SDimitry Andric                                                  const MemRegion *MR,
1430b57cec5SDimitry Andric                                                  CheckerContext &C) const {
1440b57cec5SDimitry Andric   if (const PtrSet *PS = State->get<RawPtrMap>(MR)) {
1450b57cec5SDimitry Andric     const Expr *Origin = Call.getOriginExpr();
1460b57cec5SDimitry Andric     for (const auto Symbol : *PS) {
1470b57cec5SDimitry Andric       // NOTE: `Origin` may be null, and will be stored so in the symbol's
1480b57cec5SDimitry Andric       // `RefState` in MallocChecker's `RegionState` program state map.
1490b57cec5SDimitry Andric       State = allocation_state::markReleased(State, Symbol, Origin);
1500b57cec5SDimitry Andric     }
1510b57cec5SDimitry Andric     State = State->remove<RawPtrMap>(MR);
1520b57cec5SDimitry Andric     C.addTransition(State);
1530b57cec5SDimitry Andric     return;
1540b57cec5SDimitry Andric   }
1550b57cec5SDimitry Andric }
1560b57cec5SDimitry Andric 
1570b57cec5SDimitry Andric void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,
1580b57cec5SDimitry Andric                                                  ProgramStateRef State,
1590b57cec5SDimitry Andric                                                  CheckerContext &C) const {
1600b57cec5SDimitry Andric   if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) {
1610b57cec5SDimitry Andric     const FunctionDecl *FD = FC->getDecl();
1620b57cec5SDimitry Andric     if (!FD || !FD->isInStdNamespace())
1630b57cec5SDimitry Andric       return;
1640b57cec5SDimitry Andric 
1650b57cec5SDimitry Andric     for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) {
1660b57cec5SDimitry Andric       QualType ParamTy = FD->getParamDecl(I)->getType();
1670b57cec5SDimitry Andric       if (!ParamTy->isReferenceType() ||
1680b57cec5SDimitry Andric           ParamTy->getPointeeType().isConstQualified())
1690b57cec5SDimitry Andric         continue;
1700b57cec5SDimitry Andric 
1710b57cec5SDimitry Andric       // In case of member operator calls, `this` is counted as an
1720b57cec5SDimitry Andric       // argument but not as a parameter.
1730b57cec5SDimitry Andric       bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC);
1740b57cec5SDimitry Andric       unsigned ArgI = isaMemberOpCall ? I+1 : I;
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric       SVal Arg = FC->getArgSVal(ArgI);
1770b57cec5SDimitry Andric       const auto *ArgRegion =
1780b57cec5SDimitry Andric           dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion());
1790b57cec5SDimitry Andric       if (!ArgRegion)
1800b57cec5SDimitry Andric         continue;
1810b57cec5SDimitry Andric 
182*81ad6265SDimitry Andric       // std::addressof functions accepts a non-const reference as an argument,
183fe6060f1SDimitry Andric       // but doesn't modify it.
184*81ad6265SDimitry Andric       if (matchesAny(Call, AddressofFn, AddressofFn_))
185fe6060f1SDimitry Andric         continue;
186fe6060f1SDimitry Andric 
1870b57cec5SDimitry Andric       markPtrSymbolsReleased(Call, State, ArgRegion, C);
1880b57cec5SDimitry Andric     }
1890b57cec5SDimitry Andric   }
1900b57cec5SDimitry Andric }
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric // [string.require]
1930b57cec5SDimitry Andric //
1940b57cec5SDimitry Andric // "References, pointers, and iterators referring to the elements of a
1950b57cec5SDimitry Andric // basic_string sequence may be invalidated by the following uses of that
1960b57cec5SDimitry Andric // basic_string object:
1970b57cec5SDimitry Andric //
1980b57cec5SDimitry Andric // -- As an argument to any standard library function taking a reference
1990b57cec5SDimitry Andric // to non-const basic_string as an argument. For example, as an argument to
2000b57cec5SDimitry Andric // non-member functions swap(), operator>>(), and getline(), or as an argument
2010b57cec5SDimitry Andric // to basic_string::swap().
2020b57cec5SDimitry Andric //
2030b57cec5SDimitry Andric // -- Calling non-const member functions, except operator[], at, front, back,
2040b57cec5SDimitry Andric // begin, rbegin, end, and rend."
2050b57cec5SDimitry Andric 
2060b57cec5SDimitry Andric void InnerPointerChecker::checkPostCall(const CallEvent &Call,
2070b57cec5SDimitry Andric                                         CheckerContext &C) const {
2080b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
2090b57cec5SDimitry Andric 
2100b57cec5SDimitry Andric   // TODO: Do we need these to be typed?
211fe6060f1SDimitry Andric   const TypedValueRegion *ObjRegion = nullptr;
212fe6060f1SDimitry Andric 
213fe6060f1SDimitry Andric   if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) {
214fe6060f1SDimitry Andric     ObjRegion = dyn_cast_or_null<TypedValueRegion>(
2150b57cec5SDimitry Andric         ICall->getCXXThisVal().getAsRegion());
216fe6060f1SDimitry Andric 
217fe6060f1SDimitry Andric     // Check [string.require] / second point.
218fe6060f1SDimitry Andric     if (isInvalidatingMemberFunction(Call)) {
219fe6060f1SDimitry Andric       markPtrSymbolsReleased(Call, State, ObjRegion, C);
220fe6060f1SDimitry Andric       return;
221fe6060f1SDimitry Andric     }
222fe6060f1SDimitry Andric   }
223fe6060f1SDimitry Andric 
224fe6060f1SDimitry Andric   if (isInnerPointerAccessFunction(Call)) {
225fe6060f1SDimitry Andric 
226fe6060f1SDimitry Andric     if (isa<SimpleFunctionCall>(Call)) {
227fe6060f1SDimitry Andric       // NOTE: As of now, we only have one free access function: std::data.
228fe6060f1SDimitry Andric       //       If we add more functions like this in the list, hardcoded
229fe6060f1SDimitry Andric       //       argument index should be changed.
230fe6060f1SDimitry Andric       ObjRegion =
231fe6060f1SDimitry Andric           dyn_cast_or_null<TypedValueRegion>(Call.getArgSVal(0).getAsRegion());
232fe6060f1SDimitry Andric     }
233fe6060f1SDimitry Andric 
2340b57cec5SDimitry Andric     if (!ObjRegion)
2350b57cec5SDimitry Andric       return;
2360b57cec5SDimitry Andric 
2370b57cec5SDimitry Andric     SVal RawPtr = Call.getReturnValue();
2380b57cec5SDimitry Andric     if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
2390b57cec5SDimitry Andric       // Start tracking this raw pointer by adding it to the set of symbols
2400b57cec5SDimitry Andric       // associated with this container object in the program state map.
2410b57cec5SDimitry Andric 
2420b57cec5SDimitry Andric       PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
2430b57cec5SDimitry Andric       const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion);
2440b57cec5SDimitry Andric       PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet();
2450b57cec5SDimitry Andric       assert(C.wasInlined || !Set.contains(Sym));
2460b57cec5SDimitry Andric       Set = F.add(Set, Sym);
2470b57cec5SDimitry Andric 
2480b57cec5SDimitry Andric       State = State->set<RawPtrMap>(ObjRegion, Set);
2490b57cec5SDimitry Andric       C.addTransition(State);
2500b57cec5SDimitry Andric     }
2510b57cec5SDimitry Andric 
2520b57cec5SDimitry Andric     return;
2530b57cec5SDimitry Andric   }
2540b57cec5SDimitry Andric 
2550b57cec5SDimitry Andric   // Check [string.require] / first point.
2560b57cec5SDimitry Andric   checkFunctionArguments(Call, State, C);
2570b57cec5SDimitry Andric }
2580b57cec5SDimitry Andric 
2590b57cec5SDimitry Andric void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper,
2600b57cec5SDimitry Andric                                            CheckerContext &C) const {
2610b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
2620b57cec5SDimitry Andric   PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
2630b57cec5SDimitry Andric   RawPtrMapTy RPM = State->get<RawPtrMap>();
264480093f4SDimitry Andric   for (const auto &Entry : RPM) {
2650b57cec5SDimitry Andric     if (!SymReaper.isLiveRegion(Entry.first)) {
2660b57cec5SDimitry Andric       // Due to incomplete destructor support, some dead regions might
2670b57cec5SDimitry Andric       // remain in the program state map. Clean them up.
2680b57cec5SDimitry Andric       State = State->remove<RawPtrMap>(Entry.first);
2690b57cec5SDimitry Andric     }
2700b57cec5SDimitry Andric     if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) {
2710b57cec5SDimitry Andric       PtrSet CleanedUpSet = *OldSet;
2720b57cec5SDimitry Andric       for (const auto Symbol : Entry.second) {
2730b57cec5SDimitry Andric         if (!SymReaper.isLive(Symbol))
2740b57cec5SDimitry Andric           CleanedUpSet = F.remove(CleanedUpSet, Symbol);
2750b57cec5SDimitry Andric       }
2760b57cec5SDimitry Andric       State = CleanedUpSet.isEmpty()
2770b57cec5SDimitry Andric                   ? State->remove<RawPtrMap>(Entry.first)
2780b57cec5SDimitry Andric                   : State->set<RawPtrMap>(Entry.first, CleanedUpSet);
2790b57cec5SDimitry Andric     }
2800b57cec5SDimitry Andric   }
2810b57cec5SDimitry Andric   C.addTransition(State);
2820b57cec5SDimitry Andric }
2830b57cec5SDimitry Andric 
2840b57cec5SDimitry Andric namespace clang {
2850b57cec5SDimitry Andric namespace ento {
2860b57cec5SDimitry Andric namespace allocation_state {
2870b57cec5SDimitry Andric 
2880b57cec5SDimitry Andric std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) {
289a7dea167SDimitry Andric   return std::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym);
2900b57cec5SDimitry Andric }
2910b57cec5SDimitry Andric 
2920b57cec5SDimitry Andric const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) {
2930b57cec5SDimitry Andric   RawPtrMapTy Map = State->get<RawPtrMap>();
294480093f4SDimitry Andric   for (const auto &Entry : Map) {
2950b57cec5SDimitry Andric     if (Entry.second.contains(Sym)) {
2960b57cec5SDimitry Andric       return Entry.first;
2970b57cec5SDimitry Andric     }
2980b57cec5SDimitry Andric   }
2990b57cec5SDimitry Andric   return nullptr;
3000b57cec5SDimitry Andric }
3010b57cec5SDimitry Andric 
3020b57cec5SDimitry Andric } // end namespace allocation_state
3030b57cec5SDimitry Andric } // end namespace ento
3040b57cec5SDimitry Andric } // end namespace clang
3050b57cec5SDimitry Andric 
306a7dea167SDimitry Andric PathDiagnosticPieceRef InnerPointerChecker::InnerPointerBRVisitor::VisitNode(
307a7dea167SDimitry Andric     const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
3080b57cec5SDimitry Andric   if (!isSymbolTracked(N->getState(), PtrToBuf) ||
3090b57cec5SDimitry Andric       isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf))
3100b57cec5SDimitry Andric     return nullptr;
3110b57cec5SDimitry Andric 
312a7dea167SDimitry Andric   const Stmt *S = N->getStmtForDiagnostics();
3130b57cec5SDimitry Andric   if (!S)
3140b57cec5SDimitry Andric     return nullptr;
3150b57cec5SDimitry Andric 
3160b57cec5SDimitry Andric   const MemRegion *ObjRegion =
3170b57cec5SDimitry Andric       allocation_state::getContainerObjRegion(N->getState(), PtrToBuf);
3180b57cec5SDimitry Andric   const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
3190b57cec5SDimitry Andric   QualType ObjTy = TypedRegion->getValueType();
3200b57cec5SDimitry Andric 
3210b57cec5SDimitry Andric   SmallString<256> Buf;
3220b57cec5SDimitry Andric   llvm::raw_svector_ostream OS(Buf);
323*81ad6265SDimitry Andric   OS << "Pointer to inner buffer of '" << ObjTy << "' obtained here";
3240b57cec5SDimitry Andric   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
3250b57cec5SDimitry Andric                              N->getLocationContext());
326a7dea167SDimitry Andric   return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
3270b57cec5SDimitry Andric }
3280b57cec5SDimitry Andric 
3290b57cec5SDimitry Andric void ento::registerInnerPointerChecker(CheckerManager &Mgr) {
3300b57cec5SDimitry Andric   registerInnerPointerCheckerAux(Mgr);
3310b57cec5SDimitry Andric   Mgr.registerChecker<InnerPointerChecker>();
3320b57cec5SDimitry Andric }
3330b57cec5SDimitry Andric 
3345ffd83dbSDimitry Andric bool ento::shouldRegisterInnerPointerChecker(const CheckerManager &mgr) {
3350b57cec5SDimitry Andric   return true;
3360b57cec5SDimitry Andric }
337