Major rewrite/refactoring of static analysis engine. We now use

EvalStore/EvalLoad to handle all loads/stores from symbolic memory, allowing us
to do checks for null dereferences, etc., at any arbitrary load/store (these
were missed checks before). This also resulted in some major cleanups, some
conceptual, and others just in the structure of the code.

This temporarily introduces a regression in the test suite (null-deref-ps.c)
before I add a new LVal type for structure fields.

llvm-svn: 50443
This commit is contained in:
Ted Kremenek 2008-04-29 21:04:26 +00:00
parent f07de734cf
commit fa5a3d0fe7
7 changed files with 623 additions and 692 deletions

View File

@ -151,12 +151,14 @@ public:
} }
ExplodedNodeImpl* generateNodeImpl(Stmt* S, void* State, ExplodedNodeImpl* generateNodeImpl(Stmt* S, void* State,
ExplodedNodeImpl* Pred); ExplodedNodeImpl* Pred,
bool isLoad = false);
inline ExplodedNodeImpl* generateNodeImpl(Stmt* S, void* State) { inline ExplodedNodeImpl* generateNodeImpl(Stmt* S, void* State,
bool isLoad = false) {
ExplodedNodeImpl* N = getLastNode(); ExplodedNodeImpl* N = getLastNode();
assert (N && "Predecessor of new node is infeasible."); assert (N && "Predecessor of new node is infeasible.");
return generateNodeImpl(S, State, N); return generateNodeImpl(S, State, N, isLoad);
} }
Stmt* getStmt() const { return B[Idx]; } Stmt* getStmt() const { return B[Idx]; }
@ -201,14 +203,14 @@ public:
return static_cast<NodeTy*>(NB.getLastNode()); return static_cast<NodeTy*>(NB.getLastNode());
} }
NodeTy* generateNode(Stmt* S, StateTy* St, NodeTy* Pred) { NodeTy* generateNode(Stmt* S, StateTy* St, NodeTy* Pred, bool isLoad = false){
HasGeneratedNode = true; HasGeneratedNode = true;
return static_cast<NodeTy*>(NB.generateNodeImpl(S, St, Pred)); return static_cast<NodeTy*>(NB.generateNodeImpl(S, St, Pred, isLoad));
} }
NodeTy* generateNode(Stmt* S, StateTy* St) { NodeTy* generateNode(Stmt* S, StateTy* St, bool isLoad = false) {
HasGeneratedNode = true; HasGeneratedNode = true;
return static_cast<NodeTy*>(NB.generateNodeImpl(S, St)); return static_cast<NodeTy*>(NB.generateNodeImpl(S, St, isLoad));
} }
GRBlockCounter getBlockCounter() const { GRBlockCounter getBlockCounter() const {

View File

@ -425,10 +425,6 @@ protected:
return StateMgr.GetRVal(St, LV, T); return StateMgr.GetRVal(St, LV, T);
} }
RVal GetLVal(ValueState* St, Expr* Ex) {
return StateMgr.GetLVal(St, Ex);
}
inline NonLVal MakeConstantVal(uint64_t X, Expr* Ex) { inline NonLVal MakeConstantVal(uint64_t X, Expr* Ex) {
return NonLVal::MakeVal(BasicVals, X, Ex->getType()); return NonLVal::MakeVal(BasicVals, X, Ex->getType());
} }
@ -475,10 +471,6 @@ protected:
return Builder->MakeNode(Dst, S, Pred, St); return Builder->MakeNode(Dst, S, Pred, St);
} }
/// HandleUndefinedStore - Create the necessary sink node to represent
/// a store to an "undefined" LVal.
void HandleUndefinedStore(Stmt* S, NodeTy* Pred);
/// Visit - Transfer function logic for all statements. Dispatches to /// Visit - Transfer function logic for all statements. Dispatches to
/// other functions that handle specific kinds of statements. /// other functions that handle specific kinds of statements.
void Visit(Stmt* S, NodeTy* Pred, NodeSet& Dst); void Visit(Stmt* S, NodeTy* Pred, NodeSet& Dst);
@ -520,7 +512,8 @@ protected:
void VisitCast(Expr* CastE, Expr* Ex, NodeTy* Pred, NodeSet& Dst); void VisitCast(Expr* CastE, Expr* Ex, NodeTy* Pred, NodeSet& Dst);
/// VisitDeclRefExpr - Transfer function logic for DeclRefExprs. /// VisitDeclRefExpr - Transfer function logic for DeclRefExprs.
void VisitDeclRefExpr(DeclRefExpr* DR, NodeTy* Pred, NodeSet& Dst); void VisitDeclRefExpr(DeclRefExpr* DR, NodeTy* Pred, NodeSet& Dst,
bool asLval);
/// VisitDeclStmt - Transfer function logic for DeclStmts. /// VisitDeclStmt - Transfer function logic for DeclStmts.
void VisitDeclStmt(DeclStmt* DS, NodeTy* Pred, NodeSet& Dst); void VisitDeclStmt(DeclStmt* DS, NodeTy* Pred, NodeSet& Dst);
@ -528,12 +521,6 @@ protected:
void VisitDeclStmtAux(DeclStmt* DS, ScopedDecl* D, void VisitDeclStmtAux(DeclStmt* DS, ScopedDecl* D,
NodeTy* Pred, NodeSet& Dst); NodeTy* Pred, NodeSet& Dst);
void VisitDeref(UnaryOperator* U, NodeTy* Pred, NodeSet& Dst,
bool GetLVal = false);
void VisitDeref(Expr* Ex, RVal V, ValueState* St, NodeTy* Pred, NodeSet& Dst,
bool GetLVal);
/// VisitGuardedExpr - Transfer function logic for ?, __builtin_choose /// VisitGuardedExpr - Transfer function logic for ?, __builtin_choose
void VisitGuardedExpr(Expr* Ex, Expr* L, Expr* R, NodeTy* Pred, NodeSet& Dst); void VisitGuardedExpr(Expr* Ex, Expr* L, Expr* R, NodeTy* Pred, NodeSet& Dst);
@ -543,10 +530,6 @@ protected:
/// VisitMemberExpr - Transfer function for member expressions. /// VisitMemberExpr - Transfer function for member expressions.
void VisitMemberExpr(MemberExpr* M, NodeTy* Pred, NodeSet& Dst, bool asLVal); void VisitMemberExpr(MemberExpr* M, NodeTy* Pred, NodeSet& Dst, bool asLVal);
void VisitMemberExprField(MemberExpr* M, Expr* Base, NodeTy* Pred,
NodeSet& Dst, bool asLVal);
/// VisitObjCMessageExpr - Transfer function for ObjC message expressions. /// VisitObjCMessageExpr - Transfer function for ObjC message expressions.
void VisitObjCMessageExpr(ObjCMessageExpr* ME, NodeTy* Pred, NodeSet& Dst); void VisitObjCMessageExpr(ObjCMessageExpr* ME, NodeTy* Pred, NodeSet& Dst);
@ -565,14 +548,11 @@ protected:
void VisitSizeOfAlignOfTypeExpr(SizeOfAlignOfTypeExpr* Ex, NodeTy* Pred, void VisitSizeOfAlignOfTypeExpr(SizeOfAlignOfTypeExpr* Ex, NodeTy* Pred,
NodeSet& Dst); NodeSet& Dst);
// VisitSizeOfExpr - Transfer function for sizeof(expr).
void VisitSizeOfExpr(UnaryOperator* U, NodeTy* Pred, NodeSet& Dst);
/// VisitUnaryOperator - Transfer function logic for unary operators. /// VisitUnaryOperator - Transfer function logic for unary operators.
void VisitUnaryOperator(UnaryOperator* B, NodeTy* Pred, NodeSet& Dst); void VisitUnaryOperator(UnaryOperator* B, NodeTy* Pred, NodeSet& Dst,
bool asLVal);
bool CheckDivideZero(Expr* Ex, ValueState* St, NodeTy* Pred, RVal Denom);
RVal EvalCast(RVal X, QualType CastT) { RVal EvalCast(RVal X, QualType CastT) {
if (X.isUnknownOrUndef()) if (X.isUnknownOrUndef())
@ -584,8 +564,6 @@ protected:
return TF->EvalCast(*this, cast<NonLVal>(X), CastT); return TF->EvalCast(*this, cast<NonLVal>(X), CastT);
} }
RVal EvalMinus(UnaryOperator* U, RVal X) { RVal EvalMinus(UnaryOperator* U, RVal X) {
return X.isValid() ? TF->EvalMinus(*this, U, cast<NonLVal>(X)) : X; return X.isValid() ? TF->EvalMinus(*this, U, cast<NonLVal>(X)) : X;
} }
@ -643,6 +621,15 @@ protected:
void EvalStore(NodeSet& Dst, Expr* E, NodeTy* Pred, ValueState* St, void EvalStore(NodeSet& Dst, Expr* E, NodeTy* Pred, ValueState* St,
RVal TargetLV, RVal Val); RVal TargetLV, RVal Val);
// FIXME: The "CheckOnly" option exists only because Array and Field
// loads aren't fully implemented. Eventually this option will go away.
void EvalLoad(NodeSet& Dst, Expr* Ex, NodeTy* Pred,
ValueState* St, RVal location, bool CheckOnly = false);
ValueState* EvalLocation(Expr* Ex, NodeTy* Pred,
ValueState* St, RVal location, bool isLoad = false);
void EvalReturn(NodeSet& Dst, ReturnStmt* s, NodeTy* Pred); void EvalReturn(NodeSet& Dst, ReturnStmt* s, NodeTy* Pred);
ValueState* MarkBranch(ValueState* St, Stmt* Terminator, bool branchTaken); ValueState* MarkBranch(ValueState* St, Stmt* Terminator, bool branchTaken);

