diff options
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/Checkers.td | 11 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp | 160 | ||||
-rw-r--r-- | test/Analysis/keychainAPI.m | 71 |
4 files changed, 243 insertions, 0 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 8dc7f385a5..5968f007df 100644 --- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -31,6 +31,7 @@ add_clang_library(clangStaticAnalyzerCheckers IdempotentOperationChecker.cpp IteratorsChecker.cpp LLVMConventionsChecker.cpp + MacOSKeychainAPIChecker.cpp MacOSXAPIChecker.cpp MallocChecker.cpp NSAutoreleasePoolChecker.cpp diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 2c196b597b..a450240286 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -42,6 +42,8 @@ def UnixExperimental : Package<"experimental">, InPackage<Unix>, InGroup<AllExperimental>, Hidden; def OSX : Package<"osx">; +def OSXExperimental : Package<"experimental">, InPackage<OSX>, + InGroup<AllExperimental>, Hidden; def Cocoa : Package<"cocoa">, InPackage<OSX>; def CocoaExperimental : Package<"experimental">, InPackage<Cocoa>, InGroup<AllExperimental>, Hidden; @@ -276,6 +278,15 @@ def OSAtomicChecker : Checker<"AtomicCAS">, } // end "macosx" +let ParentPackage = OSXExperimental in { + +def MacOSKeychainAPIChecker : Checker<"KeychainAPI">, + InPackage<OSX>, + HelpText<"Check for proper uses of Secure Keychain APIs">, + DescFile<"MacOSKeychainAPIChecker.cpp">; + +} // end "osx.experimental" + let ParentPackage = Cocoa in { def ObjCAtSyncChecker : Checker<"AtSync">, diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp new file mode 100644 index 0000000000..3e80d9cc42 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -0,0 +1,160 @@ +//==--- 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>(); +} diff --git a/test/Analysis/keychainAPI.m b/test/Analysis/keychainAPI.m new file mode 100644 index 0000000000..85cc8eafaa --- /dev/null +++ b/test/Analysis/keychainAPI.m @@ -0,0 +1,71 @@ +// 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; +} |