[4/5] - extend -Wstringop-overflow to detect out-of-bounds accesses to array parameters

Message ID 91515012-b304-3e3d-63d7-91bb997c89df@gmail.com
State New
Headers show
Series
  • add checking of function array parameters (PR 50584)
Related show

Commit Message

Bill Schmidt via Gcc-patches July 29, 2020, 1:22 a.m.
Patch 4 adds support to the machinery behind -Wstringop-overflow
to issue warnings for (likely) out of bounds accesses in calls to
functions with the internal attribute access specification.  This
implements the feature pr50584 asks for (plus more).

Comments

Bill Schmidt via Gcc-patches Aug. 13, 2020, 4:31 p.m. | #1
On Tue, 2020-07-28 at 19:22 -0600, Martin Sebor via Gcc-patches wrote:
> Patch 4 adds support to the machinery behind -Wstringop-overflow

> to issue warnings for (likely) out of bounds accesses in calls to

> functions with the internal attribute access specification.  This

> implements the feature pr50584 asks for (plus more).


> [4/5] - Extend -Wstringop-overflow to detect out-of-bounds accesses to array parameters.

> 

> gcc/ChangeLog:

> 

> 	* tree-ssa-uninit.c (maybe_warn_pass_by_reference): Handle attribute

> 	access internal representation of arrays.

> 

> gcc/testsuite/ChangeLog:

> 

> 	* gcc.dg/uninit-37.c: New test.

> 

> gcc/ChangeLog:

> 

> 	PR c/50584

> 	* builtins.c (warn_for_access): Add argument.  Distinguish between

> 	reads and writes.

> 	(check_access): Add argument.  Distinguish between reads and writes.

> 	(gimple_call_alloc_size): Set range even on failure.

> 	(gimple_parm_array_size): New function.

> 	(compute_objsize): Call it.

> 	(check_memop_access): Pass check_access an additional argument.

> 	(expand_builtin_memchr, expand_builtin_strcat): Same.

> 	(expand_builtin_strcpy, expand_builtin_stpcpy_1): Same.

> 	(expand_builtin_stpncpy, check_strncat_sizes): Same.

> 	(expand_builtin_strncat, expand_builtin_strncpy): Same.

> 	(expand_builtin_memcmp): Same.

> 	* builtins.h (compute_objsize): Declare a new overload.

> 	(gimple_parm_array_size): Declare.

> 	(check_access): Add argument.

> 	* calls.c (append_attrname): Simplify.

> 	(maybe_warn_rdwr_sizes): Handle internal attribute access.

> 

> gcc/testsuite/ChangeLog:

> 

> 	PR c/50584

> 	* c-c++-common/Wsizeof-pointer-memaccess1.c: Disable new expected

> 	warnings.

> 	* g++.dg/ext/attr-access.C: Update text of expected warnings.

> 	* gcc.dg/Wstringop-overflow-23.c: Same.

> 	* gcc.dg/Wstringop-overflow-24.c: Same.

> 	* gcc.dg/attr-access-none.c: Same.

> 	* gcc.dg/Wstringop-overflow-40.c: New test.

> 	* gcc.dg/attr-access-2.c: New test.

OK once the prereqs are approved.

jeff
>

Patch

[4/5] - Extend -Wstringop-overflow to detect out-of-bounds accesses to array parameters.

gcc/ChangeLog:

	* tree-ssa-uninit.c (maybe_warn_pass_by_reference): Handle attribute
	access internal representation of arrays.

gcc/testsuite/ChangeLog:

	* gcc.dg/uninit-37.c: New test.

gcc/ChangeLog:

	PR c/50584
	* builtins.c (warn_for_access): Add argument.  Distinguish between
	reads and writes.
	(check_access): Add argument.  Distinguish between reads and writes.
	(gimple_call_alloc_size): Set range even on failure.
	(gimple_parm_array_size): New function.
	(compute_objsize): Call it.
	(check_memop_access): Pass check_access an additional argument.
	(expand_builtin_memchr, expand_builtin_strcat): Same.
	(expand_builtin_strcpy, expand_builtin_stpcpy_1): Same.
	(expand_builtin_stpncpy, check_strncat_sizes): Same.
	(expand_builtin_strncat, expand_builtin_strncpy): Same.
	(expand_builtin_memcmp): Same.
	* builtins.h (compute_objsize): Declare a new overload.
	(gimple_parm_array_size): Declare.
	(check_access): Add argument.
	* calls.c (append_attrname): Simplify.
	(maybe_warn_rdwr_sizes): Handle internal attribute access.

gcc/testsuite/ChangeLog:

	PR c/50584
	* c-c++-common/Wsizeof-pointer-memaccess1.c: Disable new expected
	warnings.
	* g++.dg/ext/attr-access.C: Update text of expected warnings.
	* gcc.dg/Wstringop-overflow-23.c: Same.
	* gcc.dg/Wstringop-overflow-24.c: Same.
	* gcc.dg/attr-access-none.c: Same.
	* gcc.dg/Wstringop-overflow-40.c: New test.
	* gcc.dg/attr-access-2.c: New test.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 228db78f32b..bbaeb3aedd1 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3312,26 +3312,122 @@  determine_block_size (tree len, rtx len_rtx,
 }
 
 /* For an expression EXP issue an access warning controlled by option OPT
-   with access to a region SLEN bytes in size in the RANGE of sizes.  */
+   with access to a region SIZE bytes in size in the RANGE of sizes.
+   WRITE is true for a write access, READ for a read access, neither for
+   call that may or may not perform an access but for which the range
+   is expected to valid.
+   Returns true when a warning has been issued.  */
 
 static bool
 warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
