MacOSKeychainAPIChecker.cpp revision e68b5f1fa73f8404c5d6859a3d8a139fb1da7bbb
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/PathSensitive/CheckerContext.h"
19#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
21
22using namespace clang;
23using namespace ento;
24
25namespace {
26class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
27                                               check::PreStmt<ReturnStmt>,
28                                               check::PostStmt<CallExpr>,
29                                               check::EndPath > {
30public:
31  void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
32  void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
33  void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
34
35  void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
36
37private:
38  static const unsigned InvalidParamVal = 100000;
39
40  /// Given the function name, returns the index of the parameter which will
41  /// be allocated as a result of the call.
42  unsigned getAllocatingFunctionParam(StringRef Name) const {
43    if (Name == "SecKeychainItemCopyContent")
44      return 4;
45    if (Name == "SecKeychainFindGenericPassword")
46      return 6;
47    if (Name == "SecKeychainFindInternetPassword")
48      return 13;
49    return InvalidParamVal;
50  }
51
52  /// Given the function name, returns the index of the parameter which will
53  /// be freed by the function.
54  unsigned getDeallocatingFunctionParam(StringRef Name) const {
55    if (Name == "SecKeychainItemFreeContent")
56      return 1;
57    return InvalidParamVal;
58  }
59};
60}
61
62// GRState traits to store the currently allocated (and not yet freed) symbols.
63typedef llvm::ImmutableSet<SymbolRef> AllocatedSetTy;
64
65namespace { struct AllocatedData {}; }
66namespace clang { namespace ento {
67template<> struct GRStateTrait<AllocatedData>
68    :  public GRStatePartialTrait<AllocatedSetTy > {
69  static void *GDMIndex() { static int index = 0; return &index; }
70};
71}}
72
73void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
74                                           CheckerContext &C) const {
75  const GRState *State = C.getState();
76  const Expr *Callee = CE->getCallee();
77  SVal L = State->getSVal(Callee);
78
79  const FunctionDecl *funDecl = L.getAsFunctionDecl();
80  if (!funDecl)
81    return;
82  IdentifierInfo *funI = funDecl->getIdentifier();
83  if (!funI)
84    return;
85  StringRef funName = funI->getName();
86
87  // If a value has been freed, remove from the list.
88  unsigned idx = getDeallocatingFunctionParam(funName);
89  if (idx != InvalidParamVal) {
90    SymbolRef Param = State->getSVal(CE->getArg(idx)).getAsSymbol();
91    if (!Param)
92      return;
93    if (!State->contains<AllocatedData>(Param)) {
94      // TODO: do we care about this?
95      assert(0 && "Trying to free data which has not been allocated yet.");
96    }
97    State = State->remove<AllocatedData>(Param);
98    C.addTransition(State);
99  }
100}
101
102void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
103                                            CheckerContext &C) const {
104  const GRState *State = C.getState();
105  const Expr *Callee = CE->getCallee();
106  SVal L = State->getSVal(Callee);
107  StoreManager& SM = C.getStoreManager();
108
109  const FunctionDecl *funDecl = L.getAsFunctionDecl();
110  if (!funDecl)
111    return;
112  IdentifierInfo *funI = funDecl->getIdentifier();
113  if (!funI)
114    return;
115  StringRef funName = funI->getName();
116
117  // If a value has been allocated, add it to the set for tracking.
118  unsigned idx = getAllocatingFunctionParam(funName);
119  if (idx != InvalidParamVal) {
120    SVal Param = State->getSVal(CE->getArg(idx));
121    if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&Param)) {
122      // Add the symbolic value, which represents the location of the allocated
123      // data, to the set.
124      SymbolRef V = SM.Retrieve(State->getStore(), *X).getAsSymbol();
125      if (!V)
126        return;
127      State = State->add<AllocatedData>(V);
128
129      // We only need to track the value if the function returned noErr(0), so
130      // bind the return value of the function to 0.
131      SValBuilder &Builder = C.getSValBuilder();
132      SVal ZeroVal = Builder.makeZeroVal(Builder.getContext().CharTy);
133      State = State->BindExpr(CE, ZeroVal);
134      assert(State);
135
136      // Proceed from the new state.
137      C.addTransition(State);
138    }
139  }
140}
141
142void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
143                                           CheckerContext &C) const {
144  const Expr *retExpr = S->getRetValue();
145  if (!retExpr)
146    return;
147
148  // Check  if the value is escaping through the return.
149  const GRState *state = C.getState();
150  SymbolRef V = state->getSVal(retExpr).getAsSymbol();
151  if (!V)
152    return;
153  state->remove<AllocatedData>(V);
154
155}
156
157void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
158                                 ExprEngine &Eng) const {
159  const GRState *state = B.getState();
160  AllocatedSetTy AS = state->get<AllocatedData>();
161
162  // Anything which has been allocated but not freed (nor escaped) will be
163  // found here, so report it.
164  if (!AS.isEmpty()) {
165    assert(0 && "TODO: Report the bug here.");
166  }
167}
168
169void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
170  mgr.registerChecker<MacOSKeychainAPIChecker>();
171}
172