xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp (revision 480093f4440d54b30b3025afeac24b48f2ba7a2e)
1*480093f4SDimitry Andric //=== FuchsiaHandleChecker.cpp - Find handle leaks/double closes -*- C++ -*--=//
2*480093f4SDimitry Andric //
3*480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*480093f4SDimitry Andric //
7*480093f4SDimitry Andric //===----------------------------------------------------------------------===//
8*480093f4SDimitry Andric //
9*480093f4SDimitry Andric // This checker checks if the handle of Fuchsia is properly used according to
10*480093f4SDimitry Andric // following rules.
11*480093f4SDimitry Andric //   - If a handle is acquired, it should be released before execution
12*480093f4SDimitry Andric //        ends.
13*480093f4SDimitry Andric //   - If a handle is released, it should not be released again.
14*480093f4SDimitry Andric //   - If a handle is released, it should not be used for other purposes
15*480093f4SDimitry Andric //        such as I/O.
16*480093f4SDimitry Andric //
17*480093f4SDimitry Andric // In this checker, each tracked handle is associated with a state. When the
18*480093f4SDimitry Andric // handle variable is passed to different function calls or syscalls, its state
19*480093f4SDimitry Andric // changes. The state changes can be generally represented by following ASCII
20*480093f4SDimitry Andric // Art:
21*480093f4SDimitry Andric //
22*480093f4SDimitry Andric //
23*480093f4SDimitry Andric //                              +-+---------v-+         +------------+
24*480093f4SDimitry Andric //       acquire_func succeeded |             | Escape  |            |
25*480093f4SDimitry Andric //            +----------------->  Allocated  +--------->  Escaped   <--+
26*480093f4SDimitry Andric //            |                 |             |         |            |  |
27*480093f4SDimitry Andric //            |                 +-----+------++         +------------+  |
28*480093f4SDimitry Andric //            |                       |      |                          |
29*480093f4SDimitry Andric //            |         release_func  |      +--+                       |
30*480093f4SDimitry Andric //            |                       |         | handle  +--------+    |
31*480093f4SDimitry Andric //            |                       |         | dies    |        |    |
32*480093f4SDimitry Andric //            |                  +----v-----+   +---------> Leaked |    |
33*480093f4SDimitry Andric //            |                  |          |             |(REPORT)|    |
34*480093f4SDimitry Andric // +----------+--+               | Released | Escape      +--------+    |
35*480093f4SDimitry Andric // |             |               |          +---------------------------+
36*480093f4SDimitry Andric // | Not tracked <--+            +----+---+-+
37*480093f4SDimitry Andric // |             |  |                 |   |        As argument by value
38*480093f4SDimitry Andric // +------+------+  |    release_func |   +------+ in function call
39*480093f4SDimitry Andric //        |         |                 |          | or by reference in
40*480093f4SDimitry Andric //        |         |                 |          | use_func call
41*480093f4SDimitry Andric //        +---------+            +----v-----+    |     +-----------+
42*480093f4SDimitry Andric //        acquire_func failed    | Double   |    +-----> Use after |
43*480093f4SDimitry Andric //                               | released |          | released  |
44*480093f4SDimitry Andric //                               | (REPORT) |          | (REPORT)  |
45*480093f4SDimitry Andric //                               +----------+          +-----------+
46*480093f4SDimitry Andric //
47*480093f4SDimitry Andric // acquire_func represents the functions or syscalls that may acquire a handle.
48*480093f4SDimitry Andric // release_func represents the functions or syscalls that may release a handle.
49*480093f4SDimitry Andric // use_func represents the functions or syscall that requires an open handle.
50*480093f4SDimitry Andric //
51*480093f4SDimitry Andric // If a tracked handle dies in "Released" or "Not Tracked" state, we assume it
52*480093f4SDimitry Andric // is properly used. Otherwise a bug and will be reported.
53*480093f4SDimitry Andric //
54*480093f4SDimitry Andric // Note that, the analyzer does not always know for sure if a function failed
55*480093f4SDimitry Andric // or succeeded. In those cases we use the state MaybeAllocated.
56*480093f4SDimitry Andric // Thus, the diagramm above captures the intent, not implementation details.
57*480093f4SDimitry Andric //
58*480093f4SDimitry Andric // Due to the fact that the number of handle related syscalls in Fuchsia
59*480093f4SDimitry Andric // is large, we adopt the annotation attributes to descript syscalls'
60*480093f4SDimitry Andric // operations(acquire/release/use) on handles instead of hardcoding
61*480093f4SDimitry Andric // everything in the checker.
62*480093f4SDimitry Andric //
63*480093f4SDimitry Andric // We use following annotation attributes for handle related syscalls or
64*480093f4SDimitry Andric // functions:
65*480093f4SDimitry Andric //  1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired
66*480093f4SDimitry Andric //  2. __attribute__((release_handle("Fuchsia"))) |handle will be released
67*480093f4SDimitry Andric //  3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to
68*480093f4SDimitry Andric //     escaped state, it also needs to be open.
69*480093f4SDimitry Andric //
70*480093f4SDimitry Andric // For example, an annotated syscall:
71*480093f4SDimitry Andric //   zx_status_t zx_channel_create(
72*480093f4SDimitry Andric //   uint32_t options,
73*480093f4SDimitry Andric //   zx_handle_t* out0 __attribute__((acquire_handle("Fuchsia"))) ,
74*480093f4SDimitry Andric //   zx_handle_t* out1 __attribute__((acquire_handle("Fuchsia"))));
75*480093f4SDimitry Andric // denotes a syscall which will acquire two handles and save them to 'out0' and
76*480093f4SDimitry Andric // 'out1' when succeeded.
77*480093f4SDimitry Andric //
78*480093f4SDimitry Andric //===----------------------------------------------------------------------===//
79*480093f4SDimitry Andric 
80*480093f4SDimitry Andric #include "clang/AST/Attr.h"
81*480093f4SDimitry Andric #include "clang/AST/Decl.h"
82*480093f4SDimitry Andric #include "clang/AST/Type.h"
83*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
84*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
85*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
86*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
87*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
88*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
89*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
90*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
91*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
92*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
93*480093f4SDimitry Andric 
94*480093f4SDimitry Andric using namespace clang;
95*480093f4SDimitry Andric using namespace ento;
96*480093f4SDimitry Andric 
97*480093f4SDimitry Andric namespace {
98*480093f4SDimitry Andric 
99*480093f4SDimitry Andric static const StringRef HandleTypeName = "zx_handle_t";
100*480093f4SDimitry Andric static const StringRef ErrorTypeName = "zx_status_t";
101*480093f4SDimitry Andric 
102*480093f4SDimitry Andric class HandleState {
103*480093f4SDimitry Andric private:
104*480093f4SDimitry Andric   enum class Kind { MaybeAllocated, Allocated, Released, Escaped } K;
105*480093f4SDimitry Andric   SymbolRef ErrorSym;
106*480093f4SDimitry Andric   HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
107*480093f4SDimitry Andric 
108*480093f4SDimitry Andric public:
109*480093f4SDimitry Andric   bool operator==(const HandleState &Other) const {
110*480093f4SDimitry Andric     return K == Other.K && ErrorSym == Other.ErrorSym;
111*480093f4SDimitry Andric   }
112*480093f4SDimitry Andric   bool isAllocated() const { return K == Kind::Allocated; }
113*480093f4SDimitry Andric   bool maybeAllocated() const { return K == Kind::MaybeAllocated; }
114*480093f4SDimitry Andric   bool isReleased() const { return K == Kind::Released; }
115*480093f4SDimitry Andric   bool isEscaped() const { return K == Kind::Escaped; }
116*480093f4SDimitry Andric 
117*480093f4SDimitry Andric   static HandleState getMaybeAllocated(SymbolRef ErrorSym) {
118*480093f4SDimitry Andric     return HandleState(Kind::MaybeAllocated, ErrorSym);
119*480093f4SDimitry Andric   }
120*480093f4SDimitry Andric   static HandleState getAllocated(ProgramStateRef State, HandleState S) {
121*480093f4SDimitry Andric     assert(S.maybeAllocated());
122*480093f4SDimitry Andric     assert(State->getConstraintManager()
123*480093f4SDimitry Andric                .isNull(State, S.getErrorSym())
124*480093f4SDimitry Andric                .isConstrained());
125*480093f4SDimitry Andric     return HandleState(Kind::Allocated, nullptr);
126*480093f4SDimitry Andric   }
127*480093f4SDimitry Andric   static HandleState getReleased() {
128*480093f4SDimitry Andric     return HandleState(Kind::Released, nullptr);
129*480093f4SDimitry Andric   }
130*480093f4SDimitry Andric   static HandleState getEscaped() {
131*480093f4SDimitry Andric     return HandleState(Kind::Escaped, nullptr);
132*480093f4SDimitry Andric   }
133*480093f4SDimitry Andric 
134*480093f4SDimitry Andric   SymbolRef getErrorSym() const { return ErrorSym; }
135*480093f4SDimitry Andric 
136*480093f4SDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const {
137*480093f4SDimitry Andric     ID.AddInteger(static_cast<int>(K));
138*480093f4SDimitry Andric     ID.AddPointer(ErrorSym);
139*480093f4SDimitry Andric   }
140*480093f4SDimitry Andric 
141*480093f4SDimitry Andric   LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
142*480093f4SDimitry Andric     switch (K) {
143*480093f4SDimitry Andric #define CASE(ID)                                                               \
144*480093f4SDimitry Andric   case ID:                                                                     \
145*480093f4SDimitry Andric     OS << #ID;                                                                 \
146*480093f4SDimitry Andric     break;
147*480093f4SDimitry Andric       CASE(Kind::MaybeAllocated)
148*480093f4SDimitry Andric       CASE(Kind::Allocated)
149*480093f4SDimitry Andric       CASE(Kind::Released)
150*480093f4SDimitry Andric       CASE(Kind::Escaped)
151*480093f4SDimitry Andric     }
152*480093f4SDimitry Andric   }
153*480093f4SDimitry Andric 
154*480093f4SDimitry Andric   LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
155*480093f4SDimitry Andric };
156*480093f4SDimitry Andric 
157*480093f4SDimitry Andric template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
158*480093f4SDimitry Andric   return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
159*480093f4SDimitry Andric }
160*480093f4SDimitry Andric 
161*480093f4SDimitry Andric class FuchsiaHandleChecker
162*480093f4SDimitry Andric     : public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
163*480093f4SDimitry Andric                      check::PointerEscape, eval::Assume> {
164*480093f4SDimitry Andric   BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error",
165*480093f4SDimitry Andric                       /*SuppressOnSink=*/true};
166*480093f4SDimitry Andric   BugType DoubleReleaseBugType{this, "Fuchsia handle double release",
167*480093f4SDimitry Andric                                "Fuchsia Handle Error"};
168*480093f4SDimitry Andric   BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release",
169*480093f4SDimitry Andric                                  "Fuchsia Handle Error"};
170*480093f4SDimitry Andric 
171*480093f4SDimitry Andric public:
172*480093f4SDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
173*480093f4SDimitry Andric   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
174*480093f4SDimitry Andric   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
175*480093f4SDimitry Andric   ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
176*480093f4SDimitry Andric                              bool Assumption) const;
177*480093f4SDimitry Andric   ProgramStateRef checkPointerEscape(ProgramStateRef State,
178*480093f4SDimitry Andric                                      const InvalidatedSymbols &Escaped,
179*480093f4SDimitry Andric                                      const CallEvent *Call,
180*480093f4SDimitry Andric                                      PointerEscapeKind Kind) const;
181*480093f4SDimitry Andric 
182*480093f4SDimitry Andric   ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
183*480093f4SDimitry Andric                             CheckerContext &C, ExplodedNode *Pred) const;
184*480093f4SDimitry Andric 
185*480093f4SDimitry Andric   void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,
186*480093f4SDimitry Andric                            CheckerContext &C) const;
187*480093f4SDimitry Andric 
188*480093f4SDimitry Andric   void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,
189*480093f4SDimitry Andric                           CheckerContext &C) const;
190*480093f4SDimitry Andric 
191*480093f4SDimitry Andric   void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
192*480093f4SDimitry Andric                  const SourceRange *Range, const BugType &Type,
193*480093f4SDimitry Andric                  StringRef Msg) const;
194*480093f4SDimitry Andric 
195*480093f4SDimitry Andric   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
196*480093f4SDimitry Andric                   const char *Sep) const override;
197*480093f4SDimitry Andric };
198*480093f4SDimitry Andric } // end anonymous namespace
199*480093f4SDimitry Andric 
200*480093f4SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
201*480093f4SDimitry Andric 
202*480093f4SDimitry Andric static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym,
203*480093f4SDimitry Andric                                           CheckerContext &Ctx) {
204*480093f4SDimitry Andric   ProgramStateRef State = N->getState();
205*480093f4SDimitry Andric   // When bug type is handle leak, exploded node N does not have state info for
206*480093f4SDimitry Andric   // leaking handle. Get the predecessor of N instead.
207*480093f4SDimitry Andric   if (!State->get<HStateMap>(Sym))
208*480093f4SDimitry Andric     N = N->getFirstPred();
209*480093f4SDimitry Andric 
210*480093f4SDimitry Andric   const ExplodedNode *Pred = N;
211*480093f4SDimitry Andric   while (N) {
212*480093f4SDimitry Andric     State = N->getState();
213*480093f4SDimitry Andric     if (!State->get<HStateMap>(Sym)) {
214*480093f4SDimitry Andric       const HandleState *HState = Pred->getState()->get<HStateMap>(Sym);
215*480093f4SDimitry Andric       if (HState && (HState->isAllocated() || HState->maybeAllocated()))
216*480093f4SDimitry Andric         return N;
217*480093f4SDimitry Andric     }
218*480093f4SDimitry Andric     Pred = N;
219*480093f4SDimitry Andric     N = N->getFirstPred();
220*480093f4SDimitry Andric   }
221*480093f4SDimitry Andric   return nullptr;
222*480093f4SDimitry Andric }
223*480093f4SDimitry Andric 
224*480093f4SDimitry Andric /// Returns the symbols extracted from the argument or null if it cannot be
225*480093f4SDimitry Andric /// found.
226*480093f4SDimitry Andric static SymbolRef getFuchsiaHandleSymbol(QualType QT, SVal Arg,
227*480093f4SDimitry Andric                                         ProgramStateRef State) {
228*480093f4SDimitry Andric   int PtrToHandleLevel = 0;
229*480093f4SDimitry Andric   while (QT->isAnyPointerType() || QT->isReferenceType()) {
230*480093f4SDimitry Andric     ++PtrToHandleLevel;
231*480093f4SDimitry Andric     QT = QT->getPointeeType();
232*480093f4SDimitry Andric   }
233*480093f4SDimitry Andric   if (const auto *HandleType = QT->getAs<TypedefType>()) {
234*480093f4SDimitry Andric     if (HandleType->getDecl()->getName() != HandleTypeName)
235*480093f4SDimitry Andric       return nullptr;
236*480093f4SDimitry Andric     if (PtrToHandleLevel > 1) {
237*480093f4SDimitry Andric       // Not supported yet.
238*480093f4SDimitry Andric       return nullptr;
239*480093f4SDimitry Andric     }
240*480093f4SDimitry Andric 
241*480093f4SDimitry Andric     if (PtrToHandleLevel == 0) {
242*480093f4SDimitry Andric       return Arg.getAsSymbol();
243*480093f4SDimitry Andric     } else {
244*480093f4SDimitry Andric       assert(PtrToHandleLevel == 1);
245*480093f4SDimitry Andric       if (Optional<Loc> ArgLoc = Arg.getAs<Loc>())
246*480093f4SDimitry Andric         return State->getSVal(*ArgLoc).getAsSymbol();
247*480093f4SDimitry Andric     }
248*480093f4SDimitry Andric   }
249*480093f4SDimitry Andric   return nullptr;
250*480093f4SDimitry Andric }
251*480093f4SDimitry Andric 
252*480093f4SDimitry Andric void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
253*480093f4SDimitry Andric                                         CheckerContext &C) const {
254*480093f4SDimitry Andric   ProgramStateRef State = C.getState();
255*480093f4SDimitry Andric   const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
256*480093f4SDimitry Andric   if (!FuncDecl) {
257*480093f4SDimitry Andric     // Unknown call, escape by value handles. They are not covered by
258*480093f4SDimitry Andric     // PointerEscape callback.
259*480093f4SDimitry Andric     for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
260*480093f4SDimitry Andric       if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol())
261*480093f4SDimitry Andric         State = State->set<HStateMap>(Handle, HandleState::getEscaped());
262*480093f4SDimitry Andric     }
263*480093f4SDimitry Andric     C.addTransition(State);
264*480093f4SDimitry Andric     return;
265*480093f4SDimitry Andric   }
266*480093f4SDimitry Andric 
267*480093f4SDimitry Andric   for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
268*480093f4SDimitry Andric     if (Arg >= FuncDecl->getNumParams())
269*480093f4SDimitry Andric       break;
270*480093f4SDimitry Andric     const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
271*480093f4SDimitry Andric     SymbolRef Handle =
272*480093f4SDimitry Andric         getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
273*480093f4SDimitry Andric     if (!Handle)
274*480093f4SDimitry Andric       continue;
275*480093f4SDimitry Andric 
276*480093f4SDimitry Andric     // Handled in checkPostCall.
277*480093f4SDimitry Andric     if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
278*480093f4SDimitry Andric         hasFuchsiaAttr<AcquireHandleAttr>(PVD))
279*480093f4SDimitry Andric       continue;
280*480093f4SDimitry Andric 
281*480093f4SDimitry Andric     const HandleState *HState = State->get<HStateMap>(Handle);
282*480093f4SDimitry Andric     if (!HState || HState->isEscaped())
283*480093f4SDimitry Andric       continue;
284*480093f4SDimitry Andric 
285*480093f4SDimitry Andric     if (hasFuchsiaAttr<UseHandleAttr>(PVD) || PVD->getType()->isIntegerType()) {
286*480093f4SDimitry Andric       if (HState->isReleased()) {
287*480093f4SDimitry Andric         reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
288*480093f4SDimitry Andric         return;
289*480093f4SDimitry Andric       }
290*480093f4SDimitry Andric     }
291*480093f4SDimitry Andric     if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
292*480093f4SDimitry Andric         PVD->getType()->isIntegerType()) {
293*480093f4SDimitry Andric       // Working around integer by-value escapes.
294*480093f4SDimitry Andric       State = State->set<HStateMap>(Handle, HandleState::getEscaped());
295*480093f4SDimitry Andric     }
296*480093f4SDimitry Andric   }
297*480093f4SDimitry Andric   C.addTransition(State);
298*480093f4SDimitry Andric }
299*480093f4SDimitry Andric 
300*480093f4SDimitry Andric void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
301*480093f4SDimitry Andric                                          CheckerContext &C) const {
302*480093f4SDimitry Andric   const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
303*480093f4SDimitry Andric   if (!FuncDecl)
304*480093f4SDimitry Andric     return;
305*480093f4SDimitry Andric 
306*480093f4SDimitry Andric   ProgramStateRef State = C.getState();
307*480093f4SDimitry Andric 
308*480093f4SDimitry Andric   std::vector<std::function<std::string(BugReport & BR)>> Notes;
309*480093f4SDimitry Andric   SymbolRef ResultSymbol = nullptr;
310*480093f4SDimitry Andric   if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())
311*480093f4SDimitry Andric     if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
312*480093f4SDimitry Andric       ResultSymbol = Call.getReturnValue().getAsSymbol();
313*480093f4SDimitry Andric 
314*480093f4SDimitry Andric   // Function returns an open handle.
315*480093f4SDimitry Andric   if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
316*480093f4SDimitry Andric     SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
317*480093f4SDimitry Andric     State =
318*480093f4SDimitry Andric         State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));
319*480093f4SDimitry Andric   }
320*480093f4SDimitry Andric 
321*480093f4SDimitry Andric   for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
322*480093f4SDimitry Andric     if (Arg >= FuncDecl->getNumParams())
323*480093f4SDimitry Andric       break;
324*480093f4SDimitry Andric     const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
325*480093f4SDimitry Andric     SymbolRef Handle =
326*480093f4SDimitry Andric         getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
327*480093f4SDimitry Andric     if (!Handle)
328*480093f4SDimitry Andric       continue;
329*480093f4SDimitry Andric 
330*480093f4SDimitry Andric     const HandleState *HState = State->get<HStateMap>(Handle);
331*480093f4SDimitry Andric     if (HState && HState->isEscaped())
332*480093f4SDimitry Andric       continue;
333*480093f4SDimitry Andric     if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
334*480093f4SDimitry Andric       if (HState && HState->isReleased()) {
335*480093f4SDimitry Andric         reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
336*480093f4SDimitry Andric         return;
337*480093f4SDimitry Andric       } else {
338*480093f4SDimitry Andric         Notes.push_back([Handle](BugReport &BR) {
339*480093f4SDimitry Andric           auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
340*480093f4SDimitry Andric           if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
341*480093f4SDimitry Andric             return "Handle released here.";
342*480093f4SDimitry Andric           } else
343*480093f4SDimitry Andric             return "";
344*480093f4SDimitry Andric         });
345*480093f4SDimitry Andric         State = State->set<HStateMap>(Handle, HandleState::getReleased());
346*480093f4SDimitry Andric       }
347*480093f4SDimitry Andric     } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
348*480093f4SDimitry Andric       Notes.push_back([Handle](BugReport &BR) {
349*480093f4SDimitry Andric         auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
350*480093f4SDimitry Andric         if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
351*480093f4SDimitry Andric           return "Handle allocated here.";
352*480093f4SDimitry Andric         } else
353*480093f4SDimitry Andric           return "";
354*480093f4SDimitry Andric       });
355*480093f4SDimitry Andric       State = State->set<HStateMap>(
356*480093f4SDimitry Andric           Handle, HandleState::getMaybeAllocated(ResultSymbol));
357*480093f4SDimitry Andric     }
358*480093f4SDimitry Andric   }
359*480093f4SDimitry Andric   const NoteTag *T = nullptr;
360*480093f4SDimitry Andric   if (!Notes.empty()) {
361*480093f4SDimitry Andric     T = C.getNoteTag(
362*480093f4SDimitry Andric         [this, Notes{std::move(Notes)}](BugReport &BR) -> std::string {
363*480093f4SDimitry Andric           if (&BR.getBugType() != &UseAfterReleaseBugType &&
364*480093f4SDimitry Andric               &BR.getBugType() != &LeakBugType &&
365*480093f4SDimitry Andric               &BR.getBugType() != &DoubleReleaseBugType)
366*480093f4SDimitry Andric             return "";
367*480093f4SDimitry Andric           for (auto &Note : Notes) {
368*480093f4SDimitry Andric             std::string Text = Note(BR);
369*480093f4SDimitry Andric             if (!Text.empty())
370*480093f4SDimitry Andric               return Text;
371*480093f4SDimitry Andric           }
372*480093f4SDimitry Andric           return "";
373*480093f4SDimitry Andric         });
374*480093f4SDimitry Andric   }
375*480093f4SDimitry Andric   C.addTransition(State, T);
376*480093f4SDimitry Andric }
377*480093f4SDimitry Andric 
378*480093f4SDimitry Andric void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
379*480093f4SDimitry Andric                                             CheckerContext &C) const {
380*480093f4SDimitry Andric   ProgramStateRef State = C.getState();
381*480093f4SDimitry Andric   SmallVector<SymbolRef, 2> LeakedSyms;
382*480093f4SDimitry Andric   HStateMapTy TrackedHandles = State->get<HStateMap>();
383*480093f4SDimitry Andric   for (auto &CurItem : TrackedHandles) {
384*480093f4SDimitry Andric     if (!SymReaper.isDead(CurItem.first))
385*480093f4SDimitry Andric       continue;
386*480093f4SDimitry Andric     if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
387*480093f4SDimitry Andric       LeakedSyms.push_back(CurItem.first);
388*480093f4SDimitry Andric     State = State->remove<HStateMap>(CurItem.first);
389*480093f4SDimitry Andric   }
390*480093f4SDimitry Andric 
391*480093f4SDimitry Andric   ExplodedNode *N = C.getPredecessor();
392*480093f4SDimitry Andric   if (!LeakedSyms.empty())
393*480093f4SDimitry Andric     N = reportLeaks(LeakedSyms, C, N);
394*480093f4SDimitry Andric 
395*480093f4SDimitry Andric   C.addTransition(State, N);
396*480093f4SDimitry Andric }
397*480093f4SDimitry Andric 
398*480093f4SDimitry Andric // Acquiring a handle is not always successful. In Fuchsia most functions
399*480093f4SDimitry Andric // return a status code that determines the status of the handle.
400*480093f4SDimitry Andric // When we split the path based on this status code we know that on one
401*480093f4SDimitry Andric // path we do have the handle and on the other path the acquire failed.
402*480093f4SDimitry Andric // This method helps avoiding false positive leak warnings on paths where
403*480093f4SDimitry Andric // the function failed.
404*480093f4SDimitry Andric // Moreover, when a handle is known to be zero (the invalid handle),
405*480093f4SDimitry Andric // we no longer can follow the symbol on the path, becaue the constant
406*480093f4SDimitry Andric // zero will be used instead of the symbol. We also do not need to release
407*480093f4SDimitry Andric // an invalid handle, so we remove the corresponding symbol from the state.
408*480093f4SDimitry Andric ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
409*480093f4SDimitry Andric                                                  SVal Cond,
410*480093f4SDimitry Andric                                                  bool Assumption) const {
411*480093f4SDimitry Andric   // TODO: add notes about successes/fails for APIs.
412*480093f4SDimitry Andric   ConstraintManager &Cmr = State->getConstraintManager();
413*480093f4SDimitry Andric   HStateMapTy TrackedHandles = State->get<HStateMap>();
414*480093f4SDimitry Andric   for (auto &CurItem : TrackedHandles) {
415*480093f4SDimitry Andric     ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first);
416*480093f4SDimitry Andric     if (HandleVal.isConstrainedTrue()) {
417*480093f4SDimitry Andric       // The handle is invalid. We can no longer follow the symbol on this path.
418*480093f4SDimitry Andric       State = State->remove<HStateMap>(CurItem.first);
419*480093f4SDimitry Andric     }
420*480093f4SDimitry Andric     SymbolRef ErrorSym = CurItem.second.getErrorSym();
421*480093f4SDimitry Andric     if (!ErrorSym)
422*480093f4SDimitry Andric       continue;
423*480093f4SDimitry Andric     ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);
424*480093f4SDimitry Andric     if (ErrorVal.isConstrainedTrue()) {
425*480093f4SDimitry Andric       // Allocation succeeded.
426*480093f4SDimitry Andric       if (CurItem.second.maybeAllocated())
427*480093f4SDimitry Andric         State = State->set<HStateMap>(
428*480093f4SDimitry Andric             CurItem.first, HandleState::getAllocated(State, CurItem.second));
429*480093f4SDimitry Andric     } else if (ErrorVal.isConstrainedFalse()) {
430*480093f4SDimitry Andric       // Allocation failed.
431*480093f4SDimitry Andric       if (CurItem.second.maybeAllocated())
432*480093f4SDimitry Andric         State = State->remove<HStateMap>(CurItem.first);
433*480093f4SDimitry Andric     }
434*480093f4SDimitry Andric   }
435*480093f4SDimitry Andric   return State;
436*480093f4SDimitry Andric }
437*480093f4SDimitry Andric 
438*480093f4SDimitry Andric ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(
439*480093f4SDimitry Andric     ProgramStateRef State, const InvalidatedSymbols &Escaped,
440*480093f4SDimitry Andric     const CallEvent *Call, PointerEscapeKind Kind) const {
441*480093f4SDimitry Andric   const FunctionDecl *FuncDecl =
442*480093f4SDimitry Andric       Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr;
443*480093f4SDimitry Andric 
444*480093f4SDimitry Andric   llvm::DenseSet<SymbolRef> UnEscaped;
445*480093f4SDimitry Andric   // Not all calls should escape our symbols.
446*480093f4SDimitry Andric   if (FuncDecl &&
447*480093f4SDimitry Andric       (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall ||
448*480093f4SDimitry Andric        Kind == PSK_EscapeOutParameters)) {
449*480093f4SDimitry Andric     for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
450*480093f4SDimitry Andric       if (Arg >= FuncDecl->getNumParams())
451*480093f4SDimitry Andric         break;
452*480093f4SDimitry Andric       const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
453*480093f4SDimitry Andric       SymbolRef Handle =
454*480093f4SDimitry Andric           getFuchsiaHandleSymbol(PVD->getType(), Call->getArgSVal(Arg), State);
455*480093f4SDimitry Andric       if (!Handle)
456*480093f4SDimitry Andric         continue;
457*480093f4SDimitry Andric       if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
458*480093f4SDimitry Andric           hasFuchsiaAttr<ReleaseHandleAttr>(PVD))
459*480093f4SDimitry Andric         UnEscaped.insert(Handle);
460*480093f4SDimitry Andric     }
461*480093f4SDimitry Andric   }
462*480093f4SDimitry Andric 
463*480093f4SDimitry Andric   // For out params, we have to deal with derived symbols. See
464*480093f4SDimitry Andric   // MacOSKeychainAPIChecker for details.
465*480093f4SDimitry Andric   for (auto I : State->get<HStateMap>()) {
466*480093f4SDimitry Andric     if (Escaped.count(I.first) && !UnEscaped.count(I.first))
467*480093f4SDimitry Andric       State = State->set<HStateMap>(I.first, HandleState::getEscaped());
468*480093f4SDimitry Andric     if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) {
469*480093f4SDimitry Andric       auto ParentSym = SD->getParentSymbol();
470*480093f4SDimitry Andric       if (Escaped.count(ParentSym))
471*480093f4SDimitry Andric         State = State->set<HStateMap>(I.first, HandleState::getEscaped());
472*480093f4SDimitry Andric     }
473*480093f4SDimitry Andric   }
474*480093f4SDimitry Andric 
475*480093f4SDimitry Andric   return State;
476*480093f4SDimitry Andric }
477*480093f4SDimitry Andric 
478*480093f4SDimitry Andric ExplodedNode *
479*480093f4SDimitry Andric FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
480*480093f4SDimitry Andric                                   CheckerContext &C, ExplodedNode *Pred) const {
481*480093f4SDimitry Andric   ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);
482*480093f4SDimitry Andric   for (SymbolRef LeakedHandle : LeakedHandles) {
483*480093f4SDimitry Andric     reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType,
484*480093f4SDimitry Andric               "Potential leak of handle");
485*480093f4SDimitry Andric   }
486*480093f4SDimitry Andric   return ErrNode;
487*480093f4SDimitry Andric }
488*480093f4SDimitry Andric 
489*480093f4SDimitry Andric void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
490*480093f4SDimitry Andric                                                const SourceRange &Range,
491*480093f4SDimitry Andric                                                CheckerContext &C) const {
492*480093f4SDimitry Andric   ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
493*480093f4SDimitry Andric   reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
494*480093f4SDimitry Andric             "Releasing a previously released handle");
495*480093f4SDimitry Andric }
496*480093f4SDimitry Andric 
497*480093f4SDimitry Andric void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
498*480093f4SDimitry Andric                                               const SourceRange &Range,
499*480093f4SDimitry Andric                                               CheckerContext &C) const {
500*480093f4SDimitry Andric   ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
501*480093f4SDimitry Andric   reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,
502*480093f4SDimitry Andric             "Using a previously released handle");
503*480093f4SDimitry Andric }
504*480093f4SDimitry Andric 
505*480093f4SDimitry Andric void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
506*480093f4SDimitry Andric                                      CheckerContext &C,
507*480093f4SDimitry Andric                                      const SourceRange *Range,
508*480093f4SDimitry Andric                                      const BugType &Type, StringRef Msg) const {
509*480093f4SDimitry Andric   if (!ErrorNode)
510*480093f4SDimitry Andric     return;
511*480093f4SDimitry Andric 
512*480093f4SDimitry Andric   std::unique_ptr<PathSensitiveBugReport> R;
513*480093f4SDimitry Andric   if (Type.isSuppressOnSink()) {
514*480093f4SDimitry Andric     const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);
515*480093f4SDimitry Andric     if (AcquireNode) {
516*480093f4SDimitry Andric       PathDiagnosticLocation LocUsedForUniqueing =
517*480093f4SDimitry Andric           PathDiagnosticLocation::createBegin(
518*480093f4SDimitry Andric               AcquireNode->getStmtForDiagnostics(), C.getSourceManager(),
519*480093f4SDimitry Andric               AcquireNode->getLocationContext());
520*480093f4SDimitry Andric 
521*480093f4SDimitry Andric       R = std::make_unique<PathSensitiveBugReport>(
522*480093f4SDimitry Andric           Type, Msg, ErrorNode, LocUsedForUniqueing,
523*480093f4SDimitry Andric           AcquireNode->getLocationContext()->getDecl());
524*480093f4SDimitry Andric     }
525*480093f4SDimitry Andric   }
526*480093f4SDimitry Andric   if (!R)
527*480093f4SDimitry Andric     R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
528*480093f4SDimitry Andric   if (Range)
529*480093f4SDimitry Andric     R->addRange(*Range);
530*480093f4SDimitry Andric   R->markInteresting(Sym);
531*480093f4SDimitry Andric   C.emitReport(std::move(R));
532*480093f4SDimitry Andric }
533*480093f4SDimitry Andric 
534*480093f4SDimitry Andric void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
535*480093f4SDimitry Andric   mgr.registerChecker<FuchsiaHandleChecker>();
536*480093f4SDimitry Andric }
537*480093f4SDimitry Andric 
538*480093f4SDimitry Andric bool ento::shouldRegisterFuchsiaHandleChecker(const LangOptions &LO) {
539*480093f4SDimitry Andric   return true;
540*480093f4SDimitry Andric }
541*480093f4SDimitry Andric 
542*480093f4SDimitry Andric void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
543*480093f4SDimitry Andric                                       const char *NL, const char *Sep) const {
544*480093f4SDimitry Andric 
545*480093f4SDimitry Andric   HStateMapTy StateMap = State->get<HStateMap>();
546*480093f4SDimitry Andric 
547*480093f4SDimitry Andric   if (!StateMap.isEmpty()) {
548*480093f4SDimitry Andric     Out << Sep << "FuchsiaHandleChecker :" << NL;
549*480093f4SDimitry Andric     for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
550*480093f4SDimitry Andric          ++I) {
551*480093f4SDimitry Andric       I.getKey()->dumpToStream(Out);
552*480093f4SDimitry Andric       Out << " : ";
553*480093f4SDimitry Andric       I.getData().dump(Out);
554*480093f4SDimitry Andric       Out << NL;
555*480093f4SDimitry Andric     }
556*480093f4SDimitry Andric   }
557*480093f4SDimitry Andric }
558