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
106using namespace clang;
107using namespace ento;
108
109namespace {
110
111static const StringRef HandleTypeName = "zx_handle_t";
112static const StringRef ErrorTypeName = "zx_status_t";
113
114class HandleState {
115private:
116  enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K;
117  SymbolRef ErrorSym;
118  HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
119
120public:
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
178template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
179  return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
180}
181
182template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) {
183  return D->hasAttr<Attr>() &&
184         D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned";
185}
186
187class 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
199public:
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
231REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
232
233static 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
255namespace {
256class FuchsiaHandleSymbolVisitor final : public SymbolVisitor {
257public:
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
267private:
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.
274static SmallVector<SymbolRef, 1024>
275getFuchsiaHandleSymbols(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
317void 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
361void 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
503void 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.
539ProgramStateRef 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
569ProgramStateRef 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
610ExplodedNode *
611FuchsiaHandleChecker::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
621void 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
629void 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
637void 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
645void 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
674void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
675  mgr.registerChecker<FuchsiaHandleChecker>();
676}
677
678bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {
679  return true;
680}
681
682void 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 (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
690         ++I) {
691      I.getKey()->dumpToStream(Out);
692      Out << " : ";
693      I.getData().dump(Out);
694      Out << NL;
695    }
696  }
697}
698