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