Add static analyzer support for conditionally executing static initializers.

llvm-svn: 178318
This commit is contained in:
Ted Kremenek 2013-03-29 00:09:28 +00:00
parent 233c1b0c77
commit 338c3aa8d1
10 changed files with 81 additions and 29 deletions

View File

@ -96,6 +96,10 @@ private:
void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B, void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B,
ExplodedNode *Pred); ExplodedNode *Pred);
/// Handle conditional logic for running static initializers.
void HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
ExplodedNode *Pred);
private: private:
CoreEngine(const CoreEngine &) LLVM_DELETED_FUNCTION; CoreEngine(const CoreEngine &) LLVM_DELETED_FUNCTION;
void operator=(const CoreEngine &) LLVM_DELETED_FUNCTION; void operator=(const CoreEngine &) LLVM_DELETED_FUNCTION;

View File

@ -224,6 +224,15 @@ public:
const CFGBlock *DstT, const CFGBlock *DstT,
const CFGBlock *DstF); const CFGBlock *DstF);
/// Called by CoreEngine. Used to processing branching behavior
/// at static initalizers.
void processStaticInitializer(const DeclStmt *DS,
NodeBuilderContext& BuilderCtx,
ExplodedNode *Pred,
ExplodedNodeSet &Dst,
const CFGBlock *DstT,
const CFGBlock *DstF);
/// processIndirectGoto - Called by CoreEngine. Used to generate successor /// processIndirectGoto - Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a computed goto jump. /// nodes by processing the 'effects' of a computed goto jump.
void processIndirectGoto(IndirectGotoNodeBuilder& builder); void processIndirectGoto(IndirectGotoNodeBuilder& builder);

View File

@ -72,6 +72,15 @@ public:
const CFGBlock *DstT, const CFGBlock *DstT,
const CFGBlock *DstF) = 0; const CFGBlock *DstF) = 0;
/// Called by CoreEngine. Used to processing branching behavior
/// at static initalizers.
virtual void processStaticInitializer(const DeclStmt *DS,
NodeBuilderContext& BuilderCtx,
ExplodedNode *Pred,
ExplodedNodeSet &Dst,
const CFGBlock *DstT,
const CFGBlock *DstF) = 0;
/// Called by CoreEngine. Used to generate successor /// Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a computed goto jump. /// nodes by processing the 'effects' of a computed goto jump.
virtual void processIndirectGoto(IndirectGotoNodeBuilder& builder) = 0; virtual void processIndirectGoto(IndirectGotoNodeBuilder& builder) = 0;

View File

@ -1665,6 +1665,7 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
// whether or not they are initialized. // whether or not they are initialized.
if (Block) { if (Block) {
Succ = Block; Succ = Block;
Block = 0;
if (badCFG) if (badCFG)
return 0; return 0;
} }
@ -1714,15 +1715,14 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
if (ScopePos && VD == *ScopePos) if (ScopePos && VD == *ScopePos)
++ScopePos; ++ScopePos;
CFGBlock *B = Block ? Block : LastBlock; CFGBlock *B = LastBlock;
if (blockBeforeInit) { if (blockBeforeInit) {
Succ = B; Succ = B;
Block = 0; Block = createBlock(false);
CFGBlock *branchBlock = createBlock(false); Block->setTerminator(DS);
branchBlock->setTerminator(DS); addSuccessor(Block, blockBeforeInit);
addSuccessor(branchBlock, blockBeforeInit); addSuccessor(Block, B);
addSuccessor(branchBlock, B); B = Block;
B = branchBlock;
} }
return B; return B;

View File

@ -238,6 +238,6 @@ bool AnalyzerOptions::shouldPrunePaths() {
} }
bool AnalyzerOptions::shouldConditionalizeStaticInitializers() { bool AnalyzerOptions::shouldConditionalizeStaticInitializers() {
return getBooleanOption("conditional-static-initializers", false); return getBooleanOption("cfg-conditional-static-initializers", true);
} }

View File

