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