-		 tree slen, bool access)
+		 tree size, bool write, bool read)
 {
   bool warned = false;
 
-  if (access)
+  if (write && read)
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+	warned = (func
+		  ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+			       "%K%qD accessing %E byte in a region "
+			       "of size %E",
+			       "%K%qD accessing %E bytes in a region "
+			       "of size %E",
+			       exp, func, range[0], size)
+		  : warning_n (loc, opt, tree_to_uhwi (range[0]),
+			       "%Kaccessing %E byte in a region "
+			       "of size %E",
+			       "%Kaccessing %E bytes in a region "
+			       "of size %E",
+			       exp, range[0], size));
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.  */
+	  warned = (func
+		    ? warning_at (loc, opt,
+				  "%K%qD accessing %E or more bytes in "
+				  "a region of size %E",
+				  exp, func, range[0], size)
+		    : warning_at (loc, opt,
+				  "%Kaccessing %E or more bytes in "
+				  "a region of size %E",
+				  exp, range[0], size));
+	}
+      else
+	warned = (func
+		  ? warning_at (loc, opt,
+				"%K%qD accessing between %E and %E bytes "
+				"in a region of size %E",
+				exp, func, range[0], range[1],
+				size)
+		  : warning_at (loc, opt,
+				"%Kaccessing between %E and %E bytes "
+				"in a region of size %E",
+				exp, range[0], range[1],
+				size));
+      return warned;
+    }
+
+  if (write)
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+	warned = (func
+		  ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+			       "%K%qD writing %E byte into a region "
+			       "of size %E overflows the destination",
+			       "%K%qD writing %E bytes into a region "
+			       "of size %E overflows the destination",
+			       exp, func, range[0], size)
+		  : warning_n (loc, opt, tree_to_uhwi (range[0]),
+			       "%Kwriting %E byte into a region "
+			       "of size %E overflows the destination",
+			       "%Kwriting %E bytes into a region "
+			       "of size %E overflows the destination",
+			       exp, range[0], size));
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.  */
+	  warned = (func
+		    ? warning_at (loc, opt,
+				  "%K%qD writing %E or more bytes into "
+				  "a region of size %E overflows "
+				  "the destination",
+				  exp, func, range[0], size)
+		    : warning_at (loc, opt,
+				  "%Kwriting %E or more bytes into "
+				  "a region of size %E overflows "
+				  "the destination",
+				  exp, range[0], size));
+	}
+      else
+	warned = (func
+		  ? warning_at (loc, opt,
+				"%K%qD writing between %E and %E bytes "
+				"into a region of size %E overflows "
+				"the destination",
+				exp, func, range[0], range[1],
+				size)
+		  : warning_at (loc, opt,
+				"%Kwriting between %E and %E bytes "
+				"into a region of size %E overflows "
+				"the destination",
+				exp, range[0], range[1],
+				size));
+      return warned;
+    }
+
+  if (read)
     {
       if (tree_int_cst_equal (range[0], range[1]))
 	warned = (func
 		  ? warning_n (loc, opt, tree_to_uhwi (range[0]),
 			       "%K%qD reading %E byte from a region of size %E",
 			       "%K%qD reading %E bytes from a region of size %E",
-			       exp, func, range[0], slen)
+			       exp, func, range[0], size)
 		  : warning_n (loc, opt, tree_to_uhwi (range[0]),
 			       "%Kreading %E byte from a region of size %E",
 			       "%Kreading %E bytes from a region of size %E",
-			       exp, range[0], slen));
+			       exp, range[0], size));
       else if (tree_int_cst_sign_bit (range[1]))
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
@@ -3339,59 +3435,47 @@  warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
 		    ? warning_at (loc, opt,
 				  "%K%qD reading %E or more bytes from a region "
 				  "of size %E",
-				  exp, func, range[0], slen)
+				  exp, func, range[0], size)
 		    : warning_at (loc, opt,
 				  "%Kreading %E or more bytes from a region "
 				  "of size %E",
-				  exp, range[0], slen));
+				  exp, range[0], size));
 	}
       else
 	warned = (func
 		  ? warning_at (loc, opt,
 				"%K%qD reading between %E and %E bytes from "
 				"a region of size %E",
-				exp, func, range[0], range[1], slen)
+				exp, func, range[0], range[1], size)
 		  : warning_at (loc, opt,
 				"%Kreading between %E and %E bytes from "
 				"a region of size %E",
-				exp, range[0], range[1], slen));
+				exp, range[0], range[1], size));
 
       return warned;
     }
 
-  if (tree_int_cst_equal (range[0], range[1]))
+  if (tree_int_cst_equal (range[0], range[1])
+      || tree_int_cst_sign_bit (range[1]))
     warned = (func
-	      ? warning_n (loc, opt, tree_to_uhwi (range[0]),
-			   "%K%qD epecting %E byte in a region of size %E",
-			   "%K%qD expecting %E bytes in a region of size %E",
-			   exp, func, range[0], slen)
-	      : warning_n (loc, opt, tree_to_uhwi (range[0]),
-			   "%Kexpecting %E byte in a region of size %E",
-			   "%Kexpecting %E bytes in a region of size %E",
-			   exp, range[0], slen));
-  else if (tree_int_cst_sign_bit (range[1]))
-    {
-      /* Avoid printing the upper bound if it's invalid.  */
-      warned = (func
-		? warning_at (loc, opt,
-			      "%K%qD expecting %E or more bytes in a region "
-			      "of size %E",
-			      exp, func, range[0], slen)
-		: warning_at (loc, opt,
-			      "%Kexpecting %E or more bytes in a region "
-			      "of size %E",
-			      exp, range[0], slen));
-    }
+	      ? warning_at (loc, OPT_Warray_parameter_,
+			    "%K%qD expecting %E or more bytes in a region "
+			    "of size %E",
+			    exp, func, range[0], size)
+	      : warning_at (loc, OPT_Warray_parameter_,
+			    "%Kexpecting %E or more bytes in a region "
+			    "of size %E",
+			    exp, range[0], size));
   else
     warned = (func
-	      ? warning_at (loc, opt,
+	      ? warning_at (loc, OPT_Warray_parameter_,
 			    "%K%qD expecting between %E and %E bytes in "
 			    "a region of size %E",
-			    exp, func, range[0], range[1], slen)
-	      : warning_at (loc, opt,
+			    exp, func, range[0], range[1], size)
+	      : warning_at (loc, OPT_Warray_parameter_,
 			    "%Kexpectting between %E and %E bytes in "
 			    "a region of size %E",
-			    exp, range[0], range[1], slen));
+			    exp, range[0], range[1], size));
   return warned;
 }
 
@@ -3545,8 +3629,9 @@  inform_access (const access_ref &ref, bool write)
    When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
-   ACCESS is true for accesses, false for simple size checks in calls
-   to functions that neither read from nor write to the region.
+   WRITE is true for write accesses, READ is true for reads.  Both are
+   false for simple size checks in calls to functions that neither read
+   from nor write to the region.
 
    When nonnull, PAD points to a more detailed description of the access.
 
@@ -3556,7 +3641,7 @@  inform_access (const access_ref &ref, bool write)
 bool
 check_access (tree exp, tree, tree, tree dstwrite,
 	      tree maxread, tree srcstr, tree dstsize,
-	      bool access /* = true */,
+	      bool write /* = true */, bool read /* = true */,
 	      const access_data *pad /* = NULL */)
 {
   int opt = OPT_Wstringop_overflow_;
@@ -3636,6 +3721,11 @@  check_access (tree exp, tree, tree, tree dstwrite,
     get_size_range (dstwrite, range);
 
   tree func = get_callee_fndecl (exp);
+  /* Read vs write access by built-ins can be determined from the const
+     qualifiers on the pointer argument.  In the absence of attribute
+     access, non-const qualified pointer arguments to user-defined
+     functions are assumed to both read and write the objects.  */
+  const bool builtin = func ? fndecl_built_in_p (func) : false;
 
   /* First check the number of bytes to be written against the maximum
      object size.  */
@@ -3717,49 +3807,10 @@  check_access (tree exp, tree, tree, tree dstwrite,
 				      "the destination",
 				      exp, range[0], dstsize));
 	    }
-	  else if (tree_int_cst_equal (range[0], range[1]))
-	    warned = (func
-		      ? warning_n (loc, opt, tree_to_uhwi (range[0]),
-				   "%K%qD writing %E byte into a region "
-				   "of size %E overflows the destination",
-				   "%K%qD writing %E bytes into a region "
-				   "of size %E overflows the destination",
-				   exp, func, range[0], dstsize)
-		      : warning_n (loc, opt, tree_to_uhwi (range[0]),
-				   "%Kwriting %E byte into a region "
-				   "of size %E overflows the destination",
-				   "%Kwriting %E bytes into a region "
-				   "of size %E overflows the destination",
-				   exp, range[0], dstsize));
-	  else if (tree_int_cst_sign_bit (range[1]))
-	    {
-	      /* Avoid printing the upper bound if it's invalid.  */
-	      warned = (func
-			? warning_at (loc, opt,
-				      "%K%qD writing %E or more bytes into "
-				      "a region of size %E overflows "
-				      "the destination",
-				      exp, func, range[0], dstsize)
-			: warning_at (loc, opt,
-				      "%Kwriting %E or more bytes into "
-				      "a region of size %E overflows "
-				      "the destination",
-				      exp, range[0], dstsize));
-	    }
 	  else
-	    warned = (func
-		      ? warning_at (loc, opt,
-				    "%K%qD writing between %E and %E bytes "
-				    "into a region of size %E overflows "
-				    "the destination",
-				    exp, func, range[0], range[1],
-				    dstsize)
-		      : warning_at (loc, opt,
-				    "%Kwriting between %E and %E bytes "
-				    "into a region of size %E overflows "
-				    "the destination",
-				    exp, range[0], range[1],
-				    dstsize));
+	    warned = warn_for_access (loc, func, exp, opt, range, dstsize,
+				      write, read && !builtin);
+
 	  if (warned)
 	    {
 	      TREE_NO_WARNING (exp) = true;
@@ -3872,7 +3923,8 @@  check_access (tree exp, tree, tree, tree dstwrite,
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
-      if (warn_for_access (loc, func, exp, opt, range, slen, access))
+      if (warn_for_access (loc, func, exp, opt, range, slen,
+			   write && !builtin, read))
 	{
 	  TREE_NO_WARNING (exp) = true;
 	  if (pad)
@@ -3885,8 +3937,11 @@  check_access (tree exp, tree, tree, tree dstwrite,
 }
 
 /* If STMT is a call to an allocation function, returns the constant
-   size of the object allocated by the call represented as sizetype.
-   If nonnull, sets RNG1[] to the range of the size.  */
+   maximum size of the object allocated by the call represented as
+   sizetype.  If nonnull, sets RNG1[] to the range of the size.
+   When nonnull, uses RVALS for range information, otherwise calls
+   get_range_info to get it.
+   Returns null when STMT is not a call to a valid allocation function.  */
 
 tree
 gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
@@ -3942,8 +3997,14 @@  gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   if (!rng1)
     rng1 = rng1_buf;
 
+  const int prec = ADDR_MAX_PRECISION;
+  const tree size_max = TYPE_MAX_VALUE (sizetype);
   if (!get_range (size, rng1, rvals))
-    return NULL_TREE;
+    {
+      /* Use the full non-negative range on failure.  */
+      rng1[0] = wi::zero (prec);
+      rng1[1] = wi::to_wide (size_max, prec);
+    }
 
   if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST)
     return fold_convert (sizetype, size);
@@ -3953,10 +4014,13 @@  gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   tree n = argidx2 < nargs ? gimple_call_arg (stmt, argidx2) : integer_one_node;
   wide_int rng2[2];
   if (!get_range (n, rng2, rvals))
-    return NULL_TREE;
+    {
+      /* As above, use the full non-negative range on failure.  */
+      rng2[0] = wi::zero (prec);
+      rng2[1] = wi::to_wide (size_max, prec);
+    }
 
   /* Extend to the maximum precision to avoid overflow.  */
-  const int prec = ADDR_MAX_PRECISION;
   rng1[0] = wide_int::from (rng1[0], prec, UNSIGNED);
   rng1[1] = wide_int::from (rng1[1], prec, UNSIGNED);
   rng2[0] = wide_int::from (rng2[0], prec, UNSIGNED);
@@ -3966,7 +4030,6 @@  gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
      of SIZE_MAX and the product of the upper bounds as a constant.  */
   rng1[0] = rng1[0] * rng2[0];
   rng1[1] = rng1[1] * rng2[1];
-  tree size_max = TYPE_MAX_VALUE (sizetype);
   if (wi::gtu_p (rng1[1], wi::to_wide (size_max, prec)))
     {
       rng1[1] = wi::to_wide (size_max);
@@ -3976,6 +4039,61 @@  gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   return wide_int_to_tree (sizetype, rng1[1]);
 }
 
+/* For an access to an object referenced to by the function parameter PTR
+   of pointer type, and set RNG[] to the range of sizes of the object
+   obtainedfrom the attribute access specification for the current function.
+   Return the function parameter on success and null otherwise.  */
+
+tree
+gimple_parm_array_size (tree ptr, wide_int rng[2],
+			const vr_values * /* = NULL */)
+{
+  /* For a function argument try to determine the byte size of the array
+     from the current function declaratation (e.g., attribute access or
+     related).  */
+  tree var = SSA_NAME_VAR (ptr);
+  if (TREE_CODE (var) != PARM_DECL)
+    return NULL_TREE;
+
+  const unsigned prec = TYPE_PRECISION (sizetype);
+
+  rdwr_map rdwr_idx;
+  attr_access *access = get_parm_access (rdwr_idx, var);
+  if (!access)
+    return NULL_TREE;
+
+  if (access->sizarg != UINT_MAX)
+    {
+      /* TODO: Try to extract the range from the argument based on
+	 those of subsequent assertions or based on known calls to
+	 the current function.  */
+      return NULL_TREE;
+    }
+
+  if (!access->minsize)
+    return NULL_TREE;
+
+  /* Only consider ordinary array bound at level 2 (or above if it's
+     ever added).  */
+  if (warn_array_parameter < 2 && !access->static_p)
+    return NULL_TREE;
+
+  rng[0] = wi::zero (prec);
+  rng[1] = wi::uhwi (access->minsize, prec);
+  /* If the PTR argument points to an array multiply MINSIZE by the size
+     of array element type.  Otherwise, multiply it by the size of what
+     the pointer points to.  */
+  tree eltype = TREE_TYPE (TREE_TYPE (ptr));
+  if (TREE_CODE (eltype) == ARRAY_TYPE)
+    eltype = TREE_TYPE (eltype);
+  tree size = TYPE_SIZE_UNIT (eltype);
+  if (!size || TREE_CODE (size) != INTEGER_CST)
+    return NULL_TREE;
+
+  rng[1] *= wi::to_wide (size, prec);
+  return var;
+}
+
 /* Wrapper around the wide_int overload of get_range.  Returns the same
    result but accepts offset_int instead.  */
 
@@ -4168,6 +4286,21 @@  compute_objsize (tree ptr, int ostype, access_ref *pref,
 	  return false;
 	}
 
+      if (gimple_nop_p (stmt))
+	{
+	  /* For a function argument try to determine the byte size
+	     of the array from the current function declaratation
+	     (e.g., attribute access or related).  */
+	  wide_int wr[2];
+	  tree ref = gimple_parm_array_size (ptr, wr, rvals);
+	  if (!ref)
+	    return NULL_TREE;
+	  pref->ref = ref;
+	  pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
+	  pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+	  return true;
+	}
+
       /* TODO: Handle PHI.  */
 
       if (!is_gimple_assign (stmt))
@@ -4220,9 +4353,9 @@  compute_objsize (tree ptr, int ostype, access_ref *pref,
 
 /* Convenience wrapper around the above.  */
 
-static tree
+tree
 compute_objsize (tree ptr, int ostype, access_ref *pref,
-		 const vr_values *rvals = NULL)
+		 const vr_values *rvals /* = NULL */)
 {
   bitmap visited = NULL;
 
@@ -4291,7 +4424,7 @@  check_memop_access (tree exp, tree dest, tree src, tree size)
   tree dstsize = compute_objsize (dest, 0, &data.dst);
 
   return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE,
-		       srcsize, dstsize, true, &data);
+		       srcsize, dstsize, true, true, &data);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -4315,7 +4448,7 @@  expand_builtin_memchr (tree exp, rtx)
       tree size = compute_objsize (arg1, 0, &data.src);
       check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
 		    /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE,
-		    true, &data);
+		    false, true, &data);
     }
 
   return NULL_RTX;
