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