1*0fca6ea1SDimitry Andric //===--------------------------------------------------------------*- C++ -*--// 2*0fca6ea1SDimitry Andric // 3*0fca6ea1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0fca6ea1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0fca6ea1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0fca6ea1SDimitry Andric // 7*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 8*0fca6ea1SDimitry Andric 9*0fca6ea1SDimitry Andric #include "NoOwnershipChangeVisitor.h" 10*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" 11*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 12*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 13*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 14*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" 15*0fca6ea1SDimitry Andric #include "llvm/ADT/SetOperations.h" 16*0fca6ea1SDimitry Andric 17*0fca6ea1SDimitry Andric using namespace clang; 18*0fca6ea1SDimitry Andric using namespace ento; 19*0fca6ea1SDimitry Andric using OwnerSet = NoOwnershipChangeVisitor::OwnerSet; 20*0fca6ea1SDimitry Andric 21*0fca6ea1SDimitry Andric namespace { 22*0fca6ea1SDimitry Andric // Collect which entities point to the allocated memory, and could be 23*0fca6ea1SDimitry Andric // responsible for deallocating it. 24*0fca6ea1SDimitry Andric class OwnershipBindingsHandler : public StoreManager::BindingsHandler { 25*0fca6ea1SDimitry Andric SymbolRef Sym; 26*0fca6ea1SDimitry Andric OwnerSet &Owners; 27*0fca6ea1SDimitry Andric 28*0fca6ea1SDimitry Andric public: 29*0fca6ea1SDimitry Andric OwnershipBindingsHandler(SymbolRef Sym, OwnerSet &Owners) 30*0fca6ea1SDimitry Andric : Sym(Sym), Owners(Owners) {} 31*0fca6ea1SDimitry Andric 32*0fca6ea1SDimitry Andric bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *Region, 33*0fca6ea1SDimitry Andric SVal Val) override { 34*0fca6ea1SDimitry Andric if (Val.getAsSymbol() == Sym) 35*0fca6ea1SDimitry Andric Owners.insert(Region); 36*0fca6ea1SDimitry Andric return true; 37*0fca6ea1SDimitry Andric } 38*0fca6ea1SDimitry Andric 39*0fca6ea1SDimitry Andric LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); } 40*0fca6ea1SDimitry Andric LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &out) const { 41*0fca6ea1SDimitry Andric out << "Owners: {\n"; 42*0fca6ea1SDimitry Andric for (const MemRegion *Owner : Owners) { 43*0fca6ea1SDimitry Andric out << " "; 44*0fca6ea1SDimitry Andric Owner->dumpToStream(out); 45*0fca6ea1SDimitry Andric out << ",\n"; 46*0fca6ea1SDimitry Andric } 47*0fca6ea1SDimitry Andric out << "}\n"; 48*0fca6ea1SDimitry Andric } 49*0fca6ea1SDimitry Andric }; 50*0fca6ea1SDimitry Andric } // namespace 51*0fca6ea1SDimitry Andric 52*0fca6ea1SDimitry Andric OwnerSet NoOwnershipChangeVisitor::getOwnersAtNode(const ExplodedNode *N) { 53*0fca6ea1SDimitry Andric OwnerSet Ret; 54*0fca6ea1SDimitry Andric 55*0fca6ea1SDimitry Andric ProgramStateRef State = N->getState(); 56*0fca6ea1SDimitry Andric OwnershipBindingsHandler Handler{Sym, Ret}; 57*0fca6ea1SDimitry Andric State->getStateManager().getStoreManager().iterBindings(State->getStore(), 58*0fca6ea1SDimitry Andric Handler); 59*0fca6ea1SDimitry Andric return Ret; 60*0fca6ea1SDimitry Andric } 61*0fca6ea1SDimitry Andric 62*0fca6ea1SDimitry Andric LLVM_DUMP_METHOD std::string 63*0fca6ea1SDimitry Andric NoOwnershipChangeVisitor::getFunctionName(const ExplodedNode *CallEnterN) { 64*0fca6ea1SDimitry Andric if (const CallExpr *CE = llvm::dyn_cast_or_null<CallExpr>( 65*0fca6ea1SDimitry Andric CallEnterN->getLocationAs<CallEnter>()->getCallExpr())) 66*0fca6ea1SDimitry Andric if (const FunctionDecl *FD = CE->getDirectCallee()) 67*0fca6ea1SDimitry Andric return FD->getQualifiedNameAsString(); 68*0fca6ea1SDimitry Andric return ""; 69*0fca6ea1SDimitry Andric } 70*0fca6ea1SDimitry Andric 71*0fca6ea1SDimitry Andric bool NoOwnershipChangeVisitor::wasModifiedInFunction( 72*0fca6ea1SDimitry Andric const ExplodedNode *CallEnterN, const ExplodedNode *CallExitEndN) { 73*0fca6ea1SDimitry Andric const Decl *Callee = 74*0fca6ea1SDimitry Andric CallExitEndN->getFirstPred()->getLocationContext()->getDecl(); 75*0fca6ea1SDimitry Andric const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee); 76*0fca6ea1SDimitry Andric 77*0fca6ea1SDimitry Andric // Given that the stack frame was entered, the body should always be 78*0fca6ea1SDimitry Andric // theoretically obtainable. In case of body farms, the synthesized body 79*0fca6ea1SDimitry Andric // is not attached to declaration, thus triggering the '!FD->hasBody()' 80*0fca6ea1SDimitry Andric // branch. That said, would a synthesized body ever intend to handle 81*0fca6ea1SDimitry Andric // ownership? As of today they don't. And if they did, how would we 82*0fca6ea1SDimitry Andric // put notes inside it, given that it doesn't match any source locations? 83*0fca6ea1SDimitry Andric if (!FD || !FD->hasBody()) 84*0fca6ea1SDimitry Andric return false; 85*0fca6ea1SDimitry Andric if (!doesFnIntendToHandleOwnership( 86*0fca6ea1SDimitry Andric Callee, 87*0fca6ea1SDimitry Andric CallExitEndN->getState()->getAnalysisManager().getASTContext())) 88*0fca6ea1SDimitry Andric return true; 89*0fca6ea1SDimitry Andric 90*0fca6ea1SDimitry Andric if (hasResourceStateChanged(CallEnterN->getState(), CallExitEndN->getState())) 91*0fca6ea1SDimitry Andric return true; 92*0fca6ea1SDimitry Andric 93*0fca6ea1SDimitry Andric OwnerSet CurrOwners = getOwnersAtNode(CallEnterN); 94*0fca6ea1SDimitry Andric OwnerSet ExitOwners = getOwnersAtNode(CallExitEndN); 95*0fca6ea1SDimitry Andric 96*0fca6ea1SDimitry Andric // Owners in the current set may be purged from the analyzer later on. 97*0fca6ea1SDimitry Andric // If a variable is dead (is not referenced directly or indirectly after 98*0fca6ea1SDimitry Andric // some point), it will be removed from the Store before the end of its 99*0fca6ea1SDimitry Andric // actual lifetime. 100*0fca6ea1SDimitry Andric // This means that if the ownership status didn't change, CurrOwners 101*0fca6ea1SDimitry Andric // must be a superset of, but not necessarily equal to ExitOwners. 102*0fca6ea1SDimitry Andric return !llvm::set_is_subset(ExitOwners, CurrOwners); 103*0fca6ea1SDimitry Andric } 104*0fca6ea1SDimitry Andric 105*0fca6ea1SDimitry Andric PathDiagnosticPieceRef NoOwnershipChangeVisitor::maybeEmitNoteForParameters( 106*0fca6ea1SDimitry Andric PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N) { 107*0fca6ea1SDimitry Andric // TODO: Factor the logic of "what constitutes as an entity being passed 108*0fca6ea1SDimitry Andric // into a function call" out by reusing the code in 109*0fca6ea1SDimitry Andric // NoStoreFuncVisitor::maybeEmitNoteForParameters, maybe by incorporating 110*0fca6ea1SDimitry Andric // the printing technology in UninitializedObject's FieldChainInfo. 111*0fca6ea1SDimitry Andric ArrayRef<ParmVarDecl *> Parameters = Call.parameters(); 112*0fca6ea1SDimitry Andric for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) { 113*0fca6ea1SDimitry Andric SVal V = Call.getArgSVal(I); 114*0fca6ea1SDimitry Andric if (V.getAsSymbol() == Sym) 115*0fca6ea1SDimitry Andric return emitNote(N); 116*0fca6ea1SDimitry Andric } 117*0fca6ea1SDimitry Andric return nullptr; 118*0fca6ea1SDimitry Andric } 119