diff mbox

enhance buffer overflow warnings (and c/53562)

Message ID 904d9d3b-8662-e714-cc82-e08c72c54c0e@gmail.com
State Superseded
Headers show

Commit Message

Martin Sebor Nov. 1, 2016, 2:39 a.m. UTC
Attached is an updated patch that works around the problem with
the definition of the NOTE_DATA macro discussed below.  I've
raised bug 78174 for it and temporarily worked around it in
the patch.  I'll see if I can come up with a patch to fix the
macro the "right way" but would prefer to do that separately.
The NOTE_DATA macro is implemented in terms of the RTL_CHECK1
macro that will need to change and that macro is used in many
others, so I would rather not mess around with those as part
of this patch.

I tested the updated patch with today's top of trunk on x86_64.
The reason why I missed the problem in the previous patch is
because I accidentally tested an earlier version of it.

In following up on bug 78174 it occurred to me that the warning
option added by the patch for the non-checking functions could
perhaps be enhanced to specify the object size checking type
(right now it hardcodes type 1 for all non-checking functions).
That would make it possible to request looser checking for some
functions (e.g., memset, for compatibility with Glibc's setting
of zero even at _FORTIFY_SOURCE=2), and stricter checking for
others (e.g., strcpy where Glibc uses type-1 with
_FORTIFY_SOURCE=2).  If this is something people would think
would be worthwhile please let me know.

Martin

On 10/31/2016 10:44 AM, Martin Sebor wrote:
> On 10/31/2016 06:39 AM, Tobias Burnus wrote:

>> Martin Sebor wrote:

>>> Attached is an updated patch that adds checks for excessive sizes

>>> and bounds (those in excess of SIZE_MAX / 2), and also enables

>>> the same checking for strcat and strncat).  This version also

>>> fixes an issue with the interpretation of anti-ranges in the

>>> first patch.  The improvements exposed two bugs in the regression

>>> tests.

>>

>> If I apply this patch to my local trunk - and try to bootstrap GCC,

>> bootstrapping fails (on x86-64_gnu-linux) as following. I have not

>> tried to figure out whether the warning (-Werror) makes sense or not.

>>

>> ../../gcc/emit-rtl.c: In function ‘rtx_note* make_note_raw(insn_note)’:

>> ../../gcc/emit-rtl.c:3933:59: error: void* memset(void*, int, size_t)

>> writing 8 bytes into a region of size 0 overflows the destination

>> [-Werror=stringop-overflow]

>>    memset (&NOTE_DATA (note), 0, sizeof (NOTE_DATA (note)));

>>

>>

>> where NOTE_DATA is defined in rtl.h as

>>

>> /* Opaque data.  */

>> #define NOTE_DATA(INSN)         RTL_CHECKC1 (INSN, 3, NOTE)

>

> Thanks for trying it out!  The patch bootstrapped and passed regression

> tests for me yesterday, also on x86_64, and today on powepc64le, but

> just now I reproduced the error with the top of today's trunk on

> x86_64.  I think the error is justified because the call to memset

> in the make_note_raw function references a fourth element

> (rtx_note::rtx_insn::rtx_def::u.fld[3]) of what is just a single

> element array.  After macro expansion, the memset call itself:

>

>   memset (&((note)->u.fld[3]), 0, sizeof (((note)->u.fld[3])));

>

> is within the bounds of the object pointed to by note but the index

> 3 is out of bounds for note->u.fld and so undefined.  An unpatched

> GCC issues a similar error when the call to memset is replaced with

> __builtin___memset_chk like so:

>

>   __builtin___memset_chk (&((note)->u.fld[3]), 0,

>                           sizeof (((note)->u.fld[3])),

>               __builtin_object_size (&((note)->u.fld[3]), 1));

>

> /src/gcc/53562/gcc/emit-rtl.c: In function ‘rtx_note*

> make_note_raw(insn_note)’:

> /src/gcc/53562/gcc/emit-rtl.c:3941:53: warning: call to void*

> __builtin___memset_chk(void*, int, long unsigned int, long unsigned int)

> will always overflow destination buffer

>

> The code can be made valid and the warning avoided by deriving

> the same address not from an invalid pointer/subscript but rather

