diff mbox

warn for reading past the end by library functions (PR 54924, 79234)

Message ID 492b5881-e14a-f8cb-5330-0db39249d8c8@gmail.com
State New
Headers show

Commit Message

Martin Sebor April 20, 2017, 10:49 p.m. UTC
PR libstdc++/54924 - Warn for std::string constructor with wrong
size asks for a warning when constructing a std::string from
a character array and a number of elements that's in excess of
the number of elements.  E.g.,

   std::string s ("abc", 7);

PR middle-end/79234 - warn on past the end reads by library functions
is a more general enhancement that suggests warning for calls to any
standard library functions that read past the end of a provided array.
For example:

   char a[8];
   memcpy (a, "abcdef", sizeof a);

The attached patch extends the -Wstringop-overflow warning to also
detect and warn for reading past the end in memcmp, memchr, memcpy,
and mempcpy.  The patch doesn't handle memmove because there's
a separate bug for -Wstringop-overflow not handling the function.
A patch for it was submitted in January and deferred to GCC 8:

   https://gcc.gnu.org/ml/gcc-patches/2017-01/msg01994.html

Although the patch handles the std::string case fine the warning
for it is suppressed by -Wsystem-headers.  There's also a separate
bug for that (bug 79214) and a patch for it was submitted back in
January and deferred to GCC 8:

   https://gcc.gnu.org/ml/gcc-patches/2017-01/msg01994.html

Martin

Comments

Jeff Law May 4, 2017, 7:27 p.m. UTC | #1
On 04/20/2017 04:49 PM, Martin Sebor wrote:
> PR libstdc++/54924 - Warn for std::string constructor with wrong
> size asks for a warning when constructing a std::string from
> a character array and a number of elements that's in excess of
> the number of elements.  E.g.,
> 
>    std::string s ("abc", 7);
> 
> PR middle-end/79234 - warn on past the end reads by library functions
> is a more general enhancement that suggests warning for calls to any
> standard library functions that read past the end of a provided array.
> For example:
> 
>    char a[8];
>    memcpy (a, "abcdef", sizeof a);
> 
> The attached patch extends the -Wstringop-overflow warning to also
> detect and warn for reading past the end in memcmp, memchr, memcpy,
> and mempcpy.  The patch doesn't handle memmove because there's
> a separate bug for -Wstringop-overflow not handling the function.
> A patch for it was submitted in January and deferred to GCC 8:
> 
> https://gcc.gnu.org/ml/gcc-patches/2017-01/msg01994.html
> 
> Although the patch handles the std::string case fine the warning
> for it is suppressed by -Wsystem-headers.  There's also a separate
> bug for that (bug 79214) and a patch for it was submitted back in
> January and deferred to GCC 8:
> 
> https://gcc.gnu.org/ml/gcc-patches/2017-01/msg01994.html
Note I just ack'd the deferred patch noted above.


