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