[analyzer] Warn when passing pointers to const but uninitialized memory.

Passing a pointer to an uninitialized memory buffer is normally okay,
but if the function is declared to take a pointer-to-const then it's
very unlikely it will be modifying the buffer. In this case the analyzer
should warn that there will likely be a read of uninitialized memory.

This doesn't check all elements of an array, only the first one.
It also doesn't yet check Objective-C methods, only C functions and
C++ methods.

This is controlled by a new check: alpha.core.CallAndMessageUnInitRefArg.

Patch by Per Viberg!

llvm-svn: 203822
This commit is contained in:
Jordan Rose 2014-03-13 17:55:39 +00:00
parent 11765bce2d
commit 821a3a0f77
4 changed files with 457 additions and 23 deletions

View File

@ -27,6 +27,15 @@ using namespace clang;
using namespace ento; using namespace ento;
namespace { namespace {
struct ChecksFilter {
DefaultBool Check_CallAndMessageUnInitRefArg;
DefaultBool Check_CallAndMessageChecker;
CheckName CheckName_CallAndMessageUnInitRefArg;
CheckName CheckName_CallAndMessageChecker;
};
class CallAndMessageChecker class CallAndMessageChecker
: public Checker< check::PreStmt<CallExpr>, : public Checker< check::PreStmt<CallExpr>,
check::PreStmt<CXXDeleteExpr>, check::PreStmt<CXXDeleteExpr>,
@ -46,6 +55,7 @@ class CallAndMessageChecker
mutable std::unique_ptr<BugType> BT_call_few_args; mutable std::unique_ptr<BugType> BT_call_few_args;
public: public:
ChecksFilter Filter;
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
@ -53,10 +63,11 @@ public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
private: private:
bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange, bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange,
const Expr *argEx, bool IsFirstArgument, const Expr *ArgEx, bool IsFirstArgument,
bool checkUninitFields, const CallEvent &Call, bool CheckUninitFields, const CallEvent &Call,
std::unique_ptr<BugType> &BT) const; std::unique_ptr<BugType> &BT,
const ParmVarDecl *ParamDecl) const;
static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE); static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE);
void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
@ -70,6 +81,10 @@ private:
if (!BT) if (!BT)
BT.reset(new BuiltinBug(this, desc)); BT.reset(new BuiltinBug(this, desc));
} }
bool uninitRefOrPointer(CheckerContext &C, const SVal &V,
const SourceRange &ArgRange,
const Expr *ArgEx, std::unique_ptr<BugType> &BT,
const ParmVarDecl *ParamDecl, const char *BD) const;
}; };
} // end anonymous namespace } // end anonymous namespace
@ -114,27 +129,86 @@ static StringRef describeUninitializedArgumentInCall(const CallEvent &Call,
} }
} }
bool CallAndMessageChecker::PreVisitProcessArg( bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &C,
CheckerContext &C, SVal V, SourceRange argRange, const Expr *argEx, const SVal &V,
bool IsFirstArgument, bool checkUninitFields, const CallEvent &Call, const SourceRange &ArgRange,
std::unique_ptr<BugType> &BT) const { const Expr *ArgEx,
std::unique_ptr<BugType> &BT,
const ParmVarDecl *ParamDecl,
const char *BD) const {
if (!Filter.Check_CallAndMessageUnInitRefArg)
return false;
// No parameter declaration available, i.e. variadic function argument.
if(!ParamDecl)
return false;
// If parameter is declared as pointer to const in function declaration,
// then check if corresponding argument in function call is
// pointing to undefined symbol value (uninitialized memory).
StringRef Message;
if (ParamDecl->getType()->isPointerType()) {
Message = "Function call argument is a pointer to uninitialized value";
} else if (ParamDecl->getType()->isReferenceType()) {
Message = "Function call argument is an uninitialized value";
} else
return false;
if(!ParamDecl->getType()->getPointeeType().isConstQualified())
return false;
if (const MemRegion *SValMemRegion = V.getAsRegion()) {
const ProgramStateRef State = C.getState();
const SVal PSV = State->getSVal(SValMemRegion);
if (PSV.isUndef()) {
if (ExplodedNode *N = C.generateSink()) {
LazyInit_BT(BD, BT);
BugReport *R = new BugReport(*BT, Message, N);
R->addRange(ArgRange);
if (ArgEx) {
bugreporter::trackNullOrUndefValue(N, ArgEx, *R);
}
C.emitReport(R);
}
return true;
}
}
return false;
}
bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
SVal V,
SourceRange ArgRange,
const Expr *ArgEx,
bool IsFirstArgument,
bool CheckUninitFields,
const CallEvent &Call,
std::unique_ptr<BugType> &BT,
const ParmVarDecl *ParamDecl
) const {
const char *BD = "Uninitialized argument value";
if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD))
return true;
if (V.isUndef()) { if (V.isUndef()) {
if (ExplodedNode *N = C.generateSink()) { if (ExplodedNode *N = C.generateSink()) {
LazyInit_BT("Uninitialized argument value", BT); LazyInit_BT(BD, BT);
// Generate a report for this bug. // Generate a report for this bug.
StringRef Desc = describeUninitializedArgumentInCall(Call, StringRef Desc =
IsFirstArgument); describeUninitializedArgumentInCall(Call, IsFirstArgument);
BugReport *R = new BugReport(*BT, Desc, N); BugReport *R = new BugReport(*BT, Desc, N);
R->addRange(argRange); R->addRange(ArgRange);
if (argEx) if (ArgEx)
bugreporter::trackNullOrUndefValue(N, argEx, *R); bugreporter::trackNullOrUndefValue(N, ArgEx, *R);
C.emitReport(R); C.emitReport(R);
} }
return true; return true;
} }
if (!checkUninitFields) if (!CheckUninitFields)
return false; return false;
if (Optional<nonloc::LazyCompoundVal> LV = if (Optional<nonloc::LazyCompoundVal> LV =
@ -185,7 +259,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(
if (F.Find(D->getRegion())) { if (F.Find(D->getRegion())) {
if (ExplodedNode *N = C.generateSink()) { if (ExplodedNode *N = C.generateSink()) {
LazyInit_BT("Uninitialized argument value", BT); LazyInit_BT(BD, BT);
SmallString<512> Str; SmallString<512> Str;
llvm::raw_svector_ostream os(Str); llvm::raw_svector_ostream os(Str);
os << "Passed-by-value struct argument contains uninitialized data"; os << "Passed-by-value struct argument contains uninitialized data";
@ -208,7 +282,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(
// Generate a report for this bug. // Generate a report for this bug.
BugReport *R = new BugReport(*BT, os.str(), N); BugReport *R = new BugReport(*BT, os.str(), N);
R->addRange(argRange); R->addRange(ArgRange);
// FIXME: enhance track back for uninitialized value for arbitrary // FIXME: enhance track back for uninitialized value for arbitrary
// memregions // memregions
@ -308,7 +382,8 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
} }
const Decl *D = Call.getDecl(); const Decl *D = Call.getDecl();
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) { const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
if (FD) {
// If we have a declaration, we can make sure we pass enough parameters to // If we have a declaration, we can make sure we pass enough parameters to
// the function. // the function.
unsigned Params = FD->getNumParams(); unsigned Params = FD->getNumParams();
@ -343,11 +418,15 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
else else
BT = &BT_call_arg; BT = &BT_call_arg;
for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
const ParmVarDecl *ParamDecl = NULL;
if(FD && i < FD->getNumParams())
ParamDecl = FD->getParamDecl(i);
if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i), if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
Call.getArgExpr(i), /*IsFirstArgument=*/i == 0, Call.getArgExpr(i), /*IsFirstArgument=*/i == 0,
checkUninitFields, Call, *BT)) checkUninitFields, Call, *BT, ParamDecl))
return; return;
}
// If we make it here, record our assumptions about the callee. // If we make it here, record our assumptions about the callee.
C.addTransition(State); C.addTransition(State);
@ -507,6 +586,13 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
C.addTransition(state); C.addTransition(state);
} }
void ento::registerCallAndMessageChecker(CheckerManager &mgr) { #define REGISTER_CHECKER(name) \
mgr.registerChecker<CallAndMessageChecker>(); void ento::register##name(CheckerManager &mgr) { \
} CallAndMessageChecker *Checker = \
mgr.registerChecker<CallAndMessageChecker>(); \
Checker->Filter.Check_##name = true; \
Checker->Filter.CheckName_##name = mgr.getCurrentCheckName(); \
}
REGISTER_CHECKER(CallAndMessageUnInitRefArg)
REGISTER_CHECKER(CallAndMessageChecker)