@@ -4599,7 +4732,7 @@  expand_builtin_strcat (tree exp)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
 
   check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
-		destsize, true, &data);
+		destsize, true, true, &data);
 
   return NULL_RTX;
 }
@@ -4624,7 +4757,7 @@  expand_builtin_strcpy (tree exp, rtx target)
       tree destsize = compute_objsize (dest, warn_stringop_overflow - 1,
 				       &data.dst);
       check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
-		    src, destsize, true, &data);
+		    src, destsize, true, true, &data);
     }
 
   if (rtx ret = expand_builtin_strcpy_args (exp, dest, src, target))
@@ -4684,7 +4817,7 @@  expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1,
 				       &data.dst);
       check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
-		    src, destsize, true, &data);
+		    src, destsize, true, true, &data);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -4798,7 +4931,7 @@  expand_builtin_stpncpy (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
 
   check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize,
-		true, &data);
+		true, true, &data);
 
   return NULL_RTX;
 }
@@ -4878,7 +5011,7 @@  check_strncat_sizes (tree exp, tree objsize)
   /* The number of bytes to write is LEN but check_access will alsoa
      check SRCLEN if LEN's value isn't known.  */
   return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
-		       objsize, true, &data);
+		       objsize, true, true, &data);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -4951,7 +5084,7 @@  expand_builtin_strncat (tree exp, rtx)
     srclen = maxread;
 
   check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize,
-		true, &data);
+		true, true, &data);
 
   return NULL_RTX;
 }
@@ -4987,7 +5120,7 @@  expand_builtin_strncpy (tree exp, rtx target)
       /* The number of bytes to write is LEN but check_access will also
 	 check SLEN if LEN's value isn't known.  */
       check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
-		    destsize, true, &data);
+		    destsize, true, true, &data);
     }
 
   /* We must be passed a constant len and src parameter.  */
@@ -5304,14 +5437,14 @@  expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
   tree size = compute_objsize (arg1, 0, &data.src);
   no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
 			      len, /*maxread=*/NULL_TREE, size,
-			      /*objsize=*/NULL_TREE, true, &data);
+			      /*objsize=*/NULL_TREE, false, true, &data);
   if (no_overflow)
     {
       access_data data;
       size = compute_objsize (arg2, 0, &data.src);
       no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
 				  len,  /*maxread=*/NULL_TREE, size,
-				  /*objsize=*/NULL_TREE, true, &data);
+				  /*objsize=*/NULL_TREE, false, true, &data);
     }
 
   /* If the specified length exceeds the size of either object, 
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 8b812ceb2c4..c909ae11845 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -133,13 +133,6 @@  extern tree fold_call_stmt (gcall *, bool);
 extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
 extern bool is_simple_builtin (tree);
 extern bool is_inexpensive_builtin (tree);
-
-class vr_values;
-tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
-			     const vr_values * = NULL);
-extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
-			     const vr_values * = NULL);
-
 extern bool readonly_data_expr (tree exp);
 extern bool init_target_chars (void);
 extern unsigned HOST_WIDE_INT target_newline;
@@ -184,7 +177,15 @@  struct access_data
   access_ref dst, src;
 };
 
+class vr_values;
+extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
+				    const vr_values * = NULL);
+extern tree gimple_parm_array_size (tree, wide_int[2], const vr_values * = NULL);
+extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
+			     const vr_values * = NULL);
+extern tree compute_objsize (tree, int, access_ref *, const vr_values * = NULL);
+
 extern bool check_access (tree, tree, tree, tree, tree, tree, tree,
-			  bool = true, const access_data * = NULL);
+			  bool = true, bool = true, const access_data * = NULL);
 
 #endif /* GCC_BUILTINS_H */
diff --git a/gcc/calls.c b/gcc/calls.c
index 622417ff74b..ecb966d6cb6 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -58,6 +58,8 @@  along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "gimple-fold.h"
 
+#include "tree-pretty-print.h"
+
 /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
 #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
 
@@ -1883,36 +1885,20 @@  fntype_argno_type (tree fntype, unsigned argno)
   return NULL_TREE;
 }
 
-/* Helper to append the "rdwr" attribute specification described
-   by ACCESS to the array ATTRSTR with size STRSIZE.  Used in
+/* Helper to append the "human readable" attribute access specification
+   described by ACCESS to the array ATTRSTR with size STRSIZE.  Used in
    diagnostics.  */
 
 static inline void
 append_attrname (const std::pair<int, attr_access> &access,
 		 char *attrstr, size_t strsize)
 {
-  /* Append the relevant attribute to the string.  This (deliberately)
-     appends the attribute pointer operand even when none was specified.  */
-  size_t len = strlen (attrstr);
-
-  const char* const atname
-    = (access.second.mode == attr_access::read_only
-       ? "read_only"
-       : (access.second.mode == attr_access::write_only
-	  ? "write_only"
-	  : (access.second.mode == attr_access::read_write
-	     ? "read_write" : "none")));
-
-  const char *sep = len ? ", " : "";
-
-  if (access.second.sizarg == UINT_MAX)
-    snprintf (attrstr + len, strsize - len,
-	      "%s%s (%i)", sep, atname,
-	      access.second.ptrarg + 1);
-  else
-    snprintf (attrstr + len, strsize - len,
-	      "%s%s (%i, %i)", sep, atname,
-	      access.second.ptrarg + 1, access.second.sizarg + 1);
+  if (access.second.internal_p)
+    return;
+
+  tree str = access.second.to_external_string ();
+  gcc_assert (strsize >= (size_t) TREE_STRING_LENGTH (str));
+  strcpy (attrstr, TREE_STRING_POINTER (str));
 }
 
 /* Iterate over attribute access read-only, read-write, and write-only
@@ -1939,6 +1925,7 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
     return;
 
   auto_diagnostic_group adg;
+  bool warned = false;
 
   /* A string describing the attributes that the warnings issued by this
      function apply to.  Used to print one informational note per function
@@ -1967,35 +1954,40 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
       if (!access.second.ptr)
 	continue;
 
-      tree argtype = fntype_argno_type (fntype, ptridx);
-      argtype = TREE_TYPE (argtype);
+      tree ptrtype = fntype_argno_type (fntype, ptridx);
+      tree argtype = TREE_TYPE (ptrtype);
 
-      tree size;
+      /* The size of the access by the call.  */
+      tree access_size;
       if (sizidx == -1)
 	{
-	  /* If only the pointer attribute operand was specified
-	     and not size, set SIZE to the size of one element of
-	     the pointed to type to detect smaller objects (null
-	     pointers are diagnosed in this case only if
-	     the pointer is also declared with attribute nonnull.  */
-	  size = size_one_node;
+	  /* If only the pointer attribute operand was specified and
+	     not size, set SIZE to the greater of MINSIZE or size of
+	     one element of the pointed to type to detect smaller
+	     objects (null pointers are diagnosed in this case only
+	     if the pointer is also declared with attribute nonnull.  */
+	  if (access.second.minsize
+	      && access.second.minsize != HOST_WIDE_INT_M1U)
+	    access_size = build_int_cstu (sizetype, access.second.minsize);
+	  else
+	    access_size = size_one_node;
 	}
       else
