1 //==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // This checker flags misuses of KeyChainAPI. In particular, the password data 10 // allocated/returned by SecKeychainItemCopyContent, 11 // SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has 12 // to be freed using a call to SecKeychainItemFreeContent. 13 //===----------------------------------------------------------------------===// 14 15 #include "ClangSACheckers.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17 #include "clang/StaticAnalyzer/Core/Checker.h" 18 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 22 #include "llvm/ADT/SmallString.h" 23 #include "llvm/Support/raw_ostream.h" 24 25 using namespace clang; 26 using namespace ento; 27 28 namespace { 29 class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, 30 check::PostStmt<CallExpr>, 31 check::DeadSymbols> { 32 mutable std::unique_ptr<BugType> BT; 33 34 public: 35 /// AllocationState is a part of the checker specific state together with the 36 /// MemRegion corresponding to the allocated data. 37 struct AllocationState { 38 /// The index of the allocator function. 39 unsigned int AllocatorIdx; 40 SymbolRef Region; 41 42 AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) : 43 AllocatorIdx(Idx), 44 Region(R) {} 45 46 bool operator==(const AllocationState &X) const { 47 return (AllocatorIdx == X.AllocatorIdx && 48 Region == X.Region); 49 } 50 51 void Profile(llvm::FoldingSetNodeID &ID) const { 52 ID.AddInteger(AllocatorIdx); 53 ID.AddPointer(Region); 54 } 55 }; 56 57 void checkPreStmt(const CallExpr *S, CheckerContext &C) const; 58 void checkPostStmt(const CallExpr *S, CheckerContext &C) const; 59 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 60 61 private: 62 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; 63 typedef SmallVector<AllocationPair, 2> AllocationPairVec; 64 65 enum APIKind { 66 /// Denotes functions tracked by this checker. 67 ValidAPI = 0, 68 /// The functions commonly/mistakenly used in place of the given API. 69 ErrorAPI = 1, 70 /// The functions which may allocate the data. These are tracked to reduce 71 /// the false alarm rate. 72 PossibleAPI = 2 73 }; 74 /// Stores the information about the allocator and deallocator functions - 75 /// these are the functions the checker is tracking. 76 struct ADFunctionInfo { 77 const char* Name; 78 unsigned int Param; 79 unsigned int DeallocatorIdx; 80 APIKind Kind; 81 }; 82 static const unsigned InvalidIdx = 100000; 83 static const unsigned FunctionsToTrackSize = 8; 84 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; 85 /// The value, which represents no error return value for allocator functions. 86 static const unsigned NoErr = 0; 87 88 /// Given the function name, returns the index of the allocator/deallocator 89 /// function. 90 static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); 91 92 inline void initBugType() const { 93 if (!BT) 94 BT.reset(new BugType(this, "Improper use of SecKeychain API", 95 "API Misuse (Apple)")); 96 } 97 98 void generateDeallocatorMismatchReport(const AllocationPair &AP, 99 const Expr *ArgExpr, 100 CheckerContext &C) const; 101 102 /// Find the allocation site for Sym on the path leading to the node N. 103 const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym, 104 CheckerContext &C) const; 105 106 std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport( 107 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const; 108 109 /// Check if RetSym evaluates to an error value in the current state. 110 bool definitelyReturnedError(SymbolRef RetSym, 111 ProgramStateRef State, 112 SValBuilder &Builder, 113 bool noError = false) const; 114 115 /// Check if RetSym evaluates to a NoErr value in the current state. 116 bool definitelyDidnotReturnError(SymbolRef RetSym, 117 ProgramStateRef State, 118 SValBuilder &Builder) const { 119 return definitelyReturnedError(RetSym, State, Builder, true); 120 } 121 122 /// Mark an AllocationPair interesting for diagnostic reporting. 123 void markInteresting(BugReport *R, const AllocationPair &AP) const { 124 R->markInteresting(AP.first); 125 R->markInteresting(AP.second->Region); 126 } 127 128 /// The bug visitor which allows us to print extra diagnostics along the 129 /// BugReport path. For example, showing the allocation site of the leaked 130 /// region. 131 class SecKeychainBugVisitor 132 : public BugReporterVisitorImpl<SecKeychainBugVisitor> { 133 protected: 134 // The allocated region symbol tracked by the main analysis. 135 SymbolRef Sym; 136 137 public: 138 SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} 139 ~SecKeychainBugVisitor() override {} 140 141 void Profile(llvm::FoldingSetNodeID &ID) const override { 142 static int X = 0; 143 ID.AddPointer(&X); 144 ID.AddPointer(Sym); 145 } 146 147 PathDiagnosticPiece *VisitNode(const ExplodedNode *N, 148 const ExplodedNode *PrevN, 149 BugReporterContext &BRC, 150 BugReport &BR) override; 151 }; 152 }; 153 } 154 155 /// ProgramState traits to store the currently allocated (and not yet freed) 156 /// symbols. This is a map from the allocated content symbol to the 157 /// corresponding AllocationState. 158 REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData, 159 SymbolRef, 160 MacOSKeychainAPIChecker::AllocationState) 161 162 static bool isEnclosingFunctionParam(const Expr *E) { 163 E = E->IgnoreParenCasts(); 164 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { 165 const ValueDecl *VD = DRE->getDecl(); 166 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) 167 return true; 168 } 169 return false; 170 } 171 172 const MacOSKeychainAPIChecker::ADFunctionInfo 173 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { 174 {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0 175 {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1 176 {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2 177 {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3 178 {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4 179 {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5 180 {"free", 0, InvalidIdx, ErrorAPI}, // 6 181 {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7 182 }; 183 184 unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, 185 bool IsAllocator) { 186 for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { 187 ADFunctionInfo FI = FunctionsToTrack[I]; 188 if (FI.Name != Name) 189 continue; 190 // Make sure the function is of the right type (allocator vs deallocator). 191 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) 192 return InvalidIdx; 193 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) 194 return InvalidIdx; 195 196 return I; 197 } 198 // The function is not tracked. 199 return InvalidIdx; 200 } 201 202 static bool isBadDeallocationArgument(const MemRegion *Arg) { 203 if (!Arg) 204 return false; 205 if (isa<AllocaRegion>(Arg) || 206 isa<BlockDataRegion>(Arg) || 207 isa<TypedRegion>(Arg)) { 208 return true; 209 } 210 return false; 211 } 212 213 /// Given the address expression, retrieve the value it's pointing to. Assume 214 /// that value is itself an address, and return the corresponding symbol. 215 static SymbolRef getAsPointeeSymbol(const Expr *Expr, 216 CheckerContext &C) { 217 ProgramStateRef State = C.getState(); 218 SVal ArgV = State->getSVal(Expr, C.getLocationContext()); 219 220 if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { 221 StoreManager& SM = C.getStoreManager(); 222 SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); 223 if (sym) 224 return sym; 225 } 226 return nullptr; 227 } 228 229 // When checking for error code, we need to consider the following cases: 230 // 1) noErr / [0] 231 // 2) someErr / [1, inf] 232 // 3) unknown 233 // If noError, returns true iff (1). 234 // If !noError, returns true iff (2). 235 bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym, 236 ProgramStateRef State, 237 SValBuilder &Builder, 238 bool noError) const { 239 DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr, 240 Builder.getSymbolManager().getType(RetSym)); 241 DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal, 242 nonloc::SymbolVal(RetSym)); 243 ProgramStateRef ErrState = State->assume(NoErr, noError); 244 if (ErrState == State) { 245 return true; 246 } 247 248 return false; 249 } 250 251 // Report deallocator mismatch. Remove the region from tracking - reporting a 252 // missing free error after this one is redundant. 253 void MacOSKeychainAPIChecker:: 254 generateDeallocatorMismatchReport(const AllocationPair &AP, 255 const Expr *ArgExpr, 256 CheckerContext &C) const { 257 ProgramStateRef State = C.getState(); 258 State = State->remove<AllocatedData>(AP.first); 259 ExplodedNode *N = C.addTransition(State); 260 261 if (!N) 262 return; 263 initBugType(); 264 SmallString<80> sbuf; 265 llvm::raw_svector_ostream os(sbuf); 266 unsigned int PDeallocIdx = 267 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; 268 269 os << "Deallocator doesn't match the allocator: '" 270 << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; 271 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); 272 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); 273 Report->addRange(ArgExpr->getSourceRange()); 274 markInteresting(Report.get(), AP); 275 C.emitReport(std::move(Report)); 276 } 277 278 void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, 279 CheckerContext &C) const { 280 unsigned idx = InvalidIdx; 281 ProgramStateRef State = C.getState(); 282 283 const FunctionDecl *FD = C.getCalleeDecl(CE); 284 if (!FD || FD->getKind() != Decl::Function) 285 return; 286 287 StringRef funName = C.getCalleeName(FD); 288 if (funName.empty()) 289 return; 290 291 // If it is a call to an allocator function, it could be a double allocation. 292 idx = getTrackedFunctionIndex(funName, true); 293 if (idx != InvalidIdx) { 294 unsigned paramIdx = FunctionsToTrack[idx].Param; 295 if (CE->getNumArgs() <= paramIdx) 296 return; 297 298 const Expr *ArgExpr = CE->getArg(paramIdx); 299 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) 300 if (const AllocationState *AS = State->get<AllocatedData>(V)) { 301 if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { 302 // Remove the value from the state. The new symbol will be added for 303 // tracking when the second allocator is processed in checkPostStmt(). 304 State = State->remove<AllocatedData>(V); 305 ExplodedNode *N = C.addTransition(State); 306 if (!N) 307 return; 308 initBugType(); 309 SmallString<128> sbuf; 310 llvm::raw_svector_ostream os(sbuf); 311 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 312 os << "Allocated data should be released before another call to " 313 << "the allocator: missing a call to '" 314 << FunctionsToTrack[DIdx].Name 315 << "'."; 316 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); 317 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V)); 318 Report->addRange(ArgExpr->getSourceRange()); 319 Report->markInteresting(AS->Region); 320 C.emitReport(std::move(Report)); 321 } 322 } 323 return; 324 } 325 326 // Is it a call to one of deallocator functions? 327 idx = getTrackedFunctionIndex(funName, false); 328 if (idx == InvalidIdx) 329 return; 330 331 unsigned paramIdx = FunctionsToTrack[idx].Param; 332 if (CE->getNumArgs() <= paramIdx) 333 return; 334 335 // Check the argument to the deallocator. 336 const Expr *ArgExpr = CE->getArg(paramIdx); 337 SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext()); 338 339 // Undef is reported by another checker. 340 if (ArgSVal.isUndef()) 341 return; 342 343 SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); 344 345 // If the argument is coming from the heap, globals, or unknown, do not 346 // report it. 347 bool RegionArgIsBad = false; 348 if (!ArgSM) { 349 if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) 350 return; 351 RegionArgIsBad = true; 352 } 353 354 // Is the argument to the call being tracked? 355 const AllocationState *AS = State->get<AllocatedData>(ArgSM); 356 if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { 357 return; 358 } 359 // If trying to free data which has not been allocated yet, report as a bug. 360 // TODO: We might want a more precise diagnostic for double free 361 // (that would involve tracking all the freed symbols in the checker state). 362 if (!AS || RegionArgIsBad) { 363 // It is possible that this is a false positive - the argument might 364 // have entered as an enclosing function parameter. 365 if (isEnclosingFunctionParam(ArgExpr)) 366 return; 367 368 ExplodedNode *N = C.addTransition(State); 369 if (!N) 370 return; 371 initBugType(); 372 auto Report = llvm::make_unique<BugReport>( 373 *BT, "Trying to free data which has not been allocated.", N); 374 Report->addRange(ArgExpr->getSourceRange()); 375 if (AS) 376 Report->markInteresting(AS->Region); 377 C.emitReport(std::move(Report)); 378 return; 379 } 380 381 // Process functions which might deallocate. 382 if (FunctionsToTrack[idx].Kind == PossibleAPI) { 383 384 if (funName == "CFStringCreateWithBytesNoCopy") { 385 const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); 386 // NULL ~ default deallocator, so warn. 387 if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), 388 Expr::NPC_ValueDependentIsNotNull)) { 389 const AllocationPair AP = std::make_pair(ArgSM, AS); 390 generateDeallocatorMismatchReport(AP, ArgExpr, C); 391 return; 392 } 393 // One of the default allocators, so warn. 394 if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { 395 StringRef DeallocatorName = DE->getFoundDecl()->getName(); 396 if (DeallocatorName == "kCFAllocatorDefault" || 397 DeallocatorName == "kCFAllocatorSystemDefault" || 398 DeallocatorName == "kCFAllocatorMalloc") { 399 const AllocationPair AP = std::make_pair(ArgSM, AS); 400 generateDeallocatorMismatchReport(AP, ArgExpr, C); 401 return; 402 } 403 // If kCFAllocatorNull, which does not deallocate, we still have to 404 // find the deallocator. 405 if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") 406 return; 407 } 408 // In all other cases, assume the user supplied a correct deallocator 409 // that will free memory so stop tracking. 410 State = State->remove<AllocatedData>(ArgSM); 411 C.addTransition(State); 412 return; 413 } 414 415 llvm_unreachable("We know of no other possible APIs."); 416 } 417 418 // The call is deallocating a value we previously allocated, so remove it 419 // from the next state. 420 State = State->remove<AllocatedData>(ArgSM); 421 422 // Check if the proper deallocator is used. 423 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 424 if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { 425 const AllocationPair AP = std::make_pair(ArgSM, AS); 426 generateDeallocatorMismatchReport(AP, ArgExpr, C); 427 return; 428 } 429 430 // If the buffer can be null and the return status can be an error, 431 // report a bad call to free. 432 if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) && 433 !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { 434 ExplodedNode *N = C.addTransition(State); 435 if (!N) 436 return; 437 initBugType(); 438 auto Report = llvm::make_unique<BugReport>( 439 *BT, "Only call free if a valid (non-NULL) buffer was returned.", N); 440 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(ArgSM)); 441 Report->addRange(ArgExpr->getSourceRange()); 442 Report->markInteresting(AS->Region); 443 C.emitReport(std::move(Report)); 444 return; 445 } 446 447 C.addTransition(State); 448 } 449 450 void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, 451 CheckerContext &C) const { 452 ProgramStateRef State = C.getState(); 453 const FunctionDecl *FD = C.getCalleeDecl(CE); 454 if (!FD || FD->getKind() != Decl::Function) 455 return; 456 457 StringRef funName = C.getCalleeName(FD); 458 459 // If a value has been allocated, add it to the set for tracking. 460 unsigned idx = getTrackedFunctionIndex(funName, true); 461 if (idx == InvalidIdx) 462 return; 463 464 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 465 // If the argument entered as an enclosing function parameter, skip it to 466 // avoid false positives. 467 if (isEnclosingFunctionParam(ArgExpr) && 468 C.getLocationContext()->getParent() == nullptr) 469 return; 470 471 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { 472 // If the argument points to something that's not a symbolic region, it 473 // can be: 474 // - unknown (cannot reason about it) 475 // - undefined (already reported by other checker) 476 // - constant (null - should not be tracked, 477 // other constant will generate a compiler warning) 478 // - goto (should be reported by other checker) 479 480 // The call return value symbol should stay alive for as long as the 481 // allocated value symbol, since our diagnostics depend on the value 482 // returned by the call. Ex: Data should only be freed if noErr was 483 // returned during allocation.) 484 SymbolRef RetStatusSymbol = 485 State->getSVal(CE, C.getLocationContext()).getAsSymbol(); 486 C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); 487 488 // Track the allocated value in the checker state. 489 State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, 490 RetStatusSymbol)); 491 assert(State); 492 C.addTransition(State); 493 } 494 } 495 496 // TODO: This logic is the same as in Malloc checker. 497 const ExplodedNode * 498 MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N, 499 SymbolRef Sym, 500 CheckerContext &C) const { 501 const LocationContext *LeakContext = N->getLocationContext(); 502 // Walk the ExplodedGraph backwards and find the first node that referred to 503 // the tracked symbol. 504 const ExplodedNode *AllocNode = N; 505 506 while (N) { 507 if (!N->getState()->get<AllocatedData>(Sym)) 508 break; 509 // Allocation node, is the last node in the current or parent context in 510 // which the symbol was tracked. 511 const LocationContext *NContext = N->getLocationContext(); 512 if (NContext == LeakContext || 513 NContext->isParentOf(LeakContext)) 514 AllocNode = N; 515 N = N->pred_empty() ? nullptr : *(N->pred_begin()); 516 } 517 518 return AllocNode; 519 } 520 521 std::unique_ptr<BugReport> 522 MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( 523 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const { 524 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; 525 initBugType(); 526 SmallString<70> sbuf; 527 llvm::raw_svector_ostream os(sbuf); 528 os << "Allocated data is not released: missing a call to '" 529 << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; 530 531 // Most bug reports are cached at the location where they occurred. 532 // With leaks, we want to unique them by the location where they were 533 // allocated, and only report a single path. 534 PathDiagnosticLocation LocUsedForUniqueing; 535 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C); 536 const Stmt *AllocStmt = nullptr; 537 ProgramPoint P = AllocNode->getLocation(); 538 if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>()) 539 AllocStmt = Exit->getCalleeContext()->getCallSite(); 540 else if (Optional<clang::PostStmt> PS = P.getAs<clang::PostStmt>()) 541 AllocStmt = PS->getStmt(); 542 543 if (AllocStmt) 544 LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, 545 C.getSourceManager(), 546 AllocNode->getLocationContext()); 547 548 auto Report = 549 llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing, 550 AllocNode->getLocationContext()->getDecl()); 551 552 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); 553 markInteresting(Report.get(), AP); 554 return Report; 555 } 556 557 void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, 558 CheckerContext &C) const { 559 ProgramStateRef State = C.getState(); 560 AllocatedDataTy ASet = State->get<AllocatedData>(); 561 if (ASet.isEmpty()) 562 return; 563 564 bool Changed = false; 565 AllocationPairVec Errors; 566 for (AllocatedDataTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { 567 if (SR.isLive(I->first)) 568 continue; 569 570 Changed = true; 571 State = State->remove<AllocatedData>(I->first); 572 // If the allocated symbol is null or if the allocation call might have 573 // returned an error, do not report. 574 ConstraintManager &CMgr = State->getConstraintManager(); 575 ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey()); 576 if (AllocFailed.isConstrainedTrue() || 577 definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) 578 continue; 579 Errors.push_back(std::make_pair(I->first, &I->second)); 580 } 581 if (!Changed) { 582 // Generate the new, cleaned up state. 583 C.addTransition(State); 584 return; 585 } 586 587 static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak"); 588 ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); 589 590 // Generate the error reports. 591 for (const auto P : Errors) 592 C.emitReport(generateAllocatedDataNotReleasedReport(P, N, C)); 593 594 // Generate the new, cleaned up state. 595 C.addTransition(State, N); 596 } 597 598 599 PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( 600 const ExplodedNode *N, 601 const ExplodedNode *PrevN, 602 BugReporterContext &BRC, 603 BugReport &BR) { 604 const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); 605 if (!AS) 606 return nullptr; 607 const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym); 608 if (ASPrev) 609 return nullptr; 610 611 // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the 612 // allocation site. 613 const CallExpr *CE = 614 cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt()); 615 const FunctionDecl *funDecl = CE->getDirectCallee(); 616 assert(funDecl && "We do not support indirect function calls as of now."); 617 StringRef funName = funDecl->getName(); 618 619 // Get the expression of the corresponding argument. 620 unsigned Idx = getTrackedFunctionIndex(funName, true); 621 assert(Idx != InvalidIdx && "This should be a call to an allocator."); 622 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); 623 PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), 624 N->getLocationContext()); 625 return new PathDiagnosticEventPiece(Pos, "Data is allocated here."); 626 } 627 628 void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { 629 mgr.registerChecker<MacOSKeychainAPIChecker>(); 630 } 631