brintos

brintos / llvm-project-archived public Read only

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