diff options
-rw-r--r-- | include/clang/Analysis/PathSensitive/AnnotatedPath.h | 65 | ||||
-rw-r--r-- | include/clang/Analysis/PathSensitive/GRAuditor.h | 38 | ||||
-rw-r--r-- | include/clang/Analysis/PathSensitive/GRCoreEngine.h | 44 | ||||
-rw-r--r-- | include/clang/Analysis/PathSensitive/GRExprEngine.h | 30 | ||||
-rw-r--r-- | include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h | 35 | ||||
-rw-r--r-- | lib/Analysis/BasicObjCFoundationChecks.cpp | 139 | ||||
-rw-r--r-- | lib/Analysis/GRExprEngine.cpp | 13 |
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(), |