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