diff mbox

enhance buffer overflow warnings (and c/53562)

Message ID ecb4cc4a-1ffc-3372-3754-1cecc21a1954@gmail.com
State New
Headers show

Commit Message

Martin Sebor Oct. 28, 2016, 2:19 a.m. UTC
The attached patch enhances the compile-time detection of buffer
overflow in functions like __builtin___memcpy_chk to consider
non-constant lengths known to be in a certain range and warn
when the lower bound of the range doesn't fit in the destination
object.

The patch does the same thing for the non-checking functions like
__builtin_memcpy and issues buffer overflow warnings for those.
For string functions like __builtin_strcpy, the patch also makes
use of ranges of lengths of non-constant strings.

To make reasoning about the warnings easier (and to help with
debugging the problems), the patch also extends the warnings to
print the ranges of lengths and sizes of the operands.  The text
and content of the warning messages is based on those issued by
the -Wformat-length warning pass.

Finally, as requested in bug 53562, the patch adds a new warning
option, -Wstringop-overflow, to control these warnings (the option
is on by default).  I chose a different name for the option than
suggested in the bug to avoid giving the impression that it
actually inserts the checking calls (all it does is warn on
buffer overflows detectable at compile-time).

I was originally going to submit a more modest version of this
patch as part of a bigger project I'm working on (bug 77608) but
then decided to submit this one first because it's independent of
the other.

Possible enhancements include letting the option accept a level
argument and at level 2 using the upper bound of the size or
string length ranges similarly to the -Wformat-length option.
With that, the following could be diagnosed as a potential
buffer overflow:

   char d[5];
   strcpy (d, x ? "123" : "123456");

Thanks
Martin
diff mbox

Patch

PR c/53562 - Add -Werror= support for -D_FORTIFY_SOURCE / __builtin___memcpy_chk

gcc/ChangeLog:
2016-10-27  Martin Sebor  <msebor@redhat.com>

	PR c/53562
	* builtins.c (check_sizes): New function.
	(expand_builtin_memcpy): Call it.
	(expand_builtin_mempcpy): Same.
	(expand_builtin_strcpy): Same.
	(expand_builtin_memset): Same.
	(expand_builtin_bzero): Same.
	(expand_builtin_memory_chk): Same.
	(maybe_emit_sprintf_chk_warning): Same.
	* doc/invoke.texi (Warning Options): Document -Wstringop-overflow.

gcc/c-family/ChangeLog:
2016-10-27  Martin Sebor  <msebor@redhat.com>

	PR c/53562
	* c.opt (-Wstringop-overflow): New option.

gcc/testsuite/ChangeLog:
2016-10-27  Martin Sebor  <msebor@redhat.com>

	PR c/53562
	* gcc.dg/builtin-stringop-chk-4.c: New test.
	* c-c++-common/Wsizeof-pointer-memaccess2.c: Adjust.
	* g++.dg/ext/builtin-object-size3.C: Same.
	* g++.dg/ext/strncpy-chk1.C: Same.
	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
	* gcc.c-torture/execute/builtins/lib/chk.c: Same.
	* gcc.c-torture/execute/builtins/mempcpy-chk.c: Same.
	* gcc.dg/Wobjsize-1.c: Same.
	* gcc.dg/attr-alloc_size.c (test): Same.
	* gcc.dg/builtin-object-size-1.c: Same.
	* gcc.dg/builtin-object-size-5.c: Same.
	* gcc.dg/builtin-stringop-chk-1.c: Same.
	* gcc.dg/builtin-stringop-chk-2.c: Same.
	* gcc.dg/builtin-strncat-chk-1.c: Same.
	* gcc.dg/memcpy-2.c: Same.
	* gcc.dg/pr40340-1.c: Same.
	* gcc.dg/pr40340-2.c: Same.
	* gcc.dg/pr40340-5.c: Same.
	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Same.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index facecd3..d1aef0a 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -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
@@ -2967,6 +2967,133 @@  expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
   return dest_addr;
 }
 