@ -346,6 +346,11 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
default: default:
llvm_unreachable("Analysis for this terminator not implemented."); llvm_unreachable("Analysis for this terminator not implemented.");
// Model static initializers.
case Stmt::DeclStmtClass:
HandleStaticInit(cast<DeclStmt>(Term), B, Pred);
return;
case Stmt::BinaryOperatorClass: // '&&' and '||' case Stmt::BinaryOperatorClass: // '&&' and '||'
HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred); HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred);
return; return;
@ -456,6 +461,19 @@ void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
enqueue(Dst); enqueue(Dst);
} }
void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
ExplodedNode *Pred) {
assert(B->succ_size() == 2);
NodeBuilderContext Ctx(*this, B, Pred);
ExplodedNodeSet Dst;
SubEng.processStaticInitializer(DS, Ctx, Pred, Dst,
*(B->succ_begin()), *(B->succ_begin()+1));
// Enqueue the new frontier onto the worklist.
enqueue(Dst);
}
void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
ExplodedNode *Pred) { ExplodedNode *Pred) {
assert(B); assert(B);

View File

@ -1344,6 +1344,34 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
currBldrCtx = 0; currBldrCtx = 0;
} }
/// The GDM component containing the set of global variables which have been
/// previously initialized with explicit initializers.
REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet,
llvm::ImmutableSet<const VarDecl *>)
void ExprEngine::processStaticInitializer(const DeclStmt *DS,
NodeBuilderContext &BuilderCtx,
ExplodedNode *Pred,
clang::ento::ExplodedNodeSet &Dst,
const CFGBlock *DstT,
const CFGBlock *DstF) {
currBldrCtx = &BuilderCtx;
const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
ProgramStateRef state = Pred->getState();
bool initHasRun = state->contains<InitializedGlobalsSet>(VD);
BranchNodeBuilder builder(Pred, Dst, BuilderCtx, DstT, DstF);
if (!initHasRun) {
state = state->add<InitializedGlobalsSet>(VD);
}
builder.generateNode(state, initHasRun, Pred);
builder.markInfeasible(!initHasRun);
currBldrCtx = 0;
}
/// processIndirectGoto - Called by CoreEngine. Used to generate successor /// processIndirectGoto - Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a computed goto jump. /// nodes by processing the 'effects' of a computed goto jump.
void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {

View File

@ -423,11 +423,6 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV)); B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV));
} }
/// The GDM component containing the set of global variables which have been
/// previously initialized with explicit initializers.
REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet,
llvm::ImmutableSet<const VarDecl *> )
void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
ExplodedNodeSet &Dst) { ExplodedNodeSet &Dst) {
// Assumption: The CFG has one DeclStmt per Decl. // Assumption: The CFG has one DeclStmt per Decl.
@ -439,15 +434,6 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
return; return;
} }
// Check if a value has been previously initialized. There will be an entry in
// the set for variables with global storage which have been previously
// initialized.
if (VD->hasGlobalStorage())
if (Pred->getState()->contains<InitializedGlobalsSet>(VD)) {
Dst.insert(Pred);
return;
}
// FIXME: all pre/post visits should eventually be handled by ::Visit(). // FIXME: all pre/post visits should eventually be handled by ::Visit().
ExplodedNodeSet dstPreVisit; ExplodedNodeSet dstPreVisit;
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this); getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this);
@ -464,11 +450,6 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
// Note in the state that the initialization has occurred. // Note in the state that the initialization has occurred.
ExplodedNode *UpdatedN = N; ExplodedNode *UpdatedN = N;
if (VD->hasGlobalStorage()) {
state = state->add<InitializedGlobalsSet>(VD);
UpdatedN = B.generateNode(DS, N, state);
}
SVal InitVal = state->getSVal(InitEx, LC); SVal InitVal = state->getSVal(InitEx, LC);
if (isa<CXXConstructExpr>(InitEx->IgnoreImplicit())) { if (isa<CXXConstructExpr>(InitEx->IgnoreImplicit())) {

View File

@ -5,6 +5,7 @@ void bar() {}
void foo() { bar(); } void foo() { bar(); }
// CHECK: [config] // CHECK: [config]
// CHECK-NEXT: cfg-conditional-static-initializers = true
// CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: cfg-temporary-dtors = false
// CHECK-NEXT: faux-bodies = true // CHECK-NEXT: faux-bodies = true
// CHECK-NEXT: graph-trim-interval = 1000 // CHECK-NEXT: graph-trim-interval = 1000
@ -15,4 +16,5 @@ void foo() { bar(); }
// CHECK-NEXT: max-times-inline-large = 32 // CHECK-NEXT: max-times-inline-large = 32
// CHECK-NEXT: mode = deep // CHECK-NEXT: mode = deep
// CHECK-NEXT: [stats] // CHECK-NEXT: [stats]
// CHECK-NEXT: num-entries = 9 // CHECK-NEXT: num-entries = 10

View File

@ -14,6 +14,7 @@ public:
// CHECK-NEXT: c++-inlining = constructors // CHECK-NEXT: c++-inlining = constructors
// CHECK-NEXT: c++-stdlib-inlining = true // CHECK-NEXT: c++-stdlib-inlining = true
// CHECK-NEXT: c++-template-inlining = true // CHECK-NEXT: c++-template-inlining = true
// CHECK-NEXT: cfg-conditional-static-initializers = true
// CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: cfg-temporary-dtors = false
// CHECK-NEXT: faux-bodies = true // CHECK-NEXT: faux-bodies = true
// CHECK-NEXT: graph-trim-interval = 1000 // CHECK-NEXT: graph-trim-interval = 1000
@ -24,4 +25,4 @@ public:
// CHECK-NEXT: max-times-inline-large = 32 // CHECK-NEXT: max-times-inline-large = 32
// CHECK-NEXT: mode = deep // CHECK-NEXT: mode = deep
// CHECK-NEXT: [stats] // CHECK-NEXT: [stats]
// CHECK-NEXT: num-entries = 12 // CHECK-NEXT: num-entries = 13