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