> 
> Martin
> 
> 
> gcc-79234.diff
> 
> 
> PR libstdc++/54924 - Warn for std::string constructor with wrong size
> PR middle-end/79234 - warn on past the end reads by library functions
> 
> gcc/ChangeLog:
> 
> 	PR middle-end/79234
> 	* builtins.c (check_sizes): Adjust to handle reading past the end.
> 	Avoid printing excessive upper bound of ranges.
> 	(expand_builtin_memchr): New function.
> 	(compute_dest_size): Rename...
> 	(compute_objsize): ...to this.
> 	(expand_builtin_memcpy): Adjust.
> 	(expand_builtin_mempcpy): Adjust.
> 	(expand_builtin_strcat): Adjust.
> 	(expand_builtin_strcpy): Adjust.
> 	(check_strncat_sizes): Adjust.
> 	(expand_builtin_strncat): Adjust.
> 	(expand_builtin_strncpy): Adjust and simplify.
> 	(expand_builtin_memset): Adjust.
> 	(expand_builtin_bzero): Adjust.
> 	(expand_builtin_memcmp): Adjust.
> 	(expand_builtin): Handle memcmp.
> 	(maybe_emit_chk_warning): Check strncat just once.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR middle-end/79234
> 	* gcc.dg/builtin-stringop-chk-8.c: New test.
> 	* gcc.dg/builtin-stringop-chk-1.c: Adjust.
> 	* gcc.dg/builtin-stringop-chk-4.c: Same.
> 	* gcc.dg/builtin-strncat-chk-1.c: Same.
> 	* g++.dg/ext/strncpy-chk1.C: Same.
> 	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
> 	* gcc.dg/out-of-bounds-1.c: Same.
> 	* gcc.dg/pr78138.c: Same.
> 	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Same.
> 	* gfortran.dg/mvbits_7.f90: Same.
> 
> diff --git a/gcc/builtins.c b/gcc/builtins.c
> index f3bee5b..892f576 100644
> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -3044,10 +3045,10 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
>      MAXLEN is the user-supplied bound on the length of the source sequence
>      (such as in strncat(d, s, N).  It specifies the upper limit on the number
>      of bytes to write.
> -   STR is the source string (such as in strcpy(d, s)) when the epxression
> +   SRC is the source string (such as in strcpy(d, s)) when the epxression
s/epxression/expression

OK with the nit fixed.

jeff
diff mbox

Patch

PR libstdc++/54924 - Warn for std::string constructor with wrong size
PR middle-end/79234 - warn on past the end reads by library functions

gcc/ChangeLog:

	PR middle-end/79234
	* builtins.c (check_sizes): Adjust to handle reading past the end.
	Avoid printing excessive upper bound of ranges.
	(expand_builtin_memchr): New function.
	(compute_dest_size): Rename...
	(compute_objsize): ...to this.
	(expand_builtin_memcpy): Adjust.
	(expand_builtin_mempcpy): Adjust.
	(expand_builtin_strcat): Adjust.
	(expand_builtin_strcpy): Adjust.
	(check_strncat_sizes): Adjust.
	(expand_builtin_strncat): Adjust.
	(expand_builtin_strncpy): Adjust and simplify.
	(expand_builtin_memset): Adjust.
	(expand_builtin_bzero): Adjust.
	(expand_builtin_memcmp): Adjust.
	(expand_builtin): Handle memcmp.
	(maybe_emit_chk_warning): Check strncat just once.

gcc/testsuite/ChangeLog:

	PR middle-end/79234
	* gcc.dg/builtin-stringop-chk-8.c: New test.
	* gcc.dg/builtin-stringop-chk-1.c: Adjust.
	* gcc.dg/builtin-stringop-chk-4.c: Same.
	* gcc.dg/builtin-strncat-chk-1.c: Same.
	* g++.dg/ext/strncpy-chk1.C: Same.
	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
	* gcc.dg/out-of-bounds-1.c: Same.
	* gcc.dg/pr78138.c: Same.
	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Same.
	* gfortran.dg/mvbits_7.f90: Same.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index f3bee5b..892f576 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -118,6 +118,7 @@  static rtx expand_builtin_va_copy (tree);
 static rtx expand_builtin_strcmp (tree, rtx);
 static rtx expand_builtin_strncmp (tree, rtx, machine_mode);
 static rtx builtin_memcpy_read_str (void *, HOST_WIDE_INT, machine_mode);
+static rtx expand_builtin_memchr (tree, rtx);
 static rtx expand_builtin_memcpy (tree, rtx);
 static rtx expand_builtin_memcpy_with_bounds (tree, rtx);
 static rtx expand_builtin_memcpy_args (tree, tree, tree, rtx, tree);
@@ -3044,10 +3045,10 @@  expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
    MAXLEN is the user-supplied bound on the length of the source sequence
    (such as in strncat(d, s, N).  It specifies the upper limit on the number
    of bytes to write.
-   STR is the source string (such as in strcpy(d, s)) when the epxression
+   SRC is the source string (such as in strcpy(d, s)) when the epxression
    EXP is a string function call (as opposed to a memory call like memcpy).
-   As an exception, STR can also be an integer denoting the precomputed
-   length of the source string.
+   As an exception, SRC can also be an integer denoting the precomputed
+   size of the source string or object (for functions like memcpy).
    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,
@@ -3060,7 +3061,7 @@  expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
    the function returns true, otherwise false..  */
 
 static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree str, tree objsize)
+check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 {
   /* The size of the largest object is half the address space, or
      SSIZE_MAX.  (This is way too permissive.)  */
@@ -3068,25 +3069,40 @@  check_sizes (int opt, tree exp, tree size, tree maxlen, tree str, tree objsize)
 
   tree slen = NULL_TREE;
 
+  tree range[2] = { NULL_TREE, NULL_TREE };
+
   /* Set to true when the exact number of bytes written by a string
      function like strcpy is not known and the only thing that is
      known is that it must be at least one (for the terminating nul).  */
   bool at_least_one = false;
-  if (str)
+  if (src)
     {
-      /* STR is normally a pointer to string but as a special case
+      /* SRC is normally a pointer to string but as a special case
 	 it can be an integer denoting the length of a string.  */
-      if (POINTER_TYPE_P (TREE_TYPE (str)))
+      if (POINTER_TYPE_P (TREE_TYPE (src)))
 	{
 	  /* Try to determine the range of lengths the source string
-	     refers to.  If it can be determined add one to it for
+	     refers to.  If it can be determined and is less than
+	     the upper bound given by MAXLEN add one to it for
 	     the terminating nul.  Otherwise, set it to one for
-	     the same reason.  */
-	  tree lenrange[2];
-	  get_range_strlen (str, lenrange);
-	  if (lenrange[0])
-	    slen = fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
-				size_one_node);
+	     the same reason, or to MAXLEN as appropriate.  */
+	  get_range_strlen (src, range);
+	  if (range[0])
+	    {
+	      if (maxlen && tree_int_cst_le (maxlen, range[0]))
+		range[0] = range[1] = maxlen;
+	      else
+		range[0] = fold_build2 (PLUS_EXPR, size_type_node,
+					range[0], size_one_node);
+
+	      if (maxlen && tree_int_cst_le (maxlen, range[1]))
+		range[1] = maxlen;
+	      else
+		range[1] = fold_build2 (PLUS_EXPR, size_type_node,
+					range[1], size_one_node);
+
+	      slen = range[0];
+	    }
 	  else
 	    {
 	      at_least_one = true;
@@ -3094,7 +3110,7 @@  check_sizes (int opt, tree exp, tree size, tree maxlen, tree str, tree objsize)
 	    }
 	}
       else
-	slen = str;
+	slen = src;
     }
 
   if (!size && !maxlen)
@@ -3106,7 +3122,8 @@  check_sizes (int opt, tree exp, tree size, tree maxlen, tree str, tree objsize)
 
       /* Otherwise, when the length of the source sequence is known
 	 (as with with strlen), set SIZE to it.  */
-      size = slen;
+      if (!range[0])
+	size = slen;
     }
 
   if (!objsize)
@@ -3116,7 +3133,6 @@  check_sizes (int opt, tree exp, tree size, tree maxlen, tree str, tree objsize)
      unsigned HOST_WIDE_INT.  */
   bool exactsize = size && tree_fits_uhwi_p (size);
 
-  tree range[2] = { NULL_TREE, NULL_TREE };
   if (size)
     get_size_range (size, range);
 
@@ -3128,19 +3144,15 @@  check_sizes (int opt, tree exp, tree size, tree maxlen, tree str, tree objsize)
 
       if (range[0] == range[1])
 	warning_at (loc, opt,
-		    "%K%qD: specified size %wu "
-		    "exceeds maximum object size %wu",
-		    exp, get_callee_fndecl (exp),
-		    tree_to_uhwi (range[0]),
-		    tree_to_uhwi (maxobjsize));
+		    "%K%qD: specified size %E "
+		    "exceeds maximum object size %E",
+		    exp, get_callee_fndecl (exp), range[0], maxobjsize);
 	  else
 	    warning_at (loc, opt,
-			"%K%qD: specified size between %wu and %wu "
-			"exceeds maximum object size %wu",
+			"%K%qD: specified size between %E and %E "
+			"exceeds maximum object size %E",
 			exp, get_callee_fndecl (exp),
-			tree_to_uhwi (range[0]),
-			tree_to_uhwi (range[1]),
-			tree_to_uhwi (maxobjsize));
+			range[0], range[1], maxobjsize);
       return false;
     }
 
