diff mbox

enhance buffer overflow warnings (and c/53562)

Message ID 6acf4a8f-1451-d58a-900c-833f6dc2e21a@gmail.com
State New
Headers show

Commit Message

Martin Sebor Nov. 2, 2016, 2:55 a.m. UTC
On 11/01/2016 01:14 PM, Jakub Jelinek wrote:
> 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

I'm not sure I understand the concern you raised there.  I would
find specific examples helpful.  My concern is that with bos 0
GCC doesn't point out or protect against far too many cases of
even obvious buffer overflows.  See the example below.

I'm also not convinced by the argument that this sort of code is
so common and safe and that warning about it would cause too many
false positives.  The patch found just one instance of such code
in GCC and it's a real bug.  It points out 8 issues in the Linux
kernel at least some of which look suspicious to me.  It found
nothing in Binutils.  (There, however, GCC complains about dozens
of fallthrough cases, making me wonder why the same concern about
false positives doesn't apply to that warning.)

In any event, I've enhanced the patch to both accept an argument
to -Wstringop-overflow=N, and add another option for raw memory
functions like memcpy, -Wrawmem-overflow=N, and to treat (N - 1)
as the object size type.  While the defaults for the patch are
what I understand you would be comfortable with I would prefer
to go with bos 1 for both instead.  I understand and agree that
the checking functions enabled by _FORTIFY_SOURCE need to be
conservative to avoid aborting on benign code.  But this patch
only issues warnings and does not cause aborts.  In my view,
it's not only acceptable but appropriate for the warnings to
be more strict.  That said, if you can suggest a code base that
you're worried about might be adversely affected by the warnings
I'm happy to verify whether it is or not and tweak the patch to
minimize its impact.

Martin

For instance, in the test case below only the second memset is
diagnosed (with bos type 1), but not the first (bos 0).  That
seems like such a gaping hole that it must make _FORTIFY_SOURCE
for the raw memory functions like memcpy and memset virtually
useless for structs accessed by pointers.

$ cat b.c && gcc -O2 -S -Wall -Wextra -Wpedantic  b.c
struct S {
   int a, b, c, d;
};

#define bos(p, t) __builtin_object_size (p, t)
#define memset0(p, i, n) __builtin___memset_chk (p, i, n, bos (p, 0))
#define memset1(p, i, n) __builtin___memset_chk (p, i, n, bos (p, 1))

void f0 (struct S *s)
{
   memset0 (&s->d, 0, 1024);   // no warning here (bos 0)
}

void f1 (struct S *s)
{
   memset1 (&s->d, 0, 1024);
}


b.c: In function ‘f1’:
b.c:7:26: warning: call to __builtin___memset_chk will always overflow 
destination buffer
  #define memset1(p, i, n) __builtin___memset_chk (p, i, n, bos (p, 1))
                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
b.c:16:3: note: in expansion of macro ‘memset1’
    memset1 (&s->d, 0, 1024);
    ^~~~~~~

Even with bos 1 the following isn't diagnosed.  IMO, this code is
suspicious to say the least and deserves a warning.  It may not
actually be a bug so it may not call for an abort but GCC could
warn on it without emitting a checking call.

struct A {
   int a, b;
};

struct B {
   int a, b, c, d;
};

void f (struct A *a, struct B *b)
{
   __builtin___memcpy_chk (a, b, sizeof *b, __builtin_object_size (a, 1));
}

Comments

Jakub Jelinek Nov. 2, 2016, 7:37 a.m. UTC | #1
On Tue, Nov 01, 2016 at 08:55:03PM -0600, Martin Sebor wrote:
> struct S {
>   int a, b, c, d;
> };
> 
> #define bos(p, t) __builtin_object_size (p, t)
> #define memset0(p, i, n) __builtin___memset_chk (p, i, n, bos (p, 0))
> #define memset1(p, i, n) __builtin___memset_chk (p, i, n, bos (p, 1))
> 
> void f0 (struct S *s)
> {
>   memset0 (&s->d, 0, 1024);   // no warning here (bos 0)
> }

But we do not want the warning here, there is nothing wrong on it.
The caller may be

void
bar (void)
{
  struct T { struct S header; char payload[1024 - offsetof (struct S, d)]; } t;
  initialize (&t);
  f0 (&t.header);
}

and the callee might rely on that.  Using some header structure at the
beginning and then conditionally on fields in that structure various
payloads occurs in many projects, starting with glibc, gcc, Linux kernel,
... The warning really must not be detached from reality.

If you want a warning for suspicious calls, sure, but
1) it has to be clearly worded significantly differently from how do you
   word it, so that users really understand you are warning about
   suspicious code (though, I really believe it is very common and there
   is really nothing the users can do about it)
2) it really shouldn't be enabled in -Wall, and I think not even in -Wextra

	Jakub
Martin Sebor Nov. 2, 2016, 3:45 p.m. UTC | #2
On 11/02/2016 01:37 AM, Jakub Jelinek wrote:
> On Tue, Nov 01, 2016 at 08:55:03PM -0600, Martin Sebor wrote:
>> struct S {
>>   int a, b, c, d;
>> };
>>
>> #define bos(p, t) __builtin_object_size (p, t)
>> #define memset0(p, i, n) __builtin___memset_chk (p, i, n, bos (p, 0))
>> #define memset1(p, i, n) __builtin___memset_chk (p, i, n, bos (p, 1))
>>
>> void f0 (struct S *s)
>> {
>>   memset0 (&s->d, 0, 1024);   // no warning here (bos 0)
>> }
>
> But we do not want the warning here, there is nothing wrong on it.
> The caller may be
>
> void
> bar (void)
> {
>   struct T { struct S header; char payload[1024 - offsetof (struct S, d)]; } t;
>   initialize (&t);
>   f0 (&t.header);
> }
>
> and the callee might rely on that.

