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; 117 HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {} 118 119 public: 120 bool operator==(const HandleState &Other) const { 121 return K == Other.K && ErrorSym == Other.ErrorSym; 122 } 123 bool isAllocated() const { return K == Kind::Allocated; } 124 bool maybeAllocated() const { return K == Kind::MaybeAllocated; } 125 bool isReleased() const { return K == Kind::Released; } 126 bool isEscaped() const { return K == Kind::Escaped; } 127 bool isUnowned() const { return K == Kind::Unowned; } 128 129 static HandleState getMaybeAllocated(SymbolRef ErrorSym) { 130 return HandleState(Kind::MaybeAllocated, ErrorSym); 131 } 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 } 139 static HandleState getReleased() { 140 return HandleState(Kind::Released, nullptr); 141 } 142 static HandleState getEscaped() { 143 return HandleState(Kind::Escaped, nullptr); 144 } 145 static HandleState getUnowned() { 146 return HandleState(Kind::Unowned, nullptr); 147 } 148 149 SymbolRef getErrorSym() const { return ErrorSym; } 150 151 void Profile(llvm::FoldingSetNodeID &ID) const { 152 ID.AddInteger(static_cast<int>(K)); 153 ID.AddPointer(ErrorSym); 154 } 155 156 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 174 LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } 175 }; 176 177 template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) { 178 return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia"; 179 } 180 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 230 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: 257 FuchsiaHandleSymbolVisitor(ProgramStateRef State) : State(std::move(State)) {} 258 ProgramStateRef getState() const { return State; } 259 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 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> 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 320 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 364 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 SBuf; 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 SBuf; 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 SBuf; 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 SBuf; 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 SBuf; 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 506 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. 542 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 572 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 * 614 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 624 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 632 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 640 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 648 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 677 void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) { 678 mgr.registerChecker<FuchsiaHandleChecker>(); 679 } 680 681 bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) { 682 return true; 683 } 684 685 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