> from a pointer to the beginning of the union itself and adding

> the offset of the fourth element, like so:

>

>   char *p = (char*) &(note)->u.fld[0];

>   p += sizeof (((note)->u.fld[0])) * 3;

>   memset (p, 0, sizeof *p);

>

> Unfortunately, because the invalid subscript is the result of

> the expansion of the RTL_CHECK1() macro fixing this isn't as

> straightforward as that.  What really should be fixed is the macro

> itself.  Until then, it can be worked (hacked would be a better

> term) around it by storing the result of &NOTE_DATA (note)

> expression in a volatile pointer, like so:

>

> diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c

> index 8afcfbe..6dd9439 100644

> --- a/gcc/emit-rtl.c

> +++ b/gcc/emit-rtl.c

> @@ -3930,7 +3930,8 @@ make_note_raw (enum insn_note subtype)

>    INSN_UID (note) = cur_insn_uid++;

>    NOTE_KIND (note) = subtype;

>    BLOCK_FOR_INSN (note) = NULL;

> -  memset (&NOTE_DATA (note), 0, sizeof (NOTE_DATA (note)));

> +  void* volatile p = &NOTE_DATA (note);

> +  memset (p, 0, sizeof (NOTE_DATA (note)));

>    return note;

>  }

>

> Martin

Comments

Jakub Jelinek Nov. 1, 2016, 2:10 p.m. UTC | #1
On Mon, Oct 31, 2016 at 08:39:15PM -0600, Martin Sebor wrote:
> Attached is an updated patch that works around the problem with

> the definition of the NOTE_DATA macro discussed below.  I've

> raised bug 78174 for it and temporarily worked around it in

> the patch.  I'll see if I can come up with a patch to fix the

> macro the "right way" but would prefer to do that separately.

> The NOTE_DATA macro is implemented in terms of the RTL_CHECK1

> macro that will need to change and that macro is used in many

> others, so I would rather not mess around with those as part

> of this patch.


No, you just shouldn't use __bos (*, 1) in the warning code
for mem* like builtins.

	Jakub
Jeff Law Nov. 1, 2016, 7:12 p.m. UTC | #2
On 11/01/2016 08:10 AM, Jakub Jelinek wrote:
> On Mon, Oct 31, 2016 at 08:39:15PM -0600, Martin Sebor wrote:

>> Attached is an updated patch that works around the problem with

>> the definition of the NOTE_DATA macro discussed below.  I've

>> raised bug 78174 for it and temporarily worked around it in

>> the patch.  I'll see if I can come up with a patch to fix the

>> macro the "right way" but would prefer to do that separately.

>> The NOTE_DATA macro is implemented in terms of the RTL_CHECK1

>> macro that will need to change and that macro is used in many

>> others, so I would rather not mess around with those as part

>> of this patch.

>

> No, you just shouldn't use __bos (*, 1) in the warning code

> for mem* like builtins.

Can you explain to Martin why so that he can adjust accordingly?

jeff
Jakub Jelinek Nov. 1, 2016, 7:14 p.m. UTC | #3
On Tue, Nov 01, 2016 at 01:12:55PM -0600, Jeff Law wrote:
> On 11/01/2016 08:10 AM, Jakub Jelinek wrote:

> >On Mon, Oct 31, 2016 at 08:39:15PM -0600, Martin Sebor wrote:

> >>Attached is an updated patch that works around the problem with

> >>the definition of the NOTE_DATA macro discussed below.  I've

> >>raised bug 78174 for it and temporarily worked around it in

> >>the patch.  I'll see if I can come up with a patch to fix the

> >>macro the "right way" but would prefer to do that separately.

> >>The NOTE_DATA macro is implemented in terms of the RTL_CHECK1

> >>macro that will need to change and that macro is used in many

> >>others, so I would rather not mess around with those as part

> >>of this patch.

> >

> >No, you just shouldn't use __bos (*, 1) in the warning code

> >for mem* like builtins.

> Can you explain to Martin why so that he can adjust accordingly?


I've tried to explain that in
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78174#c7

	Jakub
diff mbox

Patch

