PR c/53562 - Add -Werror= support for -D_FORTIFY_SOURCE / __builtin___memcpy_chk
authorMartin Sebor <msebor@redhat.com>
Thu, 8 Dec 2016 00:01:33 +0000 (00:01 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Thu, 8 Dec 2016 00:01:33 +0000 (17:01 -0700)
PR c/53562 - Add -Werror= support for -D_FORTIFY_SOURCE / __builtin___memcpy_chk
PR middle-end/77784 - duplicate warning for snprintf when n > object size
PR middle-end/78149 - missing warning on strncpy buffer overflow due to an excessive bound
PR middle-end/78138 - missing warnings on buffer overflow with non-constant source length

gcc/c-family/ChangeLog:

PR c/53562
PR middle-end/77784
PR middle-end/78149
PR middle-end/78138
* c.opt (-Wstringop-overflow): New option.

gcc/ChangeLog:

PR middle-end/77784
PR middle-end/78149
PR middle-end/78138

* builtins.c (expand_builtin_strcat, expand_builtin_strncat): New
functions.
(compute_dest_size, get_size_range, check_sizes, check_strncat_sizes)
(check_memop_sizes): Same.
(expand_builtin_memcpy): Call check memop_sizes.
(expand_builtin_mempcpy): Same.
(expand_builtin_memset): Same,
(expand_builtin_bzero): Same.
(expand_builtin_memory_chk): Call check_sizes.
(expand_builtin_strcpy): Same.
(expand_builtin_strncpy): Same.
(maybe_emit_sprintf_chk_warning): Same.
(expand_builtin): Handle strcat and strncat.
(fini_object_sizes): Reset pointers.
(compute_object_size): New function.
* gimple-ssa-sprintf.c (pass_sprintf_length::handle_gimple_call):
Avoid issuing warnings also issued during built-in expansion.
* doc/invoke.texi (Warning Options): Document -Wstringop-overflow.

gcc/testsuite/ChangeLog:

PR middle-end/77784
PR middle-end/78149
PR middle-end/78138

* c-c++-common/Wsizeof-pointer-memaccess2.c: Adjust expected diagnostic.
* g++.dg/ext/builtin-object-size3.C (bar): Same.
* g++.dg/ext/strncpy-chk1.C: Same.
* g++.dg/opt/memcpy1.C: Same.
* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
* gcc.c-torture/compile/pr55569.c: Disable -Wstringop-overflow.
* gcc.dg/Wobjsize-1.c: Adjust expected diagnostic.
* gcc.dg/attr-alloc_size.c: Same.
* gcc.dg/builtin-stringop-chk-1.c: Adjust expected diagnostic.
* gcc.dg/builtin-stringop-chk-2.c: Same.
* gcc.dg/builtin-stringop-chk-4.c: New test.
* gcc.dg/builtin-strncat-chk-1.c: Adjust expected diagnostic.
* gcc.dg/memcpy-2.c: Same.
* gcc.dg/pr40340-1.c: Same.
* gcc.dg/pr40340-2.c (main): Same.
* gcc.dg/pr40340-5.c (main): Same.
* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Same.
* gcc.dg/torture/pr71132.c: Disable -Wstringop-overflow.
* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Adjust text of expected
warning.
* gfortran.dg/char_length_3.f90: Prune expected warnings.
* gfortran.dg/pr38868.f: Add expected warnings.

From-SVN: r243419

33 files changed:
gcc/ChangeLog
gcc/builtins.c
gcc/c-family/ChangeLog
gcc/c-family/c.opt
gcc/doc/invoke.texi
gcc/gimple-ssa-sprintf.c
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
gcc/testsuite/g++.dg/ext/builtin-object-size3.C
gcc/testsuite/g++.dg/ext/strncpy-chk1.C
gcc/testsuite/g++.dg/opt/memcpy1.C
gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
gcc/testsuite/gcc.c-torture/compile/pr55569.c
gcc/testsuite/gcc.dg/Wobjsize-1.c
gcc/testsuite/gcc.dg/attr-alloc_size.c
gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c
gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-stringop-chk-6.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c
gcc/testsuite/gcc.dg/fstack-protector-strong.c
gcc/testsuite/gcc.dg/memcpy-2.c
gcc/testsuite/gcc.dg/pr40340-1.c
gcc/testsuite/gcc.dg/pr40340-2.c
gcc/testsuite/gcc.dg/pr40340-5.c
gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
gcc/testsuite/gcc.dg/torture/pr71132.c
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-3.c
gcc/testsuite/gfortran.dg/char_length_3.f90
gcc/testsuite/gfortran.dg/pr38868.f

index 79d5d6de19b0475e4a653cc5675f439454e99e83..6372ff43f190c96c2eea25813665672e491e4df6 100644 (file)
@@ -1,3 +1,28 @@
+2016-12-07  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/77784
+       PR middle-end/78149
+       PR middle-end/78138
+       
+       * builtins.c (expand_builtin_strcat, expand_builtin_strncat): New
+       functions.
+       (compute_dest_size, get_size_range, check_sizes, check_strncat_sizes)
+       (check_memop_sizes): Same.
+       (expand_builtin_memcpy): Call check memop_sizes.
+       (expand_builtin_mempcpy): Same.
+       (expand_builtin_memset): Same,
+       (expand_builtin_bzero): Same.
+       (expand_builtin_memory_chk): Call check_sizes.
+       (expand_builtin_strcpy): Same.
+       (expand_builtin_strncpy): Same.
+       (maybe_emit_sprintf_chk_warning): Same.
+       (expand_builtin): Handle strcat and strncat.
+       (fini_object_sizes): Reset pointers.
+       (compute_object_size): New function.
+       * gimple-ssa-sprintf.c (pass_sprintf_length::handle_gimple_call):
+       Avoid issuing warnings also issued during built-in expansion.
+       * doc/invoke.texi (Warning Options): Document -Wstringop-overflow.
+
 2016-12-07  Michael Meissner  <meissner@linux.vnet.ibm.com>
 
        PR target/72717
index 58ed469fc63c7925db9587ef34035ba2a3ff6ecb..b58056cf334467cac0512f62d20bf4ffb0671f30 100644 (file)
@@ -67,7 +67,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "internal-fn.h"
 #include "case-cfn-macros.h"
 #include "gimple-fold.h"
-
+#include "intl.h"
 
 struct target_builtins default_target_builtins;
 #if SWITCHABLE_TARGET
@@ -125,9 +125,11 @@ static rtx expand_builtin_mempcpy (tree, rtx, machine_mode);
 static rtx expand_builtin_mempcpy_with_bounds (tree, rtx, machine_mode);
 static rtx expand_builtin_mempcpy_args (tree, tree, tree, rtx,
                                        machine_mode, int, tree);
+static rtx expand_builtin_strcat (tree, rtx);
 static rtx expand_builtin_strcpy (tree, rtx);
 static rtx expand_builtin_strcpy_args (tree, tree, rtx);
 static rtx expand_builtin_stpcpy (tree, rtx, machine_mode);
+static rtx expand_builtin_strncat (tree, rtx);
 static rtx expand_builtin_strncpy (tree, rtx);
 static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, machine_mode);
 static rtx expand_builtin_memset (tree, rtx, machine_mode);
@@ -3010,6 +3012,292 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
   return dest_addr;
 }
 
