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