@@ -3154,32 +3166,40 @@  check_sizes (int opt, tree exp, tree size, tree maxlen, tree str, tree objsize)
 	      || (tree_fits_uhwi_p (size)
 		  && tree_int_cst_lt (size, range[0]))))
 	{
-	  unsigned HOST_WIDE_INT uwir0 = tree_to_uhwi (range[0]);
-
 	  location_t loc = tree_nonartificial_location (exp);
 
-	  if (at_least_one)
-	    warning_at (loc, opt,
-			"%K%qD: writing at least %wu byte into a region "
-			"of size %wu overflows the destination",
-			exp, get_callee_fndecl (exp), uwir0,
-			tree_to_uhwi (objsize));
-	  else if (range[0] == range[1])
+	  if (size == slen && at_least_one)
+	    {
+	      /* This is a call to strcpy with a destination of 0 size
+		 and a source of unknown length.  The call will write
+		 at least one byte past the end of the destination.  */
+	      warning_at (loc, opt,
+			  "%K%qD: writing %E or more bytes into a region "
+			  "of size %E overflows the destination",
+			  exp, get_callee_fndecl (exp), range[0], objsize);
+	    }
+	  else if (tree_int_cst_equal (range[0], range[1]))
 	    warning_at (loc, opt,
-			(uwir0 == 1
-			 ? G_("%K%qD: writing %wu byte into a region "
-			      "of size %wu overflows the destination")
-			 : G_("%K%qD writing %wu bytes into a region "
-			      "of size %wu overflows the destination")),
-			exp, get_callee_fndecl (exp), uwir0,
-			tree_to_uhwi (objsize));
+			(integer_onep (range[0])
+			 ? G_("%K%qD: writing %E byte into a region "
+			      "of size %E overflows the destination")
+			 : G_("%K%qD writing %E bytes into a region "
+			      "of size %E overflows the destination")),
+			exp, get_callee_fndecl (exp), range[0], objsize);
+	  else if (tree_int_cst_sign_bit (range[1]))
+	    {
+	      /* Avcoid printing the upper bound if it's invalid.  */
+	      warning_at (loc, opt,
+			  "%K%qD: writing %E or more bytes into a region "
+			  "of size %E overflows the destination",
+			  exp, get_callee_fndecl (exp), range[0], objsize);
+	    }
 	  else
 	    warning_at (loc, opt,
-			"%K%qD: writing between %wu and %wu bytes "
-			"into a region of size %wu overflows "
-			"the destination",
-			exp, get_callee_fndecl (exp), uwir0,
-			tree_to_uhwi (range[1]), tree_to_uhwi (objsize));
+			"%K%qD: writing between %E and %E bytes into "
+			"a region of size %E overflows the destination",
+			exp, get_callee_fndecl (exp), range[0],	range[1],
+			objsize);
 
 	  /* Return error when an overflow has been detected.  */
 	  return false;
@@ -3204,45 +3224,68 @@  check_sizes (int opt, tree exp, tree size, tree maxlen, tree str, tree objsize)
 		 is greater than the object size if both are big.  */
 	      if (range[0] == range[1])
 		warning_at (loc, opt,
-			    "%K%qD: specified bound %wu "
-			    "exceeds maximum object size %wu",
+			    "%K%qD: specified bound %E "
+			    "exceeds maximum object size %E",
 			    exp, get_callee_fndecl (exp),
-			    tree_to_uhwi (range[0]),
-			    tree_to_uhwi (maxobjsize));
+			    range[0], maxobjsize);
 	      else
 		warning_at (loc, opt,
-			    "%K%qD: specified bound between %wu and %wu "
-			    " exceeds maximum object size %wu",
+			    "%K%qD: specified bound between %E and %E "
+			    "exceeds maximum object size %E",
 			    exp, get_callee_fndecl (exp),
-			    tree_to_uhwi (range[0]),
-			    tree_to_uhwi (range[1]),
-			    tree_to_uhwi (maxobjsize));
+			    range[0], range[1], maxobjsize);
 
 	      return false;
 	    }
 
 	  if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
 	    {
-	      if (range[0] == range[1])
+	      if (tree_int_cst_equal (range[0], range[1]))
 		warning_at (loc, opt,
-			    "%K%qD: specified bound %wu "
-			    "exceeds the size %wu of the destination",
+			    "%K%qD: specified bound %E "
+			    "exceeds destination size %E",
 			    exp, get_callee_fndecl (exp),
-			    tree_to_uhwi (range[0]),
-			    tree_to_uhwi (objsize));
+			    range[0], objsize);
 	      else
 		warning_at (loc, opt,
-			    "%K%qD: specified bound between %wu and %wu "
-			    " exceeds the size %wu of the destination",
+			    "%K%qD: specified bound between %E and %E "
+			    "exceeds destination size %E",
 			    exp, get_callee_fndecl (exp),
-			    tree_to_uhwi (range[0]),
-			    tree_to_uhwi (range[1]),
-			    tree_to_uhwi (objsize));
+			    range[0], range[1], objsize);
 	      return false;
 	    }
 	}
     }
 
+  if (slen
+      && slen == src
+      && size && range[0]
+      && tree_int_cst_lt (slen, range[0]))
+    {
+      location_t loc = tree_nonartificial_location (exp);
+
+      if (tree_int_cst_equal (range[0], range[1]))
+	warning_at (loc, opt,
+		    (tree_int_cst_equal (range[0], integer_one_node)
+		     ? G_("%K%qD: reading %E byte from a region of size %E")
+		     : G_("%K%qD reading %E bytes from a region of size %E")),
+		    exp, get_callee_fndecl (exp), range[0], slen);
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.  */
+	  warning_at (loc, opt,
+		      "%K%qD: reading %E or more bytes from a region "
+		      "of size %E",
+		      exp, get_callee_fndecl (exp), range[0], slen);
+	}
+      else
+	warning_at (loc, opt,
+		    "%K%qD: reading between %E and %E bytes from a region "
+		    "of size %E",
+		    exp, get_callee_fndecl (exp), range[0], range[1], slen);
+      return false;
+    }
+
   return true;
 }
 
