blob: f9a43fdc3a4fc569f0c9b6747973212507a056b6 [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)) {
Anna Zakse68b5f12011-08-02 17:11:03 +0000122 // Add the symbolic value, which represents the location of the allocated
123 // data, to the set.
124 SymbolRef V = SM.Retrieve(State->getStore(), *X).getAsSymbol();
Anna Zaksf57be282011-08-01 22:40:01 +0000125 if (!V)
126 return;
127 State = State->add<AllocatedData>(V);
Anna Zakse68b5f12011-08-02 17:11:03 +0000128
129 // We only need to track the value if the function returned noErr(0), so
130 // bind the return value of the function to 0.
131 SValBuilder &Builder = C.getSValBuilder();
132 SVal ZeroVal = Builder.makeZeroVal(Builder.getContext().CharTy);
133 State = State->BindExpr(CE, ZeroVal);
134 assert(State);
135
136 // Proceed from the new state.
Anna Zaksf57be282011-08-01 22:40:01 +0000137 C.addTransition(State);
138 }
139 }
140}
141
142void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
143 CheckerContext &C) const {
144 const Expr *retExpr = S->getRetValue();
145 if (!retExpr)
146 return;
147
148 // Check if the value is escaping through the return.
149 const GRState *state = C.getState();
150 SymbolRef V = state->getSVal(retExpr).getAsSymbol();
151 if (!V)
152 return;
153 state->remove<AllocatedData>(V);
154
155}
156
157void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
158 ExprEngine &Eng) const {
159 const GRState *state = B.getState();
160 AllocatedSetTy AS = state->get<AllocatedData>();
161
162 // Anything which has been allocated but not freed (nor escaped) will be
163 // found here, so report it.
164 if (!AS.isEmpty()) {
165 assert(0 && "TODO: Report the bug here.");
166 }
167}
168
169void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
170 mgr.registerChecker<MacOSKeychainAPIChecker>();
171}