PR middle-end/78519 - missing warning for sprintf %s with null pointer
authorMartin Sebor <msebor@redhat.com>
Thu, 15 Dec 2016 04:35:31 +0000 (04:35 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Thu, 15 Dec 2016 04:35:31 +0000 (21:35 -0700)
gcc/ChangeLog:

PR middle-end/78519
* gimple-ssa-sprintf.c (format_string): Handle null pointers.
(format_directive): Diagnose null pointer arguments.
(pass_sprintf_length::handle_gimple_call): Diagnose null destination
pointers.  Correct location of null format string in diagnostics.

gcc/testsuite/ChangeLog:

PR middle-end/78519
* gcc.dg/tree-ssa/builtin-sprintf-warn-7.c: New test.

From-SVN: r243684

gcc/ChangeLog
gcc/gimple-ssa-sprintf.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-8.c [new file with mode: 0644]

index 159dda09e4205cd0dc32feefbcfc9b57d41e8a36..35123f0f77c3d978fd077020839966960d0e9782 100644 (file)
@@ -1,3 +1,11 @@
+2016-12-14  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/78519
+       * gimple-ssa-sprintf.c (format_string): Handle null pointers.
+       (format_directive): Diagnose null pointer arguments.
+       (pass_sprintf_length::handle_gimple_call): Diagnose null destination
+       pointers.  Correct location of null format string in diagnostics.
+
 2016-12-14  David Malcolm  <dmalcolm@redhat.com>
 
        * Makefile.in (SELFTEST_FLAGS): Add path argument to -fself-test.
index 5909e054a613b53711700fca319fae6dc62bd3e2..a91dcb8fc0b1668d744a09dfde74dc52bc1c9806 100644 (file)
@@ -439,7 +439,7 @@ struct result_range
 struct fmtresult
 {
   fmtresult ()
-  : argmin (), argmax (), knownrange (), bounded (), constant ()
+  : argmin (), argmax (), knownrange (), bounded (), constant (), nullp ()
   {
     range.min = range.max = HOST_WIDE_INT_MAX;
   }
@@ -467,6 +467,9 @@ struct fmtresult
      are also constant (such as determined by constant propagation,
      though not value range propagation).  */
   bool constant;
+
+  /* True when the argument is a null pointer.  */
+  bool nullp;
 };
 
 /* Description of a conversion specification.  */
@@ -1765,6 +1768,16 @@ format_string (const conversion_spec &spec, tree arg)
              res.range.min = 0;
            }
        }
+      else if (arg && integer_zerop (arg))
+       {
+         /* Handle null pointer argument.  */
+
+         fmtresult res;
+         res.range.min = 0;
+         res.range.max = HOST_WIDE_INT_MAX;
+         res.nullp = true;
+         return res;
+       }
       else
        {
          /* For a '%s' and '%ls' directive with a non-constant string,
@@ -1910,13 +1923,27 @@ format_directive (const pass_sprintf_length::call_info &info,
        }
     }
 
+  if (fmtres.nullp)
+    {
+      fmtwarn (dirloc, pargrange, NULL,
+              OPT_Wformat_length_,
+              "%<%.*s%> directive argument is null",
+              (int)cvtlen, cvtbeg);
+
+      /* Don't bother processing the rest of the format string.  */
+      res->warned = true;
+      res->number_chars = HOST_WIDE_INT_M1U;
+      res->number_chars_min = res->number_chars_max = res->number_chars;
+      return;
+    }
+
+  bool warned = res->warned;
+
   /* Compute the number of available bytes in the destination.  There
      must always be at least one byte of space for the terminating
      NUL that's appended after the format string has been processed.  */
   unsigned HOST_WIDE_INT navail = min_bytes_remaining (info.objsize, *res);
 
-  bool warned = res->warned;
-
   if (fmtres.range.min < fmtres.range.max)
     {
       /* The result is a range (i.e., it's inexact).  */
@@ -2871,6 +2898,9 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
       return;
     }
 
+  /* The first argument is a pointer to the destination.  */
+  tree dstptr = gimple_call_arg (info.callstmt, 0);
+
   info.format = gimple_call_arg (info.callstmt, idx_format);
 
   if (idx_dstsize == HOST_WIDE_INT_M1U)
@@ -2878,7 +2908,7 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
       /* For non-bounded functions like sprintf, determine the size
         of the destination from the object or pointer passed to it
         as the first argument.  */
