Catch uses of undefined values when they are used in assignment, thus catching such bugs closer to the source.

llvm-svn: 86003
This commit is contained in:
Ted Kremenek 2009-11-04 04:24:16 +00:00
parent 9db38f6f83
commit ef910047b2
7 changed files with 203 additions and 36 deletions

View File

@ -114,11 +114,19 @@ private:
_PreVisit(C, stmt); _PreVisit(C, stmt);
} }
void GR_VisitBind(ExplodedNodeSet &Dst,
GRStmtNodeBuilder &Builder, GRExprEngine &Eng,
const Stmt *stmt, ExplodedNode *Pred, void *tag,
SVal location, SVal val,
bool isPrevisit) {
CheckerContext C(Dst, Builder, Eng, Pred, tag, isPrevisit);
assert(isPrevisit && "Only previsit supported for now.");
PreVisitBind(C, stmt, location, val);
}
public: public:
virtual ~Checker() {} virtual ~Checker() {}
virtual void _PreVisit(CheckerContext &C, const Stmt *stmt) {} virtual void _PreVisit(CheckerContext &C, const Stmt *ST) {}
// This is a previsit which takes a node returns a node. // This is a previsit which takes a node returns a node.
virtual ExplodedNode *CheckLocation(const Stmt *S, ExplodedNode *Pred, virtual ExplodedNode *CheckLocation(const Stmt *S, ExplodedNode *Pred,
@ -126,6 +134,9 @@ public:
GRExprEngine &Eng) { GRExprEngine &Eng) {
return Pred; return Pred;
} }
virtual void PreVisitBind(CheckerContext &C, const Stmt *ST,
SVal location, SVal val) {}
virtual ExplodedNode *CheckType(QualType T, ExplodedNode *Pred, virtual ExplodedNode *CheckType(QualType T, ExplodedNode *Pred,
const GRState *state, Stmt *S, const GRState *state, Stmt *S,

View File

@ -0,0 +1,32 @@
//===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines UndefinedAssginmentChecker, a builtin check in GRExprEngine that
// checks for assigning undefined values.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_UNDEFASSIGNMENTCHECKER
#define LLVM_CLANG_UNDEFASSIGNMENTCHECKER
#include "clang/Analysis/PathSensitive/CheckerVisitor.h"
namespace clang {
class UndefinedAssignmentChecker
: public CheckerVisitor<UndefinedAssignmentChecker> {
BugType *BT;
public:
UndefinedAssignmentChecker() : BT(0) {}
static void *getTag();
virtual void PreVisitBind(CheckerContext &C, const Stmt *S, SVal location,
SVal val);
};
}
#endif

View File

@ -411,6 +411,10 @@ protected:
/// at specific statements. /// at specific statements.
void CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src, void CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
bool isPrevisit); bool isPrevisit);
void CheckerVisitBind(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
SVal location, SVal val, bool isPrevisit);
/// 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.

View File

@ -139,6 +139,41 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
// automatically. // automatically.
} }
// FIXME: This is largely copy-paste from CheckerVisit(). Need to
// unify.
void GRExprEngine::CheckerVisitBind(Stmt *S, ExplodedNodeSet &Dst,
ExplodedNodeSet &Src,
SVal location, SVal val, bool isPrevisit) {
if (Checkers.empty()) {
Dst = Src;
return;
}
ExplodedNodeSet Tmp;
ExplodedNodeSet *PrevSet = &Src;
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I)
{
ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst
: (PrevSet == &Tmp) ? &Src : &Tmp;
CurrSet->clear();
void *tag = I->first;
Checker *checker = I->second;
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
NI != NE; ++NI)
checker->GR_VisitBind(*CurrSet, *Builder, *this, S, *NI, tag, location,
val, isPrevisit);
// Update which NodeSet is the current one.
PrevSet = CurrSet;
}
// Don't autotransition. The CheckerContext objects should do this
// automatically.
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Engine construction and deletion. // Engine construction and deletion.
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -1093,36 +1128,49 @@ void GRExprEngine::VisitMemberExpr(MemberExpr* M, ExplodedNode* Pred,
void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, Stmt* Ex, ExplodedNode* Pred, void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, Stmt* Ex, ExplodedNode* Pred,
const GRState* state, SVal location, SVal Val, const GRState* state, SVal location, SVal Val,
bool atDeclInit) { bool atDeclInit) {
// Do a previsit of the bind.
ExplodedNodeSet CheckedSet, Src;
Src.Add(Pred);
CheckerVisitBind(Ex, CheckedSet, Src, location, Val, true);
for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
I!=E; ++I) {
if (Pred != *I)
state = GetState(*I);
const GRState* newState = 0;
const GRState* newState = 0; if (atDeclInit) {
const VarRegion *VR =
cast<VarRegion>(cast<loc::MemRegionVal>(location).getRegion());
if (atDeclInit) { newState = state->bindDecl(VR, Val);
const VarRegion *VR =
cast<VarRegion>(cast<loc::MemRegionVal>(location).getRegion());
newState = state->bindDecl(VR, Val);
}
else {
if (location.isUnknown()) {
// We know that the new state will be the same as the old state since
// the location of the binding is "unknown". Consequently, there
// is no reason to just create a new node.
newState = state;
} }
else { else {
// We are binding to a value other than 'unknown'. Perform the binding if (location.isUnknown()) {
// using the StoreManager. // We know that the new state will be the same as the old state since
newState = state->bindLoc(cast<Loc>(location), Val); // the location of the binding is "unknown". Consequently, there
// is no reason to just create a new node.
newState = state;
}
else {
// We are binding to a value other than 'unknown'. Perform the binding
// using the StoreManager.
newState = state->bindLoc(cast<Loc>(location), Val);
}
} }
// The next thing to do is check if the GRTransferFuncs object wants to
// update the state based on the new binding. If the GRTransferFunc object
// doesn't do anything, just auto-propagate the current state.
GRStmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, *I, newState, Ex,
newState != state);
getTF().EvalBind(BuilderRef, location, Val);
} }
// The next thing to do is check if the GRTransferFuncs object wants to
// update the state based on the new binding. If the GRTransferFunc object
// doesn't do anything, just auto-propagate the current state.
GRStmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, Pred, newState, Ex,
newState != state);
getTF().EvalBind(BuilderRef, location, Val);
} }
/// EvalStore - Handle the semantics of a store via an assignment. /// EvalStore - Handle the semantics of a store via an assignment.