-	size = rwm->get (sizidx)->size;
+	access_size = rwm->get (sizidx)->size;
 
+      bool warned = false;
+      location_t loc = EXPR_LOCATION (exp);
       tree ptr = access.second.ptr;
       tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
-      if (get_size_range (size, sizrng, true)
+      if (get_size_range (access_size, sizrng, true)
 	  && tree_int_cst_sgn (sizrng[0]) < 0
 	  && tree_int_cst_sgn (sizrng[1]) < 0)
 	{
 	  /* Warn about negative sizes.  */
-	  bool warned = false;
-	  location_t loc = EXPR_LOCATION (exp);
 	  if (tree_int_cst_equal (sizrng[0], sizrng[1]))
 	    warned = warning_at (loc, OPT_Wstringop_overflow_,
 				 "%Kargument %i value %E is negative",
-				 exp, sizidx + 1, size);
+				 exp, sizidx + 1, access_size);
 	  else
 	    warned = warning_at (loc, OPT_Wstringop_overflow_,
 				 "%Kargument %i range [%E, %E] is negative",
@@ -2012,44 +2004,56 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
 	{
 	  if (COMPLETE_TYPE_P (argtype))
 	    {
-	      /* Multiple SIZE by the size of the type the pointer
-		 argument points to.  If it's incomplete the size
-		 is used as is.  */
-	      size = NULL_TREE;
+	      /* Multiply ACCESS_SIZE by the size of the type the pointer
+		 argument points to.  If it's incomplete the size is used
+		 as is.  */
+	      access_size = NULL_TREE;
 	      if (tree argsize = TYPE_SIZE_UNIT (argtype))
 		if (TREE_CODE (argsize) == INTEGER_CST)
 		  {
 		    const int prec = TYPE_PRECISION (sizetype);
 		    wide_int minsize = wi::to_wide (sizrng[0], prec);
 		    minsize *= wi::to_wide (argsize, prec);
-		    size = wide_int_to_tree (sizetype, minsize);
+		    access_size = wide_int_to_tree (sizetype, minsize);
 		  }
 	    }
 	}
       else
-	size = NULL_TREE;
+	access_size = NULL_TREE;
 
-      if (sizidx >= 0
-	  && integer_zerop (ptr)
-	  && tree_int_cst_sgn (sizrng[0]) > 0)
+      if (integer_zerop (ptr))
 	{
-	  /* Warn about null pointers with positive sizes.  This is
-	     different from also declaring the pointer argument with
-	     attribute nonnull when the function accepts null pointers
-	     only when the corresponding size is zero.  */
-	  bool warned = false;
-	  const location_t loc = EXPR_LOC_OR_LOC (ptr, EXPR_LOCATION (exp));
-	  if (tree_int_cst_equal (sizrng[0], sizrng[1]))
-	    warned = warning_at (loc, OPT_Wnonnull,
-				 "%Kargument %i is null but the corresponding "
-				 "size argument %i value is %E",
-				 exp, ptridx + 1, sizidx + 1, size);
-	  else
-	    warned = warning_at (loc, OPT_Wnonnull,
-				 "%Kargument %i is null but the corresponding "
-				 "size argument %i range is [%E, %E]",
-				 exp, ptridx + 1, sizidx + 1,
-				 sizrng[0], sizrng[1]);
+	  if (sizidx >= 0 && tree_int_cst_sgn (sizrng[0]) > 0)
+	    {
+	      /* Warn about null pointers with positive sizes.  This is
+		 different from also declaring the pointer argument with
+		 attribute nonnull when the function accepts null pointers
+		 only when the corresponding size is zero.  */
+	      if (tree_int_cst_equal (sizrng[0], sizrng[1]))
+		warned = warning_at (loc, OPT_Wnonnull,
+				     "%Kargument %i is null but "
+				     "the corresponding size argument %i "
+				     "value is %E",
+				     exp, ptridx + 1, sizidx + 1, access_size);
+	      else
+		warned = warning_at (loc, OPT_Wnonnull,
+				     "%Kargument %i is null but "
+				     "the corresponding size argument %i "
+				     "range is [%E, %E]",
+				     exp, ptridx + 1, sizidx + 1,
+				     sizrng[0], sizrng[1]);
+	    }
+	  else if (access_size && access.second.static_p)
+	    {
+	      /* Warn about null pointers for [static N] array arguments
+		 but do not warn for ordinary (i.e., nonstatic) arrays.  */
+	      warned = warning_at (loc, OPT_Wnonnull,
+				   "%Kargument %i to %<%T[static %E]%> null "
+				   "where non-null expected",
+				   exp, ptridx + 1, argtype,
+				   sizrng[0]);
+	    }
+
 	  if (warned)
 	    {
 	      append_attrname (access, attrstr, sizeof attrstr);
@@ -2058,54 +2062,76 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
 	    }
 	}
 
-      tree objsize = compute_objsize (ptr, 0);
+      access_data data;
+      access_ref* const pobj = (access.second.mode == attr_access::write_only
+				? &data.dst : &data.src);
+      tree objsize = compute_objsize (ptr, 1, pobj);
 
-      tree srcsize;
-      if (access.second.mode == attr_access::write_only)
-	{
-	  /* For a write-only argument there is no source.  */
-	  srcsize = NULL_TREE;
-	}
-      else
+      /* The size of the destination or source object.  */
+      tree dstsize = NULL_TREE, srcsize = NULL_TREE;
+      if (access.second.mode == attr_access::read_only
+	  || access.second.mode == attr_access::none)
 	{
-	  /* For read-only and read-write attributes also set the source
-	     size.  */
+	  /* For a read-only argument there is no destination.  For
+	     no access, set the source as well and differentiate via
+	     the access flag below.  */
 	  srcsize = objsize;
-	  if (access.second.mode == attr_access::read_only
-	      || access.second.mode == attr_access::none)
-	    {
-	      /* For a read-only attribute there is no destination so
-		 clear OBJSIZE.  This emits "reading N bytes" kind of
-		 diagnostics instead of the "writing N bytes" kind,
-		 unless MODE is none.  */
-	      objsize = NULL_TREE;
-	    }
 	}
+      else
+	dstsize = objsize;
 
-      /* Clear the no-warning bit in case it was set in a prior
-	 iteration so that accesses via different arguments are
-	 diagnosed.  */
+      /* Clear the no-warning bit in case it was set by check_access
+	 in a prior iteration so that accesses via different arguments
+	 are diagnosed.  */
       TREE_NO_WARNING (exp) = false;
-      check_access (exp, NULL_TREE, NULL_TREE, size, /*maxread=*/ NULL_TREE,
-		    srcsize, objsize, access.second.mode != attr_access::none);
+      /* Set for accesses by functions explicitly declared with attribute
+	 access other than none, and clear for functions internally decorated
+	 with the attribute for user-defined functions with mode of none.  */
+      const bool read_access
+	= (access.second.mode != attr_access::none
+	   && access.second.mode != attr_access::write_only);
+      const bool write_access
+	= (access.second.mode != attr_access::none
+	   && access.second.mode != attr_access::read_only
+	   && !TYPE_READONLY (argtype));
+      check_access (exp, /*dst=*/ NULL_TREE, /*src=*/ NULL_TREE, access_size,
+		    /*maxread=*/ NULL_TREE, srcsize, dstsize, write_access,
+		    read_access, &data);
 
       if (TREE_NO_WARNING (exp))
-	/* If check_access issued a warning above, append the relevant
-	   attribute to the string.  */
-	append_attrname (access, attrstr, sizeof attrstr);
-    }
+	{
+	  warned = true;
 
-  if (!*attrstr)
-    return;
+	  if (access.second.internal_p)
+	    inform (loc, "referencing argument %u of type %qT",
+		    ptridx + 1, ptrtype);
+	  else
+	    /* If check_access issued a warning above, append the relevant
+	       attribute to the string.  */
+	    append_attrname (access, attrstr, sizeof attrstr);
+	}
+    }
 
