MacOSKeychainAPIChecker.cpp revision 166d502d5367ceacd1313a33cac43b1048b8524d
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#include "llvm/ADT/SmallString.h"
23
24using namespace clang;
25using namespace ento;
26
27namespace {
28class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
29                                               check::PreStmt<ReturnStmt>,
30                                               check::PostStmt<CallExpr>,
31                                               check::EndPath,
32                                               check::DeadSymbols> {
33  mutable OwningPtr<BugType> BT;
34
35public:
36  /// AllocationState is a part of the checker specific state together with the
37  /// MemRegion corresponding to the allocated data.
38  struct AllocationState {
39    /// The index of the allocator function.
40    unsigned int AllocatorIdx;
41    SymbolRef Region;
42
43    AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) :
44      AllocatorIdx(Idx),
45      Region(R) {}
46
47    bool operator==(const AllocationState &X) const {
48      return (AllocatorIdx == X.AllocatorIdx &&
49              Region == X.Region);
50    }
51
52    void Profile(llvm::FoldingSetNodeID &ID) const {
53      ID.AddInteger(AllocatorIdx);
54      ID.AddPointer(Region);
55    }
56  };
57
58  void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
59  void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
60  void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
61  void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
62  void checkEndPath(CheckerContext &C) const;
63
64private:
65  typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
66  typedef llvm::SmallVector<AllocationPair, 2> AllocationPairVec;
67
68  enum APIKind {
69    /// Denotes functions tracked by this checker.
70    ValidAPI = 0,
71    /// The functions commonly/mistakenly used in place of the given API.
72    ErrorAPI = 1,
73    /// The functions which may allocate the data. These are tracked to reduce
74    /// the false alarm rate.
75    PossibleAPI = 2
76  };
77  /// Stores the information about the allocator and deallocator functions -
78  /// these are the functions the checker is tracking.
79  struct ADFunctionInfo {
80    const char* Name;
81    unsigned int Param;
82    unsigned int DeallocatorIdx;
83    APIKind Kind;
84  };
85  static const unsigned InvalidIdx = 100000;
86  static const unsigned FunctionsToTrackSize = 8;
87  static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
88  /// The value, which represents no error return value for allocator functions.
89  static const unsigned NoErr = 0;
90
91  /// Given the function name, returns the index of the allocator/deallocator
92  /// function.
93  static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator);
94
95  inline void initBugType() const {
96    if (!BT)
97      BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API"));
98  }
99
100  void generateDeallocatorMismatchReport(const AllocationPair &AP,
101                                         const Expr *ArgExpr,
102                                         CheckerContext &C) const;
103
104  /// Find the allocation site for Sym on the path leading to the node N.
105  const Stmt *getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
106                                CheckerContext &C) const;
107
108  BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
109                                                    ExplodedNode *N,
110                                                    CheckerContext &C) const;
111
112  /// Check if RetSym evaluates to an error value in the current state.
113  bool definitelyReturnedError(SymbolRef RetSym,
114                               ProgramStateRef State,
115                               SValBuilder &Builder,
116                               bool noError = false) const;
117
118  /// Check if RetSym evaluates to a NoErr value in the current state.
119  bool definitelyDidnotReturnError(SymbolRef RetSym,
120                                   ProgramStateRef State,
121                                   SValBuilder &Builder) const {
122    return definitelyReturnedError(RetSym, State, Builder, true);
123  }
124
125  /// Mark an AllocationPair interesting for diagnostic reporting.
126  void markInteresting(BugReport *R, const AllocationPair &AP) const {
127    R->markInteresting(AP.first);
128    R->markInteresting(AP.second->Region);
129  }
130
131  /// The bug visitor which allows us to print extra diagnostics along the
132  /// BugReport path. For example, showing the allocation site of the leaked
133  /// region.
134  class SecKeychainBugVisitor
135    : public BugReporterVisitorImpl<SecKeychainBugVisitor> {
136  protected:
137    // The allocated region symbol tracked by the main analysis.
138    SymbolRef Sym;
139
140  public:
141    SecKeychainBugVisitor(SymbolRef S) : Sym(S) {}
142    virtual ~SecKeychainBugVisitor() {}
143
144    void Profile(llvm::FoldingSetNodeID &ID) const {
145      static int X = 0;
146      ID.AddPointer(&X);
147      ID.AddPointer(Sym);
148    }
149
150    PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
151                                   const ExplodedNode *PrevN,
152                                   BugReporterContext &BRC,
153                                   BugReport &BR);
154  };
155};
156}
157
158/// ProgramState traits to store the currently allocated (and not yet freed)
159/// symbols. This is a map from the allocated content symbol to the
160/// corresponding AllocationState.
161REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData,
162                               SymbolRef,
163                               MacOSKeychainAPIChecker::AllocationState)
164
165static bool isEnclosingFunctionParam(const Expr *E) {
166  E = E->IgnoreParenCasts();
167  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
168    const ValueDecl *VD = DRE->getDecl();
169    if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
170      return true;
171  }
172  return false;
173}
174
175const MacOSKeychainAPIChecker::ADFunctionInfo
176  MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
177    {"SecKeychainItemCopyContent", 4, 3, ValidAPI},                       // 0
178    {"SecKeychainFindGenericPassword", 6, 3, ValidAPI},                   // 1
179    {"SecKeychainFindInternetPassword", 13, 3, ValidAPI},                 // 2
180    {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},              // 3
181    {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},             // 4
182    {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},    // 5
183    {"free", 0, InvalidIdx, ErrorAPI},                                    // 6
184    {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},        // 7
185};
186
187unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
188                                                          bool IsAllocator) {
189  for (unsigned I = 0; I < FunctionsToTrackSize; ++I) {
190    ADFunctionInfo FI = FunctionsToTrack[I];
191    if (FI.Name != Name)
192      continue;
193    // Make sure the function is of the right type (allocator vs deallocator).
194    if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
195      return InvalidIdx;
196    if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
197      return InvalidIdx;
198
199    return I;
200  }
201  // The function is not tracked.
202  return InvalidIdx;
203}
204
205static bool isBadDeallocationArgument(const MemRegion *Arg) {
206  if (!Arg)
207    return false;
208  if (isa<AllocaRegion>(Arg) ||
209      isa<BlockDataRegion>(Arg) ||
210      isa<TypedRegion>(Arg)) {
211    return true;
212  }
213  return false;
214}
215
216/// Given the address expression, retrieve the value it's pointing to. Assume
217/// that value is itself an address, and return the corresponding symbol.
218static SymbolRef getAsPointeeSymbol(const Expr *Expr,
219                                    CheckerContext &C) {
220  ProgramStateRef State = C.getState();
221  SVal ArgV = State->getSVal(Expr, C.getLocationContext());
222
223  if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) {
224    StoreManager& SM = C.getStoreManager();
225    SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol();
226    if (sym)
227      return sym;
228  }
229  return 0;
230}
231
232// When checking for error code, we need to consider the following cases:
233// 1) noErr / [0]
234// 2) someErr / [1, inf]
235// 3) unknown
236// If noError, returns true iff (1).
237// If !noError, returns true iff (2).
238bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym,
239                                                      ProgramStateRef State,
240                                                      SValBuilder &Builder,
241                                                      bool noError) const {
242  DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr,
243    Builder.getSymbolManager().getType(RetSym));
244  DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal,
245                                                     nonloc::SymbolVal(RetSym));
246  ProgramStateRef ErrState = State->assume(NoErr, noError);
247  if (ErrState == State) {
248    return true;
249  }
250
251  return false;
252}
253
254// Report deallocator mismatch. Remove the region from tracking - reporting a
255// missing free error after this one is redundant.
256void MacOSKeychainAPIChecker::
257  generateDeallocatorMismatchReport(const AllocationPair &AP,
258                                    const Expr *ArgExpr,
259                                    CheckerContext &C) const {
260  ProgramStateRef State = C.getState();
261  State = State->remove<AllocatedData>(AP.first);
262  ExplodedNode *N = C.addTransition(State);
263
264  if (!N)
265    return;
266  initBugType();
267  SmallString<80> sbuf;
268  llvm::raw_svector_ostream os(sbuf);
269  unsigned int PDeallocIdx =
270               FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
271
272  os << "Deallocator doesn't match the allocator: '"
273     << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
274  BugReport *Report = new BugReport(*BT, os.str(), N);
275  Report->addVisitor(new SecKeychainBugVisitor(AP.first));
276  Report->addRange(ArgExpr->getSourceRange());
277  markInteresting(Report, AP);
278  C.emitReport(Report);
279}
280
281void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
282                                           CheckerContext &C) const {
283  unsigned idx = InvalidIdx;
284  ProgramStateRef State = C.getState();
285
286  const FunctionDecl *FD = C.getCalleeDecl(CE);
287  if (!FD || FD->getKind() != Decl::Function)
288    return;
289
290  StringRef funName = C.getCalleeName(FD);
291  if (funName.empty())
292    return;
293
294  // If it is a call to an allocator function, it could be a double allocation.
295  idx = getTrackedFunctionIndex(funName, true);
296  if (idx != InvalidIdx) {
297    const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
298    if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
299      if (const AllocationState *AS = State->get<AllocatedData>(V)) {
300        if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) {
301          // Remove the value from the state. The new symbol will be added for
302          // tracking when the second allocator is processed in checkPostStmt().
303          State = State->remove<AllocatedData>(V);
304          ExplodedNode *N = C.addTransition(State);
305          if (!N)
306            return;
307          initBugType();
308          SmallString<128> sbuf;
309          llvm::raw_svector_ostream os(sbuf);
310          unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
311          os << "Allocated data should be released before another call to "
312              << "the allocator: missing a call to '"
313              << FunctionsToTrack[DIdx].Name
314              << "'.";
315          BugReport *Report = new BugReport(*BT, os.str(), N);
316          Report->addVisitor(new SecKeychainBugVisitor(V));
317          Report->addRange(ArgExpr->getSourceRange());
318          Report->markInteresting(AS->Region);
319          C.emitReport(Report);
320        }
321      }
322    return;
323  }
324
325  // Is it a call to one of deallocator functions?
326  idx = getTrackedFunctionIndex(funName, false);
327  if (idx == InvalidIdx)
328    return;
329
330  // Check the argument to the deallocator.
331  const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
332  SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext());
333
334  // Undef is reported by another checker.
335  if (ArgSVal.isUndef())
336    return;
337
338  SymbolRef ArgSM = ArgSVal.getAsLocSymbol();
339
340  // If the argument is coming from the heap, globals, or unknown, do not
341  // report it.
342  bool RegionArgIsBad = false;
343  if (!ArgSM) {
344    if (!isBadDeallocationArgument(ArgSVal.getAsRegion()))
345      return;
346    RegionArgIsBad = true;
347  }
348
349  // Is the argument to the call being tracked?
350  const AllocationState *AS = State->get<AllocatedData>(ArgSM);
351  if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) {
352    return;
353  }
354  // If trying to free data which has not been allocated yet, report as a bug.
355  // TODO: We might want a more precise diagnostic for double free
356  // (that would involve tracking all the freed symbols in the checker state).
357  if (!AS || RegionArgIsBad) {
358    // It is possible that this is a false positive - the argument might
359    // have entered as an enclosing function parameter.
360    if (isEnclosingFunctionParam(ArgExpr))
361      return;
362
363    ExplodedNode *N = C.addTransition(State);
364    if (!N)
365      return;
366    initBugType();
367    BugReport *Report = new BugReport(*BT,
368        "Trying to free data which has not been allocated.", N);
369    Report->addRange(ArgExpr->getSourceRange());
370    if (AS)
371      Report->markInteresting(AS->Region);
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 buffer can be null and the return status can be an error,
424  // report a bad call to free.
425  if (State->assume(cast<DefinedSVal>(ArgSVal), false) &&
426      !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) {
427    ExplodedNode *N = C.addTransition(State);
428    if (!N)
429      return;
430    initBugType();
431    BugReport *Report = new BugReport(*BT,
432        "Only call free if a valid (non-NULL) buffer was returned.", N);
433    Report->addVisitor(new SecKeychainBugVisitor(ArgSM));
434    Report->addRange(ArgExpr->getSourceRange());
435    Report->markInteresting(AS->Region);
436    C.emitReport(Report);
437    return;
438  }
439
440  C.addTransition(State);
441}
442
443void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
444                                            CheckerContext &C) const {
445  ProgramStateRef State = C.getState();
446  const FunctionDecl *FD = C.getCalleeDecl(CE);
447  if (!FD || FD->getKind() != Decl::Function)
448    return;
449
450  StringRef funName = C.getCalleeName(FD);
451
452  // If a value has been allocated, add it to the set for tracking.
453  unsigned idx = getTrackedFunctionIndex(funName, true);
454  if (idx == InvalidIdx)
455    return;
456
457  const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
458  // If the argument entered as an enclosing function parameter, skip it to
459  // avoid false positives.
460  if (isEnclosingFunctionParam(ArgExpr) &&
461      C.getLocationContext()->getParent() == 0)
462    return;
463
464  if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) {
465    // If the argument points to something that's not a symbolic region, it
466    // can be:
467    //  - unknown (cannot reason about it)
468    //  - undefined (already reported by other checker)
469    //  - constant (null - should not be tracked,
470    //              other constant will generate a compiler warning)
471    //  - goto (should be reported by other checker)
472
473    // The call return value symbol should stay alive for as long as the
474    // allocated value symbol, since our diagnostics depend on the value
475    // returned by the call. Ex: Data should only be freed if noErr was
476    // returned during allocation.)
477    SymbolRef RetStatusSymbol =
478      State->getSVal(CE, C.getLocationContext()).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
489void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
490                                           CheckerContext &C) const {
491  const Expr *retExpr = S->getRetValue();
492  if (!retExpr)
493    return;
494
495  // If inside inlined call, skip it.
496  const LocationContext *LC = C.getLocationContext();
497  if (LC->getParent() != 0)
498    return;
499
500  // Check  if the value is escaping through the return.
501  ProgramStateRef state = C.getState();
502  SymbolRef sym = state->getSVal(retExpr, LC).getAsLocSymbol();
503  if (!sym)
504    return;
505  state = state->remove<AllocatedData>(sym);
506
507  // Proceed from the new state.
508  C.addTransition(state);
509}
510
511// TODO: This logic is the same as in Malloc checker.
512const Stmt *
513MacOSKeychainAPIChecker::getAllocationSite(const ExplodedNode *N,
514                                           SymbolRef Sym,
515                                           CheckerContext &C) const {
516  const LocationContext *LeakContext = N->getLocationContext();
517  // Walk the ExplodedGraph backwards and find the first node that referred to
518  // the tracked symbol.
519  const ExplodedNode *AllocNode = N;
520
521  while (N) {
522    if (!N->getState()->get<AllocatedData>(Sym))
523      break;
524    // Allocation node, is the last node in the current context in which the
525    // symbol was tracked.
526    if (N->getLocationContext() == LeakContext)
527      AllocNode = N;
528    N = N->pred_empty() ? NULL : *(N->pred_begin());
529  }
530
531  ProgramPoint P = AllocNode->getLocation();
532  if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P))
533    return Exit->getCalleeContext()->getCallSite();
534  if (clang::PostStmt *PS = dyn_cast<clang::PostStmt>(&P))
535    return PS->getStmt();
536  return 0;
537}
538
539BugReport *MacOSKeychainAPIChecker::
540  generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
541                                         ExplodedNode *N,
542                                         CheckerContext &C) const {
543  const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
544  initBugType();
545  SmallString<70> sbuf;
546  llvm::raw_svector_ostream os(sbuf);
547  os << "Allocated data is not released: missing a call to '"
548      << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
549
550  // Most bug reports are cached at the location where they occurred.
551  // With leaks, we want to unique them by the location where they were
552  // allocated, and only report a single path.
553  PathDiagnosticLocation LocUsedForUniqueing;
554  if (const Stmt *AllocStmt = getAllocationSite(N, AP.first, C))
555    LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt,
556                            C.getSourceManager(), N->getLocationContext());
557
558  BugReport *Report = new BugReport(*BT, os.str(), N, LocUsedForUniqueing);
559  Report->addVisitor(new SecKeychainBugVisitor(AP.first));
560  markInteresting(Report, AP);
561  return Report;
562}
563
564void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
565                                               CheckerContext &C) const {
566  ProgramStateRef State = C.getState();
567  AllocatedDataTy ASet = State->get<AllocatedData>();
568  if (ASet.isEmpty())
569    return;
570
571  bool Changed = false;
572  AllocationPairVec Errors;
573  for (AllocatedDataTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) {
574    if (SR.isLive(I->first))
575      continue;
576
577    Changed = true;
578    State = State->remove<AllocatedData>(I->first);
579    // If the allocated symbol is null or if the allocation call might have
580    // returned an error, do not report.
581    ConstraintManager &CMgr = State->getConstraintManager();
582    ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey());
583    if (AllocFailed.isConstrainedTrue() ||
584        definitelyReturnedError(I->second.Region, State, C.getSValBuilder()))
585      continue;
586    Errors.push_back(std::make_pair(I->first, &I->second));
587  }
588  if (!Changed) {
589    // Generate the new, cleaned up state.
590    C.addTransition(State);
591    return;
592  }
593
594  static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : DeadSymbolsLeak");
595  ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag);
596
597  // Generate the error reports.
598  for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
599                                                       I != E; ++I) {
600    C.emitReport(generateAllocatedDataNotReleasedReport(*I, N, C));
601  }
602
603  // Generate the new, cleaned up state.
604  C.addTransition(State, N);
605}
606
607// TODO: Remove this after we ensure that checkDeadSymbols are always called.
608void MacOSKeychainAPIChecker::checkEndPath(CheckerContext &C) const {
609  ProgramStateRef state = C.getState();
610
611  // If inside inlined call, skip it.
612  if (C.getLocationContext()->getParent() != 0)
613    return;
614
615  AllocatedDataTy AS = state->get<AllocatedData>();
616  if (AS.isEmpty())
617    return;
618
619  // Anything which has been allocated but not freed (nor escaped) will be
620  // found here, so report it.
621  bool Changed = false;
622  AllocationPairVec Errors;
623  for (AllocatedDataTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) {
624    Changed = true;
625    state = state->remove<AllocatedData>(I->first);
626    // If the allocated symbol is null or if error code was returned at
627    // allocation, do not report.
628    ConstraintManager &CMgr = state->getConstraintManager();
629    ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey());
630    if (AllocFailed.isConstrainedTrue() ||
631        definitelyReturnedError(I->second.Region, state,
632                                C.getSValBuilder())) {
633      continue;
634    }
635    Errors.push_back(std::make_pair(I->first, &I->second));
636  }
637
638  // If no change, do not generate a new state.
639  if (!Changed) {
640    C.addTransition(state);
641    return;
642  }
643
644  static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : EndPathLeak");
645  ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag);
646
647  // Generate the error reports.
648  for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
649                                                       I != E; ++I) {
650    C.emitReport(generateAllocatedDataNotReleasedReport(*I, N, C));
651  }
652
653  C.addTransition(state, N);
654}
655
656
657PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
658                                                      const ExplodedNode *N,
659                                                      const ExplodedNode *PrevN,
660                                                      BugReporterContext &BRC,
661                                                      BugReport &BR) {
662  const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
663  if (!AS)
664    return 0;
665  const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym);
666  if (ASPrev)
667    return 0;
668
669  // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the
670  // allocation site.
671  const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation())
672                                                            .getStmt());
673  const FunctionDecl *funDecl = CE->getDirectCallee();
674  assert(funDecl && "We do not support indirect function calls as of now.");
675  StringRef funName = funDecl->getName();
676
677  // Get the expression of the corresponding argument.
678  unsigned Idx = getTrackedFunctionIndex(funName, true);
679  assert(Idx != InvalidIdx && "This should be a call to an allocator.");
680  const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param);
681  PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
682                             N->getLocationContext());
683  return new PathDiagnosticEventPiece(Pos, "Data is allocated here.");
684}
685
686void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
687  mgr.registerChecker<MacOSKeychainAPIChecker>();
688}
689