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