-      dstsize = get_destination_size (gimple_call_arg (info.callstmt, 0));
+      dstsize = get_destination_size (dstptr);
     }
   else if (tree size = gimple_call_arg (info.callstmt, idx_dstsize))
     {
@@ -2948,6 +2978,20 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
     }
   else
     {
+      /* For calls to non-bounded functions or to those of bounded
+        functions with a non-zero size, warn if the destination
+        pointer is null.  */
+      if (integer_zerop (dstptr))
+       {
+         /* This is diagnosed with -Wformat only when the null is a constant
+            pointer.  The warning here diagnoses instances where the pointer
+            is not constant.  */
+         location_t loc = gimple_location (info.callstmt);
+         warning_at (EXPR_LOC_OR_LOC (dstptr, loc),
+                     OPT_Wformat_length_, "null destination pointer");
+         return;
+       }
+
       /* Set the object size to the smaller of the two arguments
         of both have been specified and they're not equal.  */
       info.objsize = dstsize < objsize ? dstsize : objsize;
@@ -2971,7 +3015,8 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
       /* This is diagnosed with -Wformat only when the null is a constant
         pointer.  The warning here diagnoses instances where the pointer
         is not constant.  */
-      warning_at (EXPR_LOC_OR_LOC (info.format, input_location),
+      location_t loc = gimple_location (info.callstmt);
+      warning_at (EXPR_LOC_OR_LOC (info.format, loc),
                  OPT_Wformat_length_, "null format string");
       return;
     }
index 88665a2c6a825c49ab86da2f679a429dd761a0c0..f23ccd6ba24b4dc10af2f5bd8a66df4020b32ba2 100644 (file)
@@ -1,3 +1,8 @@
+2016-12-14  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/78519
+       * gcc.dg/tree-ssa/builtin-sprintf-warn-7.c: New test.
+
 2016-12-14  Martin Sebor  <msebor@redhat.com>
 
        PR c++/78774
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-8.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-8.c
new file mode 100644 (file)
index 0000000..2550065
--- /dev/null
@@ -0,0 +1,93 @@
+/* PR middle-end/78519 - missing warning for sprintf %s with null pointer
+   Also exercises null destination pointer and null format string.
+   { dg-do compile }
+   { dg-options "-O2 -Wformat -Wformat-length -Wno-nonnull -ftrack-macro-expansion=0" } */
+
+typedef __builtin_va_list va_list;
+
+#define sprintf   __builtin_sprintf
+#define snprintf  __builtin_snprintf
+#define vsprintf  __builtin_vsprintf
+#define vsnprintf __builtin_vsnprintf
+
+
+static char* null (void)
+{
+  return 0;
+}
+
+
+void sink (int);
+#define T sink
+
+
+/* Verify that calls with a null destination pointer are diagnosed.  */
+
+void test_null_dest (va_list va)
+{
+  char *p = null ();
+  T (sprintf (null (), "%i", 0));   /* { dg-warning "null destination pointer" } */
+  T (sprintf (p, "%i", 0));         /* { dg-warning "null destination pointer" } */
+  T (sprintf (p, "%i abc", 0));     /* { dg-warning "null destination pointer" } */
+
+  T (snprintf (null (), 1, "%i", 0));   /* { dg-warning "null destination pointer" } */
+  T (snprintf (p, 2, "%i", 0));         /* { dg-warning "null destination pointer" } */
+  T (snprintf (p, 3, "%i abc", 0));     /* { dg-warning "null destination pointer" } */
+
+  /* Snprintf with a null pointer and a zero size is a special request
+     to determine the size of output without writing any.  Such calls
+     are valid must not be diagnosed.  */
+  T (snprintf (p, 0, "%i", 0));
+
+  T (vsprintf (null (), "%i", va)); /* { dg-warning "null destination pointer" } */
+  T (vsprintf (p,       "%i", va)); /* { dg-warning "null destination pointer" } */
+
+  T (vsnprintf (null (), 1, "%i", va)); /* { dg-warning "null destination pointer" } */
+  T (vsnprintf (p,       2, "%i", va));       /* { dg-warning "null destination pointer" } */
+  T (vsnprintf (p,       0, "%i", va));
+}
+
+void test_null_format (char *d, va_list va)
+{
+  char *fmt = null ();
+
+  T (sprintf (d, null ()));    /* { dg-warning "null format string" } */
+  T (sprintf (d, fmt));        /* { dg-warning "null format string" } */
+
+  T (snprintf (d, 0, null ()));    /* { dg-warning "null format string" } */
+  T (snprintf (d, 1, fmt));        /* { dg-warning "null format string" } */
+
+  T (vsprintf (d, null (), va));   /* { dg-warning "null format string" } */
+  T (vsprintf (d, fmt, va));       /* { dg-warning "null format string" } */
+
+  T (vsnprintf (d, 0, null (), va));  /* { dg-warning "null format string" } */
+  T (vsnprintf (d, 1, fmt, va));      /* { dg-warning "null format string" } */
+}
+
+void test_null_arg (char *d, const char *s)
+{
+  char *p = null ();
+
+  T (sprintf (d, "%-s", null ()));  /* { dg-warning "directive argument is null" } */
+  T (sprintf (d, "%-s", p));        /* { dg-warning "directive argument is null" } */
+  T (sprintf (d, "%s %s", p, s));   /* { dg-warning "directive argument is null" } */
+  T (sprintf (d, "%s %s", s, p));   /* { dg-warning "directive argument is null" } */
+  T (sprintf (d, "%s %i", p, 1));   /* { dg-warning "directive argument is null" } */
+  T (sprintf (d, "%i %s", 1, p));   /* { dg-warning "directive argument is null" } */
+  T (sprintf (d, "%.0s", p));       /* { dg-warning "directive argument is null" } */
+  T (sprintf (d, "%1.0s", p));      /* { dg-warning "directive argument is null" } */
+
+  T (snprintf (d, 0, "%-s", null ()));  /* { dg-warning "directive argument is null" } */
+  T (snprintf (d, 1, "%-s", p));        /* { dg-warning "directive argument is null" } */
+
+  T (sprintf (d, "%i %s", 1, null ()));  /* { dg-warning "directive argument is null" } */
+  T (sprintf (d, "%i %s", 2, p));        /* { dg-warning "directive argument is null" } */
+
+  T (snprintf (d, 0, "%i %s", 1, null ()));  /* { dg-warning "directive argument is null" } */
+  T (snprintf (d, 9, "%i %s", 2, p));        /* { dg-warning "directive argument is null" } */
+
+  /* A sanity check that the %p directive doesn't emit a warning
+     with a null pointer.  */
+  T (sprintf (d, "%p", null ()));
+  T (sprintf (d, "%p", p));
+}