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