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/CallEvent.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 22 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 23 #include "llvm/ADT/SmallString.h" 24 #include "llvm/Support/raw_ostream.h" 25 26 using namespace clang; 27 using namespace ento; 28 29 namespace { 30 class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, 31 check::PostStmt<CallExpr>, 32 check::DeadSymbols, 33 check::PointerEscape, 34 eval::Assume> { 35 mutable std::unique_ptr<BugType> BT; 36 37 public: 38 /// AllocationState is a part of the checker specific state together with the 39 /// MemRegion corresponding to the allocated data. 40 struct AllocationState { 41 /// The index of the allocator function. 42 unsigned int AllocatorIdx; 43 SymbolRef Region; 44 45 AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) : 46 AllocatorIdx(Idx), 47 Region(R) {} 48 49 bool operator==(const AllocationState &X) const { 50 return (AllocatorIdx == X.AllocatorIdx && 51 Region == X.Region); 52 } 53 54 void Profile(llvm::FoldingSetNodeID &ID) const { 55 ID.AddInteger(AllocatorIdx); 56 ID.AddPointer(Region); 57 } 58 }; 59 60 void checkPreStmt(const CallExpr *S, CheckerContext &C) const; 61 void checkPostStmt(const CallExpr *S, CheckerContext &C) const; 62 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 63 ProgramStateRef checkPointerEscape(ProgramStateRef State, 64 const InvalidatedSymbols &Escaped, 65 const CallEvent *Call, 66 PointerEscapeKind Kind) const; 67 ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, 68 bool Assumption) const; 69 void printState(raw_ostream &Out, ProgramStateRef State, 70 const char *NL, const char *Sep) const; 71 72 private: 73 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; 74 typedef SmallVector<AllocationPair, 2> AllocationPairVec; 75 76 enum APIKind { 77 /// Denotes functions tracked by this checker. 78 ValidAPI = 0, 79 /// The functions commonly/mistakenly used in place of the given API. 80 ErrorAPI = 1, 81 /// The functions which may allocate the data. These are tracked to reduce 82 /// the false alarm rate. 83 PossibleAPI = 2 84 }; 85 /// Stores the information about the allocator and deallocator functions - 86 /// these are the functions the checker is tracking. 87 struct ADFunctionInfo { 88 const char* Name; 89 unsigned int Param; 90 unsigned int DeallocatorIdx; 91 APIKind Kind; 92 }; 93 static const unsigned InvalidIdx = 100000; 94 static const unsigned FunctionsToTrackSize = 8; 95 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; 96 /// The value, which represents no error return value for allocator functions. 97 static const unsigned NoErr = 0; 98 99 /// Given the function name, returns the index of the allocator/deallocator 100 /// function. 101 static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); 102 103 inline void initBugType() const { 104 if (!BT) 105 BT.reset(new BugType(this, "Improper use of SecKeychain API", 106 "API Misuse (Apple)")); 107 } 108 109 void generateDeallocatorMismatchReport(const AllocationPair &AP, 110 const Expr *ArgExpr, 111 CheckerContext &C) const; 112 113 /// Find the allocation site for Sym on the path leading to the node N. 114 const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym, 115 CheckerContext &C) const; 116 117 std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport( 118 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const; 119 120 /// Mark an AllocationPair interesting for diagnostic reporting. 121 void markInteresting(BugReport *R, const AllocationPair &AP) const { 122 R->markInteresting(AP.first); 123 R->markInteresting(AP.second->Region); 124 } 125 126 /// The bug visitor which allows us to print extra diagnostics along the 127 /// BugReport path. For example, showing the allocation site of the leaked 128 /// region. 129 class SecKeychainBugVisitor : public BugReporterVisitor { 130 protected: 131 // The allocated region symbol tracked by the main analysis. 132 SymbolRef Sym; 133 134 public: 135 SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} 136 137 void Profile(llvm::FoldingSetNodeID &ID) const override { 138 static int X = 0; 139 ID.AddPointer(&X); 140 ID.AddPointer(Sym); 141 } 142 143 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, 144 BugReporterContext &BRC, 145 BugReport &BR) override; 146 }; 147 }; 148 } 149 150 /// ProgramState traits to store the currently allocated (and not yet freed) 151 /// symbols. This is a map from the allocated content symbol to the 152 /// corresponding AllocationState. 153 REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData, 154 SymbolRef, 155 MacOSKeychainAPIChecker::AllocationState) 156 157 static bool isEnclosingFunctionParam(const Expr *E) { 158 E = E->IgnoreParenCasts(); 159 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { 160 const ValueDecl *VD = DRE->getDecl(); 161 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) 162 return true; 163 } 164 return false; 165 } 166 167 const MacOSKeychainAPIChecker::ADFunctionInfo 168 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { 169 {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0 170 {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1 171 {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2 172 {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3 173 {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4 174 {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5 175 {"free", 0, InvalidIdx, ErrorAPI}, // 6 176 {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7 177 }; 178 179 unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, 180 bool IsAllocator) { 181 for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { 182 ADFunctionInfo FI = FunctionsToTrack[I]; 183 if (FI.Name != Name) 184 continue; 185 // Make sure the function is of the right type (allocator vs deallocator). 186 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) 187 return InvalidIdx; 188 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) 189 return InvalidIdx; 190 191 return I; 192 } 193 // The function is not tracked. 194 return InvalidIdx; 195 } 196 197 static bool isBadDeallocationArgument(const MemRegion *Arg) { 198 if (!Arg) 199 return false; 200 return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) || 201 isa<TypedRegion>(Arg); 202 } 203 204 /// Given the address expression, retrieve the value it's pointing to. Assume 205 /// that value is itself an address, and return the corresponding symbol. 206 static SymbolRef getAsPointeeSymbol(const Expr *Expr, 207 CheckerContext &C) { 208 ProgramStateRef State = C.getState(); 209 SVal ArgV = C.getSVal(Expr); 210 211 if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { 212 StoreManager& SM = C.getStoreManager(); 213 SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); 214 if (sym) 215 return sym; 216 } 217 return nullptr; 218 } 219 220 // Report deallocator mismatch. Remove the region from tracking - reporting a 221 // missing free error after this one is redundant. 222 void MacOSKeychainAPIChecker:: 223 generateDeallocatorMismatchReport(const AllocationPair &AP, 224 const Expr *ArgExpr, 225 CheckerContext &C) const { 226 ProgramStateRef State = C.getState(); 227 State = State->remove<AllocatedData>(AP.first); 228 ExplodedNode *N = C.generateNonFatalErrorNode(State); 229 230 if (!N) 231 return; 232 initBugType(); 233 SmallString<80> sbuf; 234 llvm::raw_svector_ostream os(sbuf); 235 unsigned int PDeallocIdx = 236 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; 237 238 os << "Deallocator doesn't match the allocator: '" 239 << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; 240 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); 241 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); 242 Report->addRange(ArgExpr->getSourceRange()); 243 markInteresting(Report.get(), AP); 244 C.emitReport(std::move(Report)); 245 } 246 247 void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, 248 CheckerContext &C) const { 249 unsigned idx = InvalidIdx; 250 ProgramStateRef State = C.getState(); 251 252 const FunctionDecl *FD = C.getCalleeDecl(CE); 253 if (!FD || FD->getKind() != Decl::Function) 254 return; 255 256 StringRef funName = C.getCalleeName(FD); 257 if (funName.empty()) 258 return; 259 260 // If it is a call to an allocator function, it could be a double allocation. 261 idx = getTrackedFunctionIndex(funName, true); 262 if (idx != InvalidIdx) { 263 unsigned paramIdx = FunctionsToTrack[idx].Param; 264 if (CE->getNumArgs() <= paramIdx) 265 return; 266 267 const Expr *ArgExpr = CE->getArg(paramIdx); 268 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) 269 if (const AllocationState *AS = State->get<AllocatedData>(V)) { 270 // Remove the value from the state. The new symbol will be added for 271 // tracking when the second allocator is processed in checkPostStmt(). 272 State = State->remove<AllocatedData>(V); 273 ExplodedNode *N = C.generateNonFatalErrorNode(State); 274 if (!N) 275 return; 276 initBugType(); 277 SmallString<128> sbuf; 278 llvm::raw_svector_ostream os(sbuf); 279 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 280 os << "Allocated data should be released before another call to " 281 << "the allocator: missing a call to '" 282 << FunctionsToTrack[DIdx].Name 283 << "'."; 284 auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); 285 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V)); 286 Report->addRange(ArgExpr->getSourceRange()); 287 Report->markInteresting(AS->Region); 288 C.emitReport(std::move(Report)); 289 } 290 return; 291 } 292 293 // Is it a call to one of deallocator functions? 294 idx = getTrackedFunctionIndex(funName, false); 295 if (idx == InvalidIdx) 296 return; 297 298 unsigned paramIdx = FunctionsToTrack[idx].Param; 299 if (CE->getNumArgs() <= paramIdx) 300 return; 301 302 // Check the argument to the deallocator. 303 const Expr *ArgExpr = CE->getArg(paramIdx); 304 SVal ArgSVal = C.getSVal(ArgExpr); 305 306 // Undef is reported by another checker. 307 if (ArgSVal.isUndef()) 308 return; 309 310 SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); 311 312 // If the argument is coming from the heap, globals, or unknown, do not 313 // report it. 314 bool RegionArgIsBad = false; 315 if (!ArgSM) { 316 if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) 317 return; 318 RegionArgIsBad = true; 319 } 320 321 // Is the argument to the call being tracked? 322 const AllocationState *AS = State->get<AllocatedData>(ArgSM); 323 if (!AS) 324 return; 325 326 // TODO: We might want to report double free here. 327 // (that would involve tracking all the freed symbols in the checker state). 328 if (RegionArgIsBad) { 329 // It is possible that this is a false positive - the argument might 330 // have entered as an enclosing function parameter. 331 if (isEnclosingFunctionParam(ArgExpr)) 332 return; 333 334 ExplodedNode *N = C.generateNonFatalErrorNode(State); 335 if (!N) 336 return; 337 initBugType(); 338 auto Report = llvm::make_unique<BugReport>( 339 *BT, "Trying to free data which has not been allocated.", N); 340 Report->addRange(ArgExpr->getSourceRange()); 341 if (AS) 342 Report->markInteresting(AS->Region); 343 C.emitReport(std::move(Report)); 344 return; 345 } 346 347 // Process functions which might deallocate. 348 if (FunctionsToTrack[idx].Kind == PossibleAPI) { 349 350 if (funName == "CFStringCreateWithBytesNoCopy") { 351 const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); 352 // NULL ~ default deallocator, so warn. 353 if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), 354 Expr::NPC_ValueDependentIsNotNull)) { 355 const AllocationPair AP = std::make_pair(ArgSM, AS); 356 generateDeallocatorMismatchReport(AP, ArgExpr, C); 357 return; 358 } 359 // One of the default allocators, so warn. 360 if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { 361 StringRef DeallocatorName = DE->getFoundDecl()->getName(); 362 if (DeallocatorName == "kCFAllocatorDefault" || 363 DeallocatorName == "kCFAllocatorSystemDefault" || 364 DeallocatorName == "kCFAllocatorMalloc") { 365 const AllocationPair AP = std::make_pair(ArgSM, AS); 366 generateDeallocatorMismatchReport(AP, ArgExpr, C); 367 return; 368 } 369 // If kCFAllocatorNull, which does not deallocate, we still have to 370 // find the deallocator. 371 if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") 372 return; 373 } 374 // In all other cases, assume the user supplied a correct deallocator 375 // that will free memory so stop tracking. 376 State = State->remove<AllocatedData>(ArgSM); 377 C.addTransition(State); 378 return; 379 } 380 381 llvm_unreachable("We know of no other possible APIs."); 382 } 383 384 // The call is deallocating a value we previously allocated, so remove it 385 // from the next state. 386 State = State->remove<AllocatedData>(ArgSM); 387 388 // Check if the proper deallocator is used. 389 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 390 if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { 391 const AllocationPair AP = std::make_pair(ArgSM, AS); 392 generateDeallocatorMismatchReport(AP, ArgExpr, C); 393 return; 394 } 395 396 C.addTransition(State); 397 } 398 399 void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, 400 CheckerContext &C) const { 401 ProgramStateRef State = C.getState(); 402 const FunctionDecl *FD = C.getCalleeDecl(CE); 403 if (!FD || FD->getKind() != Decl::Function) 404 return; 405 406 StringRef funName = C.getCalleeName(FD); 407 408 // If a value has been allocated, add it to the set for tracking. 409 unsigned idx = getTrackedFunctionIndex(funName, true); 410 if (idx == InvalidIdx) 411 return; 412 413 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 414 // If the argument entered as an enclosing function parameter, skip it to 415 // avoid false positives. 416 if (isEnclosingFunctionParam(ArgExpr) && 417 C.getLocationContext()->getParent() == nullptr) 418 return; 419 420 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { 421 // If the argument points to something that's not a symbolic region, it 422 // can be: 423 // - unknown (cannot reason about it) 424 // - undefined (already reported by other checker) 425 // - constant (null - should not be tracked, 426 // other constant will generate a compiler warning) 427 // - goto (should be reported by other checker) 428 429 // The call return value symbol should stay alive for as long as the 430 // allocated value symbol, since our diagnostics depend on the value 431 // returned by the call. Ex: Data should only be freed if noErr was 432 // returned during allocation.) 433 SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol(); 434 C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); 435 436 // Track the allocated value in the checker state. 437 State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, 438 RetStatusSymbol)); 439 assert(State); 440 C.addTransition(State); 441 } 442 } 443 444 // TODO: This logic is the same as in Malloc checker. 445 const ExplodedNode * 446 MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N, 447 SymbolRef Sym, 448 CheckerContext &C) const { 449 const LocationContext *LeakContext = N->getLocationContext(); 450 // Walk the ExplodedGraph backwards and find the first node that referred to 451 // the tracked symbol. 452 const ExplodedNode *AllocNode = N; 453 454 while (N) { 455 if (!N->getState()->get<AllocatedData>(Sym)) 456 break; 457 // Allocation node, is the last node in the current or parent context in 458 // which the symbol was tracked. 459 const LocationContext *NContext = N->getLocationContext(); 460 if (NContext == LeakContext || 461 NContext->isParentOf(LeakContext)) 462 AllocNode = N; 463 N = N->pred_empty() ? nullptr : *(N->pred_begin()); 464 } 465 466 return AllocNode; 467 } 468 469 std::unique_ptr<BugReport> 470 MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( 471 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const { 472 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; 473 initBugType(); 474 SmallString<70> sbuf; 475 llvm::raw_svector_ostream os(sbuf); 476 os << "Allocated data is not released: missing a call to '" 477 << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; 478 479 // Most bug reports are cached at the location where they occurred. 480 // With leaks, we want to unique them by the location where they were 481 // allocated, and only report a single path. 482 PathDiagnosticLocation LocUsedForUniqueing; 483 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C); 484 const Stmt *AllocStmt = PathDiagnosticLocation::getStmt(AllocNode); 485 486 if (AllocStmt) 487 LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, 488 C.getSourceManager(), 489 AllocNode->getLocationContext()); 490 491 auto Report = 492 llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing, 493 AllocNode->getLocationContext()->getDecl()); 494 495 Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); 496 markInteresting(Report.get(), AP); 497 return Report; 498 } 499 500 /// If the return symbol is assumed to be error, remove the allocated info 501 /// from consideration. 502 ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State, 503 SVal Cond, 504 bool Assumption) const { 505 AllocatedDataTy AMap = State->get<AllocatedData>(); 506 if (AMap.isEmpty()) 507 return State; 508 509 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr()); 510 if (!CondBSE) 511 return State; 512 BinaryOperator::Opcode OpCode = CondBSE->getOpcode(); 513 if (OpCode != BO_EQ && OpCode != BO_NE) 514 return State; 515 516 // Match for a restricted set of patterns for cmparison of error codes. 517 // Note, the comparisons of type '0 == st' are transformed into SymIntExpr. 518 SymbolRef ReturnSymbol = nullptr; 519 if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) { 520 const llvm::APInt &RHS = SIE->getRHS(); 521 bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) || 522 (OpCode == BO_NE && RHS == NoErr); 523 if (!Assumption) 524 ErrorIsReturned = !ErrorIsReturned; 525 if (ErrorIsReturned) 526 ReturnSymbol = SIE->getLHS(); 527 } 528 529 if (ReturnSymbol) 530 for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { 531 if (ReturnSymbol == I->second.Region) 532 State = State->remove<AllocatedData>(I->first); 533 } 534 535 return State; 536 } 537 538 void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, 539 CheckerContext &C) const { 540 ProgramStateRef State = C.getState(); 541 AllocatedDataTy AMap = State->get<AllocatedData>(); 542 if (AMap.isEmpty()) 543 return; 544 545 bool Changed = false; 546 AllocationPairVec Errors; 547 for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { 548 if (!SR.isDead(I->first)) 549 continue; 550 551 Changed = true; 552 State = State->remove<AllocatedData>(I->first); 553 // If the allocated symbol is null do not report. 554 ConstraintManager &CMgr = State->getConstraintManager(); 555 ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey()); 556 if (AllocFailed.isConstrainedTrue()) 557 continue; 558 Errors.push_back(std::make_pair(I->first, &I->second)); 559 } 560 if (!Changed) { 561 // Generate the new, cleaned up state. 562 C.addTransition(State); 563 return; 564 } 565 566 static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak"); 567 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag); 568 if (!N) 569 return; 570 571 // Generate the error reports. 572 for (const auto &P : Errors) 573 C.emitReport(generateAllocatedDataNotReleasedReport(P, N, C)); 574 575 // Generate the new, cleaned up state. 576 C.addTransition(State, N); 577 } 578 579 ProgramStateRef MacOSKeychainAPIChecker::checkPointerEscape( 580 ProgramStateRef State, const InvalidatedSymbols &Escaped, 581 const CallEvent *Call, PointerEscapeKind Kind) const { 582 // FIXME: This branch doesn't make any sense at all, but it is an overfitted 583 // replacement for a previous overfitted code that was making even less sense. 584 if (!Call || Call->getDecl()) 585 return State; 586 587 for (auto I : State->get<AllocatedData>()) { 588 SymbolRef Sym = I.first; 589 if (Escaped.count(Sym)) 590 State = State->remove<AllocatedData>(Sym); 591 592 // This checker is special. Most checkers in fact only track symbols of 593 // SymbolConjured type, eg. symbols returned from functions such as 594 // malloc(). This checker tracks symbols returned as out-parameters. 595 // 596 // When a function is evaluated conservatively, the out-parameter's pointee 597 // base region gets invalidated with a SymbolConjured. If the base region is 598 // larger than the region we're interested in, the value we're interested in 599 // would be SymbolDerived based on that SymbolConjured. However, such 600 // SymbolDerived will never be listed in the Escaped set when the base 601 // region is invalidated because ExprEngine doesn't know which symbols 602 // were derived from a given symbol, while there can be infinitely many 603 // valid symbols derived from any given symbol. 604 // 605 // Hence the extra boilerplate: remove the derived symbol when its parent 606 // symbol escapes. 607 // 608 if (const auto *SD = dyn_cast<SymbolDerived>(Sym)) { 609 SymbolRef ParentSym = SD->getParentSymbol(); 610 if (Escaped.count(ParentSym)) 611 State = State->remove<AllocatedData>(Sym); 612 } 613 } 614 return State; 615 } 616 617 std::shared_ptr<PathDiagnosticPiece> 618 MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( 619 const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { 620 const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); 621 if (!AS) 622 return nullptr; 623 const AllocationState *ASPrev = 624 N->getFirstPred()->getState()->get<AllocatedData>(Sym); 625 if (ASPrev) 626 return nullptr; 627 628 // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the 629 // allocation site. 630 const CallExpr *CE = 631 cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt()); 632 const FunctionDecl *funDecl = CE->getDirectCallee(); 633 assert(funDecl && "We do not support indirect function calls as of now."); 634 StringRef funName = funDecl->getName(); 635 636 // Get the expression of the corresponding argument. 637 unsigned Idx = getTrackedFunctionIndex(funName, true); 638 assert(Idx != InvalidIdx && "This should be a call to an allocator."); 639 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); 640 PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), 641 N->getLocationContext()); 642 return std::make_shared<PathDiagnosticEventPiece>(Pos, 643 "Data is allocated here."); 644 } 645 646 void MacOSKeychainAPIChecker::printState(raw_ostream &Out, 647 ProgramStateRef State, 648 const char *NL, 649 const char *Sep) const { 650 651 AllocatedDataTy AMap = State->get<AllocatedData>(); 652 653 if (!AMap.isEmpty()) { 654 Out << Sep << "KeychainAPIChecker :" << NL; 655 for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { 656 I.getKey()->dumpToStream(Out); 657 } 658 } 659 } 660 661 662 void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { 663 mgr.registerChecker<MacOSKeychainAPIChecker>(); 664 } 665