@@ -3253,7 +3296,7 @@  check_sizes (int opt, tree exp, tree size, tree maxlen, tree str, tree objsize)
    be determined.  */
 
 static inline tree
-compute_dest_size (tree dest, int ostype)
+compute_objsize (tree dest, int ostype)
 {
   unsigned HOST_WIDE_INT size;
   if (compute_builtin_object_size (dest, ostype & 3, &size))
@@ -3263,24 +3306,53 @@  compute_dest_size (tree dest, int ostype)
 }
 
 /* Helper to determine and check the sizes of the source and the destination
-   of calls to __builtin_{bzero,memcpy,memset} calls.  Use Object Size type-0
-   regardless of the OPT_Wstringop_overflow_ setting.  Returns true on success
+   of calls to __builtin_{bzero,memcpy,mempcpy,memset} calls.  EXP is the
+   call expression, DEST is the destination argument, SRC is the source
+   argument or null, and LEN is the number of bytes.  Use Object Size type-0
+   regardless of the OPT_Wstringop_overflow_ setting.  Return true on success
    (no overflow or invalid sizes), false otherwise.  */
 
 static bool
-check_memop_sizes (tree exp, tree dest, tree size)
+check_memop_sizes (tree exp, tree dest, tree src, tree size)
 {
   if (!warn_stringop_overflow)
     return true;
 
   /* For functions like memset and memcpy that operate on raw memory
-     try to determine the size of the largest destination object using
-     type-0 Object Size regardless of the object size type specified
-     by the option.  */
-  tree objsize = compute_dest_size (dest, 0);
+     try to determine the size of the largest source and destination
+     object using type-0 Object Size regardless of the object size
+     type specified by the option.  */
+  tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
+  tree dstsize = compute_objsize (dest, 0);
 
   return check_sizes (OPT_Wstringop_overflow_, exp,
-		      size, /*maxlen=*/NULL_TREE, /*str=*/NULL_TREE, objsize);
+		      size, /*maxlen=*/NULL_TREE, srcsize, dstsize);
+}
+
+/* Validate memchr arguments without performing any expansion.
+   Return NULL_RTX.  */
+
+static rtx
+expand_builtin_memchr (tree exp, rtx)
+{
+  if (!validate_arglist (exp,
+ 			 POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+
+  tree arg1 = CALL_EXPR_ARG (exp, 0);
+  tree len = CALL_EXPR_ARG (exp, 2);
+
+  /* Diagnose calls where the specified length exceeds the size
+     of the object.  */
+  if (warn_stringop_overflow)
+    {
+      tree size = compute_objsize (arg1, 0);
+      check_sizes (OPT_Wstringop_overflow_,
+		   exp, len, /*maxlen=*/NULL_TREE,
+		   size, /*objsize=*/NULL_TREE);
+    }
+
+  return NULL_RTX;
 }
 
 /* Expand a call EXP to the memcpy builtin.
@@ -3299,7 +3371,7 @@  expand_builtin_memcpy (tree exp, rtx target)
   tree src = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, len);
+  check_memop_sizes (exp, dest, src, len);
 
   return expand_builtin_memcpy_args (dest, src, len, target, exp);
 }
@@ -3357,7 +3429,7 @@  expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode)
   /* Avoid expanding mempcpy into memcpy when the call is determined
      to overflow the buffer.  This also prevents the same overflow
      from being diagnosed again when expanding memcpy.  */
-  if (!check_memop_sizes (exp, dest, len))
+  if (!check_memop_sizes (exp, dest, src, len))
     return NULL_RTX;
 
   return expand_builtin_mempcpy_args (dest, src, len,
@@ -3553,7 +3625,7 @@  expand_builtin_strcat (tree exp, rtx)
      just diagnose cases when the souce string is longer than
      the destination object.  */
 
-  tree destsize = compute_dest_size (dest, warn_stringop_overflow - 1);
+  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   check_sizes (OPT_Wstringop_overflow_,
 	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
@@ -3577,7 +3649,7 @@  expand_builtin_strcpy (tree exp, rtx target)
 
   if (warn_stringop_overflow)
     {
-      tree destsize = compute_dest_size (dest, warn_stringop_overflow - 1);
+      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
       check_sizes (OPT_Wstringop_overflow_,
 		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
     }
@@ -3714,7 +3786,7 @@  check_strncat_sizes (tree exp, tree objsize)
       /* If it hasn't been provided by __strncat_chk, try to determine
 	 the size of the destination object into which the source is
 	 being copied.  */
-      objsize = compute_dest_size (dest, warn_stringop_overflow - 1);
+      objsize = compute_objsize (dest, warn_stringop_overflow - 1);
     }
 
   /* Add one for the terminating nul.  */
@@ -3730,9 +3802,8 @@  check_strncat_sizes (tree exp, tree 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));
+		  "specified bound %E equals destination size",
+		  maxlen);
 
       return false;
     }
@@ -3779,7 +3850,7 @@  expand_builtin_strncat (tree exp, rtx)
   /* Try to verify that the destination is big enough for the shortest
      string.  First try to determine the size of the destination object
      into which the source is being copied.  */
-  tree destsize = compute_dest_size (dest, warn_stringop_overflow - 1);
+  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   /* Add one for the terminating nul.  */
   tree srclen = (lenrange[0]
@@ -3794,9 +3865,7 @@  expand_builtin_strncat (tree exp, rtx)
       && 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));
+		  "specified bound %E equals destination size", maxlen);
 
       return NULL_RTX;
     }