PR c/53562 - Add -Werror= support for -D_FORTIFY_SOURCE / __builtin___memcpy_chk
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:
2016-10-30  Martin Sebor  <msebor@redhat.com>

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

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

	PR c/53562
	PR middle-end/78149
	PR middle-end/78138
	* builtins.c (expand_builtin_strcat, expand_builtin_strncat): New
	functions.
	(get_size_range, check_sizes, check_strncat_sizes): Same.
	(expand_builtin_memcpy): Call check sizes.
	(expand_builtin_mempcpy): Same.
	(expand_builtin_strcpy): Same.
	(expand_builtin_strncpy): Same.
	(expand_builtin_memset): Same,
	(expand_builtin_bzero): Same.
	(expand_builtin_memory_chk): Same.
	(maybe_emit_sprintf_chk_warning): Same.
	(expand_builtin): Handle strcat and strncat.
	* gcc/emit-rtl.c (make_note_raw): Temporarily work around a bug
	in the definition of the NOTE_DATA macro.
	* doc/invoke.texi (Warning Options): Document -Wstringop-overflow.

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

	PR c/53562
	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/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.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index facecd3..02f118c 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
@@ -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);
@@ -2967,6 +2969,211 @@  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)
+	{
+	  /* An anti-range implies the original variable is signed and
+	     its lower bound is negative and the upper bound positive.
+	     Since that means that the expression's value could be zero
+	     nothing interesting can be inferred from this.  */
+	}
+    }
+
+  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.
+   SLEN is the length of the source sequence (such as in strcpy(d, s),
+   SLEN = 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 true, otherwise false..  */
+
+static bool
+check_sizes (tree exp, tree size, tree maxlen, tree slen, tree objsize)
+{
+  /* The size of the largest object.  (This is way too permissive.)  */
+  tree maxobjsize = build_int_cst (sizetype, HOST_WIDE_INT_MAX);
+
+  tree range[2] = { NULL_TREE, NULL_TREE };
+
+  if (!size && !maxlen)
+    {
+      /* Handle strlen but not snprintf.  */
+      size = slen ? slen : maxobjsize;
+    }
+
+  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);
+
+  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_Wstringop_overflow,
+		    "%K%D 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_Wstringop_overflow,
+			"%K%D 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 (range[0] == range[1])
+	    warning_at (loc, OPT_Wstringop_overflow,
+			(uwir0 == 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), uwir0,
+			tree_to_uhwi (objsize));
+	  else
+	    warning_at (loc, OPT_Wstringop_overflow,
+			"%K%D 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))
+	{
+	  if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
+	    {
+	      location_t loc = tree_nonartificial_location (exp);
+
+	      if (range[0] == range[1])
+		warning_at (loc, OPT_Wstringop_overflow,
+			    "%K%D 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_Wstringop_overflow,
+			    "%K%D 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;
+	    }
+	  else if (tree_int_cst_lt (maxobjsize, range[0]))
+	    {
+	      location_t loc = tree_nonartificial_location (exp);
+
+	      if (range[0] == range[1])
+		warning_at (loc, OPT_Wstringop_overflow,
+			    "%K%D 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_Wstringop_overflow,
+			    "%K%D 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;
+	    }
+	}
+    }
+
+  return true;
+}
+
 /* 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 +3190,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, len, /*maxlen=*/NULL_TREE, NULL_TREE, objsize);
+
       return expand_builtin_memcpy_args (dest, src, len, target, exp);
     }
 }
@@ -3037,10 +3252,21 @@  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.  */
+      if (check_sizes (exp, len, NULL_TREE, NULL_TREE, destsize))
+	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.
@@ -3212,6 +3438,50 @@  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))
+    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 srouce string is longer than
+     the destination object.  */
+
+  /* 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 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
@@ -3224,6 +3494,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;
@@ -3334,6 +3628,130 @@  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)
+    {
+      /* 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.  */
+      unsigned HOST_WIDE_INT destsize;
+      if (compute_builtin_object_size (dest, 1, &destsize))
+	objsize = build_int_cst (sizetype, destsize);
+    }
+
+  /* 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 || 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 (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))
+    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.  */
+
+  /* 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 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 || 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 (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.  */
 
@@ -3347,9 +3765,39 @@  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);
 
