From 99c6ad3f22b865d0f4cce52bc36904403c9ed4c4 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Thu, 27 Mar 2008 07:25:52 +0000 Subject: [PATCH] Added "GRAuditor" and "GRSimpleAPICheck" interface to allow simple stateless checkers to be injected into the analyzer. 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 --- .../Analysis/PathSensitive/AnnotatedPath.h | 65 ++++++++ .../clang/Analysis/PathSensitive/GRAuditor.h | 38 +++++ .../Analysis/PathSensitive/GRCoreEngine.h | 44 +++++- .../Analysis/PathSensitive/GRExprEngine.h | 30 ++++ .../Analysis/PathSensitive/GRSimpleAPICheck.h | 35 +++++ lib/Analysis/BasicObjCFoundationChecks.cpp | 139 ++++++++++++++++++ lib/Analysis/GRExprEngine.cpp | 13 ++ 7 files changed, 361 insertions(+), 3 deletions(-) create mode 100644 include/clang/Analysis/PathSensitive/AnnotatedPath.h create mode 100644 include/clang/Analysis/PathSensitive/GRAuditor.h create mode 100644 include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h create mode 100644 lib/Analysis/BasicObjCFoundationChecks.cpp 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 +#include + +namespace clang { + + class Expr; + +template +class AnnotatedNode { + ExplodedNode *Node; + std::string annotation; + Expr* E; + +public: + AnnotatedNode(ExplodedNode* N, const std::string& annot, + Expr* e = NULL) + : Node(N), annotation(annot), E(e) {} + + ExplodedNode getNode() const { return Node; } + + const std::string& getString() const { return annotation; } + + Expr* getExpr() const { return E; } +}; + + +template +class AnnotatedPath { + typedef std::list > impl; + impl path; +public: + AnnotatedPath(); + + void push_back(ExplodedNode* N, const std::string& s, Expr* E = NULL) { + path.push_back(AnnotatedNode(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 +class GRAuditor { +public: + typedef ExplodedNode 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 +class GRNodeAuditor { +public: + typedef ExplodedNode NodeTy; + + virtual ~GRNodeAuditor() {} + virtual bool Audit(NodeTy* N) = 0; +}; + + template class GRStmtNodeBuilder { typedef STATE StateTy; @@ -171,10 +181,28 @@ class GRStmtNodeBuilder { GRStmtNodeBuilderImpl& NB; StateTy* CleanedState; + GRNodeAuditor **CallExprAuditBeg, **CallExprAuditEnd; + GRNodeAuditor **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 **B, + GRNodeAuditor **E) { + ObjCMsgExprAuditBeg = B; + ObjCMsgExprAuditEnd = E; + } + + void setCallExprAuditors(GRNodeAuditor **B, + GRNodeAuditor **E) { + CallExprAuditBeg = B; + CallExprAuditEnd = E; + } NodeTy* getLastNode() const { return static_cast(NB.getLastNode()); @@ -223,8 +251,18 @@ public: if (N) { if (BuildSinks) N->markAsSink(); - else + else { Dst.Add(N); + + if (isa(S)) + for (GRNodeAuditor** I = CallExprAuditBeg; + I != CallExprAuditEnd; ++I) + (*I)->Audit(N); + else if (isa(S)) + for (GRNodeAuditor** 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 IndirectGotoNodeBuilder; typedef GRSwitchNodeBuilder SwitchNodeBuilder; typedef ExplodedNodeSet NodeSet; + protected: /// G - the simulation graph. @@ -67,6 +71,12 @@ protected: /// CurrentStmt - The current block-level statement. Stmt* CurrentStmt; + + typedef llvm::SmallVector SimpleChecksTy; + + SimpleChecksTy CallChecks; + SimpleChecksTy MsgExprChecks; + typedef llvm::SmallPtrSet UndefBranchesTy; typedef llvm::SmallPtrSet 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 { +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 + +using namespace clang; + +namespace { + +class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck { + + ASTContext &Ctx; + ValueStateManager* VMgr; + std::list > 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* N); +}; + +} // end anonymous namespace + + +bool BasicObjCFoundationChecks::Audit(ExplodedNode* N) { + + ObjCMessageExpr* ME = + cast(cast(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(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()); + 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(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**) &MsgExprChecks[0], + (GRNodeAuditor**) (&MsgExprChecks[0] + MsgExprChecks.size())); + + + if (!CallChecks.empty()) + Builder->setCallExprAuditors( + (GRNodeAuditor**) &CallChecks[0], + (GRNodeAuditor**) (&CallChecks[0] + CallChecks.size())); + // Create the cleaned state. CleanedState = StateMgr.RemoveDeadBindings(StmtEntryNode->getState(), -- 2.40.0