-  if (fndecl)
-    inform (DECL_SOURCE_LOCATION (fndecl),
-	    "in a call to function %qD declared with attribute %qs",
-	    fndecl, attrstr);
-  else
-    inform (EXPR_LOCATION (fndecl),
-	    "in a call with type %qT and attribute %qs",
-	    fntype, attrstr);
+  if (*attrstr)
+    {
+      if (fndecl)
+	inform (DECL_SOURCE_LOCATION (fndecl),
+		"in a call to function %qD declared with attribute %qs",
+		fndecl, attrstr);
+      else
+	inform (EXPR_LOCATION (fndecl),
+		"in a call with type %qT and attribute %qs",
+		fntype, attrstr);
+    }
+  else if (warned)
+    {
+      if (fndecl)
+	inform (DECL_SOURCE_LOCATION (fndecl),
+		"in a call to function %qD", fndecl);
+      else
+	inform (EXPR_LOCATION (fndecl),
+		"in a call with type %qT", fntype);
+    }
 
   /* Set the bit in case if was cleared and not set above.  */
   TREE_NO_WARNING (exp) = true;
diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
index c4127b805ab..38e06ba5e62 100644
--- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
@@ -1,7 +1,7 @@ 
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument" } */
-/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat" { target c } } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat -Wno-stringop-overflow" { target c } } */
 /* { dg-require-effective-target alloca } */
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/g++.dg/ext/attr-access.C b/gcc/testsuite/g++.dg/ext/attr-access.C
index 3b9c1a36e30..b7b2a5fc4bf 100644
--- a/gcc/testsuite/g++.dg/ext/attr-access.C
+++ b/gcc/testsuite/g++.dg/ext/attr-access.C
@@ -42,8 +42,8 @@  void call_rdwrp1_rdwrr2_O0 (void)
   int32_t x[1] = { };
 
   rdwrp1_rdwrr2 (x, x[0]);
-  rdwrp1_rdwrr2 (x, x[1]);        // { dg-warning "writing 4 bytes into a region of size 0" }
-  rdwrp1_rdwrr2 (x + 1, x[0]);    // { dg-warning "writing 4 bytes into a region of size 0" }
+  rdwrp1_rdwrr2 (x, x[1]);        // { dg-warning "accessing 4 bytes in a region of size 0" }
+  rdwrp1_rdwrr2 (x + 1, x[0]);    // { dg-warning "accessing 4 bytes in a region of size 0" }
 }
 
 void call_wop1_wor2_O0 (void)
@@ -84,12 +84,12 @@  void call_rdwrp1_rdwrr2_O1 (void)
   int32_t &r2 = *(int32_t*)((char*)p1 + 1);
 
   rdwrp1_rdwrr2 (x, x[0]);
-  rdwrp1_rdwrr2 (x, x[1]);        // { dg-warning "writing 4 bytes into a region of size 0" }
-  rdwrp1_rdwrr2 (x + 1, x[0]);    // { dg-warning "writing 4 bytes into a region of size 0" }
+  rdwrp1_rdwrr2 (x, x[1]);        // { dg-warning "accessing 4 bytes in a region of size 0" }
+  rdwrp1_rdwrr2 (x + 1, x[0]);    // { dg-warning "accessing 4 bytes in a region of size 0" }
 
   rdwrp1_rdwrr2 (p0, r0);
-  rdwrp1_rdwrr2 (p0, r2);         // { dg-warning "writing 4 bytes into a region of size 2" }
-  rdwrp1_rdwrr2 (p1, r0);         // { dg-warning "writing 4 bytes into a region of size 3" }
+  rdwrp1_rdwrr2 (p0, r2);         // { dg-warning "accessing 4 bytes in a region of size 2" }
+  rdwrp1_rdwrr2 (p1, r0);         // { dg-warning "accessing 4 bytes in a region of size 3" }
 }
 
 void call_wop1_wor2_O1 (void)
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-30.c b/gcc/testsuite/gcc.dg/Warray-bounds-30.c
index b9965682101..048a95d6dcf 100644
--- a/gcc/testsuite/gcc.dg/Warray-bounds-30.c
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-30.c
@@ -73,8 +73,7 @@  void test_global_int_array (void)
 
   T (&p[min]);      /* { dg-warning "subscript -\[0-9\]+ is \(below|outside\) array bounds of .int\\\[1]." } */
   T (&p[-1]);       /* { dg-warning "subscript -1 is \(below|outside\) array bounds of .int\\\[1]." } */
-  T (&p[0]);
-  T (&p[1]);
+  T (&p[0], &p[1]);
   T (&p[2]);        /* { dg-warning "subscript 2 is \(above|outside\) array bounds of .int\\\[1]." } */
   T (&p[max]);      /* { dg-warning "subscript \[0-9\]+ is \(above|outside\) array bounds of .int\\\[1]." } */
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c
index 6d9fcb9d52e..755ebea5ff4 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c
@@ -20,7 +20,7 @@  typedef __INT32_TYPE__ int32_t;
 /* Exercise null pointer detection.  */
 
 RDONLY (2, 1) void
-rd2_1 (int, const void*);       // { dg-message "in a call to function 'rd2_1' declared with attribute 'read_only \\\(2, 1\\\)" }
+rd2_1 (int, const void*);       // { dg-message "in a call to function 'rd2_1' declared with attribute 'access \\\(read_only, 2, 1\\\)" "note" }
 
 void test_rd2_1 (void)
 {
@@ -44,7 +44,7 @@  void test_rd2_1 (void)
 }
 
 WRONLY (3, 1) void
