clang  19.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 #include <optional>
105 
106 using namespace clang;
107 using namespace ento;
108 
109 namespace {
110 
111 static const StringRef HandleTypeName = "zx_handle_t";
112 static const StringRef ErrorTypeName = "zx_status_t";
113 
114 class HandleState {
115 private:
116  enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K;
117  SymbolRef ErrorSym;
118  HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
119 
120 public:
121  bool operator==(const HandleState &Other) const {
122  return K == Other.K && ErrorSym == Other.ErrorSym;
123  }
124  bool isAllocated() const { return K == Kind::Allocated; }
125  bool maybeAllocated() const { return K == Kind::MaybeAllocated; }
126  bool isReleased() const { return K == Kind::Released; }
127  bool isEscaped() const { return K == Kind::Escaped; }
128  bool isUnowned() const { return K == Kind::Unowned; }
129 
130  static HandleState getMaybeAllocated(SymbolRef ErrorSym) {
131  return HandleState(Kind::MaybeAllocated, ErrorSym);
132  }
133  static HandleState getAllocated(ProgramStateRef State, HandleState S) {
134  assert(S.maybeAllocated());
135  assert(State->getConstraintManager()
136  .isNull(State, S.getErrorSym())
137  .isConstrained());
138  return HandleState(Kind::Allocated, nullptr);
139  }
140  static HandleState getReleased() {
141  return HandleState(Kind::Released, nullptr);
142  }
143  static HandleState getEscaped() {
144  return HandleState(Kind::Escaped, nullptr);
145  }
146  static HandleState getUnowned() {
147  return HandleState(Kind::Unowned, nullptr);
148  }
149 
150  SymbolRef getErrorSym() const { return ErrorSym; }
151 
152  void Profile(llvm::FoldingSetNodeID &ID) const {
153  ID.AddInteger(static_cast<int>(K));
154  ID.AddPointer(ErrorSym);
155  }
156 
157  LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
158  switch (K) {
159 #define CASE(ID) \
160  case ID: \
161  OS << #ID; \
162  break;
163  CASE(Kind::MaybeAllocated)
164  CASE(Kind::Allocated)
165  CASE(Kind::Released)
166  CASE(Kind::Escaped)
167  CASE(Kind::Unowned)
168  }
169  if (ErrorSym) {
170  OS << " ErrorSym: ";
171  ErrorSym->dumpToStream(OS);
172  }
173  }
174 
175  LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
176 };
177 
178 template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
179  return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
180 }
181 
182 template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) {
183  return D->hasAttr<Attr>() &&
184  D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned";
185 }
186 
187 class FuchsiaHandleChecker
188  : public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
189  check::PointerEscape, eval::Assume> {
190  BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error",
191  /*SuppressOnSink=*/true};
192  BugType DoubleReleaseBugType{this, "Fuchsia handle double release",
193  "Fuchsia Handle Error"};
194  BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release",
195  "Fuchsia Handle Error"};
196  BugType ReleaseUnownedBugType{
197  this, "Fuchsia handle release of unowned handle", "Fuchsia Handle Error"};
198 
199 public:
200  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
201  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
202  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
203  ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
204  bool Assumption) const;
205  ProgramStateRef checkPointerEscape(ProgramStateRef State,
206  const InvalidatedSymbols &Escaped,
207  const CallEvent *Call,
208  PointerEscapeKind Kind) const;
209 
210  ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
211  CheckerContext &C, ExplodedNode *Pred) const;
212 
213  void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,
214  CheckerContext &C) const;
215 
216  void reportUnownedRelease(SymbolRef HandleSym, const SourceRange &Range,
217  CheckerContext &C) const;
218 
219  void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,
220  CheckerContext &C) const;
221 
222  void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
223  const SourceRange *Range, const BugType &Type,
224  StringRef Msg) const;
225 
226  void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
227  const char *Sep) const override;
228 };
229 } // end anonymous namespace
230 
231 REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
232 
233 static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym,
234  CheckerContext &Ctx) {
235  ProgramStateRef State = N->getState();
236  // When bug type is handle leak, exploded node N does not have state info for
237  // leaking handle. Get the predecessor of N instead.
238  if (!State->get<HStateMap>(Sym))
239  N = N->getFirstPred();
240 
241  const ExplodedNode *Pred = N;
242  while (N) {
243  State = N->getState();
244  if (!State->get<HStateMap>(Sym)) {
245  const HandleState *HState = Pred->getState()->get<HStateMap>(Sym);
246  if (HState && (HState->isAllocated() || HState->maybeAllocated()))
247  return N;
248  }
249  Pred = N;
250  N = N->getFirstPred();
251  }
252  return nullptr;
253 }
254 
255 namespace {
256 class FuchsiaHandleSymbolVisitor final : public SymbolVisitor {
257 public:
258  bool VisitSymbol(SymbolRef S) override {
259  if (const auto *HandleType = S->getType()->getAs<TypedefType>())
260  if (HandleType->getDecl()->getName() == HandleTypeName)
261  Symbols.push_back(S);
262  return true;
263  }
264 
265  SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; }
266 
267 private:
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.
276  int PtrToHandleLevel = 0;
277  while (QT->isAnyPointerType() || QT->isReferenceType()) {
278  ++PtrToHandleLevel;
279  QT = QT->getPointeeType();
280  }
281  if (QT->isStructureType()) {
282  // If we see a structure, see if there is any handle referenced by the
283  // structure.
284  FuchsiaHandleSymbolVisitor Visitor;
285  State->scanReachableSymbols(Arg, Visitor);
286  return Visitor.GetSymbols();
287  }
288  if (const auto *HandleType = QT->getAs<TypedefType>()) {
289  if (HandleType->getDecl()->getName() != HandleTypeName)
290  return {};
291  if (PtrToHandleLevel > 1)
292  // Not supported yet.
293  return {};
294 
295  if (PtrToHandleLevel == 0) {
296  SymbolRef Sym = Arg.getAsSymbol();
297  if (Sym) {
298  return {Sym};
299  } else {
300  return {};
301  }
302  } else {
303  assert(PtrToHandleLevel == 1);
304  if (std::optional<Loc> ArgLoc = Arg.getAs<Loc>()) {
305  SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol();
306  if (Sym) {
307  return {Sym};
308  } else {
309  return {};
310  }
311  }
312  }
313  }
314  return {};
315 }
316 
317 void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
318  CheckerContext &C) const {
319  ProgramStateRef State = C.getState();
320  const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
321  if (!FuncDecl) {
322  // Unknown call, escape by value handles. They are not covered by
323  // PointerEscape callback.
324  for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
325  if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol())
326  State = State->set<HStateMap>(Handle, HandleState::getEscaped());
327  }
328  C.addTransition(State);
329  return;
330  }
331 
332  for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
333  if (Arg >= FuncDecl->getNumParams())
334  break;
335  const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
337  getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
338 
339  // Handled in checkPostCall.
340  if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
341  hasFuchsiaAttr<AcquireHandleAttr>(PVD))
342  continue;
343 
344  for (SymbolRef Handle : Handles) {
345  const HandleState *HState = State->get<HStateMap>(Handle);
346  if (!HState || HState->isEscaped())
347  continue;
348 
349  if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
350  PVD->getType()->isIntegerType()) {
351  if (HState->isReleased()) {
352  reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
353  return;
354  }
355  }
356  }
357  }
358  C.addTransition(State);
359 }
360 
361 void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
362  CheckerContext &C) const {
363  const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
364  if (!FuncDecl)
365  return;
366 
367  // If we analyzed the function body, then ignore the annotations.
368  if (C.wasInlined)
369  return;
370 
371  ProgramStateRef State = C.getState();
372 
373  std::vector<std::function<std::string(BugReport & BR)>> Notes;
374  SymbolRef ResultSymbol = nullptr;
375  if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())
376  if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
377  ResultSymbol = Call.getReturnValue().getAsSymbol();
378 
379  // Function returns an open handle.
380  if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
381  SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
382  Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
383  auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
384  if (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 (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;
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 (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 (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 (PathBR->getInterestingnessKind(Handle)) {
463  std::string SBuf;
464  llvm::raw_string_ostream OS(SBuf);
465  OS << "Unowned handle allocated through " << ParamDiagIdx
466  << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
467  return SBuf;
468  } else
469  return "";
470  });
471  State = State->set<HStateMap>(Handle, HandleState::getUnowned());
472  } else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
473  PVD->getType()->isIntegerType()) {
474  // Working around integer by-value escapes.
475  // The by-value escape would not be captured in checkPointerEscape.
476  // If the function was not analyzed (otherwise wasInlined should be
477  // true) and there is no annotation on the handle, we assume the handle
478  // is escaped.
479  State = State->set<HStateMap>(Handle, HandleState::getEscaped());
480  }
481  }
482  }
483  const NoteTag *T = nullptr;
484  if (!Notes.empty()) {
485  T = C.getNoteTag([this, Notes{std::move(Notes)}](
486  PathSensitiveBugReport &BR) -> std::string {
487  if (&BR.getBugType() != &UseAfterReleaseBugType &&
488  &BR.getBugType() != &LeakBugType &&
489  &BR.getBugType() != &DoubleReleaseBugType &&
490  &BR.getBugType() != &ReleaseUnownedBugType)
491  return "";
492  for (auto &Note : Notes) {
493  std::string Text = Note(BR);
494  if (!Text.empty())
495  return Text;
496  }
497  return "";
498  });
499  }
500  C.addTransition(State, T);
501 }
502 
503 void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
504  CheckerContext &C) const {
505  ProgramStateRef State = C.getState();
506  SmallVector<SymbolRef, 2> LeakedSyms;
507  HStateMapTy TrackedHandles = State->get<HStateMap>();
508  for (auto &CurItem : TrackedHandles) {
509  SymbolRef ErrorSym = CurItem.second.getErrorSym();
510  // Keeping zombie handle symbols. In case the error symbol is dying later
511  // than the handle symbol we might produce spurious leak warnings (in case
512  // we find out later from the status code that the handle allocation failed
513  // in the first place).
514  if (!SymReaper.isDead(CurItem.first) ||
515  (ErrorSym && !SymReaper.isDead(ErrorSym)))
516  continue;
517  if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
518  LeakedSyms.push_back(CurItem.first);
519  State = State->remove<HStateMap>(CurItem.first);
520  }
521 
522  ExplodedNode *N = C.getPredecessor();
523  if (!LeakedSyms.empty())
524  N = reportLeaks(LeakedSyms, C, N);
525 
526  C.addTransition(State, N);
527 }
528 
529 // Acquiring a handle is not always successful. In Fuchsia most functions
530 // return a status code that determines the status of the handle.
531 // When we split the path based on this status code we know that on one
532 // path we do have the handle and on the other path the acquire failed.
533 // This method helps avoiding false positive leak warnings on paths where
534 // the function failed.
535 // Moreover, when a handle is known to be zero (the invalid handle),
536 // we no longer can follow the symbol on the path, becaue the constant
537 // zero will be used instead of the symbol. We also do not need to release
538 // an invalid handle, so we remove the corresponding symbol from the state.
539 ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
540  SVal Cond,
541  bool Assumption) const {
542  // TODO: add notes about successes/fails for APIs.
543  ConstraintManager &Cmr = State->getConstraintManager();
544  HStateMapTy TrackedHandles = State->get<HStateMap>();
545  for (auto &CurItem : TrackedHandles) {
546  ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first);
547  if (HandleVal.isConstrainedTrue()) {
548  // The handle is invalid. We can no longer follow the symbol on this path.
549  State = State->remove<HStateMap>(CurItem.first);
550  }
551  SymbolRef ErrorSym = CurItem.second.getErrorSym();
552  if (!ErrorSym)
553  continue;
554  ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);
555  if (ErrorVal.isConstrainedTrue()) {
556  // Allocation succeeded.
557  if (CurItem.second.maybeAllocated())
558  State = State->set<HStateMap>(
559  CurItem.first, HandleState::getAllocated(State, CurItem.second));
560  } else if (ErrorVal.isConstrainedFalse()) {
561  // Allocation failed.
562  if (CurItem.second.maybeAllocated())
563  State = State->remove<HStateMap>(CurItem.first);
564  }
565  }
566  return State;
567 }
568 
569 ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(
570  ProgramStateRef State, const InvalidatedSymbols &Escaped,
571  const CallEvent *Call, PointerEscapeKind Kind) const {
572  const FunctionDecl *FuncDecl =
573  Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr;
574 
575  llvm::DenseSet<SymbolRef> UnEscaped;
576  // Not all calls should escape our symbols.
577  if (FuncDecl &&
580  for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
581  if (Arg >= FuncDecl->getNumParams())
582  break;
583  const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
585  getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State);
586  for (SymbolRef Handle : Handles) {
587  if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
588  hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
589  UnEscaped.insert(Handle);
590  }
591  }
592  }
593  }
594 
595  // For out params, we have to deal with derived symbols. See
596  // MacOSKeychainAPIChecker for details.
597  for (auto I : State->get<HStateMap>()) {
598  if (Escaped.count(I.first) && !UnEscaped.count(I.first))
599  State = State->set<HStateMap>(I.first, HandleState::getEscaped());
600  if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) {
601  auto ParentSym = SD->getParentSymbol();
602  if (Escaped.count(ParentSym))
603  State = State->set<HStateMap>(I.first, HandleState::getEscaped());
604  }
605  }
606 
607  return State;
608 }
609 
610 ExplodedNode *
611 FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
612  CheckerContext &C, ExplodedNode *Pred) const {
613  ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);
614  for (SymbolRef LeakedHandle : LeakedHandles) {
615  reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType,
616  "Potential leak of handle");
617  }
618  return ErrNode;
619 }
620 
621 void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
622  const SourceRange &Range,
623  CheckerContext &C) const {
624  ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
625  reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
626  "Releasing a previously released handle");
627 }
628 
629 void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym,
630  const SourceRange &Range,
631  CheckerContext &C) const {
632  ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
633  reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType,
634  "Releasing an unowned handle");
635 }
636 
637 void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
638  const SourceRange &Range,
639  CheckerContext &C) const {
640  ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
641  reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,
642  "Using a previously released handle");
643 }
644 
645 void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
646  CheckerContext &C,
647  const SourceRange *Range,
648  const BugType &Type, StringRef Msg) const {
649  if (!ErrorNode)
650  return;
651 
652  std::unique_ptr<PathSensitiveBugReport> R;
653  if (Type.isSuppressOnSink()) {
654  const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);
655  if (AcquireNode) {
656  const Stmt *S = AcquireNode->getStmtForDiagnostics();
657  assert(S && "Statement cannot be null.");
658  PathDiagnosticLocation LocUsedForUniqueing =
660  S, C.getSourceManager(), AcquireNode->getLocationContext());
661 
662  R = std::make_unique<PathSensitiveBugReport>(
663  Type, Msg, ErrorNode, LocUsedForUniqueing,
664  AcquireNode->getLocationContext()->getDecl());
665  }
666  }
667  if (!R)
668  R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
669  if (Range)
670  R->addRange(*Range);
671  R->markInteresting(Sym);
672  C.emitReport(std::move(R));
673 }
674 
675 void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
676  mgr.registerChecker<FuchsiaHandleChecker>();
677 }
678 
679 bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {
680  return true;
681 }
682 
683 void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
684  const char *NL, const char *Sep) const {
685 
686  HStateMapTy StateMap = State->get<HStateMap>();
687 
688  if (!StateMap.isEmpty()) {
689  Out << Sep << "FuchsiaHandleChecker :" << NL;
690  for (const auto &[Sym, HandleState] : StateMap) {
691  Sym->dumpToStream(Out);
692  Out << " : ";
693  HandleState.dump(Out);
694  Out << NL;
695  }
696  }
697 }
static char ID
Definition: Arena.cpp:183
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
StringRef Text
Definition: Format.cpp:2977
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.
static const ExplodedNode * getAcquireSite(const ExplodedNode *N, SymbolRef Sym, CheckerContext &Ctx)
#define CASE(ID)
static bool isReleased(SymbolRef Sym, CheckerContext &C)
Check if the memory associated with this symbol was released.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
C Language Family Type Representation.
LineState State
Attr - This represents one attribute.
Definition: Attr.h:46
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
bool hasAttr() const
Definition: DeclBase.h:583
T * getAttr() const
Definition: DeclBase.h:579
Represents a function declaration or definition.
Definition: Decl.h:1972
QualType getReturnType() const
Definition: Decl.h:2757
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition: Decl.cpp:3696
const ParmVarDecl * getParamDecl(unsigned i) const
Definition: Decl.h:2709
const Decl * getDecl() const
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:315
Represents a parameter to a function.
Definition: Decl.h:1762
unsigned getFunctionScopeIndex() const
Returns the index of this parameter in its prototype or method scope.
Definition: Decl.h:1822
A (possibly-)qualified type.
Definition: Type.h:940
A trivial tuple used to represent a source range.
Stmt - This represents one statement.
Definition: Stmt.h:84
The base class of the type hierarchy.
Definition: Type.h:1813
bool isStructureType() const
Definition: Type.cpp:629
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:7979
bool isReferenceType() const
Definition: Type.h:7636
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition: Type.cpp:705
bool isAnyPointerType() const
Definition: Type.h:7628
const T * getAs() const
Member-template getAs<specific type>'.
Definition: Type.h:8160
QualType getType() const
Definition: Decl.h:718
This class provides an interface through which checkers can create individual bug reports.
Definition: BugReporter.h:119
const BugType & getBugType() const
Definition: BugReporter.h:149
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
bool isConstrainedFalse() const
Return true if the constraint is perfectly constrained to 'false'.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
ConditionTruthVal isNull(ProgramStateRef State, SymbolRef Sym)
Convenience method to query the state to see if a symbol is null or not null, or if neither assumptio...
ExplodedNode * getFirstPred()
const ProgramStateRef & getState() const
const LocationContext * getLocationContext() const
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
The tag upon which the TagVisitor reacts.
Definition: BugReporter.h:779
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
A Range represents the closed range [from, to].
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:55
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:104
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition: SVals.h:86
Symbolic value.
Definition: SymExpr.h:30
virtual void dumpToStream(raw_ostream &os) const
Definition: SymExpr.h:61
A class responsible for cleaning up unused symbols.
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
@ PSK_DirectEscapeOnCall
The pointer has been passed to a function call directly.
@ PSK_IndirectEscapeOnCall
The pointer has been passed to a function indirectly.
@ PSK_EscapeOutParameters
Escape for a new symbol that was generated into a region that the analyzer cannot follow during a con...
bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize)
Definition: Interp.h:2179
The JSON file list parser is used to communicate input to InstallAPI.
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
Definition: CallGraph.h:223
const FunctionProtoType * T
@ Other
Other implicit parameter.