@@ -3835,24 +3904,13 @@  expand_builtin_strncpy (tree exp, rtx target)
 
       if (warn_stringop_overflow)
 	{
-	  /* Try to determine the range of lengths that the source expression
-	     refers to.  */
-	  tree lenrange[2];
-	  if (slen)
-	    lenrange[0] = lenrange[1] = slen;
-	  else
-	    {
-	      get_range_strlen (src, lenrange);
-	      slen = lenrange[0];
-	    }
-
-	  tree destsize = compute_dest_size (dest,
-					     warn_stringop_overflow - 1);
+	  tree destsize = compute_objsize (dest,
+					   warn_stringop_overflow - 1);
 
 	  /* The number of bytes to write is LEN but check_sizes will also
 	     check SLEN if LEN's value isn't known.  */
 	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE, slen, destsize);
+		       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
 	}
 
       /* We must be passed a constant len and src parameter.  */
@@ -3947,7 +4005,7 @@  expand_builtin_memset (tree exp, rtx target, machine_mode mode)
   tree val = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, len);
+  check_memop_sizes (exp, dest, NULL_TREE, len);
 
   return expand_builtin_memset_args (dest, val, len, target, mode, exp);
 }
@@ -4136,7 +4194,7 @@  expand_builtin_bzero (tree exp)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree size = CALL_EXPR_ARG (exp, 1);
 
-  check_memop_sizes (exp, dest, size);
+  check_memop_sizes (exp, dest, NULL_TREE, size);
 
   /* New argument list transforming bzero(ptr x, int y) to
      memset(ptr x, int 0, size_t y).   This is done this way
@@ -4189,6 +4247,23 @@  expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
   tree arg1 = CALL_EXPR_ARG (exp, 0);
   tree arg2 = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
+
+  /* Diagnose calls where the specified length exceeds the size of either
+     object.  */
+  if (warn_stringop_overflow)
+    {
+      tree size = compute_objsize (arg1, 0);
+      if (check_sizes (OPT_Wstringop_overflow_,
+		       exp, len, /*maxlen=*/NULL_TREE,
+		       size, /*objsize=*/NULL_TREE))
+	{
+	  size = compute_objsize (arg2, 0);
+	  check_sizes (OPT_Wstringop_overflow_,
+		       exp, len, /*maxlen=*/NULL_TREE,
+		       size, /*objsize=*/NULL_TREE);
+	}
+    }
+
   machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
   location_t loc = EXPR_LOCATION (exp);
 
@@ -6712,6 +6787,12 @@  expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
 	return target;
       break;
 
+    case BUILT_IN_MEMCHR:
+      target = expand_builtin_memchr (exp, target);
+      if (target)
+	return target;
+      break;
+
     case BUILT_IN_MEMCPY:
       target = expand_builtin_memcpy (exp, target);
       if (target)
@@ -9698,8 +9779,8 @@  maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       /* 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;
+      check_strncat_sizes (exp, objsize);
+      return;
     }
 
   check_sizes (OPT_Wstringop_overflow_, exp,
diff --git a/gcc/testsuite/g++.dg/ext/strncpy-chk1.C b/gcc/testsuite/g++.dg/ext/strncpy-chk1.C
index d67d6bf..ff591a0 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 "specified bound 36 exceeds the size 35 of the destination" }
+  __builtin___strncpy_chk (dest, src, n, __builtin_object_size (dest, 0));	// { dg-warning "specified bound 36 exceeds destination size 35" }
 }
 
 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 2e6189b..c72532b 100644
--- a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
+++ b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
@@ -1,6 +1,6 @@ 
 // Test -Wsizeof-pointer-memaccess warnings.
 // { dg-do compile }
-// { dg-options "-Wall -Wno-sizeof-array-argument" }
+// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" }
 // Test just twice, once with -O0 non-fortified, once with -O2 fortified.
 // { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } }
 // { dg-skip-if "" { *-*-* }  { "-flto" } { "" } }
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
index 3d970d7..ebf6e85 100644
--- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
@@ -37,9 +37,9 @@  test (int arg, ...)
   vx = stpcpy (&buf2[18], "a");
   vx = stpcpy (&buf2[18], "ab"); /* { dg-warning "writing 3" "stpcpy" } */
   strncpy (&buf2[18], "a", 2);
-  strncpy (&buf2[18], "a", 3); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" "strncpy" } */
+  strncpy (&buf2[18], "a", 3); /* { dg-warning "specified bound 3 exceeds destination size 2" "strncpy" } */
   strncpy (&buf2[18], "abc", 2);
-  strncpy (&buf2[18], "abc", 3); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" "strncpy" } */
+  strncpy (&buf2[18], "abc", 3); /* { dg-warning "writing 3 " "strncpy" } */
   memset (buf2, '\0', sizeof (buf2));
   strcat (&buf2[18], "a");
   memset (buf2, '\0', sizeof (buf2));
@@ -54,7 +54,7 @@  test (int arg, ...)
      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 "specified bound 3 exceeds the size 2 of the destination" "snprintf" } */
+  snprintf (&buf2[18], 3, "%d", x); /* { dg-warning "specified bound 3 exceeds destination size 2" "snprintf" } */
   va_start (ap, arg);
   vsprintf (&buf2[18], "a", ap);
   va_end (ap);
@@ -67,7 +67,7 @@  test (int arg, ...)
   va_end (ap);
   va_start (ap, arg);
   /* See snprintf above.  */
-  vsnprintf (&buf2[18], 3, "%s", ap); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" "vsnprintf" } */
+  vsnprintf (&buf2[18], 3, "%s", ap); /* { dg-warning "specified bound 3 exceeds destination size 2" "vsnprintf" } */
   va_end (ap);
 
   p = p + 10;
