Detect pass-by-value arguments that are structs that contain

uninitialized data.

llvm-svn: 98796
This commit is contained in:
Ted Kremenek 2010-03-18 02:17:27 +00:00
parent e174fda979
commit 9c05f4ef69
2 changed files with 119 additions and 24 deletions

View File

@ -24,7 +24,7 @@ namespace {
class CallAndMessageChecker
: public CheckerVisitor<CallAndMessageChecker> {
BugType *BT_call_null;
BugType *BT_call_undef;
BugType *BT_call_undef;
BugType *BT_call_arg;
BugType *BT_msg_undef;
BugType *BT_msg_arg;
@ -47,9 +47,15 @@ private:
void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE);
void EmitNilReceiverBug(CheckerContext &C, const ObjCMessageExpr *ME,
ExplodedNode *N);
void HandleNilReceiver(CheckerContext &C, const GRState *state,
const ObjCMessageExpr *ME);
const ObjCMessageExpr *ME);
void LazyInit_BT_call_arg() {
if (!BT_call_arg)
BT_call_arg = new BuiltinBug("Pass-by-value argument in function call"
" is undefined");
}
};
} // end anonymous namespace
@ -62,19 +68,19 @@ void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C,
ExplodedNode *N = C.GenerateSink();
if (!N)
return;
EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N);
R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
bugreporter::GetCalleeExpr(N));
C.EmitReport(R);
}
void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C,
void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C,
const CallExpr *CE){
const Expr *Callee = CE->getCallee()->IgnoreParens();
SVal L = C.getState()->getSVal(Callee);
if (L.isUndef()) {
if (!BT_call_undef)
BT_call_undef =
@ -82,21 +88,21 @@ void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C,
EmitBadCall(BT_call_undef, C, CE);
return;
}
if (isa<loc::ConcreteInt>(L)) {
if (!BT_call_null)
BT_call_null =
new BuiltinBug("Called function pointer is null (null dereference)");
EmitBadCall(BT_call_null, C, CE);
}
}
for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end();
I != E; ++I) {
if (C.getState()->getSVal(*I).isUndef()) {
const SVal &V = C.getState()->getSVal(*I);
if (V.isUndef()) {
if (ExplodedNode *N = C.GenerateSink()) {
if (!BT_call_arg)
BT_call_arg = new BuiltinBug("Pass-by-value argument in function call"
" is undefined");
LazyInit_BT_call_arg();
// Generate a report for this bug.
EnhancedBugReport *R = new EnhancedBugReport(*BT_call_arg,
BT_call_arg->getName(), N);
@ -106,6 +112,89 @@ void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C,
return;
}
}
if (const nonloc::LazyCompoundVal *LV =
dyn_cast<nonloc::LazyCompoundVal>(&V)) {
const LazyCompoundValData *D = LV->getCVData();
class FindUninitializedField {
public:
llvm::SmallVector<const FieldDecl *, 10> FieldChain;
private:
ASTContext &C;
StoreManager &StoreMgr;
MemRegionManager &MrMgr;
Store store;
public:
FindUninitializedField(ASTContext &c, StoreManager &storeMgr,
MemRegionManager &mrMgr, Store s)
: C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {}
bool Find(const TypedRegion *R) {
QualType T = R->getValueType(C);
if (const RecordType *RT = T->getAsStructureType()) {
const RecordDecl *RD = RT->getDecl()->getDefinition();
assert(RD && "Referred record has no definition");
for (RecordDecl::field_iterator I =
RD->field_begin(), E = RD->field_end(); I!=E; ++I) {
const FieldRegion *FR = MrMgr.getFieldRegion(*I, R);
FieldChain.push_back(*I);
T = (*I)->getType();
if (T->getAsStructureType()) {
if (Find(FR))
return true;
}
else {
const SVal &V = StoreMgr.Retrieve(store, loc::MemRegionVal(FR));
if (V.isUndef())
return true;
}
FieldChain.pop_back();
}
}
return false;
}
};
FindUninitializedField F(C.getASTContext(),
C.getState()->getStateManager().getStoreManager(),
C.getValueManager().getRegionManager(),
D->getStore());
if (F.Find(D->getRegion())) {
if (ExplodedNode *N = C.GenerateSink()) {
LazyInit_BT_call_arg();
llvm::SmallString<512> Str;
llvm::raw_svector_ostream os(Str);
os << "Passed-by-value struct argument contains uninitialized data";
if (F.FieldChain.size() == 1)
os << " (e.g., field: '" << F.FieldChain[0]->getNameAsString() << "')";
else {
os << " (e.g., via the field chain: '";
bool first = true;
for (llvm::SmallVectorImpl<const FieldDecl *>::iterator
DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){
if (first)
first = false;
else
os << '.';
os << (*DI)->getNameAsString();
}
os << "')";
}
// Generate a report for this bug.
EnhancedBugReport *R =
new EnhancedBugReport(*BT_call_arg, os.str(), N);
R->addRange((*I)->getSourceRange());
// FIXME: enhance track back for uninitialized value for arbitrary
// memregions
C.EmitReport(R);
}
}
}
}
}
@ -138,7 +227,7 @@ void CallAndMessageChecker::PreVisitObjCMessageExpr(CheckerContext &C,
if (!BT_msg_arg)
BT_msg_arg =
new BuiltinBug("Pass-by-value argument in message expression"
" is undefined");
" is undefined");
// Generate a report for this bug.
EnhancedBugReport *R = new EnhancedBugReport(*BT_msg_arg,
BT_msg_arg->getName(), N);
@ -160,24 +249,24 @@ bool CallAndMessageChecker::EvalNilReceiver(CheckerContext &C,
void CallAndMessageChecker::EmitNilReceiverBug(CheckerContext &C,
const ObjCMessageExpr *ME,
ExplodedNode *N) {
if (!BT_msg_ret)
BT_msg_ret =
new BuiltinBug("Receiver in message expression is "
"'nil' and returns a garbage value");
llvm::SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
os << "The receiver of message '" << ME->getSelector().getAsString()
<< "' is nil and returns a value of type '"
<< ME->getType().getAsString() << "' that will be garbage";
EnhancedBugReport *report = new EnhancedBugReport(*BT_msg_ret, os.str(), N);
const Expr *receiver = ME->getReceiver();
report->addRange(receiver->getSourceRange());
report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
receiver);
C.EmitReport(report);
C.EmitReport(report);
}
static bool SupportsNilWithFloatRet(const llvm::Triple &triple) {
@ -188,11 +277,11 @@ static bool SupportsNilWithFloatRet(const llvm::Triple &triple) {
void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
const GRState *state,
const ObjCMessageExpr *ME) {
// Check the return type of the message expression. A message to nil will
// return different values depending on the return type and the architecture.
QualType RetTy = ME->getType();
ASTContext &Ctx = C.getASTContext();
CanQualType CanRetTy = Ctx.getCanonicalType(RetTy);
@ -216,7 +305,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
if (CanRetTy != Ctx.VoidTy &&
C.getPredecessor()->getParentMap().isConsumedExpr(ME)) {
// Compute: sizeof(void *) and sizeof(return type)
const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);
if (voidPtrSize < returnTypeSize &&
@ -247,6 +336,6 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
C.GenerateNode(state->BindExpr(ME, V));
return;
}
C.addTransition(state);
}

View File

@ -45,3 +45,9 @@ void test_uninit_neg() {
test_unit_aux2(v2.x + v1.y); // no-warning
}
extern void test_uninit_struct_arg_aux(struct TestUninit arg);
void test_uninit_struct_arg() {
struct TestUninit x;
test_uninit_struct_arg_aux(x); // expected-warning{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'x')}}
}