+/* Fill the 2-element RANGE array with the minimum and maximum values
+   EXP is known to have and return true, otherwise null and return
+   false.  */
+
+static bool
+get_size_range (tree exp, tree range[2])
+{
+  if (tree_fits_uhwi_p (exp))
+    {
+      range[0] = range[1] = exp;
+      return true;
+    }
+
+  if (TREE_CODE (exp) == SSA_NAME)
+    {
+      wide_int min, max;
+      enum value_range_type range_type = get_range_info (exp, &min, &max);
+
+      if (range_type == VR_RANGE)
+       {
+         /* Interpret the bound in the variable's type.  */
+         range[0] = wide_int_to_tree (TREE_TYPE (exp), min);
+         range[1] = wide_int_to_tree (TREE_TYPE (exp), max);
+         return true;
+       }
+      else if (range_type == VR_ANTI_RANGE)
+       {
+         /* FIXME: Handle anti-ranges.  */
+       }
+    }
+
+  range[0] = NULL_TREE;
+  range[1] = NULL_TREE;
+  return false;
+}
+
+/* Try to verify that the sizes and lengths of the arguments to a string
+   manipulation function given by EXP are within valid bounds and that
+   the operation does not lead to buffer overflow.  Arguments other than
+   EXP may be null.  When non-null, the arguments have the following
+   meaning:
+   SIZE is the user-supplied size argument to the function (such as in
+   memcpy(d, s, SIZE) or strncpy(d, s, SIZE).  It specifies the exact
+   number of bytes to write.
+   MAXLEN is the user-supplied bound on the length of the source sequence
+   (such as in strncat(d, s, N).  It specifies the upper limit on the number
+   of bytes to write.
+   STR is the source string (such as in strcpy(d, s)) when the epxression
+   EXP is a string function call (as opposed to a memory call like memcpy).
+   As an exception, STR can also be an integer denoting the precomputed
+   length of the source string.
+   OBJSIZE is the size of the destination object specified by the last
+   argument to the _chk builtins, typically resulting from the expansion
+   of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
+   OBJSIZE).
+
+   When SIZE is null LEN is checked to verify that it doesn't exceed
+   SIZE_MAX.
+
+   If the call is successfully verified as safe from buffer overflow
+   the function returns true, otherwise false..  */
+
+static bool
+check_sizes (int opt, tree exp, tree size, tree maxlen, tree str, tree objsize)
+{
+  /* The size of the largest object is half the address space, or
+     SSIZE_MAX.  (This is way too permissive.)  */
+  tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
+
+  tree slen = NULL_TREE;
+
+  /* Set to true when the exact number of bytes written by a string
+     function like strcpy is not known and the only thing that is
+     known is that it must be at least one (for the terminating nul).  */
+  bool at_least_one = false;
+  if (str)
+    {
+      /* STR is normally a pointer to string but as a special case
+        it can be an integer denoting the length of a string.  */
+      if (TREE_CODE (TREE_TYPE (str)) == POINTER_TYPE)
+       {
+         /* Try to determine the range of lengths the source string
+            refers to.  If it can be determined add one to it for
+            the terminating nul.  Otherwise, set it to one for
+            the same reason.  */
+         tree lenrange[2];
+         get_range_strlen (str, lenrange);
+         if (lenrange[0])
+           slen = fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+                               size_one_node);
+         else
+           {
+             at_least_one = true;
+             slen = size_one_node;
+           }
+       }
+      else
+       slen = str;
+    }
+
+  if (!size && !maxlen)
+    {
+      /* When the only available piece of data is the object size
+        there is nothing to do.  */
+      if (!slen)
+       return true;
+
+      /* Otherwise, when the length of the source sequence is known
+        (as with with strlen), set SIZE to it.  */
+      size = slen;
+    }
+
+  if (!objsize)
+    objsize = maxobjsize;
+
+  /* The SIZE is exact if it's non-null, constant, and in range of
+     unsigned HOST_WIDE_INT.  */
+  bool exactsize = size && tree_fits_uhwi_p (size);
+
+  tree range[2] = { NULL_TREE, NULL_TREE };
+  if (size)
+    get_size_range (size, range);
+
+  /* First check the number of bytes to be written against the maximum
+     object size.  */
+  if (range[0] && tree_int_cst_lt (maxobjsize, range[0]))
+    {
+      location_t loc = tree_nonartificial_location (exp);
+
+      if (range[0] == range[1])
+       warning_at (loc, opt,
+                   "%K%qD: specified size %wu "
+                   "exceeds maximum object size %wu",
+                   exp, get_callee_fndecl (exp),
+                   tree_to_uhwi (range[0]),
+                   tree_to_uhwi (maxobjsize));
+         else
+           warning_at (loc, opt,
+                       "%K%qD: specified size between %wu and %wu "
+                       "exceeds maximum object size %wu",
+                       exp, get_callee_fndecl (exp),
+                       tree_to_uhwi (range[0]),
+                       tree_to_uhwi (range[1]),
+                       tree_to_uhwi (maxobjsize));
+      return false;
+    }
+
+  /* Next check the number of bytes to be written against the destination
+     object size.  */
+  if (range[0] || !exactsize || integer_all_onesp (size))
+    {
+      if (range[0]
+         && ((tree_fits_uhwi_p (objsize)
+              && tree_int_cst_lt (objsize, range[0]))
+             || (tree_fits_uhwi_p (size)
+                 && tree_int_cst_lt (size, range[0]))))
+       {
+         unsigned HOST_WIDE_INT uwir0 = tree_to_uhwi (range[0]);
+
+         location_t loc = tree_nonartificial_location (exp);
+
+         if (at_least_one)
+           warning_at (loc, opt,
+                       "%K%qD: writing at least %wu byte into a region "
+                       "of size %wu overflows the destination",
+                       exp, get_callee_fndecl (exp), uwir0,
+                       tree_to_uhwi (objsize));
+         else if (range[0] == range[1])
+           warning_at (loc, opt,
+                       (uwir0 == 1
+                        ? G_("%K%qD: writing %wu byte into a region "
+                             "of size %wu overflows the destination")
+                        : G_("%K%qD writing %wu bytes into a region "
+                             "of size %wu overflows the destination")),
+                       exp, get_callee_fndecl (exp), uwir0,
+                       tree_to_uhwi (objsize));
+         else
+           warning_at (loc, opt,
+                       "%K%qD: writing between %wu and %wu bytes "
+                       "into a region of size %wu overflows "
+                       "the destination",
+                       exp, get_callee_fndecl (exp), uwir0,
+                       tree_to_uhwi (range[1]), tree_to_uhwi (objsize));
+
+         /* Return error when an overflow has been detected.  */
+         return false;
+       }
+    }
+
+  /* Check the maximum length of the source sequence against the size
+     of the destination object if known, or against the maximum size
+     of an object.  */
+  if (maxlen)
+    {
+      get_size_range (maxlen, range);
+
+      if (range[0] && objsize && tree_fits_uhwi_p (objsize))
+       {
+         location_t loc = tree_nonartificial_location (exp);
+
+         if (tree_int_cst_lt (maxobjsize, range[0]))
+           {
+             /* Warn about crazy big sizes first since that's more
+                likely to be meaningful than saying that the bound
+                is greater than the object size if both are big.  */
+             if (range[0] == range[1])
+               warning_at (loc, opt,
+                           "%K%qD: specified bound %wu "
+                           "exceeds maximum object size %wu",
+                           exp, get_callee_fndecl (exp),
+                           tree_to_uhwi (range[0]),
+                           tree_to_uhwi (maxobjsize));
+             else
+               warning_at (loc, opt,
+                           "%K%qD: specified bound between %wu and %wu "
+                           " exceeds maximum object size %wu",
+                           exp, get_callee_fndecl (exp),
+                           tree_to_uhwi (range[0]),
+                           tree_to_uhwi (range[1]),
+                           tree_to_uhwi (maxobjsize));
+
+             return false;
+           }
+
+         if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
+           {
+             if (range[0] == range[1])
+               warning_at (loc, opt,
+                           "%K%qD: specified bound %wu "
+                           "exceeds the size %wu of the destination",
+                           exp, get_callee_fndecl (exp),
+                           tree_to_uhwi (range[0]),
+                           tree_to_uhwi (objsize));
+             else
+               warning_at (loc, opt,
+                           "%K%qD: specified bound between %wu and %wu "
+                           " exceeds the size %wu of the destination",
+                           exp, get_callee_fndecl (exp),
+                           tree_to_uhwi (range[0]),
+                           tree_to_uhwi (range[1]),
+                           tree_to_uhwi (objsize));
+             return false;
+           }
+       }
+    }
+
+  return true;
+}
+
+/* Helper to compute the size of the object referenced by the DEST
+   expression which must of of pointer type, using Object Size type
+   OSTYPE (only the least significant 2 bits are used).  Return
+   the size of the object if successful or NULL when the size cannot
+   be determined.  */
+
+static inline tree
+compute_dest_size (tree dest, int ostype)
+{
+  unsigned HOST_WIDE_INT size;
+  if (compute_builtin_object_size (dest, ostype & 3, &size))
+    return build_int_cst (sizetype, size);
+
+  return NULL_TREE;
+}
+
+/* Helper to determine and check the sizes of the source and the destination
+   of calls to __builtin_{bzero,memcpy,memset} calls.  Use Object Size type-0
+   regardless of the OPT_Wstringop_overflow_ setting.  Returns true on success
+   (no overflow or invalid sizes), false otherwise.  */
+
+static bool
+check_memop_sizes (tree exp, tree dest, tree size)
+{
+  if (!warn_stringop_overflow)
+    return true;
+
+  /* For functions like memset and memcpy that operate on raw memory
+     try to determine the size of the largest destination object using
+     type-0 Object Size regardless of the object size type specified
+     by the option.  */
+  tree objsize = compute_dest_size (dest, 0);
+
+  return check_sizes (OPT_Wstringop_overflow_, exp,
+                     size, /*maxlen=*/NULL_TREE, /*str=*/NULL_TREE, objsize);
+}
+
 /* Expand a call EXP to the memcpy builtin.
    Return NULL_RTX if we failed, the caller should emit a normal call,
    otherwise try to get the result in TARGET, if convenient (and in
@@ -3021,13 +3309,14 @@ expand_builtin_memcpy (tree exp, rtx target)
   if (!validate_arglist (exp,
                         POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
-  else
-    {
-      tree dest = CALL_EXPR_ARG (exp, 0);
-      tree src = CALL_EXPR_ARG (exp, 1);
-      tree len = CALL_EXPR_ARG (exp, 2);
-      return expand_builtin_memcpy_args (dest, src, len, target, exp);
-    }
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+
+  check_memop_sizes (exp, dest, len);
+
+  return expand_builtin_memcpy_args (dest, src, len, target, exp);
 }
 
 /* Expand an instrumented call EXP to the memcpy builtin.
@@ -3075,15 +3364,20 @@ expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode)
   if (!validate_arglist (exp,
                         POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
-  else
-    {
-      tree dest = CALL_EXPR_ARG (exp, 0);
-      tree src = CALL_EXPR_ARG (exp, 1);
-      tree len = CALL_EXPR_ARG (exp, 2);
-      return expand_builtin_mempcpy_args (dest, src, len,
-                                         target, mode, /*endp=*/ 1,
-                                         exp);
-    }
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+
+  /* Avoid expanding mempcpy into memcpy when the call is determined
+     to overflow the buffer.  This also prevents the same overflow
+     from being diagnosed again when expanding memcpy.  */
+  if (!check_memop_sizes (exp, dest, len))
+    return NULL_RTX;
+
+  return expand_builtin_mempcpy_args (dest, src, len,
+                                     target, mode, /*endp=*/ 1,
+                                     exp);
 }
 
 /* Expand an instrumented call EXP to the mempcpy builtin.
@@ -3255,6 +3549,33 @@ expand_movstr (tree dest, tree src, rtx target, int endp)
   return target;
 }
 
+/* Do some very basic size validation of a call to the strcpy builtin
+   given by EXP.  Return NULL_RTX to have the built-in expand to a call
+   to the library function.  */
+
+static rtx
+expand_builtin_strcat (tree exp, rtx)
+{
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
+      || !warn_stringop_overflow)
+    return NULL_RTX;
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+
+  /* There is no way here to determine the length of the string in
+     the destination to which the SRC string is being appended so
+     just diagnose cases when the souce string is longer than
+     the destination object.  */
+
+  tree destsize = compute_dest_size (dest, warn_stringop_overflow - 1);
+
+  check_sizes (OPT_Wstringop_overflow_,
+              exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+
+  return NULL_RTX;
+}
+
 /* Expand expression EXP, which is a call to the strcpy builtin.  Return
    NULL_RTX if we failed the caller should emit a normal call, otherwise
    try to get the result in TARGET, if convenient (and in mode MODE if that's
@@ -3263,13 +3584,20 @@ expand_movstr (tree dest, tree src, rtx target, int endp)
 static rtx
 expand_builtin_strcpy (tree exp, rtx target)
 {
-  if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
-   {
-     tree dest = CALL_EXPR_ARG (exp, 0);
-     tree src = CALL_EXPR_ARG (exp, 1);
-     return expand_builtin_strcpy_args (dest, src, target);
-   }
-   return NULL_RTX;
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+
+  if (warn_stringop_overflow)
+    {
+      tree destsize = compute_dest_size (dest, warn_stringop_overflow - 1);
+      check_sizes (OPT_Wstringop_overflow_,
+                  exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+    }
+
+  return expand_builtin_strcpy_args (dest, src, target);
 }
 
 /* Helper function to do the actual work for expand_builtin_strcpy.  The
@@ -3377,6 +3705,131 @@ builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset,
   return c_readstr (str + offset, mode);
 }
 
+/* Helper to check the sizes of sequences and the destination of calls
+   to __builtin_strncat and __builtin___strncat_chk.  Returns true on
+   success (no overflow or invalid sizes), false otherwise.  */
+
+static bool
+check_strncat_sizes (tree exp, tree objsize)
+{
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree maxlen = CALL_EXPR_ARG (exp, 2);
+
+  /* Try to determine the range of lengths that the source expression
+     refers to.  */
+  tree lenrange[2];
+  get_range_strlen (src, lenrange);
+
+  /* Try to verify that the destination is big enough for the shortest
+     string.  */
+
+  if (!objsize && warn_stringop_overflow)
+    {
+      /* If it hasn't been provided by __strncat_chk, try to determine
+        the size of the destination object into which the source is
+        being copied.  */
+      objsize = compute_dest_size (dest, warn_stringop_overflow - 1);
+    }
+
+  /* Add one for the terminating nul.  */
+  tree srclen = (lenrange[0]
+                ? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+                               size_one_node)
+                : NULL_TREE);
+
+  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+     nul so the specified upper bound should never be equal to (or greater
+     than) the size of the destination.  */
+  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxlen))
+    {
+      warning_at (EXPR_LOCATION (exp), OPT_Wstringop_overflow_,
+                 "specified bound %wu "
+                 "equals the size of the destination",
+                 tree_to_uhwi (maxlen));
+
+      return false;
+    }
+
+  if (!srclen
+      || (maxlen && tree_fits_uhwi_p (maxlen)
+         && tree_fits_uhwi_p (srclen)
+         && tree_int_cst_lt (maxlen, srclen)))
+    srclen = maxlen;
+
+  /* The number of bytes to write is LEN but check_sizes will also
+     check SRCLEN if LEN's value isn't known.  */
+  return check_sizes (OPT_Wstringop_overflow_,
+                     exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+}
+
+/* Similar to expand_builtin_strcat, do some very basic size validation
+   of a call to the strcpy builtin given by EXP.  Return NULL_RTX to have
+   the built-in expand to a call to the library function.  */
+
+static rtx
+expand_builtin_strncat (tree exp, rtx)
+{
+  if (!validate_arglist (exp,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
+      || !warn_stringop_overflow)
+    return NULL_RTX;
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  /* The upper bound on the number of bytes to write.  */
+  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  /* The length of the source sequence.  */
+  tree slen = c_strlen (src, 1);
+
+  /* Try to determine the range of lengths that the source expression
+     refers to.  */
+  tree lenrange[2];
+  if (slen)
+    lenrange[0] = lenrange[1] = slen;
+  else
+    get_range_strlen (src, lenrange);
+
+  /* Try to verify that the destination is big enough for the shortest
+     string.  First try to determine the size of the destination object
+     into which the source is being copied.  */
+  tree destsize = compute_dest_size (dest, warn_stringop_overflow - 1);
+
+  /* Add one for the terminating nul.  */
+  tree srclen = (lenrange[0]
+                ? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+                               size_one_node)
+                : NULL_TREE);
+
+  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+     nul so the specified upper bound should never be equal to (or greater
+     than) the size of the destination.  */
+  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxlen))
+    {
+      warning_at (EXPR_LOCATION (exp), OPT_Wstringop_overflow_,
+                 "specified bound %wu "
+                 "equals the size of the destination",
+                 tree_to_uhwi (maxlen));
+
+      return NULL_RTX;
+    }
+
+  if (!srclen
+      || (maxlen && tree_fits_uhwi_p (maxlen)
+         && tree_fits_uhwi_p (srclen)
+         && tree_int_cst_lt (maxlen, srclen)))
+    srclen = maxlen;
+
+  /* The number of bytes to write is LEN but check_sizes will also
+     check SRCLEN if LEN's value isn't known.  */
+  check_sizes (OPT_Wstringop_overflow_,
+              exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+
+  return NULL_RTX;
+}
+
 /* Expand expression EXP, which is a call to the strncpy builtin.  Return
    NULL_RTX if we failed the caller should emit a normal call.  */
 
