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