clang 19.0.0git
FuchsiaHandleChecker.cpp
Go to the documentation of this file.
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"
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(static_cast<int>(K));
154 ID.AddPointer(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);
172 }
173 }
174
175 LLVM_DUMP_METHOD void dump() const { dump(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
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();
240
241 const ExplodedNode *Pred = N;
242 while (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()))
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(S);
262 return true;
263 }
264
265 SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; }
266
267private:
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.
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(Arg, 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(*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>(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(Arg).getAsSymbol())
326 State = State->set<HStateMap>(Handle, 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(Arg);
337 getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(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>(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([RetSym, FuncDecl](BugReport &BR) -> std::string {
383 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
384 if (PathBR->getInterestingnessKind(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>(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)) {
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>(RetSym, 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(Arg);
416 unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1;
418 getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(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([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, 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(CurItem.first) ||
515 (ErrorSym && !SymReaper.isDead(ErrorSym)))
516 continue;
517 if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
518 LeakedSyms.push_back(CurItem.first);
519 State = State->remove<HStateMap>(CurItem.first);
520 }
521
522 ExplodedNode *N = C.getPredecessor();
523 if (!LeakedSyms.empty())
524 N = reportLeaks(LeakedSyms, C, N);
525
526 C.addTransition(State, 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, 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);
550 }
551 SymbolRef ErrorSym = CurItem.second.getErrorSym();
552 if (!ErrorSym)
553 continue;
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);
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>(Call->getDecl()) : nullptr;
574
576 // Not all calls should escape our symbols.
577 if (FuncDecl &&
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(Arg);
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);
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(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());
604 }
605 }
606
607 return State;
608}
609
611FuchsiaHandleChecker::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");
617 }
618 return ErrNode;
619}
620
621void 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");
627}
628
629void 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");
635}
636
637void 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");
643}
644
645void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
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(ErrorNode, Sym, C);
655 if (AcquireNode) {
656 const Stmt *S = AcquireNode->getStmtForDiagnostics();
657 assert(S && "Statement cannot be null.");
658 PathDiagnosticLocation LocUsedForUniqueing =
660 S, C.getSourceManager(), AcquireNode->getLocationContext());
661
662 R = std::make_unique<PathSensitiveBugReport>(
663 Type, Msg, ErrorNode, LocUsedForUniqueing,
664 AcquireNode->getLocationContext()->getDecl());
665 }
666 }
667 if (!R)
668 R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
669 if (Range)
670 R->addRange(*Range);
671 R->markInteresting(Sym);
672 C.emitReport(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(Out);
692 Out << " : ";
693 HandleState.dump(Out);
694 Out << NL;
695 }
696 }
697}
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
StringRef Text
Definition: Format.cpp:2973
#define CASE(ID)
static const ExplodedNode * getAcquireSite(const ExplodedNode *N, SymbolRef Sym, CheckerContext &Ctx)
static SmallVector< SymbolRef, 1024 > getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State)
Returns the symbols extracted from the argument or empty vector if it cannot be found.
static bool isReleased(SymbolRef Sym, CheckerContext &C)
Check if the memory associated with this symbol was released.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
C Language Family Type Representation.
Attr - This represents one attribute.
Definition: Attr.h:42
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:85
T * getAttr() const
Definition: DeclBase.h:578
bool hasAttr() const
Definition: DeclBase.h:582
Represents a function declaration or definition.
Definition: Decl.h:1971
const ParmVarDecl * getParamDecl(unsigned i) const
Definition: Decl.h:2707
QualType getReturnType() const
Definition: Decl.h:2755
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition: Decl.cpp:3692
const Decl * getDecl() const
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:315
Represents a parameter to a function.
Definition: Decl.h:1761
unsigned getFunctionScopeIndex() const
Returns the index of this parameter in its prototype or method scope.
Definition: Decl.h:1821
A (possibly-)qualified type.
Definition: Type.h:738
A trivial tuple used to represent a source range.
Stmt - This represents one statement.
Definition: Stmt.h:84
The base class of the type hierarchy.
Definition: Type.h:1607
bool isStructureType() const
Definition: Type.cpp:628
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:7735
bool isReferenceType() const
Definition: Type.h:7414
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition: Type.cpp:694
bool isAnyPointerType() const
Definition: Type.h:7406
const T * getAs() const
Member-template getAs<specific type>'.
Definition: Type.h:7913
QualType getType() const
Definition: Decl.h:717
This class provides an interface through which checkers can create individual bug reports.
Definition: BugReporter.h:119
const BugType & getBugType() const
Definition: BugReporter.h:149
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
virtual void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const
See CheckerManager::runCheckersForPrintState.
Definition: Checker.h:496
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
bool isConstrainedFalse() const
Return true if the constraint is perfectly constrained to 'false'.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
ConditionTruthVal isNull(ProgramStateRef State, SymbolRef Sym)
Convenience method to query the state to see if a symbol is null or not null, or if neither assumptio...
const ProgramStateRef & getState() const
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
const LocationContext * getLocationContext() const
ExplodedNode * getFirstPred()
The tag upon which the TagVisitor reacts.
Definition: BugReporter.h:779
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
A Range represents the closed range [from, to].
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:55
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:104
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition: SVals.h:86
Symbolic value.
Definition: SymExpr.h:30
virtual void dumpToStream(raw_ostream &os) const
Definition: SymExpr.h:61
A class responsible for cleaning up unused symbols.
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
virtual bool VisitSymbol(SymbolRef sym)=0
A visitor method invoked by ProgramStateManager::scanReachableSymbols.
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
@ PSK_DirectEscapeOnCall
The pointer has been passed to a function call directly.
@ PSK_IndirectEscapeOnCall
The pointer has been passed to a function indirectly.
@ PSK_EscapeOutParameters
Escape for a new symbol that was generated into a region that the analyzer cannot follow during a con...
The JSON file list parser is used to communicate input to InstallAPI.
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
Definition: CallGraph.h:207
const FunctionProtoType * T
@ Other
Other implicit parameter.