xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp (revision 15f496c11869ae3c0a570bd03eb94480e25657fc)
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       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 
131 void 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 
146 void 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 
158 void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
159   mgr.registerChecker<MacOSKeychainAPIChecker>();
160 }
161