@@ -94,7 +94,7 @@  void
 test2 (const H h)
 {
   char c;
-  strncpy (&c, str, 3); /* { dg-warning "specified bound 3 exceeds the size 1 of the destination" "strncpy" } */
+  strncpy (&c, str, 3); /* { dg-warning "specified bound 3 exceeds destination size 1" "strncpy" } */
 
   struct { char b[4]; } x;
   sprintf (x.b, "%s", "ABCD"); /* { dg-warning "writing 5" "sprintf" } */
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c
index 4857bda..06d1779 100644
--- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c
@@ -77,7 +77,7 @@  signed_range (ptrdiff_t min, ptrdiff_t max)
 #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
+/* Return a pointer to constant string whose length is at least MINLEN
    and at most 10.  */
 static inline const char*
 string_range (size_t minlen)
@@ -122,7 +122,7 @@  void test_memcpy_range (void *d, const void *s)
 
   memcpy (buf + size_max, s, UR (1, 2));  /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" "excessive pointer offset" { xfail *-*-* } } */
 
-  memcpy (buf, s, UR (ssize_max, size_max));   /* { dg-warning "writing between \[0-9\]+ and \[0-9\]+ bytes into a region of size 5 overflows the destination" } */
+  memcpy (buf, s, UR (ssize_max, size_max));   /* { dg-warning "writing \[0-9\]+ or more 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" } */
 
@@ -207,7 +207,7 @@  void test_mempcpy_range (void *d, const void *s)
 
   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, size_max));   /* { dg-warning "writing \[0-9\]+ or more 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" } */
 
@@ -233,7 +233,7 @@  void test_memset_range (void *d)
 
   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, size_max));   /* { dg-warning "writing \[0-9\]+ or more 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" } */
 
@@ -259,7 +259,7 @@  void test_bzero_range (void *d)
 
   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, size_max));   /* { dg-warning "writing \[0-9\]+ or more 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" } */
 
@@ -281,7 +281,7 @@  void test_strcat_range (void)
   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 " } */
+  strcat (buf, S (5));   /* { dg-warning "writing between 6 and 11 bytes into a region of size 5 " } */
 
   {
     /* The implementation of the warning isn't smart enough to determine
@@ -320,10 +320,10 @@  void test_strcpy (const char *src)
   strcpy (a.a + 2, src);    /* { dg-warning "writing at least 1 byte into a region of size 0 " "strcpy into empty substring" { xfail *-*-* } } */
 
   /* This does work.  */
-  strcpy (a.a + 5, src);    /* { dg-warning "writing at least 1 byte into a region of size 0 " } */
+  strcpy (a.a + 5, src);    /* { dg-warning "writing 1 or more bytes into a region of size 0 " } */
 
   /* As does this.  */
-  strcpy (a.a + 17, src);    /* { dg-warning "writing at least 1 byte into a region of size 0 " } */
+  strcpy (a.a + 17, src);    /* { dg-warning "writing 1 or more bytes into a region of size 0 " } */
 }
 
 /* Test strcpy with a non-constant source string of length in a known
@@ -337,16 +337,16 @@  void test_strcpy_range (void)
   strcpy (buf, S (1));
   strcpy (buf, S (2));
   strcpy (buf, S (4));
-  strcpy (buf, S (5));   /* { dg-warning "writing 6 bytes into a region of size 5 " } */
-  strcpy (buf, S (6));   /* { dg-warning "writing 7 bytes into a region of size 5 " } */
-  strcpy (buf, S (7));   /* { dg-warning "writing 8 bytes into a region of size 5 " } */
-  strcpy (buf, S (8));   /* { dg-warning "writing 9 bytes into a region of size 5 " } */
-  strcpy (buf, S (9));   /* { dg-warning "writing 10 bytes into a region of size 5 " } */
-  strcpy (buf, S (10));   /* { dg-warning "writing 11 bytes into a region of size 5 " } */
+  strcpy (buf, S (5));   /* { dg-warning "writing between 6 and 11 bytes into a region of size 5 " } */
+  strcpy (buf, S (6));   /* { dg-warning "writing between 7 and 11 bytes" } */
+  strcpy (buf, S (7));   /* { dg-warning "writing between 8 and 11 bytes" } */
+  strcpy (buf, S (8));   /* { dg-warning "writing between 9 and 11 bytes" } */
+  strcpy (buf, S (9));   /* { dg-warning "writing between 10 and 11 bytes" } */
+  strcpy (buf, S (10));   /* { dg-warning "writing 11 bytes" } */
 
-  strcpy (buf + 5, S (0));   /* { dg-warning "writing 1 byte into a region of size 0 " } */
+  strcpy (buf + 5, S (0));   /* { dg-warning "writing between 1 and 11 bytes" } */
 
-  strcpy (buf + 17, S (0));   /* { dg-warning "writing 1 byte into a region of size 0 " } */
+  strcpy (buf + 17, S (0));   /* { dg-warning "writing between 1 and 11 bytes " } */
 }
 
 /* Test strncat with an argument referencing a non-constant string of
@@ -364,31 +364,31 @@  void test_strncat_range (void)
 
   strncat (buf + 5, S (0), 0);
 
-  strncat (buf + 5, S (0), 1);   /* { dg-warning "specified bound 1 exceeds the size 0 of the destination " } */
-  strncat (buf + 5, S (1), 1);   /* { dg-warning "specified bound 1 exceeds the size 0 of the destination " } */
+  strncat (buf + 5, S (0), 1);   /* { dg-warning "specified bound 1 exceeds destination size 0" } */
+  strncat (buf + 5, S (1), 1);   /* { dg-warning "specified bound 1 exceeds destination size 0" } */
 
   /* 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 (0), 5);   /* { dg-warning "specified bound 5 equals destination size" } */
+  strncat (buf, S (0), 6);   /* { dg-warning "specified bound 6 exceeds destination size 5" } */
 
   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" } */
+  strncat (buf, S (1), 5);   /* { dg-warning "specified bound 5 equals destination size" } */
+  strncat (buf, S (1), 6);   /* { dg-warning "specified bound 6 exceeds destination size 5" } */
+  strncat (buf, S (2), 6);   /* { dg-warning "specified bound 6 exceeds destination size 5" } */
 
   /* 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" } */
