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