blob: d173c0f1c5e9336454ead029e0abae3dd76586c0 [file] [log] [blame]
Anna Zaksf57be282011-08-01 22:40:01 +00001//==--- 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"
Anna Zaks03826aa2011-08-04 00:26:57 +000018#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
Anna Zaksf57be282011-08-01 22:40:01 +000019#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
28 check::PreStmt<ReturnStmt>,
29 check::PostStmt<CallExpr>,
30 check::EndPath > {
Anna Zaks03826aa2011-08-04 00:26:57 +000031 mutable llvm::OwningPtr<BugType> BT;
32
Anna Zaksf57be282011-08-01 22:40:01 +000033public:
34 void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
35 void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
36 void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
37
38 void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
39
40private:
41 static const unsigned InvalidParamVal = 100000;
42
43 /// Given the function name, returns the index of the parameter which will
44 /// be allocated as a result of the call.
45 unsigned getAllocatingFunctionParam(StringRef Name) const {
46 if (Name == "SecKeychainItemCopyContent")
47 return 4;
48 if (Name == "SecKeychainFindGenericPassword")
49 return 6;
50 if (Name == "SecKeychainFindInternetPassword")
51 return 13;
52 return InvalidParamVal;
53 }
54
55 /// Given the function name, returns the index of the parameter which will
56 /// be freed by the function.
57 unsigned getDeallocatingFunctionParam(StringRef Name) const {
58 if (Name == "SecKeychainItemFreeContent")
59 return 1;
60 return InvalidParamVal;
61 }
Anna Zaks03826aa2011-08-04 00:26:57 +000062
63 inline void initBugType() const {
64 if (!BT)
65 BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API"));
66 }
Anna Zaksf57be282011-08-01 22:40:01 +000067};
68}
69
Anna Zaks03826aa2011-08-04 00:26:57 +000070struct AllocationInfo {
71 const Expr *Address;
72
73 AllocationInfo(const Expr *E) : Address(E) {}
74 bool operator==(const AllocationInfo &X) const {
75 return Address == X.Address;
76 }
77 void Profile(llvm::FoldingSetNodeID &ID) const {
78 ID.AddPointer(Address);
79 }
80};
81
Anna Zaksf57be282011-08-01 22:40:01 +000082// GRState traits to store the currently allocated (and not yet freed) symbols.
Anna Zaks03826aa2011-08-04 00:26:57 +000083typedef llvm::ImmutableMap<const MemRegion*, AllocationInfo> AllocatedSetTy;
Anna Zaksf57be282011-08-01 22:40:01 +000084
85namespace { struct AllocatedData {}; }
86namespace clang { namespace ento {
87template<> struct GRStateTrait<AllocatedData>
88 : public GRStatePartialTrait<AllocatedSetTy > {
89 static void *GDMIndex() { static int index = 0; return &index; }
90};
91}}
92
Anna Zaks03826aa2011-08-04 00:26:57 +000093static bool isEnclosingFunctionParam(const Expr *E) {
94 E = E->IgnoreParenCasts();
95 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
96 const ValueDecl *VD = DRE->getDecl();
97 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
98 return true;
99 }
100 return false;
101}
102
Anna Zaksf57be282011-08-01 22:40:01 +0000103void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
104 CheckerContext &C) const {
105 const GRState *State = C.getState();
106 const Expr *Callee = CE->getCallee();
107 SVal L = State->getSVal(Callee);
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 freed, remove from the list.
118 unsigned idx = getDeallocatingFunctionParam(funName);
Anna Zaks08551b52011-08-04 00:31:38 +0000119 if (idx == InvalidParamVal)
120 return;
121
122 const Expr *ArgExpr = CE->getArg(idx);
123 const MemRegion *Arg = State->getSVal(ArgExpr).getAsRegion();
124 if (!Arg)
125 return;
126
127 // If trying to free data which has not been allocated yet, report as bug.
128 if (State->get<AllocatedData>(Arg) == 0) {
129 // It is possible that this is a false positive - the argument might
130 // have entered as an enclosing function parameter.
131 if (isEnclosingFunctionParam(ArgExpr))
Anna Zaksf57be282011-08-01 22:40:01 +0000132 return;
Anna Zaks03826aa2011-08-04 00:26:57 +0000133
Anna Zaks08551b52011-08-04 00:31:38 +0000134 ExplodedNode *N = C.generateNode(State);
135 if (!N)
136 return;
137 initBugType();
138 RangedBugReport *Report = new RangedBugReport(*BT,
139 "Trying to free data which has not been allocated.", N);
140 Report->addRange(ArgExpr->getSourceRange());
141 C.EmitReport(Report);
Anna Zaksf57be282011-08-01 22:40:01 +0000142 }
Anna Zaks08551b52011-08-04 00:31:38 +0000143
144 // Continue exploring from the new state.
145 State = State->remove<AllocatedData>(Arg);
146 C.addTransition(State);
Anna Zaksf57be282011-08-01 22:40:01 +0000147}
148
149void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
150 CheckerContext &C) const {
151 const GRState *State = C.getState();
152 const Expr *Callee = CE->getCallee();
153 SVal L = State->getSVal(Callee);
154 StoreManager& SM = C.getStoreManager();
155
156 const FunctionDecl *funDecl = L.getAsFunctionDecl();
157 if (!funDecl)
158 return;
159 IdentifierInfo *funI = funDecl->getIdentifier();
160 if (!funI)
161 return;
162 StringRef funName = funI->getName();
163
164 // If a value has been allocated, add it to the set for tracking.
165 unsigned idx = getAllocatingFunctionParam(funName);
Anna Zaks08551b52011-08-04 00:31:38 +0000166 if (idx == InvalidParamVal)
167 return;
Anna Zaks03826aa2011-08-04 00:26:57 +0000168
Anna Zaks08551b52011-08-04 00:31:38 +0000169 SVal Arg = State->getSVal(CE->getArg(idx));
170 if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&Arg)) {
171 // Add the symbolic value, which represents the location of the allocated
172 // data, to the set.
173 const MemRegion *V = SM.Retrieve(State->getStore(), *X).getAsRegion();
174 // If this is not a region, it can be:
175 // - unknown (cannot reason about it)
176 // - undefined (already reported by other checker)
177 // - constant (null - should not be tracked, other - report a warning?)
178 // - goto (should be reported by other checker)
179 if (!V)
180 return;
Anna Zakse68b5f12011-08-02 17:11:03 +0000181
Anna Zaks08551b52011-08-04 00:31:38 +0000182 State = State->set<AllocatedData>(V, AllocationInfo(CE->getArg(idx)));
Anna Zakse68b5f12011-08-02 17:11:03 +0000183
Anna Zaks08551b52011-08-04 00:31:38 +0000184 // We only need to track the value if the function returned noErr(0), so
185 // bind the return value of the function to 0.
186 SValBuilder &Builder = C.getSValBuilder();
187 SVal ZeroVal = Builder.makeZeroVal(Builder.getContext().CharTy);
188 State = State->BindExpr(CE, ZeroVal);
189 assert(State);
190
191 // Proceed from the new state.
192 C.addTransition(State);
Anna Zaksf57be282011-08-01 22:40:01 +0000193 }
194}
195
196void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
197 CheckerContext &C) const {
198 const Expr *retExpr = S->getRetValue();
199 if (!retExpr)
200 return;
201
202 // Check if the value is escaping through the return.
203 const GRState *state = C.getState();
Anna Zaks03826aa2011-08-04 00:26:57 +0000204 const MemRegion *V = state->getSVal(retExpr).getAsRegion();
Anna Zaksf57be282011-08-01 22:40:01 +0000205 if (!V)
206 return;
Anna Zaks03826aa2011-08-04 00:26:57 +0000207 state = state->remove<AllocatedData>(V);
Anna Zaksf57be282011-08-01 22:40:01 +0000208
Anna Zaks03826aa2011-08-04 00:26:57 +0000209 // Proceed from the new state.
210 C.addTransition(state);
Anna Zaksf57be282011-08-01 22:40:01 +0000211}
212
213void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
Anna Zaks03826aa2011-08-04 00:26:57 +0000214 ExprEngine &Eng) const {
Anna Zaksf57be282011-08-01 22:40:01 +0000215 const GRState *state = B.getState();
216 AllocatedSetTy AS = state->get<AllocatedData>();
Anna Zaks03826aa2011-08-04 00:26:57 +0000217 ExplodedNode *N = B.generateNode(state);
218 if (!N)
219 return;
220 initBugType();
Anna Zaksf57be282011-08-01 22:40:01 +0000221
222 // Anything which has been allocated but not freed (nor escaped) will be
223 // found here, so report it.
Anna Zaks03826aa2011-08-04 00:26:57 +0000224 for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) {
225 RangedBugReport *Report = new RangedBugReport(*BT,
226 "Missing a call to SecKeychainItemFreeContent.", N);
227 // TODO: The report has to mention the expression which contains the
228 // allocated content as well as the point at which it has been allocated.
229 // Currently, the next line is useless.
230 Report->addRange(I->second.Address->getSourceRange());
231 Eng.getBugReporter().EmitReport(Report);
Anna Zaksf57be282011-08-01 22:40:01 +0000232 }
233}
234
235void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
236 mgr.registerChecker<MacOSKeychainAPIChecker>();
237}