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 "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 170b57cec5SDimitry Andric #include "InterCheckerAPI.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" 210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 230b57cec5SDimitry Andric 240b57cec5SDimitry Andric using namespace clang; 250b57cec5SDimitry Andric using namespace ento; 260b57cec5SDimitry Andric 270b57cec5SDimitry Andric // Associate container objects with a set of raw pointer symbols. 280b57cec5SDimitry Andric REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(PtrSet, SymbolRef) 290b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet) 300b57cec5SDimitry Andric 310b57cec5SDimitry Andric 320b57cec5SDimitry Andric namespace { 330b57cec5SDimitry Andric 340b57cec5SDimitry Andric class InnerPointerChecker 350b57cec5SDimitry Andric : public Checker<check::DeadSymbols, check::PostCall> { 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn, 380b57cec5SDimitry Andric InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn, 390b57cec5SDimitry Andric ShrinkToFitFn, SwapFn; 400b57cec5SDimitry Andric 410b57cec5SDimitry Andric public: 420b57cec5SDimitry Andric class InnerPointerBRVisitor : public BugReporterVisitor { 430b57cec5SDimitry Andric SymbolRef PtrToBuf; 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric public: 460b57cec5SDimitry Andric InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {} 470b57cec5SDimitry Andric 480b57cec5SDimitry Andric static void *getTag() { 490b57cec5SDimitry Andric static int Tag = 0; 500b57cec5SDimitry Andric return &Tag; 510b57cec5SDimitry Andric } 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const override { 540b57cec5SDimitry Andric ID.AddPointer(getTag()); 550b57cec5SDimitry Andric } 560b57cec5SDimitry Andric 57*a7dea167SDimitry Andric virtual PathDiagnosticPieceRef 58*a7dea167SDimitry Andric VisitNode(const ExplodedNode *N, BugReporterContext &BRC, 59*a7dea167SDimitry Andric PathSensitiveBugReport &BR) override; 600b57cec5SDimitry Andric 610b57cec5SDimitry Andric // FIXME: Scan the map once in the visitor's constructor and do a direct 620b57cec5SDimitry Andric // lookup by region. 630b57cec5SDimitry Andric bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) { 640b57cec5SDimitry Andric RawPtrMapTy Map = State->get<RawPtrMap>(); 650b57cec5SDimitry Andric for (const auto Entry : Map) { 660b57cec5SDimitry Andric if (Entry.second.contains(Sym)) 670b57cec5SDimitry Andric return true; 680b57cec5SDimitry Andric } 690b57cec5SDimitry Andric return false; 700b57cec5SDimitry Andric } 710b57cec5SDimitry Andric }; 720b57cec5SDimitry Andric 730b57cec5SDimitry Andric InnerPointerChecker() 740b57cec5SDimitry Andric : AppendFn({"std", "basic_string", "append"}), 750b57cec5SDimitry Andric AssignFn({"std", "basic_string", "assign"}), 760b57cec5SDimitry Andric ClearFn({"std", "basic_string", "clear"}), 770b57cec5SDimitry Andric CStrFn({"std", "basic_string", "c_str"}), 780b57cec5SDimitry Andric DataFn({"std", "basic_string", "data"}), 790b57cec5SDimitry Andric EraseFn({"std", "basic_string", "erase"}), 800b57cec5SDimitry Andric InsertFn({"std", "basic_string", "insert"}), 810b57cec5SDimitry Andric PopBackFn({"std", "basic_string", "pop_back"}), 820b57cec5SDimitry Andric PushBackFn({"std", "basic_string", "push_back"}), 830b57cec5SDimitry Andric ReplaceFn({"std", "basic_string", "replace"}), 840b57cec5SDimitry Andric ReserveFn({"std", "basic_string", "reserve"}), 850b57cec5SDimitry Andric ResizeFn({"std", "basic_string", "resize"}), 860b57cec5SDimitry Andric ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}), 870b57cec5SDimitry Andric SwapFn({"std", "basic_string", "swap"}) {} 880b57cec5SDimitry Andric 890b57cec5SDimitry Andric /// Check whether the called member function potentially invalidates 900b57cec5SDimitry Andric /// pointers referring to the container object's inner buffer. 910b57cec5SDimitry Andric bool isInvalidatingMemberFunction(const CallEvent &Call) const; 920b57cec5SDimitry Andric 930b57cec5SDimitry Andric /// Mark pointer symbols associated with the given memory region released 940b57cec5SDimitry Andric /// in the program state. 950b57cec5SDimitry Andric void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State, 960b57cec5SDimitry Andric const MemRegion *ObjRegion, 970b57cec5SDimitry Andric CheckerContext &C) const; 980b57cec5SDimitry Andric 990b57cec5SDimitry Andric /// Standard library functions that take a non-const `basic_string` argument by 1000b57cec5SDimitry Andric /// reference may invalidate its inner pointers. Check for these cases and 1010b57cec5SDimitry Andric /// mark the pointers released. 1020b57cec5SDimitry Andric void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State, 1030b57cec5SDimitry Andric CheckerContext &C) const; 1040b57cec5SDimitry Andric 1050b57cec5SDimitry Andric /// Record the connection between raw pointers referring to a container 1060b57cec5SDimitry Andric /// object's inner buffer and the object's memory region in the program state. 1070b57cec5SDimitry Andric /// Mark potentially invalidated pointers released. 1080b57cec5SDimitry Andric void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 1090b57cec5SDimitry Andric 1100b57cec5SDimitry Andric /// Clean up the program state map. 1110b57cec5SDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 1120b57cec5SDimitry Andric }; 1130b57cec5SDimitry Andric 1140b57cec5SDimitry Andric } // end anonymous namespace 1150b57cec5SDimitry Andric 1160b57cec5SDimitry Andric bool InnerPointerChecker::isInvalidatingMemberFunction( 1170b57cec5SDimitry Andric const CallEvent &Call) const { 1180b57cec5SDimitry Andric if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) { 1190b57cec5SDimitry Andric OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator(); 1200b57cec5SDimitry Andric if (Opc == OO_Equal || Opc == OO_PlusEqual) 1210b57cec5SDimitry Andric return true; 1220b57cec5SDimitry Andric return false; 1230b57cec5SDimitry Andric } 1240b57cec5SDimitry Andric return (isa<CXXDestructorCall>(Call) || Call.isCalled(AppendFn) || 1250b57cec5SDimitry Andric Call.isCalled(AssignFn) || Call.isCalled(ClearFn) || 1260b57cec5SDimitry Andric Call.isCalled(EraseFn) || Call.isCalled(InsertFn) || 1270b57cec5SDimitry Andric Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) || 1280b57cec5SDimitry Andric Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) || 1290b57cec5SDimitry Andric Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) || 1300b57cec5SDimitry Andric Call.isCalled(SwapFn)); 1310b57cec5SDimitry Andric } 1320b57cec5SDimitry Andric 1330b57cec5SDimitry Andric void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call, 1340b57cec5SDimitry Andric ProgramStateRef State, 1350b57cec5SDimitry Andric const MemRegion *MR, 1360b57cec5SDimitry Andric CheckerContext &C) const { 1370b57cec5SDimitry Andric if (const PtrSet *PS = State->get<RawPtrMap>(MR)) { 1380b57cec5SDimitry Andric const Expr *Origin = Call.getOriginExpr(); 1390b57cec5SDimitry Andric for (const auto Symbol : *PS) { 1400b57cec5SDimitry Andric // NOTE: `Origin` may be null, and will be stored so in the symbol's 1410b57cec5SDimitry Andric // `RefState` in MallocChecker's `RegionState` program state map. 1420b57cec5SDimitry Andric State = allocation_state::markReleased(State, Symbol, Origin); 1430b57cec5SDimitry Andric } 1440b57cec5SDimitry Andric State = State->remove<RawPtrMap>(MR); 1450b57cec5SDimitry Andric C.addTransition(State); 1460b57cec5SDimitry Andric return; 1470b57cec5SDimitry Andric } 1480b57cec5SDimitry Andric } 1490b57cec5SDimitry Andric 1500b57cec5SDimitry Andric void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call, 1510b57cec5SDimitry Andric ProgramStateRef State, 1520b57cec5SDimitry Andric CheckerContext &C) const { 1530b57cec5SDimitry Andric if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) { 1540b57cec5SDimitry Andric const FunctionDecl *FD = FC->getDecl(); 1550b57cec5SDimitry Andric if (!FD || !FD->isInStdNamespace()) 1560b57cec5SDimitry Andric return; 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) { 1590b57cec5SDimitry Andric QualType ParamTy = FD->getParamDecl(I)->getType(); 1600b57cec5SDimitry Andric if (!ParamTy->isReferenceType() || 1610b57cec5SDimitry Andric ParamTy->getPointeeType().isConstQualified()) 1620b57cec5SDimitry Andric continue; 1630b57cec5SDimitry Andric 1640b57cec5SDimitry Andric // In case of member operator calls, `this` is counted as an 1650b57cec5SDimitry Andric // argument but not as a parameter. 1660b57cec5SDimitry Andric bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC); 1670b57cec5SDimitry Andric unsigned ArgI = isaMemberOpCall ? I+1 : I; 1680b57cec5SDimitry Andric 1690b57cec5SDimitry Andric SVal Arg = FC->getArgSVal(ArgI); 1700b57cec5SDimitry Andric const auto *ArgRegion = 1710b57cec5SDimitry Andric dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion()); 1720b57cec5SDimitry Andric if (!ArgRegion) 1730b57cec5SDimitry Andric continue; 1740b57cec5SDimitry Andric 1750b57cec5SDimitry Andric markPtrSymbolsReleased(Call, State, ArgRegion, C); 1760b57cec5SDimitry Andric } 1770b57cec5SDimitry Andric } 1780b57cec5SDimitry Andric } 1790b57cec5SDimitry Andric 1800b57cec5SDimitry Andric // [string.require] 1810b57cec5SDimitry Andric // 1820b57cec5SDimitry Andric // "References, pointers, and iterators referring to the elements of a 1830b57cec5SDimitry Andric // basic_string sequence may be invalidated by the following uses of that 1840b57cec5SDimitry Andric // basic_string object: 1850b57cec5SDimitry Andric // 1860b57cec5SDimitry Andric // -- As an argument to any standard library function taking a reference 1870b57cec5SDimitry Andric // to non-const basic_string as an argument. For example, as an argument to 1880b57cec5SDimitry Andric // non-member functions swap(), operator>>(), and getline(), or as an argument 1890b57cec5SDimitry Andric // to basic_string::swap(). 1900b57cec5SDimitry Andric // 1910b57cec5SDimitry Andric // -- Calling non-const member functions, except operator[], at, front, back, 1920b57cec5SDimitry Andric // begin, rbegin, end, and rend." 1930b57cec5SDimitry Andric 1940b57cec5SDimitry Andric void InnerPointerChecker::checkPostCall(const CallEvent &Call, 1950b57cec5SDimitry Andric CheckerContext &C) const { 1960b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 1970b57cec5SDimitry Andric 1980b57cec5SDimitry Andric if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) { 1990b57cec5SDimitry Andric // TODO: Do we need these to be typed? 2000b57cec5SDimitry Andric const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>( 2010b57cec5SDimitry Andric ICall->getCXXThisVal().getAsRegion()); 2020b57cec5SDimitry Andric if (!ObjRegion) 2030b57cec5SDimitry Andric return; 2040b57cec5SDimitry Andric 2050b57cec5SDimitry Andric if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) { 2060b57cec5SDimitry Andric SVal RawPtr = Call.getReturnValue(); 2070b57cec5SDimitry Andric if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) { 2080b57cec5SDimitry Andric // Start tracking this raw pointer by adding it to the set of symbols 2090b57cec5SDimitry Andric // associated with this container object in the program state map. 2100b57cec5SDimitry Andric 2110b57cec5SDimitry Andric PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>(); 2120b57cec5SDimitry Andric const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion); 2130b57cec5SDimitry Andric PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet(); 2140b57cec5SDimitry Andric assert(C.wasInlined || !Set.contains(Sym)); 2150b57cec5SDimitry Andric Set = F.add(Set, Sym); 2160b57cec5SDimitry Andric 2170b57cec5SDimitry Andric State = State->set<RawPtrMap>(ObjRegion, Set); 2180b57cec5SDimitry Andric C.addTransition(State); 2190b57cec5SDimitry Andric } 2200b57cec5SDimitry Andric return; 2210b57cec5SDimitry Andric } 2220b57cec5SDimitry Andric 2230b57cec5SDimitry Andric // Check [string.require] / second point. 2240b57cec5SDimitry Andric if (isInvalidatingMemberFunction(Call)) { 2250b57cec5SDimitry Andric markPtrSymbolsReleased(Call, State, ObjRegion, C); 2260b57cec5SDimitry Andric return; 2270b57cec5SDimitry Andric } 2280b57cec5SDimitry Andric } 2290b57cec5SDimitry Andric 2300b57cec5SDimitry Andric // Check [string.require] / first point. 2310b57cec5SDimitry Andric checkFunctionArguments(Call, State, C); 2320b57cec5SDimitry Andric } 2330b57cec5SDimitry Andric 2340b57cec5SDimitry Andric void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper, 2350b57cec5SDimitry Andric CheckerContext &C) const { 2360b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 2370b57cec5SDimitry Andric PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>(); 2380b57cec5SDimitry Andric RawPtrMapTy RPM = State->get<RawPtrMap>(); 2390b57cec5SDimitry Andric for (const auto Entry : RPM) { 2400b57cec5SDimitry Andric if (!SymReaper.isLiveRegion(Entry.first)) { 2410b57cec5SDimitry Andric // Due to incomplete destructor support, some dead regions might 2420b57cec5SDimitry Andric // remain in the program state map. Clean them up. 2430b57cec5SDimitry Andric State = State->remove<RawPtrMap>(Entry.first); 2440b57cec5SDimitry Andric } 2450b57cec5SDimitry Andric if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) { 2460b57cec5SDimitry Andric PtrSet CleanedUpSet = *OldSet; 2470b57cec5SDimitry Andric for (const auto Symbol : Entry.second) { 2480b57cec5SDimitry Andric if (!SymReaper.isLive(Symbol)) 2490b57cec5SDimitry Andric CleanedUpSet = F.remove(CleanedUpSet, Symbol); 2500b57cec5SDimitry Andric } 2510b57cec5SDimitry Andric State = CleanedUpSet.isEmpty() 2520b57cec5SDimitry Andric ? State->remove<RawPtrMap>(Entry.first) 2530b57cec5SDimitry Andric : State->set<RawPtrMap>(Entry.first, CleanedUpSet); 2540b57cec5SDimitry Andric } 2550b57cec5SDimitry Andric } 2560b57cec5SDimitry Andric C.addTransition(State); 2570b57cec5SDimitry Andric } 2580b57cec5SDimitry Andric 2590b57cec5SDimitry Andric namespace clang { 2600b57cec5SDimitry Andric namespace ento { 2610b57cec5SDimitry Andric namespace allocation_state { 2620b57cec5SDimitry Andric 2630b57cec5SDimitry Andric std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) { 264*a7dea167SDimitry Andric return std::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym); 2650b57cec5SDimitry Andric } 2660b57cec5SDimitry Andric 2670b57cec5SDimitry Andric const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) { 2680b57cec5SDimitry Andric RawPtrMapTy Map = State->get<RawPtrMap>(); 2690b57cec5SDimitry Andric for (const auto Entry : Map) { 2700b57cec5SDimitry Andric if (Entry.second.contains(Sym)) { 2710b57cec5SDimitry Andric return Entry.first; 2720b57cec5SDimitry Andric } 2730b57cec5SDimitry Andric } 2740b57cec5SDimitry Andric return nullptr; 2750b57cec5SDimitry Andric } 2760b57cec5SDimitry Andric 2770b57cec5SDimitry Andric } // end namespace allocation_state 2780b57cec5SDimitry Andric } // end namespace ento 2790b57cec5SDimitry Andric } // end namespace clang 2800b57cec5SDimitry Andric 281*a7dea167SDimitry Andric PathDiagnosticPieceRef InnerPointerChecker::InnerPointerBRVisitor::VisitNode( 282*a7dea167SDimitry Andric const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) { 2830b57cec5SDimitry Andric if (!isSymbolTracked(N->getState(), PtrToBuf) || 2840b57cec5SDimitry Andric isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf)) 2850b57cec5SDimitry Andric return nullptr; 2860b57cec5SDimitry Andric 287*a7dea167SDimitry Andric const Stmt *S = N->getStmtForDiagnostics(); 2880b57cec5SDimitry Andric if (!S) 2890b57cec5SDimitry Andric return nullptr; 2900b57cec5SDimitry Andric 2910b57cec5SDimitry Andric const MemRegion *ObjRegion = 2920b57cec5SDimitry Andric allocation_state::getContainerObjRegion(N->getState(), PtrToBuf); 2930b57cec5SDimitry Andric const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion); 2940b57cec5SDimitry Andric QualType ObjTy = TypedRegion->getValueType(); 2950b57cec5SDimitry Andric 2960b57cec5SDimitry Andric SmallString<256> Buf; 2970b57cec5SDimitry Andric llvm::raw_svector_ostream OS(Buf); 2980b57cec5SDimitry Andric OS << "Pointer to inner buffer of '" << ObjTy.getAsString() 2990b57cec5SDimitry Andric << "' obtained here"; 3000b57cec5SDimitry Andric PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 3010b57cec5SDimitry Andric N->getLocationContext()); 302*a7dea167SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); 3030b57cec5SDimitry Andric } 3040b57cec5SDimitry Andric 3050b57cec5SDimitry Andric void ento::registerInnerPointerChecker(CheckerManager &Mgr) { 3060b57cec5SDimitry Andric registerInnerPointerCheckerAux(Mgr); 3070b57cec5SDimitry Andric Mgr.registerChecker<InnerPointerChecker>(); 3080b57cec5SDimitry Andric } 3090b57cec5SDimitry Andric 3100b57cec5SDimitry Andric bool ento::shouldRegisterInnerPointerChecker(const LangOptions &LO) { 3110b57cec5SDimitry Andric return true; 3120b57cec5SDimitry Andric } 313