+      /* 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.  At a (future) stricter warning setting the longest length
+	 should be used instead.  */
+
+      /* 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 srclen = (lenrange[0]
+		     ? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+				    size_one_node)
+		     : NULL_TREE);
+      /* The number of bytes to write is LEN but check_sizes will also
+	 check SRCLEN if LEN's value isn't known.  */
+      check_sizes (exp, len, /*maxlen=*/NULL_TREE, srclen, 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;
@@ -3442,6 +3890,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, len, NULL_TREE, NULL_TREE, objsize);
+
       return expand_builtin_memset_args (dest, val, len, target, mode, exp);
     }
 }
@@ -3633,6 +4090,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, size, NULL_TREE, NULL_TREE, 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
@@ -6119,12 +6584,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)
@@ -9212,22 +9689,20 @@  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 (exp, len, NULL_TREE, 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)
@@ -9313,68 +9788,84 @@  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;
+  /* Compute the length of shortest string the source pointer points to.  */
+  if (srclen)
+    {
+      tree lenrange[2];
+      get_range_strlen (srclen, lenrange);
+      srclen = lenrange[0];
+    }
 
-  if (is_strlen)
+  if (catlen && 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)
+
+  if (srclen)
     {
-      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;
+      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 +9919,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/emit-rtl.c b/gcc/emit-rtl.c
index 2d6d1eb..9de68d0 100644
--- a/gcc/emit-rtl.c
+++ b/gcc/emit-rtl.c
@@ -3929,7 +3929,14 @@  make_note_raw (enum insn_note subtype)
   INSN_UID (note) = cur_insn_uid++;
   NOTE_KIND (note) = subtype;
   BLOCK_FOR_INSN (note) = NULL;
-  memset (&NOTE_DATA (note), 0, sizeof (NOTE_DATA (note)));
+
+  /* FIXME: The NOTE_DATA() macro uses an invalid array subscript
+     that causes a -Wstringop-overflow warning in the call to memset
+     below.  The resulting pointer is valid but the way it's derived
+     is undefined.  Hide the pointer computation from memset to avoid
+     the warning until the macro is fixed.  */
+  void* volatile p = &NOTE_DATA (note);
+  memset (p, 0, sizeof (NOTE_DATA (note)));
   return note;
 }
 
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/compile/pr55569.c b/gcc/testsuite/gcc.c-torture/compile/pr55569.c
index cffbcfc..7708f21 100644
--- a/gcc/testsuite/gcc.c-torture/compile/pr55569.c
+++ b/gcc/testsuite/gcc.c-torture/compile/pr55569.c
@@ -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;
 }
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-stringop-chk-1.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
index e491ff5..7689287 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 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" } */
 }
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-3.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-3.c
new file mode 100644
index 0000000..3b0ba0f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-3.c
@@ -0,0 +1,344 @@ 
+/* 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;
+
+void sink (void*);
+
+/* Define memcpy as a macro (as opposed to an inline function) so that
+   warnings point to its invocation in the tests (as opposed to its
+   definition), making sure its first argument is evaluated exactly
+   once.  */
+#define memcpy(d, s, n)							\
+  do {									\
+    __typeof__ (d) __d = (d);						\
+    __builtin___memcpy_chk (__d, s, n, __builtin_object_size (__d, 1));	\
+    sink (__d);								\
+  } while (0)
+
+/* Function to generate a unique offset 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);
+
+/* For brevity. */
+#define X() random_value ()
+
+/* Test memcpy with a variable offset not known to be in any range
+   and with a constant number of bytes.  */
+
+void test_var_const (const void *p)
+{
+  char buf[5];
+
+  memcpy (buf + X(), p, 6);  /* { dg-warning "writing 6 bytes into a region of size 5" } */
+
+  memcpy (&buf[X()], p, 6);  /* { dg-warning "writing" } */
+
+  /* Since X() below can be assumed to be non-negative (otherwise it would
+     result in forming a pointer before the beginning of BUF), then because
+     of the +1 added to the resulting address there must be at most enough
+     room for (sizeof(buf) - 1) or 4 bytes.  */
+  memcpy (&buf[X()] + 1, p, 4);
+
+  memcpy (&buf[X()] + 1, p, 5);  /* { dg-warning "writing" } */
+
+  memcpy (&buf[X()] + 5, p, 6);  /* { dg-warning "writing" } */
+
+  /* The negative constant offset below must have no effect on the maximum
+     size of the buffer.  */
+  memcpy (&buf[X()] - 1, p, 5);
+
+  memcpy (&buf[X()] - 1, p, 6);  /* { dg-warning "writing" } */
+
+  memcpy (&buf[X()] - 5, p, 6);  /* { dg-warning "writing" } */
+
+  memcpy (&buf[X()] + X(), p, 5);
+
+  memcpy (&buf[X()] + X(), p, 6);  /* { dg-warning "writing" } */
+
+  memcpy (&buf[X()] - X(), p, 5);
+
+  memcpy (&buf[X()] - X(), p, 6);  /* { dg-warning "writing" } */
+}
+
+static inline size_t
+range (size_t min, size_t max)
+{
+  const size_t val = random_value ();
+  return val < min || max < val ? min : val;
+}
+
+/* Test memcpy with a variable offset not known to be in any range
+   and with a number of bytes bounded by a known range.  */
+
+void test_var_range (void *dst, const void *p)
+{
+  char buf[5];
+
+  memcpy (&buf[X()], p, range (0, 5));
+  memcpy (&buf[X()], p, range (1, 5));
+  memcpy (&buf[X()], p, range (2, 5));
+  memcpy (&buf[X()], p, range (3, 5));
+  memcpy (&buf[X()], p, range (4, 5));
+
+  memcpy (&buf[X()], p, range (6, 7));  /* { dg-warning "writing" } */
+
+  const size_t max = SIZE_MAX;
+  memcpy (&buf[X()], p, range (max - 1, max));  /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+
+  memcpy (dst, p, range (max / 2 + 1, max));  /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
+}
+
+/* Return an ingeger in the range [MIN, MASK].  Use bitwise operations
+   rather than inequality to avoid relying on optimization passes
+   beyond Early Value Range Propagation that __builtin_object_size
+   doesn't make use of (yet).  */
+
+static inline size_t
+simple_range (unsigned min, unsigned mask)
+{
+  return ((unsigned)random_value () & mask) | (min & mask);
+}
+
+/* For brevity. */
+#define R(min, max) simple_range (min, max)
+
+void test_range_auto (const void *p)
+{
+  char buf[5];
+
+  memcpy (buf + R (0, 1), p, 6);  /* { dg-warning "writing" } */
+
+  /* Some of these could be diagnosed as an extension because they would
+     overflow when (if) the non-const index were sufficiently large.
+     The challenge is distinguishing a range where a variable is likely
+     to exceed the minimum required for the overflow to occur from one
+     where it isn't so likely.  */
+  memcpy (buf + R (0, 1), p, 5);
+
+  memcpy (buf + R (0, 1), p, 4);
+
+  memcpy (buf + R (0, 2), p, 3);
+
+  memcpy (buf + R (0, 3), p, 2);
+
+  memcpy (buf + R (0, 7), p, 1);
+
+  memcpy (buf + R (0, 5), p, 0);
+
+  /* The offset is known to be at least 1 so the size of the object
+     is at most 4.  */
+  memcpy (buf + R (1, 2), p, 3);
+
+  memcpy (buf + R (1, 3), p, 3);
+
+  memcpy (buf + R (1, 2), p, 4);
+
+  memcpy (buf + R (1, 3) + 1, p, 4);  /* { dg-warning "writing" } */
+
+  memcpy (buf + R (1, 3), p, 5);  /* { dg-warning "writing" } */
+
+  memcpy (buf + R (2, 3), p, 2);
+
+  memcpy (buf + R (2, 3), p, 3);
+
+  memcpy (buf + R (2, 3) + 1, p, 3);  /* { dg-warning "writing" } */
+
+  memcpy (buf + R (2, 3), p, 4);  /* { dg-warning "writing" } */
+
+  memcpy (buf + R (3, 4), p, 2);
+
+  memcpy (buf + R (3, 7), p, 3);  /* { dg-warning "writing" } */
+
+  memcpy (buf + R (3, 7), p, 9);  /* { dg-warning "writing" } */
+
+  memcpy (&buf[R (0, 1)] + 1, p, 4);
+
+  memcpy (&buf[R (0, 2)] + 1, p, 4);
+
+  memcpy (&buf[R (0, 3)] + 1, p, 4);
+
+  memcpy (&buf[R (0, 4)] + 1, p, 4);
+
+  memcpy (&buf[R (0, 5)] + 1, p, 4);
+
+  memcpy (&buf[R (0, 2)] + 2, p, 4);  /* { dg-warning "writing" } */
+
+  memcpy (&buf[R (0, 2)] - 2, p, 3);
+  memcpy (&buf[R (0, 2)] - 2, p, 3);
+
+  memcpy (&buf[R (0, 2)] - 2, p, 3);
+
+  memcpy (&buf[R (5, 15)], p, 1);  /* { dg-warning "writing" } */
+
+  /* With the offset given by the two ranges below there is at most
+     1 byte left.  */
+  memcpy (buf + R (1, 2) + R (3, 4), p, 1);
+
+  memcpy (buf + R (1, 3) + R (3, 7), p, 2);   /* { dg-warning "writing" } */
+
+  /* Unfortunately, the following isn't handled quite right: only
+     the lower bound given by the first range is used, the second
+     one is disregarded.  */
+  memcpy (&buf [R (1, 2)] + R (3, 4), p, 2);   /* { dg-warning "writing" "index in range plus offset in range" { xfail *-*-* } } */
+
+  memcpy (buf + R (2, 3) + R (2, 3), p, 1);
+
+  memcpy (buf + R (2, 3) + R (2, 3), p, 2);   /* { dg-warning "writing" } */
+}
+
+void test_range_malloc (const void *p)
+{
+  char *buf = __builtin_malloc (5);
+
+  memcpy (buf + R (0, 1), p, 6);  /* { dg-warning "writing" } */
+
+  memcpy (buf + R (0, 1), p, 5);
+
+  memcpy (buf + R (0, 1), p, 4);
+
+  memcpy (buf + R (0, 2), p, 3);
+
+  memcpy (buf + R (0, 3), p, 2);
+
+  memcpy (buf + R (0, 4), p, 1);
+
+  memcpy (buf + R (0, 5), p, 0);
+}
+
+void test_range_schar (signed char i, const void *s)
+{
+  char a [130];
+
+  /* The range of I is [-128, 127] so the size of the destination below
+     is at most 2 (i.e., 130 - 128) bytes. */
+  memcpy (&a [130] + i, s, 2);
+
+  /* Strictly, the range of I below is [0, 127] because a negative value
+     would result in forming an invalid pointer, so the destination is at
+     most 2 bytes. */
+  memcpy (&a [i] + 128, s, 2);
+
+  /* Reset I's range just in case it gets set above.  */
+  i = random_value ();
+  memcpy (&a [130] + i, s, 3);   /* { dg-warning "writing" } */
+
+  i = random_value ();
+  memcpy (&a [i] + 128, s, 3);   /* { dg-warning "writing" } */
+}
+
+void test_range_uchar (unsigned char i, const void *s)
+{
+  char a [260];
+
+  /* The range of I is [0, 255] so the size of the destination below
+     is at most 2 (i.e., 260 - 258 + 0) bytes. */
+  memcpy (&a [258] + i, s, 2);
+
+  memcpy (&a [i] + 128, s, 2);
+
+  /* Reset I's range just in case it gets set above.  */
+  i = random_value ();
+  memcpy (&a [258] + i, s, 3);   /* { dg-warning "writing" } */
+
+  i = random_value ();
+  memcpy (&a [i] + 258, s, 3);   /* { dg-warning "writing" } */
+
+  i = random_value ();
+  memcpy (&a [259] + i, s, 2);   /* { dg-warning "writing" } */
+
+  i = random_value ();
+  memcpy (&a [i] + 259, s, 2);   /* { dg-warning "writing" } */
+
+  i = random_value ();
+  memcpy (&a [260] + i, s, 1);   /* { dg-warning "writing" } */
+
+  i = random_value ();
+  memcpy (&a [i] + 260, s, 1);   /* { dg-warning "writing" } */
+
+  i = random_value ();
+  memcpy (&a [260] + i, s, 0);
+
+  i = random_value ();
+  memcpy (&a [i] + 260, s, 0);
+}
+
+void test_range_int (int i, const void *s)
+{
+  const size_t max = (size_t)INT_MAX * 2 + 1;
+
+  char *a = __builtin_malloc (max);
+
+  memcpy (&a [max] + i, s, INT_MAX);
+  memcpy (&a [max] - i, s, INT_MAX);
+  /* &*/
+  memcpy (&a [max] + i, s, INT_MAX + (size_t)1);
+  memcpy (&a [max] - i, s, INT_MAX + (size_t)1); /* { dg-warning "writing" } */
+  memcpy (&a [max] + i, s, INT_MAX + (size_t)2); /* { dg-warning "writing" } */
+  memcpy (&a [max] - i, s, INT_MAX + (size_t)2); /* { dg-warning "writing" } */
+
+  char *end = &a [max];
+  memcpy (end + i, s, INT_MAX);
+  memcpy (end - i, s, INT_MAX);
+  /* &*/
+  memcpy (end + i, s, INT_MAX + (size_t)1);
+  memcpy (end - i, s, INT_MAX + (size_t)1); /* { dg-warning "writing" } */
+  memcpy (end + i, s, INT_MAX + (size_t)2); /* { dg-warning "writing" } */
+  memcpy (end - i, s, INT_MAX + (size_t)2); /* { dg-warning "writing" } */
+
+  memcpy (&a [i] + max, s, 1);
+}
+
+
+void test_range_ptrdiff_t (ptrdiff_t i, const void *s)
+{
+  const size_t max = PTRDIFF_MAX;
+
+  char *a = __builtin_malloc (max);
+
+  memcpy (&a [max] + i, s, max);
+
+  memcpy (&a [i] + max - 1, s, 1);
+}
+
+void test_range_size_t (size_t i, const void *s)
+{
+  const size_t diffmax = PTRDIFF_MAX;
+
+  char *a = __builtin_malloc (diffmax);
+
+  memcpy (&a [diffmax] + i, s, 0);
+  memcpy (&a [i] + diffmax, s, 0);
+
+  memcpy (&a [diffmax] + i, s, 1);  /* { dg-warning "writing" } */
+
+  memcpy (&a [i] + diffmax, s, 1);  /* { dg-warning "writing" } */
+}
+
+struct S {
+  int i;
+  char a7[7];
+  int b;
+};
+
+void test_range_member_array (struct S *s, const void *p)
+{
+  memcpy (s->a7 + R (0, 1), p, 6);
+
+  memcpy (s->a7 + R (0, 1), p, 7);
+
+  memcpy (s->a7 + R (0, 1), p, 8);  /* { dg-warning "writing" } */
+
+  memcpy (&s->a7 [R (0, 1)], p, 7);
+
+  memcpy (&s->a7 [R (1, 3)], p, 7);  /* { dg-warning "writing" } */
+}
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..7979a13
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c
@@ -0,0 +1,459 @@ 
+/* 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;
+
+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, 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 *-*-* } } */
+  }
+}
+
+/* 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 " } */
+}
+
+/* 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 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), size_max);   /* { 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 *d, const char *s)
+{
+  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" } */
+
+  /* Exercise strncpy into a destination of unknown size  with a valid
+     and invalid constant number of bytes.  */
+  strncpy (d, S (0), UR (5, 6));
+  strncpy (d, S (1), UR (6, 7));
+  strncpy (d, S (2), UR (7, 8));
+
+  strncpy (d, S (3), UR (ssize_max, ssize_max + 1));
+
+  strncpy (d, 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-strncat-chk-1.c b/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c
index 44677f1..daff680 100644
--- a/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c
@@ -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" } */
 }
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\]*" } */
diff --git a/gcc/testsuite/gcc.dg/torture/pr71132.c b/gcc/testsuite/gcc.dg/torture/pr71132.c
index 2991718..2544eb1 100644
--- a/gcc/testsuite/gcc.dg/torture/pr71132.c
+++ b/gcc/testsuite/gcc.dg/torture/pr71132.c
@@ -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 {