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
105 using namespace clang;
106 using namespace ento;
107
108 namespace {
109
110 static const StringRef HandleTypeName = "zx_handle_t";
111 static const StringRef ErrorTypeName = "zx_status_t";
112
113 class HandleState {
114 private:
115 enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K;
116 SymbolRef ErrorSym;
HandleState(Kind K,SymbolRef ErrorSym)117 HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
118
119 public:
operator ==(const HandleState & Other) const120 bool operator==(const HandleState &Other) const {
121 return K == Other.K && ErrorSym == Other.ErrorSym;
122 }
isAllocated() const123 bool isAllocated() const { return K == Kind::Allocated; }
maybeAllocated() const124 bool maybeAllocated() const { return K == Kind::MaybeAllocated; }
isReleased() const125 bool isReleased() const { return K == Kind::Released; }
isEscaped() const126 bool isEscaped() const { return K == Kind::Escaped; }
isUnowned() const127 bool isUnowned() const { return K == Kind::Unowned; }
128
getMaybeAllocated(SymbolRef ErrorSym)129 static HandleState getMaybeAllocated(SymbolRef ErrorSym) {
130 return HandleState(Kind::MaybeAllocated, ErrorSym);
131 }
getAllocated(ProgramStateRef State,HandleState S)132 static HandleState getAllocated(ProgramStateRef State, HandleState S) {
133 assert(S.maybeAllocated());
134 assert(State->getConstraintManager()
135 .isNull(State, S.getErrorSym())
136 .isConstrained());
137 return HandleState(Kind::Allocated, nullptr);
138 }
getReleased()139 static HandleState getReleased() {
140 return HandleState(Kind::Released, nullptr);
141 }
getEscaped()142 static HandleState getEscaped() {
143 return HandleState(Kind::Escaped, nullptr);
144 }
getUnowned()145 static HandleState getUnowned() {
146 return HandleState(Kind::Unowned, nullptr);
147 }
148
getErrorSym() const149 SymbolRef getErrorSym() const { return ErrorSym; }
150
Profile(llvm::FoldingSetNodeID & ID) const151 void Profile(llvm::FoldingSetNodeID &ID) const {
152 ID.AddInteger(static_cast<int>(K));
153 ID.AddPointer(ErrorSym);
154 }
155
dump(raw_ostream & OS) const156 LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
157 switch (K) {
158 #define CASE(ID) \
159 case ID: \
160 OS << #ID; \
161 break;
162 CASE(Kind::MaybeAllocated)
163 CASE(Kind::Allocated)
164 CASE(Kind::Released)
165 CASE(Kind::Escaped)
166 CASE(Kind::Unowned)
167 }
168 if (ErrorSym) {
169 OS << " ErrorSym: ";
170 ErrorSym->dumpToStream(OS);
171 }
172 }
173
dump() const174 LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
175 };
176
hasFuchsiaAttr(const Decl * D)177 template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
178 return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
179 }
180
hasFuchsiaUnownedAttr(const Decl * D)181 template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) {
182 return D->hasAttr<Attr>() &&
183 D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned";
184 }
185
186 class FuchsiaHandleChecker
187 : public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
188 check::PointerEscape, eval::Assume> {
189 BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error",
190 /*SuppressOnSink=*/true};
191 BugType DoubleReleaseBugType{this, "Fuchsia handle double release",
192 "Fuchsia Handle Error"};
193 BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release",
194 "Fuchsia Handle Error"};
195 BugType ReleaseUnownedBugType{
196 this, "Fuchsia handle release of unowned handle", "Fuchsia Handle Error"};
197
198 public:
199 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
200 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
201 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
202 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
203 bool Assumption) const;
204 ProgramStateRef checkPointerEscape(ProgramStateRef State,
205 const InvalidatedSymbols &Escaped,
206 const CallEvent *Call,
207 PointerEscapeKind Kind) const;
208
209 ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
210 CheckerContext &C, ExplodedNode *Pred) const;
211
212 void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,
213 CheckerContext &C) const;
214
215 void reportUnownedRelease(SymbolRef HandleSym, const SourceRange &Range,
216 CheckerContext &C) const;
217
218 void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,
219 CheckerContext &C) const;
220
221 void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
222 const SourceRange *Range, const BugType &Type,
223 StringRef Msg) const;
224
225 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
226 const char *Sep) const override;
227 };
228 } // end anonymous namespace
229
REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap,SymbolRef,HandleState) const230 REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
231
232 static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym,
233 CheckerContext &Ctx) {
234 ProgramStateRef State = N->getState();
235 // When bug type is handle leak, exploded node N does not have state info for
236 // leaking handle. Get the predecessor of N instead.
237 if (!State->get<HStateMap>(Sym))
238 N = N->getFirstPred();
239
240 const ExplodedNode *Pred = N;
241 while (N) {
242 State = N->getState();
243 if (!State->get<HStateMap>(Sym)) {
244 const HandleState *HState = Pred->getState()->get<HStateMap>(Sym);
245 if (HState && (HState->isAllocated() || HState->maybeAllocated()))
246 return N;
247 }
248 Pred = N;
249 N = N->getFirstPred();
250 }
251 return nullptr;
252 }
253
254 namespace {
255 class FuchsiaHandleSymbolVisitor final : public SymbolVisitor {
256 public:
VisitSymbol(SymbolRef S)257 bool VisitSymbol(SymbolRef S) override {
258 if (const auto *HandleType = S->getType()->getAs<TypedefType>())
259 if (HandleType->getDecl()->getName() == HandleTypeName)
260 Symbols.push_back(S);
261 return true;
262 }
263
GetSymbols()264 SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; }
265
266 private:
267 SmallVector<SymbolRef, 1024> Symbols;
268 };
269 } // end anonymous namespace
270
271 /// Returns the symbols extracted from the argument or empty vector if it cannot
272 /// be found. It is unlikely to have over 1024 symbols in one argument.
273 static SmallVector<SymbolRef, 1024>
getFuchsiaHandleSymbols(QualType QT,SVal Arg,ProgramStateRef State)274 getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) {
275 int PtrToHandleLevel = 0;
276 while (QT->isAnyPointerType() || QT->isReferenceType()) {
277 ++PtrToHandleLevel;
278 QT = QT->getPointeeType();
279 }
280 if (QT->isStructureType()) {
281 // If we see a structure, see if there is any handle referenced by the
282 // structure.
283 FuchsiaHandleSymbolVisitor Visitor;
284 State->scanReachableSymbols(Arg, Visitor);
285 return Visitor.GetSymbols();
286 }
287 if (const auto *HandleType = QT->getAs<TypedefType>()) {
288 if (HandleType->getDecl()->getName() != HandleTypeName)
289 return {};
290 if (PtrToHandleLevel > 1)
291 // Not supported yet.
292 return {};
293
294 if (PtrToHandleLevel == 0) {
295 SymbolRef Sym = Arg.getAsSymbol();
296 if (Sym) {
297 return {Sym};
298 } else {
299 return {};
300 }
301 } else {
302 assert(PtrToHandleLevel == 1);
303 if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) {
304 SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol();
305 if (Sym) {
306 return {Sym};
307 } else {
308 return {};
309 }
310 }
311 }
312 }
313 return {};
314 }
315
checkPreCall(const CallEvent & Call,CheckerContext & C) const316 void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
317 CheckerContext &C) const {
318 ProgramStateRef State = C.getState();
319 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
320 if (!FuncDecl) {
321 // Unknown call, escape by value handles. They are not covered by
322 // PointerEscape callback.
323 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
324 if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol())
325 State = State->set<HStateMap>(Handle, HandleState::getEscaped());
326 }
327 C.addTransition(State);
328 return;
329 }
330
331 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
332 if (Arg >= FuncDecl->getNumParams())
333 break;
334 const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
335 SmallVector<SymbolRef, 1024> Handles =
336 getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
337
338 // Handled in checkPostCall.
339 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
340 hasFuchsiaAttr<AcquireHandleAttr>(PVD))
341 continue;
342
343 for (SymbolRef Handle : Handles) {
344 const HandleState *HState = State->get<HStateMap>(Handle);
345 if (!HState || HState->isEscaped())
346 continue;
347
348 if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
349 PVD->getType()->isIntegerType()) {
350 if (HState->isReleased()) {
351 reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
352 return;
353 }
354 }
355 }
356 }
357 C.addTransition(State);
358 }
359
checkPostCall(const CallEvent & Call,CheckerContext & C) const360 void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
361 CheckerContext &C) const {
362 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
363 if (!FuncDecl)
364 return;
365
366 // If we analyzed the function body, then ignore the annotations.
367 if (C.wasInlined)
368 return;
369
370 ProgramStateRef State = C.getState();
371
372 std::vector<std::function<std::string(BugReport & BR)>> Notes;
373 SymbolRef ResultSymbol = nullptr;
374 if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())
375 if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
376 ResultSymbol = Call.getReturnValue().getAsSymbol();
377
378 // Function returns an open handle.
379 if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
380 SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
381 Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
382 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
383 if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
384 std::string SBuf;
385 llvm::raw_string_ostream OS(SBuf);
386 OS << "Function '" << FuncDecl->getDeclName()
387 << "' returns an open handle";
388 return SBuf;
389 } else
390 return "";
391 });
392 State =
393 State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));
394 } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) {
395 // Function returns an unowned handle
396 SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
397 Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
398 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
399 if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
400 std::string SBuf;
401 llvm::raw_string_ostream OS(SBuf);
402 OS << "Function '" << FuncDecl->getDeclName()
403 << "' returns an unowned handle";
404 return SBuf;
405 } else
406 return "";
407 });
408 State = State->set<HStateMap>(RetSym, HandleState::getUnowned());
409 }
410
411 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
412 if (Arg >= FuncDecl->getNumParams())
413 break;
414 const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
415 unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1;
416 SmallVector<SymbolRef, 1024> Handles =
417 getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
418
419 for (SymbolRef Handle : Handles) {
420 const HandleState *HState = State->get<HStateMap>(Handle);
421 if (HState && HState->isEscaped())
422 continue;
423 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
424 if (HState && HState->isReleased()) {
425 reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
426 return;
427 } else if (HState && HState->isUnowned()) {
428 reportUnownedRelease(Handle, Call.getArgSourceRange(Arg), C);
429 return;
430 } else {
431 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
432 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
433 if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
434 std::string SBuf;
435 llvm::raw_string_ostream OS(SBuf);
436 OS << "Handle released through " << ParamDiagIdx
437 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
438 return SBuf;
439 } else
440 return "";
441 });
442 State = State->set<HStateMap>(Handle, HandleState::getReleased());
443 }
444 } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
445 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
446 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
447 if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
448 std::string SBuf;
449 llvm::raw_string_ostream OS(SBuf);
450 OS << "Handle allocated through " << ParamDiagIdx
451 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
452 return SBuf;
453 } else
454 return "";
455 });
456 State = State->set<HStateMap>(
457 Handle, HandleState::getMaybeAllocated(ResultSymbol));
458 } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) {
459 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
460 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
461 if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
462 std::string SBuf;
463 llvm::raw_string_ostream OS(SBuf);
464 OS << "Unowned handle allocated through " << ParamDiagIdx
465 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
466 return SBuf;
467 } else
468 return "";
469 });
470 State = State->set<HStateMap>(Handle, HandleState::getUnowned());
471 } else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
472 PVD->getType()->isIntegerType()) {
473 // Working around integer by-value escapes.
474 // The by-value escape would not be captured in checkPointerEscape.
475 // If the function was not analyzed (otherwise wasInlined should be
476 // true) and there is no annotation on the handle, we assume the handle
477 // is escaped.
478 State = State->set<HStateMap>(Handle, HandleState::getEscaped());
479 }
480 }
481 }
482 const NoteTag *T = nullptr;
483 if (!Notes.empty()) {
484 T = C.getNoteTag([this, Notes{std::move(Notes)}](
485 PathSensitiveBugReport &BR) -> std::string {
486 if (&BR.getBugType() != &UseAfterReleaseBugType &&
487 &BR.getBugType() != &LeakBugType &&
488 &BR.getBugType() != &DoubleReleaseBugType &&
489 &BR.getBugType() != &ReleaseUnownedBugType)
490 return "";
491 for (auto &Note : Notes) {
492 std::string Text = Note(BR);
493 if (!Text.empty())
494 return Text;
495 }
496 return "";
497 });
498 }
499 C.addTransition(State, T);
500 }
501
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const502 void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
503 CheckerContext &C) const {
504 ProgramStateRef State = C.getState();
505 SmallVector<SymbolRef, 2> LeakedSyms;
506 HStateMapTy TrackedHandles = State->get<HStateMap>();
507 for (auto &CurItem : TrackedHandles) {
508 SymbolRef ErrorSym = CurItem.second.getErrorSym();
509 // Keeping zombie handle symbols. In case the error symbol is dying later
510 // than the handle symbol we might produce spurious leak warnings (in case
511 // we find out later from the status code that the handle allocation failed
512 // in the first place).
513 if (!SymReaper.isDead(CurItem.first) ||
514 (ErrorSym && !SymReaper.isDead(ErrorSym)))
515 continue;
516 if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
517 LeakedSyms.push_back(CurItem.first);
518 State = State->remove<HStateMap>(CurItem.first);
519 }
520
521 ExplodedNode *N = C.getPredecessor();
522 if (!LeakedSyms.empty())
523 N = reportLeaks(LeakedSyms, C, N);
524
525 C.addTransition(State, N);
526 }
527
528 // Acquiring a handle is not always successful. In Fuchsia most functions
529 // return a status code that determines the status of the handle.
530 // When we split the path based on this status code we know that on one
531 // path we do have the handle and on the other path the acquire failed.
532 // This method helps avoiding false positive leak warnings on paths where
533 // the function failed.
534 // Moreover, when a handle is known to be zero (the invalid handle),
535 // we no longer can follow the symbol on the path, becaue the constant
536 // zero will be used instead of the symbol. We also do not need to release
537 // an invalid handle, so we remove the corresponding symbol from the state.
evalAssume(ProgramStateRef State,SVal Cond,bool Assumption) const538 ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
539 SVal Cond,
540 bool Assumption) const {
541 // TODO: add notes about successes/fails for APIs.
542 ConstraintManager &Cmr = State->getConstraintManager();
543 HStateMapTy TrackedHandles = State->get<HStateMap>();
544 for (auto &CurItem : TrackedHandles) {
545 ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first);
546 if (HandleVal.isConstrainedTrue()) {
547 // The handle is invalid. We can no longer follow the symbol on this path.
548 State = State->remove<HStateMap>(CurItem.first);
549 }
550 SymbolRef ErrorSym = CurItem.second.getErrorSym();
551 if (!ErrorSym)
552 continue;
553 ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);
554 if (ErrorVal.isConstrainedTrue()) {
555 // Allocation succeeded.
556 if (CurItem.second.maybeAllocated())
557 State = State->set<HStateMap>(
558 CurItem.first, HandleState::getAllocated(State, CurItem.second));
559 } else if (ErrorVal.isConstrainedFalse()) {
560 // Allocation failed.
561 if (CurItem.second.maybeAllocated())
562 State = State->remove<HStateMap>(CurItem.first);
563 }
564 }
565 return State;
566 }
567
checkPointerEscape(ProgramStateRef State,const InvalidatedSymbols & Escaped,const CallEvent * Call,PointerEscapeKind Kind) const568 ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(
569 ProgramStateRef State, const InvalidatedSymbols &Escaped,
570 const CallEvent *Call, PointerEscapeKind Kind) const {
571 const FunctionDecl *FuncDecl =
572 Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr;
573
574 llvm::DenseSet<SymbolRef> UnEscaped;
575 // Not all calls should escape our symbols.
576 if (FuncDecl &&
577 (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall ||
578 Kind == PSK_EscapeOutParameters)) {
579 for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
580 if (Arg >= FuncDecl->getNumParams())
581 break;
582 const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
583 SmallVector<SymbolRef, 1024> Handles =
584 getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State);
585 for (SymbolRef Handle : Handles) {
586 if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
587 hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
588 UnEscaped.insert(Handle);
589 }
590 }
591 }
592 }
593
594 // For out params, we have to deal with derived symbols. See
595 // MacOSKeychainAPIChecker for details.
596 for (auto I : State->get<HStateMap>()) {
597 if (Escaped.count(I.first) && !UnEscaped.count(I.first))
598 State = State->set<HStateMap>(I.first, HandleState::getEscaped());
599 if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) {
600 auto ParentSym = SD->getParentSymbol();
601 if (Escaped.count(ParentSym))
602 State = State->set<HStateMap>(I.first, HandleState::getEscaped());
603 }
604 }
605
606 return State;
607 }
608
609 ExplodedNode *
reportLeaks(ArrayRef<SymbolRef> LeakedHandles,CheckerContext & C,ExplodedNode * Pred) const610 FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
611 CheckerContext &C, ExplodedNode *Pred) const {
612 ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);
613 for (SymbolRef LeakedHandle : LeakedHandles) {
614 reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType,
615 "Potential leak of handle");
616 }
617 return ErrNode;
618 }
619
reportDoubleRelease(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const620 void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
621 const SourceRange &Range,
622 CheckerContext &C) const {
623 ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
624 reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
625 "Releasing a previously released handle");
626 }
627
reportUnownedRelease(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const628 void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym,
629 const SourceRange &Range,
630 CheckerContext &C) const {
631 ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
632 reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType,
633 "Releasing an unowned handle");
634 }
635
reportUseAfterFree(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const636 void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
637 const SourceRange &Range,
638 CheckerContext &C) const {
639 ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
640 reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,
641 "Using a previously released handle");
642 }
643
reportBug(SymbolRef Sym,ExplodedNode * ErrorNode,CheckerContext & C,const SourceRange * Range,const BugType & Type,StringRef Msg) const644 void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
645 CheckerContext &C,
646 const SourceRange *Range,
647 const BugType &Type, StringRef Msg) const {
648 if (!ErrorNode)
649 return;
650
651 std::unique_ptr<PathSensitiveBugReport> R;
652 if (Type.isSuppressOnSink()) {
653 const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);
654 if (AcquireNode) {
655 PathDiagnosticLocation LocUsedForUniqueing =
656 PathDiagnosticLocation::createBegin(
657 AcquireNode->getStmtForDiagnostics(), C.getSourceManager(),
658 AcquireNode->getLocationContext());
659
660 R = std::make_unique<PathSensitiveBugReport>(
661 Type, Msg, ErrorNode, LocUsedForUniqueing,
662 AcquireNode->getLocationContext()->getDecl());
663 }
664 }
665 if (!R)
666 R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
667 if (Range)
668 R->addRange(*Range);
669 R->markInteresting(Sym);
670 C.emitReport(std::move(R));
671 }
672
registerFuchsiaHandleChecker(CheckerManager & mgr)673 void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
674 mgr.registerChecker<FuchsiaHandleChecker>();
675 }
676
shouldRegisterFuchsiaHandleChecker(const CheckerManager & mgr)677 bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {
678 return true;
679 }
680
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const681 void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
682 const char *NL, const char *Sep) const {
683
684 HStateMapTy StateMap = State->get<HStateMap>();
685
686 if (!StateMap.isEmpty()) {
687 Out << Sep << "FuchsiaHandleChecker :" << NL;
688 for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
689 ++I) {
690 I.getKey()->dumpToStream(Out);
691 Out << " : ";
692 I.getData().dump(Out);
693 Out << NL;
694 }
695 }
696 }
697