--- /dev/null
+//=-- 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
--- /dev/null
+//==- 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
#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"
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;
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());
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;
#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:
typedef GRIndirectGotoNodeBuilder<GRExprEngine> IndirectGotoNodeBuilder;
typedef GRSwitchNodeBuilder<GRExprEngine> SwitchNodeBuilder;
typedef ExplodedNodeSet<StateTy> NodeSet;
+
protected:
/// G - the simulation graph.
/// 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;
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);
--- /dev/null
+// 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
--- /dev/null
+//== 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;
+}
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(),