Refactored auditor interface within GRExprEngine and GRCoreEngine to use a "batch auditor" to dispatch to specialized auditors instead of having a separate vector for each audited Expr*. This not only provides a much cleaner implementation, but also allows us to install auditors for any expression.
llvm-svn: 53464
This commit is contained in:
parent
05706e8859
commit
c50e1a196e
|
|
@ -165,36 +165,26 @@ public:
|
||||||
|
|
||||||
template<typename STATE>
|
template<typename STATE>
|
||||||
class GRStmtNodeBuilder {
|
class GRStmtNodeBuilder {
|
||||||
|
public:
|
||||||
typedef STATE StateTy;
|
typedef STATE StateTy;
|
||||||
typedef ExplodedNode<StateTy> NodeTy;
|
typedef ExplodedNode<StateTy> NodeTy;
|
||||||
|
|
||||||
|
private:
|
||||||
GRStmtNodeBuilderImpl& NB;
|
GRStmtNodeBuilderImpl& NB;
|
||||||
const StateTy* CleanedState;
|
const StateTy* CleanedState;
|
||||||
|
GRAuditor<StateTy>* Auditor;
|
||||||
GRAuditor<StateTy> **CallExprAuditBeg, **CallExprAuditEnd;
|
|
||||||
GRAuditor<StateTy> **ObjCMsgExprAuditBeg, **ObjCMsgExprAuditEnd;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GRStmtNodeBuilder(GRStmtNodeBuilderImpl& nb) : NB(nb),
|
GRStmtNodeBuilder(GRStmtNodeBuilderImpl& nb) : NB(nb),
|
||||||
CallExprAuditBeg(0), CallExprAuditEnd(0),
|
Auditor(0), PurgingDeadSymbols(false),
|
||||||
ObjCMsgExprAuditBeg(0), ObjCMsgExprAuditEnd(0),
|
|
||||||
PurgingDeadSymbols(false),
|
|
||||||
BuildSinks(false), HasGeneratedNode(false) {
|
BuildSinks(false), HasGeneratedNode(false) {
|
||||||
|
|
||||||
CleanedState = getLastNode()->getState();
|
CleanedState = getLastNode()->getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setObjCMsgExprAuditors(GRAuditor<StateTy> **B,
|
void setAuditor(GRAuditor<StateTy>* A) {
|
||||||
GRAuditor<StateTy> **E) {
|
Auditor = A;
|
||||||
ObjCMsgExprAuditBeg = B;
|
|
||||||
ObjCMsgExprAuditEnd = E;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCallExprAuditors(GRAuditor<StateTy> **B,
|
|
||||||
GRAuditor<StateTy> **E) {
|
|
||||||
CallExprAuditBeg = B;
|
|
||||||
CallExprAuditEnd = E;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeTy* getLastNode() const {
|
NodeTy* getLastNode() const {
|
||||||
return static_cast<NodeTy*>(NB.getLastNode());
|
return static_cast<NodeTy*>(NB.getLastNode());
|
||||||
|
|
@ -241,23 +231,9 @@ public:
|
||||||
NodeTy* Pred, const StateTy* St) {
|
NodeTy* Pred, const StateTy* St) {
|
||||||
|
|
||||||
const StateTy* PredState = GetState(Pred);
|
const StateTy* PredState = GetState(Pred);
|
||||||
|
|
||||||
GRAuditor<StateTy> **AB = NULL, **AE = NULL;
|
|
||||||
|
|
||||||
switch (S->getStmtClass()) {
|
|
||||||
default: break;
|
|
||||||
case Stmt::CallExprClass:
|
|
||||||
AB = CallExprAuditBeg;
|
|
||||||
AE = CallExprAuditEnd;
|
|
||||||
break;
|
|
||||||
case Stmt::ObjCMessageExprClass:
|
|
||||||
AB = ObjCMsgExprAuditBeg;
|
|
||||||
AE = ObjCMsgExprAuditEnd;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the state hasn't changed, don't generate a new node.
|
// If the state hasn't changed, don't generate a new node.
|
||||||
if (!BuildSinks && St == PredState && AB == NULL) {
|
if (!BuildSinks && St == PredState && Auditor == 0) {
|
||||||
Dst.Add(Pred);
|
Dst.Add(Pred);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -268,9 +244,8 @@ public:
|
||||||
if (BuildSinks)
|
if (BuildSinks)
|
||||||
N->markAsSink();
|
N->markAsSink();
|
||||||
else {
|
else {
|
||||||
for ( ; AB != AE; ++AB)
|
if (Auditor && Auditor->Audit(N))
|
||||||
if ((*AB)->Audit(N))
|
N->markAsSink();
|
||||||
N->markAsSink();
|
|
||||||
|
|
||||||
Dst.Add(N);
|
Dst.Add(N);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,10 +98,7 @@ protected:
|
||||||
Selector* NSExceptionInstanceRaiseSelectors;
|
Selector* NSExceptionInstanceRaiseSelectors;
|
||||||
Selector RaiseSel;
|
Selector RaiseSel;
|
||||||
|
|
||||||
typedef llvm::SmallVector<GRSimpleAPICheck*,2> SimpleChecksTy;
|
llvm::OwningPtr<GRSimpleAPICheck> BatchAuditor;
|
||||||
|
|
||||||
SimpleChecksTy CallChecks;
|
|
||||||
SimpleChecksTy MsgExprChecks;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef llvm::SmallPtrSet<NodeTy*,2> UndefBranchesTy;
|
typedef llvm::SmallPtrSet<NodeTy*,2> UndefBranchesTy;
|
||||||
|
|
@ -347,21 +344,7 @@ public:
|
||||||
return UndefReceivers.end();
|
return UndefReceivers.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef SimpleChecksTy::iterator simple_checks_iterator;
|
void AddCheck(GRSimpleAPICheck* A, Stmt::StmtClass C);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
void AddObjCMessageExprCheck(GRSimpleAPICheck* A);
|
|
||||||
|
|
||||||
/// ProcessStmt - Called by GRCoreEngine. Used to generate new successor
|
/// ProcessStmt - Called by GRCoreEngine. Used to generate new successor
|
||||||
/// nodes by processing the 'effects' of a block-level statement.
|
/// nodes by processing the 'effects' of a block-level statement.
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,13 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "clang/Analysis/PathSensitive/BasicStore.h"
|
||||||
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
|
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
|
||||||
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
||||||
#include "clang/Basic/SourceManager.h"
|
#include "clang/Basic/SourceManager.h"
|
||||||
#include "llvm/Support/Streams.h"
|
#include "llvm/Support/Streams.h"
|
||||||
|
#include "llvm/ADT/ImmutableList.h"
|
||||||
#include "clang/Analysis/PathSensitive/BasicStore.h"
|
#include "llvm/Support/Compiler.h"
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#include "llvm/Support/GraphWriter.h"
|
#include "llvm/Support/GraphWriter.h"
|
||||||
|
|
@ -34,6 +35,79 @@ using llvm::APSInt;
|
||||||
// Engine construction and deletion.
|
// Engine construction and deletion.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN MappedBatchAuditor : public GRSimpleAPICheck {
|
||||||
|
typedef llvm::ImmutableList<GRSimpleAPICheck*> Checks;
|
||||||
|
typedef llvm::DenseMap<void*,Checks> MapTy;
|
||||||
|
|
||||||
|
MapTy M;
|
||||||
|
Checks::Factory F;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MappedBatchAuditor(llvm::BumpPtrAllocator& Alloc) : F(Alloc) {}
|
||||||
|
|
||||||
|
virtual ~MappedBatchAuditor() {
|
||||||
|
llvm::DenseSet<GRSimpleAPICheck*> AlreadyVisited;
|
||||||
|
|
||||||
|
for (MapTy::iterator MI = M.begin(), ME = M.end(); MI != ME; ++MI)
|
||||||
|
for (Checks::iterator I=MI->second.begin(), E=MI->second.end(); I!=E;++I){
|
||||||
|
|
||||||
|
GRSimpleAPICheck* check = *I;
|
||||||
|
|
||||||
|
if (AlreadyVisited.count(check))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
AlreadyVisited.insert(check);
|
||||||
|
delete check;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddCheck(GRSimpleAPICheck* A, Stmt::StmtClass C) {
|
||||||
|
assert (A && "Check cannot be null.");
|
||||||
|
void* key = reinterpret_cast<void*>((uintptr_t) C);
|
||||||
|
MapTy::iterator I = M.find(key);
|
||||||
|
M[key] = F.Concat(A, I == M.end() ? F.GetEmptyList() : I->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void EmitWarnings(BugReporter& BR) {
|
||||||
|
llvm::DenseSet<GRSimpleAPICheck*> AlreadyVisited;
|
||||||
|
|
||||||
|
for (MapTy::iterator MI = M.begin(), ME = M.end(); MI != ME; ++MI)
|
||||||
|
for (Checks::iterator I=MI->second.begin(), E=MI->second.end(); I!=E;++I){
|
||||||
|
|
||||||
|
GRSimpleAPICheck* check = *I;
|
||||||
|
|
||||||
|
if (AlreadyVisited.count(check))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
check->EmitWarnings(BR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool Audit(NodeTy* N) {
|
||||||
|
Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
|
||||||
|
void* key = reinterpret_cast<void*>((uintptr_t) S->getStmtClass());
|
||||||
|
MapTy::iterator MI = M.find(key);
|
||||||
|
|
||||||
|
if (MI == M.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool isSink = false;
|
||||||
|
|
||||||
|
for (Checks::iterator I=MI->second.begin(), E=MI->second.end(); I!=E; ++I)
|
||||||
|
isSink |= (*I)->Audit(N);
|
||||||
|
|
||||||
|
return isSink;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Engine construction and deletion.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) {
|
static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) {
|
||||||
IdentifierInfo* II = &Ctx.Idents.get(name);
|
IdentifierInfo* II = &Ctx.Idents.get(name);
|
||||||
return Ctx.Selectors.getSelector(0, &II);
|
return Ctx.Selectors.getSelector(0, &II);
|
||||||
|
|
@ -58,14 +132,7 @@ GRExprEngine::GRExprEngine(CFG& cfg, Decl& CD, ASTContext& Ctx,
|
||||||
GRExprEngine::~GRExprEngine() {
|
GRExprEngine::~GRExprEngine() {
|
||||||
for (BugTypeSet::iterator I = BugTypes.begin(), E = BugTypes.end(); I!=E; ++I)
|
for (BugTypeSet::iterator I = BugTypes.begin(), E = BugTypes.end(); I!=E; ++I)
|
||||||
delete *I;
|
delete *I;
|
||||||
|
|
||||||
for (SimpleChecksTy::iterator I = CallChecks.begin(), E = CallChecks.end();
|
|
||||||
I != E; ++I)
|
|
||||||
delete *I;
|
|
||||||
|
|
||||||
for (SimpleChecksTy::iterator I=MsgExprChecks.begin(), E=MsgExprChecks.end();
|
|
||||||
I != E; ++I)
|
|
||||||
delete *I;
|
|
||||||
|
|
||||||
delete [] NSExceptionInstanceRaiseSelectors;
|
delete [] NSExceptionInstanceRaiseSelectors;
|
||||||
}
|
}
|
||||||
|
|
@ -104,16 +171,9 @@ void GRExprEngine::EmitWarnings(BugReporterData& BRData) {
|
||||||
(*I)->EmitWarnings(BR);
|
(*I)->EmitWarnings(BR);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (SimpleChecksTy::iterator I = CallChecks.begin(), E = CallChecks.end();
|
if (BatchAuditor) {
|
||||||
I != E; ++I) {
|
|
||||||
GRBugReporter BR(BRData, *this);
|
GRBugReporter BR(BRData, *this);
|
||||||
(*I)->EmitWarnings(BR);
|
BatchAuditor->EmitWarnings(BR);
|
||||||
}
|
|
||||||
|
|
||||||
for (SimpleChecksTy::iterator I=MsgExprChecks.begin(), E=MsgExprChecks.end();
|
|
||||||
I != E; ++I) {
|
|
||||||
GRBugReporter BR(BRData, *this);
|
|
||||||
(*I)->EmitWarnings(BR);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,12 +182,11 @@ void GRExprEngine::setTransferFunctions(GRTransferFuncs* tf) {
|
||||||
TF->RegisterChecks(*this);
|
TF->RegisterChecks(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRExprEngine::AddCallCheck(GRSimpleAPICheck* A) {
|
void GRExprEngine::AddCheck(GRSimpleAPICheck* A, Stmt::StmtClass C) {
|
||||||
CallChecks.push_back(A);
|
if (!BatchAuditor)
|
||||||
}
|
BatchAuditor.reset(new MappedBatchAuditor(getGraph().getAllocator()));
|
||||||
|
|
||||||
void GRExprEngine::AddObjCMessageExprCheck(GRSimpleAPICheck* A) {
|
((MappedBatchAuditor*) BatchAuditor.get())->AddCheck(A, C);
|
||||||
MsgExprChecks.push_back(A);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ValueState* GRExprEngine::getInitialState() {
|
const ValueState* GRExprEngine::getInitialState() {
|
||||||
|
|
@ -186,26 +245,14 @@ void GRExprEngine::ProcessStmt(Stmt* S, StmtNodeBuilder& builder) {
|
||||||
CurrentStmt = S;
|
CurrentStmt = S;
|
||||||
|
|
||||||
// Set up our simple checks.
|
// Set up our simple checks.
|
||||||
|
if (BatchAuditor)
|
||||||
|
Builder->setAuditor(BatchAuditor.get());
|
||||||
|
|
||||||
// FIXME: This can probably be installed directly in GRCoreEngine, obviating
|
// Create the cleaned state.
|
||||||
// the need to do a copy every time we hit a block-level statement.
|
|
||||||
|
|
||||||
if (!MsgExprChecks.empty())
|
|
||||||
Builder->setObjCMsgExprAuditors((GRAuditor<ValueState>**) &MsgExprChecks[0],
|
|
||||||
(GRAuditor<ValueState>**) (&MsgExprChecks[0] + MsgExprChecks.size()));
|
|
||||||
|
|
||||||
|
|
||||||
if (!CallChecks.empty())
|
|
||||||
Builder->setCallExprAuditors((GRAuditor<ValueState>**) &CallChecks[0],
|
|
||||||
(GRAuditor<ValueState>**) (&CallChecks[0] + CallChecks.size()));
|
|
||||||
|
|
||||||
// Create the cleaned state.
|
|
||||||
|
|
||||||
CleanedState = StateMgr.RemoveDeadBindings(EntryNode->getState(), CurrentStmt,
|
CleanedState = StateMgr.RemoveDeadBindings(EntryNode->getState(), CurrentStmt,
|
||||||
Liveness, DeadSymbols);
|
Liveness, DeadSymbols);
|
||||||
|
|
||||||
// Process any special transfer function for dead symbols.
|
// Process any special transfer function for dead symbols.
|
||||||
|
|
||||||
NodeSet Tmp;
|
NodeSet Tmp;
|
||||||
|
|
||||||
if (DeadSymbols.empty())
|
if (DeadSymbols.empty())
|
||||||
|
|
|
||||||
|
|
@ -354,11 +354,11 @@ void GRSimpleVals::RegisterChecks(GRExprEngine& Eng) {
|
||||||
ASTContext& Ctx = Eng.getContext();
|
ASTContext& Ctx = Eng.getContext();
|
||||||
ValueStateManager* VMgr = &Eng.getStateManager();
|
ValueStateManager* VMgr = &Eng.getStateManager();
|
||||||
|
|
||||||
GRSimpleAPICheck* Check = CreateBasicObjCFoundationChecks(Ctx, VMgr);
|
GRSimpleAPICheck* Check = CreateBasicObjCFoundationChecks(Ctx, VMgr);
|
||||||
Eng.AddObjCMessageExprCheck(Check);
|
Eng.AddCheck(Check, Stmt::ObjCMessageExprClass);
|
||||||
|
|
||||||
Check = CreateAuditCFNumberCreate(Ctx, VMgr);
|
Check = CreateAuditCFNumberCreate(Ctx, VMgr);
|
||||||
Eng.AddCallCheck(Check);
|
Eng.AddCheck(Check, Stmt::CallExprClass);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue