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:
FuchsiaHandleSymbolVisitor(ProgramStateRef State)257 FuchsiaHandleSymbolVisitor(ProgramStateRef State) : State(std::move(State)) {}
getState() const258 ProgramStateRef getState() const { return State; }
259
VisitSymbol(SymbolRef S)260 bool VisitSymbol(SymbolRef S) override {
261 if (const auto *HandleType = S->getType()->getAs<TypedefType>())
262 if (HandleType->getDecl()->getName() == HandleTypeName)
263 Symbols.push_back(S);
264 return true;
265 }
266
GetSymbols()267 SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; }
268
269 private:
270 SmallVector<SymbolRef, 1024> Symbols;
271 ProgramStateRef State;
272 };
273 } // end anonymous namespace
274
275 /// Returns the symbols extracted from the argument or empty vector if it cannot
276 /// be found. It is unlikely to have over 1024 symbols in one argument.
277 static SmallVector<SymbolRef, 1024>
getFuchsiaHandleSymbols(QualType QT,SVal Arg,ProgramStateRef State)278 getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) {
279 int PtrToHandleLevel = 0;
280 while (QT->isAnyPointerType() || QT->isReferenceType()) {
281 ++PtrToHandleLevel;
282 QT = QT->getPointeeType();
283 }
284 if (QT->isStructureType()) {
285 // If we see a structure, see if there is any handle referenced by the
286 // structure.
287 FuchsiaHandleSymbolVisitor Visitor(State);
288 State->scanReachableSymbols(Arg, Visitor);
289 return Visitor.GetSymbols();
290 }
291 if (const auto *HandleType = QT->getAs<TypedefType>()) {
292 if (HandleType->getDecl()->getName() != HandleTypeName)
293 return {};
294 if (PtrToHandleLevel > 1)
295 // Not supported yet.
296 return {};
297
298 if (PtrToHandleLevel == 0) {
299 SymbolRef Sym = Arg.getAsSymbol();
300 if (Sym) {
301 return {Sym};
302 } else {
303 return {};
304 }
305 } else {
306 assert(PtrToHandleLevel == 1);
307 if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) {
308 SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol();
309 if (Sym) {
310 return {Sym};
311 } else {
312 return {};
313 }
314 }
315 }
316 }
317 return {};
318 }
319
checkPreCall(const CallEvent & Call,CheckerContext & C) const320 void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
321 CheckerContext &C) const {
322 ProgramStateRef State = C.getState();
323 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
324 if (!FuncDecl) {
325 // Unknown call, escape by value handles. They are not covered by
326 // PointerEscape callback.
327 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
328 if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol())
329 State = State->set<HStateMap>(Handle, HandleState::getEscaped());
330 }
331 C.addTransition(State);
332 return;
333 }
334
335 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
336 if (Arg >= FuncDecl->getNumParams())
337 break;
338 const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
339 SmallVector<SymbolRef, 1024> Handles =
340 getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
341
342 // Handled in checkPostCall.
343 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
344 hasFuchsiaAttr<AcquireHandleAttr>(PVD))
345 continue;
346
347 for (SymbolRef Handle : Handles) {
348 const HandleState *HState = State->get<HStateMap>(Handle);
349 if (!HState || HState->isEscaped())
350 continue;
351
352 if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
353 PVD->getType()->isIntegerType()) {
354 if (HState->isReleased()) {
355 reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
356 return;
357 }
358 }
359 }
360 }
361 C.addTransition(State);
362 }
363
checkPostCall(const CallEvent & Call,CheckerContext & C) const364 void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
365 CheckerContext &C) const {
366 const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
367 if (!FuncDecl)
368 return;
369
370 // If we analyzed the function body, then ignore the annotations.
371 if (C.wasInlined)
372 return;
373
374 ProgramStateRef State = C.getState();
375
376 std::vector<std::function<std::string(BugReport & BR)>> Notes;
377 SymbolRef ResultSymbol = nullptr;
378 if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())
379 if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
380 ResultSymbol = Call.getReturnValue().getAsSymbol();
381
382 // Function returns an open handle.
383 if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
384 SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
385 Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
386 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
387 if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
388 std::string SBuf;
389 llvm::raw_string_ostream OS(SBuf);
390 OS << "Function '" << FuncDecl->getDeclName()
391 << "' returns an open handle";
392 return OS.str();
393 } else
394 return "";
395 });
396 State =
397 State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));
398 } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) {
399 // Function returns an unowned handle
400 SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
401 Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
402 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
403 if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
404 std::string SBuf;
405 llvm::raw_string_ostream OS(SBuf);
406 OS << "Function '" << FuncDecl->getDeclName()
407 << "' returns an unowned handle";
408 return OS.str();
409 } else
410 return "";
411 });
412 State = State->set<HStateMap>(RetSym, HandleState::getUnowned());
413 }
414
415 for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
416 if (Arg >= FuncDecl->getNumParams())
417 break;
418 const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
419 unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1;
420 SmallVector<SymbolRef, 1024> Handles =
421 getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
422
423 for (SymbolRef Handle : Handles) {
424 const HandleState *HState = State->get<HStateMap>(Handle);
425 if (HState && HState->isEscaped())
426 continue;
427 if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
428 if (HState && HState->isReleased()) {
429 reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
430 return;
431 } else if (HState && HState->isUnowned()) {
432 reportUnownedRelease(Handle, Call.getArgSourceRange(Arg), C);
433 return;
434 } else {
435 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
436 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
437 if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
438 std::string SBuf;
439 llvm::raw_string_ostream OS(SBuf);
440 OS << "Handle released through " << ParamDiagIdx
441 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
442 return OS.str();
443 } else
444 return "";
445 });
446 State = State->set<HStateMap>(Handle, HandleState::getReleased());
447 }
448 } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
449 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
450 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
451 if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
452 std::string SBuf;
453 llvm::raw_string_ostream OS(SBuf);
454 OS << "Handle allocated through " << ParamDiagIdx
455 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
456 return OS.str();
457 } else
458 return "";
459 });
460 State = State->set<HStateMap>(
461 Handle, HandleState::getMaybeAllocated(ResultSymbol));
462 } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) {
463 Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
464 auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
465 if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
466 std::string SBuf;
467 llvm::raw_string_ostream OS(SBuf);
468 OS << "Unowned handle allocated through " << ParamDiagIdx
469 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
470 return OS.str();
471 } else
472 return "";
473 });
474 State = State->set<HStateMap>(Handle, HandleState::getUnowned());
475 } else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
476 PVD->getType()->isIntegerType()) {
477 // Working around integer by-value escapes.
478 // The by-value escape would not be captured in checkPointerEscape.
479 // If the function was not analyzed (otherwise wasInlined should be
480 // true) and there is no annotation on the handle, we assume the handle
481 // is escaped.
482 State = State->set<HStateMap>(Handle, HandleState::getEscaped());
483 }
484 }
485 }
486 const NoteTag *T = nullptr;
487 if (!Notes.empty()) {
488 T = C.getNoteTag([this, Notes{std::move(Notes)}](
489 PathSensitiveBugReport &BR) -> std::string {
490 if (&BR.getBugType() != &UseAfterReleaseBugType &&
491 &BR.getBugType() != &LeakBugType &&
492 &BR.getBugType() != &DoubleReleaseBugType &&
493 &BR.getBugType() != &ReleaseUnownedBugType)
494 return "";
495 for (auto &Note : Notes) {
496 std::string Text = Note(BR);
497 if (!Text.empty())
498 return Text;
499 }
500 return "";
501 });
502 }
503 C.addTransition(State, T);
504 }
505
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const506 void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
507 CheckerContext &C) const {
508 ProgramStateRef State = C.getState();
509 SmallVector<SymbolRef, 2> LeakedSyms;
510 HStateMapTy TrackedHandles = State->get<HStateMap>();
511 for (auto &CurItem : TrackedHandles) {
512 SymbolRef ErrorSym = CurItem.second.getErrorSym();
513 // Keeping zombie handle symbols. In case the error symbol is dying later
514 // than the handle symbol we might produce spurious leak warnings (in case
515 // we find out later from the status code that the handle allocation failed
516 // in the first place).
517 if (!SymReaper.isDead(CurItem.first) ||
518 (ErrorSym && !SymReaper.isDead(ErrorSym)))
519 continue;
520 if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
521 LeakedSyms.push_back(CurItem.first);
522 State = State->remove<HStateMap>(CurItem.first);
523 }
524
525 ExplodedNode *N = C.getPredecessor();
526 if (!LeakedSyms.empty())
527 N = reportLeaks(LeakedSyms, C, N);
528
529 C.addTransition(State, N);
530 }
531
532 // Acquiring a handle is not always successful. In Fuchsia most functions
533 // return a status code that determines the status of the handle.
534 // When we split the path based on this status code we know that on one
535 // path we do have the handle and on the other path the acquire failed.
536 // This method helps avoiding false positive leak warnings on paths where
537 // the function failed.
538 // Moreover, when a handle is known to be zero (the invalid handle),
539 // we no longer can follow the symbol on the path, becaue the constant
540 // zero will be used instead of the symbol. We also do not need to release
541 // an invalid handle, so we remove the corresponding symbol from the state.
evalAssume(ProgramStateRef State,SVal Cond,bool Assumption) const542 ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
543 SVal Cond,
544 bool Assumption) const {
545 // TODO: add notes about successes/fails for APIs.
546 ConstraintManager &Cmr = State->getConstraintManager();
547 HStateMapTy TrackedHandles = State->get<HStateMap>();
548 for (auto &CurItem : TrackedHandles) {
549 ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first);
550 if (HandleVal.isConstrainedTrue()) {
551 // The handle is invalid. We can no longer follow the symbol on this path.
552 State = State->remove<HStateMap>(CurItem.first);
553 }
554 SymbolRef ErrorSym = CurItem.second.getErrorSym();
555 if (!ErrorSym)
556 continue;
557 ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);
558 if (ErrorVal.isConstrainedTrue()) {
559 // Allocation succeeded.
560 if (CurItem.second.maybeAllocated())
561 State = State->set<HStateMap>(
562 CurItem.first, HandleState::getAllocated(State, CurItem.second));
563 } else if (ErrorVal.isConstrainedFalse()) {
564 // Allocation failed.
565 if (CurItem.second.maybeAllocated())
566 State = State->remove<HStateMap>(CurItem.first);
567 }
568 }
569 return State;
570 }
571
checkPointerEscape(ProgramStateRef State,const InvalidatedSymbols & Escaped,const CallEvent * Call,PointerEscapeKind Kind) const572 ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(
573 ProgramStateRef State, const InvalidatedSymbols &Escaped,
574 const CallEvent *Call, PointerEscapeKind Kind) const {
575 const FunctionDecl *FuncDecl =
576 Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr;
577
578 llvm::DenseSet<SymbolRef> UnEscaped;
579 // Not all calls should escape our symbols.
580 if (FuncDecl &&
581 (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall ||
582 Kind == PSK_EscapeOutParameters)) {
583 for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
584 if (Arg >= FuncDecl->getNumParams())
585 break;
586 const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
587 SmallVector<SymbolRef, 1024> Handles =
588 getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State);
589 for (SymbolRef Handle : Handles) {
590 if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
591 hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
592 UnEscaped.insert(Handle);
593 }
594 }
595 }
596 }
597
598 // For out params, we have to deal with derived symbols. See
599 // MacOSKeychainAPIChecker for details.
600 for (auto I : State->get<HStateMap>()) {
601 if (Escaped.count(I.first) && !UnEscaped.count(I.first))
602 State = State->set<HStateMap>(I.first, HandleState::getEscaped());
603 if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) {
604 auto ParentSym = SD->getParentSymbol();
605 if (Escaped.count(ParentSym))
606 State = State->set<HStateMap>(I.first, HandleState::getEscaped());
607 }
608 }
609
610 return State;
611 }
612
613 ExplodedNode *
reportLeaks(ArrayRef<SymbolRef> LeakedHandles,CheckerContext & C,ExplodedNode * Pred) const614 FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
615 CheckerContext &C, ExplodedNode *Pred) const {
616 ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);
617 for (SymbolRef LeakedHandle : LeakedHandles) {
618 reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType,
619 "Potential leak of handle");
620 }
621 return ErrNode;
622 }
623
reportDoubleRelease(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const624 void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
625 const SourceRange &Range,
626 CheckerContext &C) const {
627 ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
628 reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
629 "Releasing a previously released handle");
630 }
631
reportUnownedRelease(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const632 void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym,
633 const SourceRange &Range,
634 CheckerContext &C) const {
635 ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
636 reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType,
637 "Releasing an unowned handle");
638 }
639
reportUseAfterFree(SymbolRef HandleSym,const SourceRange & Range,CheckerContext & C) const640 void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
641 const SourceRange &Range,
642 CheckerContext &C) const {
643 ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
644 reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,
645 "Using a previously released handle");
646 }
647
reportBug(SymbolRef Sym,ExplodedNode * ErrorNode,CheckerContext & C,const SourceRange * Range,const BugType & Type,StringRef Msg) const648 void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
649 CheckerContext &C,
650 const SourceRange *Range,
651 const BugType &Type, StringRef Msg) const {
652 if (!ErrorNode)
653 return;
654
655 std::unique_ptr<PathSensitiveBugReport> R;
656 if (Type.isSuppressOnSink()) {
657 const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);
658 if (AcquireNode) {
659 PathDiagnosticLocation LocUsedForUniqueing =
660 PathDiagnosticLocation::createBegin(
661 AcquireNode->getStmtForDiagnostics(), C.getSourceManager(),
662 AcquireNode->getLocationContext());
663
664 R = std::make_unique<PathSensitiveBugReport>(
665 Type, Msg, ErrorNode, LocUsedForUniqueing,
666 AcquireNode->getLocationContext()->getDecl());
667 }
668 }
669 if (!R)
670 R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
671 if (Range)
672 R->addRange(*Range);
673 R->markInteresting(Sym);
674 C.emitReport(std::move(R));
675 }
676
registerFuchsiaHandleChecker(CheckerManager & mgr)677 void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
678 mgr.registerChecker<FuchsiaHandleChecker>();
679 }
680
shouldRegisterFuchsiaHandleChecker(const CheckerManager & mgr)681 bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {
682 return true;
683 }
684
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const685 void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
686 const char *NL, const char *Sep) const {
687
688 HStateMapTy StateMap = State->get<HStateMap>();
689
690 if (!StateMap.isEmpty()) {
691 Out << Sep << "FuchsiaHandleChecker :" << NL;
692 for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
693 ++I) {
694 I.getKey()->dumpToStream(Out);
695 Out << " : ";
696 I.getData().dump(Out);
697 Out << NL;
698 }
699 }
700 }
701