Sure, they might and in that case the warning would be a false
positive.  It wouldn't be the first such warning that wasn't 100%
free of them.  But my testing with Binutils, GCC, and the Linux
kernel has exposed only 10 instances of new warnings and I don't
think I saw this idiom among them.  But even if some were, or if
all of them were false positives I think that would be well within
the acceptable rates.  Here are the numbers of warnings for
Binutils and the kernel:

113   -Wimplicit-fallthrough
  38   -Wformat-length=
  12   -Wunused-const-variable=
  10   -Wstringop-overflow
   2   -Wdangling-else
   2   -Wframe-address
   2   -Wint-in-bool-context
   1   -Wbool-operation

> Using some header structure at the
> beginning and then conditionally on fields in that structure various
> payloads occurs in many projects, starting with glibc, gcc, Linux kernel,
> ... The warning really must not be detached from reality.

That's an unfair assertion in light of the numbers above.

> If you want a warning for suspicious calls, sure, but
> 1) it has to be clearly worded significantly differently from how do you
>    word it, so that users really understand you are warning about
>    suspicious code (though, I really believe it is very common and there
>    is really nothing the users can do about it)
> 2) it really shouldn't be enabled in -Wall, and I think not even in -Wextra

As you have argued yourself recently in our discussion of
-Wimplicit-fallthrough, warnings that aren't enabled by either
of these options don't generally benefit users because very few
turn them on explicitly.  I agree with that argument although I
would be in favor of rolling out a new warning gradually over
two or more releases if it were known to be prone to high rates
of false positive. The -Wstringop-overflow warning clearly isn't
in that category so there's no need for it.  My offer to do
additional testing is still good if you'd like to see more data.

As for the wording, I welcome suggestions for improvements.

Martin
Martin Sebor Nov. 2, 2016, 4:55 p.m. UTC | #3
> Sure, they might and in that case the warning would be a false
> positive.  It wouldn't be the first such warning that wasn't 100%
> free of them.  But my testing with Binutils, GCC, and the Linux
> kernel has exposed only 10 instances of new warnings and I don't
> think I saw this idiom among them.  But even if some were, or if
> all of them were false positives I think that would be well within
> the acceptable rates.

I've gone through all the new warnings.  With one exception where
it's unclear what exactly is going on they all appear to be due
to the idiom of writing into a struct member to clear or copy
data into the member and those that follow to the end of the
struct, like so:

   struct S { int a, b, c } *s;
   memset (&s->b, 0, sizeof *s - offsetof (struct S, b));

It should be doable to recognize this idiom and suppress the
warning in this case.  Let me work on enhancing the patch to
do that.

> Here are the numbers of warnings for
> Binutils and the kernel:
>
> 113   -Wimplicit-fallthrough
>  38   -Wformat-length=
>  12   -Wunused-const-variable=
>  10   -Wstringop-overflow
>   2   -Wdangling-else
>   2   -Wframe-address
>   2   -Wint-in-bool-context
>   1   -Wbool-operation
>
>> Using some header structure at the
>> beginning and then conditionally on fields in that structure various
>> payloads occurs in many projects, starting with glibc, gcc, Linux kernel,
>> ... The warning really must not be detached from reality.
>
> That's an unfair assertion in light of the numbers above.
>
>> If you want a warning for suspicious calls, sure, but
>> 1) it has to be clearly worded significantly differently from how do you
>>    word it, so that users really understand you are warning about
>>    suspicious code (though, I really believe it is very common and there
>>    is really nothing the users can do about it)
>> 2) it really shouldn't be enabled in -Wall, and I think not even in
>> -Wextra
>
> As you have argued yourself recently in our discussion of
> -Wimplicit-fallthrough, warnings that aren't enabled by either
> of these options don't generally benefit users because very few
> turn them on explicitly.  I agree with that argument although I
> would be in favor of rolling out a new warning gradually over
> two or more releases if it were known to be prone to high rates
> of false positive. The -Wstringop-overflow warning clearly isn't
> in that category so there's no need for it.  My offer to do
> additional testing is still good if you'd like to see more data.
>
> As for the wording, I welcome suggestions for improvements.
>
> Martin
Jakub Jelinek Nov. 2, 2016, 7:32 p.m. UTC | #4
On Wed, Nov 02, 2016 at 10:55:23AM -0600, Martin Sebor wrote:
> >That's an unfair assertion in light of the numbers above.
> >
> >>If you want a warning for suspicious calls, sure, but
> >>1) it has to be clearly worded significantly differently from how do you
> >>   word it, so that users really understand you are warning about
> >>   suspicious code (though, I really believe it is very common and there
> >>   is really nothing the users can do about it)
> >>2) it really shouldn't be enabled in -Wall, and I think not even in
> >>-Wextra
> >
> >As you have argued yourself recently in our discussion of
> >-Wimplicit-fallthrough, warnings that aren't enabled by either
> >of these options don't generally benefit users because very few
> >turn them on explicitly.  I agree with that argument although I
> >would be in favor of rolling out a new warning gradually over
> >two or more releases if it were known to be prone to high rates
> >of false positive. The -Wstringop-overflow warning clearly isn't
> >in that category so there's no need for it.  My offer to do
> >additional testing is still good if you'd like to see more data.