+/* 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).
+   N is the user-supplied length of the source sequence (such as in
+   strncpy(d, s, N).
+   LEN is the length of the source sequence (such as in strcpy(d, s),
+   LEN = strlen(s)).
+   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 verfified as safe from buffer overflow
+   the function returns EXP.  Otherwise, if the call is determined to
+   overflow the function returns error_mark_node.  Otherwise, if
+   the call cannot be verified, the function returns NULL.  */
+
+static tree
+check_sizes (tree exp, tree size, tree n, tree len, tree objsize)
+{
+  tree minlen = NULL_TREE;
+  tree maxlen = NULL_TREE;
+
+  if (!size)
+    size = objsize;
+
+  if (!len)
+    len = size_zero_node;
+
+  if (tree_fits_uhwi_p (len))
+    minlen = maxlen = len;
+  else if (TREE_CODE (len) == SSA_NAME)
+    {
+      wide_int min, max;
+      enum value_range_type range_type = get_range_info (len, &min, &max);
+
+      if (range_type == VR_RANGE)
+	{
+	  /* Interpret the bounds in the variable's type.  */
+	  minlen = wide_int_to_tree (TREE_TYPE (len), min);
+	  maxlen = wide_int_to_tree (TREE_TYPE (len), max);
+	}
+      else if (range_type == VR_ANTI_RANGE)
+	{
+	  min = min - 1;
+	  max = max + 1;
+	  minlen = wide_int_to_tree (TREE_TYPE (len), min);
+	  maxlen = wide_int_to_tree (TREE_TYPE (len), max);
+	}
+    }
+
+  /* The SIZE is known if it's non-null, constant, and in range
+     of unsigned HOST_WIDE_INT.  */
+  bool knownsize = size && tree_fits_uhwi_p (size);
+  if (minlen || !knownsize || integer_all_onesp (size))
+    {
+      if ((!knownsize &&
+	   minlen && tree_int_cst_lt (TYPE_MAX_VALUE (ssizetype), minlen))
+	  || (knownsize
+	      && (tree_int_cst_lt (size, minlen))))
+	{
+	  unsigned HOST_WIDE_INT uwilen = tree_to_uhwi (minlen);
+
+	  location_t loc = tree_nonartificial_location (exp);
+
+	  if (!knownsize)
+	    warning_at (loc, OPT_Wstringop_overflow,
+			(minlen == maxlen
+			 ? G_("%K%D writing %wu bytes overflows "
+			      "the destination")
+			 : G_("%K%D writing between %wu and %wu bytes "
+			      "overflows the destination")),
+			exp, get_callee_fndecl (exp), uwilen,
+			tree_to_uhwi (maxlen));
+	  else if (minlen == maxlen)
+	    warning_at (loc, OPT_Wstringop_overflow,
+			(uwilen == 1
+			 ? G_("%K%D writing %wu byte into a region "
+			      "of size %wu overflows the destination")
+			 : G_("%K%D writing %wu bytes into a region "
+			      "of size %wu overflows the destination")),
+			exp, get_callee_fndecl (exp), uwilen,
+			tree_to_uhwi (size));
+	  else
+	    warning_at (loc, OPT_Wstringop_overflow,
+			(ubvflow
+			 ? G_("%K%D writing between %wu and %wu bytes "
+			      "into a region of size %wu may overflow "
+			      "the destination")
+			 : G_("%K%D writing between %wu and %wu bytes "
+			      "into a region of size %wu overflows "
+			      "the destination")),
+			exp, get_callee_fndecl (exp), uwilen,
+			tree_to_uhwi (maxlen), tree_to_uhwi (size));
+
+	  /* Return error when an overflow has been detected.  */
+	  return error_mark_node;
+	}
+
+      if (!knownsize)
+	return error_mark_node;
+    }
+
+  if (size == objsize && n)
+    size = n;
+
+  if (size && objsize && tree_fits_uhwi_p (size)
+      && tree_int_cst_lt (objsize, size))
+    {
+      location_t loc = tree_nonartificial_location (exp);
+
+      warning_at (loc, OPT_Wstringop_overflow,
+		  "specified size %wu exceeds the size %wu "
+		  "of the destination object",
+		  tree_to_uhwi (size), tree_to_uhwi (objsize));
+    }
+
+  return NULL_TREE;
+}
+
 /* 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
@@ -2983,6 +3110,14 @@  expand_builtin_memcpy (tree exp, rtx target)
       tree dest = CALL_EXPR_ARG (exp, 0);
       tree src = CALL_EXPR_ARG (exp, 1);
       tree len = CALL_EXPR_ARG (exp, 2);
+
+      tree objsize = NULL_TREE;
+      unsigned HOST_WIDE_INT uhwisize;
+      if (compute_builtin_object_size (dest, 1, &uhwisize))
+	objsize = build_int_cst (sizetype, uhwisize);
+
+      check_sizes (exp, NULL_TREE, NULL_TREE, len, objsize);
+
       return expand_builtin_memcpy_args (dest, src, len, target, exp);
     }
 }
@@ -3037,10 +3172,22 @@  expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode)
       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 destsize = NULL_TREE;
+      unsigned HOST_WIDE_INT objsize;
+      if (compute_builtin_object_size (dest, 1, &objsize))
+	destsize = build_int_cst (sizetype, objsize);
+
+      /* 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.  */
+      tree ret = check_sizes (exp, NULL_TREE, NULL_TREE, len, destsize);
+      if (ret != error_mark_node)
+	return expand_builtin_mempcpy_args (dest, src, len,
+					    target, mode, /*endp=*/ 1,
+					    exp);
     }
