Home | History | Annotate | Download | only in Checkers
      1 //==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 // This checker flags misuses of KeyChainAPI. In particular, the password data
     10 // allocated/returned by SecKeychainItemCopyContent,
     11 // SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has
     12 // to be freed using a call to SecKeychainItemFreeContent.
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "ClangSACheckers.h"
     16 #include "clang/StaticAnalyzer/Core/Checker.h"
     17 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
     18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.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 
     23 using namespace clang;
     24 using namespace ento;
     25 
     26 namespace {
     27 class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
     28                                                check::PreStmt<ReturnStmt>,
     29                                                check::PostStmt<CallExpr>,
     30                                                check::EndPath,
     31                                                check::DeadSymbols> {
     32   mutable llvm::OwningPtr<BugType> BT;
     33 
     34 public:
     35   /// AllocationState is a part of the checker specific state together with the
     36   /// MemRegion corresponding to the allocated data.
     37   struct AllocationState {
     38     /// The index of the allocator function.
     39     unsigned int AllocatorIdx;
     40     SymbolRef Region;
     41 
     42     AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) :
     43       AllocatorIdx(Idx),
     44       Region(R) {}
     45 
     46     bool operator==(const AllocationState &X) const {
     47       return (AllocatorIdx == X.AllocatorIdx &&
     48               Region == X.Region);
     49     }
     50 
     51     void Profile(llvm::FoldingSetNodeID &ID) const {
     52       ID.AddInteger(AllocatorIdx);
     53       ID.AddPointer(Region);
     54     }
     55   };
     56 
     57   void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
     58   void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
     59   void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
     60   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
     61   void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
     62 
     63 private:
     64   typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
     65   typedef llvm::SmallVector<AllocationPair, 2> AllocationPairVec;
     66 
     67   enum APIKind {
     68     /// Denotes functions tracked by this checker.
     69     ValidAPI = 0,
     70     /// The functions commonly/mistakenly used in place of the given API.
     71     ErrorAPI = 1,
     72     /// The functions which may allocate the data. These are tracked to reduce
     73     /// the false alarm rate.
     74     PossibleAPI = 2
     75   };
     76   /// Stores the information about the allocator and deallocator functions -
     77   /// these are the functions the checker is tracking.
     78   struct ADFunctionInfo {
     79     const char* Name;
     80     unsigned int Param;
     81     unsigned int DeallocatorIdx;
     82     APIKind Kind;
     83   };
     84   static const unsigned InvalidIdx = 100000;
     85   static const unsigned FunctionsToTrackSize = 8;
     86   static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
     87   /// The value, which represents no error return value for allocator functions.
     88   static const unsigned NoErr = 0;
     89 
     90   /// Given the function name, returns the index of the allocator/deallocator
     91   /// function.
     92   static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator);
     93 
     94   inline void initBugType() const {
     95     if (!BT)
     96       BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API"));
     97   }
     98 
     99   void generateDeallocatorMismatchReport(const AllocationPair &AP,
    100                                          const Expr *ArgExpr,
    101                                          CheckerContext &C) const;
    102 
    103   BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
    104                                                     ExplodedNode *N) const;
    105 
    106   /// Check if RetSym evaluates to an error value in the current state.
    107   bool definitelyReturnedError(SymbolRef RetSym,
    108                                const ProgramState *State,
    109                                SValBuilder &Builder,
    110                                bool noError = false) const;
    111 
    112   /// Check if RetSym evaluates to a NoErr value in the current state.
    113   bool definitelyDidnotReturnError(SymbolRef RetSym,
    114                                    const ProgramState *State,
    115                                    SValBuilder &Builder) const {
    116     return definitelyReturnedError(RetSym, State, Builder, true);
    117   }
    118 
    119   /// The bug visitor which allows us to print extra diagnostics along the
    120   /// BugReport path. For example, showing the allocation site of the leaked
    121   /// region.
    122   class SecKeychainBugVisitor : public BugReporterVisitor {
    123   protected:
    124     // The allocated region symbol tracked by the main analysis.
    125     SymbolRef Sym;
    126 
    127   public:
    128     SecKeychainBugVisitor(SymbolRef S) : Sym(S) {}
    129     virtual ~SecKeychainBugVisitor() {}
    130 
    131     void Profile(llvm::FoldingSetNodeID &ID) const {
    132       static int X = 0;
    133       ID.AddPointer(&X);
    134       ID.AddPointer(Sym);
    135     }
    136 
    137     PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
    138                                    const ExplodedNode *PrevN,
    139                                    BugReporterContext &BRC,
    140                                    BugReport &BR);
    141   };
    142 };
    143 }
    144 
    145 /// ProgramState traits to store the currently allocated (and not yet freed)
    146 /// symbols. This is a map from the allocated content symbol to the
    147 /// corresponding AllocationState.
    148 typedef llvm::ImmutableMap<SymbolRef,
    149                        MacOSKeychainAPIChecker::AllocationState> AllocatedSetTy;
    150 
    151 namespace { struct AllocatedData {}; }
    152 namespace clang { namespace ento {
    153 template<> struct ProgramStateTrait<AllocatedData>
    154     :  public ProgramStatePartialTrait<AllocatedSetTy > {
    155   static void *GDMIndex() { static int index = 0; return &index; }
    156 };
    157 }}
    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>(VD) || isa<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 SymbolRef getSymbolForRegion(CheckerContext &C,
    200                                    const MemRegion *R) {
    201   // Implicit casts (ex: void* -> char*) can turn Symbolic region into element
    202   // region, if that is the case, get the underlining region.
    203   R = R->StripCasts();
    204   if (!isa<SymbolicRegion>(R)) {
    205       return 0;
    206   }
    207   return cast<SymbolicRegion>(R)->getSymbol();
    208 }
    209 
    210 static bool isBadDeallocationArgument(const MemRegion *Arg) {
    211   if (isa<AllocaRegion>(Arg) ||
    212       isa<BlockDataRegion>(Arg) ||
    213       isa<TypedRegion>(Arg)) {
    214     return true;
    215   }
    216   return false;
    217 }
    218 /// Given the address expression, retrieve the value it's pointing to. Assume
    219 /// that value is itself an address, and return the corresponding symbol.
    220 static SymbolRef getAsPointeeSymbol(const Expr *Expr,
    221                                     CheckerContext &C) {
    222   const ProgramState *State = C.getState();
    223   SVal ArgV = State->getSVal(Expr);
    224 
    225   if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) {
    226     StoreManager& SM = C.getStoreManager();
    227     const MemRegion *V = SM.Retrieve(State->getStore(), *X).getAsRegion();
    228     if (V)
    229       return getSymbolForRegion(C, V);
    230   }
    231   return 0;
    232 }
    233 
    234 // When checking for error code, we need to consider the following cases:
    235 // 1) noErr / [0]
    236 // 2) someErr / [1, inf]
    237 // 3) unknown
    238 // If noError, returns true iff (1).
    239 // If !noError, returns true iff (2).
    240 bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym,
    241                                                       const ProgramState *State,
    242                                                       SValBuilder &Builder,
    243                                                       bool noError) const {
    244   DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr,
    245     Builder.getSymbolManager().getType(RetSym));
    246   DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal,
    247                                                      nonloc::SymbolVal(RetSym));
    248   const ProgramState *ErrState = State->assume(NoErr, noError);
    249   if (ErrState == State) {
    250     return true;
    251   }
    252 
    253   return false;
    254 }
    255 
    256 // Report deallocator mismatch. Remove the region from tracking - reporting a
    257 // missing free error after this one is redundant.
    258 void MacOSKeychainAPIChecker::
    259   generateDeallocatorMismatchReport(const AllocationPair &AP,
    260                                     const Expr *ArgExpr,
    261                                     CheckerContext &C) const {
    262   const ProgramState *State = C.getState();
    263   State = State->remove<AllocatedData>(AP.first);
    264   ExplodedNode *N = C.generateNode(State);
    265 
    266   if (!N)
    267     return;
    268   initBugType();
    269   llvm::SmallString<80> sbuf;
    270   llvm::raw_svector_ostream os(sbuf);
    271   unsigned int PDeallocIdx =
    272                FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
    273 
    274   os << "Deallocator doesn't match the allocator: '"
    275      << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
    276   BugReport *Report = new BugReport(*BT, os.str(), N);
    277   Report->addVisitor(new SecKeychainBugVisitor(AP.first));
    278   Report->addRange(ArgExpr->getSourceRange());
    279   C.EmitReport(Report);
    280 }
    281 
    282 void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
    283                                            CheckerContext &C) const {
    284   const ProgramState *State = C.getState();
    285   const Expr *Callee = CE->getCallee();
    286   SVal L = State->getSVal(Callee);
    287   unsigned idx = InvalidIdx;
    288 
    289   const FunctionDecl *funDecl = L.getAsFunctionDecl();
    290   if (!funDecl)
    291     return;
    292   IdentifierInfo *funI = funDecl->getIdentifier();
    293   if (!funI)
    294     return;
    295   StringRef funName = funI->getName();
    296 
    297   // If it is a call to an allocator function, it could be a double allocation.
    298   idx = getTrackedFunctionIndex(funName, true);
    299   if (idx != InvalidIdx) {
    300     const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
    301     if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
    302       if (const AllocationState *AS = State->get<AllocatedData>(V)) {
    303         if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) {
    304           // Remove the value from the state. The new symbol will be added for
    305           // tracking when the second allocator is processed in checkPostStmt().
    306           State = State->remove<AllocatedData>(V);
    307           ExplodedNode *N = C.generateNode(State);
    308           if (!N)
    309             return;
    310           initBugType();
    311           llvm::SmallString<128> sbuf;
    312           llvm::raw_svector_ostream os(sbuf);
    313           unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
    314           os << "Allocated data should be released before another call to "
    315               << "the allocator: missing a call to '"
    316               << FunctionsToTrack[DIdx].Name
    317               << "'.";
    318           BugReport *Report = new BugReport(*BT, os.str(), N);
    319           Report->addVisitor(new SecKeychainBugVisitor(V));
    320           Report->addRange(ArgExpr->getSourceRange());
    321           C.EmitReport(Report);
    322         }
    323       }
    324     return;
    325   }
    326 
    327   // Is it a call to one of deallocator functions?
    328   idx = getTrackedFunctionIndex(funName, false);
    329   if (idx == InvalidIdx)
    330     return;
    331 
    332   // Check the argument to the deallocator.
    333   const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
    334   SVal ArgSVal = State->getSVal(ArgExpr);
    335 
    336   // Undef is reported by another checker.
    337   if (ArgSVal.isUndef())
    338     return;
    339 
    340   const MemRegion *Arg = ArgSVal.getAsRegion();
    341   if (!Arg)
    342     return;
    343 
    344   SymbolRef ArgSM = getSymbolForRegion(C, Arg);
    345   bool RegionArgIsBad = ArgSM ? false : isBadDeallocationArgument(Arg);
    346   // If the argument is coming from the heap, globals, or unknown, do not
    347   // report it.
    348   if (!ArgSM && !RegionArgIsBad)
    349     return;
    350 
    351   // Is the argument to the call being tracked?
    352   const AllocationState *AS = State->get<AllocatedData>(ArgSM);
    353   if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) {
    354     return;
    355   }
    356   // If trying to free data which has not been allocated yet, report as a bug.
    357   // TODO: We might want a more precise diagnostic for double free
    358   // (that would involve tracking all the freed symbols in the checker state).
    359   if (!AS || RegionArgIsBad) {
    360     // It is possible that this is a false positive - the argument might
    361     // have entered as an enclosing function parameter.
    362     if (isEnclosingFunctionParam(ArgExpr))
    363       return;
    364 
    365     ExplodedNode *N = C.generateNode(State);
    366     if (!N)
    367       return;
    368     initBugType();
    369     BugReport *Report = new BugReport(*BT,
    370         "Trying to free data which has not been allocated.", N);
    371     Report->addRange(ArgExpr->getSourceRange());
    372     C.EmitReport(Report);
    373     return;
    374   }
    375 
    376   // Process functions which might deallocate.
    377   if (FunctionsToTrack[idx].Kind == PossibleAPI) {
    378 
    379     if (funName == "CFStringCreateWithBytesNoCopy") {
    380       const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts();
    381       // NULL ~ default deallocator, so warn.
    382       if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(),
    383           Expr::NPC_ValueDependentIsNotNull)) {
    384         const AllocationPair AP = std::make_pair(ArgSM, AS);
    385         generateDeallocatorMismatchReport(AP, ArgExpr, C);
    386         return;
    387       }
    388       // One of the default allocators, so warn.
    389       if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
    390         StringRef DeallocatorName = DE->getFoundDecl()->getName();
    391         if (DeallocatorName == "kCFAllocatorDefault" ||
    392             DeallocatorName == "kCFAllocatorSystemDefault" ||
    393             DeallocatorName == "kCFAllocatorMalloc") {
    394           const AllocationPair AP = std::make_pair(ArgSM, AS);
    395           generateDeallocatorMismatchReport(AP, ArgExpr, C);
    396           return;
    397         }
    398         // If kCFAllocatorNull, which does not deallocate, we still have to
    399         // find the deallocator. Otherwise, assume that the user had written a
    400         // custom deallocator which does the right thing.
    401         if (DE->getFoundDecl()->getName() != "kCFAllocatorNull") {
    402           State = State->remove<AllocatedData>(ArgSM);
    403           C.addTransition(State);
    404           return;
    405         }
    406       }
    407     }
    408     return;
    409   }
    410 
    411   // The call is deallocating a value we previously allocated, so remove it
    412   // from the next state.
    413   State = State->remove<AllocatedData>(ArgSM);
    414 
    415   // Check if the proper deallocator is used.
    416   unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
    417   if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) {
    418     const AllocationPair AP = std::make_pair(ArgSM, AS);
    419     generateDeallocatorMismatchReport(AP, ArgExpr, C);
    420     return;
    421   }
    422 
    423   // If the return status is undefined or is error, report a bad call to free.
    424   if (!definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) {
    425     ExplodedNode *N = C.generateNode(State);
    426     if (!N)
    427       return;
    428     initBugType();
    429     BugReport *Report = new BugReport(*BT,
    430         "Call to free data when error was returned during allocation.", N);
    431     Report->addVisitor(new SecKeychainBugVisitor(ArgSM));
    432     Report->addRange(ArgExpr->getSourceRange());
    433     C.EmitReport(Report);
    434     return;
    435   }
    436 
    437   C.addTransition(State);
    438 }
    439 
    440 void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
    441                                             CheckerContext &C) const {
    442   const ProgramState *State = C.getState();
    443   const Expr *Callee = CE->getCallee();
    444   SVal L = State->getSVal(Callee);
    445 
    446   const FunctionDecl *funDecl = L.getAsFunctionDecl();
    447   if (!funDecl)
    448     return;
    449   IdentifierInfo *funI = funDecl->getIdentifier();
    450   if (!funI)
    451     return;
    452   StringRef funName = funI->getName();
    453 
    454   // If a value has been allocated, add it to the set for tracking.
    455   unsigned idx = getTrackedFunctionIndex(funName, true);
    456   if (idx == InvalidIdx)
    457     return;
    458 
    459   const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
    460   // If the argument entered as an enclosing function parameter, skip it to
    461   // avoid false positives.
    462   if (isEnclosingFunctionParam(ArgExpr))
    463     return;
    464 
    465   if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) {
    466     // If the argument points to something that's not a symbolic region, it
    467     // can be:
    468     //  - unknown (cannot reason about it)
    469     //  - undefined (already reported by other checker)
    470     //  - constant (null - should not be tracked,
    471     //              other constant will generate a compiler warning)
    472     //  - goto (should be reported by other checker)
    473 
    474     // The call return value symbol should stay alive for as long as the
    475     // allocated value symbol, since our diagnostics depend on the value
    476     // returned by the call. Ex: Data should only be freed if noErr was
    477     // returned during allocation.)
    478     SymbolRef RetStatusSymbol = State->getSVal(CE).getAsSymbol();
    479     C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);
    480 
    481     // Track the allocated value in the checker state.
    482     State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
    483                                                          RetStatusSymbol));
    484     assert(State);
    485     C.addTransition(State);
    486   }
    487 }
    488 
    489 void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
    490                                            CheckerContext &C) const {
    491   const Expr *retExpr = S->getRetValue();
    492   if (!retExpr)
    493     return;
    494 
    495   // Check  if the value is escaping through the return.
    496   const ProgramState *state = C.getState();
    497   const MemRegion *V = state->getSVal(retExpr).getAsRegion();
    498   if (!V)
    499     return;
    500   state = state->remove<AllocatedData>(getSymbolForRegion(C, V));
    501 
    502   // Proceed from the new state.
    503   C.addTransition(state);
    504 }
    505 
    506 BugReport *MacOSKeychainAPIChecker::
    507   generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
    508                                          ExplodedNode *N) const {
    509   const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
    510   initBugType();
    511   llvm::SmallString<70> sbuf;
    512   llvm::raw_svector_ostream os(sbuf);
    513 
    514   os << "Allocated data is not released: missing a call to '"
    515       << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
    516   BugReport *Report = new BugReport(*BT, os.str(), N);
    517   Report->addVisitor(new SecKeychainBugVisitor(AP.first));
    518   Report->addRange(SourceRange());
    519   return Report;
    520 }
    521 
    522 void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
    523                                                CheckerContext &C) const {
    524   const ProgramState *State = C.getState();
    525   AllocatedSetTy ASet = State->get<AllocatedData>();
    526   if (ASet.isEmpty())
    527     return;
    528 
    529   bool Changed = false;
    530   AllocationPairVec Errors;
    531   for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) {
    532     if (SR.isLive(I->first))
    533       continue;
    534 
    535     Changed = true;
    536     State = State->remove<AllocatedData>(I->first);
    537     // If the allocated symbol is null or if the allocation call might have
    538     // returned an error, do not report.
    539     if (State->getSymVal(I->first) ||
    540         definitelyReturnedError(I->second.Region, State, C.getSValBuilder()))
    541       continue;
    542     Errors.push_back(std::make_pair(I->first, &I->second));
    543   }
    544   if (!Changed)
    545     return;
    546 
    547   // Generate the new, cleaned up state.
    548   ExplodedNode *N = C.generateNode(State);
    549   if (!N)
    550     return;
    551 
    552   // Generate the error reports.
    553   for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
    554                                                        I != E; ++I) {
    555     C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N));
    556   }
    557 }
    558 
    559 // TODO: Remove this after we ensure that checkDeadSymbols are always called.
    560 void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
    561                                            ExprEngine &Eng) const {
    562   const ProgramState *state = B.getState();
    563   AllocatedSetTy AS = state->get<AllocatedData>();
    564   if (AS.isEmpty())
    565     return;
    566 
    567   // Anything which has been allocated but not freed (nor escaped) will be
    568   // found here, so report it.
    569   bool Changed = false;
    570   AllocationPairVec Errors;
    571   for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) {
    572     Changed = true;
    573     state = state->remove<AllocatedData>(I->first);
    574     // If the allocated symbol is null or if error code was returned at
    575     // allocation, do not report.
    576     if (state->getSymVal(I.getKey()) ||
    577         definitelyReturnedError(I->second.Region, state,
    578                                 Eng.getSValBuilder())) {
    579       continue;
    580     }
    581     Errors.push_back(std::make_pair(I->first, &I->second));
    582   }
    583 
    584   // If no change, do not generate a new state.
    585   if (!Changed)
    586     return;
    587 
    588   ExplodedNode *N = B.generateNode(state);
    589   if (!N)
    590     return;
    591 
    592   // Generate the error reports.
    593   for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
    594                                                        I != E; ++I) {
    595     Eng.getBugReporter().EmitReport(
    596       generateAllocatedDataNotReleasedReport(*I, N));
    597   }
    598 }
    599 
    600 
    601 PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
    602                                                       const ExplodedNode *N,
    603                                                       const ExplodedNode *PrevN,
    604                                                       BugReporterContext &BRC,
    605                                                       BugReport &BR) {
    606   const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
    607   if (!AS)
    608     return 0;
    609   const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym);
    610   if (ASPrev)
    611     return 0;
    612 
    613   // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the
    614   // allocation site.
    615   const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation())
    616                                                             .getStmt());
    617   const FunctionDecl *funDecl = CE->getDirectCallee();
    618   assert(funDecl && "We do not support indirect function calls as of now.");
    619   StringRef funName = funDecl->getName();
    620 
    621   // Get the expression of the corresponding argument.
    622   unsigned Idx = getTrackedFunctionIndex(funName, true);
    623   assert(Idx != InvalidIdx && "This should be a call to an allocator.");
    624   const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param);
    625   PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
    626                              N->getLocationContext());
    627   return new PathDiagnosticEventPiece(Pos, "Data is allocated here.");
    628 }
    629 
    630 void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
    631   mgr.registerChecker<MacOSKeychainAPIChecker>();
    632 }
    633