]> granicus.if.org Git - clang/commitdiff
Add a skeleton for the Keychain Services API Checker. Register it as OSX experimental...
authorAnna Zaks <ganna@apple.com>
Mon, 1 Aug 2011 22:40:01 +0000 (22:40 +0000)
committerAnna Zaks <ganna@apple.com>
Mon, 1 Aug 2011 22:40:01 +0000 (22:40 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@136659 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Checkers/CMakeLists.txt
lib/StaticAnalyzer/Checkers/Checkers.td
lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp [new file with mode: 0644]
test/Analysis/keychainAPI.m [new file with mode: 0644]

index 8dc7f385a58d9522939aa5dc549880561a405961..5968f007df968b9c1ed21fda82ba15d71f80ea98 100644 (file)
@@ -31,6 +31,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   IdempotentOperationChecker.cpp
   IteratorsChecker.cpp
   LLVMConventionsChecker.cpp
+  MacOSKeychainAPIChecker.cpp
   MacOSXAPIChecker.cpp
   MallocChecker.cpp
   NSAutoreleasePoolChecker.cpp
index 2c196b597b7d1586111934977a4d69df0eba58b2..a450240286f791515ecc3df5310f1d7981e71626 100644 (file)
@@ -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 (file)
index 0000000..3e80d9c
--- /dev/null
@@ -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 (file)
index 0000000..85cc8ea
--- /dev/null
@@ -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;
+}