View File

@ -19,6 +19,7 @@
#include "clang/Analysis/PathSensitive/Checkers/DivZeroChecker.h" #include "clang/Analysis/PathSensitive/Checkers/DivZeroChecker.h"
#include "clang/Analysis/PathSensitive/Checkers/BadCallChecker.h" #include "clang/Analysis/PathSensitive/Checkers/BadCallChecker.h"
#include "clang/Analysis/PathSensitive/Checkers/UndefinedArgChecker.h" #include "clang/Analysis/PathSensitive/Checkers/UndefinedArgChecker.h"
#include "clang/Analysis/PathSensitive/Checkers/UndefinedAssignmentChecker.h"
#include "clang/Analysis/PathSensitive/Checkers/AttrNonNullChecker.h" #include "clang/Analysis/PathSensitive/Checkers/AttrNonNullChecker.h"
#include "clang/Analysis/PathSensitive/Checkers/VLASizeChecker.h" #include "clang/Analysis/PathSensitive/Checkers/VLASizeChecker.h"
#include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/PathDiagnostic.h"
@ -476,12 +477,13 @@ void GRExprEngine::RegisterInternalChecks() {
// their associated BugType will get registered with the BugReporter // their associated BugType will get registered with the BugReporter
// automatically. Note that the check itself is owned by the GRExprEngine // automatically. Note that the check itself is owned by the GRExprEngine
// object. // object.
registerCheck<AttrNonNullChecker>(new AttrNonNullChecker()); registerCheck(new AttrNonNullChecker());
registerCheck<UndefinedArgChecker>(new UndefinedArgChecker()); registerCheck(new UndefinedArgChecker());
registerCheck<BadCallChecker>(new BadCallChecker()); registerCheck(new UndefinedAssignmentChecker());
registerCheck<DivZeroChecker>(new DivZeroChecker()); registerCheck(new BadCallChecker());
registerCheck<UndefDerefChecker>(new UndefDerefChecker()); registerCheck(new DivZeroChecker());
registerCheck<NullDerefChecker>(new NullDerefChecker()); registerCheck(new UndefDerefChecker());
registerCheck<UndefSizedVLAChecker>(new UndefSizedVLAChecker()); registerCheck(new NullDerefChecker());
registerCheck<ZeroSizedVLAChecker>(new ZeroSizedVLAChecker()); registerCheck(new UndefSizedVLAChecker());
registerCheck(new ZeroSizedVLAChecker());
} }

View File

@ -0,0 +1,59 @@
//===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines UndefinedAssginmentChecker, a builtin check in GRExprEngine that
// checks for assigning undefined values.
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/PathSensitive/Checkers/UndefinedAssignmentChecker.h"
#include "clang/Analysis/PathSensitive/BugReporter.h"
using namespace clang;
void *UndefinedAssignmentChecker::getTag() {
static int x = 0;
return &x;
}
void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C,
const Stmt *S,
SVal location,
SVal val) {
if (!val.isUndef())
return;
ExplodedNode *N = C.GenerateNode(S, true);
if (!N)
return;
if (!BT)
BT = new BugType("Assigned value is garbage or undefined",
"Logic error");
// Generate a report for this bug.
EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName().c_str(), N);
const Expr *ex = 0;
if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S))
ex = B->getRHS();
else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
const VarDecl* VD = dyn_cast<VarDecl>(DS->getSingleDecl());
ex = VD->getInit();
}
if (ex) {
R->addRange(ex->getSourceRange());
R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, ex);
}
C.EmitReport(R);
}

View File

@ -24,9 +24,20 @@ void test_uninit_pos() {
struct TestUninit v1 = { 0, 0 }; struct TestUninit v1 = { 0, 0 };
struct TestUninit v2 = test_uninit_aux(); struct TestUninit v2 = test_uninit_aux();
int z; int z;
v1.y = z; v1.y = z; // expected-warning{{Assigned value is garbage or undefined}}
test_unit_aux2(v2.x + v1.y); // expected-warning{{The right operand of '+' is a garbage value}} test_unit_aux2(v2.x + v1.y);
} }
void test_uninit_pos_2() {
struct TestUninit v1 = { 0, 0 };
struct TestUninit v2;
test_unit_aux2(v2.x + v1.y); // expected-warning{{The left operand of '+' is a garbage value}}
}
void test_uninit_pos_3() {
struct TestUninit v1 = { 0, 0 };
struct TestUninit v2;
test_unit_aux2(v1.y + v2.x); // expected-warning{{The right operand of '+' is a garbage value}}
}
void test_uninit_neg() { void test_uninit_neg() {
struct TestUninit v1 = { 0, 0 }; struct TestUninit v1 = { 0, 0 };
struct TestUninit v2 = test_uninit_aux(); struct TestUninit v2 = test_uninit_aux();