]> granicus.if.org Git - clang/commitdiff
Added "GRAuditor" and "GRSimpleAPICheck" interface to allow simple stateless checkers...
authorTed Kremenek <kremenek@apple.com>
Thu, 27 Mar 2008 07:25:52 +0000 (07:25 +0000)
committerTed Kremenek <kremenek@apple.com>
Thu, 27 Mar 2008 07:25:52 +0000 (07:25 +0000)
Added "AnnotatedPath" class to record an annotated path that will be useful for inspecting paths.
Added some boilerplate code for simple checks of Apple's Foundation API.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@48867 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Analysis/PathSensitive/AnnotatedPath.h [new file with mode: 0644]
include/clang/Analysis/PathSensitive/GRAuditor.h [new file with mode: 0644]
include/clang/Analysis/PathSensitive/GRCoreEngine.h
include/clang/Analysis/PathSensitive/GRExprEngine.h
include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h [new file with mode: 0644]
lib/Analysis/BasicObjCFoundationChecks.cpp [new file with mode: 0644]
lib/Analysis/GRExprEngine.cpp

diff --git a/include/clang/Analysis/PathSensitive/AnnotatedPath.h b/include/clang/Analysis/PathSensitive/AnnotatedPath.h
new file mode 100644 (file)
index 0000000..299634b
--- /dev/null
@@ -0,0 +1,65 @@
+//=-- AnnotatedPath.h - An annotated list of ExplodedNodes -*- C++ -*-------==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines AnnotatedPath, which represents a collection of
+//  annotated ExplodedNodes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_ANNOTPATH
+#define LLVM_CLANG_ANALYSIS_ANNOTPATH
+
+#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
+#include <string>
+#include <list>
+
+namespace clang {
+
+  class Expr;
+
+template <typename STATE>
+class AnnotatedNode {
+  ExplodedNode<STATE> *Node;
+  std::string annotation;
+  Expr* E;
+
+public:
+  AnnotatedNode(ExplodedNode<STATE>* N, const std::string& annot,
+                Expr* e = NULL)
+  : Node(N), annotation(annot), E(e) {}
+
+  ExplodedNode<STATE> getNode() const { return Node; }
+  
+  const std::string& getString() const { return annotation; }
+  
+  Expr* getExpr() const { return E; }
+};
+  
+
+template <typename STATE>
+class AnnotatedPath {
+  typedef std::list<AnnotatedNode<STATE> >  impl;
+  impl path;
+public:
+  AnnotatedPath();
+  
+  void push_back(ExplodedNode<STATE>* N, const std::string& s, Expr* E = NULL) {
+    path.push_back(AnnotatedNode<STATE>(N, s, E));
+  }
+  
+  typedef typename impl::iterator iterator;
+  
+  iterator begin() { return path.begin(); }
+  iterator end() { return path.end(); }
+  
+};
+  
+} // end clang namespace
+
+#endif
diff --git a/include/clang/Analysis/PathSensitive/GRAuditor.h b/include/clang/Analysis/PathSensitive/GRAuditor.h
new file mode 100644 (file)
index 0000000..29fb3bc
--- /dev/null
@@ -0,0 +1,38 @@
+//==- GRAuditor.h - Observers of the creation of ExplodedNodes------*- C++ -*-//
+//             
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines GRAuditor and its primary subclasses, an interface
+//  to audit the creation of ExplodedNodes.  This interface can be used
+//  to implement simple checkers that do not mutate analysis state but
+//  instead operate by perfoming simple logical checks at key monitoring
+//  locations (e.g., function calls).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_GRAUDITOR
+#define LLVM_CLANG_ANALYSIS_GRAUDITOR
+
+#include "clang/AST/Expr.h"
+#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
+
+namespace clang {
+  
+template <typename STATE>
+class GRAuditor {
+public:
+  typedef ExplodedNode<STATE>   NodeTy;
+  
+  virtual ~GRAuditor() {}
+  virtual bool Audit(NodeTy* N) = 0;
+};
+  
+  
+} // end clang namespace
+
+#endif
index a00292b1c9b5da2de539bc43cb708a1b098356e3..e22df35ab61ae981c93643642f05b6eaf0cda003 100644 (file)
@@ -15,7 +15,7 @@
 #ifndef LLVM_CLANG_ANALYSIS_GRENGINE
 #define LLVM_CLANG_ANALYSIS_GRENGINE
 
