blob: 3e80d9cc428be170e678e5835123e3c812b39014 [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"
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 SymbolRef V = SM.Retrieve (State->getStore(), *X).getAsSymbol();
123 if (!V)
124 return;
125 State = State->add<AllocatedData>(V);
126 C.addTransition(State);
127 }
128 }
129}
130
131void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
132 CheckerContext &C) const {
133 const Expr *retExpr = S->getRetValue();
134 if (!retExpr)
135 return;
136
137 // Check if the value is escaping through the return.
138 const GRState *state = C.getState();
139 SymbolRef V = state->getSVal(retExpr).getAsSymbol();
140 if (!V)
141 return;
142 state->remove<AllocatedData>(V);
143
144}
145
146void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
147 ExprEngine &Eng) const {
148 const GRState *state = B.getState();
149 AllocatedSetTy AS = state->get<AllocatedData>();
150
151 // Anything which has been allocated but not freed (nor escaped) will be
152 // found here, so report it.
153 if (!AS.isEmpty()) {
154 assert(0 && "TODO: Report the bug here.");
155 }
156}
157
158void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
159 mgr.registerChecker<MacOSKeychainAPIChecker>();
160}