1 //=== FuchsiaHandleChecker.cpp - Find handle leaks/double closes -*- C++ -*--=//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This checker checks if the handle of Fuchsia is properly used according to
10 // following rules.
11 //   - If a handle is acquired, it should be released before execution
12 //        ends.
13 //   - If a handle is released, it should not be released again.
14 //   - If a handle is released, it should not be used for other purposes
15 //        such as I/O.
16 //
17 // In this checker, each tracked handle is associated with a state. When the
18 // handle variable is passed to different function calls or syscalls, its state
19 // changes. The state changes can be generally represented by following ASCII
20 // Art:
21 //
22 //
23 //                                 +-------------+         +------------+
24 //          acquire_func succeeded |             | Escape  |            |
25 //               +----------------->  Allocated  +--------->  Escaped   <--+
26 //               |                 |             |         |            |  |
27 //               |                 +-----+------++         +------------+  |
28 //               |                       |      |                          |
29 // acquire_func  |         release_func  |      +--+                       |
30 //    failed     |                       |         | handle  +--------+    |
31 // +---------+   |                       |         | dies    |        |    |
32 // |         |   |                  +----v-----+   +---------> Leaked |    |
33 // |         |   |                  |          |             |(REPORT)|    |
34 // |  +----------+--+               | Released | Escape      +--------+    |
35 // |  |             |               |          +---------------------------+
36 // +--> Not tracked |               +----+---+-+
37 //    |             |                    |   |        As argument by value
38 //    +----------+--+       release_func |   +------+ in function call
39 //               |                       |          | or by reference in
40 //               |                       |          | use_func call
41 //    unowned    |                  +----v-----+    |     +-----------+
42 //  acquire_func |                  | Double   |    +-----> Use after |
43 //   succeeded   |                  | released |          | released  |
44 //               |                  | (REPORT) |          | (REPORT)  |
45 //        +---------------+         +----------+          +-----------+
46 //        | Allocated     |
47 //        | Unowned       |  release_func
48 //        |               +---------+
49 //        +---------------+         |
50 //                                  |
51 //                            +-----v----------+
52 //                            | Release of     |
53 //                            | unowned handle |
54 //                            | (REPORT)       |
55 //                            +----------------+
56 //
57 // acquire_func represents the functions or syscalls that may acquire a handle.
58 // release_func represents the functions or syscalls that may release a handle.
59 // use_func represents the functions or syscall that requires an open handle.
60 //
61 // If a tracked handle dies in "Released" or "Not Tracked" state, we assume it
62 // is properly used. Otherwise a bug and will be reported.
63 //
64 // Note that, the analyzer does not always know for sure if a function failed
65 // or succeeded. In those cases we use the state MaybeAllocated.
66 // Thus, the diagram above captures the intent, not implementation details.
67 //
68 // Due to the fact that the number of handle related syscalls in Fuchsia
69 // is large, we adopt the annotation attributes to descript syscalls'
70 // operations(acquire/release/use) on handles instead of hardcoding
71 // everything in the checker.
72 //
73 // We use following annotation attributes for handle related syscalls or
74 // functions:
75 //  1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired
76 //  2. __attribute__((release_handle("Fuchsia"))) |handle will be released
77 //  3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to
78 //     escaped state, it also needs to be open.
79 //
80 // For example, an annotated syscall:
81 //   zx_status_t zx_channel_create(
82 //   uint32_t options,
83 //   zx_handle_t* out0 __attribute__((acquire_handle("Fuchsia"))) ,
84 //   zx_handle_t* out1 __attribute__((acquire_handle("Fuchsia"))));
85 // denotes a syscall which will acquire two handles and save them to 'out0' and
86 // 'out1' when succeeded.
87 //
88 //===----------------------------------------------------------------------===//
89 
90 #include "clang/AST/Attr.h"
91 #include "clang/AST/Decl.h"
92 #include "clang/AST/Type.h"
93 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
94 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
95 #include "clang/StaticAnalyzer/Core/Checker.h"
96 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
97 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
98 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
99 #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
100 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
101 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
102 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
103 #include "llvm/ADT/StringExtras.h"
104 
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;
HandleState(Kind K,SymbolRef ErrorSym)117   HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
118 
119 public:
operator ==(const HandleState & Other) const120   bool operator==(const HandleState &Other) const {
121     return K == Other.K && ErrorSym == Other.ErrorSym;
122   }
isAllocated() const123   bool isAllocated() const { return K == Kind::Allocated; }
maybeAllocated() const124   bool maybeAllocated() const { return K == Kind::MaybeAllocated; }
isReleased() const125   bool isReleased() const { return K == Kind::Released; }
isEscaped() const126   bool isEscaped() const { return K == Kind::Escaped; }
isUnowned() const127   bool isUnowned() const { return K == Kind::Unowned; }
128 
getMaybeAllocated(SymbolRef ErrorSym)129   static HandleState getMaybeAllocated(SymbolRef ErrorSym) {
130     return HandleState(Kind::MaybeAllocated, ErrorSym);
131   }
getAllocated(ProgramStateRef State,HandleState S)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   }
getReleased()139   static HandleState getReleased() {
140     return HandleState(Kind::Released, nullptr);
141   }
getEscaped()142   static HandleState getEscaped() {
143     return HandleState(Kind::Escaped, nullptr);
144   }
getUnowned()145   static HandleState getUnowned() {
146     return HandleState(Kind::Unowned, nullptr);
147   }
148 
getErrorSym() const149   SymbolRef getErrorSym() const { return ErrorSym; }
150 
Profile(llvm::FoldingSetNodeID & ID) const151   void Profile(llvm::FoldingSetNodeID &ID) const {
152     ID.AddInteger(static_cast<int>(K));
153     ID.AddPointer(ErrorSym);
154   }
155 
dump(raw_ostream & OS) const156   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 
dump() const174   LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
175 };
176 
hasFuchsiaAttr(const Decl * D)177 template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
178   return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
179 }
180 
hasFuchsiaUnownedAttr(const Decl * D)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 
REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap,SymbolRef,HandleState) const230 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:
FuchsiaHandleSymbolVisitor(ProgramStateRef State)257   FuchsiaHandleSymbolVisitor(ProgramStateRef State) : State(std::move(State)) {}
getState() const258   ProgramStateRef getState() const { return State; }
259 
VisitSymbol(SymbolRef S)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 
GetSymbols()267   SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; }
268 
269 private:
270   SmallVector<SymbolRef, 1024> Symbols;
271   ProgramStateRef State;
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>
getFuchsiaHandleSymbols(QualType QT,SVal Arg,ProgramStateRef State)278 getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) {
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 
checkPreCall(const CallEvent & Call,CheckerContext & C) const320 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 
checkPostCall(const CallEvent & Call,CheckerContext & C) const364 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 
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const506 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.
evalAssume(ProgramStateRef State,SVal Cond,bool Assumption) const542 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 
checkPointerEscape(ProgramStateRef State,const InvalidatedSymbols & Escaped,const CallEvent * Call,PointerEscapeKind Kind) const572 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 &&
581       (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall ||
582        Kind == PSK_EscapeOutParameters)) {
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 *
reportLeaks(ArrayRef<SymbolRef> LeakedHandles,CheckerContext & C,ExplodedNode * Pred) const614 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 
reportDoubleRelease(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const624 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 
reportUnownedRelease(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const632 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 
reportUseAfterFree(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const640 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 
reportBug(SymbolRef Sym,ExplodedNode * ErrorNode,CheckerContext & C,const SourceRange * Range,const BugType & Type,StringRef Msg) const648 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 =
660           PathDiagnosticLocation::createBegin(
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 
registerFuchsiaHandleChecker(CheckerManager & mgr)677 void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
678   mgr.registerChecker<FuchsiaHandleChecker>();
679 }
680 
shouldRegisterFuchsiaHandleChecker(const CheckerManager & mgr)681 bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {
682   return true;
683 }
684 
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const685 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 }
701