xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp (revision 49b1e38e4bee0f0c6f8b49e1a62d5284084e09e7)
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(CheckerContext &Ctx) 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                                ProgramStateRef 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                                    ProgramStateRef 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   ProgramStateRef State = C.getState();
223   SVal ArgV = State->getSVal(Expr, C.getLocationContext());
224 
225   if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) {
226     StoreManager& SM = C.getStoreManager();
227     const MemRegion *V = SM.getBinding(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                                                       ProgramStateRef 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   ProgramStateRef 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   ProgramStateRef State = C.getState();
263   State = State->remove<AllocatedData>(AP.first);
264   ExplodedNode *N = C.addTransition(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   unsigned idx = InvalidIdx;
285   ProgramStateRef State = C.getState();
286 
287   StringRef funName = C.getCalleeName(CE);
288   if (funName.empty())
289     return;
290 
291   // If it is a call to an allocator function, it could be a double allocation.
292   idx = getTrackedFunctionIndex(funName, true);
293   if (idx != InvalidIdx) {
294     const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
295     if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
296       if (const AllocationState *AS = State->get<AllocatedData>(V)) {
297         if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) {
298           // Remove the value from the state. The new symbol will be added for
299           // tracking when the second allocator is processed in checkPostStmt().
300           State = State->remove<AllocatedData>(V);
301           ExplodedNode *N = C.addTransition(State);
302           if (!N)
303             return;
304           initBugType();
305           llvm::SmallString<128> sbuf;
306           llvm::raw_svector_ostream os(sbuf);
307           unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
308           os << "Allocated data should be released before another call to "
309               << "the allocator: missing a call to '"
310               << FunctionsToTrack[DIdx].Name
311               << "'.";
312           BugReport *Report = new BugReport(*BT, os.str(), N);
313           Report->addVisitor(new SecKeychainBugVisitor(V));
314           Report->addRange(ArgExpr->getSourceRange());
315           C.EmitReport(Report);
316         }
317       }
318     return;
319   }
320 
321   // Is it a call to one of deallocator functions?
322   idx = getTrackedFunctionIndex(funName, false);
323   if (idx == InvalidIdx)
324     return;
325 
326   // Check the argument to the deallocator.
327   const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
328   SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext());
329 
330   // Undef is reported by another checker.
331   if (ArgSVal.isUndef())
332     return;
333 
334   const MemRegion *Arg = ArgSVal.getAsRegion();
335   if (!Arg)
336     return;
337 
338   SymbolRef ArgSM = getSymbolForRegion(C, Arg);
339   bool RegionArgIsBad = ArgSM ? false : isBadDeallocationArgument(Arg);
340   // If the argument is coming from the heap, globals, or unknown, do not
341   // report it.
342   if (!ArgSM && !RegionArgIsBad)
343     return;
344 
345   // Is the argument to the call being tracked?
346   const AllocationState *AS = State->get<AllocatedData>(ArgSM);
347   if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) {
348     return;
349   }
350   // If trying to free data which has not been allocated yet, report as a bug.
351   // TODO: We might want a more precise diagnostic for double free
352   // (that would involve tracking all the freed symbols in the checker state).
353   if (!AS || RegionArgIsBad) {
354     // It is possible that this is a false positive - the argument might
355     // have entered as an enclosing function parameter.
356     if (isEnclosingFunctionParam(ArgExpr))
357       return;
358 
359     ExplodedNode *N = C.addTransition(State);
360     if (!N)
361       return;
362     initBugType();
363     BugReport *Report = new BugReport(*BT,
364         "Trying to free data which has not been allocated.", N);
365     Report->addRange(ArgExpr->getSourceRange());
366     C.EmitReport(Report);
367     return;
368   }
369 
370   // Process functions which might deallocate.
371   if (FunctionsToTrack[idx].Kind == PossibleAPI) {
372 
373     if (funName == "CFStringCreateWithBytesNoCopy") {
374       const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts();
375       // NULL ~ default deallocator, so warn.
376       if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(),
377           Expr::NPC_ValueDependentIsNotNull)) {
378         const AllocationPair AP = std::make_pair(ArgSM, AS);
379         generateDeallocatorMismatchReport(AP, ArgExpr, C);
380         return;
381       }
382       // One of the default allocators, so warn.
383       if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
384         StringRef DeallocatorName = DE->getFoundDecl()->getName();
385         if (DeallocatorName == "kCFAllocatorDefault" ||
386             DeallocatorName == "kCFAllocatorSystemDefault" ||
387             DeallocatorName == "kCFAllocatorMalloc") {
388           const AllocationPair AP = std::make_pair(ArgSM, AS);
389           generateDeallocatorMismatchReport(AP, ArgExpr, C);
390           return;
391         }
392         // If kCFAllocatorNull, which does not deallocate, we still have to
393         // find the deallocator. Otherwise, assume that the user had written a
394         // custom deallocator which does the right thing.
395         if (DE->getFoundDecl()->getName() != "kCFAllocatorNull") {
396           State = State->remove<AllocatedData>(ArgSM);
397           C.addTransition(State);
398           return;
399         }
400       }
401     }
402     return;
403   }
404 
405   // The call is deallocating a value we previously allocated, so remove it
406   // from the next state.
407   State = State->remove<AllocatedData>(ArgSM);
408 
409   // Check if the proper deallocator is used.
410   unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
411   if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) {
412     const AllocationPair AP = std::make_pair(ArgSM, AS);
413     generateDeallocatorMismatchReport(AP, ArgExpr, C);
414     return;
415   }
416 
417   // If the buffer can be null and the return status can be an error,
418   // report a bad call to free.
419   if (State->assume(cast<DefinedSVal>(ArgSVal), false) &&
420       !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) {
421     ExplodedNode *N = C.addTransition(State);
422     if (!N)
423       return;
424     initBugType();
425     BugReport *Report = new BugReport(*BT,
426         "Only call free if a valid (non-NULL) buffer was returned.", N);
427     Report->addVisitor(new SecKeychainBugVisitor(ArgSM));
428     Report->addRange(ArgExpr->getSourceRange());
429     C.EmitReport(Report);
430     return;
431   }
432 
433   C.addTransition(State);
434 }
435 
436 void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
437                                             CheckerContext &C) const {
438   ProgramStateRef State = C.getState();
439   StringRef funName = C.getCalleeName(CE);
440 
441   // If a value has been allocated, add it to the set for tracking.
442   unsigned idx = getTrackedFunctionIndex(funName, true);
443   if (idx == InvalidIdx)
444     return;
445 
446   const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
447   // If the argument entered as an enclosing function parameter, skip it to
448   // avoid false positives.
449   if (isEnclosingFunctionParam(ArgExpr))
450     return;
451 
452   if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) {
453     // If the argument points to something that's not a symbolic region, it
454     // can be:
455     //  - unknown (cannot reason about it)
456     //  - undefined (already reported by other checker)
457     //  - constant (null - should not be tracked,
458     //              other constant will generate a compiler warning)
459     //  - goto (should be reported by other checker)
460 
461     // The call return value symbol should stay alive for as long as the
462     // allocated value symbol, since our diagnostics depend on the value
463     // returned by the call. Ex: Data should only be freed if noErr was
464     // returned during allocation.)
465     SymbolRef RetStatusSymbol =
466       State->getSVal(CE, C.getLocationContext()).getAsSymbol();
467     C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);
468 
469     // Track the allocated value in the checker state.
470     State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
471                                                          RetStatusSymbol));
472     assert(State);
473     C.addTransition(State);
474   }
475 }
476 
477 void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
478                                            CheckerContext &C) const {
479   const Expr *retExpr = S->getRetValue();
480   if (!retExpr)
481     return;
482 
483   // Check  if the value is escaping through the return.
484   ProgramStateRef state = C.getState();
485   const MemRegion *V =
486     state->getSVal(retExpr, C.getLocationContext()).getAsRegion();
487   if (!V)
488     return;
489   state = state->remove<AllocatedData>(getSymbolForRegion(C, V));
490 
491   // Proceed from the new state.
492   C.addTransition(state);
493 }
494 
495 BugReport *MacOSKeychainAPIChecker::
496   generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
497                                          ExplodedNode *N) const {
498   const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
499   initBugType();
500   llvm::SmallString<70> sbuf;
501   llvm::raw_svector_ostream os(sbuf);
502 
503   os << "Allocated data is not released: missing a call to '"
504       << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
505   BugReport *Report = new BugReport(*BT, os.str(), N);
506   Report->addVisitor(new SecKeychainBugVisitor(AP.first));
507   Report->addRange(SourceRange());
508   return Report;
509 }
510 
511 void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
512                                                CheckerContext &C) const {
513   ProgramStateRef State = C.getState();
514   AllocatedSetTy ASet = State->get<AllocatedData>();
515   if (ASet.isEmpty())
516     return;
517 
518   bool Changed = false;
519   AllocationPairVec Errors;
520   for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) {
521     if (SR.isLive(I->first))
522       continue;
523 
524     Changed = true;
525     State = State->remove<AllocatedData>(I->first);
526     // If the allocated symbol is null or if the allocation call might have
527     // returned an error, do not report.
528     if (State->getSymVal(I->first) ||
529         definitelyReturnedError(I->second.Region, State, C.getSValBuilder()))
530       continue;
531     Errors.push_back(std::make_pair(I->first, &I->second));
532   }
533   if (!Changed)
534     return;
535 
536   // Generate the new, cleaned up state.
537   ExplodedNode *N = C.addTransition(State);
538   if (!N)
539     return;
540 
541   // Generate the error reports.
542   for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
543                                                        I != E; ++I) {
544     C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N));
545   }
546 }
547 
548 // TODO: Remove this after we ensure that checkDeadSymbols are always called.
549 void MacOSKeychainAPIChecker::checkEndPath(CheckerContext &Ctx) const {
550   ProgramStateRef state = Ctx.getState();
551   AllocatedSetTy AS = state->get<AllocatedData>();
552   if (AS.isEmpty())
553     return;
554 
555   // Anything which has been allocated but not freed (nor escaped) will be
556   // found here, so report it.
557   bool Changed = false;
558   AllocationPairVec Errors;
559   for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) {
560     Changed = true;
561     state = state->remove<AllocatedData>(I->first);
562     // If the allocated symbol is null or if error code was returned at
563     // allocation, do not report.
564     if (state->getSymVal(I.getKey()) ||
565         definitelyReturnedError(I->second.Region, state,
566                                 Ctx.getSValBuilder())) {
567       continue;
568     }
569     Errors.push_back(std::make_pair(I->first, &I->second));
570   }
571 
572   // If no change, do not generate a new state.
573   if (!Changed)
574     return;
575 
576   ExplodedNode *N = Ctx.addTransition(state);
577   if (!N)
578     return;
579 
580   // Generate the error reports.
581   for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
582                                                        I != E; ++I) {
583     Ctx.EmitReport(generateAllocatedDataNotReleasedReport(*I, N));
584   }
585 }
586 
587 
588 PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
589                                                       const ExplodedNode *N,
590                                                       const ExplodedNode *PrevN,
591                                                       BugReporterContext &BRC,
592                                                       BugReport &BR) {
593   const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
594   if (!AS)
595     return 0;
596   const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym);
597   if (ASPrev)
598     return 0;
599 
600   // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the
601   // allocation site.
602   const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation())
603                                                             .getStmt());
604   const FunctionDecl *funDecl = CE->getDirectCallee();
605   assert(funDecl && "We do not support indirect function calls as of now.");
606   StringRef funName = funDecl->getName();
607 
608   // Get the expression of the corresponding argument.
609   unsigned Idx = getTrackedFunctionIndex(funName, true);
610   assert(Idx != InvalidIdx && "This should be a call to an allocator.");
611   const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param);
612   PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
613                              N->getLocationContext());
614   return new PathDiagnosticEventPiece(Pos, "Data is allocated here.");
615 }
616 
617 void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
618   mgr.registerChecker<MacOSKeychainAPIChecker>();
619 }
620