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