aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Analysis/PathSensitive/AnnotatedPath.h65
-rw-r--r--include/clang/Analysis/PathSensitive/GRAuditor.h38
-rw-r--r--include/clang/Analysis/PathSensitive/GRCoreEngine.h44
-rw-r--r--include/clang/Analysis/PathSensitive/GRExprEngine.h30
-rw-r--r--include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h35
-rw-r--r--lib/Analysis/BasicObjCFoundationChecks.cpp139
-rw-r--r--lib/Analysis/GRExprEngine.cpp13
7 files changed, 361 insertions, 3 deletions
diff --git a/include/clang/Analysis/PathSensitive/AnnotatedPath.h b/include/clang/Analysis/PathSensitive/AnnotatedPath.h
new file mode 100644
index 0000000000..299634b7f9
--- /dev/null
+++ b/include/clang/Analysis/PathSensitive/AnnotatedPath.h
@@ -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
index 0000000000..29fb3bc186
--- /dev/null
+++ b/include/clang/Analysis/PathSensitive/GRAuditor.h
@@ -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
diff --git a/include/clang/Analysis/PathSensitive/GRCoreEngine.h b/include/clang/Analysis/PathSensitive/GRCoreEngine.h
index a00292b1c9..e22df35ab6 100644
--- a/include/clang/Analysis/PathSensitive/GRCoreEngine.h
+++ b/include/clang/Analysis/PathSensitive/GRCoreEngine.h
@@ -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;
diff --git a/include/clang/Analysis/PathSensitive/GRExprEngine.h b/include/clang/Analysis/PathSensitive/GRExprEngine.h
index 96a6d4ab5e..b04469efc3 100644
--- a/include/clang/Analysis/PathSensitive/GRExprEngine.h
+++ b/include/clang/Analysis/PathSensitive/GRExprEngine.h
@@ -15,10 +15,13 @@
#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
index 0000000000..2da2cb4045
--- /dev/null
+++ b/include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h
@@ -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
index 0000000000..0fae7ddcd3
--- /dev/null
+++ b/lib/Analysis/BasicObjCFoundationChecks.cpp
@@ -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;
+}
diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp
index bce1cbd90c..b6f164a57f 100644
--- a/lib/Analysis/GRExprEngine.cpp
+++ b/lib/Analysis/GRExprEngine.cpp
@@ -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(),