@@ -3390,9 +3843,33 @@ expand_builtin_strncpy (tree exp, rtx target)
     {
       tree dest = CALL_EXPR_ARG (exp, 0);
       tree src = CALL_EXPR_ARG (exp, 1);
+      /* The number of bytes to write (not the maximum).  */
       tree len = CALL_EXPR_ARG (exp, 2);
+      /* The length of the source sequence.  */
       tree slen = c_strlen (src, 1);
 
+      if (warn_stringop_overflow)
+       {
+         /* Try to determine the range of lengths that the source expression
+            refers to.  */
+         tree lenrange[2];
+         if (slen)
+           lenrange[0] = lenrange[1] = slen;
+         else
+           {
+             get_range_strlen (src, lenrange);
+             slen = lenrange[0];
+           }
+
+         tree destsize = compute_dest_size (dest,
+                                            warn_stringop_overflow - 1);
+
+         /* The number of bytes to write is LEN but check_sizes will also
+            check SLEN if LEN's value isn't known.  */
+         check_sizes (OPT_Wstringop_overflow_,
+                      exp, len, /*maxlen=*/NULL_TREE, slen, destsize);
+       }
+
       /* We must be passed a constant len and src parameter.  */
       if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
        return NULL_RTX;
@@ -3480,13 +3957,14 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode)
   if (!validate_arglist (exp,
                         POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
-  else
-    {
-      tree dest = CALL_EXPR_ARG (exp, 0);
-      tree val = CALL_EXPR_ARG (exp, 1);
-      tree len = CALL_EXPR_ARG (exp, 2);
-      return expand_builtin_memset_args (dest, val, len, target, mode, exp);
-    }
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree val = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+
+  check_memop_sizes (exp, dest, len);
+
+  return expand_builtin_memset_args (dest, val, len, target, mode, exp);
 }
 
 /* Expand expression EXP, which is an instrumented call to the memset builtin.
@@ -3667,20 +4145,21 @@ expand_builtin_memset_args (tree dest, tree val, tree len,
 static rtx
 expand_builtin_bzero (tree exp)
 {
-  tree dest, size;
-  location_t loc = EXPR_LOCATION (exp);
-
   if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest = CALL_EXPR_ARG (exp, 0);
-  size = CALL_EXPR_ARG (exp, 1);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree size = CALL_EXPR_ARG (exp, 1);
+
+  check_memop_sizes (exp, dest, size);
 
   /* New argument list transforming bzero(ptr x, int y) to
      memset(ptr x, int 0, size_t y).   This is done this way
      so that if it isn't expanded inline, we fallback to
      calling bzero instead of memset.  */
 
+  location_t loc = EXPR_LOCATION (exp);
+
   return expand_builtin_memset_args (dest, integer_zero_node,
                                     fold_convert_loc (loc,
                                                       size_type_node, size),
@@ -6205,12 +6684,24 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
        return target;
       break;
 
+    case BUILT_IN_STRCAT:
+      target = expand_builtin_strcat (exp, target);
+      if (target)
+       return target;
+      break;
+
     case BUILT_IN_STRCPY:
       target = expand_builtin_strcpy (exp, target);
       if (target)
        return target;
       break;
 
+    case BUILT_IN_STRNCAT:
+      target = expand_builtin_strncat (exp, target);
+      if (target)
+       return target;
+      break;
+
     case BUILT_IN_STRNCPY:
       target = expand_builtin_strncpy (exp, target);
       if (target)
@@ -9052,22 +9543,22 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
   len = CALL_EXPR_ARG (exp, 2);
   size = CALL_EXPR_ARG (exp, 3);
 
-  if (! tree_fits_uhwi_p (size))
+  bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
+                              exp, len, /*maxlen=*/NULL_TREE,
+                              /*str=*/NULL_TREE, size);
+
+  if (!tree_fits_uhwi_p (size))
     return NULL_RTX;
 
   if (tree_fits_uhwi_p (len) || integer_all_onesp (size))
     {
-      tree fn;
-
-      if (! integer_all_onesp (size) && tree_int_cst_lt (size, len))
-       {
-         warning_at (tree_nonartificial_location (exp),
-                     0, "%Kcall to %D will always overflow destination buffer",
-                     exp, get_callee_fndecl (exp));
-         return NULL_RTX;
-       }
+      /* Avoid transforming the checking call to an ordinary one when
+        an overflow has been detected or when the call couldn't be
+        validated because the size is not constant.  */
+      if (!sizes_ok && !integer_all_onesp (size) && tree_int_cst_lt (size, len))
+       return NULL_RTX;
 
-      fn = NULL_TREE;
+      tree fn = NULL_TREE;
       /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
         mem{cpy,pcpy,move,set} is available.  */
       switch (fcode)
@@ -9153,68 +9644,68 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 static void
 maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
 {
-  int is_strlen = 0;
-  tree len, size;
-  location_t loc = tree_nonartificial_location (exp);
+  /* The source string.  */
+  tree srcstr = NULL_TREE;
+  /* The size of the destination object.  */
+  tree objsize = NULL_TREE;
+  /* The string that is being concatenated with (as in __strcat_chk)
+     or null if it isn't.  */
+  tree catstr = NULL_TREE;
+  /* The maximum length of the source sequence in a bounded operation
+     (such as __strncat_chk) or null if the operation isn't bounded
+     (such as __strcat_chk).  */
+  tree maxlen = NULL_TREE;
 
   switch (fcode)
     {
     case BUILT_IN_STRCPY_CHK:
     case BUILT_IN_STPCPY_CHK:
-    /* For __strcat_chk the warning will be emitted only if overflowing
-       by at least strlen (dest) + 1 bytes.  */
+      srcstr = CALL_EXPR_ARG (exp, 1);
+      objsize = CALL_EXPR_ARG (exp, 2);
+      break;
+
     case BUILT_IN_STRCAT_CHK:
-      len = CALL_EXPR_ARG (exp, 1);
-      size = CALL_EXPR_ARG (exp, 2);
-      is_strlen = 1;
+      /* For __strcat_chk the warning will be emitted only if overflowing
+        by at least strlen (dest) + 1 bytes.  */
+      catstr = CALL_EXPR_ARG (exp, 0);
+      srcstr = CALL_EXPR_ARG (exp, 1);
+      objsize = CALL_EXPR_ARG (exp, 2);
       break;
+
     case BUILT_IN_STRNCAT_CHK:
+      catstr = CALL_EXPR_ARG (exp, 0);
+      srcstr = CALL_EXPR_ARG (exp, 1);
+      maxlen = CALL_EXPR_ARG (exp, 2);
+      objsize = CALL_EXPR_ARG (exp, 3);
+      break;
+
     case BUILT_IN_STRNCPY_CHK:
     case BUILT_IN_STPNCPY_CHK:
-      len = CALL_EXPR_ARG (exp, 2);
-      size = CALL_EXPR_ARG (exp, 3);
+      srcstr = CALL_EXPR_ARG (exp, 1);
+      maxlen = CALL_EXPR_ARG (exp, 2);
+      objsize = CALL_EXPR_ARG (exp, 3);
       break;
+
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
-      len = CALL_EXPR_ARG (exp, 1);
-      size = CALL_EXPR_ARG (exp, 3);
+      maxlen = CALL_EXPR_ARG (exp, 1);
+      objsize = CALL_EXPR_ARG (exp, 3);
       break;
     default:
       gcc_unreachable ();
     }
 
-  if (!len || !size)
-    return;
-
-  if (! tree_fits_uhwi_p (size) || integer_all_onesp (size))
-    return;
-
-  if (is_strlen)
+  if (catstr && maxlen)
     {
-      len = c_strlen (len, 1);
-      if (! len || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size))
+      /* Check __strncat_chk.  There is no way to determine the length
+        of the string to which the source string is being appended so
+        just warn when the length of the source string is not known.  */
+      if (!check_strncat_sizes (exp, objsize))
        return;
     }
-  else if (fcode == BUILT_IN_STRNCAT_CHK)
-    {
-      tree src = CALL_EXPR_ARG (exp, 1);
-      if (! src || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size))
-       return;
-      src = c_strlen (src, 1);
-      if (! src || ! tree_fits_uhwi_p (src))
-       {
-         warning_at (loc, 0, "%Kcall to %D might overflow destination buffer",
-                     exp, get_callee_fndecl (exp));
-         return;
-       }
-      else if (tree_int_cst_lt (src, size))
-       return;
-    }
-  else if (! tree_fits_uhwi_p (len) || ! tree_int_cst_lt (size, len))
-    return;
 
-  warning_at (loc, 0, "%Kcall to %D will always overflow destination buffer",
-             exp, get_callee_fndecl (exp));
+  check_sizes (OPT_Wstringop_overflow_, exp,
+              /*size=*/NULL_TREE, maxlen, srcstr, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9268,10 +9759,10 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
   else
     return;
 
-  if (! tree_int_cst_lt (len, size))
-    warning_at (tree_nonartificial_location (exp),
-               0, "%Kcall to %D will always overflow destination buffer",
-               exp, get_callee_fndecl (exp));
+  /* Add one for the terminating nul.  */
+  len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
+  check_sizes (OPT_Wstringop_overflow_,
+              exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
index 58907981b96986bb508d90a7260666aa76be4d65..aba0b14fa2688973dca664ad57a1e2958b9fa179 100644 (file)
@@ -1,3 +1,11 @@
+2016-12-07  Martin Sebor  <msebor@redhat.com>
+
+       PR c/53562
+       PR middle-end/77784
+       PR middle-end/78149
+       PR middle-end/78138
+       * c.opt (-Wstringop-overflow): New option.
+
 2016-12-02  Maxim Ostapenko  <m.ostapenko@samsung.com>
 
        * c-attribs.c (asan odr indicator): New attribute.
index 2d47d54563aea7e0f4c2858644f1fc302234dd67..288e4ce12fbf760e92e6ca4e60270c7c2279ba91 100644 (file)
@@ -684,6 +684,16 @@ Wsizeof-array-argument
 C ObjC C++ ObjC++ Var(warn_sizeof_array_argument) Warning Init(1)
 Warn when sizeof is applied on a parameter declared as an array.
 
+Wstringop-overflow
+C ObjC C++ ObjC++ Warning Alias(Wstringop-overflow=, 2, 0)
+Warn about buffer overflow in string manipulation functions like memcpy
+and strcpy.
+
+Wstringop-overflow=
+C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_stringop_overflow) Init(2) Warning
+Under the control of Object Size type, warn about buffer overflow in string
+manipulation functions like memcpy and strcpy.
+
 Wsuggest-attribute=format
 C ObjC C++ ObjC++ Var(warn_suggest_attribute_format) Warning
 Warn about functions which might be candidates for format attributes.
index 034ae983468f450dbe10d092e5f7986e962a0771..5622c0f107e191c6d70f17d5876443d7a6e109ab 100644 (file)
@@ -304,6 +304,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wsizeof-pointer-memaccess  -Wsizeof-array-argument @gol
 -Wstack-protector -Wstack-usage=@var{len} -Wstrict-aliasing @gol
 -Wstrict-aliasing=n -Wstrict-overflow -Wstrict-overflow=@var{n} @gol
+-Wstringop-overflow=@var{n} @gol
 -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]} @gol
 -Wsuggest-final-types @gol -Wsuggest-final-methods -Wsuggest-override @gol
 -Wmissing-format-attribute -Wsubobject-linkage @gol
@@ -4936,6 +4937,86 @@ comparisons, so this warning level gives a very large number of
 false positives.
 @end table
 
+@item -Wstringop-overflow
+@itemx -Wstringop-overflow=@var{type}
+@opindex Wstringop-overflow
+@opindex Wno-stringop-overflow
+Warn for calls to string manipulation functions such as @code{memcpy} and
+@code{strcpy} that are determined to overflow the destination buffer.  The
+optional argument is one greater than the type of Object Size Checking to
+perform to determine the size of the destination.  @xref{Object Size Checking}.
+The argument is meaningful only for functions that operate on character arrays
+but not for raw memory functions like @code{memcpy} which always make use
+of Object Size type-0.  The option also warns for calls that specify a size
+in excess of the largest possible object or at most @code{SIZE_MAX / 2} bytes.
+The option produces the best results with optimization enabled but can detect
+a small subset of simple buffer overflows even without optimization in
+calls to the GCC built-in functions like @code{__builtin_memcpy} that
+correspond to the standard functions.  In any case, the option warns about
+just a subset of buffer overflows detected by the corresponding overflow
+checking built-ins.  For example, the option will issue a warning for
+the @code{strcpy} call below because it copies at least 5 characters
+(the string @code{"blue"} including the terminating NUL) into the buffer
+of size 4.
+
+@smallexample
+enum Color @{ blue, purple, yellow @};
+const char* f (enum Color clr)
+@{
+  static char buf [4];
+  const char *str;
+  switch (clr)
+    @{
+      case blue: str = "blue"; break;
+      case purple: str = "purple"; break;
+      case yellow: str = "yellow"; break;
+    @}
+
+  return strcpy (buf, str);   // warning here
+@}
+@end smallexample
+
+Option @option{-Wstringop-overflow=2} is enabled by default.
+
+@table @gcctabopt
+@item -Wstringop-overflow
+@item -Wstringop-overflow=1
+@opindex Wstringop-overflow
+@opindex Wno-stringop-overflow
+The @option{-Wstringop-overflow=1} option uses type-zero Object Size Checking
+to determine the sizes of destination objects.  This is the default setting
+of the option.  At this setting the option will not warn for writes past
+the end of subobjects of larger objects accessed by pointers unless the
+size of the largest surrounding object is known.  When the destination may
+be one of several objects it is assumed to be the largest one of them.  On
+Linux systems, when optimization is enabled at this setting the option warns
+for the same code as when the @code{_FORTIFY_SOURCE} macro is defined to
+a non-zero value.
+
+@item -Wstringop-overflow=2
+The @option{-Wstringop-overflow=2} option uses type-one Object Size Checking
+to determine the sizes of destination objects.  At this setting the option
+will warn about overflows when writing to members of the largest complete
+objects whose exact size is known.  It will, however, not warn for excessive
+writes to the same members of unknown objects referenced by pointers since
+they may point to arrays containing unknown numbers of elements.
+
+@item -Wstringop-overflow=3
+The @option{-Wstringop-overflow=3} option uses type-two Object Size Checking
+to determine the sizes of destination objects.  At this setting the option
+warns about overflowing the smallest object or data member.  This is the
+most restrictive setting of the option that may result in warnings for safe
+code.
+
+@item -Wstringop-overflow=4
+The @option{-Wstringop-overflow=4} option uses type-three Object Size Checking
+to determine the sizes of destination objects.  At this setting the option
+will warn about overflowing any data members, and when the destination is
+one of several objects it uses the size of the largest of them to decide
+whether to issue a warning.  Similarly to @option{-Wstringop-overflow=3} this
+setting of the option may result in warnings for benign code.
+@end table
+
 @item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]}
 @opindex Wsuggest-attribute=
 @opindex Wno-suggest-attribute=
