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