PR middle-end/77671 - missing -Wformat-overflow warning on sprintf overflow with %s
authorMartin Sebor <msebor@redhat.com>
Sun, 14 May 2017 17:50:28 +0000 (17:50 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Sun, 14 May 2017 17:50:28 +0000 (11:50 -0600)
gcc/ChangeLog:

PR middle-end/77671
* gimple-fold.c (gimple_fold_builtin_sprintf): Make extern.
(gimple_fold_builtin_snprintf): Same.
* gimple-fold.h (gimple_fold_builtin_sprintf): Declare.
(gimple_fold_builtin_snprintf): Same.
* gimple-ssa-sprintf.c (get_format_string): Correct the detection
of character types.
(is_call_safe): New function.
(try_substitute_return_value): Call it.
(try_simplify_call): New function.
(pass_sprintf_length::handle_gimple_call): Call it.

gcc/testsuite/ChangeLog:

PR middle-end/77671
* gcc.dg/tree-ssa/builtin-sprintf-7.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf-8.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Adjust.
* gcc.dg/tree-ssa/builtin-sprintf-warn-2.c: Adjust.
* gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: Adjust.

From-SVN: r248035

gcc/ChangeLog
gcc/gimple-fold.c
gcc/gimple-fold.h
gcc/gimple-ssa-sprintf.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-7.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-8.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-2.c
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-3.c

index b35a5343fbc8e6ef03a16aa81a9a850feb540203..4f20bcef11e37cb43bb8d312452853ee3dbae368 100644 (file)
@@ -1,3 +1,17 @@
+2017-05-14  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/77671
+       * gimple-fold.c (gimple_fold_builtin_sprintf): Make extern.
+       (gimple_fold_builtin_snprintf): Same.
+       * gimple-fold.h (gimple_fold_builtin_sprintf): Declare.
+       (gimple_fold_builtin_snprintf): Same.
+       * gimple-ssa-sprintf.c (get_format_string): Correct the detection
+       of character types.
+       (is_call_safe): New function.
+       (try_substitute_return_value): Call it.
+       (try_simplify_call): New function.
+       (pass_sprintf_length::handle_gimple_call): Call it.
+
 2017-05-14  Martin Sebor  <msebor@redhat.com>
 
        PR middle-end/80669
index aca842be54bd7758ee0a7098034b61361eec9ad6..921e120cf865ea3edd89506b93d3299e134bf8f2 100644 (file)
@@ -2670,11 +2670,9 @@ gimple_fold_builtin_sprintf_chk (gimple_stmt_iterator *gsi,
    ORIG may be null if this is a 2-argument call.  We don't attempt to
    simplify calls with more than 3 arguments.
 
-   Return NULL_TREE if no simplification was possible, otherwise return the
-   simplified form of the call as a tree.  If IGNORED is true, it means that
-   the caller does not use the returned value of the function.  */
+   Return true if simplification was possible, otherwise false.  */
 
-static bool
+bool
 gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
 {
   gimple *stmt = gsi_stmt (*gsi);
@@ -2795,11 +2793,9 @@ gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
    FMT, and ORIG.  ORIG may be null if this is a 3-argument call.  We don't
    attempt to simplify calls with more than 4 arguments.
 
-   Return NULL_TREE if no simplification was possible, otherwise return the
-   simplified form of the call as a tree.  If IGNORED is true, it means that
-   the caller does not use the returned value of the function.  */
+   Return true if simplification was possible, otherwise false.  */
 
-static bool
+bool
 gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
 {
   gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
@@ -3384,10 +3380,7 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
       return gimple_fold_builtin_snprintf_chk (gsi, fcode);
-    case BUILT_IN_SNPRINTF:
-      return gimple_fold_builtin_snprintf (gsi);
-    case BUILT_IN_SPRINTF:
-      return gimple_fold_builtin_sprintf (gsi);
+
     case BUILT_IN_FPRINTF:
     case BUILT_IN_FPRINTF_UNLOCKED:
     case BUILT_IN_VFPRINTF:
index d34d880af4598a02db4f07739d129475e79ba5ec..ad0b00dccadb8b1d13b71c256ba2aa00d9c82cb0 100644 (file)
@@ -54,6 +54,8 @@ extern tree gimple_get_virt_method_for_vtable (HOST_WIDE_INT, tree,
                                               unsigned HOST_WIDE_INT,
                                               bool *can_refer = NULL);
 extern tree gimple_fold_indirect_ref (tree);
+extern bool gimple_fold_builtin_sprintf (gimple_stmt_iterator *);
+extern bool gimple_fold_builtin_snprintf (gimple_stmt_iterator *);
 extern bool arith_code_with_undefined_signed_overflow (tree_code);
 extern gimple_seq rewrite_to_defined_overflow (gimple *);
 
index a3153c10019383ae7f75c7e8d331f9e378282f56..f43778bbcfcd6ddf51ec9f8fc6c54f4fefe27bf4 100644 (file)
@@ -526,7 +526,10 @@ get_format_string (tree format, location_t *ploc)
   if (TREE_CODE (format) != STRING_CST)
     return NULL;
 
-  if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (format))) != char_type_node)
+  tree type = TREE_TYPE (format);
+
+  if (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (type))) != MODE_INT
+      || GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (type))) != 1)
     {
       /* Wide format string.  */
       return NULL;
@@ -3511,31 +3514,25 @@ get_destination_size (tree dest)
   return HOST_WIDE_INT_M1U;
 }
 
