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