forked from OSchip/llvm-project
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:
parent
9db38f6f83
commit
ef910047b2
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue