Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
35266 views
//=== InnerPointerChecker.cpp -------------------------------------*- C++ -*--//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines a check that marks a raw pointer to a C++ container's9// inner buffer released when the object is destroyed. This information can10// be used by MallocChecker to detect use-after-free problems.11//12//===----------------------------------------------------------------------===//1314#include "AllocationState.h"15#include "InterCheckerAPI.h"16#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"17#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"18#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"19#include "clang/StaticAnalyzer/Core/Checker.h"20#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"21#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"22#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"2324using namespace clang;25using namespace ento;2627// Associate container objects with a set of raw pointer symbols.28REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(PtrSet, SymbolRef)29REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet)303132namespace {3334class InnerPointerChecker35: public Checker<check::DeadSymbols, check::PostCall> {3637CallDescriptionSet InvalidatingMemberFunctions{38CallDescription(CDM::CXXMethod, {"std", "basic_string", "append"}),39CallDescription(CDM::CXXMethod, {"std", "basic_string", "assign"}),40CallDescription(CDM::CXXMethod, {"std", "basic_string", "clear"}),41CallDescription(CDM::CXXMethod, {"std", "basic_string", "erase"}),42CallDescription(CDM::CXXMethod, {"std", "basic_string", "insert"}),43CallDescription(CDM::CXXMethod, {"std", "basic_string", "pop_back"}),44CallDescription(CDM::CXXMethod, {"std", "basic_string", "push_back"}),45CallDescription(CDM::CXXMethod, {"std", "basic_string", "replace"}),46CallDescription(CDM::CXXMethod, {"std", "basic_string", "reserve"}),47CallDescription(CDM::CXXMethod, {"std", "basic_string", "resize"}),48CallDescription(CDM::CXXMethod, {"std", "basic_string", "shrink_to_fit"}),49CallDescription(CDM::CXXMethod, {"std", "basic_string", "swap"})};5051CallDescriptionSet AddressofFunctions{52CallDescription(CDM::SimpleFunc, {"std", "addressof"}),53CallDescription(CDM::SimpleFunc, {"std", "__addressof"})};5455CallDescriptionSet InnerPointerAccessFunctions{56CallDescription(CDM::CXXMethod, {"std", "basic_string", "c_str"}),57CallDescription(CDM::SimpleFunc, {"std", "data"}, 1),58CallDescription(CDM::CXXMethod, {"std", "basic_string", "data"})};5960public:61class InnerPointerBRVisitor : public BugReporterVisitor {62SymbolRef PtrToBuf;6364public:65InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {}6667static void *getTag() {68static int Tag = 0;69return &Tag;70}7172void Profile(llvm::FoldingSetNodeID &ID) const override {73ID.AddPointer(getTag());74}7576PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,77BugReporterContext &BRC,78PathSensitiveBugReport &BR) override;7980// FIXME: Scan the map once in the visitor's constructor and do a direct81// lookup by region.82bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {83RawPtrMapTy Map = State->get<RawPtrMap>();84for (const auto &Entry : Map) {85if (Entry.second.contains(Sym))86return true;87}88return false;89}90};9192/// Check whether the called member function potentially invalidates93/// pointers referring to the container object's inner buffer.94bool isInvalidatingMemberFunction(const CallEvent &Call) const;9596/// Mark pointer symbols associated with the given memory region released97/// in the program state.98void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State,99const MemRegion *ObjRegion,100CheckerContext &C) const;101102/// Standard library functions that take a non-const `basic_string` argument by103/// reference may invalidate its inner pointers. Check for these cases and104/// mark the pointers released.105void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State,106CheckerContext &C) const;107108/// Record the connection between raw pointers referring to a container109/// object's inner buffer and the object's memory region in the program state.110/// Mark potentially invalidated pointers released.111void checkPostCall(const CallEvent &Call, CheckerContext &C) const;112113/// Clean up the program state map.114void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;115};116117} // end anonymous namespace118119bool InnerPointerChecker::isInvalidatingMemberFunction(120const CallEvent &Call) const {121if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) {122OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator();123if (Opc == OO_Equal || Opc == OO_PlusEqual)124return true;125return false;126}127return isa<CXXDestructorCall>(Call) ||128InvalidatingMemberFunctions.contains(Call);129}130131void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,132ProgramStateRef State,133const MemRegion *MR,134CheckerContext &C) const {135if (const PtrSet *PS = State->get<RawPtrMap>(MR)) {136const Expr *Origin = Call.getOriginExpr();137for (const auto Symbol : *PS) {138// NOTE: `Origin` may be null, and will be stored so in the symbol's139// `RefState` in MallocChecker's `RegionState` program state map.140State = allocation_state::markReleased(State, Symbol, Origin);141}142State = State->remove<RawPtrMap>(MR);143C.addTransition(State);144return;145}146}147148void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,149ProgramStateRef State,150CheckerContext &C) const {151if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) {152const FunctionDecl *FD = FC->getDecl();153if (!FD || !FD->isInStdNamespace())154return;155156for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) {157QualType ParamTy = FD->getParamDecl(I)->getType();158if (!ParamTy->isReferenceType() ||159ParamTy->getPointeeType().isConstQualified())160continue;161162// In case of member operator calls, `this` is counted as an163// argument but not as a parameter.164bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC);165unsigned ArgI = isaMemberOpCall ? I+1 : I;166167SVal Arg = FC->getArgSVal(ArgI);168const auto *ArgRegion =169dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion());170if (!ArgRegion)171continue;172173// std::addressof functions accepts a non-const reference as an argument,174// but doesn't modify it.175if (AddressofFunctions.contains(Call))176continue;177178markPtrSymbolsReleased(Call, State, ArgRegion, C);179}180}181}182183// [string.require]184//185// "References, pointers, and iterators referring to the elements of a186// basic_string sequence may be invalidated by the following uses of that187// basic_string object:188//189// -- As an argument to any standard library function taking a reference190// to non-const basic_string as an argument. For example, as an argument to191// non-member functions swap(), operator>>(), and getline(), or as an argument192// to basic_string::swap().193//194// -- Calling non-const member functions, except operator[], at, front, back,195// begin, rbegin, end, and rend."196197void InnerPointerChecker::checkPostCall(const CallEvent &Call,198CheckerContext &C) const {199ProgramStateRef State = C.getState();200201// TODO: Do we need these to be typed?202const TypedValueRegion *ObjRegion = nullptr;203204if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) {205ObjRegion = dyn_cast_or_null<TypedValueRegion>(206ICall->getCXXThisVal().getAsRegion());207208// Check [string.require] / second point.209if (isInvalidatingMemberFunction(Call)) {210markPtrSymbolsReleased(Call, State, ObjRegion, C);211return;212}213}214215if (InnerPointerAccessFunctions.contains(Call)) {216217if (isa<SimpleFunctionCall>(Call)) {218// NOTE: As of now, we only have one free access function: std::data.219// If we add more functions like this in the list, hardcoded220// argument index should be changed.221ObjRegion =222dyn_cast_or_null<TypedValueRegion>(Call.getArgSVal(0).getAsRegion());223}224225if (!ObjRegion)226return;227228SVal RawPtr = Call.getReturnValue();229if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {230// Start tracking this raw pointer by adding it to the set of symbols231// associated with this container object in the program state map.232233PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();234const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion);235PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet();236assert(C.wasInlined || !Set.contains(Sym));237Set = F.add(Set, Sym);238239State = State->set<RawPtrMap>(ObjRegion, Set);240C.addTransition(State);241}242243return;244}245246// Check [string.require] / first point.247checkFunctionArguments(Call, State, C);248}249250void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper,251CheckerContext &C) const {252ProgramStateRef State = C.getState();253PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();254RawPtrMapTy RPM = State->get<RawPtrMap>();255for (const auto &Entry : RPM) {256if (!SymReaper.isLiveRegion(Entry.first)) {257// Due to incomplete destructor support, some dead regions might258// remain in the program state map. Clean them up.259State = State->remove<RawPtrMap>(Entry.first);260}261if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) {262PtrSet CleanedUpSet = *OldSet;263for (const auto Symbol : Entry.second) {264if (!SymReaper.isLive(Symbol))265CleanedUpSet = F.remove(CleanedUpSet, Symbol);266}267State = CleanedUpSet.isEmpty()268? State->remove<RawPtrMap>(Entry.first)269: State->set<RawPtrMap>(Entry.first, CleanedUpSet);270}271}272C.addTransition(State);273}274275namespace clang {276namespace ento {277namespace allocation_state {278279std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) {280return std::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym);281}282283const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) {284RawPtrMapTy Map = State->get<RawPtrMap>();285for (const auto &Entry : Map) {286if (Entry.second.contains(Sym)) {287return Entry.first;288}289}290return nullptr;291}292293} // end namespace allocation_state294} // end namespace ento295} // end namespace clang296297PathDiagnosticPieceRef InnerPointerChecker::InnerPointerBRVisitor::VisitNode(298const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {299if (!isSymbolTracked(N->getState(), PtrToBuf) ||300isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf))301return nullptr;302303const Stmt *S = N->getStmtForDiagnostics();304if (!S)305return nullptr;306307const MemRegion *ObjRegion =308allocation_state::getContainerObjRegion(N->getState(), PtrToBuf);309const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);310QualType ObjTy = TypedRegion->getValueType();311312SmallString<256> Buf;313llvm::raw_svector_ostream OS(Buf);314OS << "Pointer to inner buffer of '" << ObjTy << "' obtained here";315PathDiagnosticLocation Pos(S, BRC.getSourceManager(),316N->getLocationContext());317return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);318}319320void ento::registerInnerPointerChecker(CheckerManager &Mgr) {321registerInnerPointerCheckerAux(Mgr);322Mgr.registerChecker<InnerPointerChecker>();323}324325bool ento::shouldRegisterInnerPointerChecker(const CheckerManager &mgr) {326return true;327}328329330