View File

@ -120,6 +120,10 @@ def SizeofPointerChecker : Checker<"SizeofPtr">,
HelpText<"Warn about unintended use of sizeof() on pointer expressions">, HelpText<"Warn about unintended use of sizeof() on pointer expressions">,
DescFile<"CheckSizeofPointer.cpp">; DescFile<"CheckSizeofPointer.cpp">;
def CallAndMessageUnInitRefArg : Checker<"CallAndMessageUnInitRefArg">,
HelpText<"Check for logical errors for function calls and Objective-C message expressions (e.g., uninitialized arguments, null function pointers, and pointer to undefined variables)">,
DescFile<"CallAndMessageChecker.cpp">;
} // end "alpha.core" } // end "alpha.core"
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -0,0 +1,216 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=unix.Malloc,core,alpha.core.CallAndMessageUnInitRefArg -analyzer-output=text -verify %s
// Passing uninitialized const data to function
#include "Inputs/system-header-simulator.h"
typedef __typeof(sizeof(int)) size_t;
void *malloc(size_t);
void *valloc(size_t);
void free(void *);
void doStuff3(const int y){}
void doStuff2(int g){}
void doStuff_pointerToConstInt(const int *u){};
void doStuff_arrayOfConstInt(const int a[]){};
void doStuff_constPointerToConstInt (int const * const u){};
void doStuff_constPointerToConstPointerToConstInt(int const * const * const u){};
void doStuff_pointerToConstPointerToConstInt(int const * const * u){};
void doStuff_pointerToPointerToConstInt (int const **u){};
void doStuff_constStaticSizedArray(const int a[static 10]) {}
void doStuff_variadic(const int *u, ...){};
void f_1(void) {
int t;
int* tp = &t; // expected-note {{'tp' initialized here}}
doStuff_pointerToConstInt(tp); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}
void f_1_1(void) {
int t;
int* tp1 = &t;
int* tp2 = tp1; // expected-note {{'tp2' initialized here}}
doStuff_pointerToConstInt(tp2); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}
int *f_2_sub(int *p) {
return p;
}
void f_2(void) {
int t;
int* p = f_2_sub(&t);
int* tp = p; // expected-note {{'tp' initialized here}}
doStuff_pointerToConstInt(tp); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}
int z;
void f_3(void) {
doStuff_pointerToConstInt(&z); // no warning
}
void f_4(void) {
int x=5;
doStuff_pointerToConstInt(&x); // no warning
}
void f_5(void) {
int ta[5];
int* tp = ta; // expected-note {{'tp' initialized here}}
doStuff_pointerToConstInt(tp); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}
void f_5_1(void) {
int ta[5]; // expected-note {{'ta' initialized here}}
doStuff_pointerToConstInt(ta); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}
void f_6(void) {
int ta[5] = {1,2,3,4,5};
int* tp = ta;
doStuff_pointerToConstInt(tp); // no-warning
}
void f_6_1(void) {
int ta[5] = {1,2,3,4,5};
doStuff_pointerToConstInt(ta); // no-warning
}
void f_7(void) {
int z; // expected-note {{'z' declared without an initial value}}
int y=z; // expected-warning {{Assigned value is garbage or undefined}}
// expected-note@-1 {{Assigned value is garbage or undefined}}
doStuff3(y);
}
void f_8(void) {
int g; // expected-note {{'g' declared without an initial value}}
doStuff2(g); // expected-warning {{Function call argument is an uninitialized value}}
// expected-note@-1 {{Function call argument is an uninitialized value}}
}
void f_9(void) {
int a[6];
int const *ptau = a; // expected-note {{'ptau' initialized here}}
doStuff_arrayOfConstInt(ptau); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}
void f_10(void) {
int a[6]; // expected-note {{'a' initialized here}}
doStuff_arrayOfConstInt(a); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}
void f_11(void) {
int t[10]; //expected-note {{'t' initialized here}}
doStuff_constStaticSizedArray(t); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}
void f_12(void) {
int t[10] = {0,1,2,3,4,5,6,7,8,9};
doStuff_constStaticSizedArray(t); // no-warning
}
int f_malloc_1(void) {
int *ptr;
ptr = (int *)malloc(sizeof(int)); // expected-note {{Value assigned to 'ptr'}}
doStuff_pointerToConstInt(ptr); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
free(ptr);
return 0;
}
int f_malloc_2(void) {
int *ptr;
ptr = (int *)malloc(sizeof(int));
*ptr = 25;
doStuff_pointerToConstInt(ptr); // no warning
free(ptr);
return 0;
}
// uninit pointer, uninit val
void f_variadic_unp_unv(void) {
int t;
int v;
int* tp = &t; // expected-note {{'tp' initialized here}}
doStuff_variadic(tp,v); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}
// uninit pointer, init val
void f_variadic_unp_inv(void) {
int t;
int v = 3;
int* tp = &t; // expected-note {{'tp' initialized here}}
doStuff_variadic(tp,v); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}
// init pointer, uninit val
void f_variadic_inp_unv(void) {
int t=5;
int v; // expected-note {{'v' declared without an initial value}}
int* tp = &t;
doStuff_variadic(tp,v);// expected-warning {{Function call argument is an uninitialized value}}
// expected-note@-1 {{Function call argument is an uninitialized value}}
}
// init pointer, init val
void f_variadic_inp_inv(void) {
int t=5;
int v = 3;
int* tp = &t;
doStuff_variadic(tp,v); // no-warning
}
// init pointer, init pointer
void f_variadic_inp_inp(void) {
int t=5;
int u=3;
int *vp = &u ;
int *tp = &t;
doStuff_variadic(tp,vp); // no-warning
}
//uninit pointer, init pointer
void f_variadic_unp_inp(void) {
int t;
int u=3;
int *vp = &u ;
int *tp = &t; // expected-note {{'tp' initialized here}}
doStuff_variadic(tp,vp); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}
//init pointer, uninit pointer
void f_variadic_inp_unp(void) {
int t=5;
int u;
int *vp = &u ;
int *tp = &t;
doStuff_variadic(tp,vp); // no-warning
}
//uninit pointer, uninit pointer
void f_variadic_unp_unp(void) {
int t;
int u;
int *vp = &u ;
int *tp = &t; // expected-note {{'tp' initialized here}}
doStuff_variadic(tp,vp); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}

