clang  14.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  FuchsiaHandleSymbolVisitor(ProgramStateRef State) : State(std::move(State)) {}
258  ProgramStateRef getState() const { return State; }
259 
260  bool VisitSymbol(SymbolRef S) override {
261  if (const auto *HandleType = S->getType()->getAs<TypedefType>())
262  if (HandleType->getDecl()->getName() == HandleTypeName)
263  Symbols.push_back(S);
264  return true;
265  }
266 
267  SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; }
268 
269 private:
270  SmallVector<SymbolRef, 1024> Symbols;
272 };
273 } // end anonymous namespace
274 
275 /// Returns the symbols extracted from the argument or empty vector if it cannot
276 /// be found. It is unlikely to have over 1024 symbols in one argument.
277 static SmallVector<SymbolRef, 1024>
279  int PtrToHandleLevel = 0;
280  while (QT->isAnyPointerType() || QT->isReferenceType()) {
281  ++PtrToHandleLevel;
282  QT = QT->getPointeeType();
283  }
284  if (QT->isStructureType()) {
285  // If we see a structure, see if there is any handle referenced by the
286  // structure.
287  FuchsiaHandleSymbolVisitor Visitor(State);
288  State->scanReachableSymbols(Arg, Visitor);
289  return Visitor.GetSymbols();
290  }
291  if (const auto *HandleType = QT->getAs<TypedefType>()) {
292  if (HandleType->getDecl()->getName() != HandleTypeName)
293  return {};
294  if (PtrToHandleLevel > 1)
295  // Not supported yet.
296  return {};
297 
298  if (PtrToHandleLevel == 0) {
299  SymbolRef Sym = Arg.getAsSymbol();
300  if (Sym) {
301  return {Sym};
302  } else {
303  return {};
304  }
305  } else {
306  assert(PtrToHandleLevel == 1);
307  if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) {
308  SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol();
309  if (Sym) {
310  return {Sym};
311  } else {
312  return {};
313  }
314  }
315  }
316  }
317  return {};
318 }
319 
320 void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
321  CheckerContext &C) const {
322  ProgramStateRef State = C.getState();
323  const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
324  if (!FuncDecl) {
325  // Unknown call, escape by value handles. They are not covered by
326  // PointerEscape callback.
327  for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
328  if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol())
329  State = State->set<HStateMap>(Handle, HandleState::getEscaped());
330  }
331  C.addTransition(State);
332  return;
333  }
334 
335  for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
336  if (Arg >= FuncDecl->getNumParams())
337  break;
338  const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
339  SmallVector<SymbolRef, 1024> Handles =
340  getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
341 
342  // Handled in checkPostCall.
343  if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
344  hasFuchsiaAttr<AcquireHandleAttr>(PVD))
345  continue;
346 
347  for (SymbolRef Handle : Handles) {
348  const HandleState *HState = State->get<HStateMap>(Handle);
349  if (!HState || HState->isEscaped())
350  continue;
351 
352  if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
353  PVD->getType()->isIntegerType()) {
354  if (HState->isReleased()) {
355  reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
356  return;
357  }
358  }
359  }
360  }
361  C.addTransition(State);
362 }
363 
364 void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
365  CheckerContext &C) const {
366  const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
367  if (!FuncDecl)
368  return;
369 
370  // If we analyzed the function body, then ignore the annotations.
371  if (C.wasInlined)
372  return;
373 
374  ProgramStateRef State = C.getState();
375 
376  std::vector<std::function<std::string(BugReport & BR)>> Notes;
377  SymbolRef ResultSymbol = nullptr;
378  if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())
379  if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
380  ResultSymbol = Call.getReturnValue().getAsSymbol();
381 
382  // Function returns an open handle.
383  if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
384  SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
385  Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
386  auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
387  if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
388  std::string SBuf;
389  llvm::raw_string_ostream OS(SBuf);
390  OS << "Function '" << FuncDecl->getDeclName()
391  << "' returns an open handle";
392  return OS.str();
393  } else
394  return "";
395  });
396  State =
397  State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));
398  } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) {
399  // Function returns an unowned handle
400  SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
401  Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
402  auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
403  if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
404  std::string SBuf;
405  llvm::raw_string_ostream OS(SBuf);
406  OS << "Function '" << FuncDecl->getDeclName()
407  << "' returns an unowned handle";
408  return OS.str();
409  } else
410  return "";
411  });
412  State = State->set<HStateMap>(RetSym, HandleState::getUnowned());
413  }
414 
415  for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
416  if (Arg >= FuncDecl->getNumParams())
417  break;
418  const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
419  unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1;
420  SmallVector<SymbolRef, 1024> Handles =
421  getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
422 
423  for (SymbolRef Handle : Handles) {
424  const HandleState *HState = State->get<HStateMap>(Handle);
425  if (HState && HState->isEscaped())
426  continue;
427  if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
428  if (HState && HState->isReleased()) {
429  reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
430  return;
431  } else if (HState && HState->isUnowned()) {
432  reportUnownedRelease(Handle, Call.getArgSourceRange(Arg), C);
433  return;
434  } else {
435  Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
436  auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
437  if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
438  std::string SBuf;
439  llvm::raw_string_ostream OS(SBuf);
440  OS << "Handle released through " << ParamDiagIdx
441  << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
442  return OS.str();
443  } else
444  return "";
445  });
446  State = State->set<HStateMap>(Handle, HandleState::getReleased());
447  }
448  } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
449  Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
450  auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
451  if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
452  std::string SBuf;
453  llvm::raw_string_ostream OS(SBuf);
454  OS << "Handle allocated through " << ParamDiagIdx
455  << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
456  return OS.str();
457  } else
458  return "";
459  });
460  State = State->set<HStateMap>(
461  Handle, HandleState::getMaybeAllocated(ResultSymbol));
462  } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) {
463  Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
464  auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
465  if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
466  std::string SBuf;
467  llvm::raw_string_ostream OS(SBuf);
468  OS << "Unowned handle allocated through " << ParamDiagIdx
469  << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
470  return OS.str();
471  } else
472  return "";
473  });
474  State = State->set<HStateMap>(Handle, HandleState::getUnowned());
475  } else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
476  PVD->getType()->isIntegerType()) {
477  // Working around integer by-value escapes.
478  // The by-value escape would not be captured in checkPointerEscape.
479  // If the function was not analyzed (otherwise wasInlined should be
480  // true) and there is no annotation on the handle, we assume the handle
481  // is escaped.
482  State = State->set<HStateMap>(Handle, HandleState::getEscaped());
483  }
484  }
485  }
486  const NoteTag *T = nullptr;
487  if (!Notes.empty()) {
488  T = C.getNoteTag([this, Notes{std::move(Notes)}](
489  PathSensitiveBugReport &BR) -> std::string {
490  if (&BR.getBugType() != &UseAfterReleaseBugType &&
491  &BR.getBugType() != &LeakBugType &&
492  &BR.getBugType() != &DoubleReleaseBugType &&
493  &BR.getBugType() != &ReleaseUnownedBugType)
494  return "";
495  for (auto &Note : Notes) {
496  std::string Text = Note(BR);
497  if (!Text.empty())
498  return Text;
499  }
500  return "";
501  });
502  }
503  C.addTransition(State, T);
504 }
505 
506 void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
507  CheckerContext &C) const {
508  ProgramStateRef State = C.getState();
509  SmallVector<SymbolRef, 2> LeakedSyms;
510  HStateMapTy TrackedHandles = State->get<HStateMap>();
511  for (auto &CurItem : TrackedHandles) {
512  SymbolRef ErrorSym = CurItem.second.getErrorSym();
513  // Keeping zombie handle symbols. In case the error symbol is dying later
514  // than the handle symbol we might produce spurious leak warnings (in case
515  // we find out later from the status code that the handle allocation failed
516  // in the first place).
517  if (!SymReaper.isDead(CurItem.first) ||
518  (ErrorSym && !SymReaper.isDead(ErrorSym)))
519  continue;
520  if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
521  LeakedSyms.push_back(CurItem.first);
522  State = State->remove<HStateMap>(CurItem.first);
523  }
524 
525  ExplodedNode *N = C.getPredecessor();
526  if (!LeakedSyms.empty())
527  N = reportLeaks(LeakedSyms, C, N);
528 
529  C.addTransition(State, N);
530 }
531 
532 // Acquiring a handle is not always successful. In Fuchsia most functions
533 // return a status code that determines the status of the handle.
534 // When we split the path based on this status code we know that on one
535 // path we do have the handle and on the other path the acquire failed.
536 // This method helps avoiding false positive leak warnings on paths where
537 // the function failed.
538 // Moreover, when a handle is known to be zero (the invalid handle),
539 // we no longer can follow the symbol on the path, becaue the constant
540 // zero will be used instead of the symbol. We also do not need to release
541 // an invalid handle, so we remove the corresponding symbol from the state.
542 ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
543  SVal Cond,
544  bool Assumption) const {
545  // TODO: add notes about successes/fails for APIs.
546  ConstraintManager &Cmr = State->getConstraintManager();
547  HStateMapTy TrackedHandles = State->get<HStateMap>();
548  for (auto &CurItem : TrackedHandles) {
549  ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first);
550  if (HandleVal.isConstrainedTrue()) {
551  // The handle is invalid. We can no longer follow the symbol on this path.
552  State = State->remove<HStateMap>(CurItem.first);
553  }
554  SymbolRef ErrorSym = CurItem.second.getErrorSym();
555  if (!ErrorSym)
556  continue;
557  ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);
558  if (ErrorVal.isConstrainedTrue()) {
559  // Allocation succeeded.
560  if (CurItem.second.maybeAllocated())
561  State = State->set<HStateMap>(
562  CurItem.first, HandleState::getAllocated(State, CurItem.second));
563  } else if (ErrorVal.isConstrainedFalse()) {
564  // Allocation failed.
565  if (CurItem.second.maybeAllocated())
566  State = State->remove<HStateMap>(CurItem.first);
567  }
568  }
569  return State;
570 }
571 
572 ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(
573  ProgramStateRef State, const InvalidatedSymbols &Escaped,
574  const CallEvent *Call, PointerEscapeKind Kind) const {
575  const FunctionDecl *FuncDecl =
576  Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr;
577 
578  llvm::DenseSet<SymbolRef> UnEscaped;
579  // Not all calls should escape our symbols.
580  if (FuncDecl &&
583  for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
584  if (Arg >= FuncDecl->getNumParams())
585  break;
586  const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
587  SmallVector<SymbolRef, 1024> Handles =
588  getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State);
589  for (SymbolRef Handle : Handles) {
590  if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
591  hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
592  UnEscaped.insert(Handle);
593  }
594  }
595  }
596  }
597 
598  // For out params, we have to deal with derived symbols. See
599  // MacOSKeychainAPIChecker for details.
600  for (auto I : State->get<HStateMap>()) {
601  if (Escaped.count(I.first) && !UnEscaped.count(I.first))
602  State = State->set<HStateMap>(I.first, HandleState::getEscaped());
603  if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) {
604  auto ParentSym = SD->getParentSymbol();
605  if (Escaped.count(ParentSym))
606  State = State->set<HStateMap>(I.first, HandleState::getEscaped());
607  }
608  }
609 
610  return State;
611 }
612 
613 ExplodedNode *
614 FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
615  CheckerContext &C, ExplodedNode *Pred) const {
616  ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);
617  for (SymbolRef LeakedHandle : LeakedHandles) {
618  reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType,
619  "Potential leak of handle");
620  }
621  return ErrNode;
622 }
623 
624 void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
625  const SourceRange &Range,
626  CheckerContext &C) const {
627  ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
628  reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
629  "Releasing a previously released handle");
630 }
631 
632 void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym,
633  const SourceRange &Range,
634  CheckerContext &C) const {
635  ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
636  reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType,
637  "Releasing an unowned handle");
638 }
639 
640 void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
641  const SourceRange &Range,
642  CheckerContext &C) const {
643  ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
644  reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,
645  "Using a previously released handle");
646 }
647 
648 void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
649  CheckerContext &C,
650  const SourceRange *Range,
651  const BugType &Type, StringRef Msg) const {
652  if (!ErrorNode)
653  return;
654 
655  std::unique_ptr<PathSensitiveBugReport> R;
656  if (Type.isSuppressOnSink()) {
657  const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);
658  if (AcquireNode) {
659  PathDiagnosticLocation LocUsedForUniqueing =
661  AcquireNode->getStmtForDiagnostics(), C.getSourceManager(),
662  AcquireNode->getLocationContext());
663 
664  R = std::make_unique<PathSensitiveBugReport>(
665  Type, Msg, ErrorNode, LocUsedForUniqueing,
666  AcquireNode->getLocationContext()->getDecl());
667  }
668  }
669  if (!R)
670  R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
671  if (Range)
672  R->addRange(*Range);
673  R->markInteresting(Sym);
674  C.emitReport(std::move(R));
675 }
676 
677 void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
678  mgr.registerChecker<FuchsiaHandleChecker>();
679 }
680 
681 bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {
682  return true;
683 }
684 
685 void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
686  const char *NL, const char *Sep) const {
687 
688  HStateMapTy StateMap = State->get<HStateMap>();
689 
690  if (!StateMap.isEmpty()) {
691  Out << Sep << "FuchsiaHandleChecker :" << NL;
692  for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
693  ++I) {
694  I.getKey()->dumpToStream(Out);
695  Out << " : ";
696  I.getData().dump(Out);
697  Out << NL;
698  }
699  }
700 }
clang::ento::PSK_DirectEscapeOnCall
@ PSK_DirectEscapeOnCall
The pointer has been passed to a function call directly.
Definition: CheckerManager.h:85
clang::FunctionDecl::getNumParams
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition: Decl.cpp:3380
ConstraintManager.h
clang::FunctionDecl::getReturnType
QualType getReturnType() const
Definition: Decl.h:2537
clang::SourceRange
A trivial tuple used to represent a source range.
Definition: SourceLocation.h:212
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:547
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
clang::QualType
A (possibly-)qualified type.
Definition: Type.h:673
Attr.h
clang::FunctionDecl::getParamDecl
const ParmVarDecl * getParamDecl(unsigned i) const
Definition: Decl.h:2506
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:54
clang::ParmVarDecl
Represents a parameter to a function.
Definition: Decl.h:1665
clang::ento::SymbolRef
const SymExpr * SymbolRef
Definition: SymExpr.h:110
llvm::Optional
Definition: LLVM.h:40
clang::serialized_diags::Note
@ Note
Definition: SerializedDiagnostics.h:45
clang::Decl::getAttr
T * getAttr() const
Definition: DeclBase.h:543
clang::index::SymbolRole::Call
@ Call
clang::Type
The base class of the type hierarchy.
Definition: Type.h:1490
Decl.h
clang::TypedefType
Definition: Type.h:4371
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:84
SymExpr.h
clang::Type::isReferenceType
bool isReferenceType() const
Definition: Type.h:6684
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:7161
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:95
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:1519
llvm::ArrayRef
Definition: LLVM.h:34
clang::Decl
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:89
clang::ParmVarDecl::getFunctionScopeIndex
unsigned getFunctionScopeIndex() const
Returns the index of this parameter in its prototype or method scope.
Definition: Decl.h:1725
State
LineState State
Definition: UnwrappedLineFormatter.cpp:986
clang::ento::PSK_IndirectEscapeOnCall
@ PSK_IndirectEscapeOnCall
The pointer has been passed to a function indirectly.
Definition: CheckerManager.h:90
CheckerContext.h
clang::ObjCPropertyAttribute::Kind
Kind
Definition: DeclObjCCommon.h:22
ProgramState.h
Checker.h
std
Definition: Format.h:4034
clang::Builtin::ID
ID
Definition: Builtins.h:48
clang
Definition: CalledOnceCheck.h:17
clang::Type::isAnyPointerType
bool isAnyPointerType() const
Definition: Type.h:6676
Text
StringRef Text
Definition: Format.cpp:2334
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:46
clang::NamedDecl::getDeclName
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:313
clang::ento::PointerEscapeKind
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
Definition: CheckerManager.h:79
isReleased
static bool isReleased(SymbolRef Sym, CheckerContext &C)
Check if the memory associated with this symbol was released.
Definition: MallocChecker.cpp:2953
clang::ValueDecl::getType
QualType getType() const
Definition: Decl.h:687
clang::Type::isIntegerType
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:6987
clang::ento::InvalidatedSymbols
llvm::DenseSet< SymbolRef > InvalidatedSymbols
Definition: Store.h:51
clang::FunctionDecl
Represents a function declaration or definition.
Definition: Decl.h:1856
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:278
clang::ento::ObjKind::OS
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...