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 //                              +-+---------v-+         +------------+
24 //       acquire_func succeeded |             | Escape  |            |
25 //            +----------------->  Allocated  +--------->  Escaped   <--+
26 //            |                 |             |         |            |  |
27 //            |                 +-----+------++         +------------+  |
28 //            |                       |      |                          |
29 //            |         release_func  |      +--+                       |
30 //            |                       |         | 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 //        +---------+            +----v-----+    |     +-----------+
42 //        acquire_func failed    | Double   |    +-----> Use after |
43 //                               | released |          | released  |
44 //                               | (REPORT) |          | (REPORT)  |
45 //                               +----------+          +-----------+
46 //
47 // acquire_func represents the functions or syscalls that may acquire a handle.
48 // release_func represents the functions or syscalls that may release a handle.
49 // use_func represents the functions or syscall that requires an open handle.
50 //
51 // If a tracked handle dies in "Released" or "Not Tracked" state, we assume it
52 // is properly used. Otherwise a bug and will be reported.
53 //
54 // Note that, the analyzer does not always know for sure if a function failed
55 // or succeeded. In those cases we use the state MaybeAllocated.
56 // Thus, the diagram above captures the intent, not implementation details.
57 //
58 // Due to the fact that the number of handle related syscalls in Fuchsia
59 // is large, we adopt the annotation attributes to descript syscalls'
60 // operations(acquire/release/use) on handles instead of hardcoding
61 // everything in the checker.
62 //
63 // We use following annotation attributes for handle related syscalls or
64 // functions:
65 //  1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired
66 //  2. __attribute__((release_handle("Fuchsia"))) |handle will be released
67 //  3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to
68 //     escaped state, it also needs to be open.
69 //
70 // For example, an annotated syscall:
71 //   zx_status_t zx_channel_create(
72 //   uint32_t options,
73 //   zx_handle_t* out0 __attribute__((acquire_handle("Fuchsia"))) ,
74 //   zx_handle_t* out1 __attribute__((acquire_handle("Fuchsia"))));
75 // denotes a syscall which will acquire two handles and save them to 'out0' and
76 // 'out1' when succeeded.
77 //
78 //===----------------------------------------------------------------------===//
79 
80 #include "clang/AST/Attr.h"
81 #include "clang/AST/Decl.h"
82 #include "clang/AST/Type.h"
83 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
84 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
85 #include "clang/StaticAnalyzer/Core/Checker.h"
86 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
87 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
88 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
89 #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
90 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
91 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
92 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
93 #include "llvm/ADT/StringExtras.h"
94 
95 using namespace clang;
96 using namespace ento;
97 
98 namespace {
99 
100 static const StringRef HandleTypeName = "zx_handle_t";
101 static const StringRef ErrorTypeName = "zx_status_t";
102 
103 class HandleState {
104 private:
105   enum class Kind { MaybeAllocated, Allocated, Released, Escaped } K;
106   SymbolRef ErrorSym;
HandleState(Kind K,SymbolRef ErrorSym)107   HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
108 
109 public:
operator ==(const HandleState & Other) const110   bool operator==(const HandleState &Other) const {
111     return K == Other.K && ErrorSym == Other.ErrorSym;
112   }
isAllocated() const113   bool isAllocated() const { return K == Kind::Allocated; }
maybeAllocated() const114   bool maybeAllocated() const { return K == Kind::MaybeAllocated; }
isReleased() const115   bool isReleased() const { return K == Kind::Released; }
isEscaped() const116   bool isEscaped() const { return K == Kind::Escaped; }
117 
getMaybeAllocated(SymbolRef ErrorSym)118   static HandleState getMaybeAllocated(SymbolRef ErrorSym) {
119     return HandleState(Kind::MaybeAllocated, ErrorSym);
120   }
getAllocated(ProgramStateRef State,HandleState S)121   static HandleState getAllocated(ProgramStateRef State, HandleState S) {
122     assert(S.maybeAllocated());
123     assert(State->getConstraintManager()
124                .isNull(State, S.getErrorSym())
125                .isConstrained());
126     return HandleState(Kind::Allocated, nullptr);
127   }
getReleased()128   static HandleState getReleased() {
129     return HandleState(Kind::Released, nullptr);
130   }
getEscaped()131   static HandleState getEscaped() {
132     return HandleState(Kind::Escaped, nullptr);
133   }
134 
getErrorSym() const135   SymbolRef getErrorSym() const { return ErrorSym; }
136 
Profile(llvm::FoldingSetNodeID & ID) const137   void Profile(llvm::FoldingSetNodeID &ID) const {
138     ID.AddInteger(static_cast<int>(K));
139     ID.AddPointer(ErrorSym);
140   }
141 
dump(raw_ostream & OS) const142   LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
143     switch (K) {
144 #define CASE(ID)                                                               \
145   case ID:                                                                     \
146     OS << #ID;                                                                 \
147     break;
148       CASE(Kind::MaybeAllocated)
149       CASE(Kind::Allocated)
150       CASE(Kind::Released)
151       CASE(Kind::Escaped)
152     }
153     if (ErrorSym) {
154       OS << " ErrorSym: ";
155       ErrorSym->dumpToStream(OS);
156     }
157   }
158 
dump() const159   LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
160 };
161 
hasFuchsiaAttr(const Decl * D)162 template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
163   return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
164 }
165 
166 class FuchsiaHandleChecker
167     : public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
168                      check::PointerEscape, eval::Assume> {
169   BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error",
170                       /*SuppressOnSink=*/true};
171   BugType DoubleReleaseBugType{this, "Fuchsia handle double release",
172                                "Fuchsia Handle Error"};
173   BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release",
174                                  "Fuchsia Handle Error"};
175 
176 public:
177   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
178   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
179   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
180   ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
181                              bool Assumption) const;
182   ProgramStateRef checkPointerEscape(ProgramStateRef State,
183                                      const InvalidatedSymbols &Escaped,
184                                      const CallEvent *Call,
185                                      PointerEscapeKind Kind) const;
186 
187   ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
188                             CheckerContext &C, ExplodedNode *Pred) const;
189 
190   void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,
191                            CheckerContext &C) const;
192 
193   void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,
194                           CheckerContext &C) const;
195 
196   void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
197                  const SourceRange *Range, const BugType &Type,
198                  StringRef Msg) const;
199 
200   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
201                   const char *Sep) const override;
202 };
203 } // end anonymous namespace
204 
REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap,SymbolRef,HandleState) const205 REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
206 
207 static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym,
208                                           CheckerContext &Ctx) {
209   ProgramStateRef State = N->getState();
210   // When bug type is handle leak, exploded node N does not have state info for
211   // leaking handle. Get the predecessor of N instead.
212   if (!State->get<HStateMap>(Sym))
213     N = N->getFirstPred();
214 
215   const ExplodedNode *Pred = N;
216   while (N) {
217     State = N->getState();
218     if (!State->get<HStateMap>(Sym)) {
219       const HandleState *HState = Pred->getState()->get<HStateMap>(Sym);
220       if (HState && (HState->isAllocated() || HState->maybeAllocated()))
221         return N;
222     }
223     Pred = N;
224     N = N->getFirstPred();
225   }
226   return nullptr;
227 }
228 
229 namespace {
230 class FuchsiaHandleSymbolVisitor final : public SymbolVisitor {
231 public:
FuchsiaHandleSymbolVisitor(ProgramStateRef State)232   FuchsiaHandleSymbolVisitor(ProgramStateRef State) : State(std::move(State)) {}
getState() const233   ProgramStateRef getState() const { return State; }
234 
VisitSymbol(SymbolRef S)235   bool VisitSymbol(SymbolRef S) override {
236     if (const auto *HandleType = S->getType()->getAs<TypedefType>())
237       if (HandleType->getDecl()->getName() == HandleTypeName)
238         Symbols.push_back(S);
239     return true;
240   }
241 
GetSymbols()242   SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; }
243 
244 private:
245   SmallVector<SymbolRef, 1024> Symbols;
246   ProgramStateRef State;
247 };
248 } // end anonymous namespace
249 
250 /// Returns the symbols extracted from the argument or empty vector if it cannot
251 /// be found. It is unlikely to have over 1024 symbols in one argument.
252 static SmallVector<SymbolRef, 1024>
getFuchsiaHandleSymbols(QualType QT,SVal Arg,ProgramStateRef State)253 getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) {
254   int PtrToHandleLevel = 0;
255   while (QT->isAnyPointerType() || QT->isReferenceType()) {
256     ++PtrToHandleLevel;
257     QT = QT->getPointeeType();
258   }
259   if (QT->isStructureType()) {
260     // If we see a structure, see if there is any handle referenced by the
261     // structure.
262     FuchsiaHandleSymbolVisitor Visitor(State);
263     State->scanReachableSymbols(Arg, Visitor);
264     return Visitor.GetSymbols();
265   }
266   if (const auto *HandleType = QT->getAs<TypedefType>()) {
267     if (HandleType->getDecl()->getName() != HandleTypeName)
268       return {};
269     if (PtrToHandleLevel > 1)
270       // Not supported yet.
271       return {};
272 
273     if (PtrToHandleLevel == 0) {
274       SymbolRef Sym = Arg.getAsSymbol();
275       if (Sym) {
276         return {Sym};
277       } else {
278         return {};
279       }
280     } else {
281       assert(PtrToHandleLevel == 1);
282       if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) {
283         SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol();
284         if (Sym) {
285           return {Sym};
286         } else {
287           return {};
288         }
289       }
290     }
291   }
292   return {};
293 }
294 
checkPreCall(const CallEvent & Call,CheckerContext & C) const295 void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
296                                         CheckerContext &C) const {
297   ProgramStateRef State = C.getState();
298   const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
299   if (!FuncDecl) {
300     // Unknown call, escape by value handles. They are not covered by
301     // PointerEscape callback.
302     for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
303       if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol())
304         State = State->set<HStateMap>(Handle, HandleState::getEscaped());
305     }
306     C.addTransition(State);
307     return;
308   }
309 
310   for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
311     if (Arg >= FuncDecl->getNumParams())
312       break;
313     const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
314     SmallVector<SymbolRef, 1024> Handles =
315         getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
316 
317     // Handled in checkPostCall.
318     if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
319         hasFuchsiaAttr<AcquireHandleAttr>(PVD))
320       continue;
321 
322     for (SymbolRef Handle : Handles) {
323       const HandleState *HState = State->get<HStateMap>(Handle);
324       if (!HState || HState->isEscaped())
325         continue;
326 
327       if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
328           PVD->getType()->isIntegerType()) {
329         if (HState->isReleased()) {
330           reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
331           return;
332         }
333       }
334     }
335   }
336   C.addTransition(State);
337 }
338 
checkPostCall(const CallEvent & Call,CheckerContext & C) const339 void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
340                                          CheckerContext &C) const {
341   const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
342   if (!FuncDecl)
343     return;
344 
345   // If we analyzed the function body, then ignore the annotations.
346   if (C.wasInlined)
347     return;
348 
349   ProgramStateRef State = C.getState();
350 
351   std::vector<std::function<std::string(BugReport & BR)>> Notes;
352   SymbolRef ResultSymbol = nullptr;
353   if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())
354     if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
355       ResultSymbol = Call.getReturnValue().getAsSymbol();
356 
357   // Function returns an open handle.
358   if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
359     SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
360     Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
361       auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
362       if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
363         std::string SBuf;
364         llvm::raw_string_ostream OS(SBuf);
365         OS << "Function '" << FuncDecl->getDeclName()
366            << "' returns an open handle";
367         return OS.str();
368       } else
369         return "";
370     });
371     State =
372         State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));
373   }
374 
375   for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
376     if (Arg >= FuncDecl->getNumParams())
377       break;
378     const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
379     unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1;
380     SmallVector<SymbolRef, 1024> Handles =
381         getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
382 
383     for (SymbolRef Handle : Handles) {
384       const HandleState *HState = State->get<HStateMap>(Handle);
385       if (HState && HState->isEscaped())
386         continue;
387       if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
388         if (HState && HState->isReleased()) {
389           reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
390           return;
391         } else {
392           Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
393             auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
394             if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
395               std::string SBuf;
396               llvm::raw_string_ostream OS(SBuf);
397               OS << "Handle released through " << ParamDiagIdx
398                  << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
399               return OS.str();
400             } else
401               return "";
402           });
403           State = State->set<HStateMap>(Handle, HandleState::getReleased());
404         }
405       } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
406         Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
407           auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
408           if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
409             std::string SBuf;
410             llvm::raw_string_ostream OS(SBuf);
411             OS << "Handle allocated through " << ParamDiagIdx
412                << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
413             return OS.str();
414           } else
415             return "";
416         });
417         State = State->set<HStateMap>(
418             Handle, HandleState::getMaybeAllocated(ResultSymbol));
419       } else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
420                  PVD->getType()->isIntegerType()) {
421         // Working around integer by-value escapes.
422         // The by-value escape would not be captured in checkPointerEscape.
423         // If the function was not analyzed (otherwise wasInlined should be
424         // true) and there is no annotation on the handle, we assume the handle
425         // is escaped.
426         State = State->set<HStateMap>(Handle, HandleState::getEscaped());
427       }
428     }
429   }
430   const NoteTag *T = nullptr;
431   if (!Notes.empty()) {
432     T = C.getNoteTag([this, Notes{std::move(Notes)}](
433                          PathSensitiveBugReport &BR) -> std::string {
434       if (&BR.getBugType() != &UseAfterReleaseBugType &&
435           &BR.getBugType() != &LeakBugType &&
436           &BR.getBugType() != &DoubleReleaseBugType)
437         return "";
438       for (auto &Note : Notes) {
439         std::string Text = Note(BR);
440         if (!Text.empty())
441           return Text;
442       }
443       return "";
444     });
445   }
446   C.addTransition(State, T);
447 }
448 
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const449 void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
450                                             CheckerContext &C) const {
451   ProgramStateRef State = C.getState();
452   SmallVector<SymbolRef, 2> LeakedSyms;
453   HStateMapTy TrackedHandles = State->get<HStateMap>();
454   for (auto &CurItem : TrackedHandles) {
455     SymbolRef ErrorSym = CurItem.second.getErrorSym();
456     // Keeping zombie handle symbols. In case the error symbol is dying later
457     // than the handle symbol we might produce spurious leak warnings (in case
458     // we find out later from the status code that the handle allocation failed
459     // in the first place).
460     if (!SymReaper.isDead(CurItem.first) ||
461         (ErrorSym && !SymReaper.isDead(ErrorSym)))
462       continue;
463     if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
464       LeakedSyms.push_back(CurItem.first);
465     State = State->remove<HStateMap>(CurItem.first);
466   }
467 
468   ExplodedNode *N = C.getPredecessor();
469   if (!LeakedSyms.empty())
470     N = reportLeaks(LeakedSyms, C, N);
471 
472   C.addTransition(State, N);
473 }
474 
475 // Acquiring a handle is not always successful. In Fuchsia most functions
476 // return a status code that determines the status of the handle.
477 // When we split the path based on this status code we know that on one
478 // path we do have the handle and on the other path the acquire failed.
479 // This method helps avoiding false positive leak warnings on paths where
480 // the function failed.
481 // Moreover, when a handle is known to be zero (the invalid handle),
482 // we no longer can follow the symbol on the path, becaue the constant
483 // zero will be used instead of the symbol. We also do not need to release
484 // an invalid handle, so we remove the corresponding symbol from the state.
evalAssume(ProgramStateRef State,SVal Cond,bool Assumption) const485 ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
486                                                  SVal Cond,
487                                                  bool Assumption) const {
488   // TODO: add notes about successes/fails for APIs.
489   ConstraintManager &Cmr = State->getConstraintManager();
490   HStateMapTy TrackedHandles = State->get<HStateMap>();
491   for (auto &CurItem : TrackedHandles) {
492     ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first);
493     if (HandleVal.isConstrainedTrue()) {
494       // The handle is invalid. We can no longer follow the symbol on this path.
495       State = State->remove<HStateMap>(CurItem.first);
496     }
497     SymbolRef ErrorSym = CurItem.second.getErrorSym();
498     if (!ErrorSym)
499       continue;
500     ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);
501     if (ErrorVal.isConstrainedTrue()) {
502       // Allocation succeeded.
503       if (CurItem.second.maybeAllocated())
504         State = State->set<HStateMap>(
505             CurItem.first, HandleState::getAllocated(State, CurItem.second));
506     } else if (ErrorVal.isConstrainedFalse()) {
507       // Allocation failed.
508       if (CurItem.second.maybeAllocated())
509         State = State->remove<HStateMap>(CurItem.first);
510     }
511   }
512   return State;
513 }
514 
checkPointerEscape(ProgramStateRef State,const InvalidatedSymbols & Escaped,const CallEvent * Call,PointerEscapeKind Kind) const515 ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(
516     ProgramStateRef State, const InvalidatedSymbols &Escaped,
517     const CallEvent *Call, PointerEscapeKind Kind) const {
518   const FunctionDecl *FuncDecl =
519       Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr;
520 
521   llvm::DenseSet<SymbolRef> UnEscaped;
522   // Not all calls should escape our symbols.
523   if (FuncDecl &&
524       (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall ||
525        Kind == PSK_EscapeOutParameters)) {
526     for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
527       if (Arg >= FuncDecl->getNumParams())
528         break;
529       const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
530       SmallVector<SymbolRef, 1024> Handles =
531           getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State);
532       for (SymbolRef Handle : Handles) {
533         if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
534             hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
535           UnEscaped.insert(Handle);
536         }
537       }
538     }
539   }
540 
541   // For out params, we have to deal with derived symbols. See
542   // MacOSKeychainAPIChecker for details.
543   for (auto I : State->get<HStateMap>()) {
544     if (Escaped.count(I.first) && !UnEscaped.count(I.first))
545       State = State->set<HStateMap>(I.first, HandleState::getEscaped());
546     if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) {
547       auto ParentSym = SD->getParentSymbol();
548       if (Escaped.count(ParentSym))
549         State = State->set<HStateMap>(I.first, HandleState::getEscaped());
550     }
551   }
552 
553   return State;
554 }
555 
556 ExplodedNode *
reportLeaks(ArrayRef<SymbolRef> LeakedHandles,CheckerContext & C,ExplodedNode * Pred) const557 FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
558                                   CheckerContext &C, ExplodedNode *Pred) const {
559   ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);
560   for (SymbolRef LeakedHandle : LeakedHandles) {
561     reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType,
562               "Potential leak of handle");
563   }
564   return ErrNode;
565 }
566 
reportDoubleRelease(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const567 void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
568                                                const SourceRange &Range,
569                                                CheckerContext &C) const {
570   ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
571   reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
572             "Releasing a previously released handle");
573 }
574 
reportUseAfterFree(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const575 void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
576                                               const SourceRange &Range,
577                                               CheckerContext &C) const {
578   ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
579   reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,
580             "Using a previously released handle");
581 }
582 
reportBug(SymbolRef Sym,ExplodedNode * ErrorNode,CheckerContext & C,const SourceRange * Range,const BugType & Type,StringRef Msg) const583 void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
584                                      CheckerContext &C,
585                                      const SourceRange *Range,
586                                      const BugType &Type, StringRef Msg) const {
587   if (!ErrorNode)
588     return;
589 
590   std::unique_ptr<PathSensitiveBugReport> R;
591   if (Type.isSuppressOnSink()) {
592     const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);
593     if (AcquireNode) {
594       PathDiagnosticLocation LocUsedForUniqueing =
595           PathDiagnosticLocation::createBegin(
596               AcquireNode->getStmtForDiagnostics(), C.getSourceManager(),
597               AcquireNode->getLocationContext());
598 
599       R = std::make_unique<PathSensitiveBugReport>(
600           Type, Msg, ErrorNode, LocUsedForUniqueing,
601           AcquireNode->getLocationContext()->getDecl());
602     }
603   }
604   if (!R)
605     R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
606   if (Range)
607     R->addRange(*Range);
608   R->markInteresting(Sym);
609   C.emitReport(std::move(R));
610 }
611 
registerFuchsiaHandleChecker(CheckerManager & mgr)612 void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
613   mgr.registerChecker<FuchsiaHandleChecker>();
614 }
615 
shouldRegisterFuchsiaHandleChecker(const CheckerManager & mgr)616 bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {
617   return true;
618 }
619 
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const620 void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
621                                       const char *NL, const char *Sep) const {
622 
623   HStateMapTy StateMap = State->get<HStateMap>();
624 
625   if (!StateMap.isEmpty()) {
626     Out << Sep << "FuchsiaHandleChecker :" << NL;
627     for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
628          ++I) {
629       I.getKey()->dumpToStream(Out);
630       Out << " : ";
631       I.getData().dump(Out);
632       Out << NL;
633     }
634   }
635 }
636