clang  15.0.0git
FuchsiaHandleChecker.cpp
Go to the documentation of this file.
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"
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:
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.
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);
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;
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 &&
579  for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
580  if (Arg >= FuncDecl->getNumParams())
581  break;
582  const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
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 =
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 }
clang::ento::PSK_DirectEscapeOnCall
@ PSK_DirectEscapeOnCall
The pointer has been passed to a function call directly.
Definition: CheckerManager.h:84
clang::FunctionDecl::getNumParams
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition: Decl.cpp:3445
ConstraintManager.h
clang::FunctionDecl::getReturnType
QualType getReturnType() const
Definition: Decl.h:2564
clang::SourceRange
A trivial tuple used to represent a source range.
Definition: SourceLocation.h:210
string
string(SUBSTRING ${CMAKE_CURRENT_BINARY_DIR} 0 ${PATH_LIB_START} PATH_HEAD) string(SUBSTRING $
Definition: CMakeLists.txt:22
clang::Decl::hasAttr
bool hasAttr() const
Definition: DeclBase.h:549
llvm::SmallVector
Definition: LLVM.h:38
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
clang::QualType
A (possibly-)qualified type.
Definition: Type.h:731
Attr.h
clang::FunctionDecl::getParamDecl
const ParmVarDecl * getParamDecl(unsigned i) const
Definition: Decl.h:2533
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:55
clang::ParmVarDecl
Represents a parameter to a function.
Definition: Decl.h:1680
clang::ento::SymbolRef
const SymExpr * SymbolRef
Definition: SymExpr.h:111
llvm::Optional
Definition: LLVM.h:40
clang::serialized_diags::Note
@ Note
Definition: SerializedDiagnostics.h:45
clang::Decl::getAttr
T * getAttr() const
Definition: DeclBase.h:545
clang::index::SymbolRole::Call
@ Call
clang::Type
The base class of the type hierarchy.
Definition: Type.h:1556
Decl.h
clang::TypedefType
Definition: Type.h:4464
CallEvent.h
REGISTER_MAP_WITH_PROGRAMSTATE
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Definition: ProgramStateTrait.h:87
SymExpr.h
clang::Type::isReferenceType
bool isReferenceType() const
Definition: Type.h:6819
BuiltinCheckerRegistration.h
getAcquireSite
static const ExplodedNode * getAcquireSite(const ExplodedNode *N, SymbolRef Sym, CheckerContext &Ctx)
Definition: FuchsiaHandleChecker.cpp:232
CheckerManager.h
clang::Type::getAs
const T * getAs() const
Member-template getAs<specific type>'.
Definition: Type.h:7302
CASE
#define CASE(ID)
clang::ento::PSK_EscapeOutParameters
@ PSK_EscapeOutParameters
Escape for a new symbol that was generated into a region that the analyzer cannot follow during a con...
Definition: CheckerManager.h:94
Type.h
clang::Type::isStructureType
bool isStructureType() const
Definition: Type.cpp:563
clang::Type::getPointeeType
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition: Type.cpp:625
llvm::DenseSet< SymbolRef >
ExplodedGraph.h
BugType.h
dump
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
Definition: CoverageMappingGen.cpp:1533
llvm::ArrayRef
Definition: LLVM.h:34
clang::Decl
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:83
clang::ParmVarDecl::getFunctionScopeIndex
unsigned getFunctionScopeIndex() const
Returns the index of this parameter in its prototype or method scope.
Definition: Decl.h:1740
State
LineState State
Definition: UnwrappedLineFormatter.cpp:1126
clang::ento::PSK_IndirectEscapeOnCall
@ PSK_IndirectEscapeOnCall
The pointer has been passed to a function indirectly.
Definition: CheckerManager.h:89
CheckerContext.h
clang::ObjCPropertyAttribute::Kind
Kind
Definition: DeclObjCCommon.h:22
ProgramState.h
Checker.h
clang::Builtin::ID
ID
Definition: Builtins.h:52
clang
Definition: CalledOnceCheck.h:17
clang::Type::isAnyPointerType
bool isAnyPointerType() const
Definition: Type.h:6811
Text
StringRef Text
Definition: Format.cpp:2573
clang::ento::PathDiagnosticLocation::createBegin
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
Definition: PathDiagnostic.cpp:580
clang::Attr
Attr - This represents one attribute.
Definition: Attr.h:41
clang::NamedDecl::getDeclName
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:311
clang::ento::PointerEscapeKind
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
Definition: CheckerManager.h:78
isReleased
static bool isReleased(SymbolRef Sym, CheckerContext &C)
Check if the memory associated with this symbol was released.
Definition: MallocChecker.cpp:3000
clang::ValueDecl::getType
QualType getType() const
Definition: Decl.h:685
clang::Type::isIntegerType
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:7128
clang::ento::InvalidatedSymbols
llvm::DenseSet< SymbolRef > InvalidatedSymbols
Definition: Store.h:51
clang::FunctionDecl
Represents a function declaration or definition.
Definition: Decl.h:1872
clang::operator==
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
Definition: CallGraph.h:207
getFuchsiaHandleSymbols
static SmallVector< SymbolRef, 1024 > getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State)
Returns the symbols extracted from the argument or empty vector if it cannot be found.
Definition: FuchsiaHandleChecker.cpp:274
clang::ento::ObjKind::OS
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...