+  return NULL_RTX;
 }
 
 /* Expand an instrumented call EXP to the mempcpy builtin.
@@ -3224,6 +3371,30 @@  expand_builtin_strcpy (tree exp, rtx target)
    {
      tree dest = CALL_EXPR_ARG (exp, 0);
      tree src = CALL_EXPR_ARG (exp, 1);
+
+     /* 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.  At a (future) stricter warning setting the longest length
+	should be used instead.  */
+     if (lenrange[0])
+       {
+	 /* Try to determine the size of the destination object into
+	    which the source is being copied.  */
+	 tree destsize = NULL_TREE;
+	 unsigned HOST_WIDE_INT objsize;
+	 if (compute_builtin_object_size (dest, 1, &objsize))
+	   destsize = build_int_cst (sizetype, objsize);
+
+	 /* Add one for the terminating nul.  */
+	 tree len = fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+				 size_one_node);
+	 check_sizes (exp, NULL_TREE, NULL_TREE, len, destsize);
+       }
+
      return expand_builtin_strcpy_args (dest, src, target);
    }
    return NULL_RTX;
@@ -3442,6 +3613,15 @@  expand_builtin_memset (tree exp, rtx target, machine_mode mode)
       tree dest = CALL_EXPR_ARG (exp, 0);
       tree val = CALL_EXPR_ARG (exp, 1);
       tree len = CALL_EXPR_ARG (exp, 2);
+
+      /* Try to determine the size of the destination object.  */
+      tree objsize = NULL_TREE;
+      unsigned HOST_WIDE_INT uhwisize;
+      if (compute_builtin_object_size (dest, 1, &uhwisize))
+	objsize = build_int_cst (sizetype, uhwisize);
+
+      check_sizes (exp, NULL_TREE, NULL_TREE, len, objsize);
+
       return expand_builtin_memset_args (dest, val, len, target, mode, exp);
     }
 }
@@ -3633,6 +3813,14 @@  expand_builtin_bzero (tree exp)
   dest = CALL_EXPR_ARG (exp, 0);
   size = CALL_EXPR_ARG (exp, 1);
 
+  /* Try to determine the size of the destination object.  */
+  tree objsize = NULL_TREE;
+  unsigned HOST_WIDE_INT uhwisize;
+  if (compute_builtin_object_size (dest, 1, &uhwisize))
+    objsize = build_int_cst (sizetype, uhwisize);
+
+  check_sizes (exp, NULL_TREE, NULL_TREE, size, objsize);
+
   /* 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
@@ -9212,22 +9408,15 @@  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))
-    return NULL_RTX;
-
-  if (tree_fits_uhwi_p (len) || integer_all_onesp (size))
+  if (tree ret = check_sizes (exp, NULL_TREE, NULL_TREE, len, 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 (ret == error_mark_node)
+	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)
@@ -9313,68 +9502,96 @@  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 length of the source sequence of the memory operation, and
+     the size of the destination object.  */
+  tree srclen = NULL_TREE;
+  tree objsize = NULL_TREE;
+  /* The length of the sequence that the source sequence is being
+     concatenated with (as with __strcat_chk) or null if it isn't.  */
+  tree catlen = 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.  */
+      srclen = 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.  */
+      catlen = CALL_EXPR_ARG (exp, 0);
+      srclen = CALL_EXPR_ARG (exp, 1);
+      objsize = CALL_EXPR_ARG (exp, 2);
       break;
+
     case BUILT_IN_STRNCAT_CHK:
+      catlen = CALL_EXPR_ARG (exp, 0);
+      srclen = 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);
+      srclen = 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;
+  /* When the type of SRCLEN is a pointer compute the length of string
+     the pointer points to.  Otherwise it is an integer that gives the
+     upper bound on the length of the operation (as in __strncpy_chk).  */
+  if (srclen && TREE_CODE (TREE_TYPE (srclen)) == POINTER_TYPE)
+    srclen = c_strlen (srclen, 1);
 
