Newer
Older
//=-- GRExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ---*- 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 a meta-engine for path-sensitive dataflow analysis that
// is built on GREngine, but provides the boilerplate to execute transfer
// functions and build the ExplodedGraph at the expression level.
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenek
committed
#include "clang/Basic/SourceManager.h"
Ted Kremenek
committed
#include "llvm/ADT/ImmutableList.h"
#include "llvm/Support/Compiler.h"
Ted Kremenek
committed
#ifndef NDEBUG
#include "llvm/Support/GraphWriter.h"
#include <sstream>
#endif
using namespace clang;
using llvm::dyn_cast;
using llvm::cast;
using llvm::APSInt;
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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, GRStateManager& VMgr) {
Ted Kremenek
committed
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)
Ted Kremenek
committed
isSink |= (*I)->Audit(N, VMgr);
Ted Kremenek
committed
return isSink;
}
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) {
IdentifierInfo* II = &Ctx.Idents.get(name);
return Ctx.Selectors.getSelector(0, &II);
}
Ted Kremenek
committed
GRExprEngine::GRExprEngine(CFG& cfg, Decl& CD, ASTContext& Ctx,
Zhongxing Xu
committed
LiveVariables& L, bool purgeDead,
StoreManagerCreator SMC,
ConstraintManagerCreator CMC)
: CoreEngine(cfg, CD, Ctx, *this),
G(CoreEngine.getGraph()),
Ted Kremenek
committed
Liveness(L),
Builder(NULL),
StateMgr(G.getContext(), SMC, CMC, G.getAllocator(), cfg, CD, L),
SymMgr(StateMgr.getSymbolManager()),
Ted Kremenek
committed
CurrentStmt(NULL),
NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL),
RaiseSel(GetNullarySelector("raise", G.getContext())),
PurgeDead(purgeDead){}
for (BugTypeSet::iterator I = BugTypes.begin(), E = BugTypes.end(); I!=E; ++I)
delete *I;
Ted Kremenek
committed
Ted Kremenek
committed
delete [] NSExceptionInstanceRaiseSelectors;
}
//===----------------------------------------------------------------------===//
// Utility methods.
//===----------------------------------------------------------------------===//
// SaveAndRestore - A utility class that uses RIIA to save and restore
// the value of a variable.
template<typename T>
struct VISIBILITY_HIDDEN SaveAndRestore {
SaveAndRestore(T& x) : X(x), old_value(x) {}
~SaveAndRestore() { X = old_value; }
T get() { return old_value; }
T& X;
T old_value;
};
// SaveOr - Similar to SaveAndRestore. Operates only on bools; the old
// value of a variable is saved, and during the dstor the old value is
// or'ed with the new value.
struct VISIBILITY_HIDDEN SaveOr {
SaveOr(bool& x) : X(x), old_value(x) { x = false; }
~SaveOr() { X |= old_value; }
bool& X;
bool old_value;
};
void GRExprEngine::EmitWarnings(BugReporterData& BRData) {
for (bug_type_iterator I = bug_types_begin(), E = bug_types_end(); I!=E; ++I){
GRBugReporter BR(BRData, *this);
(*I)->EmitWarnings(BR);
}
Ted Kremenek
committed
if (BatchAuditor) {
GRBugReporter BR(BRData, *this);
Ted Kremenek
committed
BatchAuditor->EmitWarnings(BR);
}
}
void GRExprEngine::setTransferFunctions(GRTransferFuncs* tf) {
Ted Kremenek
committed
StateMgr.TF = tf;
tf->RegisterChecks(*this);
tf->RegisterPrinters(getStateManager().Printers);
}
Ted Kremenek
committed
void GRExprEngine::AddCheck(GRSimpleAPICheck* A, Stmt::StmtClass C) {
if (!BatchAuditor)
BatchAuditor.reset(new MappedBatchAuditor(getGraph().getAllocator()));
((MappedBatchAuditor*) BatchAuditor.get())->AddCheck(A, C);
}
const GRState* GRExprEngine::getInitialState() {
//===----------------------------------------------------------------------===//
// Top-level transfer function logic (Dispatcher).
//===----------------------------------------------------------------------===//
void GRExprEngine::ProcessStmt(Stmt* S, StmtNodeBuilder& builder) {
Builder = &builder;
Ted Kremenek
committed
EntryNode = builder.getLastNode();
Ted Kremenek
committed
// FIXME: Consolidate.
CurrentStmt = S;
Ted Kremenek
committed
StateMgr.CurrentStmt = S;
// Set up our simple checks.
Ted Kremenek
committed
if (BatchAuditor)
Builder->setAuditor(BatchAuditor.get());
Ted Kremenek
committed
Ted Kremenek
committed
// Create the cleaned state.
Ted Kremenek
committed
SymbolReaper SymReaper(Liveness, SymMgr);
CleanedState = PurgeDead ? StateMgr.RemoveDeadBindings(EntryNode->getState(),
CurrentStmt, SymReaper)
: EntryNode->getState();
// Process any special transfer function for dead symbols.
NodeSet Tmp;
Ted Kremenek
committed
if (!SymReaper.hasDeadSymbols())
Ted Kremenek
committed
Tmp.Add(EntryNode);
else {
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
Ted Kremenek
committed
SaveOr OldHasGen(Builder->HasGeneratedNode);
SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols);
Builder->PurgingDeadSymbols = true;
Ted Kremenek
committed
getTF().EvalDeadSymbols(Tmp, *this, *Builder, EntryNode, S,
Ted Kremenek
committed
CleanedState, SymReaper);
Ted Kremenek
committed
if (!Builder->BuildSinks && !Builder->HasGeneratedNode)
Tmp.Add(EntryNode);
}
Ted Kremenek
committed
bool HasAutoGenerated = false;
for (NodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
Ted Kremenek
committed
NodeSet Dst;
// Set the cleaned state.
Ted Kremenek
committed
Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I));
// Visit the statement.
Ted Kremenek
committed
Visit(S, *I, Dst);
// Do we need to auto-generate a node? We only need to do this to generate
// a node with a "cleaned" state; GRCoreEngine will actually handle
// auto-transitions for other cases.
if (Dst.size() == 1 && *Dst.begin() == EntryNode
&& !Builder->HasGeneratedNode && !HasAutoGenerated) {
HasAutoGenerated = true;
builder.generateNode(S, GetState(EntryNode), *I);
}
}
// NULL out these variables to cleanup.
Ted Kremenek
committed
CleanedState = NULL;
EntryNode = NULL;
Ted Kremenek
committed
// FIXME: Consolidate.
StateMgr.CurrentStmt = 0;
CurrentStmt = 0;
Builder = NULL;
}
void GRExprEngine::Visit(Stmt* S, NodeTy* Pred, NodeSet& Dst) {
// FIXME: add metadata to the CFG so that we can disable
// this check when we KNOW that there is no block-level subexpression.
// The motivation is that this check requires a hashtable lookup.
if (S != CurrentStmt && getCFG().isBlkExpr(S)) {
Dst.Add(Pred);
return;
}
switch (S->getStmtClass()) {
default:
// Cases we intentionally have "default" handle:
// AddrLabelExpr, IntegerLiteral, CharacterLiteral
Dst.Add(Pred); // No-op. Simply propagate the current state unchanged.
break;
case Stmt::ArraySubscriptExprClass:
VisitArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst, false);
break;
case Stmt::AsmStmtClass:
VisitAsmStmt(cast<AsmStmt>(S), Pred, Dst);
break;
case Stmt::BinaryOperatorClass: {
BinaryOperator* B = cast<BinaryOperator>(S);
if (B->isLogicalOp()) {
VisitLogicalExpr(B, Pred, Dst);
break;
}
else if (B->getOpcode() == BinaryOperator::Comma) {
MakeNode(Dst, B, Pred, BindExpr(St, B, GetSVal(St, B->getRHS())));
break;
}
VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst);
break;
}
case Stmt::CallExprClass:
case Stmt::CXXOperatorCallExprClass: {
CallExpr* C = cast<CallExpr>(S);
VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst);
}
// FIXME: ChooseExpr is really a constant. We need to fix
// the CFG do not model them as explicit control-flow.
case Stmt::ChooseExprClass: { // __builtin_choose_expr
ChooseExpr* C = cast<ChooseExpr>(S);
VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst);
break;
}
case Stmt::CompoundAssignOperatorClass:
VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst);
break;
Zhongxing Xu
committed
case Stmt::CompoundLiteralExprClass:
VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(S), Pred, Dst, false);
break;
case Stmt::ConditionalOperatorClass: { // '?' operator
ConditionalOperator* C = cast<ConditionalOperator>(S);
VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst);
break;
}
case Stmt::DeclRefExprClass:
case Stmt::QualifiedDeclRefExprClass:
VisitDeclRefExpr(cast<DeclRefExpr>(S), Pred, Dst, false);
break;
case Stmt::DeclStmtClass:
VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst);
break;
case Stmt::ImplicitCastExprClass:
case Stmt::CStyleCastExprClass: {
CastExpr* C = cast<CastExpr>(S);
VisitCast(C, C->getSubExpr(), Pred, Dst);
break;
}
Zhongxing Xu
committed
case Stmt::InitListExprClass:
VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst);
break;
case Stmt::MemberExprClass:
Ted Kremenek
committed
VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst, false);
break;
case Stmt::ObjCIvarRefExprClass:
VisitObjCIvarRefExpr(cast<ObjCIvarRefExpr>(S), Pred, Dst, false);
break;
Ted Kremenek
committed
case Stmt::ObjCForCollectionStmtClass:
VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S), Pred, Dst);
break;
Ted Kremenek
committed
case Stmt::ObjCMessageExprClass: {
VisitObjCMessageExpr(cast<ObjCMessageExpr>(S), Pred, Dst);
break;
}
Ted Kremenek
committed
case Stmt::ObjCAtThrowStmtClass: {
// FIXME: This is not complete. We basically treat @throw as
// an abort.
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
Builder->BuildSinks = true;
MakeNode(Dst, S, Pred, GetState(Pred));
break;
}
case Stmt::ParenExprClass:
Visit(cast<ParenExpr>(S)->getSubExpr()->IgnoreParens(), Pred, Dst);
break;
case Stmt::ReturnStmtClass:
VisitReturnStmt(cast<ReturnStmt>(S), Pred, Dst);
break;
Sebastian Redl
committed
case Stmt::SizeOfAlignOfExprClass:
VisitSizeOfAlignOfExpr(cast<SizeOfAlignOfExpr>(S), Pred, Dst);
break;
case Stmt::StmtExprClass: {
StmtExpr* SE = cast<StmtExpr>(S);
// FIXME: Not certain if we can have empty StmtExprs. If so, we should
// probably just remove these from the CFG.
assert (!SE->getSubStmt()->body_empty());
if (Expr* LastExpr = dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin()))
MakeNode(Dst, SE, Pred, BindExpr(St, SE, GetSVal(St, LastExpr)));
else
Dst.Add(Pred);
break;
}
case Stmt::StringLiteralClass:
VisitLValue(cast<StringLiteral>(S), Pred, Dst);
break;
case Stmt::UnaryOperatorClass:
VisitUnaryOperator(cast<UnaryOperator>(S), Pred, Dst, false);
break;
}
}
Zhongxing Xu
committed
void GRExprEngine::VisitLValue(Expr* Ex, NodeTy* Pred, NodeSet& Dst) {
Ex = Ex->IgnoreParens();
if (Ex != CurrentStmt && getCFG().isBlkExpr(Ex)) {
Dst.Add(Pred);
return;
}
switch (Ex->getStmtClass()) {
case Stmt::ArraySubscriptExprClass:
VisitArraySubscriptExpr(cast<ArraySubscriptExpr>(Ex), Pred, Dst, true);
return;
case Stmt::DeclRefExprClass:
case Stmt::QualifiedDeclRefExprClass:
VisitDeclRefExpr(cast<DeclRefExpr>(Ex), Pred, Dst, true);
return;
case Stmt::ObjCIvarRefExprClass:
VisitObjCIvarRefExpr(cast<ObjCIvarRefExpr>(Ex), Pred, Dst, true);
return;
case Stmt::UnaryOperatorClass:
VisitUnaryOperator(cast<UnaryOperator>(Ex), Pred, Dst, true);
return;
case Stmt::MemberExprClass:
VisitMemberExpr(cast<MemberExpr>(Ex), Pred, Dst, true);
return;
Ted Kremenek
committed
case Stmt::CompoundLiteralExprClass:
Zhongxing Xu
committed
VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(Ex), Pred, Dst, true);
return;
Ted Kremenek
committed
case Stmt::ObjCPropertyRefExprClass:
// FIXME: Property assignments are lvalues, but not really "locations".
// e.g.: self.x = something;
// Here the "self.x" really can translate to a method call (setter) when
// the assignment is made. Moreover, the entire assignment expression
// evaluate to whatever "something" is, not calling the "getter" for
// the property (which would make sense since it can have side effects).
// We'll probably treat this as a location, but not one that we can
// take the address of. Perhaps we need a new SVal class for cases
// like thsis?
// Note that we have a similar problem for bitfields, since they don't
// have "locations" in the sense that we can take their address.
Dst.Add(Pred);
Ted Kremenek
committed
return;
Zhongxing Xu
committed
case Stmt::StringLiteralClass: {
const GRState* St = GetState(Pred);
SVal V = StateMgr.GetLValue(St, cast<StringLiteral>(Ex));
Zhongxing Xu
committed
return;
}
Ted Kremenek
committed
Ted Kremenek
committed
default:
// Arbitrary subexpressions can return aggregate temporaries that
// can be used in a lvalue context. We need to enhance our support
// of such temporaries in both the environment and the store, so right
// now we just do a regular visit.
assert ((Ex->getType()->isAggregateType() ||
Ex->getType()->isUnionType()) &&
"Other kinds of expressions with non-aggregate/union types do"
" not have lvalues.");
Ted Kremenek
committed
Ted Kremenek
committed
Visit(Ex, Pred, Dst);
}
}
//===----------------------------------------------------------------------===//
// Block entrance. (Update counters).
//===----------------------------------------------------------------------===//
bool GRExprEngine::ProcessBlockEntrance(CFGBlock* B, const GRState*,
GRBlockCounter BC) {
return BC.getNumVisited(B->getBlockID()) < 3;
}
//===----------------------------------------------------------------------===//
// Branch processing.
//===----------------------------------------------------------------------===//
const GRState* GRExprEngine::MarkBranch(const GRState* St,
Stmt* Terminator,
bool branchTaken) {
switch (Terminator->getStmtClass()) {
default:
return St;
case Stmt::BinaryOperatorClass: { // '&&' and '||'
BinaryOperator* B = cast<BinaryOperator>(Terminator);
BinaryOperator::Opcode Op = B->getOpcode();
assert (Op == BinaryOperator::LAnd || Op == BinaryOperator::LOr);
// For &&, if we take the true branch, then the value of the whole
// expression is that of the RHS expression.
//
// For ||, if we take the false branch, then the value of the whole
// expression is that of the RHS expression.
Expr* Ex = (Op == BinaryOperator::LAnd && branchTaken) ||
(Op == BinaryOperator::LOr && !branchTaken)
? B->getRHS() : B->getLHS();
}
case Stmt::ConditionalOperatorClass: { // ?:
ConditionalOperator* C = cast<ConditionalOperator>(Terminator);
// For ?, if branchTaken == true then the value is either the LHS or
// the condition itself. (GNU extension).
Expr* Ex;
if (branchTaken)
Ex = C->getLHS() ? C->getLHS() : C->getCond();
else
Ex = C->getRHS();
}
case Stmt::ChooseExprClass: { // ?:
ChooseExpr* C = cast<ChooseExpr>(Terminator);
Expr* Ex = branchTaken ? C->getLHS() : C->getRHS();
}
}
}
Ted Kremenek
committed
void GRExprEngine::ProcessBranch(Stmt* Condition, Stmt* Term,
BranchNodeBuilder& builder) {
// Remove old bindings for subexpressions.
StateMgr.RemoveSubExprBindings(builder.getState());
Ted Kremenek
committed
// Check for NULL conditions; e.g. "for(;;)"
if (!Condition) {
builder.markInfeasible(false);
return;
}
Zhongxing Xu
committed
SVal V = GetSVal(PrevState, Condition);
switch (V.getBaseKind()) {
default:
break;
Zhongxing Xu
committed
case SVal::UnknownKind:
Ted Kremenek
committed
builder.generateNode(MarkBranch(PrevState, Term, true), true);
builder.generateNode(MarkBranch(PrevState, Term, false), false);
return;
Zhongxing Xu
committed
case SVal::UndefinedKind: {
NodeTy* N = builder.generateNode(PrevState, true);
if (N) {
N->markAsSink();
Ted Kremenek
committed
UndefBranches.insert(N);
}
builder.markInfeasible(false);
return;
}
}
Ted Kremenek
committed
// Process the true branch.
Ted Kremenek
committed
bool isFeasible = false;
const GRState* St = Assume(PrevState, V, true, isFeasible);
Ted Kremenek
committed
if (isFeasible)
builder.generateNode(MarkBranch(St, Term, true), true);
else
builder.markInfeasible(true);
Ted Kremenek
committed
// Process the false branch.
Ted Kremenek
committed
isFeasible = false;
St = Assume(PrevState, V, false, isFeasible);
Ted Kremenek
committed
if (isFeasible)
builder.generateNode(MarkBranch(St, Term, false), false);
else
builder.markInfeasible(false);
}
/// ProcessIndirectGoto - Called by GRCoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a computed goto jump.
void GRExprEngine::ProcessIndirectGoto(IndirectGotoNodeBuilder& builder) {
Zhongxing Xu
committed
SVal V = GetSVal(St, builder.getTarget());
// Three possibilities:
//
// (1) We know the computed label.
Ted Kremenek
committed
// (2) The label is NULL (or some other constant), or Undefined.
// (3) We have no clue about the label. Dispatch to all targets.
//
typedef IndirectGotoNodeBuilder::iterator iterator;
Zhongxing Xu
committed
if (isa<loc::GotoLabel>(V)) {
LabelStmt* L = cast<loc::GotoLabel>(V).getLabel();
for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) {
if (I.getLabel() == L) {
builder.generateNode(I, St);
return;
}
}
assert (false && "No block with label.");
return;
}
Zhongxing Xu
committed
if (isa<loc::ConcreteInt>(V) || isa<UndefinedVal>(V)) {
// Dispatch to the first target and mark it as a sink.
NodeTy* N = builder.generateNode(builder.begin(), St, true);
Ted Kremenek
committed
UndefBranches.insert(N);
return;
}
// This is really a catch-all. We don't support symbolics yet.
assert (V.isUnknown());
for (iterator I=builder.begin(), E=builder.end(); I != E; ++I)
void GRExprEngine::VisitGuardedExpr(Expr* Ex, Expr* L, Expr* R,
NodeTy* Pred, NodeSet& Dst) {
assert (Ex == CurrentStmt && getCFG().isBlkExpr(Ex));
Zhongxing Xu
committed
SVal X = GetBlkExprSVal(St, Ex);
assert (X.isUndef());
Expr* SE = (Expr*) cast<UndefinedVal>(X).getData();
assert (SE);
Zhongxing Xu
committed
X = GetBlkExprSVal(St, SE);
// Make sure that we invalidate the previous binding.
MakeNode(Dst, Ex, Pred, StateMgr.BindExpr(St, Ex, X, true, true));
}
/// ProcessSwitch - Called by GRCoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a switch statement.
Ted Kremenek
committed
void GRExprEngine::ProcessSwitch(SwitchNodeBuilder& builder) {
typedef SwitchNodeBuilder::iterator iterator;
Expr* CondE = builder.getCondition();
Zhongxing Xu
committed
SVal CondV = GetSVal(St, CondE);
Ted Kremenek
committed
if (CondV.isUndef()) {
NodeTy* N = builder.generateDefaultCaseNode(St, true);
Ted Kremenek
committed
UndefBranches.insert(N);
return;
}
Ted Kremenek
committed
const GRState* DefaultSt = St;
bool DefaultFeasible = false;
for (iterator I = builder.begin(), EI = builder.end(); I != EI; ++I) {
CaseStmt* Case = cast<CaseStmt>(I.getCase());
Ted Kremenek
committed
// Evaluate the LHS of the case value.
Expr::EvalResult V1;
bool b = Case->getLHS()->Evaluate(V1, getContext());
Ted Kremenek
committed
// Sanity checks. These go away in Release builds.
assert(b && V1.Val.isInt() && !V1.HasSideEffects
&& "Case condition must evaluate to an integer constant.");
b = b; // silence unused variable warning
assert(V1.Val.getInt().getBitWidth() ==
getContext().getTypeSize(CondE->getType()));
// Get the RHS of the case, if it exists.
Ted Kremenek
committed
Expr::EvalResult V2;
if (Expr* E = Case->getRHS()) {
Ted Kremenek
committed
b = E->Evaluate(V2, getContext());
assert(b && V2.Val.isInt() && !V2.HasSideEffects
&& "Case condition must evaluate to an integer constant.");
b = b; // silence unused variable warning
else
V2 = V1;
// FIXME: Eventually we should replace the logic below with a range
// comparison, rather than concretize the values within the range.
// This should be easy once we have "ranges" for NonLVals.
Ted Kremenek
committed
nonloc::ConcreteInt CaseVal(getBasicVals().getValue(V1.Val.getInt()));
Zhongxing Xu
committed
SVal Res = EvalBinOp(BinaryOperator::EQ, CondV, CaseVal);
Ted Kremenek
committed
// Now "assume" that the case matches.
Ted Kremenek
committed
bool isFeasible = false;
const GRState* StNew = Assume(St, Res, true, isFeasible);
if (isFeasible) {
builder.generateCaseStmtNode(I, StNew);
// If CondV evaluates to a constant, then we know that this
// is the *only* case that we can take, so stop evaluating the
// others.
Zhongxing Xu
committed
if (isa<nonloc::ConcreteInt>(CondV))
return;
}
// Now "assume" that the case doesn't match. Add this state
// to the default state (if it is feasible).
Ted Kremenek
committed
isFeasible = false;
StNew = Assume(DefaultSt, Res, false, isFeasible);
if (isFeasible) {
DefaultFeasible = true;
DefaultSt = StNew;
// Concretize the next value in the range.
Ted Kremenek
committed
if (V1.Val.getInt() == V2.Val.getInt())
Ted Kremenek
committed
++V1.Val.getInt();
assert (V1.Val.getInt() <= V2.Val.getInt());
} while (true);
}
// If we reach here, than we know that the default branch is
// possible.
if (DefaultFeasible) builder.generateDefaultCaseNode(DefaultSt);
}
//===----------------------------------------------------------------------===//
// Transfer functions: logical operations ('&&', '||').
//===----------------------------------------------------------------------===//
void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, NodeTy* Pred,
NodeSet& Dst) {
assert (B->getOpcode() == BinaryOperator::LAnd ||
B->getOpcode() == BinaryOperator::LOr);
assert (B == CurrentStmt && getCFG().isBlkExpr(B));
Zhongxing Xu
committed
SVal X = GetBlkExprSVal(St, B);
Ted Kremenek
committed
assert (X.isUndef());
Ted Kremenek
committed
Expr* Ex = (Expr*) cast<UndefinedVal>(X).getData();
assert (Ex);
if (Ex == B->getRHS()) {
Zhongxing Xu
committed
X = GetBlkExprSVal(St, Ex);
Ted Kremenek
committed
// Handle undefined values.
Ted Kremenek
committed
Ted Kremenek
committed
if (X.isUndef()) {
Ted Kremenek
committed
return;
}
// We took the RHS. Because the value of the '&&' or '||' expression must
// evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0
// or 1. Alternatively, we could take a lazy approach, and calculate this
// value later when necessary. We don't have the machinery in place for
// this right now, and since most logical expressions are used for branches,
// the payoff is not likely to be large. Instead, we do eager evaluation.
bool isFeasible = false;
const GRState* NewState = Assume(St, X, true, isFeasible);
if (isFeasible)
isFeasible = false;
NewState = Assume(St, X, false, isFeasible);
if (isFeasible)
}
else {
// We took the LHS expression. Depending on whether we are '&&' or
// '||' we know what the value of the expression is via properties of
// the short-circuiting.
X = MakeConstantVal( B->getOpcode() == BinaryOperator::LAnd ? 0U : 1U, B);
}
//===----------------------------------------------------------------------===//
// Transfer functions: Loads and stores.
//===----------------------------------------------------------------------===//
Zhongxing Xu
committed
void GRExprEngine::VisitDeclRefExpr(DeclRefExpr* Ex, NodeTy* Pred, NodeSet& Dst,
bool asLValue) {
Zhongxing Xu
committed
const NamedDecl* D = Ex->getDecl();
Zhongxing Xu
committed
if (const VarDecl* VD = dyn_cast<VarDecl>(D)) {
Zhongxing Xu
committed
SVal V = StateMgr.GetLValue(St, VD);
Zhongxing Xu
committed
if (asLValue)
Zhongxing Xu
committed
else
EvalLoad(Dst, Ex, Pred, St, V);
return;
} else if (const EnumConstantDecl* ED = dyn_cast<EnumConstantDecl>(D)) {
assert(!asLValue && "EnumConstantDecl does not have lvalue.");
BasicValueFactory& BasicVals = StateMgr.getBasicVals();
Zhongxing Xu
committed
SVal V = nonloc::ConcreteInt(BasicVals.getValue(ED->getInitVal()));
Zhongxing Xu
committed
return;
} else if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(D)) {
Zhongxing Xu
committed
SVal V = loc::FuncVal(FD);
Zhongxing Xu
committed
return;
Zhongxing Xu
committed
assert (false &&
"ValueDecl support for this ValueDecl not implemented.");
/// VisitArraySubscriptExpr - Transfer function for array accesses
void GRExprEngine::VisitArraySubscriptExpr(ArraySubscriptExpr* A, NodeTy* Pred,
Zhongxing Xu
committed
NodeSet& Dst, bool asLValue) {
Expr* Base = A->getBase()->IgnoreParens();
Expr* Idx = A->getIdx()->IgnoreParens();
NodeSet Tmp;
Ted Kremenek
committed
Visit(Base, Pred, Tmp); // Get Base's rvalue, which should be an LocVal.
Ted Kremenek
committed
for (NodeSet::iterator I1=Tmp.begin(), E1=Tmp.end(); I1!=E1; ++I1) {
NodeSet Tmp2;
Ted Kremenek
committed
Visit(Idx, *I1, Tmp2); // Evaluate the index.
for (NodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end(); I2!=E2; ++I2) {
Zhongxing Xu
committed
SVal V = StateMgr.GetLValue(St, GetSVal(St, Base), GetSVal(St, Idx));
Zhongxing Xu
committed
if (asLValue)
else
EvalLoad(Dst, A, *I2, St, V);
}
Ted Kremenek
committed
/// VisitMemberExpr - Transfer function for member expressions.
void GRExprEngine::VisitMemberExpr(MemberExpr* M, NodeTy* Pred,
Zhongxing Xu
committed
NodeSet& Dst, bool asLValue) {
Ted Kremenek
committed
Expr* Base = M->getBase()->IgnoreParens();
NodeSet Tmp;
if (M->isArrow())
Visit(Base, Pred, Tmp); // p->f = ... or ... = p->f
else
VisitLValue(Base, Pred, Tmp); // x.f = ... or ... = x.f
FieldDecl *Field = dyn_cast<FieldDecl>(M->getMemberDecl());
if (!Field) // FIXME: skipping member expressions for non-fields
return;
Zhongxing Xu
committed
for (NodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) {
Ted Kremenek
committed
// FIXME: Should we insert some assumption logic in here to determine
// if "Base" is a valid piece of memory? Before we put this assumption
// later when using FieldOffset lvals (which we no longer have).
SVal L = StateMgr.GetLValue(St, GetSVal(St, Base), Field);
Ted Kremenek
committed
Zhongxing Xu
committed
if (asLValue)
Zhongxing Xu
committed
else
EvalLoad(Dst, M, *I, St, L);
Ted Kremenek
committed
}
}
void GRExprEngine::EvalStore(NodeSet& Dst, Expr* Ex, NodeTy* Pred,
Zhongxing Xu
committed
const GRState* St, SVal location, SVal Val) {
assert (Builder && "GRStmtNodeBuilder must be defined.");
// Evaluate the location (checks for bad dereferences).
return;
// Proceed with the store.
unsigned size = Dst.size();
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
Ted Kremenek
committed
SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind);
SaveOr OldHasGen(Builder->HasGeneratedNode);
assert (!location.isUndef());
Ted Kremenek
committed
Builder->PointKind = ProgramPoint::PostStoreKind;
Ted Kremenek
committed
Ted Kremenek
committed
getTF().EvalStore(Dst, *this, *Builder, Ex, Pred, St, location, Val);
// Handle the case where no nodes where generated. Auto-generate that
// contains the updated state if we aren't generating sinks.
if (!Builder->BuildSinks && Dst.size() == size && !Builder->HasGeneratedNode)
Ted Kremenek
committed
getTF().GRTransferFuncs::EvalStore(Dst, *this, *Builder, Ex, Pred, St,
location, Val);
}
void GRExprEngine::EvalLoad(NodeSet& Dst, Expr* Ex, NodeTy* Pred,
Zhongxing Xu
committed
const GRState* St, SVal location,
bool CheckOnly) {
// Evaluate the location (checks for bad dereferences).
Pred = EvalLocation(Ex, Pred, St, location);
return;
// Proceed with the load.
ProgramPoint::Kind K = ProgramPoint::PostLoadKind;
// FIXME: Currently symbolic analysis "generates" new symbols
// for the contents of values. We need a better approach.
// FIXME: The "CheckOnly" option exists only because Array and Field
// loads aren't fully implemented. Eventually this option will go away.