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