-  if (is_strlen)
-    {
-      len = c_strlen (len, 1);
-      if (! len || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size))
-	return;
-    }
-  else if (fcode == BUILT_IN_STRNCAT_CHK)
+  if (catlen)
     {
-      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))
+      if ((!srclen || !tree_fits_uhwi_p (srclen))
+	  && (!maxlen
+	      || (tree_fits_uhwi_p (maxlen)
+		  && tree_fits_uhwi_p (objsize)
+		  && !tree_int_cst_lt (maxlen, objsize))))
 	{
+	  location_t loc = tree_nonartificial_location (exp);
+
 	  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))
+
+      if (!srclen)
 	return;
+
+      catlen = c_strlen (catlen, 1);
+      if (catlen)
+	srclen = fold_build2 (PLUS_EXPR, TREE_TYPE (srclen), srclen, catlen);
+    }
+
+  if (srclen)
+    {
+      srclen = fold_build2 (PLUS_EXPR, TREE_TYPE (srclen), srclen,
+			    size_one_node);
+      if (maxlen && tree_fits_uhwi_p (maxlen)
+	  && tree_int_cst_lt (maxlen, srclen))
+	srclen = maxlen;
     }
-  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 (exp, NULL_TREE, maxlen, srclen, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9428,10 +9645,9 @@  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 (exp, NULL_TREE, NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index e146781..6635464 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -647,6 +647,10 @@  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++ Var(warn_stringop_overflow) Init(1) Warning
+Warn about buffer overflow in string manipulation functions.
+
 Wsuggest-attribute=format
 C ObjC C++ ObjC++ Var(warn_suggest_attribute_format) Warning
 Warn about functions which might be candidates for format attributes.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d9667e7..81cbe63 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -299,6 +299,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 @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
@@ -4831,6 +4832,27 @@  comparisons, so this warning level gives a very large number of
 false positives.
 @end table
 
+@item -Wstringop-overflow
+@opindex Wstringop-overflow
+@opindex Wno-stringop-overflow
+Warn for calls to string manipulation functions such as memcpy and strcpy
+hat are determined to overflow the destination buffer.  The option works
+best with optimization enabled but it can detect a small subset of buffer
+overflows even without optimization.  In any case, the option warns about
+just a subset of buffer overflows detected by the corresponding overflow
+checking built-ins.  @xref{Object Size Checking}.  For example,
+@option{-Wstringop-overflow} will warn on the following:
+
+@smallexample
+char buf[6];
+void f (int i)
+@{
+  strcpy (buf, i < 0 ? "yellow" : "orange");
+@};
+@end smallexample
+
+The @option{-Wstringop-overflow} option is enabled by default.
+
 @item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]}
 @opindex Wsuggest-attribute=
 @opindex Wno-suggest-attribute=
diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
index d9ec7e2..9a02373 100644
--- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
+++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
@@ -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\]*" } */
diff --git a/gcc/testsuite/g++.dg/ext/builtin-object-size3.C b/gcc/testsuite/g++.dg/ext/builtin-object-size3.C
index 09263e5..0207f9a 100644
--- a/gcc/testsuite/g++.dg/ext/builtin-object-size3.C
+++ b/gcc/testsuite/g++.dg/ext/builtin-object-size3.C
@@ -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);
 }
diff --git a/gcc/testsuite/g++.dg/ext/strncpy-chk1.C b/gcc/testsuite/g++.dg/ext/strncpy-chk1.C
index ebafc99..85b3977 100644
--- a/gcc/testsuite/g++.dg/ext/strncpy-chk1.C
+++ b/gcc/testsuite/g++.dg/ext/strncpy-chk1.C
@@ -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 "overflows" }
 }
 
 void bar (const char *, int);
diff --git a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
index 8b5c33e..2e6189b 100644
--- a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
+++ b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
@@ -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\]*" }
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c
index b19d7bf..d2ea835 100644
--- a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c
+++ b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c
@@ -5,6 +5,11 @@ 
 
 extern void abort (void);
 
