MacOSKeychainAPIChecker.cpp revision 226586
1226586Sdim//==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==//
2226586Sdim//
3226586Sdim//                     The LLVM Compiler Infrastructure
4226586Sdim//
5226586Sdim// This file is distributed under the University of Illinois Open Source
6226586Sdim// License. See LICENSE.TXT for details.
7226586Sdim//
8226586Sdim//===----------------------------------------------------------------------===//
9226586Sdim// This checker flags misuses of KeyChainAPI. In particular, the password data
10226586Sdim// allocated/returned by SecKeychainItemCopyContent,
11226586Sdim// SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has
12226586Sdim// to be freed using a call to SecKeychainItemFreeContent.
13226586Sdim//===----------------------------------------------------------------------===//
14226586Sdim
15226586Sdim#include "ClangSACheckers.h"
16226586Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
17226586Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
18226586Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19226586Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20226586Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
21226586Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
22226586Sdim
23226586Sdimusing namespace clang;
24226586Sdimusing namespace ento;
25226586Sdim
26226586Sdimnamespace {
27226586Sdimclass MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
28226586Sdim                                               check::PreStmt<ReturnStmt>,
29226586Sdim                                               check::PostStmt<CallExpr>,
30226586Sdim                                               check::EndPath,
31226586Sdim                                               check::DeadSymbols> {
32226586Sdim  mutable llvm::OwningPtr<BugType> BT;
33226586Sdim
34226586Sdimpublic:
35226586Sdim  /// AllocationState is a part of the checker specific state together with the
36226586Sdim  /// MemRegion corresponding to the allocated data.
37226586Sdim  struct AllocationState {
38226586Sdim    /// The index of the allocator function.
39226586Sdim    unsigned int AllocatorIdx;
40226586Sdim    SymbolRef Region;
41226586Sdim
42226586Sdim    AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) :
43226586Sdim      AllocatorIdx(Idx),
44226586Sdim      Region(R) {}
45226586Sdim
46226586Sdim    bool operator==(const AllocationState &X) const {
47226586Sdim      return (AllocatorIdx == X.AllocatorIdx &&
48226586Sdim              Region == X.Region);
49226586Sdim    }
50226586Sdim
51226586Sdim    void Profile(llvm::FoldingSetNodeID &ID) const {
52226586Sdim      ID.AddInteger(AllocatorIdx);
53226586Sdim      ID.AddPointer(Region);
54226586Sdim    }
55226586Sdim  };
56226586Sdim
57226586Sdim  void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
58226586Sdim  void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
59226586Sdim  void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
60226586Sdim  void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
61226586Sdim  void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
62226586Sdim
63226586Sdimprivate:
64226586Sdim  typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
65226586Sdim  typedef llvm::SmallVector<AllocationPair, 2> AllocationPairVec;
66226586Sdim
67226586Sdim  enum APIKind {
68226586Sdim    /// Denotes functions tracked by this checker.
69226586Sdim    ValidAPI = 0,
70226586Sdim    /// The functions commonly/mistakenly used in place of the given API.
71226586Sdim    ErrorAPI = 1,
72226586Sdim    /// The functions which may allocate the data. These are tracked to reduce
73226586Sdim    /// the false alarm rate.
74226586Sdim    PossibleAPI = 2
75226586Sdim  };
76226586Sdim  /// Stores the information about the allocator and deallocator functions -
77226586Sdim  /// these are the functions the checker is tracking.
78226586Sdim  struct ADFunctionInfo {
79226586Sdim    const char* Name;
80226586Sdim    unsigned int Param;
81226586Sdim    unsigned int DeallocatorIdx;
82226586Sdim    APIKind Kind;
83226586Sdim  };
84226586Sdim  static const unsigned InvalidIdx = 100000;
85226586Sdim  static const unsigned FunctionsToTrackSize = 8;
86226586Sdim  static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
87226586Sdim  /// The value, which represents no error return value for allocator functions.
88226586Sdim  static const unsigned NoErr = 0;
89226586Sdim
90226586Sdim  /// Given the function name, returns the index of the allocator/deallocator
91226586Sdim  /// function.
92226586Sdim  static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator);
93226586Sdim
94226586Sdim  inline void initBugType() const {
95226586Sdim    if (!BT)
96226586Sdim      BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API"));
97226586Sdim  }
98226586Sdim
99226586Sdim  void generateDeallocatorMismatchReport(const AllocationPair &AP,
100226586Sdim                                         const Expr *ArgExpr,
101226586Sdim                                         CheckerContext &C) const;
102226586Sdim
103226586Sdim  BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
104226586Sdim                                                    ExplodedNode *N) const;
105226586Sdim
106226586Sdim  /// Check if RetSym evaluates to an error value in the current state.
107226586Sdim  bool definitelyReturnedError(SymbolRef RetSym,
108226586Sdim                               const ProgramState *State,
109226586Sdim                               SValBuilder &Builder,
110226586Sdim                               bool noError = false) const;
111226586Sdim
112226586Sdim  /// Check if RetSym evaluates to a NoErr value in the current state.
113226586Sdim  bool definitelyDidnotReturnError(SymbolRef RetSym,
114226586Sdim                                   const ProgramState *State,
115226586Sdim                                   SValBuilder &Builder) const {
116226586Sdim    return definitelyReturnedError(RetSym, State, Builder, true);
117226586Sdim  }
118226586Sdim
119226586Sdim  /// The bug visitor which allows us to print extra diagnostics along the
120226586Sdim  /// BugReport path. For example, showing the allocation site of the leaked
121226586Sdim  /// region.
122226586Sdim  class SecKeychainBugVisitor : public BugReporterVisitor {
123226586Sdim  protected:
124226586Sdim    // The allocated region symbol tracked by the main analysis.
125226586Sdim    SymbolRef Sym;
126226586Sdim
127226586Sdim  public:
128226586Sdim    SecKeychainBugVisitor(SymbolRef S) : Sym(S) {}
129226586Sdim    virtual ~SecKeychainBugVisitor() {}
130226586Sdim
131226586Sdim    void Profile(llvm::FoldingSetNodeID &ID) const {
132226586Sdim      static int X = 0;
133226586Sdim      ID.AddPointer(&X);
134226586Sdim      ID.AddPointer(Sym);
135226586Sdim    }
136226586Sdim
137226586Sdim    PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
138226586Sdim                                   const ExplodedNode *PrevN,
139226586Sdim                                   BugReporterContext &BRC,
140226586Sdim                                   BugReport &BR);
141226586Sdim  };
142226586Sdim};
143226586Sdim}
144226586Sdim
145226586Sdim/// ProgramState traits to store the currently allocated (and not yet freed)
146226586Sdim/// symbols. This is a map from the allocated content symbol to the
147226586Sdim/// corresponding AllocationState.
148226586Sdimtypedef llvm::ImmutableMap<SymbolRef,
149226586Sdim                       MacOSKeychainAPIChecker::AllocationState> AllocatedSetTy;
150226586Sdim
151226586Sdimnamespace { struct AllocatedData {}; }
152226586Sdimnamespace clang { namespace ento {
153226586Sdimtemplate<> struct ProgramStateTrait<AllocatedData>
154226586Sdim    :  public ProgramStatePartialTrait<AllocatedSetTy > {
155226586Sdim  static void *GDMIndex() { static int index = 0; return &index; }
156226586Sdim};
157226586Sdim}}
158226586Sdim
159226586Sdimstatic bool isEnclosingFunctionParam(const Expr *E) {
160226586Sdim  E = E->IgnoreParenCasts();
161226586Sdim  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
162226586Sdim    const ValueDecl *VD = DRE->getDecl();
163226586Sdim    if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
164226586Sdim      return true;
165226586Sdim  }
166226586Sdim  return false;
167226586Sdim}
168226586Sdim
169226586Sdimconst MacOSKeychainAPIChecker::ADFunctionInfo
170226586Sdim  MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
171226586Sdim    {"SecKeychainItemCopyContent", 4, 3, ValidAPI},                       // 0
172226586Sdim    {"SecKeychainFindGenericPassword", 6, 3, ValidAPI},                   // 1
173226586Sdim    {"SecKeychainFindInternetPassword", 13, 3, ValidAPI},                 // 2
174226586Sdim    {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},              // 3
175226586Sdim    {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},             // 4
176226586Sdim    {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},    // 5
177226586Sdim    {"free", 0, InvalidIdx, ErrorAPI},                                    // 6
178226586Sdim    {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},        // 7
179226586Sdim};
180226586Sdim
181226586Sdimunsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
182226586Sdim                                                          bool IsAllocator) {
183226586Sdim  for (unsigned I = 0; I < FunctionsToTrackSize; ++I) {
184226586Sdim    ADFunctionInfo FI = FunctionsToTrack[I];
185226586Sdim    if (FI.Name != Name)
186226586Sdim      continue;
187226586Sdim    // Make sure the function is of the right type (allocator vs deallocator).
188226586Sdim    if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
189226586Sdim      return InvalidIdx;
190226586Sdim    if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
191226586Sdim      return InvalidIdx;
192226586Sdim
193226586Sdim    return I;
194226586Sdim  }
195226586Sdim  // The function is not tracked.
196226586Sdim  return InvalidIdx;
197226586Sdim}
198226586Sdim
199226586Sdimstatic SymbolRef getSymbolForRegion(CheckerContext &C,
200226586Sdim                                   const MemRegion *R) {
201226586Sdim  // Implicit casts (ex: void* -> char*) can turn Symbolic region into element
202226586Sdim  // region, if that is the case, get the underlining region.
203226586Sdim  R = R->StripCasts();
204226586Sdim  if (!isa<SymbolicRegion>(R)) {
205226586Sdim      return 0;
206226586Sdim  }
207226586Sdim  return cast<SymbolicRegion>(R)->getSymbol();
208226586Sdim}
209226586Sdim
210226586Sdimstatic bool isBadDeallocationArgument(const MemRegion *Arg) {
211226586Sdim  if (isa<AllocaRegion>(Arg) ||
212226586Sdim      isa<BlockDataRegion>(Arg) ||
213226586Sdim      isa<TypedRegion>(Arg)) {
214226586Sdim    return true;
215226586Sdim  }
216226586Sdim  return false;
217226586Sdim}
218226586Sdim/// Given the address expression, retrieve the value it's pointing to. Assume
219226586Sdim/// that value is itself an address, and return the corresponding symbol.
220226586Sdimstatic SymbolRef getAsPointeeSymbol(const Expr *Expr,
221226586Sdim                                    CheckerContext &C) {
222226586Sdim  const ProgramState *State = C.getState();
223226586Sdim  SVal ArgV = State->getSVal(Expr);
224226586Sdim
225226586Sdim  if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) {
226226586Sdim    StoreManager& SM = C.getStoreManager();
227226586Sdim    const MemRegion *V = SM.Retrieve(State->getStore(), *X).getAsRegion();
228226586Sdim    if (V)
229226586Sdim      return getSymbolForRegion(C, V);
230226586Sdim  }
231226586Sdim  return 0;
232226586Sdim}
233226586Sdim
234226586Sdim// When checking for error code, we need to consider the following cases:
235226586Sdim// 1) noErr / [0]
236226586Sdim// 2) someErr / [1, inf]
237226586Sdim// 3) unknown
238226586Sdim// If noError, returns true iff (1).
239226586Sdim// If !noError, returns true iff (2).
240226586Sdimbool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym,
241226586Sdim                                                      const ProgramState *State,
242226586Sdim                                                      SValBuilder &Builder,
243226586Sdim                                                      bool noError) const {
244226586Sdim  DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr,
245226586Sdim    Builder.getSymbolManager().getType(RetSym));
246226586Sdim  DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal,
247226586Sdim                                                     nonloc::SymbolVal(RetSym));
248226586Sdim  const ProgramState *ErrState = State->assume(NoErr, noError);
249226586Sdim  if (ErrState == State) {
250226586Sdim    return true;
251226586Sdim  }
252226586Sdim
253226586Sdim  return false;
254226586Sdim}
255226586Sdim
256226586Sdim// Report deallocator mismatch. Remove the region from tracking - reporting a
257226586Sdim// missing free error after this one is redundant.
258226586Sdimvoid MacOSKeychainAPIChecker::
259226586Sdim  generateDeallocatorMismatchReport(const AllocationPair &AP,
260226586Sdim                                    const Expr *ArgExpr,
261226586Sdim                                    CheckerContext &C) const {
262226586Sdim  const ProgramState *State = C.getState();
263226586Sdim  State = State->remove<AllocatedData>(AP.first);
264226586Sdim  ExplodedNode *N = C.generateNode(State);
265226586Sdim
266226586Sdim  if (!N)
267226586Sdim    return;
268226586Sdim  initBugType();
269226586Sdim  llvm::SmallString<80> sbuf;
270226586Sdim  llvm::raw_svector_ostream os(sbuf);
271226586Sdim  unsigned int PDeallocIdx =
272226586Sdim               FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
273226586Sdim
274226586Sdim  os << "Deallocator doesn't match the allocator: '"
275226586Sdim     << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
276226586Sdim  BugReport *Report = new BugReport(*BT, os.str(), N);
277226586Sdim  Report->addVisitor(new SecKeychainBugVisitor(AP.first));
278226586Sdim  Report->addRange(ArgExpr->getSourceRange());
279226586Sdim  C.EmitReport(Report);
280226586Sdim}
281226586Sdim
282226586Sdimvoid MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
283226586Sdim                                           CheckerContext &C) const {
284226586Sdim  const ProgramState *State = C.getState();
285226586Sdim  const Expr *Callee = CE->getCallee();
286226586Sdim  SVal L = State->getSVal(Callee);
287226586Sdim  unsigned idx = InvalidIdx;
288226586Sdim
289226586Sdim  const FunctionDecl *funDecl = L.getAsFunctionDecl();
290226586Sdim  if (!funDecl)
291226586Sdim    return;
292226586Sdim  IdentifierInfo *funI = funDecl->getIdentifier();
293226586Sdim  if (!funI)
294226586Sdim    return;
295226586Sdim  StringRef funName = funI->getName();
296226586Sdim
297226586Sdim  // If it is a call to an allocator function, it could be a double allocation.
298226586Sdim  idx = getTrackedFunctionIndex(funName, true);
299226586Sdim  if (idx != InvalidIdx) {
300226586Sdim    const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
301226586Sdim    if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
302226586Sdim      if (const AllocationState *AS = State->get<AllocatedData>(V)) {
303226586Sdim        if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) {
304226586Sdim          // Remove the value from the state. The new symbol will be added for
305226586Sdim          // tracking when the second allocator is processed in checkPostStmt().
306226586Sdim          State = State->remove<AllocatedData>(V);
307226586Sdim          ExplodedNode *N = C.generateNode(State);
308226586Sdim          if (!N)
309226586Sdim            return;
310226586Sdim          initBugType();
311226586Sdim          llvm::SmallString<128> sbuf;
312226586Sdim          llvm::raw_svector_ostream os(sbuf);
313226586Sdim          unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
314226586Sdim          os << "Allocated data should be released before another call to "
315226586Sdim              << "the allocator: missing a call to '"
316226586Sdim              << FunctionsToTrack[DIdx].Name
317226586Sdim              << "'.";
318226586Sdim          BugReport *Report = new BugReport(*BT, os.str(), N);
319226586Sdim          Report->addVisitor(new SecKeychainBugVisitor(V));
320226586Sdim          Report->addRange(ArgExpr->getSourceRange());
321226586Sdim          C.EmitReport(Report);
322226586Sdim        }
323226586Sdim      }
324226586Sdim    return;
325226586Sdim  }
326226586Sdim
327226586Sdim  // Is it a call to one of deallocator functions?
328226586Sdim  idx = getTrackedFunctionIndex(funName, false);
329226586Sdim  if (idx == InvalidIdx)
330226586Sdim    return;
331226586Sdim
332226586Sdim  // Check the argument to the deallocator.
333226586Sdim  const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
334226586Sdim  SVal ArgSVal = State->getSVal(ArgExpr);
335226586Sdim
336226586Sdim  // Undef is reported by another checker.
337226586Sdim  if (ArgSVal.isUndef())
338226586Sdim    return;
339226586Sdim
340226586Sdim  const MemRegion *Arg = ArgSVal.getAsRegion();
341226586Sdim  if (!Arg)
342226586Sdim    return;
343226586Sdim
344226586Sdim  SymbolRef ArgSM = getSymbolForRegion(C, Arg);
345226586Sdim  bool RegionArgIsBad = ArgSM ? false : isBadDeallocationArgument(Arg);
346226586Sdim  // If the argument is coming from the heap, globals, or unknown, do not
347226586Sdim  // report it.
348226586Sdim  if (!ArgSM && !RegionArgIsBad)
349226586Sdim    return;
350226586Sdim
351226586Sdim  // Is the argument to the call being tracked?
352226586Sdim  const AllocationState *AS = State->get<AllocatedData>(ArgSM);
353226586Sdim  if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) {
354226586Sdim    return;
355226586Sdim  }
356226586Sdim  // If trying to free data which has not been allocated yet, report as a bug.
357226586Sdim  // TODO: We might want a more precise diagnostic for double free
358226586Sdim  // (that would involve tracking all the freed symbols in the checker state).
359226586Sdim  if (!AS || RegionArgIsBad) {
360226586Sdim    // It is possible that this is a false positive - the argument might
361226586Sdim    // have entered as an enclosing function parameter.
362226586Sdim    if (isEnclosingFunctionParam(ArgExpr))
363226586Sdim      return;
364226586Sdim
365226586Sdim    ExplodedNode *N = C.generateNode(State);
366226586Sdim    if (!N)
367226586Sdim      return;
368226586Sdim    initBugType();
369226586Sdim    BugReport *Report = new BugReport(*BT,
370226586Sdim        "Trying to free data which has not been allocated.", N);
371226586Sdim    Report->addRange(ArgExpr->getSourceRange());
372226586Sdim    C.EmitReport(Report);
373226586Sdim    return;
374226586Sdim  }
375226586Sdim
376226586Sdim  // Process functions which might deallocate.
377226586Sdim  if (FunctionsToTrack[idx].Kind == PossibleAPI) {
378226586Sdim
379226586Sdim    if (funName == "CFStringCreateWithBytesNoCopy") {
380226586Sdim      const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts();
381226586Sdim      // NULL ~ default deallocator, so warn.
382226586Sdim      if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(),
383226586Sdim          Expr::NPC_ValueDependentIsNotNull)) {
384226586Sdim        const AllocationPair AP = std::make_pair(ArgSM, AS);
385226586Sdim        generateDeallocatorMismatchReport(AP, ArgExpr, C);
386226586Sdim        return;
387226586Sdim      }
388226586Sdim      // One of the default allocators, so warn.
389226586Sdim      if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
390226586Sdim        StringRef DeallocatorName = DE->getFoundDecl()->getName();
391226586Sdim        if (DeallocatorName == "kCFAllocatorDefault" ||
392226586Sdim            DeallocatorName == "kCFAllocatorSystemDefault" ||
393226586Sdim            DeallocatorName == "kCFAllocatorMalloc") {
394226586Sdim          const AllocationPair AP = std::make_pair(ArgSM, AS);
395226586Sdim          generateDeallocatorMismatchReport(AP, ArgExpr, C);
396226586Sdim          return;
397226586Sdim        }
398226586Sdim        // If kCFAllocatorNull, which does not deallocate, we still have to
399226586Sdim        // find the deallocator. Otherwise, assume that the user had written a
400226586Sdim        // custom deallocator which does the right thing.
401226586Sdim        if (DE->getFoundDecl()->getName() != "kCFAllocatorNull") {
402226586Sdim          State = State->remove<AllocatedData>(ArgSM);
403226586Sdim          C.addTransition(State);
404226586Sdim          return;
405226586Sdim        }
406226586Sdim      }
407226586Sdim    }
408226586Sdim    return;
409226586Sdim  }
410226586Sdim
411226586Sdim  // The call is deallocating a value we previously allocated, so remove it
412226586Sdim  // from the next state.
413226586Sdim  State = State->remove<AllocatedData>(ArgSM);
414226586Sdim
415226586Sdim  // Check if the proper deallocator is used.
416226586Sdim  unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
417226586Sdim  if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) {
418226586Sdim    const AllocationPair AP = std::make_pair(ArgSM, AS);
419226586Sdim    generateDeallocatorMismatchReport(AP, ArgExpr, C);
420226586Sdim    return;
421226586Sdim  }
422226586Sdim
423226586Sdim  // If the return status is undefined or is error, report a bad call to free.
424226586Sdim  if (!definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) {
425226586Sdim    ExplodedNode *N = C.generateNode(State);
426226586Sdim    if (!N)
427226586Sdim      return;
428226586Sdim    initBugType();
429226586Sdim    BugReport *Report = new BugReport(*BT,
430226586Sdim        "Call to free data when error was returned during allocation.", N);
431226586Sdim    Report->addVisitor(new SecKeychainBugVisitor(ArgSM));
432226586Sdim    Report->addRange(ArgExpr->getSourceRange());
433226586Sdim    C.EmitReport(Report);
434226586Sdim    return;
435226586Sdim  }
436226586Sdim
437226586Sdim  C.addTransition(State);
438226586Sdim}
439226586Sdim
440226586Sdimvoid MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
441226586Sdim                                            CheckerContext &C) const {
442226586Sdim  const ProgramState *State = C.getState();
443226586Sdim  const Expr *Callee = CE->getCallee();
444226586Sdim  SVal L = State->getSVal(Callee);
445226586Sdim
446226586Sdim  const FunctionDecl *funDecl = L.getAsFunctionDecl();
447226586Sdim  if (!funDecl)
448226586Sdim    return;
449226586Sdim  IdentifierInfo *funI = funDecl->getIdentifier();
450226586Sdim  if (!funI)
451226586Sdim    return;
452226586Sdim  StringRef funName = funI->getName();
453226586Sdim
454226586Sdim  // If a value has been allocated, add it to the set for tracking.
455226586Sdim  unsigned idx = getTrackedFunctionIndex(funName, true);
456226586Sdim  if (idx == InvalidIdx)
457226586Sdim    return;
458226586Sdim
459226586Sdim  const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
460226586Sdim  // If the argument entered as an enclosing function parameter, skip it to
461226586Sdim  // avoid false positives.
462226586Sdim  if (isEnclosingFunctionParam(ArgExpr))
463226586Sdim    return;
464226586Sdim
465226586Sdim  if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) {
466226586Sdim    // If the argument points to something that's not a symbolic region, it
467226586Sdim    // can be:
468226586Sdim    //  - unknown (cannot reason about it)
469226586Sdim    //  - undefined (already reported by other checker)
470226586Sdim    //  - constant (null - should not be tracked,
471226586Sdim    //              other constant will generate a compiler warning)
472226586Sdim    //  - goto (should be reported by other checker)
473226586Sdim
474226586Sdim    // The call return value symbol should stay alive for as long as the
475226586Sdim    // allocated value symbol, since our diagnostics depend on the value
476226586Sdim    // returned by the call. Ex: Data should only be freed if noErr was
477226586Sdim    // returned during allocation.)
478226586Sdim    SymbolRef RetStatusSymbol = State->getSVal(CE).getAsSymbol();
479226586Sdim    C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);
480226586Sdim
481226586Sdim    // Track the allocated value in the checker state.
482226586Sdim    State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
483226586Sdim                                                         RetStatusSymbol));
484226586Sdim    assert(State);
485226586Sdim    C.addTransition(State);
486226586Sdim  }
487226586Sdim}
488226586Sdim
489226586Sdimvoid MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
490226586Sdim                                           CheckerContext &C) const {
491226586Sdim  const Expr *retExpr = S->getRetValue();
492226586Sdim  if (!retExpr)
493226586Sdim    return;
494226586Sdim
495226586Sdim  // Check  if the value is escaping through the return.
496226586Sdim  const ProgramState *state = C.getState();
497226586Sdim  const MemRegion *V = state->getSVal(retExpr).getAsRegion();
498226586Sdim  if (!V)
499226586Sdim    return;
500226586Sdim  state = state->remove<AllocatedData>(getSymbolForRegion(C, V));
501226586Sdim
502226586Sdim  // Proceed from the new state.
503226586Sdim  C.addTransition(state);
504226586Sdim}
505226586Sdim
506226586SdimBugReport *MacOSKeychainAPIChecker::
507226586Sdim  generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
508226586Sdim                                         ExplodedNode *N) const {
509226586Sdim  const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
510226586Sdim  initBugType();
511226586Sdim  llvm::SmallString<70> sbuf;
512226586Sdim  llvm::raw_svector_ostream os(sbuf);
513226586Sdim
514226586Sdim  os << "Allocated data is not released: missing a call to '"
515226586Sdim      << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
516226586Sdim  BugReport *Report = new BugReport(*BT, os.str(), N);
517226586Sdim  Report->addVisitor(new SecKeychainBugVisitor(AP.first));
518226586Sdim  Report->addRange(SourceRange());
519226586Sdim  return Report;
520226586Sdim}
521226586Sdim
522226586Sdimvoid MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
523226586Sdim                                               CheckerContext &C) const {
524226586Sdim  const ProgramState *State = C.getState();
525226586Sdim  AllocatedSetTy ASet = State->get<AllocatedData>();
526226586Sdim  if (ASet.isEmpty())
527226586Sdim    return;
528226586Sdim
529226586Sdim  bool Changed = false;
530226586Sdim  AllocationPairVec Errors;
531226586Sdim  for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) {
532226586Sdim    if (SR.isLive(I->first))
533226586Sdim      continue;
534226586Sdim
535226586Sdim    Changed = true;
536226586Sdim    State = State->remove<AllocatedData>(I->first);
537226586Sdim    // If the allocated symbol is null or if the allocation call might have
538226586Sdim    // returned an error, do not report.
539226586Sdim    if (State->getSymVal(I->first) ||
540226586Sdim        definitelyReturnedError(I->second.Region, State, C.getSValBuilder()))
541226586Sdim      continue;
542226586Sdim    Errors.push_back(std::make_pair(I->first, &I->second));
543226586Sdim  }
544226586Sdim  if (!Changed)
545226586Sdim    return;
546226586Sdim
547226586Sdim  // Generate the new, cleaned up state.
548226586Sdim  ExplodedNode *N = C.generateNode(State);
549226586Sdim  if (!N)
550226586Sdim    return;
551226586Sdim
552226586Sdim  // Generate the error reports.
553226586Sdim  for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
554226586Sdim                                                       I != E; ++I) {
555226586Sdim    C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N));
556226586Sdim  }
557226586Sdim}
558226586Sdim
559226586Sdim// TODO: Remove this after we ensure that checkDeadSymbols are always called.
560226586Sdimvoid MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
561226586Sdim                                           ExprEngine &Eng) const {
562226586Sdim  const ProgramState *state = B.getState();
563226586Sdim  AllocatedSetTy AS = state->get<AllocatedData>();
564226586Sdim  if (AS.isEmpty())
565226586Sdim    return;
566226586Sdim
567226586Sdim  // Anything which has been allocated but not freed (nor escaped) will be
568226586Sdim  // found here, so report it.
569226586Sdim  bool Changed = false;
570226586Sdim  AllocationPairVec Errors;
571226586Sdim  for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) {
572226586Sdim    Changed = true;
573226586Sdim    state = state->remove<AllocatedData>(I->first);
574226586Sdim    // If the allocated symbol is null or if error code was returned at
575226586Sdim    // allocation, do not report.
576226586Sdim    if (state->getSymVal(I.getKey()) ||
577226586Sdim        definitelyReturnedError(I->second.Region, state,
578226586Sdim                                Eng.getSValBuilder())) {
579226586Sdim      continue;
580226586Sdim    }
581226586Sdim    Errors.push_back(std::make_pair(I->first, &I->second));
582226586Sdim  }
583226586Sdim
584226586Sdim  // If no change, do not generate a new state.
585226586Sdim  if (!Changed)
586226586Sdim    return;
587226586Sdim
588226586Sdim  ExplodedNode *N = B.generateNode(state);
589226586Sdim  if (!N)
590226586Sdim    return;
591226586Sdim
592226586Sdim  // Generate the error reports.
593226586Sdim  for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
594226586Sdim                                                       I != E; ++I) {
595226586Sdim    Eng.getBugReporter().EmitReport(
596226586Sdim      generateAllocatedDataNotReleasedReport(*I, N));
597226586Sdim  }
598226586Sdim}
599226586Sdim
600226586Sdim
601226586SdimPathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
602226586Sdim                                                      const ExplodedNode *N,
603226586Sdim                                                      const ExplodedNode *PrevN,
604226586Sdim                                                      BugReporterContext &BRC,
605226586Sdim                                                      BugReport &BR) {
606226586Sdim  const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
607226586Sdim  if (!AS)
608226586Sdim    return 0;
609226586Sdim  const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym);
610226586Sdim  if (ASPrev)
611226586Sdim    return 0;
612226586Sdim
613226586Sdim  // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the
614226586Sdim  // allocation site.
615226586Sdim  const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation())
616226586Sdim                                                            .getStmt());
617226586Sdim  const FunctionDecl *funDecl = CE->getDirectCallee();
618226586Sdim  assert(funDecl && "We do not support indirect function calls as of now.");
619226586Sdim  StringRef funName = funDecl->getName();
620226586Sdim
621226586Sdim  // Get the expression of the corresponding argument.
622226586Sdim  unsigned Idx = getTrackedFunctionIndex(funName, true);
623226586Sdim  assert(Idx != InvalidIdx && "This should be a call to an allocator.");
624226586Sdim  const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param);
625226586Sdim  PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
626226586Sdim                             N->getLocationContext());
627226586Sdim  return new PathDiagnosticEventPiece(Pos, "Data is allocated here.");
628226586Sdim}
629226586Sdim
630226586Sdimvoid ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
631226586Sdim  mgr.registerChecker<MacOSKeychainAPIChecker>();
632226586Sdim}
633