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:
OwnershipBindingsHandler(SymbolRef Sym,OwnerSet & Owners)29*0fca6ea1SDimitry Andric OwnershipBindingsHandler(SymbolRef Sym, OwnerSet &Owners)
30*0fca6ea1SDimitry Andric : Sym(Sym), Owners(Owners) {}
31*0fca6ea1SDimitry Andric
HandleBinding(StoreManager & SMgr,Store Store,const MemRegion * Region,SVal Val)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
dump() const39*0fca6ea1SDimitry Andric LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
dumpToStream(llvm::raw_ostream & out) const40*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
getOwnersAtNode(const ExplodedNode * N)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
getFunctionName(const ExplodedNode * CallEnterN)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
wasModifiedInFunction(const ExplodedNode * CallEnterN,const ExplodedNode * CallExitEndN)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
maybeEmitNoteForParameters(PathSensitiveBugReport & R,const CallEvent & Call,const ExplodedNode * N)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