But obviously not all levels of the warning can/should be enabled
with -Wall/-Werror.  There are cases which are worth warning by default
(the case where we want to inform the user if you reach this stmt,
you'll get your program killed (will call __chk_fail)) is something
that ought like before be enabled by default; can have a warning
switch users can disable.
Then there is the case where there is a sure buffer overflow (not using
-D_FORTIFY_SOURCE, but still __bos (, 0) tells the buffer is too short,
and it is unconditional (no tricks with PHIs where one path has short
and another part has long size).  This is something that is useful
in -Wall.
The rest I'm very doubtful about even for -Wextra.

One thing is that for useful warnings users have to be able to do something
to quiet them.  For -Wmisleading-indentation you indent your code properly,
for -Wimplicit-fallthrough you add attributes or comments, etc.
But I really don't know what you want people to do to tell the compiler
the warning is a false positive.  Add asm ("" : "+g" (ptr)); to hide
everything from the compiler?  Too ugly, too unportable, pessimizing correct
code.

Another thing is that __builtin_object_size (x, 1) is very fragile thing
especially since the introduction of MEM_REF, but also various foldings etc.
Just look up the numerous open PRs about it.  We try to mitigate it in some
early optimization passes, and had to introduce an early objsz pass copy to
deal with that.  But further down the optimization passes the distinctions
that __bos (x, 1) needs to do are very often lost, optimizers fold
(char *)&s + offsetof (struct S, fld) into &s.fld and vice versa, MEM_REF
just doesn't tell you anything about what field is used, etc.  It is
unreliable in both directions.  So trying to use it for anything very late
in the GIMPLE opts is just a bad idea.

	Jakub
Martin Sebor Nov. 7, 2016, 12:50 a.m. UTC | #5
> But obviously not all levels of the warning can/should be enabled
> with -Wall/-Werror.  There are cases which are worth warning by default
> (the case where we want to inform the user if you reach this stmt,
> you'll get your program killed (will call __chk_fail)) is something
> that ought like before be enabled by default; can have a warning
> switch users can disable.
> Then there is the case where there is a sure buffer overflow (not using
> -D_FORTIFY_SOURCE, but still __bos (, 0) tells the buffer is too short,
> and it is unconditional (no tricks with PHIs where one path has short
> and another part has long size).  This is something that is useful
> in -Wall.
> The rest I'm very doubtful about even for -Wextra.

I've posted an updated patch that disables the warning for the cases
you were concerned about but I want to respond these two points.

GCC issues a number of different kinds of warnings under -Wall and
-Wextra for constructs that are strictly safe but that have been
known to cause bugs down the line.  Those warnings are helpful even
if some of their instances could be considered false positives.
I think the same is even more true for warnings that help detect
defects as serious as buffer overflows.

> One thing is that for useful warnings users have to be able to do something
> to quiet them.  For -Wmisleading-indentation you indent your code properly,
> for -Wimplicit-fallthrough you add attributes or comments, etc.
> But I really don't know what you want people to do to tell the compiler
> the warning is a false positive.  Add asm ("" : "+g" (ptr)); to hide
> everything from the compiler?  Too ugly, too unportable, pessimizing correct
> code.

That's a valid question to ask.  There are a few portable ways to
suppress these warnings.  The latest patch doesn't warn on the
examples you posted above anymore but here's one where it does
warn where  _FORTIIFY_SOURCE did not before the patch.  The example
shows the code that triggers the warning (in function f) and how
the warning can be suppressed (in function g).

The code in f is questionable (and, in my view, would be even if
it called memcpy instead) and suggests that the programmer may
have made a mistake.  If the intent is to copy the string over
the whole struct then it's better written as in g (though using
memcpy would be more appropriate).

$ cat b.c && gcc -O2 -S -Wall -Wextra b.c
extern char* strcpy (char*, const char*);

const char* const s0 = "1234";
const char* const s1 = "12345";

struct S { char a[4]; char b[4]; };

void f (struct S *s, _Bool i)
{
    strcpy (s->a, i ? s1 : s0);
}

void g (struct S *s, _Bool i)
{
    strcpy ((char*)s, i ? s1 : s0);
}

b.c: In function ‘f’:
b.c:10:3: warning: strcpy writing 5 bytes into a region of size 4 
overflows the destination [-Wstringop-overflow=]
     strcpy (s->a, i ? s1 : s0);
     ^~~~~~~~~~~~~~~~~~~~~~~~~~

Martin
Jeff Law Nov. 16, 2016, 6:10 p.m. UTC | #6
[ I'm catching up on a variety of things...  So apologies if y'all
   have settled these issues. ]

On 11/02/2016 01:32 PM, Jakub Jelinek wrote:
> But obviously not all levels of the warning can/should be enabled
> with -Wall/-Werror.  There are cases which are worth warning by default
> (the case where we want to inform the user if you reach this stmt,
> you'll get your program killed (will call __chk_fail)) is something
> that ought like before be enabled by default; can have a warning
> switch users can disable.
> Then there is the case where there is a sure buffer overflow (not using
> -D_FORTIFY_SOURCE, but still __bos (, 0) tells the buffer is too short,
> and it is unconditional (no tricks with PHIs where one path has short
> and another part has long size).  This is something that is useful
> in -Wall.
> The rest I'm very doubtful about even for -Wextra.
I would hesitate on distinguishing between something that flows via a 
PHI vs something that is explicit in the IL.

It is entirely possible that an unrelated path isolation might take a 
PHI where one path is short and one long and split it into two paths. 
At that point they're both going to be explicit in the IL.  You'd then 
have to use something like global anticipability analysis to determine 
if they're executed unconditionally.

Jeff
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 cc711a0..f697506 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);
@@ -3011,6 +3013,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 (int opt, 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,
+		    "%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,
+			"%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,
+			(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,
+			"%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,
+			    "%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,
+			    "%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,
+			    "%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,
+			    "%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
@@ -3027,6 +3234,18 @@  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);
+
+      if (int ostype = warn_rawmem_overflow)
+	{
+	  tree objsize = NULL_TREE;
+	  unsigned HOST_WIDE_INT uhwisize;
+	  if (compute_builtin_object_size (dest, ostype - 1, &uhwisize))
+	    objsize = build_int_cst (sizetype, uhwisize);
+
+	  check_sizes (OPT_Wrawmem_overflow_,
+		       exp, len, /*maxlen=*/NULL_TREE, NULL_TREE, objsize);
+	}
+
       return expand_builtin_memcpy_args (dest, src, len, target, exp);
     }
 }
@@ -3076,15 +3295,29 @@  expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode)
   if (!validate_arglist (exp,
  			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
-  else
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+
+  if (int ostype = warn_rawmem_overflow)
     {
-      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, ostype - 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 (OPT_Wrawmem_overflow_,
+			exp, len, NULL_TREE, NULL_TREE, destsize))
+	return NULL_RTX;
     }
+
+  return expand_builtin_mempcpy_args (dest, src, len,
+				      target, mode, /*endp=*/ 1,
+				      exp);
 }
 
 /* Expand an instrumented call EXP to the mempcpy builtin.
@@ -3256,6 +3489,52 @@  expand_movstr (tree dest, tree src, rtx target, int endp)
   return target;
 }
 
+/* Do some very basic size validation of a call to the strcpy builtin
+   given by EXP.  Return NULL_RTX to have the built-in expand to a call
+   to the library function.  */
+
+static rtx
+expand_builtin_strcat (tree exp, rtx)
+{
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
+      || !warn_stringop_overflow)
+    return NULL_RTX;
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+
+  /* There is no way here to determine the length of the string in
+     the destination to which the SRC string is being appended so
+     just diagnose cases when the 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, warn_stringop_overflow - 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 (OPT_Wstringop_overflow_,
+		   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
@@ -3264,10 +3543,37 @@  expand_movstr (tree dest, tree src, rtx target, int endp)
 static rtx
 expand_builtin_strcpy (tree exp, rtx target)
 {
-  if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+  if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
+      && warn_stringop_overflow)
    {
      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, warn_stringop_overflow - 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 (OPT_Wstringop_overflow_,
+		      exp, NULL_TREE, NULL_TREE, len, destsize);
+       }
+
      return expand_builtin_strcpy_args (dest, src, target);
    }
    return NULL_RTX;
@@ -3378,6 +3684,136 @@  builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset,
   return c_readstr (str + offset, mode);
 }
 
+/* Helper to check the sizes of sequences and the destination of calls
+   to __builtin_strncat and __builtin___strncat_chk.  Returns true on
+   success (no overflow or invalid sizes), false otherwise.  */
+
+static bool
+check_strncat_sizes (tree exp, tree objsize)
+{
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree maxlen = CALL_EXPR_ARG (exp, 2);
+
+  /* Try to determine the range of lengths that the source expression
+     refers to.  */
+  tree lenrange[2];
+  get_range_strlen (src, lenrange);
+
+  /* Try to verify that the destination is big enough for the shortest
+     string.  */
+
+  if (!objsize && warn_stringop_overflow)
+    {
+      /* If it hasn't been provided by __strncat_chk, try to determine
+	 the size of the destination object into which the source is
+	 being copied.  */
+      unsigned HOST_WIDE_INT destsize;
+      if (compute_builtin_object_size (dest, warn_stringop_overflow - 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
+      || (maxlen && tree_fits_uhwi_p (maxlen)
+	  && tree_fits_uhwi_p (srclen)
+	  && tree_int_cst_lt (maxlen, srclen)))
+    srclen = maxlen;
+
+  /* The number of bytes to write is LEN but check_sizes will also
+     check SRCLEN if LEN's value isn't known.  */
+  return check_sizes (OPT_Wstringop_overflow_,
+		      exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+}
+
+/* Similar to expand_builtin_strcat, do some very basic size validation
+   of a call to the strcpy builtin given by EXP.  Return NULL_RTX to have
+   the built-in expand to a call to the library function.  */
+
+static rtx
+expand_builtin_strncat (tree exp, rtx)
+{
+  if (!validate_arglist (exp,
+			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
+      || !warn_stringop_overflow)
+    return NULL_RTX;
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  /* The upper bound on the number of bytes to write.  */
+  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  /* The length of the source sequence.  */
+  tree slen = c_strlen (src, 1);
+
+  /* Try to determine the range of lengths that the source expression
+     refers to.  */
+  tree lenrange[2];
+  if (slen)
+    lenrange[0] = lenrange[1] = slen;
+  else
+    get_range_strlen (src, lenrange);
+
+  /* Try to verify that the destination is big enough for the shortest
+     string.  */
+
+  /* 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, warn_stringop_overflow - 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 (OPT_Wstringop_overflow_,
+	       exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+
+  return NULL_RTX;
+}
+
 /* Expand expression EXP, which is a call to the strncpy builtin.  Return
    NULL_RTX if we failed the caller should emit a normal call.  */
 
@@ -3391,9 +3827,44 @@  expand_builtin_strncpy (tree exp, rtx target)
     {
       tree dest = CALL_EXPR_ARG (exp, 0);
       tree src = CALL_EXPR_ARG (exp, 1);
+      /* The number of bytes to write (not the maximum).  */
       tree len = CALL_EXPR_ARG (exp, 2);
+      /* The length of the source sequence.  */
       tree slen = c_strlen (src, 1);
 
+      if (warn_stringop_overflow)
+	{
+	  /* Try to determine the range of lengths that the source expression
+	     refers to.  */
+	  tree lenrange[2];
+	  if (slen)
+	    lenrange[0] = lenrange[1] = slen;
+	  else
+	    get_range_strlen (src, lenrange);
+
+	  /* 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, warn_stringop_overflow - 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 (OPT_Wstringop_overflow_,
+		       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;
@@ -3486,6 +3957,19 @@  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);
+
+      if (int ostype = warn_rawmem_overflow)
+	{
+	  /* Try to determine the size of the destination object.  */
+	  tree objsize = NULL_TREE;
+	  unsigned HOST_WIDE_INT uhwisize;
+	  if (compute_builtin_object_size (dest, ostype - 1, &uhwisize))
+	    objsize = build_int_cst (sizetype, uhwisize);
+
+	  check_sizes (OPT_Wrawmem_overflow_,
+		       exp, len, NULL_TREE, NULL_TREE, objsize);
+	}
+
       return expand_builtin_memset_args (dest, val, len, target, mode, exp);
     }
 }
@@ -3677,6 +4161,18 @@  expand_builtin_bzero (tree exp)
   dest = CALL_EXPR_ARG (exp, 0);
   size = CALL_EXPR_ARG (exp, 1);
 
+  if (int ostype = warn_rawmem_overflow)
+    {
+      /* Try to determine the size of the destination object.  */
+      tree objsize = NULL_TREE;
+      unsigned HOST_WIDE_INT uhwisize;
+      if (compute_builtin_object_size (dest, ostype - 1, &uhwisize))
+	objsize = build_int_cst (sizetype, uhwisize);
+
+      check_sizes (OPT_Wrawmem_overflow_,
+		   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
@@ -6214,12 +6710,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)
@@ -9130,22 +9638,21 @@  expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
   len = CALL_EXPR_ARG (exp, 2);
   size = CALL_EXPR_ARG (exp, 3);
 
-  if (! tree_fits_uhwi_p (size))
+  bool sizes_ok = check_sizes (OPT_Wrawmem_overflow_,
+			       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)
@@ -9231,68 +9738,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 (OPT_Wstringop_overflow_, exp, NULL_TREE, maxlen, srclen, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9346,10 +9869,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 (OPT_Wstringop_overflow_, 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 7e86dbf..e4e1f75 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -653,6 +653,14 @@  Wnamespaces
 C++ ObjC++ Var(warn_namespaces) Warning
 Warn on namespace definition.
 
+Wrawmem-overflow
+C ObjC C++ ObjC++ Warning Alias(Wrawmem-overflow=, 1, 0)
+Warn about buffer overflow in raw memory manipulation functions like memcpy.
+
+Wrawmem-overflow=
+C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_rawmem_overflow) Init(1) Warning
+Warn about buffer overflow in raw memory manipulation functions like memcpy.
+
 Wsized-deallocation
 C++ ObjC++ Var(warn_sized_deallocation) Warning EnabledBy(Wextra)
 Warn about missing sized deallocation functions.
@@ -665,6 +673,14 @@  Wsizeof-array-argument
 C ObjC C++ ObjC++ Var(warn_sizeof_array_argument) Warning Init(1)
 Warn when sizeof is applied on a parameter declared as an array.
 
+Wstringop-overflow
+C ObjC C++ ObjC++ Warning Alias(Wstringop-overflow=, 2, 0)
+Warn about buffer overflow in string manipulation functions like strcpy.
+
+Wstringop-overflow=
+C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_stringop_overflow) Init(2) Warning
+Warn about buffer overflow in string manipulation functions like strcpy.
+
 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 fd783bf..0afab4e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -291,7 +291,8 @@  Objective-C and Objective-C++ Dialects}.
 -Wparentheses -Wno-pedantic-ms-format @gol
 -Wplacement-new -Wplacement-new=@var{n} @gol
 -Wpointer-arith  -Wno-pointer-to-int-cast @gol
--Wno-pragmas -Wredundant-decls  -Wno-return-local-addr @gol
+-Wno-pragmas -Wrawmem-overflow=@var{n} -Wredundant-decls @gol
+-Wno-return-local-addr @gol
 -Wreturn-type  -Wsequence-point  -Wshadow  -Wno-shadow-ivar @gol
 -Wshadow=global, -Wshadow=local, -Wshadow=compatible-local @gol
 -Wshift-overflow -Wshift-overflow=@var{n} @gol
@@ -301,6 +302,7 @@  Objective-C and Objective-C++ Dialects}.
 -Wsizeof-pointer-memaccess  -Wsizeof-array-argument @gol
 -Wstack-protector -Wstack-usage=@var{len} -Wstrict-aliasing @gol
 -Wstrict-aliasing=n -Wstrict-overflow -Wstrict-overflow=@var{n} @gol
+-Wstringop-overflow=@var{n} @gol
 -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]} @gol
 -Wsuggest-final-types @gol -Wsuggest-final-methods -Wsuggest-override @gol
 -Wmissing-format-attribute -Wsubobject-linkage @gol
@@ -4799,6 +4801,93 @@  Do not warn about misuses of pragmas, such as incorrect parameters,
 invalid syntax, or conflicts between pragmas.  See also
 @option{-Wunknown-pragmas}.
 
+@item -Wrawmem-overflow
+@itemx -Wrawmem-overflow=@var{type}
+@opindex Wrawmem-overflow
+@opindex Wno-rawmem-overflow
+Warn for calls to functions that write raw memory such as @code{bzero} and
+@code{memcpy} that are determined to overflow the destination buffer.  The
+optional argument specifies the type of Object Size Checking to perform to
+determine the size of the destination.  @xref{Object Size Checking}.  The
+option also warns for calls that specify a size in excess of the largest
+possible object or at most @code{SIZE_MAX / 2} bytes.  The option works best
+with optimization enabled but it can detect a small subset of simple 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.  The following example illustrates the differences
+between the first three of the four argument values to the option option.
+At the default setting, @option{-Wrawmem-overflow=1} warns only on the call
+to @code{memcpy} in @code{f1}.  @option{-Wrawmem-overflow=2} also warns on
+the call in @code{f2}, and @option{-Wrawmem-overflow=3} also on
+the @code{memcpy} in @code{f3}.
+
+@smallexample
+struct A @{
+  int a, b;
+@} a;
+
+struct B @{
+  struct A a;
+  int c, d;
+@} b;
+
+void f1 (void)
+@{
+  memcpy (&b.a, &b, sizeof b);   // -Wrawmem-overflow=1
+@}
+
+void f2 (void)
+@{
+  memcpy (&b.a, &b, sizeof b);   // -Wrawmem-overflow=2
+@}
+
+void f3 (struct A *pa)
+@{
+  memcpy (pa, &b, sizeof b);   // -Wrawmem-overflow=3
+@}
+@end smallexample
+
+The @option{-Wrawmem-overflow} option is enabled by default.
+
+@table @gcctabopt
+@item -Wrawmem-overflow
+@item -Wrawmem-overflow=1
+@opindex Wrawmem-overflow
+@opindex Wno-rawmem-overflow
+The @option{-Wrawmem-overflow=1} option uses type-zero Object Size Checking
+to determine the sizes of destination objects.  This is the default setting
+of the option.  At this setting the option will not warn for writes past
+the end of subobjects of larger objects accessed by pointers unless the
+size of the largest surrounding object is known.  When the destination may
+be one of several objects it is assumed to be the largest one of them.  On
+Linux systems, when optimization is enabled at this setting the option warns
+for the same code as when the @code{_FORTIFY_SOURCE} macro is defined to
+a non-zero value.
+
+@item -Wrawmem-overflow=2
+The @option{-Wrawmem-overflow=2} option uses type-one Object Size Checking
+to determine the sizes of destination objects.  At this setting the option
+will warn about overflows when writing to members of the largest complete
+objects whose exact size is known.  It will, however, not warn for excessive
+writes to the same members of unknown objects referenced by pointers since
+they may point to arrays containing unknown numbers of elements.
+
+@item -Wrawmem-overflow=3
+The @option{-Wrawmem-overflow=3} option uses type-two Object Size Checking
+to determine the sizes of destination objects.  At this setting the option
+warns about overflowing the smallest object or data member.  This is the
+most restrictive setting of the option that may result in warnings for safe
+code.
+
+@item -Wrawmem-overflow=4
+The @option{-Wrawmem-overflow=4} option uses type-three Object Size Checking
+to determine the sizes of destination objects.  At this setting the option
+will warn about overflowing any data members, and when the destination is
+one of several objects it uses the size of the largest of them to decide
+whether to issue a warning.  Similarly to @option{-Wrawmem-overflow=3} this
+setting of the option may result in warnings for benign code.
+@end table
+
 @item -Wstrict-aliasing
 @opindex Wstrict-aliasing
 @opindex Wno-strict-aliasing
@@ -4896,6 +4985,67 @@  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
+Similar to @option{-Wrawmem-overflow}, this option warns for calls to string
+manipulation functions such as @code{strcpy} that are determined to overflow
+the destination buffer.  The optional argument specifies the type of Object
+Size Checking to perform to determine the size of the destination.
+@xref{Object Size Checking}.  The option also warns for calls to bounded
+functions such as @code{snprintf} and @code{strncat} that specify a bound
+in excess of the largest possible object or at most @code{SIZE_MAX / 2}
+bytes.  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.
+
+The default setting of the option is @option{-Wstringop-overflow=2}.
+
+@table @gcctabopt
+@item -Wstringop-overflow
+@item -Wstringop-overflow=1
+@opindex Wstringop-overflow
+@opindex Wno-rawmem-overflow
+The @option{-Wstringop-overflow=1} option uses type-zero Object Size Checking
+to determine the sizes of destination character arrays.  At this setting
+the option will not warn for writes past the end of character array members
+of larger objects accessed by pointers unless the size of the largest
+surrounding object is known.  When the destination may be one of several
+character arrays it is assumed to be the largest one of them.  On Linux
+systems, when optimization is enabled at this setting the option warns
+for the same code as when the @code{_FORTIFY_SOURCE} macro is defined to
+@code{1}.
+
+@item -Wstringop-overflow=2
+The @option{-Wstringop-overflow=2} option uses type-one Object Size Checking
+to determine the sizes of destination character arrays.  This is the default
+setting of the option.  At this setting the option will warn about overflows
+when writing to member character arrays of the largest complete objects whose
+exact size is known.  It will, however, not warn for excessive writes to
+the same character array members of unknown objects referenced by pointers
+since they may point to arrays containing unknown numbers of elements.  On
+Linux systems, when optimization is enabled at this setting the option warns
+for the same code as when the @code{_FORTIFY_SOURCE} macro is defined to
+@code{2}.
+
+@item -Wstringop-overflow=3
+The @option{-Wstringop-overflow=3} option uses type-two Object Size Checking
+to determine the sizes of destination character arrays.  At this setting
+the option warns about overflowing the smallest character array or array
+member.  This is the most restrictive setting of the option that may result
+in warnings for safe code.
+
+@item -Wstringop-overflow=4
+The @option{-Wstringop-overflow=4} option uses type-three Object Size Checking
+to determine the sizes of destination character arrays.  At this setting
+the option will warn about overflowing any character member arrays, and when
+the destination is one of several arrays it uses the size of the largest of
+them to decide whether to issue a warning.  Similarly to
+@option{-Wstringop-overflow=3} this setting of the option may result in
+warnings for benign code.
+@end table
+
 @item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]}
 @opindex Wsuggest-attribute=
 @opindex Wno-suggest-attribute=
diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
index 8afcfbe..29f816e 100644
--- a/gcc/emit-rtl.c
+++ b/gcc/emit-rtl.c
@@ -3930,7 +3930,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..3911498
--- /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 -Wrawmem-overflow=2 -Wstringop-overflow=2 -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..86d7764
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c
@@ -0,0 +1,459 @@ 
+/* Test exercising buffer overflow warnings emitted for raw memory and
+   string manipulation builtins involving ranges of sizes and strings
+   of varying lengths.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -ftrack-macro-expansion=0" } */
+
+#define INT_MAX      __INT_MAX__
+#define PTRDIFF_MAX  __PTRDIFF_MAX__
+#define SIZE_MAX     __SIZE_MAX__
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__    size_t;
+
+static const size_t ssize_max = SIZE_MAX / 2;
+static const size_t size_max = SIZE_MAX;
+
+extern signed char    schar_val;
+extern signed short   sshrt_val;
+extern signed int     sint_val;
+extern signed long    slong_val;
+extern unsigned char  uchar_val;
+extern unsigned short ushrt_val;
+extern unsigned int   uint_val;
+extern unsigned long  ulong_val;
+
+#define memcpy(d, s, n) (memcpy ((d), (s), (n)), sink ((d)))
+extern void* (memcpy)(void*, const void*, size_t);
+
+#define mempcpy(d, s, n) (mempcpy ((d), (s), (n)), sink ((d)))
+extern void* (mempcpy)(void*, const void*, size_t);
+
+#define memset(d, c, n) (memset ((d), (c), (n)), sink ((d)))
+extern void* (memset)(void*, int, size_t);
+
+#define bzero(d, n) (bzero ((d), (n)), sink ((d)))
+extern void (bzero)(void*, size_t);
+
+#define strcat(d, s) (strcat ((d), (s)), sink ((d)))
+extern char* (strcat)(char*, const char*);
+
+#define strncat(d, s, n) (strncat ((d), (s), (n)), sink ((d)))
+extern char* (strncat)(char*, const char*, size_t);
+
+#define strcpy(d, s) (strcpy ((d), (s)), sink ((d)))
+extern char* (strcpy)(char*, const char*);
+
+#define strncpy(d, s, n) (strncpy ((d), (s), (n)), sink ((d)))
+extern char* (strncpy)(char*, const char*, size_t);
+
+void sink (void*);
+
+/* Function to "generate" a random number each time it's called.  Declared
+   (but not defined) and used to prevent GCC from making assumptions about
+   their values based on the variables uses in the tested expressions.  */
+size_t random_unsigned_value (void);
+ptrdiff_t random_signed_value (void);
+
+/* Return a random unsigned value between MIN and MAX.  */
+
+static inline size_t
+unsigned_range (size_t min, size_t max)
+{
+  const size_t val = random_unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+/* Return a random signed value between MIN and MAX.  */
+
+static inline ptrdiff_t
+signed_range (ptrdiff_t min, ptrdiff_t max)
+{
+  const ptrdiff_t val = random_signed_value ();
+  return val < min || max < val ? min : val;
+}
+
+/* For brevity.  */
+#define UR(min, max)   unsigned_range (min, max)
+#define SR(min, max)   signed_range (min, max)
+
+/* UReturn a pointer to constant string whose length is at least MINLEN
+   and at most 10.  */
+static inline const char*
+string_range (size_t minlen)
+{
+  static const char str[] = "0123456789";
+
+  const size_t len = unsigned_range (minlen, sizeof str - 1);
+
+  switch (len)
+    {
+    case 10: return "0123456789";
+    case  9: return "012345678";
+    case  8: return "01234567";
+    case  7: return "0123456";
+    case  6: return "012345";
+    case  5: return "01234";
+    case  4: return "0123";
+    case  3: return "012";
+    case  2: return "01";
+    case  1: return "0";
+    case  0: return "";
+    }
+}
+
+#define S(minlen)   string_range (minlen)
+
+/* Test memcpy with a number of bytes bounded by a known range.  */
+
+void test_memcpy_range (void *d, const void *s)
+{
+  char buf[5];
+
+  memcpy (buf, s, UR (0, 5));
+  memcpy (buf, s, UR (1, 5));
+  memcpy (buf, s, UR (2, 5));
+  memcpy (buf, s, UR (3, 5));
+  memcpy (buf, s, UR (4, 5));
+
+  memcpy (buf, s, UR (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+  memcpy (buf, 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-stringop-chk-5.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
new file mode 100644
index 0000000..7c3ad89
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
@@ -0,0 +1,63 @@ 
+/* Test exercising -Wrawmem-overflow and -Wstringop-overflow warnings.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wrawmem-overflow=1 -Wstringop-overflow=1" } */
+
+struct A { char a, b; };
+struct B { struct A a; char c, d; };
+
+extern void* memcpy (void*, const void*, __SIZE_TYPE__);
+
+void sink (void*);
+
+void test_memop_warn (const struct B *pb, unsigned n)
+{
+  if (n < 2 * sizeof (struct B) || 32 < n) return;
+
+  struct A a[2];
+
+  /* At -Wrawmem-overflow=1 the destination is considered to be
+     the whole array and its size is therefore sizeof a.  */
+  memcpy (&a[0], pb, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
+
+  sink (a);
+}
+
+void test_memop_nowarn (const struct B *pb, unsigned n)
+{
+  if (n < 2 * sizeof (struct B) || 32 < n) return;
+
+  struct B b[2];
+
+  memcpy (&b[0].a, pb, n);
+
+  sink (b);
+}
+
+struct C { char a[3], b; };
+struct D { struct C c; char d, e; };
+
+extern char* strncpy (char*, const char*, __SIZE_TYPE__);
+
+void test_stringop_warn (unsigned n)
+{
+  if (n < 2 * sizeof (struct D) || 32 < n) return;
+
+  struct C c[2];
+
+  /* Similarly, at -Wstringop-overflow=1 the destination is considered
+     to be the whole array and its size is therefore sizeof c.  */
+  strncpy (c[0].a, "123", n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" } */
+
+  sink (c);
+}
+
+void test_stringop_nowarn (unsigned n)
+{
+  if (n < 2 * sizeof (struct D) || 32 < n) return;
+
+  struct D d[2];
+
+  strncpy (d[0].c.a, "123", n);
+
+  sink (d);
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-6.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-6.c
new file mode 100644
index 0000000..5bc9af5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-6.c
@@ -0,0 +1,68 @@ 
+/* Test exercising -Wrawmem-overflow and -Wstringop-overflow warnings.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wrawmem-overflow=2 -Wstringop-overflow=2" } */
+
+struct A { char a, b; };
+struct B { struct A a; char c, d; };
+
+extern void* memcpy (void*, const void*, __SIZE_TYPE__);
+
+void sink (void*);
+
+void test_memop_warn_object (const struct B *pb, unsigned n)
+{
+  if (n < 2 * sizeof (struct B) || 32 < n) return;
+
+  struct A a[2];
+
+  /* At -Wrawmem-overflow=2 the destination is considered to be
+     the whole array and its size is therefore sizeof a.  */
+  memcpy (&a[0], pb, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
+
+  sink (a);
+}
+
+void test_memop_warn_subobject (const struct B *pb, unsigned n)
+{
+  if (n < 2 * sizeof (struct B) || 32 < n) return;
+
+  struct B b[2];
+
+  /* At -Wrawmem-overflow=2 the destination is considered to be
+     the member sobobject of the first array element and its size
+     is therefore sizeof b[0].a.  */
+  memcpy (&b[0].a, pb, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 2 overflows the destination" } */
+
+  sink (b);
+}
+
+struct C { char a[3], b; };
+struct D { struct C c; char d, e; };
+
+extern char* strncpy (char*, const char*, __SIZE_TYPE__);
+
+void test_stringop_warn_object (unsigned n)
+{
+  if (n < 2 * sizeof (struct D) || 32 < n) return;
+
+  struct C c[2];
+
+  /* Similarly, at -Wstringop-overflow=2 the destination is considered
+     to be the array member of the first element of the array c and its
+     size is therefore sizeof c[0].a.  */
+  strncpy (c[0].a, "123", n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 3 overflows the destination" } */
+
+  sink (c);
+}
+
+void test_stringop_warn_subobject (unsigned n)
+{
+  if (n < 2 * sizeof (struct D) || 32 < n) return;
+
+  struct D d[2];
+
+  /* Same as above.  */
+  strncpy (d[0].c.a, "123", n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 3 overflows the destination" } */
+
+  sink (d);
+}
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 {