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