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", "Mac OS API")); 95 } 96 97 void generateDeallocatorMismatchReport(const AllocationPair &AP, 98 const Expr *ArgExpr, 99 CheckerContext &C) const; 100 101 /// Find the allocation site for Sym on the path leading to the node N. 102 const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym, 103 CheckerContext &C) const; 104 105 BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 106 ExplodedNode *N, 107 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 virtual ~SecKeychainBugVisitor() {} 140 141 void Profile(llvm::FoldingSetNodeID &ID) const { 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); 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 0; 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 BugReport *Report = new BugReport(*BT, os.str(), N); 272 Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 273 Report->addRange(ArgExpr->getSourceRange()); 274 markInteresting(Report, AP); 275 C.emitReport(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 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 295 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) 296 if (const AllocationState *AS = State->get<AllocatedData>(V)) { 297 if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { 298 // Remove the value from the state. The new symbol will be added for 299 // tracking when the second allocator is processed in checkPostStmt(). 300 State = State->remove<AllocatedData>(V); 301 ExplodedNode *N = C.addTransition(State); 302 if (!N) 303 return; 304 initBugType(); 305 SmallString<128> sbuf; 306 llvm::raw_svector_ostream os(sbuf); 307 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 308 os << "Allocated data should be released before another call to " 309 << "the allocator: missing a call to '" 310 << FunctionsToTrack[DIdx].Name 311 << "'."; 312 BugReport *Report = new BugReport(*BT, os.str(), N); 313 Report->addVisitor(new SecKeychainBugVisitor(V)); 314 Report->addRange(ArgExpr->getSourceRange()); 315 Report->markInteresting(AS->Region); 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 SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); 336 337 // If the argument is coming from the heap, globals, or unknown, do not 338 // report it. 339 bool RegionArgIsBad = false; 340 if (!ArgSM) { 341 if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) 342 return; 343 RegionArgIsBad = true; 344 } 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 if (AS) 368 Report->markInteresting(AS->Region); 369 C.emitReport(Report); 370 return; 371 } 372 373 // Process functions which might deallocate. 374 if (FunctionsToTrack[idx].Kind == PossibleAPI) { 375 376 if (funName == "CFStringCreateWithBytesNoCopy") { 377 const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); 378 // NULL ~ default deallocator, so warn. 379 if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), 380 Expr::NPC_ValueDependentIsNotNull)) { 381 const AllocationPair AP = std::make_pair(ArgSM, AS); 382 generateDeallocatorMismatchReport(AP, ArgExpr, C); 383 return; 384 } 385 // One of the default allocators, so warn. 386 if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { 387 StringRef DeallocatorName = DE->getFoundDecl()->getName(); 388 if (DeallocatorName == "kCFAllocatorDefault" || 389 DeallocatorName == "kCFAllocatorSystemDefault" || 390 DeallocatorName == "kCFAllocatorMalloc") { 391 const AllocationPair AP = std::make_pair(ArgSM, AS); 392 generateDeallocatorMismatchReport(AP, ArgExpr, C); 393 return; 394 } 395 // If kCFAllocatorNull, which does not deallocate, we still have to 396 // find the deallocator. 397 if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") 398 return; 399 } 400 // In all other cases, assume the user supplied a correct deallocator 401 // that will free memory so stop tracking. 402 State = State->remove<AllocatedData>(ArgSM); 403 C.addTransition(State); 404 return; 405 } 406 407 llvm_unreachable("We know of no other possible APIs."); 408 } 409 410 // The call is deallocating a value we previously allocated, so remove it 411 // from the next state. 412 State = State->remove<AllocatedData>(ArgSM); 413 414 // Check if the proper deallocator is used. 415 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 416 if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { 417 const AllocationPair AP = std::make_pair(ArgSM, AS); 418 generateDeallocatorMismatchReport(AP, ArgExpr, C); 419 return; 420 } 421 422 // If the buffer can be null and the return status can be an error, 423 // report a bad call to free. 424 if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) && 425 !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { 426 ExplodedNode *N = C.addTransition(State); 427 if (!N) 428 return; 429 initBugType(); 430 BugReport *Report = new BugReport(*BT, 431 "Only call free if a valid (non-NULL) buffer was returned.", N); 432 Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); 433 Report->addRange(ArgExpr->getSourceRange()); 434 Report->markInteresting(AS->Region); 435 C.emitReport(Report); 436 return; 437 } 438 439 C.addTransition(State); 440 } 441 442 void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, 443 CheckerContext &C) const { 444 ProgramStateRef State = C.getState(); 445 const FunctionDecl *FD = C.getCalleeDecl(CE); 446 if (!FD || FD->getKind() != Decl::Function) 447 return; 448 449 StringRef funName = C.getCalleeName(FD); 450 451 // If a value has been allocated, add it to the set for tracking. 452 unsigned idx = getTrackedFunctionIndex(funName, true); 453 if (idx == InvalidIdx) 454 return; 455 456 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 457 // If the argument entered as an enclosing function parameter, skip it to 458 // avoid false positives. 459 if (isEnclosingFunctionParam(ArgExpr) && 460 C.getLocationContext()->getParent() == 0) 461 return; 462 463 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { 464 // If the argument points to something that's not a symbolic region, it 465 // can be: 466 // - unknown (cannot reason about it) 467 // - undefined (already reported by other checker) 468 // - constant (null - should not be tracked, 469 // other constant will generate a compiler warning) 470 // - goto (should be reported by other checker) 471 472 // The call return value symbol should stay alive for as long as the 473 // allocated value symbol, since our diagnostics depend on the value 474 // returned by the call. Ex: Data should only be freed if noErr was 475 // returned during allocation.) 476 SymbolRef RetStatusSymbol = 477 State->getSVal(CE, C.getLocationContext()).getAsSymbol(); 478 C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); 479 480 // Track the allocated value in the checker state. 481 State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, 482 RetStatusSymbol)); 483 assert(State); 484 C.addTransition(State); 485 } 486 } 487 488 // TODO: This logic is the same as in Malloc checker. 489 const ExplodedNode * 490 MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N, 491 SymbolRef Sym, 492 CheckerContext &C) const { 493 const LocationContext *LeakContext = N->getLocationContext(); 494 // Walk the ExplodedGraph backwards and find the first node that referred to 495 // the tracked symbol. 496 const ExplodedNode *AllocNode = N; 497 498 while (N) { 499 if (!N->getState()->get<AllocatedData>(Sym)) 500 break; 501 // Allocation node, is the last node in the current context in which the 502 // symbol was tracked. 503 if (N->getLocationContext() == LeakContext) 504 AllocNode = N; 505 N = N->pred_empty() ? NULL : *(N->pred_begin()); 506 } 507 508 return AllocNode; 509 } 510 511 BugReport *MacOSKeychainAPIChecker:: 512 generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 513 ExplodedNode *N, 514 CheckerContext &C) const { 515 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; 516 initBugType(); 517 SmallString<70> sbuf; 518 llvm::raw_svector_ostream os(sbuf); 519 os << "Allocated data is not released: missing a call to '" 520 << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; 521 522 // Most bug reports are cached at the location where they occurred. 523 // With leaks, we want to unique them by the location where they were 524 // allocated, and only report a single path. 525 PathDiagnosticLocation LocUsedForUniqueing; 526 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C); 527 const Stmt *AllocStmt = 0; 528 ProgramPoint P = AllocNode->getLocation(); 529 if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>()) 530 AllocStmt = Exit->getCalleeContext()->getCallSite(); 531 else if (Optional<clang::PostStmt> PS = P.getAs<clang::PostStmt>()) 532 AllocStmt = PS->getStmt(); 533 534 if (AllocStmt) 535 LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, 536 C.getSourceManager(), 537 AllocNode->getLocationContext()); 538 539 BugReport *Report = new BugReport(*BT, os.str(), N, LocUsedForUniqueing, 540 AllocNode->getLocationContext()->getDecl()); 541 542 Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 543 markInteresting(Report, AP); 544 return Report; 545 } 546 547 void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, 548 CheckerContext &C) const { 549 ProgramStateRef State = C.getState(); 550 AllocatedDataTy ASet = State->get<AllocatedData>(); 551 if (ASet.isEmpty()) 552 return; 553 554 bool Changed = false; 555 AllocationPairVec Errors; 556 for (AllocatedDataTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { 557 if (SR.isLive(I->first)) 558 continue; 559 560 Changed = true; 561 State = State->remove<AllocatedData>(I->first); 562 // If the allocated symbol is null or if the allocation call might have 563 // returned an error, do not report. 564 ConstraintManager &CMgr = State->getConstraintManager(); 565 ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey()); 566 if (AllocFailed.isConstrainedTrue() || 567 definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) 568 continue; 569 Errors.push_back(std::make_pair(I->first, &I->second)); 570 } 571 if (!Changed) { 572 // Generate the new, cleaned up state. 573 C.addTransition(State); 574 return; 575 } 576 577 static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : DeadSymbolsLeak"); 578 ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); 579 580 // Generate the error reports. 581 for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); 582 I != E; ++I) { 583 C.emitReport(generateAllocatedDataNotReleasedReport(*I, N, C)); 584 } 585 586 // Generate the new, cleaned up state. 587 C.addTransition(State, N); 588 } 589 590 591 PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( 592 const ExplodedNode *N, 593 const ExplodedNode *PrevN, 594 BugReporterContext &BRC, 595 BugReport &BR) { 596 const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); 597 if (!AS) 598 return 0; 599 const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym); 600 if (ASPrev) 601 return 0; 602 603 // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the 604 // allocation site. 605 const CallExpr *CE = 606 cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt()); 607 const FunctionDecl *funDecl = CE->getDirectCallee(); 608 assert(funDecl && "We do not support indirect function calls as of now."); 609 StringRef funName = funDecl->getName(); 610 611 // Get the expression of the corresponding argument. 612 unsigned Idx = getTrackedFunctionIndex(funName, true); 613 assert(Idx != InvalidIdx && "This should be a call to an allocator."); 614 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); 615 PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), 616 N->getLocationContext()); 617 return new PathDiagnosticEventPiece(Pos, "Data is allocated here."); 618 } 619 620 void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { 621 mgr.registerChecker<MacOSKeychainAPIChecker>(); 622 } 623