1 //=== FuchsiaHandleChecker.cpp - Find handle leaks/double closes -*- C++ -*--=//
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
7 //===----------------------------------------------------------------------===//
9 // This checker checks if the handle of Fuchsia is properly used according to
11 // - If a handle is acquired, it should be released before execution
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
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
23 // +-------------+ +------------+
24 // acquire_func succeeded | | Escape | |
25 // +-----------------> Allocated +---------> Escaped <--+
27 // | +-----+------++ +------------+ |
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 // +---------------+ +----------+ +-----------+
47 // | Unowned | release_func
49 // +---------------+ |
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.
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.
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.
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.
73 // We use following annotation attributes for handle related syscalls or
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.
80 // For example, an annotated syscall:
81 // zx_status_t zx_channel_create(
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.
88 //===----------------------------------------------------------------------===//
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"
106 using namespace clang
;
107 using namespace ento
;
111 static const StringRef HandleTypeName
= "zx_handle_t";
112 static const StringRef ErrorTypeName
= "zx_status_t";
116 enum class Kind
{ MaybeAllocated
, Allocated
, Released
, Escaped
, Unowned
} K
;
118 HandleState(Kind K
, SymbolRef ErrorSym
) : K(K
), ErrorSym(ErrorSym
) {}
121 bool operator==(const HandleState
&Other
) const {
122 return K
== Other
.K
&& ErrorSym
== Other
.ErrorSym
;
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
; }
130 static HandleState
getMaybeAllocated(SymbolRef ErrorSym
) {
131 return HandleState(Kind::MaybeAllocated
, ErrorSym
);
133 static HandleState
getAllocated(ProgramStateRef State
, HandleState S
) {
134 assert(S
.maybeAllocated());
135 assert(State
->getConstraintManager()
136 .isNull(State
, S
.getErrorSym())
138 return HandleState(Kind::Allocated
, nullptr);
140 static HandleState
getReleased() {
141 return HandleState(Kind::Released
, nullptr);
143 static HandleState
getEscaped() {
144 return HandleState(Kind::Escaped
, nullptr);
146 static HandleState
getUnowned() {
147 return HandleState(Kind::Unowned
, nullptr);
150 SymbolRef
getErrorSym() const { return ErrorSym
; }
152 void Profile(llvm::FoldingSetNodeID
&ID
) const {
153 ID
.AddInteger(static_cast<int>(K
));
154 ID
.AddPointer(ErrorSym
);
157 LLVM_DUMP_METHOD
void dump(raw_ostream
&OS
) const {
163 CASE(Kind::MaybeAllocated
)
164 CASE(Kind::Allocated
)
171 ErrorSym
->dumpToStream(OS
);
175 LLVM_DUMP_METHOD
void dump() const { dump(llvm::errs()); }
178 template <typename Attr
> static bool hasFuchsiaAttr(const Decl
*D
) {
179 return D
->hasAttr
<Attr
>() && D
->getAttr
<Attr
>()->getHandleType() == "Fuchsia";
182 template <typename Attr
> static bool hasFuchsiaUnownedAttr(const Decl
*D
) {
183 return D
->hasAttr
<Attr
>() &&
184 D
->getAttr
<Attr
>()->getHandleType() == "FuchsiaUnowned";
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"};
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;
210 ExplodedNode
*reportLeaks(ArrayRef
<SymbolRef
> LeakedHandles
,
211 CheckerContext
&C
, ExplodedNode
*Pred
) const;
213 void reportDoubleRelease(SymbolRef HandleSym
, const SourceRange
&Range
,
214 CheckerContext
&C
) const;
216 void reportUnownedRelease(SymbolRef HandleSym
, const SourceRange
&Range
,
217 CheckerContext
&C
) const;
219 void reportUseAfterFree(SymbolRef HandleSym
, const SourceRange
&Range
,
220 CheckerContext
&C
) const;
222 void reportBug(SymbolRef Sym
, ExplodedNode
*ErrorNode
, CheckerContext
&C
,
223 const SourceRange
*Range
, const BugType
&Type
,
224 StringRef Msg
) const;
226 void printState(raw_ostream
&Out
, ProgramStateRef State
, const char *NL
,
227 const char *Sep
) const override
;
229 } // end anonymous namespace
231 REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap
, SymbolRef
, HandleState
)
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();
241 const ExplodedNode
*Pred
= 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()))
250 N
= N
->getFirstPred();
256 class FuchsiaHandleSymbolVisitor final
: public SymbolVisitor
{
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
);
265 SmallVector
<SymbolRef
, 1024> GetSymbols() { return Symbols
; }
268 SmallVector
<SymbolRef
, 1024> Symbols
;
270 } // end anonymous namespace
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.
274 static SmallVector
<SymbolRef
, 1024>
275 getFuchsiaHandleSymbols(QualType QT
, SVal Arg
, ProgramStateRef State
) {
276 int PtrToHandleLevel
= 0;
277 while (QT
->isAnyPointerType() || QT
->isReferenceType()) {
279 QT
= QT
->getPointeeType();
281 if (QT
->isStructureType()) {
282 // If we see a structure, see if there is any handle referenced by the
284 FuchsiaHandleSymbolVisitor Visitor
;
285 State
->scanReachableSymbols(Arg
, Visitor
);
286 return Visitor
.GetSymbols();
288 if (const auto *HandleType
= QT
->getAs
<TypedefType
>()) {
289 if (HandleType
->getDecl()->getName() != HandleTypeName
)
291 if (PtrToHandleLevel
> 1)
292 // Not supported yet.
295 if (PtrToHandleLevel
== 0) {
296 SymbolRef Sym
= Arg
.getAsSymbol();
303 assert(PtrToHandleLevel
== 1);
304 if (std::optional
<Loc
> ArgLoc
= Arg
.getAs
<Loc
>()) {
305 SymbolRef Sym
= State
->getSVal(*ArgLoc
).getAsSymbol();
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());
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());
328 C
.addTransition(State
);
332 for (unsigned Arg
= 0; Arg
< Call
.getNumArgs(); ++Arg
) {
333 if (Arg
>= FuncDecl
->getNumParams())
335 const ParmVarDecl
*PVD
= FuncDecl
->getParamDecl(Arg
);
336 SmallVector
<SymbolRef
, 1024> Handles
=
337 getFuchsiaHandleSymbols(PVD
->getType(), Call
.getArgSVal(Arg
), State
);
339 // Handled in checkPostCall.
340 if (hasFuchsiaAttr
<ReleaseHandleAttr
>(PVD
) ||
341 hasFuchsiaAttr
<AcquireHandleAttr
>(PVD
))
344 for (SymbolRef Handle
: Handles
) {
345 const HandleState
*HState
= State
->get
<HStateMap
>(Handle
);
346 if (!HState
|| HState
->isEscaped())
349 if (hasFuchsiaAttr
<UseHandleAttr
>(PVD
) ||
350 PVD
->getType()->isIntegerType()) {
351 if (HState
->isReleased()) {
352 reportUseAfterFree(Handle
, Call
.getArgSourceRange(Arg
), C
);
358 C
.addTransition(State
);
361 void FuchsiaHandleChecker::checkPostCall(const CallEvent
&Call
,
362 CheckerContext
&C
) const {
363 const FunctionDecl
*FuncDecl
= dyn_cast_or_null
<FunctionDecl
>(Call
.getDecl());
367 // If we analyzed the function body, then ignore the annotations.
371 ProgramStateRef State
= C
.getState();
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();
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
)) {
386 llvm::raw_string_ostream
OS(SBuf
);
387 OS
<< "Function '" << FuncDecl
->getDeclName()
388 << "' returns an open handle";
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
)) {
402 llvm::raw_string_ostream
OS(SBuf
);
403 OS
<< "Function '" << FuncDecl
->getDeclName()
404 << "' returns an unowned handle";
409 State
= State
->set
<HStateMap
>(RetSym
, HandleState::getUnowned());
412 for (unsigned Arg
= 0; Arg
< Call
.getNumArgs(); ++Arg
) {
413 if (Arg
>= FuncDecl
->getNumParams())
415 const ParmVarDecl
*PVD
= FuncDecl
->getParamDecl(Arg
);
416 unsigned ParamDiagIdx
= PVD
->getFunctionScopeIndex() + 1;
417 SmallVector
<SymbolRef
, 1024> Handles
=
418 getFuchsiaHandleSymbols(PVD
->getType(), Call
.getArgSVal(Arg
), State
);
420 for (SymbolRef Handle
: Handles
) {
421 const HandleState
*HState
= State
->get
<HStateMap
>(Handle
);
422 if (HState
&& HState
->isEscaped())
424 if (hasFuchsiaAttr
<ReleaseHandleAttr
>(PVD
)) {
425 if (HState
&& HState
->isReleased()) {
426 reportDoubleRelease(Handle
, Call
.getArgSourceRange(Arg
), C
);
428 } else if (HState
&& HState
->isUnowned()) {
429 reportUnownedRelease(Handle
, Call
.getArgSourceRange(Arg
), C
);
432 Notes
.push_back([Handle
, ParamDiagIdx
](BugReport
&BR
) -> std::string
{
433 auto *PathBR
= static_cast<PathSensitiveBugReport
*>(&BR
);
434 if (PathBR
->getInterestingnessKind(Handle
)) {
436 llvm::raw_string_ostream
OS(SBuf
);
437 OS
<< "Handle released through " << ParamDiagIdx
438 << llvm::getOrdinalSuffix(ParamDiagIdx
) << " parameter";
443 State
= State
->set
<HStateMap
>(Handle
, HandleState::getReleased());
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
)) {
450 llvm::raw_string_ostream
OS(SBuf
);
451 OS
<< "Handle allocated through " << ParamDiagIdx
452 << llvm::getOrdinalSuffix(ParamDiagIdx
) << " parameter";
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
)) {
464 llvm::raw_string_ostream
OS(SBuf
);
465 OS
<< "Unowned handle allocated through " << ParamDiagIdx
466 << llvm::getOrdinalSuffix(ParamDiagIdx
) << " parameter";
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
479 State
= State
->set
<HStateMap
>(Handle
, HandleState::getEscaped());
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
)
492 for (auto &Note
: Notes
) {
493 std::string Text
= Note(BR
);
500 C
.addTransition(State
, T
);
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
)))
517 if (CurItem
.second
.isAllocated() || CurItem
.second
.maybeAllocated())
518 LeakedSyms
.push_back(CurItem
.first
);
519 State
= State
->remove
<HStateMap
>(CurItem
.first
);
522 ExplodedNode
*N
= C
.getPredecessor();
523 if (!LeakedSyms
.empty())
524 N
= reportLeaks(LeakedSyms
, C
, N
);
526 C
.addTransition(State
, N
);
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
,
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
);
551 SymbolRef ErrorSym
= CurItem
.second
.getErrorSym();
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
);
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;
575 llvm::DenseSet
<SymbolRef
> UnEscaped
;
576 // Not all calls should escape our symbols.
578 (Kind
== PSK_DirectEscapeOnCall
|| Kind
== PSK_IndirectEscapeOnCall
||
579 Kind
== PSK_EscapeOutParameters
)) {
580 for (unsigned Arg
= 0; Arg
< Call
->getNumArgs(); ++Arg
) {
581 if (Arg
>= FuncDecl
->getNumParams())
583 const ParmVarDecl
*PVD
= FuncDecl
->getParamDecl(Arg
);
584 SmallVector
<SymbolRef
, 1024> Handles
=
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
);
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());
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");
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");
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");
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");
645 void FuchsiaHandleChecker::reportBug(SymbolRef Sym
, ExplodedNode
*ErrorNode
,
647 const SourceRange
*Range
,
648 const BugType
&Type
, StringRef Msg
) const {
652 std::unique_ptr
<PathSensitiveBugReport
> R
;
653 if (Type
.isSuppressOnSink()) {
654 const ExplodedNode
*AcquireNode
= getAcquireSite(ErrorNode
, Sym
, C
);
656 const Stmt
*S
= AcquireNode
->getStmtForDiagnostics();
657 assert(S
&& "Statement cannot be null.");
658 PathDiagnosticLocation LocUsedForUniqueing
=
659 PathDiagnosticLocation::createBegin(
660 S
, C
.getSourceManager(), AcquireNode
->getLocationContext());
662 R
= std::make_unique
<PathSensitiveBugReport
>(
663 Type
, Msg
, ErrorNode
, LocUsedForUniqueing
,
664 AcquireNode
->getLocationContext()->getDecl());
668 R
= std::make_unique
<PathSensitiveBugReport
>(Type
, Msg
, ErrorNode
);
671 R
->markInteresting(Sym
);
672 C
.emitReport(std::move(R
));
675 void ento::registerFuchsiaHandleChecker(CheckerManager
&mgr
) {
676 mgr
.registerChecker
<FuchsiaHandleChecker
>();
679 bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager
&mgr
) {
683 void FuchsiaHandleChecker::printState(raw_ostream
&Out
, ProgramStateRef State
,
684 const char *NL
, const char *Sep
) const {
686 HStateMapTy StateMap
= State
->get
<HStateMap
>();
688 if (!StateMap
.isEmpty()) {
689 Out
<< Sep
<< "FuchsiaHandleChecker :" << NL
;
690 for (const auto &[Sym
, HandleState
] : StateMap
) {
691 Sym
->dumpToStream(Out
);
693 HandleState
.dump(Out
);