Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
35269 views
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
106
using namespace clang;
107
using namespace ento;
108
109
namespace {
110
111
static const StringRef HandleTypeName = "zx_handle_t";
112
static const StringRef ErrorTypeName = "zx_status_t";
113
114
class HandleState {
115
private:
116
enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K;
117
SymbolRef ErrorSym;
118
HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
119
120
public:
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
178
template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
179
return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
180
}
181
182
template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) {
183
return D->hasAttr<Attr>() &&
184
D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned";
185
}
186
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"};
198
199
public:
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
231
REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
232
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();
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
255
namespace {
256
class FuchsiaHandleSymbolVisitor final : public SymbolVisitor {
257
public:
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
267
private:
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.
274
static SmallVector<SymbolRef, 1024>
275
getFuchsiaHandleSymbols(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(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
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());
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);
336
SmallVector<SymbolRef, 1024> Handles =
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
361
void 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;
417
SmallVector<SymbolRef, 1024> Handles =
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
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)))
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.
539
ProgramStateRef 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
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;
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(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);
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
610
ExplodedNode *
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");
617
}
618
return ErrNode;
619
}
620
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");
627
}
628
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");
635
}
636
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");
643
}
644
645
void 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(ErrorNode, Sym, 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, 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
675
void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
676
mgr.registerChecker<FuchsiaHandleChecker>();
677
}
678
679
bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {
680
return true;
681
}
682
683
void 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
}
698
699