index e86c4dc71e1d5ac9b0beadccd00e949873509bdd..8de9a1e31d2c2d44d920d5102171b25ebfc376b1 100644 (file)
@@ -774,7 +774,23 @@ get_width_and_precision (const conversion_spec &spec,
   if (spec.star_width)
     {
       if (TREE_CODE (spec.star_width) == INTEGER_CST)
-       width = abs (tree_to_shwi (spec.star_width));
+       {
+         width = tree_to_shwi (spec.star_width);
+         if (width < 0)
+           {
+             if (width == HOST_WIDE_INT_MIN)
+               {
+                 /* Avoid undefined behavior due to negating a minimum.
+                    This case will be diagnosed since it will result in
+                    more than INT_MAX bytes on output, either by the
+                    directive itself (when INT_MAX < HOST_WIDE_INT_MAX)
+                    or by the format function itself.  */
+                 width = HOST_WIDE_INT_MAX;
+               }
+             else
+               width = -width;
+           }
+       }
       else
        width = HOST_WIDE_INT_MIN;
     }
@@ -1261,9 +1277,9 @@ format_floating (const conversion_spec &spec, int width, int prec)
        res.range.min = 2 + (prec < 0 ? 6 : prec);
 
        /* Compute the maximum just once.  */
-       static const int f_max[] = {
-         format_floating_max (double_type_node, 'f'),
-         format_floating_max (long_double_type_node, 'f')
+       const int f_max[] = {
+         format_floating_max (double_type_node, 'f', prec),
+         format_floating_max (long_double_type_node, 'f', prec)
        };
        res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : f_max [ldbl];
 
@@ -1279,9 +1295,9 @@ format_floating (const conversion_spec &spec, int width, int prec)
        res.range.min = 2 + (prec < 0 ? 6 : prec);
 
        /* Compute the maximum just once.  */
-       static const int g_max[] = {
-         format_floating_max (double_type_node, 'g'),
-         format_floating_max (long_double_type_node, 'g')
+       const int g_max[] = {
+         format_floating_max (double_type_node, 'g', prec),
+         format_floating_max (long_double_type_node, 'g', prec)
        };
        res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : g_max [ldbl];
 
@@ -2743,19 +2759,27 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
        {
          dstsize = tree_to_uhwi (size);
          /* No object can be larger than SIZE_MAX bytes (half the address
-            space) on the target.  This imposes a limit that's one byte
-            less than that.
+            space) on the target.
             The functions are defined only for output of at most INT_MAX
             bytes.  Specifying a bound in excess of that limit effectively
             defeats the bounds checking (and on some implementations such
             as Solaris cause the function to fail with EINVAL).  */
-         if (dstsize >= target_size_max () / 2)
-           warning_at (gimple_location (info.callstmt), OPT_Wformat_length_,
-                       "specified destination size %wu is too large",
-                       dstsize);
+         if (dstsize > target_size_max () / 2)
+           {
+             /* Avoid warning if -Wstringop-overflow is specified since
+                it also warns for the same thing though only for the
+                checking built-ins.  */
+             if ((idx_objsize == HOST_WIDE_INT_M1U
+                  || !warn_stringop_overflow))
+               warning_at (gimple_location (info.callstmt),
+                           OPT_Wformat_length_,
+                           "specified bound %wu exceeds maximum object size "
+                           "%wu",
+                           dstsize, target_size_max () / 2);
+           }
          else if (dstsize > target_int_max ())
            warning_at (gimple_location (info.callstmt), OPT_Wformat_length_,
-                       "specified destination size %wu exceeds %<INT_MAX %>",
+                       "specified bound %wu exceeds %<INT_MAX %>",
                        dstsize);
        }
       else if (TREE_CODE (size) == SSA_NAME)
@@ -2800,10 +2824,15 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
       info.objsize = dstsize < objsize ? dstsize : objsize;
 
       if (info.bounded
-         && dstsize < target_size_max () / 2 && objsize < dstsize)
+         && dstsize < target_size_max () / 2 && objsize < dstsize
+         /* Avoid warning if -Wstringop-overflow is specified since
+            it also warns for the same thing though only for the
+            checking built-ins.  */
+         && (idx_objsize == HOST_WIDE_INT_M1U
+             || !warn_stringop_overflow))
        {
          warning_at (gimple_location (info.callstmt), OPT_Wformat_length_,
-                     "specified size %wu exceeds the size %wu "
+                     "specified bound %wu exceeds the size %wu "
                      "of the destination object", dstsize, objsize);
        }
     }
index 33f1f86a05550f07de0e1e57b47b702d79a8868b..27225c2483759b37d120b305fd1e0a97ae32c991 100644 (file)
@@ -1,3 +1,32 @@
+2016-12-07  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/77784
+       PR middle-end/78149
+       PR middle-end/78138
+
+       * c-c++-common/Wsizeof-pointer-memaccess2.c: Adjust expected diagnostic.
+       * g++.dg/ext/builtin-object-size3.C (bar): Same.
+       * g++.dg/ext/strncpy-chk1.C: Same.
+       * g++.dg/opt/memcpy1.C: Same.
+       * g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
+       * gcc.c-torture/compile/pr55569.c: Disable -Wstringop-overflow.
+       * gcc.dg/Wobjsize-1.c: Adjust expected diagnostic.
+       * gcc.dg/attr-alloc_size.c: Same.
+       * gcc.dg/builtin-stringop-chk-1.c: Adjust expected diagnostic.
+       * gcc.dg/builtin-stringop-chk-2.c: Same.
+       * gcc.dg/builtin-stringop-chk-4.c: New test.
+       * gcc.dg/builtin-strncat-chk-1.c: Adjust expected diagnostic.
+       * gcc.dg/memcpy-2.c: Same.
+       * gcc.dg/pr40340-1.c: Same.
+       * gcc.dg/pr40340-2.c (main): Same.
+       * gcc.dg/pr40340-5.c (main): Same.
+       * gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Same.
+       * gcc.dg/torture/pr71132.c: Disable -Wstringop-overflow.
+       * gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Adjust text of expected
+       warning.
+       * gfortran.dg/char_length_3.f90: Prune expected warnings.
+       * gfortran.dg/pr38868.f: Add expected warnings.
+
 2016-12-07  Michael Meissner  <meissner@linux.vnet.ibm.com>
 
        PR target/72717
index d9ec7e2cace7555a1eb6e3e1402e0cd07e70f88d..9a02373970927125e4def4ad4e483e7df03fa09e 100644 (file)
@@ -481,4 +481,4 @@ f4 (char *x, char **y, int z, char w[64])
   stpncpy (x, s3, sizeof (s3));
 }
 
-/* { dg-prune-output "\[\n\r\]*will always overflow\[\n\r\]*" } */
+/* { dg-prune-output "\[\n\r\]*writing\[\n\r\]*" } */
index 09263e587196d12e70a3a1f723960f9f92267a16..0207f9ab667acffae40068dc51791cf789ad58ea 100644 (file)
@@ -20,7 +20,7 @@ bar ()
 {
   int *p = new int;
   int *q = new int[4];
-  MEMCPY (p, "abcdefghijklmnopqrstuvwxyz", sizeof (int) + 1);          // { dg-warning "will always overflow destination buffer" }
-  MEMCPY (q, "abcdefghijklmnopqrstuvwxyz", 4 * sizeof (int) + 1);      // { dg-warning "will always overflow destination buffer" }
+  MEMCPY (p, "abcdefghijklmnopqrstuvwxyz", sizeof (int) + 1);          // { dg-warning "writing" }
+  MEMCPY (q, "abcdefghijklmnopqrstuvwxyz", 4 * sizeof (int) + 1);      // { dg-warning "writing" }
   baz (p, q);
 }
index ebafc99887bd9cb7a6be6d2e09a8dc0804c281ec..d67d6bfea4536a91118d3d6000377b6c1a08f562 100644 (file)
@@ -9,7 +9,7 @@ struct B { char z[50]; };
 inline void
 foo (char *dest, const char *__restrict src, __SIZE_TYPE__ n)
 {
-  __builtin___strncpy_chk (dest, src, n, __builtin_object_size (dest, 0));     // { dg-warning "will always overflow" }
+  __builtin___strncpy_chk (dest, src, n, __builtin_object_size (dest, 0));     // { dg-warning "specified bound 36 exceeds the size 35 of the destination" }
 }
 
 void bar (const char *, int);
index f2913459973a599e58e89d0f1a0f9afe28dffe22..e2b1dd2cdf0cf88897c652604041968df0af87d4 100644 (file)
@@ -59,7 +59,10 @@ namespace CS
     }
     uint8 Clip ()
     {
-      __builtin_memcpy (this->OutP, InP, OutV * sizeof (csVector2));
+      // OutV is initialized to SIZE_MAX in the ctor above causing
+      // the multiplication below to produce a very large number
+      // in excess of the maximum possible object size (SIZE_MAX/2).
+      __builtin_memcpy (this->OutP, InP, OutV * sizeof (csVector2));   // { dg-warning "specified size \[0-9\]+ exceeds maximum object size" }
     }
   };
 }
index 8b5c33e24b3d7f6072df418bf7b58133dd16f049..2e6189b31476f5e19adba3273b7f9889ddbc3c48 100644 (file)
@@ -713,4 +713,4 @@ f4 (char *x, char **y, int z, char w[64])
   return z;
 }
 
-// { dg-prune-output "\[\n\r\]*will always overflow\[\n\r\]*" }
+// { dg-prune-output "\[\n\r\]*overflows\[\n\r\]*" }
index 0e99568d3f3d26d02281f3527e94ecb98a3b9281..a216f47033353ac7ebaa5d9522aa52792608dc2f 100644 (file)
@@ -1,7 +1,8 @@
 // Test -Wsizeof-pointer-memaccess warnings.
 // { dg-do compile }
-// { dg-options "-Wall -Wno-sizeof-array-argument" }
-// Test just twice, once with -O0 non-fortified, once with -O2 fortified.
+// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" }
+// Test just twice, once with -O0 non-fortified, once with -O2 fortified,
+// suppressing buffer overflow warnings.
 // { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } }
 // { dg-skip-if "" { *-*-* }  { "-flto" } { "" } }
 
index cffbcfc7521825bbc9ae79a171981b2c8762e2d4..cf274cdbb99a44195a19ed23a343479529287fe8 100644 (file)
@@ -1,4 +1,4 @@
-/* { dg-options "-ftree-vectorize" } */
+/* { dg-options "-Wno-stringop-overflow -ftree-vectorize" } */
 int *bar (void);
 
 void
@@ -6,6 +6,10 @@ foo (void)
 {
   long x;
   int *y = bar ();
-    for (x = -1 / sizeof (int); x; --x, ++y)
-       *y = 0;
+
+  /* The loop below may be optimized to a call to memset with a size
+     that's in excess of the maximum object size.  This is diagnosed
+     by the -Wstringop-overflow option.  */
+  for (x = -1 / sizeof (int); x; --x, ++y)
+    *y = 0;
 }
index 291cfb9be91889a220f99e164696f4f84857bedb..211e0680a1d8b87d99cdcb9ab03c8137b5d45085 100644 (file)
@@ -10,6 +10,6 @@ int main(int argc, char **argv)
   return 0;
 }
 
-/* { dg-warning "will always overflow destination buffer" "" { target *-*-* } 6 } */
+/* { dg-warning "writing" "" { target *-*-* } 6 } */
 /* { dg-message "file included" "included" { target *-*-* } 0 } */
 /* { dg-message "inlined from" "inlined" { target *-*-* } 0 } */
index e8129ceae961c6d25468471ab295aaecef31f44d..f50ba7c53db642d24cfdfbf25e90716cbe4581d3 100644 (file)
@@ -22,15 +22,15 @@ test (void)
   strcpy (p, "Hello");
   p = malloc1 (6);
   strcpy (p, "Hello");
-  strcpy (p, "Hello World"); /* { dg-warning "will always overflow" "strcpy" } */
+  strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
   p = malloc2 (__INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 6);
   strcpy (p, "World");
-  strcpy (p, "Hello World"); /* { dg-warning "will always overflow" "strcpy" } */
+  strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
   p = calloc1 (2, 5);
   strcpy (p, "World");
-  strcpy (p, "Hello World"); /* { dg-warning "will always overflow" "strcpy" } */
+  strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
   p = calloc2 (2, __INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 5);
   strcpy (p, "World");
-  strcpy (p, "Hello World"); /* { dg-warning "will always overflow" "strcpy" } */
+  strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
 }
 
index e491ff52680171d70e57046094d895dfecd4a288..7689287cb1e3247bc5390a5b2a70af438b310764 100644 (file)
@@ -8,7 +8,10 @@
 extern void abort (void);
 
 #include "../gcc.c-torture/execute/builtins/chk.h"
-#include <stdarg.h>
+
+#define va_list    __builtin_va_list
+#define va_start   __builtin_va_start
+#define va_end     __builtin_va_end
 
 volatile void *vx;
 char buf1[20];
@@ -22,60 +25,61 @@ test (int arg, ...)
   char *p = &buf1[10], *q;
 
   memcpy (&buf2[19], "ab", 1);
-  memcpy (&buf2[19], "ab", 2); /* { dg-warning "will always overflow" "memcpy" } */
+  memcpy (&buf2[19], "ab", 2); /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy" } */
   vx = mempcpy (&buf2[19], "ab", 1);
-  vx = mempcpy (&buf2[19], "ab", 2); /* { dg-warning "will always overflow" "mempcpy" } */
+  vx = mempcpy (&buf2[19], "ab", 2); /* { dg-warning "writing 2 " "mempcpy" } */
   memmove (&buf2[18], &buf1[10], 2);
-  memmove (&buf2[18], &buf1[10], 3); /* { dg-warning "will always overflow" "memmove" } */
+  memmove (&buf2[18], &buf1[10], 3); /* { dg-warning "writing 3 " "memmove" } */
   memset (&buf2[16], 'a', 4);
-  memset (&buf2[15], 'b', 6); /* { dg-warning "will always overflow" "memset" } */
+  memset (&buf2[15], 'b', 6); /* { dg-warning "writing 6 " "memset" } */
   strcpy (&buf2[18], "a");
-  strcpy (&buf2[18], "ab"); /* { dg-warning "will always overflow" "strcpy" } */
+  strcpy (&buf2[18], "ab"); /* { dg-warning "writing 3 " "strcpy" } */
   vx = stpcpy (&buf2[18], "a");
-  vx = stpcpy (&buf2[18], "ab"); /* { dg-warning "will always overflow" "stpcpy" } */
+  vx = stpcpy (&buf2[18], "ab"); /* { dg-warning "writing 3" "stpcpy" } */
   strncpy (&buf2[18], "a", 2);
