blob: 4934cda1aef57d3ff907a4825634d55efe67ec45 [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:
Anna Zaks864d2522011-08-12 21:14:26 +000034 /// AllocationState is a part of the checker specific state together with the
35 /// MemRegion corresponding to the allocated data.
36 struct AllocationState {
37 const Expr *Address;
38 /// The index of the allocator function.
39 unsigned int AllocatorIdx;
40 SymbolRef RetValue;
41
42 AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) :
43 Address(E),
44 AllocatorIdx(Idx),
45 RetValue(R) {}
46
47 bool operator==(const AllocationState &X) const {
48 return Address == X.Address;
49 }
50 void Profile(llvm::FoldingSetNodeID &ID) const {
51 ID.AddPointer(Address);
52 ID.AddInteger(AllocatorIdx);
53 }
54 };
55
Anna Zaksf57be282011-08-01 22:40:01 +000056 void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
57 void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
58 void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
59
60 void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
61
62private:
Anna Zaks083fcb22011-08-04 17:28:06 +000063 /// Stores the information about the allocator and deallocator functions -
64 /// these are the functions the checker is tracking.
65 struct ADFunctionInfo {
66 const char* Name;
67 unsigned int Param;
68 unsigned int DeallocatorIdx;
69 };
70 static const unsigned InvalidIdx = 100000;
Anna Zaks76cbb752011-08-04 21:53:01 +000071 static const unsigned FunctionsToTrackSize = 6;
Anna Zaks083fcb22011-08-04 17:28:06 +000072 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
Anna Zaks5a58c6d2011-08-05 23:52:45 +000073 /// The value, which represents no error return value for allocator functions.
74 static const unsigned NoErr = 0;
Anna Zaksf57be282011-08-01 22:40:01 +000075
Anna Zaks083fcb22011-08-04 17:28:06 +000076 /// Given the function name, returns the index of the allocator/deallocator
77 /// function.
78 unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator) const;
Anna Zaks03826aa2011-08-04 00:26:57 +000079
80 inline void initBugType() const {
81 if (!BT)
82 BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API"));
83 }
Anna Zaksf57be282011-08-01 22:40:01 +000084};
85}
86
Anna Zaks083fcb22011-08-04 17:28:06 +000087/// GRState traits to store the currently allocated (and not yet freed) symbols.
Anna Zaks864d2522011-08-12 21:14:26 +000088/// This is a map from the allocated content symbol to the corresponding
89/// AllocationState.
90typedef llvm::ImmutableMap<SymbolRef,
91 MacOSKeychainAPIChecker::AllocationState> AllocatedSetTy;
Anna Zaksf57be282011-08-01 22:40:01 +000092
93namespace { struct AllocatedData {}; }
94namespace clang { namespace ento {
95template<> struct GRStateTrait<AllocatedData>
96 : public GRStatePartialTrait<AllocatedSetTy > {
97 static void *GDMIndex() { static int index = 0; return &index; }
98};
99}}
100
Anna Zaks03826aa2011-08-04 00:26:57 +0000101static bool isEnclosingFunctionParam(const Expr *E) {
102 E = E->IgnoreParenCasts();
103 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
104 const ValueDecl *VD = DRE->getDecl();
105 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
106 return true;
107 }
108 return false;
109}
110
Anna Zaks083fcb22011-08-04 17:28:06 +0000111const MacOSKeychainAPIChecker::ADFunctionInfo
112 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
113 {"SecKeychainItemCopyContent", 4, 3}, // 0
114 {"SecKeychainFindGenericPassword", 6, 3}, // 1
115 {"SecKeychainFindInternetPassword", 13, 3}, // 2
116 {"SecKeychainItemFreeContent", 1, InvalidIdx}, // 3
Anna Zaks76cbb752011-08-04 21:53:01 +0000117 {"SecKeychainItemCopyAttributesAndData", 5, 5}, // 4
118 {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx}, // 5
Anna Zaks083fcb22011-08-04 17:28:06 +0000119};
120
121unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
122 bool IsAllocator) const {
123 for (unsigned I = 0; I < FunctionsToTrackSize; ++I) {
124 ADFunctionInfo FI = FunctionsToTrack[I];
125 if (FI.Name != Name)
126 continue;
127 // Make sure the function is of the right type (allocator vs deallocator).
128 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
129 return InvalidIdx;
130 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
131 return InvalidIdx;
132
133 return I;
134 }
135 // The function is not tracked.
136 return InvalidIdx;
137}
138
Anna Zaks864d2522011-08-12 21:14:26 +0000139static SymbolRef getSymbolForRegion(CheckerContext &C,
140 const MemRegion *R) {
141 if (!isa<SymbolicRegion>(R))
142 return 0;
143 return cast<SymbolicRegion>(R)->getSymbol();
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000144}
145
Anna Zaks864d2522011-08-12 21:14:26 +0000146static bool isBadDeallocationArgument(const MemRegion *Arg) {
147 if (isa<AllocaRegion>(Arg) ||
148 isa<BlockDataRegion>(Arg) ||
149 isa<TypedRegion>(Arg)) {
150 return true;
151 }
152 return false;
153}
Anna Zaksca0b57e2011-08-05 00:37:00 +0000154/// Given the address expression, retrieve the value it's pointing to. Assume
Anna Zaks864d2522011-08-12 21:14:26 +0000155/// that value is itself an address, and return the corresponding symbol.
156static SymbolRef getAsPointeeSymbol(const Expr *Expr,
157 CheckerContext &C) {
Anna Zaksca0b57e2011-08-05 00:37:00 +0000158 const GRState *State = C.getState();
159 SVal ArgV = State->getSVal(Expr);
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000160
Anna Zaksca0b57e2011-08-05 00:37:00 +0000161 if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) {
162 StoreManager& SM = C.getStoreManager();
163 const MemRegion *V = SM.Retrieve(State->getStore(), *X).getAsRegion();
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000164 if (V)
Anna Zaks864d2522011-08-12 21:14:26 +0000165 return getSymbolForRegion(C, V);
Anna Zaksca0b57e2011-08-05 00:37:00 +0000166 }
167 return 0;
168}
169
Anna Zaksf57be282011-08-01 22:40:01 +0000170void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
171 CheckerContext &C) const {
172 const GRState *State = C.getState();
173 const Expr *Callee = CE->getCallee();
174 SVal L = State->getSVal(Callee);
Anna Zaksca0b57e2011-08-05 00:37:00 +0000175 unsigned idx = InvalidIdx;
Anna Zaksf57be282011-08-01 22:40:01 +0000176
177 const FunctionDecl *funDecl = L.getAsFunctionDecl();
178 if (!funDecl)
179 return;
180 IdentifierInfo *funI = funDecl->getIdentifier();
181 if (!funI)
182 return;
183 StringRef funName = funI->getName();
184
Anna Zaksca0b57e2011-08-05 00:37:00 +0000185 // If it is a call to an allocator function, it could be a double allocation.
186 idx = getTrackedFunctionIndex(funName, true);
187 if (idx != InvalidIdx) {
188 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
Anna Zaks864d2522011-08-12 21:14:26 +0000189 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
Anna Zaksca0b57e2011-08-05 00:37:00 +0000190 if (const AllocationState *AS = State->get<AllocatedData>(V)) {
191 ExplodedNode *N = C.generateSink(State);
192 if (!N)
193 return;
194 initBugType();
195 std::string sbuf;
196 llvm::raw_string_ostream os(sbuf);
197 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
198 os << "Allocated data should be released before another call to "
199 << "the allocator: missing a call to '"
200 << FunctionsToTrack[DIdx].Name
201 << "'.";
202 RangedBugReport *Report = new RangedBugReport(*BT, os.str(), N);
203 Report->addRange(ArgExpr->getSourceRange());
204 C.EmitReport(Report);
205 }
206 return;
207 }
208
209 // Is it a call to one of deallocator functions?
210 idx = getTrackedFunctionIndex(funName, false);
Anna Zaks083fcb22011-08-04 17:28:06 +0000211 if (idx == InvalidIdx)
Anna Zaks08551b52011-08-04 00:31:38 +0000212 return;
213
Anna Zaks864d2522011-08-12 21:14:26 +0000214 // Check the argument to the deallocator.
Anna Zaks083fcb22011-08-04 17:28:06 +0000215 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
Anna Zaks864d2522011-08-12 21:14:26 +0000216 SVal ArgSVal = State->getSVal(ArgExpr);
217
218 // Undef is reported by another checker.
219 if (ArgSVal.isUndef())
220 return;
221
222 const MemRegion *Arg = ArgSVal.getAsRegion();
Anna Zaks08551b52011-08-04 00:31:38 +0000223 if (!Arg)
224 return;
Anna Zaks864d2522011-08-12 21:14:26 +0000225
226 SymbolRef ArgSM = getSymbolForRegion(C, Arg);
227 bool RegionArgIsBad = ArgSM ? false : isBadDeallocationArgument(Arg);
228 // If the argument is coming from the heap, globals, or unknown, do not
229 // report it.
230 if (!ArgSM && !RegionArgIsBad)
231 return;
Anna Zaks08551b52011-08-04 00:31:38 +0000232
233 // If trying to free data which has not been allocated yet, report as bug.
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000234 const AllocationState *AS = State->get<AllocatedData>(ArgSM);
Anna Zaks864d2522011-08-12 21:14:26 +0000235 if (!AS || RegionArgIsBad) {
Anna Zaks08551b52011-08-04 00:31:38 +0000236 // It is possible that this is a false positive - the argument might
237 // have entered as an enclosing function parameter.
238 if (isEnclosingFunctionParam(ArgExpr))
Anna Zaksf57be282011-08-01 22:40:01 +0000239 return;
Anna Zaks03826aa2011-08-04 00:26:57 +0000240
Anna Zaks08551b52011-08-04 00:31:38 +0000241 ExplodedNode *N = C.generateNode(State);
242 if (!N)
243 return;
244 initBugType();
245 RangedBugReport *Report = new RangedBugReport(*BT,
246 "Trying to free data which has not been allocated.", N);
247 Report->addRange(ArgExpr->getSourceRange());
248 C.EmitReport(Report);
Anna Zaks083fcb22011-08-04 17:28:06 +0000249 return;
Anna Zaksf57be282011-08-01 22:40:01 +0000250 }
Anna Zaks08551b52011-08-04 00:31:38 +0000251
Anna Zaks76cbb752011-08-04 21:53:01 +0000252 // Check if the proper deallocator is used.
253 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
254 if (PDeallocIdx != idx) {
255 ExplodedNode *N = C.generateSink(State);
256 if (!N)
257 return;
258 initBugType();
259
260 std::string sbuf;
261 llvm::raw_string_ostream os(sbuf);
262 os << "Allocator doesn't match the deallocator: '"
263 << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
264 RangedBugReport *Report = new RangedBugReport(*BT, os.str(), N);
265 Report->addRange(ArgExpr->getSourceRange());
266 C.EmitReport(Report);
267 return;
268 }
269
Anna Zaks864d2522011-08-12 21:14:26 +0000270 // The call is deallocating a value we previously allocated, so remove it
271 // from the next state.
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000272 State = State->remove<AllocatedData>(ArgSM);
Anna Zaks08551b52011-08-04 00:31:38 +0000273 C.addTransition(State);
Anna Zaksf57be282011-08-01 22:40:01 +0000274}
275
276void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
277 CheckerContext &C) const {
278 const GRState *State = C.getState();
279 const Expr *Callee = CE->getCallee();
280 SVal L = State->getSVal(Callee);
Anna Zaksf57be282011-08-01 22:40:01 +0000281
282 const FunctionDecl *funDecl = L.getAsFunctionDecl();
283 if (!funDecl)
284 return;
285 IdentifierInfo *funI = funDecl->getIdentifier();
286 if (!funI)
287 return;
288 StringRef funName = funI->getName();
289
290 // If a value has been allocated, add it to the set for tracking.
Anna Zaks083fcb22011-08-04 17:28:06 +0000291 unsigned idx = getTrackedFunctionIndex(funName, true);
292 if (idx == InvalidIdx)
Anna Zaks08551b52011-08-04 00:31:38 +0000293 return;
Anna Zaks03826aa2011-08-04 00:26:57 +0000294
Anna Zaks083fcb22011-08-04 17:28:06 +0000295 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
Anna Zaks864d2522011-08-12 21:14:26 +0000296 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) {
297 // If the argument points to something that's not a symbolic region, it
298 // can be:
Anna Zaks08551b52011-08-04 00:31:38 +0000299 // - unknown (cannot reason about it)
300 // - undefined (already reported by other checker)
Anna Zaks083fcb22011-08-04 17:28:06 +0000301 // - constant (null - should not be tracked,
302 // other constant will generate a compiler warning)
Anna Zaks08551b52011-08-04 00:31:38 +0000303 // - goto (should be reported by other checker)
Anna Zaks864d2522011-08-12 21:14:26 +0000304
Anna Zaks08551b52011-08-04 00:31:38 +0000305 // We only need to track the value if the function returned noErr(0), so
Anna Zaks76cbb752011-08-04 21:53:01 +0000306 // bind the return value of the function to 0 and proceed from the no error
307 // state.
Anna Zaks08551b52011-08-04 00:31:38 +0000308 SValBuilder &Builder = C.getSValBuilder();
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000309 SVal NoErrVal = Builder.makeIntVal(NoErr, CE->getCallReturnType());
310 const GRState *NoErr = State->BindExpr(CE, NoErrVal);
Anna Zaksca0b57e2011-08-05 00:37:00 +0000311 // Add the symbolic value V, which represents the location of the allocated
312 // data, to the set.
Anna Zaks864d2522011-08-12 21:14:26 +0000313 SymbolRef RetStatusSymbol = State->getSVal(CE).getAsSymbol();
314 NoErr = NoErr->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
315 RetStatusSymbol));
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000316
Anna Zaks76cbb752011-08-04 21:53:01 +0000317 assert(NoErr);
318 C.addTransition(NoErr);
Anna Zaks08551b52011-08-04 00:31:38 +0000319
Anna Zaks76cbb752011-08-04 21:53:01 +0000320 // Generate a transition to explore the state space when there is an error.
321 // In this case, we do not track the allocated data.
322 SVal ReturnedError = Builder.evalBinOpNN(State, BO_NE,
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000323 cast<NonLoc>(NoErrVal),
Anna Zaks76cbb752011-08-04 21:53:01 +0000324 cast<NonLoc>(State->getSVal(CE)),
325 CE->getCallReturnType());
326 const GRState *Err = State->assume(cast<NonLoc>(ReturnedError), true);
327 assert(Err);
328 C.addTransition(Err);
Anna Zaksf57be282011-08-01 22:40:01 +0000329 }
330}
331
332void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
333 CheckerContext &C) const {
334 const Expr *retExpr = S->getRetValue();
335 if (!retExpr)
336 return;
337
338 // Check if the value is escaping through the return.
339 const GRState *state = C.getState();
Anna Zaks03826aa2011-08-04 00:26:57 +0000340 const MemRegion *V = state->getSVal(retExpr).getAsRegion();
Anna Zaksf57be282011-08-01 22:40:01 +0000341 if (!V)
342 return;
Anna Zaks864d2522011-08-12 21:14:26 +0000343 state = state->remove<AllocatedData>(getSymbolForRegion(C, V));
Anna Zaksf57be282011-08-01 22:40:01 +0000344
Anna Zaks03826aa2011-08-04 00:26:57 +0000345 // Proceed from the new state.
346 C.addTransition(state);
Anna Zaksf57be282011-08-01 22:40:01 +0000347}
348
349void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
Anna Zaks03826aa2011-08-04 00:26:57 +0000350 ExprEngine &Eng) const {
Anna Zaksf57be282011-08-01 22:40:01 +0000351 const GRState *state = B.getState();
352 AllocatedSetTy AS = state->get<AllocatedData>();
Anna Zaks03826aa2011-08-04 00:26:57 +0000353 ExplodedNode *N = B.generateNode(state);
354 if (!N)
355 return;
356 initBugType();
Anna Zaksf57be282011-08-01 22:40:01 +0000357
358 // Anything which has been allocated but not freed (nor escaped) will be
359 // found here, so report it.
Anna Zaks03826aa2011-08-04 00:26:57 +0000360 for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) {
Anna Zaks76cbb752011-08-04 21:53:01 +0000361 const ADFunctionInfo &FI = FunctionsToTrack[I->second.AllocatorIdx];
362
363 std::string sbuf;
364 llvm::raw_string_ostream os(sbuf);
365 os << "Allocated data is not released: missing a call to '"
366 << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
367 RangedBugReport *Report = new RangedBugReport(*BT, os.str(), N);
Anna Zaks03826aa2011-08-04 00:26:57 +0000368 // TODO: The report has to mention the expression which contains the
369 // allocated content as well as the point at which it has been allocated.
370 // Currently, the next line is useless.
371 Report->addRange(I->second.Address->getSourceRange());
372 Eng.getBugReporter().EmitReport(Report);
Anna Zaksf57be282011-08-01 22:40:01 +0000373 }
374}
375
376void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
377 mgr.registerChecker<MacOSKeychainAPIChecker>();
378}