xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoOwnershipChangeVisitor.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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