1480093f4SDimitry Andric //=== FuchsiaHandleChecker.cpp - Find handle leaks/double closes -*- C++ -*--=//
2480093f4SDimitry Andric //
3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6480093f4SDimitry Andric //
7480093f4SDimitry Andric //===----------------------------------------------------------------------===//
8480093f4SDimitry Andric //
9480093f4SDimitry Andric // This checker checks if the handle of Fuchsia is properly used according to
10480093f4SDimitry Andric // following rules.
11480093f4SDimitry Andric //   - If a handle is acquired, it should be released before execution
12480093f4SDimitry Andric //        ends.
13480093f4SDimitry Andric //   - If a handle is released, it should not be released again.
14480093f4SDimitry Andric //   - If a handle is released, it should not be used for other purposes
15480093f4SDimitry Andric //        such as I/O.
16480093f4SDimitry Andric //
17480093f4SDimitry Andric // In this checker, each tracked handle is associated with a state. When the
18480093f4SDimitry Andric // handle variable is passed to different function calls or syscalls, its state
19480093f4SDimitry Andric // changes. The state changes can be generally represented by following ASCII
20480093f4SDimitry Andric // Art:
21480093f4SDimitry Andric //
22480093f4SDimitry Andric //
23480093f4SDimitry Andric //                              +-+---------v-+         +------------+
24480093f4SDimitry Andric //       acquire_func succeeded |             | Escape  |            |
25480093f4SDimitry Andric //            +----------------->  Allocated  +--------->  Escaped   <--+
26480093f4SDimitry Andric //            |                 |             |         |            |  |
27480093f4SDimitry Andric //            |                 +-----+------++         +------------+  |
28480093f4SDimitry Andric //            |                       |      |                          |
29480093f4SDimitry Andric //            |         release_func  |      +--+                       |
30480093f4SDimitry Andric //            |                       |         | handle  +--------+    |
31480093f4SDimitry Andric //            |                       |         | dies    |        |    |
32480093f4SDimitry Andric //            |                  +----v-----+   +---------> Leaked |    |
33480093f4SDimitry Andric //            |                  |          |             |(REPORT)|    |
34480093f4SDimitry Andric // +----------+--+               | Released | Escape      +--------+    |
35480093f4SDimitry Andric // |             |               |          +---------------------------+
36480093f4SDimitry Andric // | Not tracked <--+            +----+---+-+
37480093f4SDimitry Andric // |             |  |                 |   |        As argument by value
38480093f4SDimitry Andric // +------+------+  |    release_func |   +------+ in function call
39480093f4SDimitry Andric //        |         |                 |          | or by reference in
40480093f4SDimitry Andric //        |         |                 |          | use_func call
41480093f4SDimitry Andric //        +---------+            +----v-----+    |     +-----------+
42480093f4SDimitry Andric //        acquire_func failed    | Double   |    +-----> Use after |
43480093f4SDimitry Andric //                               | released |          | released  |
44480093f4SDimitry Andric //                               | (REPORT) |          | (REPORT)  |
45480093f4SDimitry Andric //                               +----------+          +-----------+
46480093f4SDimitry Andric //
47480093f4SDimitry Andric // acquire_func represents the functions or syscalls that may acquire a handle.
48480093f4SDimitry Andric // release_func represents the functions or syscalls that may release a handle.
49480093f4SDimitry Andric // use_func represents the functions or syscall that requires an open handle.
50480093f4SDimitry Andric //
51480093f4SDimitry Andric // If a tracked handle dies in "Released" or "Not Tracked" state, we assume it
52480093f4SDimitry Andric // is properly used. Otherwise a bug and will be reported.
53480093f4SDimitry Andric //
54480093f4SDimitry Andric // Note that, the analyzer does not always know for sure if a function failed
55480093f4SDimitry Andric // or succeeded. In those cases we use the state MaybeAllocated.
56480093f4SDimitry Andric // Thus, the diagramm above captures the intent, not implementation details.
57480093f4SDimitry Andric //
58480093f4SDimitry Andric // Due to the fact that the number of handle related syscalls in Fuchsia
59480093f4SDimitry Andric // is large, we adopt the annotation attributes to descript syscalls'
60480093f4SDimitry Andric // operations(acquire/release/use) on handles instead of hardcoding
61480093f4SDimitry Andric // everything in the checker.
62480093f4SDimitry Andric //
63480093f4SDimitry Andric // We use following annotation attributes for handle related syscalls or
64480093f4SDimitry Andric // functions:
65480093f4SDimitry Andric //  1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired
66480093f4SDimitry Andric //  2. __attribute__((release_handle("Fuchsia"))) |handle will be released
67480093f4SDimitry Andric //  3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to
68480093f4SDimitry Andric //     escaped state, it also needs to be open.
69480093f4SDimitry Andric //
70480093f4SDimitry Andric // For example, an annotated syscall:
71480093f4SDimitry Andric //   zx_status_t zx_channel_create(
72480093f4SDimitry Andric //   uint32_t options,
73480093f4SDimitry Andric //   zx_handle_t* out0 __attribute__((acquire_handle("Fuchsia"))) ,
74480093f4SDimitry Andric //   zx_handle_t* out1 __attribute__((acquire_handle("Fuchsia"))));
75480093f4SDimitry Andric // denotes a syscall which will acquire two handles and save them to 'out0' and
76480093f4SDimitry Andric // 'out1' when succeeded.
77480093f4SDimitry Andric //
78480093f4SDimitry Andric //===----------------------------------------------------------------------===//
79480093f4SDimitry Andric 
80480093f4SDimitry Andric #include "clang/AST/Attr.h"
81480093f4SDimitry Andric #include "clang/AST/Decl.h"
82480093f4SDimitry Andric #include "clang/AST/Type.h"
83480093f4SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
84480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
85480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
86480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
87480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
88480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
89480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
90480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
91480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
92480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
93*5ffd83dbSDimitry Andric #include "llvm/ADT/StringExtras.h"
94480093f4SDimitry Andric 
95480093f4SDimitry Andric using namespace clang;
96480093f4SDimitry Andric using namespace ento;
97480093f4SDimitry Andric 
98480093f4SDimitry Andric namespace {
99480093f4SDimitry Andric 
100480093f4SDimitry Andric static const StringRef HandleTypeName = "zx_handle_t";
101480093f4SDimitry Andric static const StringRef ErrorTypeName = "zx_status_t";
102480093f4SDimitry Andric 
103480093f4SDimitry Andric class HandleState {
104480093f4SDimitry Andric private:
105480093f4SDimitry Andric   enum class Kind { MaybeAllocated, Allocated, Released, Escaped } K;
106480093f4SDimitry Andric   SymbolRef ErrorSym;
107480093f4SDimitry Andric   HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
108480093f4SDimitry Andric 
109480093f4SDimitry Andric public:
110480093f4SDimitry Andric   bool operator==(const HandleState &Other) const {
111480093f4SDimitry Andric     return K == Other.K && ErrorSym == Other.ErrorSym;
112480093f4SDimitry Andric   }
113480093f4SDimitry Andric   bool isAllocated() const { return K == Kind::Allocated; }
114480093f4SDimitry Andric   bool maybeAllocated() const { return K == Kind::MaybeAllocated; }
115480093f4SDimitry Andric   bool isReleased() const { return K == Kind::Released; }
116480093f4SDimitry Andric   bool isEscaped() const { return K == Kind::Escaped; }
117480093f4SDimitry Andric 
118480093f4SDimitry Andric   static HandleState getMaybeAllocated(SymbolRef ErrorSym) {
119480093f4SDimitry Andric     return HandleState(Kind::MaybeAllocated, ErrorSym);
120480093f4SDimitry Andric   }
121480093f4SDimitry Andric   static HandleState getAllocated(ProgramStateRef State, HandleState S) {
122480093f4SDimitry Andric     assert(S.maybeAllocated());
123480093f4SDimitry Andric     assert(State->getConstraintManager()
124480093f4SDimitry Andric                .isNull(State, S.getErrorSym())
125480093f4SDimitry Andric                .isConstrained());
126480093f4SDimitry Andric     return HandleState(Kind::Allocated, nullptr);
127480093f4SDimitry Andric   }
128480093f4SDimitry Andric   static HandleState getReleased() {
129480093f4SDimitry Andric     return HandleState(Kind::Released, nullptr);
130480093f4SDimitry Andric   }
131480093f4SDimitry Andric   static HandleState getEscaped() {
132480093f4SDimitry Andric     return HandleState(Kind::Escaped, nullptr);
133480093f4SDimitry Andric   }
134480093f4SDimitry Andric 
135480093f4SDimitry Andric   SymbolRef getErrorSym() const { return ErrorSym; }
136480093f4SDimitry Andric 
137480093f4SDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const {
138480093f4SDimitry Andric     ID.AddInteger(static_cast<int>(K));
139480093f4SDimitry Andric     ID.AddPointer(ErrorSym);
140480093f4SDimitry Andric   }
141480093f4SDimitry Andric 
142480093f4SDimitry Andric   LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
143480093f4SDimitry Andric     switch (K) {
144480093f4SDimitry Andric #define CASE(ID)                                                               \
145480093f4SDimitry Andric   case ID:                                                                     \
146480093f4SDimitry Andric     OS << #ID;                                                                 \
147480093f4SDimitry Andric     break;
148480093f4SDimitry Andric       CASE(Kind::MaybeAllocated)
149480093f4SDimitry Andric       CASE(Kind::Allocated)
150480093f4SDimitry Andric       CASE(Kind::Released)
151480093f4SDimitry Andric       CASE(Kind::Escaped)
152480093f4SDimitry Andric     }
153*5ffd83dbSDimitry Andric     if (ErrorSym) {
154*5ffd83dbSDimitry Andric       OS << " ErrorSym: ";
155*5ffd83dbSDimitry Andric       ErrorSym->dumpToStream(OS);
156*5ffd83dbSDimitry Andric     }
157480093f4SDimitry Andric   }
158480093f4SDimitry Andric 
159480093f4SDimitry Andric   LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
160480093f4SDimitry Andric };
161480093f4SDimitry Andric 
162480093f4SDimitry Andric template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
163480093f4SDimitry Andric   return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
164480093f4SDimitry Andric }
165480093f4SDimitry Andric 
166480093f4SDimitry Andric class FuchsiaHandleChecker
167480093f4SDimitry Andric     : public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
168480093f4SDimitry Andric                      check::PointerEscape, eval::Assume> {
169480093f4SDimitry Andric   BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error",
170480093f4SDimitry Andric                       /*SuppressOnSink=*/true};
171480093f4SDimitry Andric   BugType DoubleReleaseBugType{this, "Fuchsia handle double release",
172480093f4SDimitry Andric                                "Fuchsia Handle Error"};
173480093f4SDimitry Andric   BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release",
174480093f4SDimitry Andric                                  "Fuchsia Handle Error"};
175480093f4SDimitry Andric 
176480093f4SDimitry Andric public:
177480093f4SDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
178480093f4SDimitry Andric   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
179480093f4SDimitry Andric   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
180480093f4SDimitry Andric   ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
181480093f4SDimitry Andric                              bool Assumption) const;
182480093f4SDimitry Andric   ProgramStateRef checkPointerEscape(ProgramStateRef State,
183480093f4SDimitry Andric                                      const InvalidatedSymbols &Escaped,
184480093f4SDimitry Andric                                      const CallEvent *Call,
185480093f4SDimitry Andric                                      PointerEscapeKind Kind) const;
186480093f4SDimitry Andric 
187480093f4SDimitry Andric   ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
188480093f4SDimitry Andric                             CheckerContext &C, ExplodedNode *Pred) const;
189480093f4SDimitry Andric 
190480093f4SDimitry Andric   void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,
191480093f4SDimitry Andric                            CheckerContext &C) const;
192480093f4SDimitry Andric 
193480093f4SDimitry Andric   void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,
194480093f4SDimitry Andric                           CheckerContext &C) const;
195480093f4SDimitry Andric 
196480093f4SDimitry Andric   void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
197480093f4SDimitry Andric                  const SourceRange *Range, const BugType &Type,
198480093f4SDimitry Andric                  StringRef Msg) const;
199480093f4SDimitry Andric 
200480093f4SDimitry Andric   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
201480093f4SDimitry Andric                   const char *Sep) const override;
202480093f4SDimitry Andric };
203480093f4SDimitry Andric } // end anonymous namespace
204480093f4SDimitry Andric 
205480093f4SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
206480093f4SDimitry Andric 
207480093f4SDimitry Andric static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym,
208480093f4SDimitry Andric                                           CheckerContext &Ctx) {
209480093f4SDimitry Andric   ProgramStateRef State = N->getState();
210480093f4SDimitry Andric   // When bug type is handle leak, exploded node N does not have state info for
211480093f4SDimitry Andric   // leaking handle. Get the predecessor of N instead.
212480093f4SDimitry Andric   if (!State->get<HStateMap>(Sym))
213480093f4SDimitry Andric     N = N->getFirstPred();
214480093f4SDimitry Andric 
215480093f4SDimitry Andric   const ExplodedNode *Pred = N;
216480093f4SDimitry Andric   while (N) {
217480093f4SDimitry Andric     State = N->getState();
218480093f4SDimitry Andric     if (!State->get<HStateMap>(Sym)) {
219480093f4SDimitry Andric       const HandleState *HState = Pred->getState()->get<HStateMap>(Sym);
220480093f4SDimitry Andric       if (HState && (HState->isAllocated() || HState->maybeAllocated()))
221480093f4SDimitry Andric         return N;
222480093f4SDimitry Andric     }
223480093f4SDimitry Andric     Pred = N;
224480093f4SDimitry Andric     N = N->getFirstPred();
225480093f4SDimitry Andric   }
226480093f4SDimitry Andric   return nullptr;
227480093f4SDimitry Andric }
228480093f4SDimitry Andric 
229480093f4SDimitry Andric /// Returns the symbols extracted from the argument or null if it cannot be
230480093f4SDimitry Andric /// found.
231480093f4SDimitry Andric static SymbolRef getFuchsiaHandleSymbol(QualType QT, SVal Arg,
232480093f4SDimitry Andric                                         ProgramStateRef State) {
233480093f4SDimitry Andric   int PtrToHandleLevel = 0;
234480093f4SDimitry Andric   while (QT->isAnyPointerType() || QT->isReferenceType()) {
235480093f4SDimitry Andric     ++PtrToHandleLevel;
236480093f4SDimitry Andric     QT = QT->getPointeeType();
237480093f4SDimitry Andric   }
238480093f4SDimitry Andric   if (const auto *HandleType = QT->getAs<TypedefType>()) {
239480093f4SDimitry Andric     if (HandleType->getDecl()->getName() != HandleTypeName)
240480093f4SDimitry Andric       return nullptr;
241480093f4SDimitry Andric     if (PtrToHandleLevel > 1) {
242480093f4SDimitry Andric       // Not supported yet.
243480093f4SDimitry Andric       return nullptr;
244480093f4SDimitry Andric     }
245480093f4SDimitry Andric 
246480093f4SDimitry Andric     if (PtrToHandleLevel == 0) {
247480093f4SDimitry Andric       return Arg.getAsSymbol();
248480093f4SDimitry Andric     } else {
249480093f4SDimitry Andric       assert(PtrToHandleLevel == 1);
250480093f4SDimitry Andric       if (Optional<Loc> ArgLoc = Arg.getAs<Loc>())
251480093f4SDimitry Andric         return State->getSVal(*ArgLoc).getAsSymbol();
252480093f4SDimitry Andric     }
253480093f4SDimitry Andric   }
254480093f4SDimitry Andric   return nullptr;
255480093f4SDimitry Andric }
256480093f4SDimitry Andric 
257480093f4SDimitry Andric void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
258480093f4SDimitry Andric                                         CheckerContext &C) const {
259480093f4SDimitry Andric   ProgramStateRef State = C.getState();
260480093f4SDimitry Andric   const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
261480093f4SDimitry Andric   if (!FuncDecl) {
262480093f4SDimitry Andric     // Unknown call, escape by value handles. They are not covered by
263480093f4SDimitry Andric     // PointerEscape callback.
264480093f4SDimitry Andric     for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
265480093f4SDimitry Andric       if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol())
266480093f4SDimitry Andric         State = State->set<HStateMap>(Handle, HandleState::getEscaped());
267480093f4SDimitry Andric     }
268480093f4SDimitry Andric     C.addTransition(State);
269480093f4SDimitry Andric     return;
270480093f4SDimitry Andric   }
271480093f4SDimitry Andric 
272480093f4SDimitry Andric   for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
273480093f4SDimitry Andric     if (Arg >= FuncDecl->getNumParams())
274480093f4SDimitry Andric       break;
275480093f4SDimitry Andric     const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
276480093f4SDimitry Andric     SymbolRef Handle =
277480093f4SDimitry Andric         getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
278480093f4SDimitry Andric     if (!Handle)
279480093f4SDimitry Andric       continue;
280480093f4SDimitry Andric 
281480093f4SDimitry Andric     // Handled in checkPostCall.
282480093f4SDimitry Andric     if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
283480093f4SDimitry Andric         hasFuchsiaAttr<AcquireHandleAttr>(PVD))
284480093f4SDimitry Andric       continue;
285480093f4SDimitry Andric 
286480093f4SDimitry Andric     const HandleState *HState = State->get<HStateMap>(Handle);
287480093f4SDimitry Andric     if (!HState || HState->isEscaped())
288480093f4SDimitry Andric       continue;
289480093f4SDimitry Andric 
290480093f4SDimitry Andric     if (hasFuchsiaAttr<UseHandleAttr>(PVD) || PVD->getType()->isIntegerType()) {
291480093f4SDimitry Andric       if (HState->isReleased()) {
292480093f4SDimitry Andric         reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
293480093f4SDimitry Andric         return;
294480093f4SDimitry Andric       }
295480093f4SDimitry Andric     }
296480093f4SDimitry Andric     if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
297480093f4SDimitry Andric         PVD->getType()->isIntegerType()) {
298480093f4SDimitry Andric       // Working around integer by-value escapes.
299480093f4SDimitry Andric       State = State->set<HStateMap>(Handle, HandleState::getEscaped());
300480093f4SDimitry Andric     }
301480093f4SDimitry Andric   }
302480093f4SDimitry Andric   C.addTransition(State);
303480093f4SDimitry Andric }
304480093f4SDimitry Andric 
305480093f4SDimitry Andric void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
306480093f4SDimitry Andric                                          CheckerContext &C) const {
307480093f4SDimitry Andric   const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
308480093f4SDimitry Andric   if (!FuncDecl)
309480093f4SDimitry Andric     return;
310480093f4SDimitry Andric 
311480093f4SDimitry Andric   ProgramStateRef State = C.getState();
312480093f4SDimitry Andric 
313480093f4SDimitry Andric   std::vector<std::function<std::string(BugReport & BR)>> Notes;
314480093f4SDimitry Andric   SymbolRef ResultSymbol = nullptr;
315480093f4SDimitry Andric   if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())
316480093f4SDimitry Andric     if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
317480093f4SDimitry Andric       ResultSymbol = Call.getReturnValue().getAsSymbol();
318480093f4SDimitry Andric 
319480093f4SDimitry Andric   // Function returns an open handle.
320480093f4SDimitry Andric   if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
321480093f4SDimitry Andric     SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
322*5ffd83dbSDimitry Andric     Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
323*5ffd83dbSDimitry Andric       auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
324*5ffd83dbSDimitry Andric       if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
325*5ffd83dbSDimitry Andric         std::string SBuf;
326*5ffd83dbSDimitry Andric         llvm::raw_string_ostream OS(SBuf);
327*5ffd83dbSDimitry Andric         OS << "Function '" << FuncDecl->getNameAsString()
328*5ffd83dbSDimitry Andric            << "' returns an open handle";
329*5ffd83dbSDimitry Andric         return OS.str();
330*5ffd83dbSDimitry Andric       } else
331*5ffd83dbSDimitry Andric         return "";
332*5ffd83dbSDimitry Andric     });
333480093f4SDimitry Andric     State =
334480093f4SDimitry Andric         State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));
335480093f4SDimitry Andric   }
336480093f4SDimitry Andric 
337480093f4SDimitry Andric   for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
338480093f4SDimitry Andric     if (Arg >= FuncDecl->getNumParams())
339480093f4SDimitry Andric       break;
340480093f4SDimitry Andric     const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
341*5ffd83dbSDimitry Andric     unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1;
342480093f4SDimitry Andric     SymbolRef Handle =
343480093f4SDimitry Andric         getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
344480093f4SDimitry Andric     if (!Handle)
345480093f4SDimitry Andric       continue;
346480093f4SDimitry Andric 
347480093f4SDimitry Andric     const HandleState *HState = State->get<HStateMap>(Handle);
348480093f4SDimitry Andric     if (HState && HState->isEscaped())
349480093f4SDimitry Andric       continue;
350480093f4SDimitry Andric     if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
351480093f4SDimitry Andric       if (HState && HState->isReleased()) {
352480093f4SDimitry Andric         reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
353480093f4SDimitry Andric         return;
354480093f4SDimitry Andric       } else {
355*5ffd83dbSDimitry Andric         Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
356480093f4SDimitry Andric           auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
357480093f4SDimitry Andric           if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
358*5ffd83dbSDimitry Andric             std::string SBuf;
359*5ffd83dbSDimitry Andric             llvm::raw_string_ostream OS(SBuf);
360*5ffd83dbSDimitry Andric             OS << "Handle released through " << ParamDiagIdx
361*5ffd83dbSDimitry Andric                << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
362*5ffd83dbSDimitry Andric             return OS.str();
363480093f4SDimitry Andric           } else
364480093f4SDimitry Andric             return "";
365480093f4SDimitry Andric         });
366480093f4SDimitry Andric         State = State->set<HStateMap>(Handle, HandleState::getReleased());
367480093f4SDimitry Andric       }
368480093f4SDimitry Andric     } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
369*5ffd83dbSDimitry Andric       Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
370480093f4SDimitry Andric         auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
371480093f4SDimitry Andric         if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
372*5ffd83dbSDimitry Andric           std::string SBuf;
373*5ffd83dbSDimitry Andric           llvm::raw_string_ostream OS(SBuf);
374*5ffd83dbSDimitry Andric           OS << "Handle allocated through " << ParamDiagIdx
375*5ffd83dbSDimitry Andric              << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
376*5ffd83dbSDimitry Andric           return OS.str();
377480093f4SDimitry Andric         } else
378480093f4SDimitry Andric           return "";
379480093f4SDimitry Andric       });
380480093f4SDimitry Andric       State = State->set<HStateMap>(
381480093f4SDimitry Andric           Handle, HandleState::getMaybeAllocated(ResultSymbol));
382480093f4SDimitry Andric     }
383480093f4SDimitry Andric   }
384480093f4SDimitry Andric   const NoteTag *T = nullptr;
385480093f4SDimitry Andric   if (!Notes.empty()) {
386*5ffd83dbSDimitry Andric     T = C.getNoteTag([this, Notes{std::move(Notes)}](
387*5ffd83dbSDimitry Andric                          PathSensitiveBugReport &BR) -> std::string {
388480093f4SDimitry Andric           if (&BR.getBugType() != &UseAfterReleaseBugType &&
389480093f4SDimitry Andric               &BR.getBugType() != &LeakBugType &&
390480093f4SDimitry Andric               &BR.getBugType() != &DoubleReleaseBugType)
391480093f4SDimitry Andric             return "";
392480093f4SDimitry Andric           for (auto &Note : Notes) {
393480093f4SDimitry Andric             std::string Text = Note(BR);
394480093f4SDimitry Andric             if (!Text.empty())
395480093f4SDimitry Andric               return Text;
396480093f4SDimitry Andric           }
397480093f4SDimitry Andric           return "";
398480093f4SDimitry Andric         });
399480093f4SDimitry Andric   }
400480093f4SDimitry Andric   C.addTransition(State, T);
401480093f4SDimitry Andric }
402480093f4SDimitry Andric 
403480093f4SDimitry Andric void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
404480093f4SDimitry Andric                                             CheckerContext &C) const {
405480093f4SDimitry Andric   ProgramStateRef State = C.getState();
406480093f4SDimitry Andric   SmallVector<SymbolRef, 2> LeakedSyms;
407480093f4SDimitry Andric   HStateMapTy TrackedHandles = State->get<HStateMap>();
408480093f4SDimitry Andric   for (auto &CurItem : TrackedHandles) {
409*5ffd83dbSDimitry Andric     SymbolRef ErrorSym = CurItem.second.getErrorSym();
410*5ffd83dbSDimitry Andric     // Keeping zombie handle symbols. In case the error symbol is dying later
411*5ffd83dbSDimitry Andric     // than the handle symbol we might produce spurious leak warnings (in case
412*5ffd83dbSDimitry Andric     // we find out later from the status code that the handle allocation failed
413*5ffd83dbSDimitry Andric     // in the first place).
414*5ffd83dbSDimitry Andric     if (!SymReaper.isDead(CurItem.first) ||
415*5ffd83dbSDimitry Andric         (ErrorSym && !SymReaper.isDead(ErrorSym)))
416480093f4SDimitry Andric       continue;
417480093f4SDimitry Andric     if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
418480093f4SDimitry Andric       LeakedSyms.push_back(CurItem.first);
419480093f4SDimitry Andric     State = State->remove<HStateMap>(CurItem.first);
420480093f4SDimitry Andric   }
421480093f4SDimitry Andric 
422480093f4SDimitry Andric   ExplodedNode *N = C.getPredecessor();
423480093f4SDimitry Andric   if (!LeakedSyms.empty())
424480093f4SDimitry Andric     N = reportLeaks(LeakedSyms, C, N);
425480093f4SDimitry Andric 
426480093f4SDimitry Andric   C.addTransition(State, N);
427480093f4SDimitry Andric }
428480093f4SDimitry Andric 
429480093f4SDimitry Andric // Acquiring a handle is not always successful. In Fuchsia most functions
430480093f4SDimitry Andric // return a status code that determines the status of the handle.
431480093f4SDimitry Andric // When we split the path based on this status code we know that on one
432480093f4SDimitry Andric // path we do have the handle and on the other path the acquire failed.
433480093f4SDimitry Andric // This method helps avoiding false positive leak warnings on paths where
434480093f4SDimitry Andric // the function failed.
435480093f4SDimitry Andric // Moreover, when a handle is known to be zero (the invalid handle),
436480093f4SDimitry Andric // we no longer can follow the symbol on the path, becaue the constant
437480093f4SDimitry Andric // zero will be used instead of the symbol. We also do not need to release
438480093f4SDimitry Andric // an invalid handle, so we remove the corresponding symbol from the state.
439480093f4SDimitry Andric ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
440480093f4SDimitry Andric                                                  SVal Cond,
441480093f4SDimitry Andric                                                  bool Assumption) const {
442480093f4SDimitry Andric   // TODO: add notes about successes/fails for APIs.
443480093f4SDimitry Andric   ConstraintManager &Cmr = State->getConstraintManager();
444480093f4SDimitry Andric   HStateMapTy TrackedHandles = State->get<HStateMap>();
445480093f4SDimitry Andric   for (auto &CurItem : TrackedHandles) {
446480093f4SDimitry Andric     ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first);
447480093f4SDimitry Andric     if (HandleVal.isConstrainedTrue()) {
448480093f4SDimitry Andric       // The handle is invalid. We can no longer follow the symbol on this path.
449480093f4SDimitry Andric       State = State->remove<HStateMap>(CurItem.first);
450480093f4SDimitry Andric     }
451480093f4SDimitry Andric     SymbolRef ErrorSym = CurItem.second.getErrorSym();
452480093f4SDimitry Andric     if (!ErrorSym)
453480093f4SDimitry Andric       continue;
454480093f4SDimitry Andric     ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);
455480093f4SDimitry Andric     if (ErrorVal.isConstrainedTrue()) {
456480093f4SDimitry Andric       // Allocation succeeded.
457480093f4SDimitry Andric       if (CurItem.second.maybeAllocated())
458480093f4SDimitry Andric         State = State->set<HStateMap>(
459480093f4SDimitry Andric             CurItem.first, HandleState::getAllocated(State, CurItem.second));
460480093f4SDimitry Andric     } else if (ErrorVal.isConstrainedFalse()) {
461480093f4SDimitry Andric       // Allocation failed.
462480093f4SDimitry Andric       if (CurItem.second.maybeAllocated())
463480093f4SDimitry Andric         State = State->remove<HStateMap>(CurItem.first);
464480093f4SDimitry Andric     }
465480093f4SDimitry Andric   }
466480093f4SDimitry Andric   return State;
467480093f4SDimitry Andric }
468480093f4SDimitry Andric 
469480093f4SDimitry Andric ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(
470480093f4SDimitry Andric     ProgramStateRef State, const InvalidatedSymbols &Escaped,
471480093f4SDimitry Andric     const CallEvent *Call, PointerEscapeKind Kind) const {
472480093f4SDimitry Andric   const FunctionDecl *FuncDecl =
473480093f4SDimitry Andric       Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr;
474480093f4SDimitry Andric 
475480093f4SDimitry Andric   llvm::DenseSet<SymbolRef> UnEscaped;
476480093f4SDimitry Andric   // Not all calls should escape our symbols.
477480093f4SDimitry Andric   if (FuncDecl &&
478480093f4SDimitry Andric       (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall ||
479480093f4SDimitry Andric        Kind == PSK_EscapeOutParameters)) {
480480093f4SDimitry Andric     for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
481480093f4SDimitry Andric       if (Arg >= FuncDecl->getNumParams())
482480093f4SDimitry Andric         break;
483480093f4SDimitry Andric       const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
484480093f4SDimitry Andric       SymbolRef Handle =
485480093f4SDimitry Andric           getFuchsiaHandleSymbol(PVD->getType(), Call->getArgSVal(Arg), State);
486480093f4SDimitry Andric       if (!Handle)
487480093f4SDimitry Andric         continue;
488480093f4SDimitry Andric       if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
489480093f4SDimitry Andric           hasFuchsiaAttr<ReleaseHandleAttr>(PVD))
490480093f4SDimitry Andric         UnEscaped.insert(Handle);
491480093f4SDimitry Andric     }
492480093f4SDimitry Andric   }
493480093f4SDimitry Andric 
494480093f4SDimitry Andric   // For out params, we have to deal with derived symbols. See
495480093f4SDimitry Andric   // MacOSKeychainAPIChecker for details.
496480093f4SDimitry Andric   for (auto I : State->get<HStateMap>()) {
497480093f4SDimitry Andric     if (Escaped.count(I.first) && !UnEscaped.count(I.first))
498480093f4SDimitry Andric       State = State->set<HStateMap>(I.first, HandleState::getEscaped());
499480093f4SDimitry Andric     if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) {
500480093f4SDimitry Andric       auto ParentSym = SD->getParentSymbol();
501480093f4SDimitry Andric       if (Escaped.count(ParentSym))
502480093f4SDimitry Andric         State = State->set<HStateMap>(I.first, HandleState::getEscaped());
503480093f4SDimitry Andric     }
504480093f4SDimitry Andric   }
505480093f4SDimitry Andric 
506480093f4SDimitry Andric   return State;
507480093f4SDimitry Andric }
508480093f4SDimitry Andric 
509480093f4SDimitry Andric ExplodedNode *
510480093f4SDimitry Andric FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
511480093f4SDimitry Andric                                   CheckerContext &C, ExplodedNode *Pred) const {
512480093f4SDimitry Andric   ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);
513480093f4SDimitry Andric   for (SymbolRef LeakedHandle : LeakedHandles) {
514480093f4SDimitry Andric     reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType,
515480093f4SDimitry Andric               "Potential leak of handle");
516480093f4SDimitry Andric   }
517480093f4SDimitry Andric   return ErrNode;
518480093f4SDimitry Andric }
519480093f4SDimitry Andric 
520480093f4SDimitry Andric void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
521480093f4SDimitry Andric                                                const SourceRange &Range,
522480093f4SDimitry Andric                                                CheckerContext &C) const {
523480093f4SDimitry Andric   ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
524480093f4SDimitry Andric   reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
525480093f4SDimitry Andric             "Releasing a previously released handle");
526480093f4SDimitry Andric }
527480093f4SDimitry Andric 
528480093f4SDimitry Andric void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
529480093f4SDimitry Andric                                               const SourceRange &Range,
530480093f4SDimitry Andric                                               CheckerContext &C) const {
531480093f4SDimitry Andric   ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
532480093f4SDimitry Andric   reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,
533480093f4SDimitry Andric             "Using a previously released handle");
534480093f4SDimitry Andric }
535480093f4SDimitry Andric 
536480093f4SDimitry Andric void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
537480093f4SDimitry Andric                                      CheckerContext &C,
538480093f4SDimitry Andric                                      const SourceRange *Range,
539480093f4SDimitry Andric                                      const BugType &Type, StringRef Msg) const {
540480093f4SDimitry Andric   if (!ErrorNode)
541480093f4SDimitry Andric     return;
542480093f4SDimitry Andric 
543480093f4SDimitry Andric   std::unique_ptr<PathSensitiveBugReport> R;
544480093f4SDimitry Andric   if (Type.isSuppressOnSink()) {
545480093f4SDimitry Andric     const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);
546480093f4SDimitry Andric     if (AcquireNode) {
547480093f4SDimitry Andric       PathDiagnosticLocation LocUsedForUniqueing =
548480093f4SDimitry Andric           PathDiagnosticLocation::createBegin(
549480093f4SDimitry Andric               AcquireNode->getStmtForDiagnostics(), C.getSourceManager(),
550480093f4SDimitry Andric               AcquireNode->getLocationContext());
551480093f4SDimitry Andric 
552480093f4SDimitry Andric       R = std::make_unique<PathSensitiveBugReport>(
553480093f4SDimitry Andric           Type, Msg, ErrorNode, LocUsedForUniqueing,
554480093f4SDimitry Andric           AcquireNode->getLocationContext()->getDecl());
555480093f4SDimitry Andric     }
556480093f4SDimitry Andric   }
557480093f4SDimitry Andric   if (!R)
558480093f4SDimitry Andric     R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
559480093f4SDimitry Andric   if (Range)
560480093f4SDimitry Andric     R->addRange(*Range);
561480093f4SDimitry Andric   R->markInteresting(Sym);
562480093f4SDimitry Andric   C.emitReport(std::move(R));
563480093f4SDimitry Andric }
564480093f4SDimitry Andric 
565480093f4SDimitry Andric void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
566480093f4SDimitry Andric   mgr.registerChecker<FuchsiaHandleChecker>();
567480093f4SDimitry Andric }
568480093f4SDimitry Andric 
569*5ffd83dbSDimitry Andric bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {
570480093f4SDimitry Andric   return true;
571480093f4SDimitry Andric }
572480093f4SDimitry Andric 
573480093f4SDimitry Andric void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
574480093f4SDimitry Andric                                       const char *NL, const char *Sep) const {
575480093f4SDimitry Andric 
576480093f4SDimitry Andric   HStateMapTy StateMap = State->get<HStateMap>();
577480093f4SDimitry Andric 
578480093f4SDimitry Andric   if (!StateMap.isEmpty()) {
579480093f4SDimitry Andric     Out << Sep << "FuchsiaHandleChecker :" << NL;
580480093f4SDimitry Andric     for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
581480093f4SDimitry Andric          ++I) {
582480093f4SDimitry Andric       I.getKey()->dumpToStream(Out);
583480093f4SDimitry Andric       Out << " : ";
584480093f4SDimitry Andric       I.getData().dump(Out);
585480093f4SDimitry Andric       Out << NL;
586480093f4SDimitry Andric     }
587480093f4SDimitry Andric   }
588480093f4SDimitry Andric }
589