MacOSKeychainAPIChecker.cpp revision 8fe83e1df954d72c0f4ffc15d20a5222ec151c21
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/Checker.h" 17#include "clang/StaticAnalyzer/Core/CheckerManager.h" 18#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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 24using namespace clang; 25using namespace ento; 26 27namespace { 28class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, 29 check::PreStmt<ReturnStmt>, 30 check::PostStmt<CallExpr>, 31 check::EndPath, 32 check::DeadSymbols> { 33 mutable llvm::OwningPtr<BugType> BT; 34 35public: 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 checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; 60 void checkPostStmt(const CallExpr *S, CheckerContext &C) const; 61 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 62 void checkEndPath(CheckerContext &Ctx) const; 63 64private: 65 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; 66 typedef llvm::SmallVector<AllocationPair, 2> AllocationPairVec; 67 68 enum APIKind { 69 /// Denotes functions tracked by this checker. 70 ValidAPI = 0, 71 /// The functions commonly/mistakenly used in place of the given API. 72 ErrorAPI = 1, 73 /// The functions which may allocate the data. These are tracked to reduce 74 /// the false alarm rate. 75 PossibleAPI = 2 76 }; 77 /// Stores the information about the allocator and deallocator functions - 78 /// these are the functions the checker is tracking. 79 struct ADFunctionInfo { 80 const char* Name; 81 unsigned int Param; 82 unsigned int DeallocatorIdx; 83 APIKind Kind; 84 }; 85 static const unsigned InvalidIdx = 100000; 86 static const unsigned FunctionsToTrackSize = 8; 87 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; 88 /// The value, which represents no error return value for allocator functions. 89 static const unsigned NoErr = 0; 90 91 /// Given the function name, returns the index of the allocator/deallocator 92 /// function. 93 static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); 94 95 inline void initBugType() const { 96 if (!BT) 97 BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API")); 98 } 99 100 void generateDeallocatorMismatchReport(const AllocationPair &AP, 101 const Expr *ArgExpr, 102 CheckerContext &C) const; 103 104 BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 105 ExplodedNode *N) const; 106 107 /// Check if RetSym evaluates to an error value in the current state. 108 bool definitelyReturnedError(SymbolRef RetSym, 109 ProgramStateRef State, 110 SValBuilder &Builder, 111 bool noError = false) const; 112 113 /// Check if RetSym evaluates to a NoErr value in the current state. 114 bool definitelyDidnotReturnError(SymbolRef RetSym, 115 ProgramStateRef State, 116 SValBuilder &Builder) const { 117 return definitelyReturnedError(RetSym, State, Builder, true); 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 virtual ~SecKeychainBugVisitor() {} 131 132 void Profile(llvm::FoldingSetNodeID &ID) const { 133 static int X = 0; 134 ID.AddPointer(&X); 135 ID.AddPointer(Sym); 136 } 137 138 PathDiagnosticPiece *VisitNode(const ExplodedNode *N, 139 const ExplodedNode *PrevN, 140 BugReporterContext &BRC, 141 BugReport &BR); 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. 149typedef llvm::ImmutableMap<SymbolRef, 150 MacOSKeychainAPIChecker::AllocationState> AllocatedSetTy; 151 152namespace { struct AllocatedData {}; } 153namespace clang { namespace ento { 154template<> struct ProgramStateTrait<AllocatedData> 155 : public ProgramStatePartialTrait<AllocatedSetTy > { 156 static void *GDMIndex() { static int index = 0; return &index; } 157}; 158}} 159 160static bool isEnclosingFunctionParam(const Expr *E) { 161 E = E->IgnoreParenCasts(); 162 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { 163 const ValueDecl *VD = DRE->getDecl(); 164 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) 165 return true; 166 } 167 return false; 168} 169 170const MacOSKeychainAPIChecker::ADFunctionInfo 171 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { 172 {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0 173 {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1 174 {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2 175 {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3 176 {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4 177 {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5 178 {"free", 0, InvalidIdx, ErrorAPI}, // 6 179 {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7 180}; 181 182unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, 183 bool IsAllocator) { 184 for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { 185 ADFunctionInfo FI = FunctionsToTrack[I]; 186 if (FI.Name != Name) 187 continue; 188 // Make sure the function is of the right type (allocator vs deallocator). 189 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) 190 return InvalidIdx; 191 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) 192 return InvalidIdx; 193 194 return I; 195 } 196 // The function is not tracked. 197 return InvalidIdx; 198} 199 200static SymbolRef getSymbolForRegion(CheckerContext &C, 201 const MemRegion *R) { 202 // Implicit casts (ex: void* -> char*) can turn Symbolic region into element 203 // region, if that is the case, get the underlining region. 204 R = R->StripCasts(); 205 if (!isa<SymbolicRegion>(R)) { 206 return 0; 207 } 208 return cast<SymbolicRegion>(R)->getSymbol(); 209} 210 211static bool isBadDeallocationArgument(const MemRegion *Arg) { 212 if (isa<AllocaRegion>(Arg) || 213 isa<BlockDataRegion>(Arg) || 214 isa<TypedRegion>(Arg)) { 215 return true; 216 } 217 return false; 218} 219/// Given the address expression, retrieve the value it's pointing to. Assume 220/// that value is itself an address, and return the corresponding symbol. 221static SymbolRef getAsPointeeSymbol(const Expr *Expr, 222 CheckerContext &C) { 223 ProgramStateRef State = C.getState(); 224 SVal ArgV = State->getSVal(Expr, C.getLocationContext()); 225 226 if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) { 227 StoreManager& SM = C.getStoreManager(); 228 const MemRegion *V = SM.getBinding(State->getStore(), *X).getAsRegion(); 229 if (V) 230 return getSymbolForRegion(C, V); 231 } 232 return 0; 233} 234 235// When checking for error code, we need to consider the following cases: 236// 1) noErr / [0] 237// 2) someErr / [1, inf] 238// 3) unknown 239// If noError, returns true iff (1). 240// If !noError, returns true iff (2). 241bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym, 242 ProgramStateRef State, 243 SValBuilder &Builder, 244 bool noError) const { 245 DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr, 246 Builder.getSymbolManager().getType(RetSym)); 247 DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal, 248 nonloc::SymbolVal(RetSym)); 249 ProgramStateRef ErrState = State->assume(NoErr, noError); 250 if (ErrState == State) { 251 return true; 252 } 253 254 return false; 255} 256 257// Report deallocator mismatch. Remove the region from tracking - reporting a 258// missing free error after this one is redundant. 259void MacOSKeychainAPIChecker:: 260 generateDeallocatorMismatchReport(const AllocationPair &AP, 261 const Expr *ArgExpr, 262 CheckerContext &C) const { 263 ProgramStateRef State = C.getState(); 264 State = State->remove<AllocatedData>(AP.first); 265 ExplodedNode *N = C.addTransition(State); 266 267 if (!N) 268 return; 269 initBugType(); 270 llvm::SmallString<80> sbuf; 271 llvm::raw_svector_ostream os(sbuf); 272 unsigned int PDeallocIdx = 273 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; 274 275 os << "Deallocator doesn't match the allocator: '" 276 << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; 277 BugReport *Report = new BugReport(*BT, os.str(), N); 278 Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 279 Report->addRange(ArgExpr->getSourceRange()); 280 C.EmitReport(Report); 281} 282 283void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, 284 CheckerContext &C) const { 285 unsigned idx = InvalidIdx; 286 ProgramStateRef State = C.getState(); 287 288 StringRef funName = C.getCalleeName(CE); 289 if (funName.empty()) 290 return; 291 292 // If it is a call to an allocator function, it could be a double allocation. 293 idx = getTrackedFunctionIndex(funName, true); 294 if (idx != InvalidIdx) { 295 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 296 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) 297 if (const AllocationState *AS = State->get<AllocatedData>(V)) { 298 if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { 299 // Remove the value from the state. The new symbol will be added for 300 // tracking when the second allocator is processed in checkPostStmt(). 301 State = State->remove<AllocatedData>(V); 302 ExplodedNode *N = C.addTransition(State); 303 if (!N) 304 return; 305 initBugType(); 306 llvm::SmallString<128> sbuf; 307 llvm::raw_svector_ostream os(sbuf); 308 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 309 os << "Allocated data should be released before another call to " 310 << "the allocator: missing a call to '" 311 << FunctionsToTrack[DIdx].Name 312 << "'."; 313 BugReport *Report = new BugReport(*BT, os.str(), N); 314 Report->addVisitor(new SecKeychainBugVisitor(V)); 315 Report->addRange(ArgExpr->getSourceRange()); 316 C.EmitReport(Report); 317 } 318 } 319 return; 320 } 321 322 // Is it a call to one of deallocator functions? 323 idx = getTrackedFunctionIndex(funName, false); 324 if (idx == InvalidIdx) 325 return; 326 327 // Check the argument to the deallocator. 328 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 329 SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext()); 330 331 // Undef is reported by another checker. 332 if (ArgSVal.isUndef()) 333 return; 334 335 const MemRegion *Arg = ArgSVal.getAsRegion(); 336 if (!Arg) 337 return; 338 339 SymbolRef ArgSM = getSymbolForRegion(C, Arg); 340 bool RegionArgIsBad = ArgSM ? false : isBadDeallocationArgument(Arg); 341 // If the argument is coming from the heap, globals, or unknown, do not 342 // report it. 343 if (!ArgSM && !RegionArgIsBad) 344 return; 345 346 // Is the argument to the call being tracked? 347 const AllocationState *AS = State->get<AllocatedData>(ArgSM); 348 if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { 349 return; 350 } 351 // If trying to free data which has not been allocated yet, report as a bug. 352 // TODO: We might want a more precise diagnostic for double free 353 // (that would involve tracking all the freed symbols in the checker state). 354 if (!AS || RegionArgIsBad) { 355 // It is possible that this is a false positive - the argument might 356 // have entered as an enclosing function parameter. 357 if (isEnclosingFunctionParam(ArgExpr)) 358 return; 359 360 ExplodedNode *N = C.addTransition(State); 361 if (!N) 362 return; 363 initBugType(); 364 BugReport *Report = new BugReport(*BT, 365 "Trying to free data which has not been allocated.", N); 366 Report->addRange(ArgExpr->getSourceRange()); 367 C.EmitReport(Report); 368 return; 369 } 370 371 // Process functions which might deallocate. 372 if (FunctionsToTrack[idx].Kind == PossibleAPI) { 373 374 if (funName == "CFStringCreateWithBytesNoCopy") { 375 const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); 376 // NULL ~ default deallocator, so warn. 377 if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), 378 Expr::NPC_ValueDependentIsNotNull)) { 379 const AllocationPair AP = std::make_pair(ArgSM, AS); 380 generateDeallocatorMismatchReport(AP, ArgExpr, C); 381 return; 382 } 383 // One of the default allocators, so warn. 384 if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { 385 StringRef DeallocatorName = DE->getFoundDecl()->getName(); 386 if (DeallocatorName == "kCFAllocatorDefault" || 387 DeallocatorName == "kCFAllocatorSystemDefault" || 388 DeallocatorName == "kCFAllocatorMalloc") { 389 const AllocationPair AP = std::make_pair(ArgSM, AS); 390 generateDeallocatorMismatchReport(AP, ArgExpr, C); 391 return; 392 } 393 // If kCFAllocatorNull, which does not deallocate, we still have to 394 // find the deallocator. Otherwise, assume that the user had written a 395 // custom deallocator which does the right thing. 396 if (DE->getFoundDecl()->getName() != "kCFAllocatorNull") { 397 State = State->remove<AllocatedData>(ArgSM); 398 C.addTransition(State); 399 return; 400 } 401 } 402 } 403 return; 404 } 405 406 // The call is deallocating a value we previously allocated, so remove it 407 // from the next state. 408 State = State->remove<AllocatedData>(ArgSM); 409 410 // Check if the proper deallocator is used. 411 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 412 if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { 413 const AllocationPair AP = std::make_pair(ArgSM, AS); 414 generateDeallocatorMismatchReport(AP, ArgExpr, C); 415 return; 416 } 417 418 // If the buffer can be null and the return status can be an error, 419 // report a bad call to free. 420 if (State->assume(cast<DefinedSVal>(ArgSVal), false) && 421 !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { 422 ExplodedNode *N = C.addTransition(State); 423 if (!N) 424 return; 425 initBugType(); 426 BugReport *Report = new BugReport(*BT, 427 "Only call free if a valid (non-NULL) buffer was returned.", N); 428 Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); 429 Report->addRange(ArgExpr->getSourceRange()); 430 C.EmitReport(Report); 431 return; 432 } 433 434 C.addTransition(State); 435} 436 437void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, 438 CheckerContext &C) const { 439 ProgramStateRef State = C.getState(); 440 StringRef funName = C.getCalleeName(CE); 441 442 // If a value has been allocated, add it to the set for tracking. 443 unsigned idx = getTrackedFunctionIndex(funName, true); 444 if (idx == InvalidIdx) 445 return; 446 447 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 448 // If the argument entered as an enclosing function parameter, skip it to 449 // avoid false positives. 450 if (isEnclosingFunctionParam(ArgExpr)) 451 return; 452 453 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { 454 // If the argument points to something that's not a symbolic region, it 455 // can be: 456 // - unknown (cannot reason about it) 457 // - undefined (already reported by other checker) 458 // - constant (null - should not be tracked, 459 // other constant will generate a compiler warning) 460 // - goto (should be reported by other checker) 461 462 // The call return value symbol should stay alive for as long as the 463 // allocated value symbol, since our diagnostics depend on the value 464 // returned by the call. Ex: Data should only be freed if noErr was 465 // returned during allocation.) 466 SymbolRef RetStatusSymbol = 467 State->getSVal(CE, C.getLocationContext()).getAsSymbol(); 468 C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); 469 470 // Track the allocated value in the checker state. 471 State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, 472 RetStatusSymbol)); 473 assert(State); 474 C.addTransition(State); 475 } 476} 477 478void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S, 479 CheckerContext &C) const { 480 const Expr *retExpr = S->getRetValue(); 481 if (!retExpr) 482 return; 483 484 // Check if the value is escaping through the return. 485 ProgramStateRef state = C.getState(); 486 const MemRegion *V = 487 state->getSVal(retExpr, C.getLocationContext()).getAsRegion(); 488 if (!V) 489 return; 490 state = state->remove<AllocatedData>(getSymbolForRegion(C, V)); 491 492 // Proceed from the new state. 493 C.addTransition(state); 494} 495 496BugReport *MacOSKeychainAPIChecker:: 497 generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 498 ExplodedNode *N) const { 499 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; 500 initBugType(); 501 llvm::SmallString<70> sbuf; 502 llvm::raw_svector_ostream os(sbuf); 503 504 os << "Allocated data is not released: missing a call to '" 505 << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; 506 BugReport *Report = new BugReport(*BT, os.str(), N); 507 Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 508 Report->addRange(SourceRange()); 509 return Report; 510} 511 512void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, 513 CheckerContext &C) const { 514 ProgramStateRef State = C.getState(); 515 AllocatedSetTy ASet = State->get<AllocatedData>(); 516 if (ASet.isEmpty()) 517 return; 518 519 bool Changed = false; 520 AllocationPairVec Errors; 521 for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { 522 if (SR.isLive(I->first)) 523 continue; 524 525 Changed = true; 526 State = State->remove<AllocatedData>(I->first); 527 // If the allocated symbol is null or if the allocation call might have 528 // returned an error, do not report. 529 if (State->getSymVal(I->first) || 530 definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) 531 continue; 532 Errors.push_back(std::make_pair(I->first, &I->second)); 533 } 534 if (!Changed) 535 return; 536 537 // Generate the new, cleaned up state. 538 ExplodedNode *N = C.addTransition(State); 539 if (!N) 540 return; 541 542 // Generate the error reports. 543 for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); 544 I != E; ++I) { 545 C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N)); 546 } 547} 548 549// TODO: Remove this after we ensure that checkDeadSymbols are always called. 550void MacOSKeychainAPIChecker::checkEndPath(CheckerContext &Ctx) const { 551 ProgramStateRef state = Ctx.getState(); 552 AllocatedSetTy AS = state->get<AllocatedData>(); 553 if (AS.isEmpty()) 554 return; 555 556 // Anything which has been allocated but not freed (nor escaped) will be 557 // found here, so report it. 558 bool Changed = false; 559 AllocationPairVec Errors; 560 for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) { 561 Changed = true; 562 state = state->remove<AllocatedData>(I->first); 563 // If the allocated symbol is null or if error code was returned at 564 // allocation, do not report. 565 if (state->getSymVal(I.getKey()) || 566 definitelyReturnedError(I->second.Region, state, 567 Ctx.getSValBuilder())) { 568 continue; 569 } 570 Errors.push_back(std::make_pair(I->first, &I->second)); 571 } 572 573 // If no change, do not generate a new state. 574 if (!Changed) 575 return; 576 577 ExplodedNode *N = Ctx.addTransition(state); 578 if (!N) 579 return; 580 581 // Generate the error reports. 582 for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); 583 I != E; ++I) { 584 Ctx.EmitReport(generateAllocatedDataNotReleasedReport(*I, N)); 585 } 586} 587 588 589PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( 590 const ExplodedNode *N, 591 const ExplodedNode *PrevN, 592 BugReporterContext &BRC, 593 BugReport &BR) { 594 const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); 595 if (!AS) 596 return 0; 597 const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym); 598 if (ASPrev) 599 return 0; 600 601 // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the 602 // allocation site. 603 const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation()) 604 .getStmt()); 605 const FunctionDecl *funDecl = CE->getDirectCallee(); 606 assert(funDecl && "We do not support indirect function calls as of now."); 607 StringRef funName = funDecl->getName(); 608 609 // Get the expression of the corresponding argument. 610 unsigned Idx = getTrackedFunctionIndex(funName, true); 611 assert(Idx != InvalidIdx && "This should be a call to an allocator."); 612 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); 613 PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), 614 N->getLocationContext()); 615 return new PathDiagnosticEventPiece(Pos, "Data is allocated here."); 616} 617 618void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { 619 mgr.registerChecker<MacOSKeychainAPIChecker>(); 620} 621