blob: b3c9955086fe9276396189a8584348d75a17322b [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 Zaks5a58c6d2011-08-05 23:52:45 +000051 /// The value, which represents no error return value for allocator functions.
52 static const unsigned NoErr = 0;
Anna Zaksf57be282011-08-01 22:40:01 +000053
Anna Zaks083fcb22011-08-04 17:28:06 +000054 /// Given the function name, returns the index of the allocator/deallocator
55 /// function.
56 unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator) const;
Anna Zaks03826aa2011-08-04 00:26:57 +000057
58 inline void initBugType() const {
59 if (!BT)
60 BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API"));
61 }
Anna Zaksf57be282011-08-01 22:40:01 +000062};
63}
64
Anna Zaks083fcb22011-08-04 17:28:06 +000065/// AllocationState is a part of the checker specific state together with the
66/// MemRegion corresponding to the allocated data.
67struct AllocationState {
Anna Zaks03826aa2011-08-04 00:26:57 +000068 const Expr *Address;
Anna Zaks083fcb22011-08-04 17:28:06 +000069 /// The index of the allocator function.
70 unsigned int AllocatorIdx;
Anna Zaks5a58c6d2011-08-05 23:52:45 +000071 SymbolRef RetValue;
Anna Zaks03826aa2011-08-04 00:26:57 +000072
Anna Zaks5a58c6d2011-08-05 23:52:45 +000073 AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) :
74 Address(E),
75 AllocatorIdx(Idx),
76 RetValue(R) {}
77
Anna Zaks083fcb22011-08-04 17:28:06 +000078 bool operator==(const AllocationState &X) const {
Anna Zaks03826aa2011-08-04 00:26:57 +000079 return Address == X.Address;
80 }
81 void Profile(llvm::FoldingSetNodeID &ID) const {
82 ID.AddPointer(Address);
Anna Zaks083fcb22011-08-04 17:28:06 +000083 ID.AddInteger(AllocatorIdx);
Anna Zaks03826aa2011-08-04 00:26:57 +000084 }
85};
86
Anna Zaks083fcb22011-08-04 17:28:06 +000087/// GRState traits to store the currently allocated (and not yet freed) symbols.
Anna Zaks5a58c6d2011-08-05 23:52:45 +000088typedef llvm::ImmutableMap<const SymbolMetadata *,
89 AllocationState> AllocatedSetTy;
Anna Zaksf57be282011-08-01 22:40:01 +000090
91namespace { struct AllocatedData {}; }
92namespace clang { namespace ento {
93template<> struct GRStateTrait<AllocatedData>
94 : public GRStatePartialTrait<AllocatedSetTy > {
95 static void *GDMIndex() { static int index = 0; return &index; }
96};
97}}
98
Anna Zaks03826aa2011-08-04 00:26:57 +000099static bool isEnclosingFunctionParam(const Expr *E) {
100 E = E->IgnoreParenCasts();
101 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
102 const ValueDecl *VD = DRE->getDecl();
103 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
104 return true;
105 }
106 return false;
107}
108
Anna Zaks083fcb22011-08-04 17:28:06 +0000109const MacOSKeychainAPIChecker::ADFunctionInfo
110 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
111 {"SecKeychainItemCopyContent", 4, 3}, // 0
112 {"SecKeychainFindGenericPassword", 6, 3}, // 1
113 {"SecKeychainFindInternetPassword", 13, 3}, // 2
114 {"SecKeychainItemFreeContent", 1, InvalidIdx}, // 3
Anna Zaks76cbb752011-08-04 21:53:01 +0000115 {"SecKeychainItemCopyAttributesAndData", 5, 5}, // 4
116 {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx}, // 5
Anna Zaks083fcb22011-08-04 17:28:06 +0000117};
118
119unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
120 bool IsAllocator) const {
121 for (unsigned I = 0; I < FunctionsToTrackSize; ++I) {
122 ADFunctionInfo FI = FunctionsToTrack[I];
123 if (FI.Name != Name)
124 continue;
125 // Make sure the function is of the right type (allocator vs deallocator).
126 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
127 return InvalidIdx;
128 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
129 return InvalidIdx;
130
131 return I;
132 }
133 // The function is not tracked.
134 return InvalidIdx;
135}
136
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000137static const SymbolMetadata *getSymbolMetadata(CheckerContext &C,
138 const MemRegion *R) {
139 QualType sizeTy = C.getSValBuilder().getContext().getSizeType();
140 return C.getSymbolManager().getMetadataSymbol(R, 0, sizeTy, 0);
141}
142
Anna Zaksca0b57e2011-08-05 00:37:00 +0000143/// Given the address expression, retrieve the value it's pointing to. Assume
144/// that value is itself an address, and return the corresponding MemRegion.
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000145static const SymbolMetadata *getAsPointeeMemoryRegion(const Expr *Expr,
146 CheckerContext &C) {
Anna Zaksca0b57e2011-08-05 00:37:00 +0000147 const GRState *State = C.getState();
148 SVal ArgV = State->getSVal(Expr);
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000149
Anna Zaksca0b57e2011-08-05 00:37:00 +0000150 if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) {
151 StoreManager& SM = C.getStoreManager();
152 const MemRegion *V = SM.Retrieve(State->getStore(), *X).getAsRegion();
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000153 if (V)
154 return getSymbolMetadata(C, V);
Anna Zaksca0b57e2011-08-05 00:37:00 +0000155 }
156 return 0;
157}
158
Anna Zaksf57be282011-08-01 22:40:01 +0000159void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
160 CheckerContext &C) const {
161 const GRState *State = C.getState();
162 const Expr *Callee = CE->getCallee();
163 SVal L = State->getSVal(Callee);
Anna Zaksca0b57e2011-08-05 00:37:00 +0000164 unsigned idx = InvalidIdx;
Anna Zaksf57be282011-08-01 22:40:01 +0000165
166 const FunctionDecl *funDecl = L.getAsFunctionDecl();
167 if (!funDecl)
168 return;
169 IdentifierInfo *funI = funDecl->getIdentifier();
170 if (!funI)
171 return;
172 StringRef funName = funI->getName();
173
Anna Zaksca0b57e2011-08-05 00:37:00 +0000174 // If it is a call to an allocator function, it could be a double allocation.
175 idx = getTrackedFunctionIndex(funName, true);
176 if (idx != InvalidIdx) {
177 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000178 if (const SymbolMetadata *V = getAsPointeeMemoryRegion(ArgExpr, C))
Anna Zaksca0b57e2011-08-05 00:37:00 +0000179 if (const AllocationState *AS = State->get<AllocatedData>(V)) {
180 ExplodedNode *N = C.generateSink(State);
181 if (!N)
182 return;
183 initBugType();
184 std::string sbuf;
185 llvm::raw_string_ostream os(sbuf);
186 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
187 os << "Allocated data should be released before another call to "
188 << "the allocator: missing a call to '"
189 << FunctionsToTrack[DIdx].Name
190 << "'.";
191 RangedBugReport *Report = new RangedBugReport(*BT, os.str(), N);
192 Report->addRange(ArgExpr->getSourceRange());
193 C.EmitReport(Report);
194 }
195 return;
196 }
197
198 // Is it a call to one of deallocator functions?
199 idx = getTrackedFunctionIndex(funName, false);
Anna Zaks083fcb22011-08-04 17:28:06 +0000200 if (idx == InvalidIdx)
Anna Zaks08551b52011-08-04 00:31:38 +0000201 return;
202
Anna Zaks083fcb22011-08-04 17:28:06 +0000203 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
Anna Zaks08551b52011-08-04 00:31:38 +0000204 const MemRegion *Arg = State->getSVal(ArgExpr).getAsRegion();
205 if (!Arg)
206 return;
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000207 const SymbolMetadata *ArgSM = getSymbolMetadata(C, Arg);
Anna Zaks08551b52011-08-04 00:31:38 +0000208
209 // If trying to free data which has not been allocated yet, report as bug.
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000210 const AllocationState *AS = State->get<AllocatedData>(ArgSM);
Anna Zaks083fcb22011-08-04 17:28:06 +0000211 if (!AS) {
Anna Zaks08551b52011-08-04 00:31:38 +0000212 // It is possible that this is a false positive - the argument might
213 // have entered as an enclosing function parameter.
214 if (isEnclosingFunctionParam(ArgExpr))
Anna Zaksf57be282011-08-01 22:40:01 +0000215 return;
Anna Zaks03826aa2011-08-04 00:26:57 +0000216
Anna Zaks08551b52011-08-04 00:31:38 +0000217 ExplodedNode *N = C.generateNode(State);
218 if (!N)
219 return;
220 initBugType();
221 RangedBugReport *Report = new RangedBugReport(*BT,
222 "Trying to free data which has not been allocated.", N);
223 Report->addRange(ArgExpr->getSourceRange());
224 C.EmitReport(Report);
Anna Zaks083fcb22011-08-04 17:28:06 +0000225 return;
Anna Zaksf57be282011-08-01 22:40:01 +0000226 }
Anna Zaks08551b52011-08-04 00:31:38 +0000227
Anna Zaks76cbb752011-08-04 21:53:01 +0000228 // Check if the proper deallocator is used.
229 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
230 if (PDeallocIdx != idx) {
231 ExplodedNode *N = C.generateSink(State);
232 if (!N)
233 return;
234 initBugType();
235
236 std::string sbuf;
237 llvm::raw_string_ostream os(sbuf);
238 os << "Allocator doesn't match the deallocator: '"
239 << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
240 RangedBugReport *Report = new RangedBugReport(*BT, os.str(), N);
241 Report->addRange(ArgExpr->getSourceRange());
242 C.EmitReport(Report);
243 return;
244 }
245
Anna Zaksca0b57e2011-08-05 00:37:00 +0000246 // If a value has been freed, remove it from the list and continue exploring
247 // from the new state.
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000248 State = State->remove<AllocatedData>(ArgSM);
Anna Zaks08551b52011-08-04 00:31:38 +0000249 C.addTransition(State);
Anna Zaksf57be282011-08-01 22:40:01 +0000250}
251
252void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
253 CheckerContext &C) const {
254 const GRState *State = C.getState();
255 const Expr *Callee = CE->getCallee();
256 SVal L = State->getSVal(Callee);
Anna Zaksf57be282011-08-01 22:40:01 +0000257
258 const FunctionDecl *funDecl = L.getAsFunctionDecl();
259 if (!funDecl)
260 return;
261 IdentifierInfo *funI = funDecl->getIdentifier();
262 if (!funI)
263 return;
264 StringRef funName = funI->getName();
265
266 // If a value has been allocated, add it to the set for tracking.
Anna Zaks083fcb22011-08-04 17:28:06 +0000267 unsigned idx = getTrackedFunctionIndex(funName, true);
268 if (idx == InvalidIdx)
Anna Zaks08551b52011-08-04 00:31:38 +0000269 return;
Anna Zaks03826aa2011-08-04 00:26:57 +0000270
Anna Zaks083fcb22011-08-04 17:28:06 +0000271 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000272 if (const SymbolMetadata *V = getAsPointeeMemoryRegion(ArgExpr, C)) {
Anna Zaksca0b57e2011-08-05 00:37:00 +0000273 // If the argument points to something that's not a region, it can be:
Anna Zaks08551b52011-08-04 00:31:38 +0000274 // - unknown (cannot reason about it)
275 // - undefined (already reported by other checker)
Anna Zaks083fcb22011-08-04 17:28:06 +0000276 // - constant (null - should not be tracked,
277 // other constant will generate a compiler warning)
Anna Zaks08551b52011-08-04 00:31:38 +0000278 // - goto (should be reported by other checker)
Anna Zakse68b5f12011-08-02 17:11:03 +0000279
Anna Zaks08551b52011-08-04 00:31:38 +0000280 // We only need to track the value if the function returned noErr(0), so
Anna Zaks76cbb752011-08-04 21:53:01 +0000281 // bind the return value of the function to 0 and proceed from the no error
282 // state.
Anna Zaks08551b52011-08-04 00:31:38 +0000283 SValBuilder &Builder = C.getSValBuilder();
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000284 SVal NoErrVal = Builder.makeIntVal(NoErr, CE->getCallReturnType());
285 const GRState *NoErr = State->BindExpr(CE, NoErrVal);
Anna Zaksca0b57e2011-08-05 00:37:00 +0000286 // Add the symbolic value V, which represents the location of the allocated
287 // data, to the set.
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000288 NoErr = NoErr->set<AllocatedData>(V,
289 AllocationState(ArgExpr, idx, State->getSVal(CE).getAsSymbol()));
290
Anna Zaks76cbb752011-08-04 21:53:01 +0000291 assert(NoErr);
292 C.addTransition(NoErr);
Anna Zaks08551b52011-08-04 00:31:38 +0000293
Anna Zaks76cbb752011-08-04 21:53:01 +0000294 // Generate a transition to explore the state space when there is an error.
295 // In this case, we do not track the allocated data.
296 SVal ReturnedError = Builder.evalBinOpNN(State, BO_NE,
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000297 cast<NonLoc>(NoErrVal),
Anna Zaks76cbb752011-08-04 21:53:01 +0000298 cast<NonLoc>(State->getSVal(CE)),
299 CE->getCallReturnType());
300 const GRState *Err = State->assume(cast<NonLoc>(ReturnedError), true);
301 assert(Err);
302 C.addTransition(Err);
Anna Zaksf57be282011-08-01 22:40:01 +0000303 }
304}
305
306void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
307 CheckerContext &C) const {
308 const Expr *retExpr = S->getRetValue();
309 if (!retExpr)
310 return;
311
312 // Check if the value is escaping through the return.
313 const GRState *state = C.getState();
Anna Zaks03826aa2011-08-04 00:26:57 +0000314 const MemRegion *V = state->getSVal(retExpr).getAsRegion();
Anna Zaksf57be282011-08-01 22:40:01 +0000315 if (!V)
316 return;
Anna Zaks5a58c6d2011-08-05 23:52:45 +0000317 state = state->remove<AllocatedData>(getSymbolMetadata(C, V));
Anna Zaksf57be282011-08-01 22:40:01 +0000318
Anna Zaks03826aa2011-08-04 00:26:57 +0000319 // Proceed from the new state.
320 C.addTransition(state);
Anna Zaksf57be282011-08-01 22:40:01 +0000321}
322
323void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
Anna Zaks03826aa2011-08-04 00:26:57 +0000324 ExprEngine &Eng) const {
Anna Zaksf57be282011-08-01 22:40:01 +0000325 const GRState *state = B.getState();
326 AllocatedSetTy AS = state->get<AllocatedData>();
Anna Zaks03826aa2011-08-04 00:26:57 +0000327 ExplodedNode *N = B.generateNode(state);
328 if (!N)
329 return;
330 initBugType();
Anna Zaksf57be282011-08-01 22:40:01 +0000331
332 // Anything which has been allocated but not freed (nor escaped) will be
333 // found here, so report it.
Anna Zaks03826aa2011-08-04 00:26:57 +0000334 for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) {
Anna Zaks76cbb752011-08-04 21:53:01 +0000335 const ADFunctionInfo &FI = FunctionsToTrack[I->second.AllocatorIdx];
336
337 std::string sbuf;
338 llvm::raw_string_ostream os(sbuf);
339 os << "Allocated data is not released: missing a call to '"
340 << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
341 RangedBugReport *Report = new RangedBugReport(*BT, os.str(), N);
Anna Zaks03826aa2011-08-04 00:26:57 +0000342 // TODO: The report has to mention the expression which contains the
343 // allocated content as well as the point at which it has been allocated.
344 // Currently, the next line is useless.
345 Report->addRange(I->second.Address->getSourceRange());
346 Eng.getBugReporter().EmitReport(Report);
Anna Zaksf57be282011-08-01 22:40:01 +0000347 }
348}
349
350void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
351 mgr.registerChecker<MacOSKeychainAPIChecker>();
352}