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