MacOSKeychainAPIChecker.cpp revision 3e67814381a56e70e68481e91e26f05a43a05ba0
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#include "llvm/ADT/SmallString.h" 23 24using namespace clang; 25using namespace ento; 26 27namespace { 28class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, 29 check::PreStmt<ReturnStmt>, 30 check::PostStmt<CallExpr>, 31 check::EndPath, 32 check::DeadSymbols> { 33 mutable OwningPtr<BugType> BT; 34 35public: 36 /// AllocationState is a part of the checker specific state together with the 37 /// MemRegion corresponding to the allocated data. 38 struct AllocationState { 39 /// The index of the allocator function. 40 unsigned int AllocatorIdx; 41 SymbolRef Region; 42 43 AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) : 44 AllocatorIdx(Idx), 45 Region(R) {} 46 47 bool operator==(const AllocationState &X) const { 48 return (AllocatorIdx == X.AllocatorIdx && 49 Region == X.Region); 50 } 51 52 void Profile(llvm::FoldingSetNodeID &ID) const { 53 ID.AddInteger(AllocatorIdx); 54 ID.AddPointer(Region); 55 } 56 }; 57 58 void checkPreStmt(const CallExpr *S, CheckerContext &C) const; 59 void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; 60 void checkPostStmt(const CallExpr *S, CheckerContext &C) const; 61 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 62 void checkEndPath(CheckerContext &C) const; 63 64private: 65 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; 66 typedef llvm::SmallVector<AllocationPair, 2> AllocationPairVec; 67 68 enum APIKind { 69 /// Denotes functions tracked by this checker. 70 ValidAPI = 0, 71 /// The functions commonly/mistakenly used in place of the given API. 72 ErrorAPI = 1, 73 /// The functions which may allocate the data. These are tracked to reduce 74 /// the false alarm rate. 75 PossibleAPI = 2 76 }; 77 /// Stores the information about the allocator and deallocator functions - 78 /// these are the functions the checker is tracking. 79 struct ADFunctionInfo { 80 const char* Name; 81 unsigned int Param; 82 unsigned int DeallocatorIdx; 83 APIKind Kind; 84 }; 85 static const unsigned InvalidIdx = 100000; 86 static const unsigned FunctionsToTrackSize = 8; 87 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; 88 /// The value, which represents no error return value for allocator functions. 89 static const unsigned NoErr = 0; 90 91 /// Given the function name, returns the index of the allocator/deallocator 92 /// function. 93 static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); 94 95 inline void initBugType() const { 96 if (!BT) 97 BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API")); 98 } 99 100 void generateDeallocatorMismatchReport(const AllocationPair &AP, 101 const Expr *ArgExpr, 102 CheckerContext &C) const; 103 104 /// Find the allocation site for Sym on the path leading to the node N. 105 const Stmt *getAllocationSite(const ExplodedNode *N, SymbolRef Sym, 106 CheckerContext &C) const; 107 108 BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 109 ExplodedNode *N, 110 CheckerContext &C) const; 111 112 /// Check if RetSym evaluates to an error value in the current state. 113 bool definitelyReturnedError(SymbolRef RetSym, 114 ProgramStateRef State, 115 SValBuilder &Builder, 116 bool noError = false) const; 117 118 /// Check if RetSym evaluates to a NoErr value in the current state. 119 bool definitelyDidnotReturnError(SymbolRef RetSym, 120 ProgramStateRef State, 121 SValBuilder &Builder) const { 122 return definitelyReturnedError(RetSym, State, Builder, true); 123 } 124 125 /// Mark an AllocationPair interesting for diagnostic reporting. 126 void markInteresting(BugReport *R, const AllocationPair &AP) const { 127 R->markInteresting(AP.first); 128 R->markInteresting(AP.second->Region); 129 } 130 131 /// The bug visitor which allows us to print extra diagnostics along the 132 /// BugReport path. For example, showing the allocation site of the leaked 133 /// region. 134 class SecKeychainBugVisitor : public BugReporterVisitor { 135 protected: 136 // The allocated region symbol tracked by the main analysis. 137 SymbolRef Sym; 138 139 public: 140 SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} 141 virtual ~SecKeychainBugVisitor() {} 142 143 void Profile(llvm::FoldingSetNodeID &ID) const { 144 static int X = 0; 145 ID.AddPointer(&X); 146 ID.AddPointer(Sym); 147 } 148 149 PathDiagnosticPiece *VisitNode(const ExplodedNode *N, 150 const ExplodedNode *PrevN, 151 BugReporterContext &BRC, 152 BugReport &BR); 153 }; 154}; 155} 156 157/// ProgramState traits to store the currently allocated (and not yet freed) 158/// symbols. This is a map from the allocated content symbol to the 159/// corresponding AllocationState. 160typedef llvm::ImmutableMap<SymbolRef, 161 MacOSKeychainAPIChecker::AllocationState> AllocatedSetTy; 162 163namespace { struct AllocatedData {}; } 164namespace clang { namespace ento { 165template<> struct ProgramStateTrait<AllocatedData> 166 : public ProgramStatePartialTrait<AllocatedSetTy > { 167 static void *GDMIndex() { static int index = 0; return &index; } 168}; 169}} 170 171static bool isEnclosingFunctionParam(const Expr *E) { 172 E = E->IgnoreParenCasts(); 173 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { 174 const ValueDecl *VD = DRE->getDecl(); 175 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) 176 return true; 177 } 178 return false; 179} 180 181const MacOSKeychainAPIChecker::ADFunctionInfo 182 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { 183 {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0 184 {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1 185 {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2 186 {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3 187 {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4 188 {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5 189 {"free", 0, InvalidIdx, ErrorAPI}, // 6 190 {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7 191}; 192 193unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, 194 bool IsAllocator) { 195 for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { 196 ADFunctionInfo FI = FunctionsToTrack[I]; 197 if (FI.Name != Name) 198 continue; 199 // Make sure the function is of the right type (allocator vs deallocator). 200 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) 201 return InvalidIdx; 202 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) 203 return InvalidIdx; 204 205 return I; 206 } 207 // The function is not tracked. 208 return InvalidIdx; 209} 210 211static bool isBadDeallocationArgument(const MemRegion *Arg) { 212 if (!Arg) 213 return false; 214 if (isa<AllocaRegion>(Arg) || 215 isa<BlockDataRegion>(Arg) || 216 isa<TypedRegion>(Arg)) { 217 return true; 218 } 219 return false; 220} 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 ProgramStateRef State = C.getState(); 227 SVal ArgV = State->getSVal(Expr, C.getLocationContext()); 228 229 if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) { 230 StoreManager& SM = C.getStoreManager(); 231 SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); 232 if (sym) 233 return sym; 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 ProgramStateRef 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 ProgramStateRef 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 ProgramStateRef State = C.getState(); 267 State = State->remove<AllocatedData>(AP.first); 268 ExplodedNode *N = C.addTransition(State); 269 270 if (!N) 271 return; 272 initBugType(); 273 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 markInteresting(Report, AP); 284 C.EmitReport(Report); 285} 286 287void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, 288 CheckerContext &C) const { 289 unsigned idx = InvalidIdx; 290 ProgramStateRef State = C.getState(); 291 292 StringRef funName = C.getCalleeName(CE); 293 if (funName.empty()) 294 return; 295 296 // If it is a call to an allocator function, it could be a double allocation. 297 idx = getTrackedFunctionIndex(funName, true); 298 if (idx != InvalidIdx) { 299 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 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(new 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 // Check the argument to the deallocator. 333 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 334 SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext()); 335 336 // Undef is reported by another checker. 337 if (ArgSVal.isUndef()) 338 return; 339 340 SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); 341 342 // If the argument is coming from the heap, globals, or unknown, do not 343 // report it. 344 bool RegionArgIsBad = false; 345 if (!ArgSM) { 346 if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) 347 return; 348 RegionArgIsBad = true; 349 } 350 351 // Is the argument to the call being tracked? 352 const AllocationState *AS = State->get<AllocatedData>(ArgSM); 353 if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { 354 return; 355 } 356 // If trying to free data which has not been allocated yet, report as a bug. 357 // TODO: We might want a more precise diagnostic for double free 358 // (that would involve tracking all the freed symbols in the checker state). 359 if (!AS || RegionArgIsBad) { 360 // It is possible that this is a false positive - the argument might 361 // have entered as an enclosing function parameter. 362 if (isEnclosingFunctionParam(ArgExpr)) 363 return; 364 365 ExplodedNode *N = C.addTransition(State); 366 if (!N) 367 return; 368 initBugType(); 369 BugReport *Report = new BugReport(*BT, 370 "Trying to free data which has not been allocated.", N); 371 Report->addRange(ArgExpr->getSourceRange()); 372 if (AS) 373 Report->markInteresting(AS->Region); 374 C.EmitReport(Report); 375 return; 376 } 377 378 // Process functions which might deallocate. 379 if (FunctionsToTrack[idx].Kind == PossibleAPI) { 380 381 if (funName == "CFStringCreateWithBytesNoCopy") { 382 const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); 383 // NULL ~ default deallocator, so warn. 384 if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), 385 Expr::NPC_ValueDependentIsNotNull)) { 386 const AllocationPair AP = std::make_pair(ArgSM, AS); 387 generateDeallocatorMismatchReport(AP, ArgExpr, C); 388 return; 389 } 390 // One of the default allocators, so warn. 391 if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { 392 StringRef DeallocatorName = DE->getFoundDecl()->getName(); 393 if (DeallocatorName == "kCFAllocatorDefault" || 394 DeallocatorName == "kCFAllocatorSystemDefault" || 395 DeallocatorName == "kCFAllocatorMalloc") { 396 const AllocationPair AP = std::make_pair(ArgSM, AS); 397 generateDeallocatorMismatchReport(AP, ArgExpr, C); 398 return; 399 } 400 // If kCFAllocatorNull, which does not deallocate, we still have to 401 // find the deallocator. Otherwise, assume that the user had written a 402 // custom deallocator which does the right thing. 403 if (DE->getFoundDecl()->getName() != "kCFAllocatorNull") { 404 State = State->remove<AllocatedData>(ArgSM); 405 C.addTransition(State); 406 return; 407 } 408 } 409 } 410 return; 411 } 412 413 // The call is deallocating a value we previously allocated, so remove it 414 // from the next state. 415 State = State->remove<AllocatedData>(ArgSM); 416 417 // Check if the proper deallocator is used. 418 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 419 if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { 420 const AllocationPair AP = std::make_pair(ArgSM, AS); 421 generateDeallocatorMismatchReport(AP, ArgExpr, C); 422 return; 423 } 424 425 // If the buffer can be null and the return status can be an error, 426 // report a bad call to free. 427 if (State->assume(cast<DefinedSVal>(ArgSVal), false) && 428 !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { 429 ExplodedNode *N = C.addTransition(State); 430 if (!N) 431 return; 432 initBugType(); 433 BugReport *Report = new BugReport(*BT, 434 "Only call free if a valid (non-NULL) buffer was returned.", N); 435 Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); 436 Report->addRange(ArgExpr->getSourceRange()); 437 Report->markInteresting(AS->Region); 438 C.EmitReport(Report); 439 return; 440 } 441 442 C.addTransition(State); 443} 444 445void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, 446 CheckerContext &C) const { 447 ProgramStateRef State = C.getState(); 448 StringRef funName = C.getCalleeName(CE); 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() == 0) 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 487void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S, 488 CheckerContext &C) const { 489 const Expr *retExpr = S->getRetValue(); 490 if (!retExpr) 491 return; 492 493 // If inside inlined call, skip it. 494 const LocationContext *LC = C.getLocationContext(); 495 if (LC->getParent() != 0) 496 return; 497 498 // Check if the value is escaping through the return. 499 ProgramStateRef state = C.getState(); 500 SymbolRef sym = state->getSVal(retExpr, LC).getAsLocSymbol(); 501 if (!sym) 502 return; 503 state = state->remove<AllocatedData>(sym); 504 505 // Proceed from the new state. 506 C.addTransition(state); 507} 508 509// TODO: This logic is the same as in Malloc checker. 510const Stmt * 511MacOSKeychainAPIChecker::getAllocationSite(const ExplodedNode *N, 512 SymbolRef Sym, 513 CheckerContext &C) const { 514 const LocationContext *LeakContext = N->getLocationContext(); 515 // Walk the ExplodedGraph backwards and find the first node that referred to 516 // the tracked symbol. 517 const ExplodedNode *AllocNode = N; 518 519 while (N) { 520 if (!N->getState()->get<AllocatedData>(Sym)) 521 break; 522 // Allocation node, is the last node in the current context in which the 523 // symbol was tracked. 524 if (N->getLocationContext() == LeakContext) 525 AllocNode = N; 526 N = N->pred_empty() ? NULL : *(N->pred_begin()); 527 } 528 529 ProgramPoint P = AllocNode->getLocation(); 530 if (!isa<StmtPoint>(P)) 531 return 0; 532 return cast<clang::PostStmt>(P).getStmt(); 533} 534 535BugReport *MacOSKeychainAPIChecker:: 536 generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 537 ExplodedNode *N, 538 CheckerContext &C) const { 539 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; 540 initBugType(); 541 SmallString<70> sbuf; 542 llvm::raw_svector_ostream os(sbuf); 543 os << "Allocated data is not released: missing a call to '" 544 << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; 545 546 // Most bug reports are cached at the location where they occurred. 547 // With leaks, we want to unique them by the location where they were 548 // allocated, and only report a single path. 549 PathDiagnosticLocation LocUsedForUniqueing; 550 if (const Stmt *AllocStmt = getAllocationSite(N, AP.first, C)) 551 LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, 552 C.getSourceManager(), N->getLocationContext()); 553 554 BugReport *Report = new BugReport(*BT, os.str(), N, LocUsedForUniqueing); 555 Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 556 markInteresting(Report, AP); 557 return Report; 558} 559 560void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, 561 CheckerContext &C) const { 562 ProgramStateRef State = C.getState(); 563 AllocatedSetTy ASet = State->get<AllocatedData>(); 564 if (ASet.isEmpty()) 565 return; 566 567 bool Changed = false; 568 AllocationPairVec Errors; 569 for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { 570 if (SR.isLive(I->first)) 571 continue; 572 573 Changed = true; 574 State = State->remove<AllocatedData>(I->first); 575 // If the allocated symbol is null or if the allocation call might have 576 // returned an error, do not report. 577 if (State->getSymVal(I->first) || 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 SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : 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// TODO: Remove this after we ensure that checkDeadSymbols are always called. 602void MacOSKeychainAPIChecker::checkEndPath(CheckerContext &C) const { 603 ProgramStateRef state = C.getState(); 604 605 // If inside inlined call, skip it. 606 if (C.getLocationContext()->getParent() != 0) 607 return; 608 609 AllocatedSetTy AS = state->get<AllocatedData>(); 610 if (AS.isEmpty()) 611 return; 612 613 // Anything which has been allocated but not freed (nor escaped) will be 614 // found here, so report it. 615 bool Changed = false; 616 AllocationPairVec Errors; 617 for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) { 618 Changed = true; 619 state = state->remove<AllocatedData>(I->first); 620 // If the allocated symbol is null or if error code was returned at 621 // allocation, do not report. 622 if (state->getSymVal(I.getKey()) || 623 definitelyReturnedError(I->second.Region, state, 624 C.getSValBuilder())) { 625 continue; 626 } 627 Errors.push_back(std::make_pair(I->first, &I->second)); 628 } 629 630 // If no change, do not generate a new state. 631 if (!Changed) { 632 C.addTransition(state); 633 return; 634 } 635 636 static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : EndPathLeak"); 637 ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); 638 639 // Generate the error reports. 640 for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); 641 I != E; ++I) { 642 C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N, C)); 643 } 644 645 C.addTransition(state, N); 646} 647 648 649PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( 650 const ExplodedNode *N, 651 const ExplodedNode *PrevN, 652 BugReporterContext &BRC, 653 BugReport &BR) { 654 const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); 655 if (!AS) 656 return 0; 657 const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym); 658 if (ASPrev) 659 return 0; 660 661 // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the 662 // allocation site. 663 const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation()) 664 .getStmt()); 665 const FunctionDecl *funDecl = CE->getDirectCallee(); 666 assert(funDecl && "We do not support indirect function calls as of now."); 667 StringRef funName = funDecl->getName(); 668 669 // Get the expression of the corresponding argument. 670 unsigned Idx = getTrackedFunctionIndex(funName, true); 671 assert(Idx != InvalidIdx && "This should be a call to an allocator."); 672 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); 673 PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), 674 N->getLocationContext()); 675 return new PathDiagnosticEventPiece(Pos, "Data is allocated here."); 676} 677 678void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { 679 mgr.registerChecker<MacOSKeychainAPIChecker>(); 680} 681