View File

@ -242,7 +242,6 @@ public:
RVal GetRVal(ValueState* St, Expr* E); RVal GetRVal(ValueState* St, Expr* E);
RVal GetRVal(ValueState* St, LVal LV, QualType T = QualType()); RVal GetRVal(ValueState* St, LVal LV, QualType T = QualType());
RVal GetLVal(ValueState* St, Expr* E);
RVal GetBlkExprRVal(ValueState* St, Expr* Ex); RVal GetBlkExprRVal(ValueState* St, Expr* Ex);

View File

@ -25,8 +25,9 @@ namespace clang {
class ProgramPoint { class ProgramPoint {
public: public:
enum Kind { BlockEntranceKind=0, PostStmtKind=1, BlockExitKind=2, enum Kind { BlockEntranceKind=0, PostStmtKind=1, PostLoadKind=2,
BlockEdgeSrcKind=3, BlockEdgeDstKind=4, BlockEdgeAuxKind=5 }; BlockExitKind=3, BlockEdgeSrcKind=4, BlockEdgeDstKind=5,
BlockEdgeAuxKind=6 };
protected: protected:
uintptr_t Data; uintptr_t Data;
@ -100,13 +101,25 @@ public:
class PostStmt : public ProgramPoint { class PostStmt : public ProgramPoint {
protected:
PostStmt(const Stmt* S, Kind k) : ProgramPoint(S, k) {}
public: public:
PostStmt(const Stmt* S) : ProgramPoint(S, PostStmtKind) {} PostStmt(const Stmt* S) : ProgramPoint(S, PostStmtKind) {}
Stmt* getStmt() const { return (Stmt*) getRawPtr(); } Stmt* getStmt() const { return (Stmt*) getRawPtr(); }
static bool classof(const ProgramPoint* Location) { static bool classof(const ProgramPoint* Location) {
return Location->getKind() == PostStmtKind; unsigned k = Location->getKind();
return k == PostStmtKind || k == PostLoadKind;
}
};
class PostLoad : public PostStmt {
public:
PostLoad(const Stmt* S) : PostStmt(S, PostLoadKind) {}
static bool classof(const ProgramPoint* Location) {
return Location->getKind() == PostLoadKind;
} }
}; };

View File

@ -103,6 +103,7 @@ bool GRCoreEngineImpl::ExecuteWorkList(unsigned Steps) {
assert (false && "BlockExit location never occur in forward analysis."); assert (false && "BlockExit location never occur in forward analysis.");
break; break;
case ProgramPoint::PostLoadKind:
case ProgramPoint::PostStmtKind: case ProgramPoint::PostStmtKind:
HandlePostStmt(cast<PostStmt>(Node->getLocation()), WU.getBlock(), HandlePostStmt(cast<PostStmt>(Node->getLocation()), WU.getBlock(),
WU.getIndex(), Node); WU.getIndex(), Node);
@ -316,11 +317,13 @@ void GRStmtNodeBuilderImpl::GenerateAutoTransition(ExplodedNodeImpl* N) {
Eng.WList->Enqueue(Succ, B, Idx+1); Eng.WList->Enqueue(Succ, B, Idx+1);
} }
ExplodedNodeImpl* GRStmtNodeBuilderImpl::generateNodeImpl(Stmt* S, void* State, ExplodedNodeImpl*
ExplodedNodeImpl* Pred) { GRStmtNodeBuilderImpl::generateNodeImpl(Stmt* S, void* State,
ExplodedNodeImpl* Pred, bool isLoad) {
bool IsNew; bool IsNew;
ExplodedNodeImpl* N = Eng.G->getNodeImpl(PostStmt(S), State, &IsNew); ProgramPoint Loc = isLoad ? PostLoad(S) : PostStmt(S);
ExplodedNodeImpl* N = Eng.G->getNodeImpl(Loc, State, &IsNew);
N->addPredecessor(Pred); N->addPredecessor(Pred);
Deferred.erase(Pred); Deferred.erase(Pred);

File diff suppressed because it is too large Load Diff

View File

@ -271,22 +271,6 @@ RVal ValueStateManager::GetRVal(ValueState* St, Expr* E) {
E = cast<ParenExpr>(E)->getSubExpr(); E = cast<ParenExpr>(E)->getSubExpr();
continue; continue;
// DeclRefExprs can either evaluate to an LVal or a Non-LVal
// (assuming an implicit "load") depending on the context. In this
// context we assume that we are retrieving the value contained
// within the referenced variables.
case Stmt::DeclRefExprClass: {
// Check if this expression is a block-level expression. If so,
// return its value.
ValueState::ExprBindingsTy::TreeTy* T=St->BlockExprBindings.SlimFind(E);
if (T) return T->getValue().second;
RVal X = RVal::MakeVal(BasicVals, cast<DeclRefExpr>(E));
return isa<lval::DeclVal>(X) ? GetRVal(St, cast<lval::DeclVal>(X)) : X;
}
case Stmt::CharacterLiteralClass: { case Stmt::CharacterLiteralClass: {
CharacterLiteral* C = cast<CharacterLiteral>(E); CharacterLiteral* C = cast<CharacterLiteral>(E);
return NonLVal::MakeVal(BasicVals, C->getValue(), C->getType()); return NonLVal::MakeVal(BasicVals, C->getValue(), C->getType());
@ -326,18 +310,6 @@ RVal ValueStateManager::GetRVal(ValueState* St, Expr* E) {
break; break;
} }
case Stmt::UnaryOperatorClass: {
UnaryOperator* U = cast<UnaryOperator>(E);
if (U->getOpcode() == UnaryOperator::Plus) {
E = U->getSubExpr();
continue;
}
break;
}
// Handle all other Expr* using a lookup. // Handle all other Expr* using a lookup.
default: default:
@ -377,34 +349,6 @@ RVal ValueStateManager::GetBlkExprRVal(ValueState* St, Expr* E) {
} }
} }
RVal ValueStateManager::GetLVal(ValueState* St, Expr* E) {
E = E->IgnoreParens();
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E)) {
ValueDecl* VD = DR->getDecl();
if (FunctionDecl* FD = dyn_cast<FunctionDecl>(VD))
return lval::FuncVal(FD);
else
return lval::DeclVal(cast<VarDecl>(DR->getDecl()));
}
if (UnaryOperator* U = dyn_cast<UnaryOperator>(E))
if (U->getOpcode() == UnaryOperator::Deref) {
E = U->getSubExpr()->IgnoreParens();
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E)) {
lval::DeclVal X(cast<VarDecl>(DR->getDecl()));
return GetRVal(St, X);
}
else
return GetRVal(St, E);
}
return GetRVal(St, E);
}
ValueState* ValueState*
ValueStateManager::SetRVal(ValueState* St, Expr* E, RVal V, ValueStateManager::SetRVal(ValueState* St, Expr* E, RVal V,
bool isBlkExpr, bool Invalidate) { bool isBlkExpr, bool Invalidate) {