+  strncat (buf, S (5), 6);   /* { dg-warning "specified bound 6 exceeds destination size 5" } */
+  strncat (buf, S (7), 6);   /* { dg-warning "specified bound 6 exceeds destination size 5" } */
 
   {
     /* The implementation of the warning isn't smart enough to determine
@@ -413,15 +413,15 @@  void test_strncat_chk_range (char *d)
   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 (0), 5);   /* { dg-warning "specified bound 5 equals destination size" } */
 
   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), 5);   /* { dg-warning "specified bound 5 equals destination size" } */
 
-  strncat_chk (buf, S (5), 10);   /* { dg-warning "specified bound \[0-9\]+ exceeds the size 5 of the destination " } */
+  strncat_chk (buf, S (5), 10);   /* { dg-warning "specified bound \[0-9\]+ exceeds destination size 5" } */
 
   strncat_chk (d, S (5), size_max);   /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size " } */
 }
@@ -491,7 +491,7 @@  void test_strncpy_string_count_range (char *dst, const char *src)
 
   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, ssize_max + 1));   /* { dg-warning "writing \[0-9\]+ or more 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" } */
 
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c
new file mode 100644
index 0000000..8d1aa5c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c
@@ -0,0 +1,173 @@ 
+/* Test exercising -Wstringop-overflow warnings for reading past the end.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wstringop-overflow=1 -ftrack-macro-expansion=0" } */
+
+#define PTRDIFF_MAX   __PTRDIFF_MAX__
+#define SIZE_MAX      __SIZE_MAX__
+
+#define offsetof(type, mem)   __builtin_offsetof (type, mem)
+
+/* Return the number of bytes from member MEM of TYPE to the end
+   of object OBJ.  */
+#define offsetfrom(type, obj, mem) (sizeof (obj) - offsetof (type, mem))
+
+
+typedef __SIZE_TYPE__ size_t;
+extern void* memchr (const void*, int, size_t);
+extern int memcmp (const void*, const void*, size_t);
+extern void* memcpy (void*, const void*, size_t);
+extern void* memmove (void*, const void*, size_t);
+extern void* mempcpy (void*, const void*, size_t);
+
+#define memchr(d, s, n) sink (memchr (d, s, n))
+#define memcmp(d, s, n) sink (d, memcmp (d, s, n))
+#define memcpy(d, s, n) sink (memcpy (d, s, n))
+#define memmove(d, s, n) sink (memmove (d, s, n))
+#define mempcpy(d, s, n) sink (mempcpy (d, s, n))
+
+struct A { char a, b; };
+struct B { struct A a; char c, d; };
+
+/* Function to call to "escape" pointers from tests below to prevent
+   GCC from assuming the values of the objects they point to stay
+   the unchanged.  */
+void 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);
+
+/* Return a random unsigned value between MIN and MAX.  */
+
+static inline size_t
+range (size_t min, size_t max)
+{
+  const size_t val = random_unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+#define R(min, max)   range (min, max)
+
+/* Verify that reading beyond the end of a local array is diagnosed.  */
+
+void test_memop_warn_local (void *p, const void *q)
+{
+  memcpy (p, "1234", R (6, 7));   /* { dg-warning "reading between 6 and 7 bytes from a region of size 5" } */
+
+  struct A a[2];
+
+  memcpy (p, a, R (7, 8));   /* { dg-warning "reading between 7 and 8 bytes from a region of size 4" } */
+
+  /* At -Wstringop-overflow=1 the destination is considered to be
+     the whole array and its size is therefore sizeof a.  */
+  memcpy (p, &a[0], R (8, 9));   /* { dg-warning "reading between 8 and 9 bytes from a region of size 4" } */
+
+  /* Verify the same as above but by reading from the first mmeber
+     of the first element of the array.  */
+  memcpy (p, &a[0].a, R (8, 9));   /* { dg-warning "reading between 8 and 9 bytes from a region of size 4" } */
+
+  struct B b[2];
+
+  memcpy (p, &b[0], R (12, 32));   /* { dg-warning "reading between 12 and 32 bytes from a region of size 8" } */
+
+  /* Verify memchr/memcmp.  */
+  int i = R (0, 255);
+  memchr ("", i, 2);   /* { dg-warning "reading 2 bytes from a region of size 1" } */
+  memchr ("", i, 2);   /* { dg-warning "reading 2 bytes from a region of size 1" } */
+  memchr ("123", i, 5);   /* { dg-warning "reading 5 bytes from a region of size 4" } */
+  memchr (a, i, sizeof a + 1);   /* { dg-warning "reading 5 bytes from a region of size 4" } */
+
+  memcmp (p, "", 2);   /* { dg-warning "reading 2 bytes from a region of size 1" } */
+  memcmp (p, "123", 5);   /* { dg-warning "reading 5 bytes from a region of size 4" } */
+  memcmp (p, a, sizeof a + 1);   /* { dg-warning "reading 5 bytes from a region of size 4" } */
+
+  size_t n = PTRDIFF_MAX + (size_t)1;
+  memchr (p, 1, n);   /* { dg-warning "exceeds maximum object size" } */
+  memcmp (p, q, n);   /* { dg-warning "exceeds maximum object size" } */
+
+  n = SIZE_MAX;
+  memchr (p, 1, n);   /* { dg-warning "exceeds maximum object size" } */
+  memcmp (p, q, n);   /* { dg-warning "exceeds maximum object size" } */
+}
+
+/* Verify that reading beyond the end of a dynamically allocated array
+   of known size is diagnosed.  */
+
+void test_memop_warn_alloc (void *p)
+{
+  size_t n;
+
+  n = range (8, 32);
+
+  struct A *a = __builtin_malloc (sizeof *a * 2);
+
+  memcpy (p, a, n);   /* { dg-warning "reading between 8 and 32 bytes from region of size 4" "memcpy from allocated" { xfail *-*-*} } */
+
+  memcpy (p, &a[0], n);   /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" { xfail *-*-*} } */
+
+  memcpy (p, &a[0].a, n);   /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" { xfail *-*-*} } */
+
+  n = range (12, 32);
+
+  struct B *b = __builtin_malloc (sizeof *b * 2);
+
+  memcpy (p, &b[0], n);   /* { dg-warning "reading between 12 and 32 bytes from a region of size 8" "memcpy from allocated" { xfail *-*-*} } */
+
+  /* Verify memchr/memcmp.  */
+  n = sizeof *b * 2 + 1;
+
+  memchr (b, 1, n);   /* { dg-warning "reading 5 bytes from a region of size 4" "memcmp from allocated" { xfail *-*-* } } */
+  memcmp (p, b, n);   /* { dg-warning "reading 5 bytes from a region of size 4" "memcmp from allocated" { xfail *-*-* } } */
+}
+
+
+void test_memop_nowarn (void *p)
+{
+  struct B b[2];
+
+  size_t n = range (sizeof b, 32);
+
+  /* Verify that copying the whole array is not diagnosed regardless
+     of whether the expression pointing to its beginning is obtained
+     from the array itself or its first member(s).  */
+  memcpy (p, b, n);
+
+  memcpy (p, &b[0], n);
+
+  memcpy (p, &b[0].a, n);
+
+  memcpy (p, &b[0].a.a, n);
+
+  /* Verify that memchr/memcmp doesn't cause a warning. */
+  memchr (p, 1, n);
+  memchr (b, 2, n);
+  memchr (&b[0], 3, n);
+  memchr (&b[0].a, 4, n);
+  memchr (&b[0].a.a, 5, n);
+  memchr ("01234567", R (0, 255), n);
+
+  memcmp (p, p, n);
+  memcmp (p, b, n);
+  memcmp (p, &b[0], n);
+  memcmp (p, &b[0].a, n);
+  memcmp (p, &b[0].a.a, n);
+  memcmp (p, "01234567", n);
+}
+
+
+/* The following function could specify in its API that it takes
+   an array of exactly two elements, as shown below (or simply be
+   called with such an array).  Verify that reading from both
+   elements is not diagnosed.  */
+void test_memop_nowarn_arg (void*, const struct A[2]);
+
+void test_memop_nowarn_arg (void *p, const struct A *a)
+{
+  memcpy (p, a, 2 * sizeof *a);
+
+  memcpy (p, a, range (2 * sizeof *a, 123));
+
+  memchr (p, 1, 1234);
+  memcmp (p, a, 1234);
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c b/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c
index daff680..851fb74 100644
--- a/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c
@@ -28,11 +28,11 @@  test (int arg, ...)
   *p = 0;
   strncat (p, "abcdefgh", 11);
   *p = 0;
-  strncat (p, "abcdefghijkl", 11); /* { dg-warning "specified bound 11 exceeds the size 10 of the destination" } */
+  strncat (p, "abcdefghijkl", 11); /* { dg-warning "specified bound 11 exceeds destination size 10" } */
   *p = 0;
   strncat (p, q, 9);
   *p = 0;
-  strncat (p, q, 10); /* { dg-warning "specified bound 10 equals the size of the destination" } */
+  strncat (p, q, 10); /* { dg-warning "specified bound 10 equals destination size" } */
   *p = 0;
-  strncat (p, q, 11); /* { dg-warning "specified bound 11 exceeds the size 10 of the destination" } */
+  strncat (p, q, 11); /* { dg-warning "specified bound 11 exceeds destination size 10" } */
 }
diff --git a/gcc/testsuite/gcc.dg/out-of-bounds-1.c b/gcc/testsuite/gcc.dg/out-of-bounds-1.c
index 8cdf643..1c76341 100644
--- a/gcc/testsuite/gcc.dg/out-of-bounds-1.c
+++ b/gcc/testsuite/gcc.dg/out-of-bounds-1.c
@@ -9,5 +9,5 @@  void ProjectOverlay(const float localTextureAxis[2], char *lump)
 {
    const void *d = &localTextureAxis;
    int size = sizeof(float)*8 ;
-   __builtin_memcpy( &lump[ 0 ], d, size );  
+   __builtin_memcpy( &lump[ 0 ], d, size );  /* { dg-warning "reading" } */
 }
diff --git a/gcc/testsuite/gcc.dg/pr78138.c b/gcc/testsuite/gcc.dg/pr78138.c
index cfc5a8c..d21b854 100644
--- a/gcc/testsuite/gcc.dg/pr78138.c
+++ b/gcc/testsuite/gcc.dg/pr78138.c
@@ -10,7 +10,7 @@  extern char* strcpy (char*, const char*);
 
 void f (int i, int j)
 {
-  strcpy (d, j ? "12345" : "123456");   /* { dg-warning ".strcpy. writing 6 bytes into a region of size 5" } */
+  strcpy (d, j ? "12345" : "123456");   /* { dg-warning ".strcpy.: writing between 6 and 7 bytes into a region of size 5 " } */
 }
 
 void g (void *p)
diff --git a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
index b5a59f4..a73e45f 100644
--- a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@ 
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument" } */
+/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
 /* Test just twice, once with -O0 non-fortified, once with -O2 fortified.  */
 /* { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } } */
 /* { dg-skip-if "" { *-*-* }  { "-flto" } { "" } } */
diff --git a/gcc/testsuite/gfortran.dg/mvbits_7.f90 b/gcc/testsuite/gfortran.dg/mvbits_7.f90
index 2c7cab8..104ec67 100644
--- a/gcc/testsuite/gfortran.dg/mvbits_7.f90
+++ b/gcc/testsuite/gfortran.dg/mvbits_7.f90
@@ -25,6 +25,6 @@  contains
   SUBROUTINE bar (x, NF4, NF3, NF1, MF1, MF4, MF3)
     TYPE(t) x(NF4, NF3)  ! Dependency through variable indices
     CALL MVBITS (x(NF4:NF1:MF1, NF1:NF3)%i, 1, &
-                 6, x(-MF4:-MF1:-NF1, -MF1:-MF3)%i, 9)
+                 6, x(-MF4:-MF1:-NF1, -MF1:-MF3)%i, 9)   ! { dg-warning "reading" }
   END SUBROUTINE
 end