-  strncpy (&buf2[18], "a", 3); /* { dg-warning "will always overflow" "strncpy" } */
+  strncpy (&buf2[18], "a", 3); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" "strncpy" } */
   strncpy (&buf2[18], "abc", 2);
-  strncpy (&buf2[18], "abc", 3); /* { dg-warning "will always overflow" "strncpy" } */
+  strncpy (&buf2[18], "abc", 3); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" "strncpy" } */
   memset (buf2, '\0', sizeof (buf2));
   strcat (&buf2[18], "a");
   memset (buf2, '\0', sizeof (buf2));
-  strcat (&buf2[18], "ab"); /* { dg-warning "will always overflow" "strcat" } */
+  strcat (&buf2[18], "ab"); /* { dg-warning "writing 3 " "strcat" } */
   sprintf (&buf2[18], "%s", buf1);
   sprintf (&buf2[18], "%s", "a");
-  sprintf (&buf2[18], "%s", "ab"); /* { dg-warning "will always overflow" "sprintf" } */
+  sprintf (&buf2[18], "%s", "ab"); /* { dg-warning "writing 3 " "sprintf" } */
   sprintf (&buf2[18], "a");
-  sprintf (&buf2[18], "ab"); /* { dg-warning "will always overflow" "sprintf" } */
+  sprintf (&buf2[18], "ab"); /* { dg-warning "writing 3 " "sprintf" } */
   snprintf (&buf2[18], 2, "%d", x);
   /* N argument to snprintf is the size of the buffer.
      Although this particular call wouldn't overflow buf2,
      incorrect buffer size was passed to it and therefore
      we want a warning and runtime failure.  */
-  snprintf (&buf2[18], 3, "%d", x); /* { dg-warning "will always overflow" "snprintf" } */
+  snprintf (&buf2[18], 3, "%d", x); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" "snprintf" } */
   va_start (ap, arg);
   vsprintf (&buf2[18], "a", ap);
   va_end (ap);
+
   va_start (ap, arg);
-  vsprintf (&buf2[18], "ab", ap); /* { dg-warning "will always overflow" "vsprintf" } */
+  vsprintf (&buf2[18], "ab", ap); /* { dg-warning "writing 3" "vsprintf" } */
   va_end (ap);
   va_start (ap, arg);
   vsnprintf (&buf2[18], 2, "%s", ap);
   va_end (ap);
   va_start (ap, arg);
   /* See snprintf above.  */
-  vsnprintf (&buf2[18], 3, "%s", ap); /* { dg-warning "will always overflow" "vsnprintf" } */
+  vsnprintf (&buf2[18], 3, "%s", ap); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" "vsnprintf" } */
   va_end (ap);
 
   p = p + 10;
   memset (p, 'd', 0);
-  q = strcpy (p, ""); /* { dg-warning "will always overflow" "strcpy" } */
+  q = strcpy (p, ""); /* { dg-warning "writing 1 " "strcpy" } */
 
   /* This invokes undefined behavior, since we are past the end of buf1.  */
   p = p + 10;
-  memset (p, 'd', 1); /* { dg-warning "will always overflow" "memset" } */
+  memset (p, 'd', 1); /* { dg-warning "writing 1 " "memset" } */
 
   memset (q, 'd', 0);
-  memset (q, 'd', 1); /* { dg-warning "will always overflow" "memset" } */
+  memset (q, 'd', 1); /* { dg-warning "writing 1 " "memset" } */
   q = q - 10;
   memset (q, 'd', 10);
 }
@@ -90,26 +94,26 @@ void
 test2 (const H h)
 {
   char c;
-  strncpy (&c, str, 3); /* { dg-warning "will always overflow" "strncpy" } */
+  strncpy (&c, str, 3); /* { dg-warning "specified bound 3 exceeds the size 1 of the destination" "strncpy" } */
 
   struct { char b[4]; } x;
-  sprintf (x.b, "%s", "ABCD"); /* { dg-warning "will always overflow" "sprintf" } */
+  sprintf (x.b, "%s", "ABCD"); /* { dg-warning "writing 5" "sprintf" } */
 
   unsigned int i;
-  memcpy (&i, &h, sizeof (h)); /* { dg-warning "will always overflow" "memcpy" } */
+  memcpy (&i, &h, sizeof (h)); /* { dg-warning "writing 16 " "memcpy" } */
 
   unsigned char buf[21];
-  memset (buf + 16, 0, 8); /* { dg-warning "will always overflow" "memset" } */
+  memset (buf + 16, 0, 8); /* { dg-warning "writing 8 " "memset" } */
 
   typedef struct { int i, j, k, l; } S;
   S *s[3];
-  memset (s, 0, sizeof (S) * 3); /* { dg-warning "will always overflow" "memset" } */
+  memset (s, 0, sizeof (S) * 3); /* { dg-warning "writing 48 " "memset" } */
 
   struct T { char a[8]; char b[4]; char c[10]; } t;
-  stpcpy (t.c,"Testing..."); /* { dg-warning "will always overflow" "stpcpy" } */
+  stpcpy (t.c,"Testing..."); /* { dg-warning "writing" "stpcpy" } */
 
   char b1[7];
   char b2[4];
   memset (b1, 0, sizeof (b1));
-  memset (b2, 0, sizeof (b1)); /* { dg-warning "will always overflow" "memset" } */
+  memset (b2, 0, sizeof (b1)); /* { dg-warning "writing 7" "memset" } */
 }
index 7c2bb60981643e9d7cffac240a92ce3ade5122d7..d537fb040c1c4135e406db322e0924e5eca8678f 100644 (file)
@@ -6,7 +6,7 @@
 /* { dg-options "-O2 -ftrack-macro-expansion=0" } */
 
 #include "../gcc.c-torture/execute/builtins/chk.h"
-   
+
 void *bar (int);
 extern void *malloc (__SIZE_TYPE__);
 