View File

@ -0,0 +1,128 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=cplusplus.NewDelete,core,alpha.core.CallAndMessageUnInitRefArg -analyzer-output=text -verify %s
// Passing uninitialized const data to unknown function
#include "Inputs/system-header-simulator-cxx.h"
void doStuff6(const int& c);
void doStuff4(const int y);
void doStuff3(int& g);
void doStuff_uninit(const int *u);
int f10(void) {
int *ptr;
ptr = new int; //
if(*ptr) {
doStuff4(*ptr);
}
delete ptr;
return 0;
}
int f9(void) {
int *ptr;
ptr = new int; //
doStuff_uninit(ptr); // no warning
delete ptr;
return 0;
}
int f8(void) {
int *ptr;
ptr = new int;
*ptr = 25;
doStuff_uninit(ptr); // no warning?
delete ptr;
return 0;
}
void f7(void) {
int m = 3;
doStuff6(m); // no warning
}
int& f6_1_sub(int &p) {
return p;
}
void f6_1(void) {
int t;
int p = f6_1_sub(t); //expected-warning {{Assigned value is garbage or undefined}}
//expected-note@-1 {{Calling 'f6_1_sub'}}
//expected-note@-2 {{Returning from 'f6_1_sub'}}
//expected-note@-3 {{Assigned value is garbage or undefined}}
int q = p;
doStuff6(q);
}
void f6_2(void) {
int t; //expected-note {{'t' declared without an initial value}}
int &p = t;
int &s = p;
int &q = s; //expected-note {{'q' initialized here}}
doStuff6(q); //expected-warning {{Function call argument is an uninitialized value}}
//expected-note@-1 {{Function call argument is an uninitialized value}}
}
void doStuff6_3(int& q_, int *ptr_) {}
void f6_3(void) {
int *ptr; //expected-note {{'ptr' declared without an initial value}}
int t;
int &p = t;
int &s = p;
int &q = s;
doStuff6_3(q,ptr); //expected-warning {{Function call argument is an uninitialized value}}
//expected-note@-1 {{Function call argument is an uninitialized value}}
}
void f6(void) {
int k; // expected-note {{'k' declared without an initial value}}
doStuff6(k); // expected-warning {{Function call argument is an uninitialized value}}
// expected-note@-1 {{Function call argument is an uninitialized value}}
}
void f5(void) {
int t;
int* tp = &t; // expected-note {{'tp' initialized here}}
doStuff_uninit(tp); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}
void f4(void) {
int y; // expected-note {{'y' declared without an initial value}}
doStuff4(y); // expected-warning {{Function call argument is an uninitialized value}}
// expected-note@-1 {{Function call argument is an uninitialized value}}
}
void f3(void) {
int g;
doStuff3(g); // no warning
}
int z;
void f2(void) {
doStuff_uninit(&z); // no warning
}
void f1(void) {
int x_=5;
doStuff_uninit(&x_); // no warning
}
void f_uninit(void) {
int x;
doStuff_uninit(&x); // expected-warning {{Function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{Function call argument is a pointer to uninitialized value}}
}