-#include "clang/AST/Stmt.h"
+#include "clang/AST/Expr.h"
 #include "clang/Analysis/PathSensitive/ExplodedGraph.h"
 #include "clang/Analysis/PathSensitive/GRWorkList.h"
 #include "clang/Analysis/PathSensitive/GRBlockCounter.h"
@@ -163,6 +163,16 @@ public:
   CFGBlock* getBlock() const { return &B; }
 };
 
+template <typename STATE>
+class GRNodeAuditor {
+public:
+  typedef ExplodedNode<STATE>   NodeTy;
+  
+  virtual ~GRNodeAuditor() {}
+  virtual bool Audit(NodeTy* N) = 0;
+};
+  
+  
 template<typename STATE>
 class GRStmtNodeBuilder  {
   typedef STATE                   StateTy;
@@ -171,10 +181,28 @@ class GRStmtNodeBuilder  {
   GRStmtNodeBuilderImpl& NB;
   StateTy* CleanedState;
   
+  GRNodeAuditor<StateTy> **CallExprAuditBeg, **CallExprAuditEnd;
+  GRNodeAuditor<StateTy> **ObjCMsgExprAuditBeg, **ObjCMsgExprAuditEnd;
+  
 public:
-  GRStmtNodeBuilder(GRStmtNodeBuilderImpl& nb) : NB(nb), BuildSinks(false) {
+  GRStmtNodeBuilder(GRStmtNodeBuilderImpl& nb) : NB(nb),
+    CallExprAuditBeg(0), CallExprAuditEnd(0),
+    ObjCMsgExprAuditBeg(0), ObjCMsgExprAuditEnd(0),  BuildSinks(false) {
+      
     CleanedState = getLastNode()->getState();
   }
+  
+  void setObjCMsgExprAuditors(GRNodeAuditor<StateTy> **B,
+                              GRNodeAuditor<StateTy> **E) {
+    ObjCMsgExprAuditBeg = B;
+    ObjCMsgExprAuditEnd = E;
+  }
+  
+  void setCallExprAuditors(GRNodeAuditor<StateTy> **B,
+                           GRNodeAuditor<StateTy> **E) {
+    CallExprAuditBeg = B;
+    CallExprAuditEnd = E;
+  }  
     
   NodeTy* getLastNode() const {
     return static_cast<NodeTy*>(NB.getLastNode());
@@ -223,8 +251,18 @@ public:
     if (N) {      
       if (BuildSinks)
         N->markAsSink();
-      else
+      else {
         Dst.Add(N);
+        
+        if (isa<CallExpr>(S))
+          for (GRNodeAuditor<StateTy>** I = CallExprAuditBeg;
+               I != CallExprAuditEnd; ++I)
+            (*I)->Audit(N);
+        else if (isa<ObjCMessageExpr>(S))
+          for (GRNodeAuditor<StateTy>** I = ObjCMsgExprAuditBeg;
+               I != ObjCMsgExprAuditEnd; ++I)
+            (*I)->Audit(N);
+      }
     }
     
     return N;
index 96a6d4ab5eac4371660265940c9ae2b130f4d8ab..b04469efc3601d2dc8df2ed913daeaa5f93ccd4c 100644 (file)
 
 #include "clang/Analysis/PathSensitive/GRCoreEngine.h"
 #include "clang/Analysis/PathSensitive/ValueState.h"
+#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
 #include "clang/Analysis/PathSensitive/GRTransferFuncs.h"
 
 namespace clang {
   
+  class StatelessChecks;
+  
 class GRExprEngine {
   
 public:
@@ -32,6 +35,7 @@ public:
   typedef GRIndirectGotoNodeBuilder<GRExprEngine>  IndirectGotoNodeBuilder;
   typedef GRSwitchNodeBuilder<GRExprEngine>        SwitchNodeBuilder;
   typedef ExplodedNodeSet<StateTy>                 NodeSet;
+  
     
 protected:
   /// G - the simulation graph.
@@ -67,6 +71,12 @@ protected:
   
   /// CurrentStmt - The current block-level statement.
   Stmt* CurrentStmt;
+  
+  typedef llvm::SmallVector<GRSimpleAPICheck*,2> SimpleChecksTy;
+  
+  SimpleChecksTy CallChecks;
+  SimpleChecksTy MsgExprChecks;
+  
 
   typedef llvm::SmallPtrSet<NodeTy*,2> UndefBranchesTy;  
   typedef llvm::SmallPtrSet<NodeTy*,2> UndefStoresTy;
@@ -278,6 +288,26 @@ public:
     return UndefReceivers.end();
   }
   
+  typedef SimpleChecksTy::iterator simple_checks_iterator;
+  
+  simple_checks_iterator call_auditors_begin() { return CallChecks.begin(); }
+  simple_checks_iterator call_auditors_end() { return CallChecks.end(); }
+  
+  simple_checks_iterator msgexpr_auditors_begin() {
+    return MsgExprChecks.begin();
+  }
+  simple_checks_iterator msgexpr_auditors_end() {
+    return MsgExprChecks.end();
+  }
+  
+  void AddCallCheck(GRSimpleAPICheck* A) {
+    CallChecks.push_back(A);
+  }
+  
+  void AddObjCMessageExprCheck(GRSimpleAPICheck* A) {
+    MsgExprChecks.push_back(A);
+  }
+  
   /// ProcessStmt - Called by GRCoreEngine. Used to generate new successor
   ///  nodes by processing the 'effects' of a block-level statement.  
   void ProcessStmt(Stmt* S, StmtNodeBuilder& builder);    
diff --git a/include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h b/include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h
new file mode 100644 (file)
index 0000000..2da2cb4
--- /dev/null
@@ -0,0 +1,35 @@
+// GRCheckAPI.h - Simple API checks based on GRAuditor ------------*- C++ -*--//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the interface for building simple, path-sensitive checks
+//  that are stateless and only emit warnings at errors that occur at
+//  CallExpr or ObjCMessageExpr.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_GRAPICHECKS
+#define LLVM_CLANG_ANALYSIS_GRAPICHECKS
+
+#include "clang/Analysis/PathSensitive/GRAuditor.h"
+
+namespace clang {
+  
+class ValueState;
+  
+class GRSimpleAPICheck : public GRAuditor<ValueState> {
+public:
+  GRSimpleAPICheck() {}
+  virtual ~GRSimpleAPICheck() {}
+
+
+};
+
+} // end namespace clang
+
+#endif
diff --git a/lib/Analysis/BasicObjCFoundationChecks.cpp b/lib/Analysis/BasicObjCFoundationChecks.cpp
new file mode 100644 (file)
index 0000000..0fae7dd
--- /dev/null
@@ -0,0 +1,139 @@
+//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines BasicObjCFoundationChecks, a class that encapsulates
+//  a set of simple checks to run on Objective-C code using Apple's Foundation
+//  classes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
+#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
+#include "clang/Analysis/PathSensitive/ValueState.h"
+#include "clang/Analysis/PathSensitive/AnnotatedPath.h"
+#include "clang/Analysis/PathDiagnostic.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ASTContext.h"
+#include "llvm/Support/Compiler.h"
+
+#include <vector>
+
+using namespace clang;
+  
+namespace {
+  
+class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
+
+ ASTContext &Ctx;
+ ValueStateManager* VMgr;
+ std::list<AnnotatedPath<ValueState> > Errors;
+      
+ RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
+      
+ bool isNSString(ObjCInterfaceType* T, const char* suffix);
+ bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
+      
+ void RegisterError(NodeTy* N, Expr* E, const char *msg);
+
+public:
+  BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr) 
+    : Ctx(ctx), VMgr(vmgr) {}
+      
+  virtual ~BasicObjCFoundationChecks() {}
+  
+  virtual bool Audit(ExplodedNode<ValueState>* N);
+};
+  
+} // end anonymous namespace
+
+
+bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
+  
+  ObjCMessageExpr* ME =
+    cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
+  
+  Expr* Receiver = ME->getReceiver();
+  
+  if (!Receiver)
+    return false;
+  
+  assert (Receiver->getType()->isPointerType());
+
+  const PointerType* T = Receiver->getType()->getAsPointerType();
+
+  ObjCInterfaceType* ReceiverType =
+    dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
+  
+  if (!ReceiverType)
+    return false;
+  
+  const char* name = ReceiverType->getDecl()->getIdentifier()->getName();  
+
+  if (name[0] != 'N' || name[1] != 'S')
+    return false;
+      
+  name += 2;
+  
+  // FIXME: Make all of this faster.
+  
+  if (isNSString(ReceiverType, name))
+    return AuditNSString(N, ME);
+
+  return false;  
+}
+
+//===----------------------------------------------------------------------===//
+// Error reporting.
+//===----------------------------------------------------------------------===//
+
+
+void BasicObjCFoundationChecks::RegisterError(NodeTy* N,
+                                              Expr* E, const char *msg) {
+  
+  Errors.push_back(AnnotatedPath<ValueState>());
+  Errors.back().push_back(N, msg, E);
+}
+
+//===----------------------------------------------------------------------===//
+// NSString checking.
+//===----------------------------------------------------------------------===//
+
+bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
+                                           const char* suffix) {
+  
+  return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
+}
+
+bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N, 
+                                              ObjCMessageExpr* ME) {
+  
+  Selector S = ME->getSelector();
+  
+  if (S.isUnarySelector())
+    return false;
+
+  // FIXME: This is going to be really slow doing these checks with
+  //  lexical comparisons.
+  
+  std::string name = S.getName();
+  ValueState* St = N->getState();
+  
+  if (name == "compare:") {
+    // Check if the compared NSString is nil.
+    Expr * E = ME->getArg(0);
+    RVal X = GetRVal(St, E);
+    
+    if (isa<lval::ConcreteInt>(X)) {
+      RegisterError(N, E,
+                    "Argument to NSString method 'compare:' cannot be nil.");
+    }
+  }
+  
+  return false;
+}
index bce1cbd90c709b45439a83339e36288834305e49..b6f164a57f1035bf38443f3857557314ffa37655 100644 (file)
@@ -407,6 +407,19 @@ void GRExprEngine::ProcessStmt(Stmt* S, StmtNodeBuilder& builder) {
   CurrentStmt = S;
   NodeSet Dst;
   
+  // Set up our simple checks.
+  
+  if (!MsgExprChecks.empty())
+    Builder->setObjCMsgExprAuditors(
+      (GRNodeAuditor<ValueState>**) &MsgExprChecks[0],
+      (GRNodeAuditor<ValueState>**) (&MsgExprChecks[0] + MsgExprChecks.size()));
+
+  
+  if (!CallChecks.empty())
+    Builder->setCallExprAuditors(
+      (GRNodeAuditor<ValueState>**) &CallChecks[0],
+      (GRNodeAuditor<ValueState>**) (&CallChecks[0] + CallChecks.size()));
+  
   // Create the cleaned state.
 
   CleanedState = StateMgr.RemoveDeadBindings(StmtEntryNode->getState(),