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 diagramm 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
94 using namespace clang;
95 using namespace ento;
96
97 namespace {
98
99 static const StringRef HandleTypeName = "zx_handle_t";
100 static const StringRef ErrorTypeName = "zx_status_t";
101
102 class HandleState {
103 private:
104 enum class Kind { MaybeAllocated, Allocated, Released, Escaped } K;
105 SymbolRef ErrorSym;
HandleState(Kind K,SymbolRef ErrorSym)106 HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
107
108 public:
operator ==(const HandleState & Other) const109 bool operator==(const HandleState &Other) const {
110 return K == Other.K && ErrorSym == Other.ErrorSym;
111 }
isAllocated() const112 bool isAllocated() const { return K == Kind::Allocated; }
maybeAllocated() const113 bool maybeAllocated() const { return K == Kind::MaybeAllocated; }
isReleased() const114 bool isReleased() const { return K == Kind::Released; }
isEscaped() const115 bool isEscaped() const { return K == Kind::Escaped; }
116
getMaybeAllocated(SymbolRef ErrorSym)117 static HandleState getMaybeAllocated(SymbolRef ErrorSym) {
118 return HandleState(Kind::MaybeAllocated, ErrorSym);
119 }
getAllocated(ProgramStateRef State,HandleState S)120 static HandleState getAllocated(ProgramStateRef State, HandleState S) {
121 assert(S.maybeAllocated());
122 assert(State->getConstraintManager()
123 .isNull(State, S.getErrorSym())
124 .isConstrained());
125 return HandleState(Kind::Allocated, nullptr);
126 }
getReleased()127 static HandleState getReleased() {
128 return HandleState(Kind::Released, nullptr);
129 }
getEscaped()130 static HandleState getEscaped() {
131 return HandleState(Kind::Escaped, nullptr);
132 }
133
getErrorSym() const134 SymbolRef getErrorSym() const { return ErrorSym; }
135
Profile(llvm::FoldingSetNodeID & ID) const136 void Profile(llvm::FoldingSetNodeID &ID) const {
137 ID.AddInteger(static_cast<int>(K));
138 ID.AddPointer(ErrorSym);
139 }
140
dump(raw_ostream & OS) const141 LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
142 switch (K) {
143 #define CASE(ID) \
144 case ID: \
145 OS << #ID; \
146 break;
147 CASE(Kind::MaybeAllocated)
148 CASE(Kind::Allocated)
149 CASE(Kind::Released)
150 CASE(Kind::Escaped)
151 }
152 }
153
dump() const154 LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
155 };
156
hasFuchsiaAttr(const Decl * D)157 template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
158 return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
159 }
160
161 class FuchsiaHandleChecker
162 : public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
163 check::PointerEscape, eval::Assume> {
164 BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error",
165 /*SuppressOnSink=*/true};
166 BugType DoubleReleaseBugType{this, "Fuchsia handle double release",
167 "Fuchsia Handle Error"};
168 BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release",
169 "Fuchsia Handle Error"};
170
171 public:
172 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
173 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
174 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
175 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
176 bool Assumption) const;
177 ProgramStateRef checkPointerEscape(ProgramStateRef State,
178 const InvalidatedSymbols &Escaped,
179 const CallEvent *Call,
180 PointerEscapeKind Kind) const;
181
182 ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
183 CheckerContext &C, ExplodedNode *Pred) const;
184
185 void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,
186 CheckerContext &C) const;
187
188 void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,
189 CheckerContext &C) const;
190
191 void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
192 const SourceRange *Range, const BugType &Type,
193 StringRef Msg) const;
194
195 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
196 const char *Sep) const override;
197 };
198 } // end anonymous namespace
199
REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap,SymbolRef,HandleState) const200 REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
201
202 static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym,
203 CheckerContext &Ctx) {
204 ProgramStateRef State = N->getState();
205 // When bug type is handle leak, exploded node N does not have state info for
206 // leaking handle. Get the predecessor of N instead.
207 if (!State->get<HStateMap>(Sym))
208 N = N->getFirstPred();
209
210 const ExplodedNode *Pred = N;
211 while (N) {
212 State = N->getState();
213 if (!State->get<HStateMap>(Sym)) {
214 const HandleState *HState = Pred->getState()->get<HStateMap>(Sym);
215 if (HState && (HState->isAllocated() || HState->maybeAllocated()))
216 return N;
217 }
218 Pred = N;
219 N = N->getFirstPred();
220 }
221 return nullptr;
222 }
223
224 /// Returns the symbols extracted from the argument or null if it cannot be
225 /// found.
getFuchsiaHandleSymbol(QualType QT,SVal Arg,ProgramStateRef State)226 static SymbolRef getFuchsiaHandleSymbol(QualType QT, SVal Arg,
227 ProgramStateRef State) {
228 int PtrToHandleLevel = 0;
229 while (QT->isAnyPointerType() || QT->isReferenceType()) {
230 ++PtrToHandleLevel;
231 QT = QT->getPointeeType();
232 }
233 if (const auto *HandleType = QT->getAs<TypedefType>()) {
234 if (HandleType->getDecl()->getName() != HandleTypeName)
235 return nullptr;
236 if (PtrToHandleLevel > 1) {
237 // Not supported yet.
238 return nullptr;
239 }
240
241 if (PtrToHandleLevel == 0) {
242 return Arg.getAsSymbol();
243 } else {
244 assert(PtrToHandleLevel == 1);
245 if (Optional<Loc> ArgLoc = Arg.getAs<Loc>())
246 return State->getSVal(*ArgLoc).getAsSymbol();
247 }
248 }
249 return nullptr;
250 }
251
checkPreCall(const CallEvent & Call,CheckerContext & C) const252 void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
253 CheckerContext &C) const {
254 ProgramStateRef State = C.getState();
255 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
256 if (!FuncDecl) {
257 // Unknown call, escape by value handles. They are not covered by
258 // PointerEscape callback.
259 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
260 if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol())
261 State = State->set<HStateMap>(Handle, HandleState::getEscaped());
262 }
263 C.addTransition(State);
264 return;
265 }
266
267 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
268 if (Arg >= FuncDecl->getNumParams())
269 break;
270 const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
271 SymbolRef Handle =
272 getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
273 if (!Handle)
274 continue;
275
276 // Handled in checkPostCall.
277 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
278 hasFuchsiaAttr<AcquireHandleAttr>(PVD))
279 continue;
280
281 const HandleState *HState = State->get<HStateMap>(Handle);
282 if (!HState || HState->isEscaped())
283 continue;
284
285 if (hasFuchsiaAttr<UseHandleAttr>(PVD) || PVD->getType()->isIntegerType()) {
286 if (HState->isReleased()) {
287 reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
288 return;
289 }
290 }
291 if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
292 PVD->getType()->isIntegerType()) {
293 // Working around integer by-value escapes.
294 State = State->set<HStateMap>(Handle, HandleState::getEscaped());
295 }
296 }
297 C.addTransition(State);
298 }
299
checkPostCall(const CallEvent & Call,CheckerContext & C) const300 void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
301 CheckerContext &C) const {
302 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
303 if (!FuncDecl)
304 return;
305
306 ProgramStateRef State = C.getState();
307
308 std::vector<std::function<std::string(BugReport & BR)>> Notes;
309 SymbolRef ResultSymbol = nullptr;
310 if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())
311 if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
312 ResultSymbol = Call.getReturnValue().getAsSymbol();
313
314 // Function returns an open handle.
315 if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
316 SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
317 State =
318 State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));
319 }
320
321 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
322 if (Arg >= FuncDecl->getNumParams())
323 break;
324 const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
325 SymbolRef Handle =
326 getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
327 if (!Handle)
328 continue;
329
330 const HandleState *HState = State->get<HStateMap>(Handle);
331 if (HState && HState->isEscaped())
332 continue;
333 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
334 if (HState && HState->isReleased()) {
335 reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
336 return;
337 } else {
338 Notes.push_back([Handle](BugReport &BR) {
339 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
340 if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
341 return "Handle released here.";
342 } else
343 return "";
344 });
345 State = State->set<HStateMap>(Handle, HandleState::getReleased());
346 }
347 } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
348 Notes.push_back([Handle](BugReport &BR) {
349 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
350 if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
351 return "Handle allocated here.";
352 } else
353 return "";
354 });
355 State = State->set<HStateMap>(
356 Handle, HandleState::getMaybeAllocated(ResultSymbol));
357 }
358 }
359 const NoteTag *T = nullptr;
360 if (!Notes.empty()) {
361 T = C.getNoteTag(
362 [this, Notes{std::move(Notes)}](BugReport &BR) -> std::string {
363 if (&BR.getBugType() != &UseAfterReleaseBugType &&
364 &BR.getBugType() != &LeakBugType &&
365 &BR.getBugType() != &DoubleReleaseBugType)
366 return "";
367 for (auto &Note : Notes) {
368 std::string Text = Note(BR);
369 if (!Text.empty())
370 return Text;
371 }
372 return "";
373 });
374 }
375 C.addTransition(State, T);
376 }
377
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const378 void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
379 CheckerContext &C) const {
380 ProgramStateRef State = C.getState();
381 SmallVector<SymbolRef, 2> LeakedSyms;
382 HStateMapTy TrackedHandles = State->get<HStateMap>();
383 for (auto &CurItem : TrackedHandles) {
384 if (!SymReaper.isDead(CurItem.first))
385 continue;
386 if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
387 LeakedSyms.push_back(CurItem.first);
388 State = State->remove<HStateMap>(CurItem.first);
389 }
390
391 ExplodedNode *N = C.getPredecessor();
392 if (!LeakedSyms.empty())
393 N = reportLeaks(LeakedSyms, C, N);
394
395 C.addTransition(State, N);
396 }
397
398 // Acquiring a handle is not always successful. In Fuchsia most functions
399 // return a status code that determines the status of the handle.
400 // When we split the path based on this status code we know that on one
401 // path we do have the handle and on the other path the acquire failed.
402 // This method helps avoiding false positive leak warnings on paths where
403 // the function failed.
404 // Moreover, when a handle is known to be zero (the invalid handle),
405 // we no longer can follow the symbol on the path, becaue the constant
406 // zero will be used instead of the symbol. We also do not need to release
407 // an invalid handle, so we remove the corresponding symbol from the state.
evalAssume(ProgramStateRef State,SVal Cond,bool Assumption) const408 ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
409 SVal Cond,
410 bool Assumption) const {
411 // TODO: add notes about successes/fails for APIs.
412 ConstraintManager &Cmr = State->getConstraintManager();
413 HStateMapTy TrackedHandles = State->get<HStateMap>();
414 for (auto &CurItem : TrackedHandles) {
415 ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first);
416 if (HandleVal.isConstrainedTrue()) {
417 // The handle is invalid. We can no longer follow the symbol on this path.
418 State = State->remove<HStateMap>(CurItem.first);
419 }
420 SymbolRef ErrorSym = CurItem.second.getErrorSym();
421 if (!ErrorSym)
422 continue;
423 ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);
424 if (ErrorVal.isConstrainedTrue()) {
425 // Allocation succeeded.
426 if (CurItem.second.maybeAllocated())
427 State = State->set<HStateMap>(
428 CurItem.first, HandleState::getAllocated(State, CurItem.second));
429 } else if (ErrorVal.isConstrainedFalse()) {
430 // Allocation failed.
431 if (CurItem.second.maybeAllocated())
432 State = State->remove<HStateMap>(CurItem.first);
433 }
434 }
435 return State;
436 }
437
checkPointerEscape(ProgramStateRef State,const InvalidatedSymbols & Escaped,const CallEvent * Call,PointerEscapeKind Kind) const438 ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(
439 ProgramStateRef State, const InvalidatedSymbols &Escaped,
440 const CallEvent *Call, PointerEscapeKind Kind) const {
441 const FunctionDecl *FuncDecl =
442 Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr;
443
444 llvm::DenseSet<SymbolRef> UnEscaped;
445 // Not all calls should escape our symbols.
446 if (FuncDecl &&
447 (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall ||
448 Kind == PSK_EscapeOutParameters)) {
449 for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
450 if (Arg >= FuncDecl->getNumParams())
451 break;
452 const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
453 SymbolRef Handle =
454 getFuchsiaHandleSymbol(PVD->getType(), Call->getArgSVal(Arg), State);
455 if (!Handle)
456 continue;
457 if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
458 hasFuchsiaAttr<ReleaseHandleAttr>(PVD))
459 UnEscaped.insert(Handle);
460 }
461 }
462
463 // For out params, we have to deal with derived symbols. See
464 // MacOSKeychainAPIChecker for details.
465 for (auto I : State->get<HStateMap>()) {
466 if (Escaped.count(I.first) && !UnEscaped.count(I.first))
467 State = State->set<HStateMap>(I.first, HandleState::getEscaped());
468 if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) {
469 auto ParentSym = SD->getParentSymbol();
470 if (Escaped.count(ParentSym))
471 State = State->set<HStateMap>(I.first, HandleState::getEscaped());
472 }
473 }
474
475 return State;
476 }
477
478 ExplodedNode *
reportLeaks(ArrayRef<SymbolRef> LeakedHandles,CheckerContext & C,ExplodedNode * Pred) const479 FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
480 CheckerContext &C, ExplodedNode *Pred) const {
481 ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);
482 for (SymbolRef LeakedHandle : LeakedHandles) {
483 reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType,
484 "Potential leak of handle");
485 }
486 return ErrNode;
487 }
488
reportDoubleRelease(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const489 void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
490 const SourceRange &Range,
491 CheckerContext &C) const {
492 ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
493 reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
494 "Releasing a previously released handle");
495 }
496
reportUseAfterFree(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const497 void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
498 const SourceRange &Range,
499 CheckerContext &C) const {
500 ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
501 reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,
502 "Using a previously released handle");
503 }
504
reportBug(SymbolRef Sym,ExplodedNode * ErrorNode,CheckerContext & C,const SourceRange * Range,const BugType & Type,StringRef Msg) const505 void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
506 CheckerContext &C,
507 const SourceRange *Range,
508 const BugType &Type, StringRef Msg) const {
509 if (!ErrorNode)
510 return;
511
512 std::unique_ptr<PathSensitiveBugReport> R;
513 if (Type.isSuppressOnSink()) {
514 const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);
515 if (AcquireNode) {
516 PathDiagnosticLocation LocUsedForUniqueing =
517 PathDiagnosticLocation::createBegin(
518 AcquireNode->getStmtForDiagnostics(), C.getSourceManager(),
519 AcquireNode->getLocationContext());
520
521 R = std::make_unique<PathSensitiveBugReport>(
522 Type, Msg, ErrorNode, LocUsedForUniqueing,
523 AcquireNode->getLocationContext()->getDecl());
524 }
525 }
526 if (!R)
527 R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
528 if (Range)
529 R->addRange(*Range);
530 R->markInteresting(Sym);
531 C.emitReport(std::move(R));
532 }
533
registerFuchsiaHandleChecker(CheckerManager & mgr)534 void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
535 mgr.registerChecker<FuchsiaHandleChecker>();
536 }
537
shouldRegisterFuchsiaHandleChecker(const LangOptions & LO)538 bool ento::shouldRegisterFuchsiaHandleChecker(const LangOptions &LO) {
539 return true;
540 }
541
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const542 void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
543 const char *NL, const char *Sep) const {
544
545 HStateMapTy StateMap = State->get<HStateMap>();
546
547 if (!StateMap.isEmpty()) {
548 Out << Sep << "FuchsiaHandleChecker :" << NL;
549 for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
550 ++I) {
551 I.getKey()->dumpToStream(Out);
552 Out << " : ";
553 I.getData().dump(Out);
554 Out << NL;
555 }
556 }
557 }
558