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