+#define abort() \
+  (__builtin_printf ("failure at %s:%i: %s\n",		\
+		     __FILE__, __LINE__, __FUNCTION__), \
+   __builtin_abort ())
+
 extern int inside_main;
 void *chk_fail_buf[256] __attribute__((aligned (16)));
 volatile int chk_fail_allowed, chk_calls;
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c
index 7a1737c..f8cb0f4 100644
--- a/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c
+++ b/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c
@@ -17,6 +17,8 @@  volatile char *s2 = "defg"; /* prevent constant propagation to happen when whole
 volatile char *s3 = "FGH"; /* prevent constant propagation to happen when whole program assumptions are made.  */
 volatile size_t l1 = 1; /* prevent constant propagation to happen when whole program assumptions are made.  */
 
+#define abort() (__builtin_printf ("failure on line %i\n", __LINE__), __builtin_abort ())
+
 void
 __attribute__((noinline))
 test1 (void)
@@ -326,6 +328,8 @@  test4 (void)
   char buf3[20];
 
   chk_fail_allowed = 1;
+  mempcpy_disallowed = 0;
+
   /* Runtime checks.  */
   if (__builtin_setjmp (chk_fail_buf) == 0)
     {
@@ -343,7 +347,9 @@  test4 (void)
       vx = mempcpy (&buf3[19], "ab", 2);
       abort ();
     }
+
   chk_fail_allowed = 0;
+  mempcpy_disallowed = 1;
 }
 
 #ifndef MAX_OFFSET
diff --git a/gcc/testsuite/gcc.dg/Wobjsize-1.c b/gcc/testsuite/gcc.dg/Wobjsize-1.c
index 291cfb9..211e068 100644
--- a/gcc/testsuite/gcc.dg/Wobjsize-1.c
+++ b/gcc/testsuite/gcc.dg/Wobjsize-1.c
@@ -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 } */
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size.c b/gcc/testsuite/gcc.dg/attr-alloc_size.c
index e8129ce..f50ba7c 100644
--- a/gcc/testsuite/gcc.dg/attr-alloc_size.c
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size.c
@@ -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" } */
 }
 
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 8cdae49..2919503 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -12,6 +12,8 @@  extern void *memcpy (void *, const void *, size_t);
 extern void *memset (void *, int, size_t);
 extern char *strcpy (char *, const char *);
 
+#define abort() (__builtin_printf ("failure in %s on line %i\n", __func__, __LINE__), __builtin_abort ())
+
 struct A
 {
   char a[10];
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
index 7c274cd..35f1350 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
@@ -2,7 +2,10 @@ 
 /* { dg-options "-O2" } */
 
 typedef __SIZE_TYPE__ size_t;
-extern void abort (void);
+extern void abort1 (void);
+extern void abort2 (void);
+extern void abort3 (void);
+extern void abort4 (void);
 extern char buf[0x40000000];
 
 void
@@ -14,7 +17,7 @@  test1 (size_t x)
   for (i = 0; i < x; ++i)
     p = p + 4;
   if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
-    abort ();
+    abort1 ();
 }
 
 void
@@ -26,7 +29,7 @@  test2 (size_t x)
   for (i = 0; i < x; ++i)
     p = p + 4;
   if (__builtin_object_size (p, 1) != sizeof (buf) - 8)
-    abort ();
+    abort2 ();
 }
 
 void
@@ -38,7 +41,7 @@  test3 (size_t x)
   for (i = 0; i < x; ++i)
     p = p + 4;
   if (__builtin_object_size (p, 2) != 0)
-    abort ();
+    abort3 ();
 }
 
 void
@@ -50,7 +53,7 @@  test4 (size_t x)
   for (i = 0; i < x; ++i)
     p = p + 4;
   if (__builtin_object_size (p, 3) != 0)
