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