xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp (revision 9ab728bb058429358fc1aba008e2d32c71106240)
1 //==--- 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 
22 using namespace clang;
23 using namespace ento;
24 
25 namespace {
26 class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
27                                                check::PreStmt<ReturnStmt>,
28                                                check::PostStmt<CallExpr>,
29                                                check::EndPath > {
30 public:
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 
37 private:
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.
63 typedef llvm::ImmutableSet<SymbolRef> AllocatedSetTy;
64 
65 namespace { struct AllocatedData {}; }
66 namespace clang { namespace ento {
67 template<> struct GRStateTrait<AllocatedData>
68     :  public GRStatePartialTrait<AllocatedSetTy > {
69   static void *GDMIndex() { static int index = 0; return &index; }
70 };
71 }}
72 
73 void 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 
102 void 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       // 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();
125       if (!V)
126         return;
127       State = State->add<AllocatedData>(V);
128 
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.
137       C.addTransition(State);
138     }
139   }
140 }
141 
142 void 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 
157 void 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 
169 void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
170   mgr.registerChecker<MacOSKeychainAPIChecker>();
171 }
172