-wr3_1 (int, int, void*);        // { dg-message "in a call to function 'wr3_1' declared with attribute 'write_only \\\(3, 1\\\)" }
+wr3_1 (int, int, void*);        // { dg-message "in a call to function 'wr3_1' declared with attribute 'access \\\(write_only, 3, 1\\\)" }
 
 void test_wr3_1 (void)
 {
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c
index a21a1f17a2b..049d1c6981c 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c
@@ -23,7 +23,7 @@  extern char d1[1], d2[2], d3[3];
    the attribute without a size operand.  */
 
 RDONLY (1) void
-rd1_int (const int32_t*);   // { dg-message "in a call to function 'rd1_int' declared with attribute 'read_only \\\(1\\\)'" }
+rd1_int (const int32_t*);   // { dg-message "in a call to function 'rd1_int' declared with attribute 'access \\\(read_only, 1\\\)'" "note" }
 
 void test_rd1_int (void)
 {
@@ -39,7 +39,7 @@  void test_rd1_int (void)
    the attribute and with non-zero size.  */
 
 RDONLY (2, 1) void
-rd2_1 (int, const void*);   // { dg-message "in a call to function 'rd2_1' declared with attribute 'read_only \\\(2, 1\\\)" }
+rd2_1 (int, const void*);   // { dg-message "in a call to function 'rd2_1' declared with attribute 'access \\\(read_only, 2, 1\\\)" "note" }
 
 void test_rd2_1 (void)
 {
@@ -49,7 +49,7 @@  void test_rd2_1 (void)
 }
 
 WRONLY (3, 1) void
-wr3_1 (int, int, void*);    // { dg-message "in a call to function 'wr3_1' declared with attribute 'write_only \\\(3, 1\\\)" }
+wr3_1 (int, int, void*);    // { dg-message "in a call to function 'wr3_1' declared with attribute 'access \\\(write_only, 3, 1\\\)" "note" }
 
 void test_wr3_1 (void)
 {
@@ -157,7 +157,7 @@  void test_rd6_1_wr5_2_rd4_3 (void)
 {
   rd6_1_wr5_2_rd4_3 (7, 2, 1, d1, d2, s3);   // { dg-warning "reading 7 bytes from a region of size 3" }
   rd6_1_wr5_2_rd4_3 (3, 8, 1, d1, d2, s3);   // { dg-warning "writing 8 bytes into a region of size 2" }
-  rd6_1_wr5_2_rd4_3 (3, 2, 9, d1, d2, s3);   // { dg-warning "writing 9 bytes into a region of size 1" }
+  rd6_1_wr5_2_rd4_3 (3, 2, 9, d1, d2, s3);   // { dg-warning "accessing 9 bytes in a region of size 1" }
 }
 
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-40.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-40.c
new file mode 100644
index 00000000000..386c92dc7a8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-40.c
@@ -0,0 +1,120 @@ 
+/* PR c/50584 - No warning for passing small array to C99 static array
+   declarator
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+typedef __INT16_TYPE__ int16_t;
+
+void fa2 (int16_t[2]);
+void fxa2 (int16_t[2]) __attribute__ ((nonnull));
+
+void fas2 (int16_t[static 2]);
+
+void fvla (unsigned n, int16_t[n]);
+
+void test_array_1_dim (void)
+{
+  int16_t a1[1];
+  int16_t a2[2];
+  int16_t i;
+
+  fa2 (0);
+  fa2 (a2);
+  fa2 (a1);                   // { dg-warning "'fa2' accessing 4 bytes in a region of size 2 " }
+  fa2 (&i);                   // { dg-warning "'fa2' accessing 4 bytes in a region of size 2 " }
+
+  fxa2 (0);                   // { dg-warning "\\\[-Wnonnull" }
+  fxa2 (a2);
+  fxa2 (a1);                  // { dg-warning "'fxa2' accessing 4 bytes in a region of size 2 " }
+  fxa2 (&i);                  // { dg-warning "'fxa2' accessing 4 bytes in a region of size 2 " }
+
+  fas2 (0);                   // { dg-warning "\\\[-Wnonnull" }
+  fas2 (a2);
+  fas2 (a1);                  // { dg-warning "'fas2' accessing 4 bytes in a region of size 2 " }
+  fas2 (&i);                  // { dg-warning "'fas2' accessing 4 bytes in a region of size 2 " }
+
+  fvla (1, 0);                // { dg-warning "\\\[-Wnonnull" }
+  fvla (1, &i);
+  fvla (2, a2);
+  fvla (2, a1);               // { dg-warning "'fvla' accessing 4 bytes in a region of size 2 " }
+  fvla (2, &i);               // { dg-warning "'fvla' accessing 4 bytes in a region of size 2 " }
+}
+
+
+void fac2 (const int16_t[2]);
+void fxac2 (const int16_t[2]) __attribute__ ((nonnull));
+
+void facs2 (const int16_t[static 2]);
+
+void fvlac (unsigned n, const int16_t[n]);
+
+void test_const_array_1_dim (void)
+{
+  int16_t a1[1];
+  int16_t a2[2];
+  int16_t i;
+
+  fac2 (0);
+  fac2 (a2);
+  fac2 (a1);                  // { dg-warning "'fac2' reading 4 bytes from a region of size 2 " }
+  fac2 (&i);                  // { dg-warning "'fac2' reading 4 bytes from a region of size 2 " }
+
+  fxac2 (0);                  // { dg-warning "\\\[-Wnonnull" }
+  fxac2 (a2);
+  fxac2 (a1);                 // { dg-warning "'fxac2' reading 4 bytes from a region of size 2 " }
+  fxac2 (&i);                 // { dg-warning "'fxac2' reading 4 bytes from a region of size 2 " }
+
+  facs2 (0);                  // { dg-warning "\\\[-Wnonnull" }
+  facs2 (a2);
+  facs2 (a1);                 // { dg-warning "'facs2' reading 4 bytes from a region of size 2 " }
+  facs2 (&i);                 // { dg-warning "'facs2' reading 4 bytes from a region of size 2 " }
+
+  fvlac (1, 0);               // { dg-warning "\\\[-Wnonnull" }
+  fvlac (1, &i);
+  fvlac (2, a2);
+  fvlac (2, a1);              // { dg-warning "'fvlac' reading 4 bytes from a region of size 2 " }
+  fvlac (2, &i);              // { dg-warning "'fvlac' reading 4 bytes from a region of size 2 " }
+}
+
+
+void fca3x5 (int16_t[3][5]);
+void fcas5x7 (int16_t[static 5][7]);
+
+struct Snx5 { int16_t a3x5[3][5], a2x5[2][5], a1x5[1][5]; };
+struct Snx7 { int16_t a5x7[5][7], a4x7[4][7], a1x7[1][7]; };
+struct S0x7 { int x; int16_t a0x7[0][7]; };
+
+void test_array_2_dim (struct Snx5 *px5, struct Snx7 *px7, struct S0x7 *p0x7)
+{
+  int16_t a0x5[0][5], a1x5[1][5], a2x5[2][5], a3x5[3][5], a4x5[4][5];
+
+  fca3x5 (a3x5);
+  fca3x5 (a4x5);
+  fca3x5 (a2x5);              // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 20" }
+  fca3x5 (a1x5);              // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 10" }
+  fca3x5 (a0x5);              // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 0" }
+
+  fca3x5 (px5->a3x5);
+  fca3x5 (px5->a2x5);         // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 20" }
+  fca3x5 (px5->a1x5);         // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 10" "pr96346" { xfail *-*-* } }
+
+  {
+    int16_t (*pa2x5)[5] = &a2x5[0];
+    fca3x5 (pa2x5);           // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 10" }
+    ++pa2x5;
+    fca3x5 (pa2x5);           // { dg-warning "'fca3x5' accessing 30 bytes " }
+  }
+
+  int16_t a0x7[0][7], a1x7[1][7], a4x7[4][7], a5x7[5][7], a99x7[99][7];
+  fcas5x7 (a99x7);
+  fcas5x7 (a5x7);
+  fcas5x7 (a4x7);             // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 56" }
+  fcas5x7 (a1x7);             // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 14" }
+  fcas5x7 (a0x7);             // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 0" }
+
+  fcas5x7 (px7->a5x7);
+  fcas5x7 (px7->a4x7);        // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 56" }
+  fcas5x7 (px7->a1x7);        // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 14" "pr96346" { xfail *-*-* } }
+
+  fcas5x7 (p0x7->a0x7);       // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 0" "pr96346" { xfail *-*-* } }
+}
diff --git a/gcc/testsuite/gcc.dg/attr-access-2.c b/gcc/testsuite/gcc.dg/attr-access-2.c
new file mode 100644
index 00000000000..7167c756320
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-access-2.c
@@ -0,0 +1,116 @@ 
+/* PR 50584 - No warning for passing small array to C99 static array declarator
+   Exercise interaction between explicit attribute access and VLA parameters.
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define RW(...) __attribute__ ((access (read_write, __VA_ARGS__)))
+
+
+void f1 (int n, int[n], int);               // { dg-message "designating the bound of variable length array argument 2" "note" }
+
+// Verify that a redundant attribute access doesn't trigger a warning.
+RW (2, 1) void f1 (int n, int[n], int);
+
+RW (2, 3) void f1 (int n, int[n], int);     // { dg-warning "attribute 'access\\\(read_write, 2, 3\\\)' positional argument 2 conflicts with previous designation by argument 1" }
+
+
+/* Verify that applying the attribute to a VLA with an unspecified bound
+   doesn't trigger any warnings, both with and without a size operand.  */
+          void f2 (int, int[*], int);
+RW (2)    void f2 (int, int[*], int);
+RW (2, 3) void f2 (int, int[*], int);
+
+/* Designating a parameter that comes before the VLA is the same as
+   using the standard VLA int[n] syntax.  It might be worth issuing
+   a portability warning suggesting to prefer the standard syntax.  */
+          void f3 (int, int[*], int);
+RW (2, 1) void f3 (int, int[*], int);
+
+/* Designating a parameter that comes after the VLA cannot be expressed
+   using the standard VLA int[n] syntax.  Verify it doesn't trigger
+   a warning.  */
+          void f4 (int, int[*], int);
+RW (2, 3) void f4 (int, int[*], int);
+
+/* Also verify the same on the same declaration.  */
+          void f5 (int[*], int) RW (1, 2);
+RW (1, 2) void f5 (int[*], int);
+RW (1, 2) void f5 (int[*], int) RW (1, 2);
+
+
+/* Verify that designating a VLA parameter with an explicit bound without
+   also designating the same bound parameter triggers a warning (it has
+   a different meaning).  */
+       void f7 (int n, int[n]);         // { dg-message "21:note: designating the bound of variable length array argument 2" "note" }
+RW (2) void f7 (int n, int[n]);         // { dg-warning "attribute 'access\\\(read_write, 2\\\)' missing positional argument 2 provided in previous designation by argument 1" }
+
+          void f8 (int n, int[n]);
+RW (2, 1) void f8 (int n, int[n]);
+
+
+          void f9 (int, char[]);        // { dg-message "25:note: previously declared as an ordinary array 'char\\\[]'" note" }
+RW (2)    void f9 (int n, char a[n])    // { dg-warning "argument 2 of type 'char\\\[n]' declared as a variable length array" }
+                                        // { dg-warning "attribute 'access *\\\(read_write, 2\\\)' positional argument 2 missing in previous designation" "" { target *-*-* } .-1 }
+                                        // { dg-message "24:note: designating the bound of variable length array argument 2" "note" { target *-*-* } .-2 }
+{ (void)&n; (void)&a; }
+
+
+          void f10 (int, char[]);       // { dg-message "26:note: previously declared as an ordinary array 'char\\\[]'" "note" }
+RW (2, 1) void f10 (int n, char a[n])   // { dg-warning "attribute 'access *\\\(read_write, 2, 1\\\)' positional argument 2 missing in previous designation" "pr????" { xfail *-*-* } }
+                                        // { dg-warning "argument 2 of type 'char\\\[n]' declared as a variable length array"  "" { target *-*-* } .-1 }
+{ (void)&n; (void)&a; }
+
+
+/* The following is diagnosed to point out declarations with the T[*]
+   form in headers where specifying the bound is just as important as
+   in the definition (to detect bugs).  */
+          void f11 (int, char[*]);      // { dg-message "previously declared as 'char\\\[\\\*]' with 1 unspecified variable bound" "note" }
+          void f11 (int m, char a[m]);  // { dg-warning "argument 2 of type 'char\\\[m]' declared with 0 unspecified variable bounds" }
+RW (2, 1) void f11 (int n, char arr[n]) // { dg-warning "argument 2 of type 'char\\\[n]' declared with 0 unspecified variable bounds" }
+{ (void)&n; (void)&arr; }
+
+
+/* Verify that redeclaring a function with attribute access applying
+   to an array parameter of any form is not diagnosed.  */
+          void f12__ (int, int[]) RW (2, 1);
+RW (2, 1) void f12__ (int, int[]);
+
+          void f12_3 (int, int[3]) RW (2, 1);
+RW (2, 1) void f12_3 (int, int[3]);
+
+          void f12_n (int n, int[n]) RW (2, 1);
+RW (2, 1) void f12_n (int n, int[n]);
+
+          void f12_x (int, int[*]) RW (2, 1);
+RW (2, 1) void f12_x (int, int[*]);
+
+          void f13__ (int, int[]);
+RW (2, 1) void f13__ (int, int[]);
+
+          void f13_5 (int, int[5]);
+RW (2, 1) void f13_5 (int, int[5]);
+
+          void f13_n (int n, int[n]);
+RW (2, 1) void f13_n (int n, int[n]);
+
+          void f13_x (int, int[*]);
+RW (2, 1) void f13_x (int, int[*]);
+
+RW (2, 1) void f14__ (int, int[]);
+          void f14__ (int, int[]);
+
+RW (2, 1) void f14_7 (int, int[7]);
+          void f14_7 (int, int[7]);
+
+RW (2, 1) void f14_n (int n, int[n]);
+          void f14_n (int n, int[n]);
+
+RW (2, 1) void f14_x (int, int[*]);
+          void f14_x (int, int[*]);
+
+typedef void G1 (int n, int[n], int);
+
+G1 g1;
+
+RW (2, 3) void g1 (int n, int[n], int);     // { dg-warning "24: attribute 'access *\\\(read_write, 2, 3\\\)' positional argument 2 conflicts with previous designation by argument 3" }
+// { dg-message "designating the bound of variable length array argument 2" "note" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/gcc.dg/attr-access-none.c b/gcc/testsuite/gcc.dg/attr-access-none.c
index d983f2fac06..85f9e73b3f4 100644
--- a/gcc/testsuite/gcc.dg/attr-access-none.c
+++ b/gcc/testsuite/gcc.dg/attr-access-none.c
@@ -23,7 +23,7 @@  void nowarn_fnone_pcv1 (void)
 
 
 int __attribute__ ((access (none, 1, 2)))
-fnone_pcv1_2 (const void*, int);  // { dg-message "in a call to function 'fnone_pcv1_2' declared with attribute 'none \\\(1, 2\\\)'" }
+fnone_pcv1_2 (const void*, int);  // { dg-message "in a call to function 'fnone_pcv1_2' declared with attribute 'access \\\(none, 1, 2\\\)'" "note" }
 
 void nowarn_fnone_pcv1_2 (void)
 {
@@ -34,5 +34,5 @@  void nowarn_fnone_pcv1_2 (void)
 void warn_fnone_pcv1_2 (void)
 {
   char a[3];
-  fnone_pcv1_2 (a, 4);        // { dg-warning "expecting 4 bytes in a region of size 3" }
+  fnone_pcv1_2 (a, 4);        // { dg-warning "expecting 4 or more bytes in a region of size 3" }
 }