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