--- /dev/null
+//==--- MacOSKeychainAPIChecker.cpp -----------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This checker flags misuses of KeyChainAPI. In particular, the password data
+// allocated/returned by SecKeychainItemCopyContent,
+// SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has
+// to be freed using a call to SecKeychainItemFreeContent.
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
+ check::PreStmt<ReturnStmt>,
+ check::PostStmt<CallExpr>,
+ check::EndPath > {
+public:
+ void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
+ void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
+ void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
+
+ void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
+
+private:
+ static const unsigned InvalidParamVal = 100000;
+
+ /// Given the function name, returns the index of the parameter which will
+ /// be allocated as a result of the call.
+ unsigned getAllocatingFunctionParam(StringRef Name) const {
+ if (Name == "SecKeychainItemCopyContent")
+ return 4;
+ if (Name == "SecKeychainFindGenericPassword")
+ return 6;
+ if (Name == "SecKeychainFindInternetPassword")
+ return 13;
+ return InvalidParamVal;
+ }
+
+ /// Given the function name, returns the index of the parameter which will
+ /// be freed by the function.
+ unsigned getDeallocatingFunctionParam(StringRef Name) const {
+ if (Name == "SecKeychainItemFreeContent")
+ return 1;
+ return InvalidParamVal;
+ }
+};
+}
+
+// GRState traits to store the currently allocated (and not yet freed) symbols.
+typedef llvm::ImmutableSet<SymbolRef> AllocatedSetTy;
+
+namespace { struct AllocatedData {}; }
+namespace clang { namespace ento {
+template<> struct GRStateTrait<AllocatedData>
+ : public GRStatePartialTrait<AllocatedSetTy > {
+ static void *GDMIndex() { static int index = 0; return &index; }
+};
+}}
+
+void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ const GRState *State = C.getState();
+ const Expr *Callee = CE->getCallee();
+ SVal L = State->getSVal(Callee);
+
+ const FunctionDecl *funDecl = L.getAsFunctionDecl();
+ if (!funDecl)
+ return;
+ IdentifierInfo *funI = funDecl->getIdentifier();
+ if (!funI)
+ return;
+ StringRef funName = funI->getName();
+
+ // If a value has been freed, remove from the list.
+ unsigned idx = getDeallocatingFunctionParam(funName);
+ if (idx != InvalidParamVal) {
+ SymbolRef Param = State->getSVal(CE->getArg(idx)).getAsSymbol();
+ if (!Param)
+ return;
+ if (!State->contains<AllocatedData>(Param)) {
+ // TODO: do we care about this?
+ assert(0 && "Trying to free data which has not been allocated yet.");
+ }
+ State = State->remove<AllocatedData>(Param);
+ C.addTransition(State);
+ }
+}
+
+void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ const GRState *State = C.getState();
+ const Expr *Callee = CE->getCallee();
+ SVal L = State->getSVal(Callee);
+ StoreManager& SM = C.getStoreManager();
+
+ const FunctionDecl *funDecl = L.getAsFunctionDecl();
+ if (!funDecl)
+ return;
+ IdentifierInfo *funI = funDecl->getIdentifier();
+ if (!funI)
+ return;
+ StringRef funName = funI->getName();
+
+ // If a value has been allocated, add it to the set for tracking.
+ unsigned idx = getAllocatingFunctionParam(funName);
+ if (idx != InvalidParamVal) {
+ SVal Param = State->getSVal(CE->getArg(idx));
+ if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&Param)) {
+ SymbolRef V = SM.Retrieve (State->getStore(), *X).getAsSymbol();
+ if (!V)
+ return;
+ State = State->add<AllocatedData>(V);
+ C.addTransition(State);
+ }
+ }
+}
+
+void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
+ CheckerContext &C) const {
+ const Expr *retExpr = S->getRetValue();
+ if (!retExpr)
+ return;
+
+ // Check if the value is escaping through the return.
+ const GRState *state = C.getState();
+ SymbolRef V = state->getSVal(retExpr).getAsSymbol();
+ if (!V)
+ return;
+ state->remove<AllocatedData>(V);
+
+}
+
+void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
+ ExprEngine &Eng) const {
+ const GRState *state = B.getState();
+ AllocatedSetTy AS = state->get<AllocatedData>();
+
+ // Anything which has been allocated but not freed (nor escaped) will be
+ // found here, so report it.
+ if (!AS.isEmpty()) {
+ assert(0 && "TODO: Report the bug here.");
+ }
+}
+
+void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
+ mgr.registerChecker<MacOSKeychainAPIChecker>();
+}
--- /dev/null
+// RUN: %clang_cc1 -analyze -analyzer-checker=osx.experimental.KeychainAPI %s -verify
+
+// Fake typedefs.
+typedef unsigned int OSStatus;
+typedef unsigned int SecKeychainAttributeList;
+typedef unsigned int SecKeychainItemRef;
+typedef unsigned int SecItemClass;
+typedef unsigned int UInt32;
+typedef unsigned int CFTypeRef;
+typedef unsigned int UInt16;
+typedef unsigned int SecProtocolType;
+typedef unsigned int SecAuthenticationType;
+enum {
+ noErr = 0,
+ GenericError = 1
+};
+
+// Functions that allocate data.
+OSStatus SecKeychainItemCopyContent (
+ SecKeychainItemRef itemRef,
+ SecItemClass *itemClass,
+ SecKeychainAttributeList *attrList,
+ UInt32 *length,
+ void **outData
+);
+OSStatus SecKeychainFindGenericPassword (
+ CFTypeRef keychainOrArray,
+ UInt32 serviceNameLength,
+ const char *serviceName,
+ UInt32 accountNameLength,
+ const char *accountName,
+ UInt32 *passwordLength,
+ void **passwordData,
+ SecKeychainItemRef *itemRef
+);
+OSStatus SecKeychainFindInternetPassword (
+ CFTypeRef keychainOrArray,
+ UInt32 serverNameLength,
+ const char *serverName,
+ UInt32 securityDomainLength,
+ const char *securityDomain,
+ UInt32 accountNameLength,
+ const char *accountName,
+ UInt32 pathLength,
+ const char *path,
+ UInt16 port,
+ SecProtocolType protocol,
+ SecAuthenticationType authenticationType,
+ UInt32 *passwordLength,
+ void **passwordData,
+ SecKeychainItemRef *itemRef
+);
+
+// Function which frees data.
+OSStatus SecKeychainItemFreeContent (
+ SecKeychainAttributeList *attrList,
+ void *data
+);
+
+int foo () {
+ unsigned int *ptr = 0;
+ OSStatus st = 0;
+
+ UInt32 length;
+ void *outData;
+
+ st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData);
+ SecKeychainItemFreeContent(ptr, outData);
+
+ return 0;
+}