blob: d4fbce71987e08d1cc3d9b3c5fb1bfd818201c23 [file] [log] [blame]
Anna Zaks083fcb22011-08-04 17:28:06 +00001//==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==//
Anna Zaksf57be282011-08-01 22:40:01 +00002//
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:
Anna Zaks083fcb22011-08-04 17:28:06 +000041 /// Stores the information about the allocator and deallocator functions -
42 /// these are the functions the checker is tracking.
43 struct ADFunctionInfo {
44 const char* Name;
45 unsigned int Param;
46 unsigned int DeallocatorIdx;
47 };
48 static const unsigned InvalidIdx = 100000;
Anna Zaks76cbb752011-08-04 21:53:01 +000049 static const unsigned FunctionsToTrackSize = 6;
Anna Zaks083fcb22011-08-04 17:28:06 +000050 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
Anna Zaksf57be282011-08-01 22:40:01 +000051
Anna Zaks083fcb22011-08-04 17:28:06 +000052 /// Given the function name, returns the index of the allocator/deallocator
53 /// function.
54 unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator) const;
Anna Zaks03826aa2011-08-04 00:26:57 +000055
56 inline void initBugType() const {
57 if (!BT)
58 BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API"));
59 }
Anna Zaksf57be282011-08-01 22:40:01 +000060};
61}
62
Anna Zaks083fcb22011-08-04 17:28:06 +000063/// AllocationState is a part of the checker specific state together with the
64/// MemRegion corresponding to the allocated data.
65struct AllocationState {
Anna Zaks03826aa2011-08-04 00:26:57 +000066 const Expr *Address;
Anna Zaks083fcb22011-08-04 17:28:06 +000067 /// The index of the allocator function.
68 unsigned int AllocatorIdx;
Anna Zaks03826aa2011-08-04 00:26:57 +000069
Anna Zaks083fcb22011-08-04 17:28:06 +000070 AllocationState(const Expr *E, unsigned int Idx) : Address(E),
71 AllocatorIdx(Idx) {}
72 bool operator==(const AllocationState &X) const {
Anna Zaks03826aa2011-08-04 00:26:57 +000073 return Address == X.Address;
74 }
75 void Profile(llvm::FoldingSetNodeID &ID) const {
76 ID.AddPointer(Address);
Anna Zaks083fcb22011-08-04 17:28:06 +000077 ID.AddInteger(AllocatorIdx);
Anna Zaks03826aa2011-08-04 00:26:57 +000078 }
79};
80
Anna Zaks083fcb22011-08-04 17:28:06 +000081/// GRState traits to store the currently allocated (and not yet freed) symbols.
82typedef llvm::ImmutableMap<const MemRegion*, AllocationState> AllocatedSetTy;
Anna Zaksf57be282011-08-01 22:40:01 +000083
84namespace { struct AllocatedData {}; }
85namespace clang { namespace ento {
86template<> struct GRStateTrait<AllocatedData>
87 : public GRStatePartialTrait<AllocatedSetTy > {
88 static void *GDMIndex() { static int index = 0; return &index; }
89};
90}}
91
Anna Zaks03826aa2011-08-04 00:26:57 +000092static bool isEnclosingFunctionParam(const Expr *E) {
93 E = E->IgnoreParenCasts();
94 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
95 const ValueDecl *VD = DRE->getDecl();
96 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
97 return true;
98 }
99 return false;
100}
101
Anna Zaks083fcb22011-08-04 17:28:06 +0000102const MacOSKeychainAPIChecker::ADFunctionInfo
103 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
104 {"SecKeychainItemCopyContent", 4, 3}, // 0
105 {"SecKeychainFindGenericPassword", 6, 3}, // 1
106 {"SecKeychainFindInternetPassword", 13, 3}, // 2
107 {"SecKeychainItemFreeContent", 1, InvalidIdx}, // 3
Anna Zaks76cbb752011-08-04 21:53:01 +0000108 {"SecKeychainItemCopyAttributesAndData", 5, 5}, // 4
109 {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx}, // 5
Anna Zaks083fcb22011-08-04 17:28:06 +0000110};
111
112unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
113 bool IsAllocator) const {
114 for (unsigned I = 0; I < FunctionsToTrackSize; ++I) {
115 ADFunctionInfo FI = FunctionsToTrack[I];
116 if (FI.Name != Name)
117 continue;
118 // Make sure the function is of the right type (allocator vs deallocator).
119 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
120 return InvalidIdx;
121 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
122 return InvalidIdx;
123
124 return I;
125 }
126 // The function is not tracked.
127 return InvalidIdx;
128}
129
Anna Zaksca0b57e2011-08-05 00:37:00 +0000130/// Given the address expression, retrieve the value it's pointing to. Assume
131/// that value is itself an address, and return the corresponding MemRegion.
132static const MemRegion *getAsPointeeMemoryRegion(const Expr *Expr,
133 CheckerContext &C) {
134 const GRState *State = C.getState();
135 SVal ArgV = State->getSVal(Expr);
136 if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) {
137 StoreManager& SM = C.getStoreManager();
138 const MemRegion *V = SM.Retrieve(State->getStore(), *X).getAsRegion();
139 return V;
140 }
141 return 0;
142}
143
Anna Zaksf57be282011-08-01 22:40:01 +0000144void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
145 CheckerContext &C) const {
146 const GRState *State = C.getState();
147 const Expr *Callee = CE->getCallee();
148 SVal L = State->getSVal(Callee);
Anna Zaksca0b57e2011-08-05 00:37:00 +0000149 unsigned idx = InvalidIdx;
Anna Zaksf57be282011-08-01 22:40:01 +0000150
151 const FunctionDecl *funDecl = L.getAsFunctionDecl();
152 if (!funDecl)
153 return;
154 IdentifierInfo *funI = funDecl->getIdentifier();
155 if (!funI)
156 return;
157 StringRef funName = funI->getName();
158
Anna Zaksca0b57e2011-08-05 00:37:00 +0000159 // If it is a call to an allocator function, it could be a double allocation.
160 idx = getTrackedFunctionIndex(funName, true);
161 if (idx != InvalidIdx) {
162 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
163 if (const MemRegion *V = getAsPointeeMemoryRegion(ArgExpr, C))
164 if (const AllocationState *AS = State->get<AllocatedData>(V)) {
165 ExplodedNode *N = C.generateSink(State);
166 if (!N)
167 return;
168 initBugType();
169 std::string sbuf;
170 llvm::raw_string_ostream os(sbuf);
171 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
172 os << "Allocated data should be released before another call to "
173 << "the allocator: missing a call to '"
174 << FunctionsToTrack[DIdx].Name
175 << "'.";
176 RangedBugReport *Report = new RangedBugReport(*BT, os.str(), N);
177 Report->addRange(ArgExpr->getSourceRange());
178 C.EmitReport(Report);
179 }
180 return;
181 }
182
183 // Is it a call to one of deallocator functions?
184 idx = getTrackedFunctionIndex(funName, false);
Anna Zaks083fcb22011-08-04 17:28:06 +0000185 if (idx == InvalidIdx)
Anna Zaks08551b52011-08-04 00:31:38 +0000186 return;
187
Anna Zaks083fcb22011-08-04 17:28:06 +0000188 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
Anna Zaks08551b52011-08-04 00:31:38 +0000189 const MemRegion *Arg = State->getSVal(ArgExpr).getAsRegion();
190 if (!Arg)
191 return;
192
193 // If trying to free data which has not been allocated yet, report as bug.
Anna Zaks083fcb22011-08-04 17:28:06 +0000194 const AllocationState *AS = State->get<AllocatedData>(Arg);
195 if (!AS) {
Anna Zaks08551b52011-08-04 00:31:38 +0000196 // It is possible that this is a false positive - the argument might
197 // have entered as an enclosing function parameter.
198 if (isEnclosingFunctionParam(ArgExpr))
Anna Zaksf57be282011-08-01 22:40:01 +0000199 return;
Anna Zaks03826aa2011-08-04 00:26:57 +0000200
Anna Zaks08551b52011-08-04 00:31:38 +0000201 ExplodedNode *N = C.generateNode(State);
202 if (!N)
203 return;
204 initBugType();
205 RangedBugReport *Report = new RangedBugReport(*BT,
206 "Trying to free data which has not been allocated.", N);
207 Report->addRange(ArgExpr->getSourceRange());
208 C.EmitReport(Report);
Anna Zaks083fcb22011-08-04 17:28:06 +0000209 return;
Anna Zaksf57be282011-08-01 22:40:01 +0000210 }
Anna Zaks08551b52011-08-04 00:31:38 +0000211
Anna Zaks76cbb752011-08-04 21:53:01 +0000212 // Check if the proper deallocator is used.
213 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
214 if (PDeallocIdx != idx) {
215 ExplodedNode *N = C.generateSink(State);
216 if (!N)
217 return;
218 initBugType();
219
220 std::string sbuf;
221 llvm::raw_string_ostream os(sbuf);
222 os << "Allocator doesn't match the deallocator: '"
223 << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
224 RangedBugReport *Report = new RangedBugReport(*BT, os.str(), N);
225 Report->addRange(ArgExpr->getSourceRange());
226 C.EmitReport(Report);
227 return;
228 }
229
Anna Zaksca0b57e2011-08-05 00:37:00 +0000230 // If a value has been freed, remove it from the list and continue exploring
231 // from the new state.
Anna Zaks08551b52011-08-04 00:31:38 +0000232 State = State->remove<AllocatedData>(Arg);
233 C.addTransition(State);
Anna Zaksf57be282011-08-01 22:40:01 +0000234}
235
236void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
237 CheckerContext &C) const {
238 const GRState *State = C.getState();
239 const Expr *Callee = CE->getCallee();
240 SVal L = State->getSVal(Callee);
Anna Zaksf57be282011-08-01 22:40:01 +0000241
242 const FunctionDecl *funDecl = L.getAsFunctionDecl();
243 if (!funDecl)
244 return;
245 IdentifierInfo *funI = funDecl->getIdentifier();
246 if (!funI)
247 return;
248 StringRef funName = funI->getName();
249
250 // If a value has been allocated, add it to the set for tracking.
Anna Zaks083fcb22011-08-04 17:28:06 +0000251 unsigned idx = getTrackedFunctionIndex(funName, true);
252 if (idx == InvalidIdx)
Anna Zaks08551b52011-08-04 00:31:38 +0000253 return;
Anna Zaks03826aa2011-08-04 00:26:57 +0000254
Anna Zaks083fcb22011-08-04 17:28:06 +0000255 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
Anna Zaksca0b57e2011-08-05 00:37:00 +0000256 if (const MemRegion *V = getAsPointeeMemoryRegion(ArgExpr, C)) {
257 // If the argument points to something that's not a region, it can be:
Anna Zaks08551b52011-08-04 00:31:38 +0000258 // - unknown (cannot reason about it)
259 // - undefined (already reported by other checker)
Anna Zaks083fcb22011-08-04 17:28:06 +0000260 // - constant (null - should not be tracked,
261 // other constant will generate a compiler warning)
Anna Zaks08551b52011-08-04 00:31:38 +0000262 // - goto (should be reported by other checker)
Anna Zakse68b5f12011-08-02 17:11:03 +0000263
Anna Zaks08551b52011-08-04 00:31:38 +0000264 // We only need to track the value if the function returned noErr(0), so
Anna Zaks76cbb752011-08-04 21:53:01 +0000265 // bind the return value of the function to 0 and proceed from the no error
266 // state.
Anna Zaks08551b52011-08-04 00:31:38 +0000267 SValBuilder &Builder = C.getSValBuilder();
Anna Zaks76cbb752011-08-04 21:53:01 +0000268 SVal ZeroVal = Builder.makeIntVal(0, CE->getCallReturnType());
269 const GRState *NoErr = State->BindExpr(CE, ZeroVal);
Anna Zaksca0b57e2011-08-05 00:37:00 +0000270 // Add the symbolic value V, which represents the location of the allocated
271 // data, to the set.
Anna Zaks76cbb752011-08-04 21:53:01 +0000272 NoErr = NoErr->set<AllocatedData>(V, AllocationState(ArgExpr, idx));
273 assert(NoErr);
274 C.addTransition(NoErr);
Anna Zaks08551b52011-08-04 00:31:38 +0000275
Anna Zaks76cbb752011-08-04 21:53:01 +0000276 // Generate a transition to explore the state space when there is an error.
277 // In this case, we do not track the allocated data.
278 SVal ReturnedError = Builder.evalBinOpNN(State, BO_NE,
279 cast<NonLoc>(ZeroVal),
280 cast<NonLoc>(State->getSVal(CE)),
281 CE->getCallReturnType());
282 const GRState *Err = State->assume(cast<NonLoc>(ReturnedError), true);
283 assert(Err);
284 C.addTransition(Err);
Anna Zaksf57be282011-08-01 22:40:01 +0000285 }
286}
287
288void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
289 CheckerContext &C) const {
290 const Expr *retExpr = S->getRetValue();
291 if (!retExpr)
292 return;
293
294 // Check if the value is escaping through the return.
295 const GRState *state = C.getState();
Anna Zaks03826aa2011-08-04 00:26:57 +0000296 const MemRegion *V = state->getSVal(retExpr).getAsRegion();
Anna Zaksf57be282011-08-01 22:40:01 +0000297 if (!V)
298 return;
Anna Zaks03826aa2011-08-04 00:26:57 +0000299 state = state->remove<AllocatedData>(V);
Anna Zaksf57be282011-08-01 22:40:01 +0000300
Anna Zaks03826aa2011-08-04 00:26:57 +0000301 // Proceed from the new state.
302 C.addTransition(state);
Anna Zaksf57be282011-08-01 22:40:01 +0000303}
304
305void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
Anna Zaks03826aa2011-08-04 00:26:57 +0000306 ExprEngine &Eng) const {
Anna Zaksf57be282011-08-01 22:40:01 +0000307 const GRState *state = B.getState();
308 AllocatedSetTy AS = state->get<AllocatedData>();
Anna Zaks03826aa2011-08-04 00:26:57 +0000309 ExplodedNode *N = B.generateNode(state);
310 if (!N)
311 return;
312 initBugType();
Anna Zaksf57be282011-08-01 22:40:01 +0000313
314 // Anything which has been allocated but not freed (nor escaped) will be
315 // found here, so report it.
Anna Zaks03826aa2011-08-04 00:26:57 +0000316 for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) {
Anna Zaks76cbb752011-08-04 21:53:01 +0000317 const ADFunctionInfo &FI = FunctionsToTrack[I->second.AllocatorIdx];
318
319 std::string sbuf;
320 llvm::raw_string_ostream os(sbuf);
321 os << "Allocated data is not released: missing a call to '"
322 << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
323 RangedBugReport *Report = new RangedBugReport(*BT, os.str(), N);
Anna Zaks03826aa2011-08-04 00:26:57 +0000324 // TODO: The report has to mention the expression which contains the
325 // allocated content as well as the point at which it has been allocated.
326 // Currently, the next line is useless.
327 Report->addRange(I->second.Address->getSourceRange());
328 Eng.getBugReporter().EmitReport(Report);
Anna Zaksf57be282011-08-01 22:40:01 +0000329 }
330}
331
332void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
333 mgr.registerChecker<MacOSKeychainAPIChecker>();
334}