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#include <optional>
105
106using namespace clang;
107using namespace ento;
108
109namespace {
110
111static const StringRef HandleTypeName = "zx_handle_t";
112static const StringRef ErrorTypeName = "zx_status_t";
113
114class HandleState {
115private:
116 enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K;
117 SymbolRef ErrorSym;
118 HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
119
120public:
121 bool operator==(const HandleState &Other) const {
122 return K == Other.K && ErrorSym == Other.ErrorSym;
123 }
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; }
129
130 static HandleState getMaybeAllocated(SymbolRef ErrorSym) {
131 return HandleState(Kind::MaybeAllocated, ErrorSym);
132 }
133 static HandleState getAllocated(ProgramStateRef State, HandleState S) {
134 assert(S.maybeAllocated());
135 assert(State->getConstraintManager()
136 .isNull(State, S.getErrorSym())
137 .isConstrained());
138 return HandleState(Kind::Allocated, nullptr);
139 }
140 static HandleState getReleased() {
141 return HandleState(Kind::Released, nullptr);
142 }
143 static HandleState getEscaped() {
144 return HandleState(Kind::Escaped, nullptr);
145 }
146 static HandleState getUnowned() {
147 return HandleState(Kind::Unowned, nullptr);
148 }
149
150 SymbolRef getErrorSym() const { return ErrorSym; }
151
152 void Profile(llvm::FoldingSetNodeID &ID) const {
153 ID.AddInteger(I: static_cast<int>(K));
154 ID.AddPointer(Ptr: ErrorSym);
155 }
156
157 LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
158 switch (K) {
159#define CASE(ID) \
160 case ID: \
161 OS << #ID; \
162 break;
163 CASE(Kind::MaybeAllocated)
164 CASE(Kind::Allocated)
165 CASE(Kind::Released)
166 CASE(Kind::Escaped)
167 CASE(Kind::Unowned)
168 }
169 if (ErrorSym) {
170 OS << " ErrorSym: ";
171 ErrorSym->dumpToStream(os&: OS);
172 }
173 }
174
175 LLVM_DUMP_METHOD void dump() const { dump(OS&: llvm::errs()); }
176};
177
178template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
179 return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
180}
181
182template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) {
183 return D->hasAttr<Attr>() &&
184 D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned";
185}
186
187class 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"};
198
199public:
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;
209
210 ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
211 CheckerContext &C, ExplodedNode *Pred) const;
212
213 void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,
214 CheckerContext &C) const;
215
216 void reportUnownedRelease(SymbolRef HandleSym, const SourceRange &Range,
217 CheckerContext &C) const;
218
219 void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,
220 CheckerContext &C) const;
221
222 void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
223 const SourceRange *Range, const BugType &Type,
224 StringRef Msg) const;
225
226 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
227 const char *Sep) const override;
228};
229} // end anonymous namespace
230
231REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
232
233static 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>(key: Sym))
239 N = N->getFirstPred();
240
241 const ExplodedNode *Pred = N;
242 while (N) {
243 State = N->getState();
244 if (!State->get<HStateMap>(key: Sym)) {
245 const HandleState *HState = Pred->getState()->get<HStateMap>(key: Sym);
246 if (HState && (HState->isAllocated() || HState->maybeAllocated()))
247 return N;
248 }
249 Pred = N;
250 N = N->getFirstPred();
251 }
252 return nullptr;
253}
254
255namespace {
256class FuchsiaHandleSymbolVisitor final : public SymbolVisitor {
257public:
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(Elt: S);
262 return true;
263 }
264
265 SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; }
266
267private:
268 SmallVector<SymbolRef, 1024> Symbols;
269};
270} // end anonymous namespace
271
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.
274static SmallVector<SymbolRef, 1024>
275getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) {
276 int PtrToHandleLevel = 0;
277 while (QT->isAnyPointerType() || QT->isReferenceType()) {
278 ++PtrToHandleLevel;
279 QT = QT->getPointeeType();
280 }
281 if (QT->isStructureType()) {
282 // If we see a structure, see if there is any handle referenced by the
283 // structure.
284 FuchsiaHandleSymbolVisitor Visitor;
285 State->scanReachableSymbols(val: Arg, visitor&: Visitor);
286 return Visitor.GetSymbols();
287 }
288 if (const auto *HandleType = QT->getAs<TypedefType>()) {
289 if (HandleType->getDecl()->getName() != HandleTypeName)
290 return {};
291 if (PtrToHandleLevel > 1)
292 // Not supported yet.
293 return {};
294
295 if (PtrToHandleLevel == 0) {
296 SymbolRef Sym = Arg.getAsSymbol();
297 if (Sym) {
298 return {Sym};
299 } else {
300 return {};
301 }
302 } else {
303 assert(PtrToHandleLevel == 1);
304 if (std::optional<Loc> ArgLoc = Arg.getAs<Loc>()) {
305 SymbolRef Sym = State->getSVal(LV: *ArgLoc).getAsSymbol();
306 if (Sym) {
307 return {Sym};
308 } else {
309 return {};
310 }
311 }
312 }
313 }
314 return {};
315}
316
317void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
318 CheckerContext &C) const {
319 ProgramStateRef State = C.getState();
320 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Val: Call.getDecl());
321 if (!FuncDecl) {
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(Index: Arg).getAsSymbol())
326 State = State->set<HStateMap>(K: Handle, E: HandleState::getEscaped());
327 }
328 C.addTransition(State);
329 return;
330 }
331
332 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
333 if (Arg >= FuncDecl->getNumParams())
334 break;
335 const ParmVarDecl *PVD = FuncDecl->getParamDecl(i: Arg);
336 SmallVector<SymbolRef, 1024> Handles =
337 getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Index: Arg), State);
338
339 // Handled in checkPostCall.
340 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
341 hasFuchsiaAttr<AcquireHandleAttr>(PVD))
342 continue;
343
344 for (SymbolRef Handle : Handles) {
345 const HandleState *HState = State->get<HStateMap>(Handle);
346 if (!HState || HState->isEscaped())
347 continue;
348
349 if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
350 PVD->getType()->isIntegerType()) {
351 if (HState->isReleased()) {
352 reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
353 return;
354 }
355 }
356 }
357 }
358 C.addTransition(State);
359}
360
361void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
362 CheckerContext &C) const {
363 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Val: Call.getDecl());
364 if (!FuncDecl)
365 return;
366
367 // If we analyzed the function body, then ignore the annotations.
368 if (C.wasInlined)
369 return;
370
371 ProgramStateRef State = C.getState();
372
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();
378
379 // Function returns an open handle.
380 if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
381 SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
382 Notes.push_back(x: [RetSym, FuncDecl](BugReport &BR) -> std::string {
383 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
384 if (PathBR->getInterestingnessKind(sym: RetSym)) {
385 std::string SBuf;
386 llvm::raw_string_ostream OS(SBuf);
387 OS << "Function '" << FuncDecl->getDeclName()
388 << "' returns an open handle";
389 return SBuf;
390 } else
391 return "";
392 });
393 State =
394 State->set<HStateMap>(K: RetSym, E: HandleState::getMaybeAllocated(ErrorSym: nullptr));
395 } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) {
396 // Function returns an unowned handle
397 SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
398 Notes.push_back(x: [RetSym, FuncDecl](BugReport &BR) -> std::string {
399 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
400 if (PathBR->getInterestingnessKind(sym: RetSym)) {
401 std::string SBuf;
402 llvm::raw_string_ostream OS(SBuf);
403 OS << "Function '" << FuncDecl->getDeclName()
404 << "' returns an unowned handle";
405 return SBuf;
406 } else
407 return "";
408 });
409 State = State->set<HStateMap>(K: RetSym, E: HandleState::getUnowned());
410 }
411
412 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
413 if (Arg >= FuncDecl->getNumParams())
414 break;
415 const ParmVarDecl *PVD = FuncDecl->getParamDecl(i: Arg);
416 unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1;
417 SmallVector<SymbolRef, 1024> Handles =
418 getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Index: Arg), State);
419
420 for (SymbolRef Handle : Handles) {
421 const HandleState *HState = State->get<HStateMap>(Handle);
422 if (HState && HState->isEscaped())
423 continue;
424 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
425 if (HState && HState->isReleased()) {
426 reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
427 return;
428 } else if (HState && HState->isUnowned()) {
429 reportUnownedRelease(Handle, Call.getArgSourceRange(Arg), C);
430 return;
431 } else {
432 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
433 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
434 if (PathBR->getInterestingnessKind(Handle)) {
435 std::string SBuf;
436 llvm::raw_string_ostream OS(SBuf);
437 OS << "Handle released through " << ParamDiagIdx
438 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
439 return SBuf;
440 } else
441 return "";
442 });
443 State = State->set<HStateMap>(Handle, HandleState::getReleased());
444 }
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)) {
449 std::string SBuf;
450 llvm::raw_string_ostream OS(SBuf);
451 OS << "Handle allocated through " << ParamDiagIdx
452 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
453 return SBuf;
454 } else
455 return "";
456 });
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)) {
463 std::string SBuf;
464 llvm::raw_string_ostream OS(SBuf);
465 OS << "Unowned handle allocated through " << ParamDiagIdx
466 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
467 return SBuf;
468 } else
469 return "";
470 });
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
478 // is escaped.
479 State = State->set<HStateMap>(Handle, HandleState::getEscaped());
480 }
481 }
482 }
483 const NoteTag *T = nullptr;
484 if (!Notes.empty()) {
485 T = C.getNoteTag(Cb: [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)
491 return "";
492 for (auto &Note : Notes) {
493 std::string Text = Note(BR);
494 if (!Text.empty())
495 return Text;
496 }
497 return "";
498 });
499 }
500 C.addTransition(State, Tag: T);
501}
502
503void 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(sym: CurItem.first) ||
515 (ErrorSym && !SymReaper.isDead(sym: ErrorSym)))
516 continue;
517 if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
518 LeakedSyms.push_back(Elt: CurItem.first);
519 State = State->remove<HStateMap>(K: CurItem.first);
520 }
521
522 ExplodedNode *N = C.getPredecessor();
523 if (!LeakedSyms.empty())
524 N = reportLeaks(LeakedHandles: LeakedSyms, C, Pred: N);
525
526 C.addTransition(State, Pred: N);
527}
528
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.
539ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
540 SVal Cond,
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, Sym: 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>(K: CurItem.first);
550 }
551 SymbolRef ErrorSym = CurItem.second.getErrorSym();
552 if (!ErrorSym)
553 continue;
554 ConditionTruthVal ErrorVal = Cmr.isNull(State, Sym: ErrorSym);
555 if (ErrorVal.isConstrainedTrue()) {
556 // Allocation succeeded.
557 if (CurItem.second.maybeAllocated())
558 State = State->set<HStateMap>(
559 K: CurItem.first, E: HandleState::getAllocated(State, S: CurItem.second));
560 } else if (ErrorVal.isConstrainedFalse()) {
561 // Allocation failed.
562 if (CurItem.second.maybeAllocated())
563 State = State->remove<HStateMap>(K: CurItem.first);
564 }
565 }
566 return State;
567}
568
569ProgramStateRef 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>(Val: Call->getDecl()) : nullptr;
574
575 llvm::DenseSet<SymbolRef> UnEscaped;
576 // Not all calls should escape our symbols.
577 if (FuncDecl &&
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())
582 break;
583 const ParmVarDecl *PVD = FuncDecl->getParamDecl(i: Arg);
584 SmallVector<SymbolRef, 1024> Handles =
585 getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Index: Arg), State);
586 for (SymbolRef Handle : Handles) {
587 if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
588 hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
589 UnEscaped.insert(Handle);
590 }
591 }
592 }
593 }
594
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(V: I.first) && !UnEscaped.count(V: I.first))
599 State = State->set<HStateMap>(K: I.first, E: HandleState::getEscaped());
600 if (const auto *SD = dyn_cast<SymbolDerived>(Val: I.first)) {
601 auto ParentSym = SD->getParentSymbol();
602 if (Escaped.count(V: ParentSym))
603 State = State->set<HStateMap>(K: I.first, E: HandleState::getEscaped());
604 }
605 }
606
607 return State;
608}
609
610ExplodedNode *
611FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
612 CheckerContext &C, ExplodedNode *Pred) const {
613 ExplodedNode *ErrNode = C.generateNonFatalErrorNode(State: C.getState(), Pred);
614 for (SymbolRef LeakedHandle : LeakedHandles) {
615 reportBug(Sym: LeakedHandle, ErrorNode: ErrNode, C, Range: nullptr, Type: LeakBugType,
616 Msg: "Potential leak of handle");
617 }
618 return ErrNode;
619}
620
621void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
622 const SourceRange &Range,
623 CheckerContext &C) const {
624 ExplodedNode *ErrNode = C.generateErrorNode(State: C.getState());
625 reportBug(Sym: HandleSym, ErrorNode: ErrNode, C, Range: &Range, Type: DoubleReleaseBugType,
626 Msg: "Releasing a previously released handle");
627}
628
629void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym,
630 const SourceRange &Range,
631 CheckerContext &C) const {
632 ExplodedNode *ErrNode = C.generateErrorNode(State: C.getState());
633 reportBug(Sym: HandleSym, ErrorNode: ErrNode, C, Range: &Range, Type: ReleaseUnownedBugType,
634 Msg: "Releasing an unowned handle");
635}
636
637void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
638 const SourceRange &Range,
639 CheckerContext &C) const {
640 ExplodedNode *ErrNode = C.generateErrorNode(State: C.getState());
641 reportBug(Sym: HandleSym, ErrorNode: ErrNode, C, Range: &Range, Type: UseAfterReleaseBugType,
642 Msg: "Using a previously released handle");
643}
644
645void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
646 CheckerContext &C,
647 const SourceRange *Range,
648 const BugType &Type, StringRef Msg) const {
649 if (!ErrorNode)
650 return;
651
652 std::unique_ptr<PathSensitiveBugReport> R;
653 if (Type.isSuppressOnSink()) {
654 const ExplodedNode *AcquireNode = getAcquireSite(N: ErrorNode, Sym, Ctx&: C);
655 if (AcquireNode) {
656 const Stmt *S = AcquireNode->getStmtForDiagnostics();
657 assert(S && "Statement cannot be null.");
658 PathDiagnosticLocation LocUsedForUniqueing =
659 PathDiagnosticLocation::createBegin(
660 S, SM: C.getSourceManager(), LAC: AcquireNode->getLocationContext());
661
662 R = std::make_unique<PathSensitiveBugReport>(
663 args: Type, args&: Msg, args&: ErrorNode, args&: LocUsedForUniqueing,
664 args: AcquireNode->getLocationContext()->getDecl());
665 }
666 }
667 if (!R)
668 R = std::make_unique<PathSensitiveBugReport>(args: Type, args&: Msg, args&: ErrorNode);
669 if (Range)
670 R->addRange(R: *Range);
671 R->markInteresting(sym: Sym);
672 C.emitReport(R: std::move(R));
673}
674
675void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
676 mgr.registerChecker<FuchsiaHandleChecker>();
677}
678
679bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {
680 return true;
681}
682
683void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
684 const char *NL, const char *Sep) const {
685
686 HStateMapTy StateMap = State->get<HStateMap>();
687
688 if (!StateMap.isEmpty()) {
689 Out << Sep << "FuchsiaHandleChecker :" << NL;
690 for (const auto &[Sym, HandleState] : StateMap) {
691 Sym->dumpToStream(os&: Out);
692 Out << " : ";
693 HandleState.dump(OS&: Out);
694 Out << NL;
695 }
696 }
697}
698

source code of clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp