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 std::unique_ptr<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(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. 157 REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData, 158 SymbolRef, 159 MacOSKeychainAPIChecker::AllocationState) 160 161 static 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 171 const 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 183 unsigned 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 201 static bool isBadDeallocationArgument(const MemRegion *Arg) { 202 if (!Arg) 203 return false; 204 return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) || 205 isa<TypedRegion>(Arg); 206 } 207 208 /// Given the address expression, retrieve the value it's pointing to. Assume 209 /// that value is itself an address, and return the corresponding symbol. 210 static SymbolRef getAsPointeeSymbol(const Expr *Expr, 211 CheckerContext &C) { 212 ProgramStateRef State = C.getState(); 213 SVal ArgV = State->getSVal(Expr, C.getLocationContext()); 214 215 if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { 216 StoreManager& SM = C.getStoreManager(); 217 SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); 218 if (sym) 219 return sym; 220 } 221 return nullptr; 222 } 223 224 // When checking for error code, we need to consider the following cases: 225 // 1) noErr / [0] 226 // 2) someErr / [1, inf] 227 // 3) unknown 228 // If noError, returns true iff (1). 229 // If !noError, returns true iff (2). 230 bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym, 231 ProgramStateRef State, 232 SValBuilder &Builder, 233 bool noError) const { 234 DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr, 235 Builder.getSymbolManager().getType(RetSym)); 236 DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal, 237 nonloc::SymbolVal(RetSym)); 238 ProgramStateRef ErrState = State->assume(NoErr, noError); 239 return ErrState == State; 240 } 241 242 // Report deallocator mismatch. Remove the region from tracking - reporting a 243 // missing free error after this one is redundant. 244 void MacOSKeychainAPIChecker:: 245 generateDeallocatorMismatchReport(const AllocationPair &AP, 246 const Expr *ArgExpr, 247 CheckerContext &C) const { 248 ProgramStateRef State = C.getState(); 249 State = State->remove<AllocatedData>(AP.first); 250 ExplodedNode *N = C.generateNonFatalErrorNode(State); 251 252 if (!N) 253 return; 254 initBugType(); 255 SmallString<80> sbuf; 256 llvm::raw_svector_ostream os(sbuf); 257 unsigned int PDeallocIdx = 258 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; 259 260 os << "Deallocator doesn't match the allocator: '" 261 << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; 262 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); 263 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); 264 Report->addRange(ArgExpr->getSourceRange()); 265 markInteresting(Report.get(), AP); 266 C.emitReport(std::move(Report)); 267 } 268 269 void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, 270 CheckerContext &C) const { 271 unsigned idx = InvalidIdx; 272 ProgramStateRef State = C.getState(); 273 274 const FunctionDecl *FD = C.getCalleeDecl(CE); 275 if (!FD || FD->getKind() != Decl::Function) 276 return; 277 278 StringRef funName = C.getCalleeName(FD); 279 if (funName.empty()) 280 return; 281 282 // If it is a call to an allocator function, it could be a double allocation. 283 idx = getTrackedFunctionIndex(funName, true); 284 if (idx != InvalidIdx) { 285 unsigned paramIdx = FunctionsToTrack[idx].Param; 286 if (CE->getNumArgs() <= paramIdx) 287 return; 288 289 const Expr *ArgExpr = CE->getArg(paramIdx); 290 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) 291 if (const AllocationState *AS = State->get<AllocatedData>(V)) { 292 if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { 293 // Remove the value from the state. The new symbol will be added for 294 // tracking when the second allocator is processed in checkPostStmt(). 295 State = State->remove<AllocatedData>(V); 296 ExplodedNode *N = C.generateNonFatalErrorNode(State); 297 if (!N) 298 return; 299 initBugType(); 300 SmallString<128> sbuf; 301 llvm::raw_svector_ostream os(sbuf); 302 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 303 os << "Allocated data should be released before another call to " 304 << "the allocator: missing a call to '" 305 << FunctionsToTrack[DIdx].Name 306 << "'."; 307 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); 308 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V)); 309 Report->addRange(ArgExpr->getSourceRange()); 310 Report->markInteresting(AS->Region); 311 C.emitReport(std::move(Report)); 312 } 313 } 314 return; 315 } 316 317 // Is it a call to one of deallocator functions? 318 idx = getTrackedFunctionIndex(funName, false); 319 if (idx == InvalidIdx) 320 return; 321 322 unsigned paramIdx = FunctionsToTrack[idx].Param; 323 if (CE->getNumArgs() <= paramIdx) 324 return; 325 326 // Check the argument to the deallocator. 327 const Expr *ArgExpr = CE->getArg(paramIdx); 328 SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext()); 329 330 // Undef is reported by another checker. 331 if (ArgSVal.isUndef()) 332 return; 333 334 SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); 335 336 // If the argument is coming from the heap, globals, or unknown, do not 337 // report it. 338 bool RegionArgIsBad = false; 339 if (!ArgSM) { 340 if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) 341 return; 342 RegionArgIsBad = true; 343 } 344 345 // Is the argument to the call being tracked? 346 const AllocationState *AS = State->get<AllocatedData>(ArgSM); 347 if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { 348 return; 349 } 350 // If trying to free data which has not been allocated yet, report as a bug. 351 // TODO: We might want a more precise diagnostic for double free 352 // (that would involve tracking all the freed symbols in the checker state). 353 if (!AS || RegionArgIsBad) { 354 // It is possible that this is a false positive - the argument might 355 // have entered as an enclosing function parameter. 356 if (isEnclosingFunctionParam(ArgExpr)) 357 return; 358 359 ExplodedNode *N = C.generateNonFatalErrorNode(State); 360 if (!N) 361 return; 362 initBugType(); 363 auto Report = llvm::make_unique<BugReport>( 364 *BT, "Trying to free data which has not been allocated.", N); 365 Report->addRange(ArgExpr->getSourceRange()); 366 if (AS) 367 Report->markInteresting(AS->Region); 368 C.emitReport(std::move(Report)); 369 return; 370 } 371 372 // Process functions which might deallocate. 373 if (FunctionsToTrack[idx].Kind == PossibleAPI) { 374 375 if (funName == "CFStringCreateWithBytesNoCopy") { 376 const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); 377 // NULL ~ default deallocator, so warn. 378 if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), 379 Expr::NPC_ValueDependentIsNotNull)) { 380 const AllocationPair AP = std::make_pair(ArgSM, AS); 381 generateDeallocatorMismatchReport(AP, ArgExpr, C); 382 return; 383 } 384 // One of the default allocators, so warn. 385 if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { 386 StringRef DeallocatorName = DE->getFoundDecl()->getName(); 387 if (DeallocatorName == "kCFAllocatorDefault" || 388 DeallocatorName == "kCFAllocatorSystemDefault" || 389 DeallocatorName == "kCFAllocatorMalloc") { 390 const AllocationPair AP = std::make_pair(ArgSM, AS); 391 generateDeallocatorMismatchReport(AP, ArgExpr, C); 392 return; 393 } 394 // If kCFAllocatorNull, which does not deallocate, we still have to 395 // find the deallocator. 396 if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") 397 return; 398 } 399 // In all other cases, assume the user supplied a correct deallocator 400 // that will free memory so stop tracking. 401 State = State->remove<AllocatedData>(ArgSM); 402 C.addTransition(State); 403 return; 404 } 405 406 llvm_unreachable("We know of no other possible APIs."); 407 } 408 409 // The call is deallocating a value we previously allocated, so remove it 410 // from the next state. 411 State = State->remove<AllocatedData>(ArgSM); 412 413 // Check if the proper deallocator is used. 414 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 415 if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { 416 const AllocationPair AP = std::make_pair(ArgSM, AS); 417 generateDeallocatorMismatchReport(AP, ArgExpr, C); 418 return; 419 } 420 421 // If the buffer can be null and the return status can be an error, 422 // report a bad call to free. 423 if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) && 424 !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { 425 ExplodedNode *N = C.generateNonFatalErrorNode(State); 426 if (!N) 427 return; 428 initBugType(); 429 auto Report = llvm::make_unique<BugReport>( 430 *BT, "Only call free if a valid (non-NULL) buffer was returned.", N); 431 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(ArgSM)); 432 Report->addRange(ArgExpr->getSourceRange()); 433 Report->markInteresting(AS->Region); 434 C.emitReport(std::move(Report)); 435 return; 436 } 437 438 C.addTransition(State); 439 } 440 441 void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, 442 CheckerContext &C) const { 443 ProgramStateRef State = C.getState(); 444 const FunctionDecl *FD = C.getCalleeDecl(CE); 445 if (!FD || FD->getKind() != Decl::Function) 446 return; 447 448 StringRef funName = C.getCalleeName(FD); 449 450 // If a value has been allocated, add it to the set for tracking. 451 unsigned idx = getTrackedFunctionIndex(funName, true); 452 if (idx == InvalidIdx) 453 return; 454 455 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 456 // If the argument entered as an enclosing function parameter, skip it to 457 // avoid false positives. 458 if (isEnclosingFunctionParam(ArgExpr) && 459 C.getLocationContext()->getParent() == nullptr) 460 return; 461 462 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { 463 // If the argument points to something that's not a symbolic region, it 464 // can be: 465 // - unknown (cannot reason about it) 466 // - undefined (already reported by other checker) 467 // - constant (null - should not be tracked, 468 // other constant will generate a compiler warning) 469 // - goto (should be reported by other checker) 470 471 // The call return value symbol should stay alive for as long as the 472 // allocated value symbol, since our diagnostics depend on the value 473 // returned by the call. Ex: Data should only be freed if noErr was 474 // returned during allocation.) 475 SymbolRef RetStatusSymbol = 476 State->getSVal(CE, C.getLocationContext()).getAsSymbol(); 477 C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); 478 479 // Track the allocated value in the checker state. 480 State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, 481 RetStatusSymbol)); 482 assert(State); 483 C.addTransition(State); 484 } 485 } 486 487 // TODO: This logic is the same as in Malloc checker. 488 const ExplodedNode * 489 MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N, 490 SymbolRef Sym, 491 CheckerContext &C) const { 492 const LocationContext *LeakContext = N->getLocationContext(); 493 // Walk the ExplodedGraph backwards and find the first node that referred to 494 // the tracked symbol. 495 const ExplodedNode *AllocNode = N; 496 497 while (N) { 498 if (!N->getState()->get<AllocatedData>(Sym)) 499 break; 500 // Allocation node, is the last node in the current or parent context in 501 // which the symbol was tracked. 502 const LocationContext *NContext = N->getLocationContext(); 503 if (NContext == LeakContext || 504 NContext->isParentOf(LeakContext)) 505 AllocNode = N; 506 N = N->pred_empty() ? nullptr : *(N->pred_begin()); 507 } 508 509 return AllocNode; 510 } 511 512 std::unique_ptr<BugReport> 513 MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( 514 const AllocationPair &AP, ExplodedNode *N, 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 = nullptr; 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 auto Report = 540 llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing, 541 AllocNode->getLocationContext()->getDecl()); 542 543 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); 544 markInteresting(Report.get(), 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 CheckerProgramPointTag Tag(this, "DeadSymbolsLeak"); 579 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag); 580 if (!N) 581 return; 582 583 // Generate the error reports. 584 for (const auto &P : Errors) 585 C.emitReport(generateAllocatedDataNotReleasedReport(P, N, C)); 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 nullptr; 600 const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym); 601 if (ASPrev) 602 return nullptr; 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