@@ -115,7 +115,7 @@ baz (const struct A *x, const unsigned char *z)
          else
            do
              {
-               memcpy (e, d, 513); /* { dg-warning "will always overflow" "memcpy" } */
+               memcpy (e, d, 513); /* { dg-warning "writing" "memcpy" } */
                e += 4;
              }
            while (--h);
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c
new file mode 100644 (file)
index 0000000..4857bda
--- /dev/null
@@ -0,0 +1,525 @@
+/* Test exercising buffer overflow warnings emitted for raw memory and
+   string manipulation builtins involving ranges of sizes and strings
+   of varying lengths.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -ftrack-macro-expansion=0" } */
+
+#define INT_MAX      __INT_MAX__
+#define PTRDIFF_MAX  __PTRDIFF_MAX__
+#define SIZE_MAX     __SIZE_MAX__
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__    size_t;
+
+static const size_t ssize_max = SIZE_MAX / 2;
+static const size_t size_max = SIZE_MAX;
+
+extern signed char    schar_val;
+extern signed short   sshrt_val;
+extern signed int     sint_val;
+extern signed long    slong_val;
+extern unsigned char  uchar_val;
+extern unsigned short ushrt_val;
+extern unsigned int   uint_val;
+extern unsigned long  ulong_val;
+
+#define memcpy(d, s, n) (memcpy ((d), (s), (n)), sink ((d)))
+extern void* (memcpy)(void*, const void*, size_t);
+
+#define mempcpy(d, s, n) (mempcpy ((d), (s), (n)), sink ((d)))
+extern void* (mempcpy)(void*, const void*, size_t);
+
+#define memset(d, c, n) (memset ((d), (c), (n)), sink ((d)))
+extern void* (memset)(void*, int, size_t);
+
+#define bzero(d, n) (bzero ((d), (n)), sink ((d)))
+extern void (bzero)(void*, size_t);
+
+#define strcat(d, s) (strcat ((d), (s)), sink ((d)))
+extern char* (strcat)(char*, const char*);
+
+#define strncat(d, s, n) (strncat ((d), (s), (n)), sink ((d)))
+extern char* (strncat)(char*, const char*, size_t);
+
+#define strcpy(d, s) (strcpy ((d), (s)), sink ((d)))
+extern char* (strcpy)(char*, const char*);
+
+#define strncpy(d, s, n) (strncpy ((d), (s), (n)), sink ((d)))
+extern char* (strncpy)(char*, const char*, size_t);
+
+void sink (void*);
+
+/* Function to "generate" a random number each time it's called.  Declared
+   (but not defined) and used to prevent GCC from making assumptions about
+   their values based on the variables uses in the tested expressions.  */
+size_t random_unsigned_value (void);
+ptrdiff_t random_signed_value (void);
+
+/* Return a random unsigned value between MIN and MAX.  */
+
+static inline size_t
+unsigned_range (size_t min, size_t max)
+{
+  const size_t val = random_unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+/* Return a random signed value between MIN and MAX.  */
+
+static inline ptrdiff_t
+signed_range (ptrdiff_t min, ptrdiff_t max)
+{
+  const ptrdiff_t val = random_signed_value ();
+  return val < min || max < val ? min : val;
+}
+
+/* For brevity.  */
+#define UR(min, max)   unsigned_range (min, max)
+#define SR(min, max)   signed_range (min, max)
+
+/* UReturn a pointer to constant string whose length is at least MINLEN
+   and at most 10.  */
+static inline const char*
+string_range (size_t minlen)
+{
+  static const char str[] = "0123456789";
+
+  const size_t len = unsigned_range (minlen, sizeof str - 1);
+
+  switch (len)
+    {
+    case 10: return "0123456789";
+    case  9: return "012345678";
+    case  8: return "01234567";
+    case  7: return "0123456";
+    case  6: return "012345";
+    case  5: return "01234";
+    case  4: return "0123";
+    case  3: return "012";
+    case  2: return "01";
+    case  1: return "0";
+    case  0: return "";
+    }
+}
+
+#define S(minlen)   string_range (minlen)
+
+/* Test memcpy with a number of bytes bounded by a known range.  */
+
+void test_memcpy_range (void *d, const void *s)
+{
+  char buf[5];
+
+  memcpy (buf, s, UR (0, 5));
+  memcpy (buf, s, UR (1, 5));
+  memcpy (buf, s, UR (2, 5));
+  memcpy (buf, s, UR (3, 5));
+  memcpy (buf, s, UR (4, 5));
+
+  memcpy (buf, s, UR (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+  memcpy (buf + 5, s, UR (1, 2));  /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" } */
+
+  memcpy (buf + size_max, s, UR (1, 2));  /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" "excessive pointer offset" { xfail *-*-* } } */
+
+  memcpy (buf, s, UR (ssize_max, size_max));   /* { dg-warning "writing between \[0-9\]+ and \[0-9\]+ bytes into a region of size 5 overflows the destination" } */
+  memcpy (buf, s, UR (ssize_max + 1, size_max));  /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+  memcpy (buf, s, UR (size_max - 1, size_max));  /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+
+  /* Exercise memcpy into a destination of unknown size with excessive
+     number of bytes.  */
+  memcpy (d, s, UR (ssize_max, size_max));
+  memcpy (d, s, UR (ssize_max + 1, size_max));   /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+
+  memcpy (buf, s, SR (-1, 1));
+  memcpy (buf, s, SR (-3, 2));
+  memcpy (buf, s, SR (-5, 3));
+  memcpy (buf, s, SR (-7, 4));
+  memcpy (buf, s, SR (-9, 5));
+  memcpy (buf, s, SR (-11, 6));
+
+  memcpy (d, s, SR (-1, 1));
+  memcpy (d, s, SR (-3, 2));
+  memcpy (d, s, SR (-5, 3));
+  memcpy (d, s, SR (-7, 4));
+  memcpy (d, s, SR (-9, 5));
+  memcpy (d, s, SR (-11, 6));
+
+  memcpy (buf, s, SR (-2, -1));   /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+  memcpy (d, s, SR (-2, -1));   /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+
+  /* Even though the following calls are bounded by the range of N's
+     type they must not cause a warning for obvious reasons.  */
+  memcpy (buf, s, schar_val);
+  memcpy (buf, s, sshrt_val);
+  memcpy (buf, s, sint_val);
+  memcpy (buf, s, slong_val);
+
+  memcpy (buf, s, uchar_val);
+  memcpy (buf, s, ushrt_val);
+  memcpy (buf, s, uint_val);
+  memcpy (buf, s, ulong_val);
+
+  memcpy (buf, s, schar_val + 1);
+  memcpy (buf, s, sshrt_val + 2);
+  memcpy (buf, s, sint_val + 3);
+  memcpy (buf, s, slong_val + 4);
+
+  memcpy (d, s, uchar_val + 5);
+  memcpy (d, s, ushrt_val + 6);
+  memcpy (d, s, uint_val + 7);
+  memcpy (d, s, ulong_val + 8);
+
+  memcpy (d, s, schar_val);
+  memcpy (d, s, sshrt_val);
+  memcpy (d, s, sint_val);
+  memcpy (d, s, slong_val);
+
+  memcpy (d, s, uchar_val);
+  memcpy (d, s, ushrt_val);
+  memcpy (d, s, uint_val);
+  memcpy (d, s, ulong_val);
+
+  memcpy (d, s, schar_val + 1);
+  memcpy (d, s, sshrt_val + 2);
+  memcpy (d, s, sint_val + 3);
+  memcpy (d, s, slong_val + 4);
+
+  memcpy (d, s, uchar_val + 5);
+  memcpy (d, s, ushrt_val + 6);
+  memcpy (d, s, uint_val + 7);
+  memcpy (d, s, ulong_val + 8);
+}
+
+/* Test mempcpy with a number of bytes bounded by a known range.  */
+
+void test_mempcpy_range (void *d, const void *s)
+{
+  char buf[5];
+
+  mempcpy (buf, s, UR (0, 5));
+  mempcpy (buf, s, UR (1, 5));
+  mempcpy (buf, s, UR (2, 5));
+  mempcpy (buf, s, UR (3, 5));
+  mempcpy (buf, s, UR (4, 5));
+
+  mempcpy (buf, s, UR (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+  mempcpy (buf, s, UR (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+  mempcpy (buf, s, UR (ssize_max, size_max));   /* { dg-warning "writing between \[0-9\]+ and \[0-9\]+ bytes into a region of size 5 overflows the destination" } */
+  mempcpy (buf, s, UR (ssize_max + 1, size_max));  /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+  mempcpy (buf, s, UR (size_max - 1, size_max));  /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+
+  /* Exercise mempcpy into a destination of unknown size with excessive
+     number of bytes.  */
+  mempcpy (d, s, UR (ssize_max, size_max));
+  mempcpy (d, s, UR (ssize_max + 1, size_max));   /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+}
+
+/* Test memset with a number of bytes bounded by a known range.  */
+
+void test_memset_range (void *d)
+{
+  char buf[5];
+
+  memset (buf, 0, UR (0, 5));
+  memset (buf, 0, UR (1, 5));
+  memset (buf, 0, UR (2, 5));
+  memset (buf, 0, UR (3, 5));
+  memset (buf, 0, UR (4, 5));
+
+  memset (buf, 0, UR (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+  memset (buf, 0, UR (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+  memset (buf, 0, UR (ssize_max, size_max));   /* { dg-warning "writing between \[0-9\]+ and \[0-9\]+ bytes into a region of size 5 overflows the destination" } */
+  memset (buf, 0, UR (ssize_max + 1, size_max));  /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+  memset (buf, 0, UR (size_max - 1, size_max));  /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+
+  /* Exercise memset into a destination of unknown size with excessive
+     number of bytes.  */
+  memset (d, 0, UR (ssize_max, size_max));
+  memset (d, 0, UR (ssize_max + 1, size_max));   /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+}
+
+/* Test bzero with a number of bytes bounded by a known range.  */
+
+void test_bzero_range (void *d)
+{
+  char buf[5];
+
+  bzero (buf, UR (0, 5));
+  bzero (buf, UR (1, 5));
+  bzero (buf, UR (2, 5));
+  bzero (buf, UR (3, 5));
+  bzero (buf, UR (4, 5));
+
+  bzero (buf, UR (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+  bzero (buf, UR (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+  bzero (buf, UR (ssize_max, size_max));   /* { dg-warning "writing between \[0-9\]+ and \[0-9\]+ bytes into a region of size 5 overflows the destination" } */
+  bzero (buf, UR (ssize_max + 1, size_max));  /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+  bzero (buf, UR (size_max - 1, size_max));  /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+
+  /* Exercise bzero into a destination of unknown size with excessive
+     number of bytes.  */
+  bzero (d, UR (ssize_max, size_max));
+  bzero (d, UR (ssize_max + 1, size_max));   /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+}
+
+/* Test strcat with an argument referencing a non-constant string of
+   lengths in a known range.  */
+
+void test_strcat_range (void)
+{
+  char buf[5] = "";
+
+  strcat (buf, S (0));
+  strcat (buf, S (1));
+  strcat (buf, S (2));
+  strcat (buf, S (3));
+  strcat (buf, S (4));
+  strcat (buf, S (5));   /* { dg-warning "writing 6 bytes into a region of size 5 " } */
+
+  {
+    /* The implementation of the warning isn't smart enough to determine
+       the length of the string in the buffer so it assumes it's empty
+       and issues the warning basically for the same cases as strcat.  */
+    char buf2[5] = "12";
+    strcat (buf2, S (4));   /* { dg-warning "writing 5 bytes into a region of size 3" "strcat to a non-empty string" { xfail *-*-* } } */
+  }
+}
+
+/* Verify that strcpy with an unknown source string doesn't cause
+   warnings unless the destination has zero size.  */
+
+void test_strcpy (const char *src)
+{
+  struct A { char a[2]; char b[3]; } a;
+
+  strcpy (a.a, src);
+  strcpy (a.a + 1, src);
+
+  /* There must be enough room in the destination for the terminating
+     nul, otherwise verify that a warning is issued.
+     The following works as expected with __builtin___strcpy_chk and
+     __builtin_object_size because they see that the offset is from
+     the a.a array.  When optimization is enabled, it isn't detected
+     by __bultin_strcpy (when __builtin_object_size isn't called
+     explicitly) because by the time it's seen the offset has been
+     transformed to one from the beginning of the whole object, i.e.,
+     as if it had been written as (char*)&a + 2 .  Then the destination
+     size is taken to be the rest of the whole object.  It is detected
+     by __builtin_strcpy when optimization is not enabled because then
+     the &a.a + 2 expression is preserved.  But without optimization
+     an ordinary call to strcpy isn't transformed to __builtin_strcpy
+     and so it can't be detected here (since the rest of the test
+     relies on optimization).  */
+  strcpy (a.a + 2, src);    /* { dg-warning "writing at least 1 byte into a region of size 0 " "strcpy into empty substring" { xfail *-*-* } } */
+
+  /* This does work.  */
+  strcpy (a.a + 5, src);    /* { dg-warning "writing at least 1 byte into a region of size 0 " } */
+
+  /* As does this.  */
+  strcpy (a.a + 17, src);    /* { dg-warning "writing at least 1 byte into a region of size 0 " } */
+}
+
+/* Test strcpy with a non-constant source string of length in a known
+   range.  */
+
+void test_strcpy_range (void)
+{
+  char buf[5];
+
+  strcpy (buf, S (0));
+  strcpy (buf, S (1));
+  strcpy (buf, S (2));
+  strcpy (buf, S (4));
+  strcpy (buf, S (5));   /* { dg-warning "writing 6 bytes into a region of size 5 " } */
+  strcpy (buf, S (6));   /* { dg-warning "writing 7 bytes into a region of size 5 " } */
+  strcpy (buf, S (7));   /* { dg-warning "writing 8 bytes into a region of size 5 " } */
+  strcpy (buf, S (8));   /* { dg-warning "writing 9 bytes into a region of size 5 " } */
+  strcpy (buf, S (9));   /* { dg-warning "writing 10 bytes into a region of size 5 " } */
+  strcpy (buf, S (10));   /* { dg-warning "writing 11 bytes into a region of size 5 " } */
+
+  strcpy (buf + 5, S (0));   /* { dg-warning "writing 1 byte into a region of size 0 " } */
+
+  strcpy (buf + 17, S (0));   /* { dg-warning "writing 1 byte into a region of size 0 " } */
+}
+
+/* Test strncat with an argument referencing a non-constant string of
+   lengths in a known range.  */
+
+void test_strncat_range (void)
+{
+  char buf[5] = "";
+
+  strncat (buf, S (0), 0);
+  strncat (buf, S (0), 1);
+  strncat (buf, S (0), 2);
+  strncat (buf, S (0), 3);
+  strncat (buf, S (0), 4);
+
+  strncat (buf + 5, S (0), 0);
+
+  strncat (buf + 5, S (0), 1);   /* { dg-warning "specified bound 1 exceeds the size 0 of the destination " } */
+  strncat (buf + 5, S (1), 1);   /* { dg-warning "specified bound 1 exceeds the size 0 of the destination " } */
+
+  /* Strncat always appends a terminating null after copying the N
+     characters so the following triggers a warning pointing out
+     that specifying sizeof(buf) as the upper bound may cause
+     the nul to overflow the destination.  */
+  strncat (buf, S (0), 5);   /* { dg-warning "specified bound 5 equals the size of the destination" } */
+  strncat (buf, S (0), 6);   /* { dg-warning "specified bound 6 exceeds the size 5 of the destination" } */
+
+  strncat (buf, S (1), 0);
+  strncat (buf, S (1), 1);
+  strncat (buf, S (1), 2);
+  strncat (buf, S (1), 3);
+  strncat (buf, S (1), 4);
+  strncat (buf, S (1), 5);   /* { dg-warning "specified bound 5 equals the size of the destination" } */
+  strncat (buf, S (1), 6);   /* { dg-warning "specified bound 6 exceeds the size 5 of the destination" } */
+  strncat (buf, S (2), 6);   /* { dg-warning "specified bound 6 exceeds the size 5 of the destination" } */
+
+  /* The following could just as well say "writing 6 bytes into a region
+     of size 5.  Either would be correct and probably equally as clear
+     in this case.  But when the length of the source string is not known
+     at all then the bound warning seems clearer.  */
+  strncat (buf, S (5), 6);   /* { dg-warning "specified bound 6 exceeds the size 5 of the destination " } */
+  strncat (buf, S (7), 6);   /* { dg-warning "specified bound 6 exceeds the size 5 of the destination" } */
+
+  {
+    /* The implementation of the warning isn't smart enough to determine
+       the length of the string in the buffer so it assumes it's empty
+       and issues the warning basically for the same cases as strncpy.  */
+    char buf2[5] = "12";
+    strncat (buf2, S (4), 4);   /* { dg-warning "writing 5 bytes into a region of size 3" "strncat to a non-empty string" { xfail *-*-* } } */
+  }
+}
+
+/* Test strncat_chk with an argument referencing a non-constant string
+   of lengths in a known range.  */
+
+void test_strncat_chk_range (char *d)
+{
+  char buf[5] = "";
+
+#define strncat_chk(d, s, n) \
+  __builtin___strncat_chk ((d), (s), (n), __builtin_object_size (d, 1));
+
+  strncat_chk (buf, S (0), 1);
+  strncat_chk (buf, S (0), 2);
+  strncat_chk (buf, S (0), 3);
+  strncat_chk (buf, S (0), 4);
+  strncat_chk (buf, S (0), 5);   /* { dg-warning "specified bound 5 equals the size of the destination " } */
+
+  strncat_chk (buf, S (5), 1);
+  strncat_chk (buf, S (5), 2);
+  strncat_chk (buf, S (5), 3);
+  strncat_chk (buf, S (5), 4);
+  strncat_chk (buf, S (5), 5);   /* { dg-warning "specified bound 5 equals the size of the destination " } */
+
+  strncat_chk (buf, S (5), 10);   /* { dg-warning "specified bound \[0-9\]+ exceeds the size 5 of the destination " } */
+
+  strncat_chk (d, S (5), size_max);   /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size " } */
+}
+
+/* Test strncpy with a non-constant source string of length in a known
+   range and a constant number of bytes.  */
+
+void test_strncpy_string_range (char *d)
+{
+  char buf[5];
+
+  strncpy (buf, S (0), 0);
+  strncpy (buf, S (0), 1);
+  strncpy (buf, S (0), 2);
+  strncpy (buf, S (0), 3);
+  strncpy (buf, S (0), 4);
+  strncpy (buf, S (0), 5);
+  strncpy (buf, S (0), 6);   /* { dg-warning "writing 6 bytes into a region of size 5 " } */
+
+  strncpy (buf, S (6), 4);
+  strncpy (buf, S (7), 5);
+  strncpy (buf, S (8), 6);   /* { dg-warning "writing 6 bytes into a region of size 5 " } */
+
+  strncpy (buf, S (1), ssize_max - 1);   /* { dg-warning "writing \[0-9\]+ bytes into a region of size 5" } */
+  strncpy (buf, S (2), ssize_max);   /* { dg-warning "writing \[0-9\]+ bytes into a region of size 5" } */
+  strncpy (buf, S (3), ssize_max + 1);   /* { dg-warning "specified size \[0-9\]+ exceeds maximum object size" } */
+  strncpy (buf, S (4), size_max);   /* { dg-warning "specified size \[0-9\]+ exceeds maximum object size" } */
+
+  /* Exercise strncpy into a destination of unknown size with a valid
+     and invalid constant number of bytes.  */
+  strncpy (d, S (1), ssize_max - 1);
+  strncpy (d, S (2), ssize_max);
+  strncpy (d, S (3), ssize_max + 1);   /* { dg-warning "specified size \[0-9\]+ exceeds maximum object size" } */
+  strncpy (d, S (4), size_max);   /* { dg-warning "specified size \[0-9\]+ exceeds maximum object size" } */
+}
+
+/* Test strncpy with a non-constant source string of length in a known
+   range and a non-constant number of bytes also in a known range.  */
+
+void test_strncpy_string_count_range (char *dst, const char *src)
+{
+  char buf[5];
+
+  strncpy (buf, S (0), UR (0, 1));
+  strncpy (buf, S (0), UR (0, 2));
+  strncpy (buf, S (0), UR (0, 3));
+  strncpy (buf, S (0), UR (0, 4));
+  strncpy (buf, S (0), UR (0, 5));
+  strncpy (buf, S (0), UR (0, 6));
+  strncpy (buf, S (0), UR (1, 6));
+  strncpy (buf, S (0), UR (2, 6));
+  strncpy (buf, S (0), UR (3, 6));
+  strncpy (buf, S (0), UR (4, 6));
+  strncpy (buf, S (0), UR (5, 6));
+
+  strncpy (buf, S (9), UR (0, 1));
+  strncpy (buf, S (8), UR (0, 2));
+  strncpy (buf, S (7), UR (0, 3));
+  strncpy (buf, S (6), UR (0, 4));
+  strncpy (buf, S (8), UR (0, 5));
+  strncpy (buf, S (7), UR (0, 6));
+  strncpy (buf, S (6), UR (1, 6));
+  strncpy (buf, S (5), UR (2, 6));
+  strncpy (buf, S (9), UR (3, 6));
+  strncpy (buf, S (8), UR (4, 6));
+  strncpy (buf, S (7), UR (5, 6));
+
+  strncpy (buf, S (0), UR (6, 7));   /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 " } */
+  strncpy (buf, S (1), UR (7, 8));   /* { dg-warning "writing between 7 and 8 bytes into a region of size 5 " } */
+  strncpy (buf, S (2), UR (ssize_max, ssize_max + 1));   /* { dg-warning "writing between \[0-9\]+ and \[0-9\]+ bytes into a region of size 5 " } */
+
+  strncpy (buf, S (2), UR (ssize_max + 1, ssize_max + 2));   /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+
+  strncpy (buf + 5, S (0), UR (0, 1));
+  strncpy (buf + 5, S (1), UR (0, 1));
+  strncpy (buf + 5, S (0), UR (1, 2));   /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 " } */
+  strncpy (buf + 5, S (1), UR (1, 2));   /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 " } */
+
+  strncpy (buf, src, UR (0, 1));
+  strncpy (buf, src, UR (0, 2));
+  strncpy (buf, src, UR (0, 3));
+  strncpy (buf, src, UR (0, 4));
+  strncpy (buf, src, UR (0, 5));
+  strncpy (buf, src, UR (0, 6));
+  strncpy (buf, src, UR (1, 6));
+  strncpy (buf, src, UR (2, 6));
+  strncpy (buf, src, UR (3, 6));
+  strncpy (buf, src, UR (4, 6));
+  strncpy (buf, src, UR (5, 6));
+  strncpy (buf, src, UR (6, 7));   /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 " } */
+
+  /* Exercise strncpy into a destination of unknown size  with a valid
+     and invalid constant number of bytes.  */
+  strncpy (dst, S (0), UR (5, 6));
+  strncpy (dst, S (1), UR (6, 7));
+  strncpy (dst, S (2), UR (7, 8));
+
+  strncpy (dst, S (3), UR (ssize_max, ssize_max + 1));
+
+  strncpy (dst, S (4), UR (ssize_max + 1, ssize_max + 2));   /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
new file mode 100644 (file)
index 0000000..489f880
--- /dev/null
@@ -0,0 +1,260 @@
+/* Test exercising -Wrawmem-overflow and -Wstringop-overflow warnings.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wstringop-overflow=1" } */
+
+#define offsetof(type, mem)   __builtin_offsetof (type, mem)
+
+/* Return the number of bytes from member MEM of TYPE to the end
+   of object OBJ.  */
+#define offsetfrom(type, obj, mem) (sizeof (obj) - offsetof (type, mem))
+
+
+typedef __SIZE_TYPE__ size_t;
+extern void* memcpy (void*, const void*, size_t);
+extern void* memset (void*, int, __SIZE_TYPE__);
+
+
+struct A { char a, b; };
+struct B { struct A a; char c, d; };
+
+/* Function to call to "escape" pointers from tests below to prevent
+   GCC from assuming the values of the objects they point to stay
+   the unchanged.  */
+void escape (void*, ...);
+
+/* Function to "generate" a random number each time it's called.  Declared
+   (but not defined) and used to prevent GCC from making assumptions about
+   their values based on the variables uses in the tested expressions.  */
+size_t random_unsigned_value (void);
+
+/* Return a random unsigned value between MIN and MAX.  */
+
+static inline size_t
+range (size_t min, size_t max)
+{
+  const size_t val = random_unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+/* Verify that writing past the end of a local array is diagnosed.  */
+
+void test_memop_warn_local (const void *src)
+{
+  size_t n;
+
+  n = range (8, 32);
+
+  struct A a[2];
+
+  memcpy (a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
+  escape (a, src);
+
+  /* At -Wrawmem-overflow=1 the destination is considered to be
+     the whole array and its size is therefore sizeof a.  */
+  memcpy (&a[0], src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
+  escape (a, src);
+
+  /* Verify the same as above but by writing into the first mmeber
+     of the first element of the array.  */
+  memcpy (&a[0].a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
+  escape (a, src);
+
+  n = range (12, 32);
+
+  struct B b[2];
+
+  memcpy (&b[0], src, n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" } */
+  escape (b);
+
+  /* The following idiom of clearing multiple members of a struct is
+     used in a few places in the Linux kernel.  Verify that a warning
+     is issued for it when it writes past the end of the array object.  */
+  memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1);   /* { dg-warning "writing 8 bytes into a region of size 7" } */
+  escape (b);
+
+  memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1);   /* { dg-warning "writing 8 bytes into a region of size 7" } */
+  escape (b);
+
+  memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1);   /* { dg-warning "writing 7 bytes into a region of size 6" } */
+  escape (b);
+
+  memset (&b->c, 0, offsetfrom (struct B, b, c) + 1);   /* { dg-warning "writing 7 bytes into a region of size 6" } */
+  escape (b);
+
+  memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1);   /* { dg-warning "writing 6 bytes into a region of size 5" } */
+  escape (b);
+
+  memset (&b->d, 0, offsetfrom (struct B, b, d) + 1);   /* { dg-warning "writing 6 bytes into a region of size 5" } */
+  escape (b);
+
+  /* Same as above but clearing just elements of the second element
+     of the array.  */
+  memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1);   /* { dg-warning "writing 4 bytes into a region of size 3" } */
+  escape (b);
+
+  memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1);   /* { dg-warning "writing 3 bytes into a region of size 2" } */
+  escape (b);
+
+  memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1);   /* { dg-warning "writing 2 bytes into a region of size 1" } */
+  escape (b);
+}
+
+/* Verify that writing past the end of a dynamically allocated array
+   of known size is diagnosed.  */
+
+void test_memop_warn_alloc (const void *src)
+{
+  size_t n;
+
+  n = range (8, 32);
+
+  struct A *a = __builtin_malloc (sizeof *a * 2);
+
+  memcpy (a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
+  escape (a, src);
+
+  /* At -Wrawmem-overflow=1 the destination is considered to be
+     the whole array and its size is therefore sizeof a.  */
+  memcpy (&a[0], src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
+  escape (a, src);
+
+  /* Verify the same as above but by writing into the first mmeber
+     of the first element of the array.  */
+  memcpy (&a[0].a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
+  escape (a, src);
+
+  n = range (12, 32);
+
+  struct B *b = __builtin_malloc (sizeof *b * 2);
+
+  memcpy (&b[0], src, n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
+  escape (b);
+
+  /* The following idiom of clearing multiple members of a struct is
+     used in a few places in the Linux kernel.  Verify that a warning
+     is issued for it when it writes past the end of the array object.  */
+  memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1);   /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
+  escape (b);
+
+  memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1);   /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
+  escape (b);
+
+  memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1);   /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
+  escape (b);
+
+  memset (&b->c, 0, offsetfrom (struct B, b, c) + 1);   /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
+  escape (b);
+
+  memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1);   /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
+  escape (b);
+
+  memset (&b->d, 0, offsetfrom (struct B, b, d) + 1);   /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
+  escape (b);
+
+  /* Same as above but clearing just elements of the second element
+     of the array.  */
+  memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1);   /* { dg-warning "writing 4 bytes into a region of size 3" "memcpy into allocated" { xfail *-*-*} } */
+  escape (b);
+
+  memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1);   /* { dg-warning "writing 3 bytes into a region of size 2" "memcpy into allocated" { xfail *-*-*} } */
+  escape (b);
+
+  memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1);   /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" { xfail *-*-*} } */
+  escape (b);
+}
+
+
+void test_memop_nowarn (const void *src)
+{
+  struct B b[2];
+
+  size_t n = range (sizeof b, 32);
+
+  /* Verify that clearing the whole array is not diagnosed regardless
+     of whether the expression pointing to its beginning is obtained
+     from the array itself or its first member(s).  */
+  memcpy (b, src, n);
+  escape (b);
+
+  memcpy (&b[0], src, n);
+  escape (b);
+
+  memcpy (&b[0].a, src, n);
+  escape (b, src);
+
+  memcpy (&b[0].a.a, src, n);
+  escape (b, src);
+
+  /* Clearing multiple elements of an array of structs.  */
+  memset (&b[0].a.b, 0, sizeof b - offsetof (struct B, a.b));
+  escape (b);
+
+  memset (&b->a.b, 0, sizeof b - offsetof (struct B, a.b));
+  escape (b);
+
+  memset (&b[0].c, 0, sizeof b - offsetof (struct B, c));
+  escape (b);
+
+  memset (&b->c, 0, sizeof b - offsetof (struct B, c));
+  escape (b);
+
+  memset (&b[0].d, 0, sizeof b - offsetof (struct B, d));
+  escape (b);
+
+  memset (&b->d, 0, sizeof b - offsetof (struct B, d));
+  escape (b);
+
+  /* Same as above but clearing just elements of the second element
+     of the array.  */
+  memset (&b[1].a.b, 0, sizeof b[1] - offsetof (struct B, a.b));
+  escape (b);
+
+  memset (&b[1].c, 0, sizeof b[1] - offsetof (struct B, c));
+  escape (b);
+
+  memset (&b[1].d, 0, sizeof b[1] - offsetof (struct B, d));
+  escape (b);
+}
+
+
+/* The foollowing function could specify in its API that it takes
+   an array of exactly two elements, as shown below.  Verify that
+   writing into both elements is not diagnosed.  */
+void test_memop_nowarn_arg (struct A[2], const void*);
+
+void test_memop_nowarn_arg (struct A *a, const void *src)
+{
+  memcpy (a, src, 2 * sizeof *a);
+  escape (a, src);
+
+  memcpy (a, src, range (2 * sizeof *a, 123));
+  escape (a, src);
+}
+
+
+struct C { char a[3], b; };
+struct D { struct C c; char d, e; };
+
+extern char* strncpy (char*, const char*, __SIZE_TYPE__);
+
+void test_stringop_warn (void)
+{
+  size_t n = range (2 * sizeof (struct D) + 1, 33);
+
+  struct C c[2];
+
+  /* Similarly, at -Wstringop-overflow=1 the destination is considered
+     to be the whole array and its size is therefore sizeof c.  */
+  strncpy (c[0].a, "123", n);   /* { dg-warning "writing between 13 and 33 bytes into a region of size 8 overflows the destination" } */
+
+  escape (c);
+}
+
+
+void test_stringop_nowarn (void)
+{
+  struct D d[2];
+
+  strncpy (d[0].c.a, "123", range (sizeof d, 32));
+  escape (d);
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-6.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-6.c
new file mode 100644 (file)
index 0000000..9572ce1
--- /dev/null
@@ -0,0 +1,112 @@
+/* Test exercising -Wrawmem-overflow and -Wstringop-overflow warnings.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wstringop-overflow=2" } */
+
+#define offsetof(type, mem)   __builtin_offsetof (type, mem)
+
+/* Return the number of bytes from member MEM of TYPE to the end
+   of object OBJ.  */
+#define offsetfrom(type, obj, mem) (sizeof (obj) - offsetof (type, mem))
+
+
+typedef __SIZE_TYPE__ size_t;
+extern void* memcpy (void*, const void*, size_t);
+extern void* memset (void*, int, __SIZE_TYPE__);
+
+
+struct A { char a, b; };
+struct B { struct A a; char c, d; };
+
+/* Function to call to "escape" pointers from tests below to prevent
+   GCC from assuming the values of the objects they point to stay
+   the unchanged.  */
+void escape (void*, ...);
+
+/* Function to "generate" a random number each time it's called.  Declared
+   (but not defined) and used to prevent GCC from making assumptions about
+   their values based on the variables uses in the tested expressions.  */
+size_t random_unsigned_value (void);
+
+/* Return a random unsigned value between MIN and MAX.  */
+
+static inline size_t
+range (size_t min, size_t max)
+{
+  const size_t val = random_unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+
+void test_memop_warn_object (const void *src)
+{
+  unsigned n = range (17, 29);
+
+  struct A a[2];
+
+  /* At both -Wstringop-overflow=2, like at 1, the destination of functions
+     that operate on raw memory is considered to be the whole array and its
+     size is therefore sizeof a.  */
+  memcpy (&a[0], src, n);   /* { dg-warning "writing between 17 and 29 bytes into a region of size 4 overflows the destination" } */
+  escape (a);
+}
+
+void test_memop_warn_subobject (const void *src)
+{
+  unsigned n = range (17, 31);
+
+  struct B b[2];
+
+  /* At -Wrawmem-overflow=2 the destination is considered to be
+     the member sobobject of the first array element and its size
+     is therefore sizeof b[0].a.  */
+  memcpy (&b[0].a, src, n);   /* { dg-warning "writing between 17 and 31 bytes into a region of size 8 overflows the destination" } */
+
+  escape (b);
+}
+
+void test_memop_nowarn_subobject (void)
+{
+  struct B b[2];
+
+  /* The following idiom of clearing multiple members of a struct
+     has been seen in a few places in the Linux kernel.  Verify
+     that a warning is not issued for it.  */
+  memset (&b[0].c, 0, sizeof b[0] - offsetof (struct B, c));
+
+  escape (b);
+}
+
+struct C { char a[3], b; };
+struct D { struct C c; char d, e; };
+
+extern char* strncpy (char*, const char*, __SIZE_TYPE__);
+
+void test_stringop_warn_object (const char *str)
+{
+  unsigned n = range (2 * sizeof (struct D), 32);
+
+  struct C c[2];
+
+  /* Similarly, at -Wstringop-overflow=2 the destination is considered
+     to be the array member of the first element of the array c and its
+     size is therefore sizeof c[0].a.  */
+  strncpy (c[0].a, "123", n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 3 overflows the destination" } */
+  escape (c);
+
+  strncpy (c[0].a, str, n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 3 overflows the destination" } */
+  escape (c);
+}
+
+void test_stringop_warn_subobject (const char *src)
+{
+  unsigned n = range (2 * sizeof (struct D), 32);
+
+  struct D d[2];
+
+  /* Same as above.  */
+  strncpy (d[0].c.a, "123", n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 3 overflows the destination" } */
+  escape (d);
+
+  strncpy (d[0].c.a, src, n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 3 overflows the destination" } */
+  escape (d);
+}
index 44677f16778257bec02bdf1c9f6494c7481a31bd..daff6801eea98bc2348a86f40d02e96b882c0a02 100644 (file)
@@ -24,15 +24,15 @@ test (int arg, ...)
   *p = 0;
   strncat (p, "abcdefghi", 10);
   *p = 0;
-  strncat (p, "abcdefghij", 10); /* { dg-warning "will always overflow" } */
+  strncat (p, "abcdefghij", 10); /* { dg-warning "writing 11 bytes into a region of size 10 overflows the destination" } */
   *p = 0;
   strncat (p, "abcdefgh", 11);
   *p = 0;
-  strncat (p, "abcdefghijkl", 11); /* { dg-warning "will always overflow" } */
+  strncat (p, "abcdefghijkl", 11); /* { dg-warning "specified bound 11 exceeds the size 10 of the destination" } */
   *p = 0;
   strncat (p, q, 9);
   *p = 0;
-  strncat (p, q, 10); /* { dg-warning "might overflow" } */
+  strncat (p, q, 10); /* { dg-warning "specified bound 10 equals the size of the destination" } */
   *p = 0;
-  strncat (p, q, 11); /* { dg-warning "might overflow" } */
+  strncat (p, q, 11); /* { dg-warning "specified bound 11 exceeds the size 10 of the destination" } */
 }
index 8e9d891aceaf84b1f06f8fb08b38e20a40d74289..94dc3508f1adb46678271e466c2f566e8a34a8f5 100644 (file)
@@ -106,7 +106,7 @@ int
 foo8 ()
 {
   char base[100];
-  memcpy ((void *)base, (const void *)pg0, 105);
+  memcpy ((void *)base, (const void *)pg0, 105);   /* { dg-warning "writing 105 bytes into a region of size 100" } */
   return (int)(base[32]);
 }
 
index 24464abd4a9257d90053917b23493f3d4ac0f832..7f839d27abde09668a9e7620c36bc8e951957e52 100644 (file)
@@ -7,7 +7,7 @@ typedef __SIZE_TYPE__ size_t;
 extern inline __attribute__((gnu_inline, always_inline, artificial)) void *
 memcpy (void *__restrict dest, const void *__restrict src, size_t len)
 {
-  return __builtin___memcpy_chk (dest, /* { dg-warning "will always overflow destination buffer" } */
+  return __builtin___memcpy_chk (dest, /* { dg-warning "writing" } */
                                 src, len, __builtin_object_size (dest, 0));
 }
 
index aae84c637814461f1c1c76a2904588152654fc4e..78540a252128fb75d12704e457f573fb53567518 100644 (file)
@@ -20,5 +20,5 @@ main (void)
   return 0;
 }
 
-/* { dg-warning "will always overflow destination buffer" "" { target *-*-* } 10 } */
+/* { dg-warning "writing" "" { target *-*-* } 10 } */
 /* { dg-message "file included" "In file included" { target *-*-* } 0 } */
index a0d6e084e7397caf0c4a806be0270b6cda9ece59..1dc21d1bb62e2f39218252f6615deae0cddd5834 100644 (file)
@@ -12,5 +12,5 @@ main (void)
   return 0;
 }
 
-/* { dg-warning "will always overflow destination buffer" "" { target *-*-* } 10 } */
+/* { dg-warning "writing" "" { target *-*-* } 10 } */
 /* { dg-message "file included" "In file included" { target *-*-* } 0 } */
index f50514c3ac9a6deb56a1773ab91841ab0ca7ccdb..e51714737161d5ce58434284ef88e116217543b1 100644 (file)
@@ -13,5 +13,5 @@ main (void)
   return 0;
 }
 
-/* { dg-warning "will always overflow destination buffer" "" { target *-*-* } 10 } */
+/* { dg-warning "writing" "" { target *-*-* } 10 } */
 /* { dg-message "file included" "In file included" { target *-*-* } 0 } */
index 7ce9eaeb26152fe76dc602efa1df0adac20b3fec..b5a59f4d0ba5705a065b2477e6c94d479e86fa31 100644 (file)
@@ -710,4 +710,4 @@ f4 (char *x, char **y, int z, char w[64])
   return z;
 }
 
-/* { dg-prune-output "\[\n\r\]*will always overflow\[\n\r\]*" } */
+/* { dg-prune-output "\[\n\r\]*writing\[\n\r\]*" } */
index 2991718ad3a57f96d42d630dbbd664e009ba4b31..2544eb14292a62620cf41410ba86798a3bbea525 100644 (file)
@@ -1,4 +1,9 @@
 /* { dg-do compile } */
+/* { dg-additional-options "-Wno-stringop-overflow" } */
+/* The loop below writes past the end of the global object a.
+   When the loop is transformed into a call to memcpy the buffer
+   overflow is detected and diagnosed by the -Wstringop-overflow
+   option enabled by default.  */
 
 typedef unsigned size_t;
 struct {
index fae584eae1ef5f9a1c2eb9d935194751eeef645f..a551e23c25154222cc88df5df2e63ee6a2d19b35 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-std=c99 -Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */
+/* { dg-options "-Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */
 /* { dg-require-effective-target int32plus } */
 
 /* When debugging, define LINE to the line number of the test case to exercise
@@ -1456,9 +1456,7 @@ void test_vsprintf_chk_int (__builtin_va_list va)
 
 void test_snprintf_c_const (char *d)
 {
-  T (-1, "%c",    0);            /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
-
-  __builtin_snprintf (d, INT_MAX, "%c", 0);   /* { dg-warning "specified destination size 2147483647 is too large" "ilp32" { target { ilp32 } } } */
+  T (-1, "%c",    0);            /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
   /* Verify the full text of the diagnostic for just the distinct messages
      and use abbreviations in subsequent test cases.  */
@@ -1506,9 +1504,9 @@ void test_snprintf_chk_c_const (void)
   /* Verify that specifying a size of the destination buffer that's
      bigger than its actual size (normally determined and passed to
      the function by __builtin_object_size) is diagnosed.  */
-  __builtin___snprintf_chk (buffer, 3, 0, 2, " ");   /* { dg-warning "always overflow|specified size 3 exceeds the size 2 of the destination" } */
+  __builtin___snprintf_chk (buffer, 3, 0, 2, " ");   /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" } */
 
-  T (-1, "%c",    0);           /* { dg-warning "specified destination size \[^ \]* is too large" } */
+  T (-1, "%c",    0);           /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
   T (0, "%c",     0);
   T (0, "%c%c",   0, 0);
@@ -1619,7 +1617,7 @@ void test_vsprintf_int (__builtin_va_list va)
 
 void test_vsnprintf_s (__builtin_va_list va)
 {
-  T (-1, "%s");             /* { dg-warning "specified destination size \[^ \]* is too large" } */
+  T (-1, "%s");             /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
   T (0, "%s");
   T (1, "%s");
@@ -1642,9 +1640,9 @@ void test_vsnprintf_chk_s (__builtin_va_list va)
   /* Verify that specifying a size of the destination buffer that's
      bigger than its actual size (normally determined and passed to
      the function by __builtin_object_size) is diagnosed.  */
-  __builtin___vsnprintf_chk (buffer, 123, 0, 122, "%-s", va);   /* { dg-warning "always overflow|specified size 123 exceeds the size 122 of the destination object" } */
+  __builtin___vsnprintf_chk (buffer, 123, 0, 122, "%-s", va);   /* { dg-warning "specified bound 123 exceeds the size 122 of the destination" } */
 
-  __builtin___vsnprintf_chk (buffer, __SIZE_MAX__, 0, 2, "%-s", va);   /* { dg-warning "always overflow|destination size .\[0-9\]+. is too large" } */
+  __builtin___vsnprintf_chk (buffer, __SIZE_MAX__, 0, 2, "%-s", va);   /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
   T (0, "%s");
   T (1, "%s");
index 00176ed056e4183acee2374734ae757547e00fb4..57fea66d69e7c6c2f4a48b681f5862269c3d0426 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-std=c99 -O2 -Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */
+/* { dg-options "-O2 -Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */
 
 typedef __SIZE_TYPE__ size_t;
 
@@ -248,34 +248,34 @@ void test_too_large (char *d, int x, __builtin_va_list va)
   const size_t imax = __INT_MAX__;
   const size_t imax_p1 = imax + 1;
 
-  __builtin_snprintf (d, imax,    "%c", x);   /* { dg-warning "specified destination size \[0-9\]+ is too large" "INT_MAX" { target ilp32 } } */
-  __builtin_snprintf (d, imax_p1, "%c", x);   /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
-  /* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */
+  __builtin_snprintf (d, imax,    "%c", x);
+  __builtin_snprintf (d, imax_p1, "%c", x);   /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
+  /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" "INT_MAX + 1" { target { ilp32 } } .-1 } */
 
-  __builtin_vsnprintf (d, imax,    "%c", va);   /* { dg-warning "specified destination size \[0-9\]+ is too large" "INT_MAX" { target ilp32 } } */
-  __builtin_vsnprintf (d, imax_p1, "%c", va);   /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
-  /* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */
+  __builtin_vsnprintf (d, imax,    "%c", va);
+  __builtin_vsnprintf (d, imax_p1, "%c", va);   /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
+  /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" "INT_MAX + 1" { target { ilp32 } } .-1 } */
 
-  __builtin___snprintf_chk (d, imax,    0, imax,    "%c", x);   /* { dg-warning "specified destination size \[0-9\]+ is too large" "INT_MAX" { target ilp32 } } */
-  __builtin___snprintf_chk (d, imax_p1, 0, imax_p1, "%c", x);   /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
-  /* { dg-warning "specified destination size \[0-9\]+ is too large" "INT_MAX + 1" { target { ilp32 } } .-1 } */
+  __builtin___snprintf_chk (d, imax,    0, imax,    "%c", x);
+  __builtin___snprintf_chk (d, imax_p1, 0, imax_p1, "%c", x);   /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
+  /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" "INT_MAX + 1" { target { ilp32 } } .-1 } */
 
-  __builtin___vsnprintf_chk (d, imax,    0, imax,    "%c", va);   /* { dg-warning "specified destination size \[0-9\]+ is too large" "INT_MAX" { target ilp32 } } */
-  __builtin___vsnprintf_chk (d, imax_p1, 0, imax_p1, "%c", va);   /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
-  /* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */
+  __builtin___vsnprintf_chk (d, imax,    0, imax,    "%c", va);
+  __builtin___vsnprintf_chk (d, imax_p1, 0, imax_p1, "%c", va);   /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
+  /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" "INT_MAX + 1" { target { ilp32 } } .-1 } */
 
   const size_t ptrmax = __PTRDIFF_MAX__;
   const size_t ptrmax_m1 = ptrmax - 1;
 
-  __builtin_snprintf (d, ptrmax_m1, "%c", x);  /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */
-  __builtin_snprintf (d, ptrmax, "  %c", x);   /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
+  __builtin_snprintf (d, ptrmax_m1, "%c", x);  /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */
+  __builtin_snprintf (d, ptrmax, "  %c", x);   /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX" { target lp64 } } */
 
-  __builtin_vsnprintf (d, ptrmax_m1, "%c", va);   /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */
-  __builtin_vsnprintf (d, ptrmax,    "%c", va);   /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
+  __builtin_vsnprintf (d, ptrmax_m1, "%c", va);   /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */
+  __builtin_vsnprintf (d, ptrmax,    "%c", va);   /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX" { target lp64 } } */
 
-  __builtin___snprintf_chk (d, ptrmax_m1, 0, ptrmax_m1, "%c", x);   /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */
-  __builtin___snprintf_chk (d, ptrmax,    0, ptrmax,    "%c", x);   /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
+  __builtin___snprintf_chk (d, ptrmax_m1, 0, ptrmax_m1, "%c", x);   /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */
+  __builtin___snprintf_chk (d, ptrmax,    0, ptrmax,    "%c", x);   /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX" { target lp64 } } */
 
-  __builtin___vsnprintf_chk (d, ptrmax_m1, 0, ptrmax_m1, "%c", va);   /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */
-  __builtin___vsnprintf_chk (d, ptrmax,    0, ptrmax,    "%c", va);   /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
+  __builtin___vsnprintf_chk (d, ptrmax_m1, 0, ptrmax_m1, "%c", va);   /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */
+  __builtin___vsnprintf_chk (d, ptrmax,    0, ptrmax,    "%c", va);   /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX" { target lp64 } } */
 }
index 97f7fb4c076b6e7740901c9d98e90092fd99927e..6529a77ff0489a183762faa3a1436e877a05993a 100644 (file)
@@ -47,3 +47,6 @@
            y(1) = 'hello world'
         end subroutine
        end
+
+ ! Remove -Wstringop-overflow warnings.
+ ! { dg-prune-output "overflows the destination" }
index a8c4469f089be53c4a3c89ce4415c0088ac21e95..583bf901b4ef733867bece84e000dcfa22964775 100644 (file)
@@ -9,7 +9,7 @@
       ANER(1)='A   '
       ANER(2)='    '
       LINE=' '
-      LINE(78:80)='xyz'
+      LINE(78:80)='xyz'   ! { dg-warning "writing 3 bytes into a region of size 2" }
       WRITE(*,'(A82)') "'"//LINE//"'"
       END