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

Jordan Rose jordan_rose at apple.com
Thu Mar 13 10:55:39 PDT 2014


Author: jrose
Date: Thu Mar 13 12:55:39 2014
New Revision: 203822

URL: http://llvm.org/viewvc/llvm-project?rev=203822&view=rev
Log:
[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!

Added:
    cfe/trunk/test/Analysis/uninit-const.c
    cfe/trunk/test/Analysis/uninit-const.cpp
Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
    cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp?rev=203822&r1=203821&r2=203822&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp Thu Mar 13 12:55:39 2014
@@ -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<CallExpr>,
                     check::PreStmt<CXXDeleteExpr>,
@@ -46,6 +55,7 @@ class CallAndMessageChecker
   mutable std::unique_ptr<BugType> 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<BugType> &BT) const;
+  bool 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;
 
   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<BugType> &BT,
+                          const ParmVarDecl *ParamDecl, const char *BD) const;
 };
 } // end anonymous namespace
 
@@ -114,27 +129,86 @@ static StringRef describeUninitializedAr
   }
 }
 
-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 {
+bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &C,
+                                               const SVal &V,
+                                               const SourceRange &ArgRange,
+                                               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 (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<nonloc::LazyCompoundVal> LV =
@@ -185,7 +259,7 @@ bool CallAndMessageChecker::PreVisitProc
 
     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::PreVisitProc
 
         // 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 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
     // the function.
     unsigned Params = FD->getNumParams();
@@ -343,11 +418,15 @@ void CallAndMessageChecker::checkPreCall
   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::HandleNilRec
   C.addTransition(state);
 }
 
-void ento::registerCallAndMessageChecker(CheckerManager &mgr) {
-  mgr.registerChecker<CallAndMessageChecker>();
-}
+#define REGISTER_CHECKER(name)                                                 \
+  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)

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td?rev=203822&r1=203821&r2=203822&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td Thu Mar 13 12:55:39 2014
@@ -120,6 +120,10 @@ def SizeofPointerChecker : Checker<"Size
   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"
 
 //===----------------------------------------------------------------------===//

Added: cfe/trunk/test/Analysis/uninit-const.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/uninit-const.c?rev=203822&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/uninit-const.c (added)
+++ cfe/trunk/test/Analysis/uninit-const.c Thu Mar 13 12:55:39 2014
@@ -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -1 {{Function call argument is a pointer to uninitialized value}}
+}

Added: cfe/trunk/test/Analysis/uninit-const.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/uninit-const.cpp?rev=203822&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/uninit-const.cpp (added)
+++ cfe/trunk/test/Analysis/uninit-const.cpp Thu Mar 13 12:55:39 2014
@@ -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 at -1 {{Calling 'f6_1_sub'}}
+                       //expected-note at -2 {{Returning from 'f6_1_sub'}}
+                       //expected-note at -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 at -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 at -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 at -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 at -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 at -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 at -1 {{Function call argument is a pointer to uninitialized value}}
+}





More information about the cfe-commits mailing list