-    abort ();
+    abort4 ();
 }
 
 /* { dg-final { scan-assembler-not "abort" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
index e491ff5..49e7f95 100644
--- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
@@ -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 size 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 "writing 3 " "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 size 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 size 3 exceeds the size 2 of the destination object" "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 size 3 exceeds the size 1 of the destination object" "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" } */
 }
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c
index 7c2bb60..d537fb0 100644
--- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c
@@ -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
index 0000000..4e12193
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c
@@ -0,0 +1,145 @@ 
+/* Test exercising buffer overflow warnings emitted for __*_chk builtins
+   in cases where the destination involves a non-constant offset into
+   an object of known size.  */
+/* { 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;
+
+#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 strcpy(d, s) (strcpy ((d), (s)), sink ((d)))
+extern char* (strcpy)(char*, const char*);
+
+void sink (void*);
+
+/* Function to "generate" a unique 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.  */
+ptrdiff_t random_value (void);
+
+/* Return a random value between MIN and MAX.  */
+static inline size_t
+value_range (size_t min, size_t max)
+{
+  const size_t val = random_value ();
+  return val < min || max < val ? min : val;
+}
+
+/* For brevity.  */
+#define R(min, max)   value_range (min, max)
+
+/* Test memcpy with a number of bytes bounded by a known range.  */
+
+void test_memcpy_range (const void *p)
+{
+  char buf[5];
+
+  memcpy (buf, p, R (0, 5));
+  memcpy (buf, p, R (1, 5));
+  memcpy (buf, p, R (2, 5));
+  memcpy (buf, p, R (3, 5));
+  memcpy (buf, p, R (4, 5));
+
+  memcpy (buf, p, R (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+  const size_t max = SIZE_MAX;
+  memcpy (buf, p, R (max - 1, max));  /* { dg-warning "writing" } */
+
+  memcpy (buf, p, R (max / 2 + 1, max));  /* { dg-warning "writing" } */
+}
+
+/* Test mempcpy with a number of bytes bounded by a known range.  */
+
+void test_mempcpy_range (const void *p)
+{
+  char buf[5];
+
+  mempcpy (buf, p, R (0, 5));
+  mempcpy (buf, p, R (1, 5));
+  mempcpy (buf, p, R (2, 5));
+  mempcpy (buf, p, R (3, 5));
+  mempcpy (buf, p, R (4, 5));
+
+  mempcpy (buf, p, R (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+  const size_t max = SIZE_MAX;
+  mempcpy (buf, p, R (max - 1, max));  /* { dg-warning "writing" } */
+
+  mempcpy (buf, p, R (max / 2 + 1, max));  /* { dg-warning "writing" } */
+}
+
+/* Test memset with a number of bytes bounded by a known range.  */
+
+void test_memset_range (const void *p)
+{
+  char buf[5];
+
+  memset (buf, 0, R (0, 5));
+  memset (buf, 1, R (1, 5));
+  memset (buf, 2, R (2, 5));
+  memset (buf, 3, R (3, 5));
+  memset (buf, 4, R (4, 5));
+
+  memset (buf, 5, R (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+  const size_t max = SIZE_MAX;
+  memset (buf, 6, R (max - 1, max));  /* { dg-warning "writing" } */
+
+  memset (buf, 7, R (max / 2 + 1, max));  /* { dg-warning "writing" } */
+}
+
+/* Return 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 = value_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)
+
+void test_strcpy_range (const char *s)
+{
+  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 " } */
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c b/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c
index 44677f1..4568680 100644
--- a/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c
@@ -24,11 +24,11 @@  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" } */
   *p = 0;
   strncat (p, "abcdefgh", 11);
   *p = 0;
-  strncat (p, "abcdefghijkl", 11); /* { dg-warning "will always overflow" } */
+  strncat (p, "abcdefghijkl", 11); /* { dg-warning "writing 11 bytes" } */
   *p = 0;
   strncat (p, q, 9);
   *p = 0;
diff --git a/gcc/testsuite/gcc.dg/memcpy-2.c b/gcc/testsuite/gcc.dg/memcpy-2.c
index 24464abd..7f839d2 100644
--- a/gcc/testsuite/gcc.dg/memcpy-2.c
+++ b/gcc/testsuite/gcc.dg/memcpy-2.c
@@ -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));
 }
 
diff --git a/gcc/testsuite/gcc.dg/pr40340-1.c b/gcc/testsuite/gcc.dg/pr40340-1.c
index aae84c6..78540a2 100644
--- a/gcc/testsuite/gcc.dg/pr40340-1.c
+++ b/gcc/testsuite/gcc.dg/pr40340-1.c
@@ -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 } */
diff --git a/gcc/testsuite/gcc.dg/pr40340-2.c b/gcc/testsuite/gcc.dg/pr40340-2.c
index a0d6e084..1dc21d1 100644
--- a/gcc/testsuite/gcc.dg/pr40340-2.c
+++ b/gcc/testsuite/gcc.dg/pr40340-2.c
@@ -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 } */
diff --git a/gcc/testsuite/gcc.dg/pr40340-5.c b/gcc/testsuite/gcc.dg/pr40340-5.c
index f50514c..e517147 100644
--- a/gcc/testsuite/gcc.dg/pr40340-5.c
+++ b/gcc/testsuite/gcc.dg/pr40340-5.c
@@ -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 } */
diff --git a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
index 7ce9eae..b5a59f4 100644
--- a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
@@ -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\]*" } */