From 821a3a0f77052fee77bd86f241fe6a7740f59549 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Thu, 13 Mar 2014 17:55:39 +0000 Subject: [PATCH] [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 --- .../Checkers/CallAndMessageChecker.cpp | 132 +++++++++-- clang/lib/StaticAnalyzer/Checkers/Checkers.td | 4 + clang/test/Analysis/uninit-const.c | 216 ++++++++++++++++++ clang/test/Analysis/uninit-const.cpp | 128 +++++++++++ 4 files changed, 457 insertions(+), 23 deletions(-) create mode 100644 clang/test/Analysis/uninit-const.c create mode 100644 clang/test/Analysis/uninit-const.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 26e19565ed46..907f516ac3f7 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -27,6 +27,15 @@ using namespace clang; using namespace ento; namespace { + +struct ChecksFilter { + DefaultBool Check_CallAndMessageUnInitRefArg; + DefaultBool Check_CallAndMessageChecker; + + CheckName CheckName_CallAndMessageUnInitRefArg; + CheckName CheckName_CallAndMessageChecker; +}; + class CallAndMessageChecker : public Checker< check::PreStmt, check::PreStmt, @@ -46,6 +55,7 @@ class CallAndMessageChecker mutable std::unique_ptr BT_call_few_args; public: + ChecksFilter Filter; void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; @@ -53,10 +63,11 @@ public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; private: - bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange, - const Expr *argEx, bool IsFirstArgument, - bool checkUninitFields, const CallEvent &Call, - std::unique_ptr &BT) const; + bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange, + const Expr *ArgEx, bool IsFirstArgument, + bool CheckUninitFields, const CallEvent &Call, + std::unique_ptr &BT, + const ParmVarDecl *ParamDecl) const; static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE); void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, @@ -70,6 +81,10 @@ private: if (!BT) BT.reset(new BuiltinBug(this, desc)); } + bool uninitRefOrPointer(CheckerContext &C, const SVal &V, + const SourceRange &ArgRange, + const Expr *ArgEx, std::unique_ptr &BT, + const ParmVarDecl *ParamDecl, const char *BD) const; }; } // end anonymous namespace @@ -114,27 +129,86 @@ static StringRef describeUninitializedArgumentInCall(const CallEvent &Call, } } -bool CallAndMessageChecker::PreVisitProcessArg( - CheckerContext &C, SVal V, SourceRange argRange, const Expr *argEx, - bool IsFirstArgument, bool checkUninitFields, const CallEvent &Call, - std::unique_ptr &BT) const { +bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &C, + const SVal &V, + const SourceRange &ArgRange, + const Expr *ArgEx, + std::unique_ptr &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 &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 (ExplodedNode *N = C.generateSink()) { - LazyInit_BT("Uninitialized argument value", BT); + LazyInit_BT(BD, BT); // Generate a report for this bug. - StringRef Desc = describeUninitializedArgumentInCall(Call, - IsFirstArgument); + StringRef Desc = + describeUninitializedArgumentInCall(Call, IsFirstArgument); BugReport *R = new BugReport(*BT, Desc, N); - R->addRange(argRange); - if (argEx) - bugreporter::trackNullOrUndefValue(N, argEx, *R); + R->addRange(ArgRange); + if (ArgEx) + bugreporter::trackNullOrUndefValue(N, ArgEx, *R); C.emitReport(R); } return true; } - if (!checkUninitFields) + if (!CheckUninitFields) return false; if (Optional LV = @@ -185,7 +259,7 @@ bool CallAndMessageChecker::PreVisitProcessArg( if (F.Find(D->getRegion())) { if (ExplodedNode *N = C.generateSink()) { - LazyInit_BT("Uninitialized argument value", BT); + LazyInit_BT(BD, BT); SmallString<512> Str; llvm::raw_svector_ostream os(Str); os << "Passed-by-value struct argument contains uninitialized data"; @@ -208,7 +282,7 @@ bool CallAndMessageChecker::PreVisitProcessArg( // Generate a report for this bug. BugReport *R = new BugReport(*BT, os.str(), N); - R->addRange(argRange); + R->addRange(ArgRange); // FIXME: enhance track back for uninitialized value for arbitrary // memregions @@ -308,7 +382,8 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call, } const Decl *D = Call.getDecl(); - if (const FunctionDecl *FD = dyn_cast_or_null(D)) { + const FunctionDecl *FD = dyn_cast_or_null(D); + if (FD) { // If we have a declaration, we can make sure we pass enough parameters to // the function. unsigned Params = FD->getNumParams(); @@ -343,11 +418,15 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call, else 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), Call.getArgExpr(i), /*IsFirstArgument=*/i == 0, - checkUninitFields, Call, *BT)) + checkUninitFields, Call, *BT, ParamDecl)) return; + } // If we make it here, record our assumptions about the callee. C.addTransition(State); @@ -507,6 +586,13 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, C.addTransition(state); } -void ento::registerCallAndMessageChecker(CheckerManager &mgr) { - mgr.registerChecker(); -} +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &mgr) { \ + CallAndMessageChecker *Checker = \ + mgr.registerChecker(); \ + Checker->Filter.Check_##name = true; \ + Checker->Filter.CheckName_##name = mgr.getCurrentCheckName(); \ + } + +REGISTER_CHECKER(CallAndMessageUnInitRefArg) +REGISTER_CHECKER(CallAndMessageChecker) diff --git a/clang/lib/StaticAnalyzer/Checkers/Checkers.td b/clang/lib/StaticAnalyzer/Checkers/Checkers.td index 7eb8364df9f4..a457b44cbecf 100644 --- a/clang/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/lib/StaticAnalyzer/Checkers/Checkers.td @@ -120,6 +120,10 @@ def SizeofPointerChecker : Checker<"SizeofPtr">, HelpText<"Warn about unintended use of sizeof() on pointer expressions">, 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" //===----------------------------------------------------------------------===// diff --git a/clang/test/Analysis/uninit-const.c b/clang/test/Analysis/uninit-const.c new file mode 100644 index 000000000000..9e42d23214de --- /dev/null +++ b/clang/test/Analysis/uninit-const.c @@ -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}} +} diff --git a/clang/test/Analysis/uninit-const.cpp b/clang/test/Analysis/uninit-const.cpp new file mode 100644 index 000000000000..56bfa08e4176 --- /dev/null +++ b/clang/test/Analysis/uninit-const.cpp @@ -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}} +}