-/* Given a suitable result RES of a call to a formatted output function
-   described by INFO, substitute the result for the return value of
-   the call.  The result is suitable if the number of bytes it represents
-   is known and exact.  A result that isn't suitable for substitution may
-   have its range set to the range of return values, if that is known.
-   Return true if the call is removed and gsi_next should not be performed
-   in the caller.  */
+/* Return true if the call described by INFO with result RES safe to
+   optimize (i.e., no undefined behavior), and set RETVAL to the range
+   of its return values.  */
 
 static bool
-try_substitute_return_value (gimple_stmt_iterator *gsi,
-                            const pass_sprintf_length::call_info &info,
-                            const format_result &res)
+is_call_safe (const pass_sprintf_length::call_info &info,
+             const format_result &res, bool under4k,
+             unsigned HOST_WIDE_INT retval[2])
 {
-  tree lhs = gimple_get_lhs (info.callstmt);
-
-  /* Set to true when the entire call has been removed.  */
-  bool removed = false;
+  if (under4k && !res.under4k)
+    return false;
 
   /* The minimum return value.  */
-  unsigned HOST_WIDE_INT minretval = res.range.min;
+  retval[0] = res.range.min;
 
   /* The maximum return value is in most cases bounded by RES.RANGE.MAX
      but in cases involving multibyte characters could be as large as
      RES.RANGE.UNLIKELY.  */
-  unsigned HOST_WIDE_INT maxretval
+  retval[1]
     = res.range.unlikely < res.range.max ? res.range.max : res.range.unlikely;
 
   /* Adjust the number of bytes which includes the terminating nul
@@ -3544,10 +3541,10 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
      a valid range before the adjustment below is [0, INT_MAX + 1]
      (the functions only return negative values on error or undefined
      behavior).  */
-  if (minretval <= target_int_max () + 1)
-    --minretval;
-  if (maxretval <= target_int_max () + 1)
-    --maxretval;
+  if (retval[0] <= target_int_max () + 1)
+    --retval[0];
+  if (retval[1] <= target_int_max () + 1)
+    --retval[1];
 
   /* Avoid the return value optimization when the behavior of the call
      is undefined either because any directive may have produced 4K or
@@ -3555,16 +3552,52 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
      the output overflows the destination object (but leave it enabled
      when the function is bounded because then the behavior is well-
      defined).  */
-  if (res.under4k
-      && minretval == maxretval
-      && (info.bounded || minretval < info.objsize)
-      && minretval <= target_int_max ()
+  if (retval[0] == retval[1]
+      && (info.bounded || retval[0] < info.objsize)
+      && retval[0] <= target_int_max ())
+    return true;
+
+  if ((info.bounded || retval[1] < info.objsize)
+      && (retval[0] < target_int_max ()
+         && retval[1] < target_int_max ()))
+    return true;
+
+  if (!under4k && (info.bounded || retval[0] < info.objsize))
+    return true;
+
+  return false;
+}
+
+/* Given a suitable result RES of a call to a formatted output function
+   described by INFO, substitute the result for the return value of
+   the call.  The result is suitable if the number of bytes it represents
+   is known and exact.  A result that isn't suitable for substitution may
+   have its range set to the range of return values, if that is known.
+   Return true if the call is removed and gsi_next should not be performed
+   in the caller.  */
+
+static bool
+try_substitute_return_value (gimple_stmt_iterator *gsi,
+                            const pass_sprintf_length::call_info &info,
+                            const format_result &res)
+{
+  tree lhs = gimple_get_lhs (info.callstmt);
+
+  /* Set to true when the entire call has been removed.  */
+  bool removed = false;
+
+  /* The minimum and maximum return value.  */
+  unsigned HOST_WIDE_INT retval[2];
+  bool safe = is_call_safe (info, res, true, retval);
+
+  if (safe
+      && retval[0] == retval[1]
       /* Not prepared to handle possibly throwing calls here; they shouldn't
         appear in non-artificial testcases, except when the __*_chk routines
         are badly declared.  */
       && !stmt_ends_bb_p (info.callstmt))
     {
-      tree cst = build_int_cst (integer_type_node, minretval);
+      tree cst = build_int_cst (integer_type_node, retval[0]);
 
       if (lhs == NULL_TREE
          && info.nowrite)
@@ -3612,18 +3645,18 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
     {
       bool setrange = false;
 
-      if ((info.bounded || maxretval < info.objsize)
-         && res.under4k
-         && (minretval < target_int_max ()
-             && maxretval < target_int_max ()))
+      if (safe
+         && (info.bounded || retval[1] < info.objsize)
+         && (retval[0] < target_int_max ()
+             && retval[1] < target_int_max ()))
        {
          /* If the result is in a valid range bounded by the size of
             the destination set it so that it can be used for subsequent
             optimizations.  */
          int prec = TYPE_PRECISION (integer_type_node);
 
-         wide_int min = wi::shwi (minretval, prec);
-         wide_int max = wi::shwi (maxretval, prec);
+         wide_int min = wi::shwi (retval[0], prec);
+         wide_int max = wi::shwi (retval[1], prec);
          set_range_info (lhs, VR_RANGE, min, max);
 
          setrange = true;
@@ -3632,21 +3665,21 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
       if (dump_file)
        {
          const char *inbounds
-           = (minretval < info.objsize
-              ? (maxretval < info.objsize
+           = (retval[0] < info.objsize
+              ? (retval[1] < info.objsize
                  ? "in" : "potentially out-of")
               : "out-of");
 
          const char *what = setrange ? "Setting" : "Discarding";
-         if (minretval != maxretval)
+         if (retval[0] != retval[1])
            fprintf (dump_file,
                     "  %s %s-bounds return value range [%llu, %llu].\n",
                     what, inbounds,
-                    (unsigned long long)minretval,
-                    (unsigned long long)maxretval);
+                    (unsigned long long)retval[0],
+                    (unsigned long long)retval[1]);
          else
            fprintf (dump_file, "  %s %s-bounds return value %llu.\n",
-                    what, inbounds, (unsigned long long)minretval);
+                    what, inbounds, (unsigned long long)retval[0]);
        }
     }
 
@@ -3656,6 +3689,34 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
   return removed;
 }
 
+/* Try to simplify a s{,n}printf call described by INFO with result
+   RES by replacing it with a simpler and presumably more efficient
+   call (such as strcpy).  */
+
+static bool
+try_simplify_call (gimple_stmt_iterator *gsi,
+                  const pass_sprintf_length::call_info &info,
+                  const format_result &res)
+{
+  unsigned HOST_WIDE_INT dummy[2];
+  if (!is_call_safe (info, res, info.retval_used (), dummy))
+    return false;
+
+  switch (info.fncode)
+    {
+    case BUILT_IN_SNPRINTF:
+      return gimple_fold_builtin_snprintf (gsi);
+
+    case BUILT_IN_SPRINTF:
+      return gimple_fold_builtin_sprintf (gsi);
+
+    default:
+      ;
+    }
+
+  return false;
+}
+
 /* Determine if a GIMPLE CALL is to one of the sprintf-like built-in
    functions and if so, handle it.  Return true if the call is removed
    and gsi_next should not be performed in the caller.  */
@@ -3907,13 +3968,23 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
      attempt to substitute the computed result for the return value of
      the call.  Avoid this optimization when -frounding-math is in effect
      and the format string contains a floating point directive.  */
-  if (success
-      && optimize > 0
-      && flag_printf_return_value
-      && (!flag_rounding_math || !res.floating))
-    return try_substitute_return_value (gsi, info, res);
+  bool call_removed = false;
+  if (success && optimize > 0)
+    {
+      /* Save a copy of the iterator pointing at the call.  The iterator
+        may change to point past the call in try_substitute_return_value
+        but the original value is needed in try_simplify_call.  */
+      gimple_stmt_iterator gsi_call = *gsi;
 
-  return false;
+      if (flag_printf_return_value
+         && (!flag_rounding_math || !res.floating))
+       call_removed = try_substitute_return_value (gsi, info, res);
+
+      if (!call_removed)
+       try_simplify_call (&gsi_call, info, res);
+    }
+
+  return call_removed;
 }
 
 /* Execute the pass for function FUN.  */
index 8def0f59106a25e498bdea4d07e7c0ac4eec34aa..ac54d650e35fec038e5cca046e3a17fa861157cc 100644 (file)
@@ -1,3 +1,12 @@
+2017-05-14  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/77671
+       * gcc.dg/tree-ssa/builtin-sprintf-7.c: New test.
+       * gcc.dg/tree-ssa/builtin-sprintf-8.c: New test.
+       * gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Adjust.
+       * gcc.dg/tree-ssa/builtin-sprintf-warn-2.c: Adjust.
+       * gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: Adjust.
+
 2017-05-14  Martin Sebor  <msebor@redhat.com>
 
        PR middle-end/80669
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-7.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-7.c
new file mode 100644 (file)
index 0000000..29954aa
--- /dev/null
@@ -0,0 +1,99 @@
+/* PR tree-optimization/77671 - missing -Wformat-overflow warning
+   on sprintf overflow with "%s"
+   { dg-compile }
+   { dg-options "-O2 -Wformat -Wno-format-zero-length -fdump-tree-optimized" } */
+
+void sink (char*);
+
+extern char buffer[];
+
+/* String exactly 4100 characters long (plus the terminating NUL).  */
+extern const char s4100[4101];
+
+void test_sprintf (const char *s)
+{
+#define IGN(...) __builtin_sprintf (buffer, __VA_ARGS__); sink (buffer)
+
+  /* Each of the following calls is expected to be transformed into
+     one of memcpy or strcpy.  */
+  IGN ("");
+  IGN ("a");
+  IGN ("ab");
+  /* FIXME: Transform to strcpy/memcpy.  */
+  /* IGN (s4100 + 5); */
+
+  IGN ("%s", "");
+  IGN ("%s", "a");
+  IGN ("%s", "ab");
+
+  IGN ("%s", s4100 + 5);
+
+  /* FIXME: This can be transformed into strcpy.  */
+  /* IGN (s); */
+  IGN ("%s", s);
+}
+
+
+void test_snprintf (void)
+{
+#undef IGN
+#define IGN(N, ...) __builtin_snprintf (buffer, N, __VA_ARGS__); sink (buffer)
+
+  /* Each of the following calls is expected to be transformed into
+     one of memcpy or strcpy.  */
+  IGN (1, "");
+  IGN (2, "1");
+  IGN (8, "1234567");
+
+  /* FIXME: Transform to strcpy/memcpy.  */
+  /* IGN (4096, s4100 + 5); */
+
+  IGN (1, "%s", "");
+  IGN (2, "%s", "1");
+  IGN (8, "%s", "1234567");
+
+  IGN (4096, "%s", s4100 + 5);
+}
+
+#if 0   /* FIXME: Implement vs{,n}printf optimization.  */
+
+void test_vsprintf (__builtin_va_list va)
+{
+#undef IGN
+#define IGN(fmt) __builtin_vsprintf (buffer, fmt, va); sink (buffer)
+
+  /* Each of the following calls is expected to be transformed into
+     one of memcpy or strcpy.  */
+  IGN ("");
+  IGN ("a");
+  IGN ("ab");
+  IGN (s4100 + 5);
+
+  IGN ("%s");
+}
+
+void test_vsnprintf (__builtin_va_list va)
+{
+#undef IGN
+#define IGN(N, fmt) __builtin_vsnprintf (buffer, N, fmt, va); sink (buffer)
+
+  /* Each of the following calls is expected to be transformed into
+     one of memcpy or strcpy.  */
+  IGN (   1, "");
+  IGN (   2, "1");
+  IGN (   8, "1234567");
+  IGN (4096, s4100 + 5);
+}
+
+#endif
+
+/* { dg-final { scan-tree-dump-not "builtin_sprintf" "optimized" } }
+   { dg-final { scan-tree-dump-not "builtin_snprintf" "optimized" } }
+   { dg-final { scan-tree-dump-not "builtin_vsprintf" "optimized" } }
+   { dg-final { scan-tree-dump-not "builtin_vsnprintf" "optimized" } } */
+
+#define S10    "0123456789"
+#define S100   S10 S10 S10 S10 S10  S10 S10 S10 S10 S10
+#define S1000  S100 S100 S100 S100 S100  S100 S100 S100 S100 S100
+
+const char s4100[4101] = S1000 S1000 S1000 S1000 S100;
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-8.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-8.c
new file mode 100644 (file)
index 0000000..2d38d12
--- /dev/null
@@ -0,0 +1,104 @@
+/* PR tree-optimization/77671 - missing -Wformat-overflow warning
+   on sprintf overflow with "%s"
+
+   Negative test verifying that sprintf family calls that must not
+   be transformed into calls to other functions (such as memcpy)
+   are preserved.
+
+   { dg-compile }
+   { dg-options "-O2 -Wformat -Wno-format-truncation -Wno-format-zero-length -fdump-tree-optimized" } */
+
+void sink (char*, ...);
+
+extern char buffer[];
+
+/* String exactly 4100 characters long (plus the terminating NUL).  */
+extern const char s4100[4101];
+
+void test_sprintf (const char *s)
+{
+  /* Macros to test the function call while eignoring and using
+     the return value, respectively.  */
+#define IGN(...) __builtin_sprintf (buffer, __VA_ARGS__), sink (buffer)
+#define USE(...) sink (buffer, __builtin_sprintf (buffer, __VA_ARGS__))
+
+  /* All of the following calls to sprintf must be emitted (and not
+     transformed into memcpy, strcpy, or similar).  */
+
+  /* Verify that a sprintf call with output in excess of the maximum
+     of 4095 bytes is not transformed into memcpy/strcpy when its
+     return value is used (the call may fail with EOVERFLOW but
+     the error is only detectable when the function's negative return
+     value indicates errno is valid ).  */
+  USE (s4100);
+
+  USE ("%s", s4100);
+
+  /* Same as above but with string of unknown length (which could
+     be in excess of 4K long).  */
+  USE (s);
+  USE ("%s", s);
+}
+
+
+void test_snprintf (void)
+{
+#undef IGN
+#define IGN(N, ...) __builtin_snprintf (buffer, N, __VA_ARGS__); sink (buffer)
+
+  /* All of the following calls to sprintf must be emitted (and not
+     transformed into memcpy, strcpy, or similar).  */
+
+  /* Truncated output could be optimized into strncpy (not done yet).  */
+  IGN (1, "123");
+  IGN (1, s4100);
+
+  IGN (1, "%s", "123");
+  IGN (1, "%s", s4100);
+
+  /* Output in excess of the maximum of 4095 bytes.  */
+  IGN (4097, s4100);
+
+  IGN (4097, "%s", s4100);
+}
+
+
+void test_vsprintf (__builtin_va_list va)
+{
+#undef IGN
+#define IGN(fmt) __builtin_vsprintf (buffer, fmt, va); sink (buffer)
+
+  /* All of the following calls to vsprintf must be emitted (and not
+     transformed into memcpy, strcpy, or similar).  */
+
+  /* Output in excess of the maximum of 4095 bytes.  */
+  IGN (s4100);
+}
+
+
+void test_vsnprintf (__builtin_va_list va)
+{
+#undef IGN
+#define IGN(N, fmt) __builtin_vsnprintf (buffer, N, fmt, va); sink (buffer)
+
+  /* All of the following calls to vsnprintf must be emitted (and not
+     transformed into memcpy, strcpy, or similar).  */
+
+  /* Truncated output could be optimized into strncpy (not done yet).  */
+  IGN (1, "123");
+  IGN (1, s4100);
+
+  /* Output in excess of the maximum of 4095 bytes.  */
+  IGN (4097, s4100);
+}
+
+/* { dg-final { scan-tree-dump-times "builtin_sprintf" 4 "optimized" } }
+   { dg-final { scan-tree-dump-times "builtin_snprintf" 6 "optimized" } }
+   { dg-final { scan-tree-dump-times "builtin_vsprintf" 1 "optimized" } }
+   { dg-final { scan-tree-dump-times "builtin_vsnprintf" 3 "optimized" } } */
+
+#define S10    "0123456789"
+#define S100   S10 S10 S10 S10 S10  S10 S10 S10 S10 S10
+#define S1000  S100 S100 S100 S100 S100  S100 S100 S100 S100 S100
+
+const char s4100[4101] = S1000 S1000 S1000 S1000 S100;
index c4dbf3e9305ffd8107828e9d0ddc8ea44b7d563f..73e927fcc95c4aa2803020296c59b88b29a493f7 100644 (file)
@@ -1068,7 +1068,7 @@ void test_sprintf_chk_e_const (void)
 void test_sprintf_chk_s_nonconst (int w, int p, const char *s)
 {
   T (-1, "%s",   s);
-  T ( 0, "%s",   s);            /* { dg-warning "writing a terminating nul" } */
+  T ( 0, "%-s",  s);            /* { dg-warning "writing a terminating nul" } */
   T ( 1, "%s",   s);
   T (-1, "%.0s", s);
   T ( 1, "%.0s", s);
index f21dae4426a33bc762e4620ad515c41023d52505..60695b88e47b2d34d120d28ee6afe7732c573da7 100644 (file)
@@ -94,8 +94,8 @@ struct Arrays {
 void test_s_nonconst (int w, int p, const char *s, const wchar_t *ws,
                      struct Arrays *a)
 {
-  T (0, "%s",   s);             /* { dg-warning "into a region" "sprintf transformed into strcpy" { xfail *-*-* } } */
-  T (1, "%s",   s);             /* { dg-warning "nul past the end" "sprintf transformed into strcpy" { xfail *-*-* } } */
+  T (0, "%s",   s);             /* { dg-warning "into a region" } */
+  T (1, "%s",   s);             /* { dg-warning "nul past the end" } */
   T (1, "%1s",  s);             /* { dg-warning "writing a terminating nul" } */
   T (1, "%.0s", s);
   T (1, "%.1s", s);             /* { dg-warning "may write a terminating nul" } */
@@ -112,30 +112,27 @@ void test_s_nonconst (int w, int p, const char *s, const wchar_t *ws,
   T (1, "%.1ls",  ws);          /* { dg-warning "may write a terminating nul" } */
   T (1, "%ls",    ws);          /* { dg-warning "may write a terminating nul" } */
 
-  /* Verify that the size of the array is used in lieu of its length.
-     The minus sign disables GCC's sprintf to strcpy transformation.
-     In the case below, the length of s->a1 can be at most zero, so
-     the call should not be diagnosed.  */
-  T (1, "%-s", a->a1);
+  /* Verify that the size of the array is used in lieu of its length.  */
+  T (1, "%s", a->a1);
 
   /* In the following test, since the length of the strings isn't known,
      their type (the array) is used to bound the maximum length to 1,
-     which means the "%-s" directive would not overflow the buffer,
+     which means the "%s" directive would not overflow the buffer,
      but it would leave no room for the terminating nul.  */
-  T (1, "%-s", a->a2);          /* { dg-warning "may write a terminating nul" } */
+  T (1, "%s", a->a2);           /* { dg-warning "may write a terminating nul" } */
 
   /* Unlike in the test above, since the length of the string is bounded
-     by the array type to at most 2, the "^-s" directive is diagnosed firts,
+     by the array type to at most 2, the "%s" directive is diagnosed firts,
      preventing the diagnostic about the terminatinb nul.  */
-  T (1, "%-s", a->a3);          /* { dg-warning "directive writing up to 2 bytes" } */
+  T (1, "%s", a->a3);           /* { dg-warning "directive writing up to 2 bytes" } */
 
   /* The length of a zero length array and flexible array member is
      unknown and at leve 2 assumed to be at least 1.  */
-  T (1, "%-s", a->a0);          /* { dg-warning "may write a terminating nul" } */
-  T (1, "%-s", a->ax);          /* { dg-warning "may write a terminating nul" } */
+  T (1, "%s", a->a0);           /* { dg-warning "may write a terminating nul" } */
+  T (1, "%s", a->ax);           /* { dg-warning "may write a terminating nul" } */
 
-  T (2, "%-s", a->a0);
-  T (2, "%-s", a->ax);
+  T (2, "%s", a->a0);
+  T (2, "%s", a->ax);
 }
 
   /* Exercise buffer overflow detection with non-const integer arguments.  */
index 9db7ad74f370bd86397c135d169d85ed6ba219db..7cbcbdbe54c882c1e133bd976a7edbdb8e268eb8 100644 (file)
@@ -57,10 +57,10 @@ void test_sprintf_chk_string (const char *s, const char *t)
 {
 #define x x ()
 
-  T (1, "%s", x ? "" : "1");       /* { dg-warning "nul past the end" } */
-  T (1, "%s", x ? "1" : "");       /* { dg-warning "nul past the end" } */
-  T (1, "%s", x ? s : "1");        /* { dg-warning "nul past the end" } */
-  T (1, "%s", x ? "1" : s);        /* { dg-warning "nul past the end" } */
+  T (1, "%-s", x ? "" : "1");       /* { dg-warning "nul past the end" } */
+  T (1, "%-s", x ? "1" : "");       /* { dg-warning "nul past the end" } */
+  T (1, "%-s", x ? s : "1");        /* { dg-warning "nul past the end" } */
+  T (1, "%-s", x ? "1" : s);        /* { dg-warning "nul past the end" } */
 
   /* When neither string is known no warning should be issued at level 1
      since their lenghts are assumed to be zero.  */