public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r11-3305] Extend -Wstringop-overflow to detect out-of-bounds accesses to array parameters.
@ 2020-09-19 23:57 Martin Sebor
  0 siblings, 0 replies; only message in thread
From: Martin Sebor @ 2020-09-19 23:57 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:baad4c48a85a354d2bf1b17e5aff71203c08adea

commit r11-3305-gbaad4c48a85a354d2bf1b17e5aff71203c08adea
Author: Martin Sebor <msebor@redhat.com>
Date:   Sat Sep 19 17:37:05 2020 -0600

    Extend -Wstringop-overflow to detect out-of-bounds accesses to array parameters.
    
    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.
            * tree-ssa-uninit.c (maybe_warn_pass_by_reference): Avoid adding
            quotes.
    
    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/dfp/composite-type.c: Prune expected warnings.
            * gcc.dg/torture/pr57147-1.c: Add a member to an otherwise empty
            struct to avoid a warning.
            * gcc.dg/torture/pr57147-3.c: Same.
            * gcc.dg/Warray-bounds-30.c: Adjust.
            * gcc.dg/attr-access-none.c: Same.
            * gcc.dg/Wstringop-overflow-40.c: New test.
            * gcc.dg/attr-access-2.c: New test.

Diff:
---
 gcc/builtins.c                                     | 314 +++++++++++++++------
 gcc/builtins.h                                     |  19 +-
 gcc/calls.c                                        | 214 ++++++++------
 .../c-c++-common/Wsizeof-pointer-memaccess1.c      |   4 +-
 gcc/testsuite/g++.dg/ext/attr-access.C             |  12 +-
 gcc/testsuite/gcc.dg/Warray-bounds-30.c            |   3 +-
 gcc/testsuite/gcc.dg/Wstringop-overflow-23.c       |   4 +-
 gcc/testsuite/gcc.dg/Wstringop-overflow-24.c       |   8 +-
 gcc/testsuite/gcc.dg/Wstringop-overflow-40.c       | 120 ++++++++
 gcc/testsuite/gcc.dg/attr-access-2.c               | 116 ++++++++
 gcc/testsuite/gcc.dg/attr-access-none.c            |   2 +-
 gcc/testsuite/gcc.dg/dfp/composite-type.c          |   3 +
 gcc/testsuite/gcc.dg/torture/pr57147-1.c           |   4 +-
 gcc/testsuite/gcc.dg/torture/pr57147-3.c           |   3 +-
 gcc/tree-ssa-uninit.c                              |   4 +-
 15 files changed, 624 insertions(+), 206 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 8b9a4a4d948..45efc1c3992 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -183,7 +183,6 @@ static void maybe_emit_chk_warning (tree, enum built_in_function);
 static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
 static void maybe_emit_free_warning (tree);
 static tree fold_builtin_object_size (tree, tree);
-static tree compute_objsize (tree, int, access_ref *, const vr_values * = NULL);
 static bool get_range (tree, signop, offset_int[2], const vr_values * = NULL);
 static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
 
@@ -3490,51 +3489,146 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
 }
 
 /* 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, tree range[2],
-		 tree slen, bool access)
+warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
+		 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_Wstringop_overread,
 			       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)
+			       "%K%qD reading %E bytes from a region of size %E",			       exp, func, range[0], size)
 		  : warning_n (loc, OPT_Wstringop_overread,
 			       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.  */
 	  warned = (func
 		    ? warning_at (loc, OPT_Wstringop_overread,
-				  "%K%qD reading %E or more bytes from a region "
-				  "of size %E",
-				  exp, func, range[0], slen)
+				  "%K%qD reading %E or more bytes from "
+				  "a region of size %E",
+				  exp, func, range[0], size)
 		    : warning_at (loc, OPT_Wstringop_overread,
 				  "%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_Wstringop_overread,
 				"%K%qD reading between %E and %E bytes from "
 				"a region of size %E",
-				exp, func, range[0], range[1], slen)
-		  : warning_at (loc, OPT_Wstringop_overread,
-				"%Kreading between %E and %E bytes from "
+				exp, func, range[0], range[1], size)
+		  : warning_at (loc, opt,
+				"%K reading between %E and %E bytes from "
 				"a region of size %E",
-				exp, range[0], range[1], slen));
+				exp, range[0], range[1], size));
 
       if (warned)
 	TREE_NO_WARNING (exp) = true;
@@ -3542,18 +3636,19 @@ warn_for_access (location_t loc, tree func, tree exp, tree range[2],
       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_Wstringop_overread,
 			   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)
+			   exp, func, range[0], size)
 	      : warning_n (loc, OPT_Wstringop_overread,
 			   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));
+			   exp, range[0], size));
   else if (tree_int_cst_sign_bit (range[1]))
     {
       /* Avoid printing the upper bound if it's invalid.  */
@@ -3561,22 +3656,22 @@ warn_for_access (location_t loc, tree func, tree exp, tree range[2],
 		? warning_at (loc, OPT_Wstringop_overread,
 			      "%K%qD expecting %E or more bytes in a region "
 			      "of size %E",
-			      exp, func, range[0], slen)
+			      exp, func, range[0], size)
 		: warning_at (loc, OPT_Wstringop_overread,
 			      "%Kexpecting %E or more bytes in a region "
 			      "of size %E",
-			      exp, range[0], slen));
+			      exp, range[0], size));
     }
   else
     warned = (func
 	      ? warning_at (loc, OPT_Wstringop_overread,
 			    "%K%qD expecting between %E and %E bytes in "
 			    "a region of size %E",
-			    exp, func, range[0], range[1], slen)
+			    exp, func, range[0], range[1], size)
 	      : warning_at (loc, OPT_Wstringop_overread,
 			    "%Kexpectting between %E and %E bytes in "
 			    "a region of size %E",
-			    exp, range[0], range[1], slen));
+			    exp, range[0], range[1], size));
 
   if (warned)
     TREE_NO_WARNING (exp) = true;
@@ -3759,8 +3854,9 @@ get_size_range (tree bound, tree range[2], const offset_int bndrng[2])
    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.
 
@@ -3857,6 +3953,11 @@ check_access (tree exp, tree dstwrite,
   get_size_range (dstwrite, range, pad ? pad->dst.bndrng : NULL);
 
   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.  */
@@ -3913,51 +4014,18 @@ check_access (tree exp, tree dstwrite,
 				      "the destination",
 				      exp, range[0], dstsize));
 	    }
-	  else if (tree_int_cst_equal (range[0], range[1]))
-	    warned = (func
-		      ? warning_n (loc, OPT_Wstringop_overflow_,
-				   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_Wstringop_overflow_,
-				   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]))
+	  else
 	    {
-	      /* Avoid printing the upper bound if it's invalid.  */
-	      warned = (func
-			? warning_at (loc, OPT_Wstringop_overflow_,
-				      "%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_Wstringop_overflow_,
-				      "%Kwriting %E or more bytes into "
-				      "a region of size %E overflows "
-				      "the destination",
-				      exp, range[0], dstsize));
+	      const bool read
+		= mode == access_read_only || mode == access_read_write;
+	      const bool write
+		= mode == access_write_only || mode == access_read_write;
+	      warned = warn_for_access (loc, func, exp,
+					OPT_Wstringop_overflow_,
+					range, dstsize,
+					write, read && !builtin);
 	    }
-	  else
-	    warned = (func
-		      ? warning_at (loc, OPT_Wstringop_overflow_,
-				    "%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_Wstringop_overflow_,
-				    "%Kwriting between %E and %E bytes "
-				    "into a region of size %E overflows "
-				    "the destination",
-				    exp, range[0], range[1],
-				    dstsize));
+
 	  if (warned)
 	    {
 	      TREE_NO_WARNING (exp) = true;
@@ -4037,10 +4105,15 @@ check_access (tree exp, 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, range, slen, mode)
-	  && pad)
-	inform_access (pad->src, access_read_only);
-
+      const bool read
+	= mode == access_read_only || mode == access_read_write;
+      if (warn_for_access (loc, func, exp, OPT_Wstringop_overread, range,
+			   slen, false, read))
+	{
+	  TREE_NO_WARNING (exp) = true;
+	  if (pad)
+	    inform_access (pad->src, access_read_only);
+	}
       return false;
     }
 
@@ -4065,8 +4138,11 @@ check_read_access (tree exp, tree src, tree bound /* = NULL_TREE */,
 }
 
 /* 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 */,
@@ -4122,8 +4198,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);
@@ -4133,10 +4215,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);
@@ -4146,7 +4231,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);
@@ -4156,6 +4240,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.  */
 
@@ -4348,6 +4487,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))
@@ -4400,7 +4554,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
 /* A "public" wrapper around the above.  Clients should use this overload
    instead.  */
 
-static tree
+tree
 compute_objsize (tree ptr, int ostype, access_ref *pref,
 		 const vr_values *rvals /* = NULL */)
 {
@@ -4977,7 +5131,6 @@ expand_builtin_stpncpy (tree exp, rtx)
   /* The size of the destination object.  */
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
   check_access (exp, len, /*maxread=*/len, src, destsize, data.mode, &data);
-
   return NULL_RTX;
 }
 
@@ -5130,7 +5283,6 @@ expand_builtin_strncat (tree exp, rtx)
 
   check_access (exp, /*dstwrite=*/NULL_TREE, maxread, srclen,
 		destsize, data.mode, &data);
-
   return NULL_RTX;
 }
 
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 94ff96b1292..8136b768750 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;
@@ -202,7 +195,15 @@ struct access_data
   access_mode mode;
 };
 
-extern bool check_access (tree, tree, tree, tree, tree,
-			  access_mode, const access_data * = NULL);
+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, access_mode,
+			  const access_data * = NULL);
 
 #endif /* GCC_BUILTINS_H */
diff --git a/gcc/calls.c b/gcc/calls.c
index b56069ded4d..0e5c696c463 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)
 
@@ -1898,36 +1900,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 == access_read_only
-       ? "read_only"
-       : (access.second.mode == access_write_only
-	  ? "write_only"
-	  : (access.second.mode == 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
@@ -1938,6 +1924,7 @@ static void
 maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
 {
   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
@@ -1966,35 +1953,40 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, 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",
@@ -2011,44 +2003,56 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, 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);
@@ -2057,18 +2061,20 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
 	    }
 	}
 
-      tree objsize = compute_objsize (ptr, 0);
+      access_data data (ptr, access.second.mode, NULL_TREE, false,
+			NULL_TREE, false);
+      access_ref* const pobj = (access.second.mode == access_write_only
+				? &data.dst : &data.src);
+      tree objsize = compute_objsize (ptr, 1, pobj);
 
-      tree srcsize;
-      if (access.second.mode == access_write_only)
+      /* The size of the destination or source object.  */
+      tree dstsize = NULL_TREE, srcsize = NULL_TREE;
+      if (access.second.mode == access_read_only
+	  || access.second.mode == access_none)
 	{
-	  /* For a write-only argument there is no source.  */
-	  srcsize = NULL_TREE;
-	}
-      else
-	{
-	  /* 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 == access_read_only
 	      || access.second.mode == access_none)
@@ -2080,31 +2086,53 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
 	      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, size, /*maxread=*/ NULL_TREE, srcsize, objsize,
-		    access.second.mode);
+      access_mode mode = data.mode;
+      if (mode == access_deferred)
+	mode = TYPE_READONLY (argtype) ? access_read_only : access_read_write;
+      check_access (exp, access_size, /*maxread=*/ NULL_TREE, srcsize,
+		    dstsize, mode, &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 69c71cc4c30..bbc12102d14 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c
@@ -21,7 +21,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)
 {
@@ -45,7 +45,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..74762610f98
--- /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-warning "argument 2 of type 'char\\\[\\\*\\\]' declared with 1 unspecified variable bound" }
+          void f11 (int m, char a[m]);  // { dg-message "subsequently declared as 'char\\\[m]' with 0 unspecified variable bounds" "note" }
+RW (2, 1) void f11 (int n, char arr[n]) // { dg-message "subsequently declared as 'char\\\[n]' with 0 unspecified variable bounds" "note" }
+{ (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..dc06d057479 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)
 {
diff --git a/gcc/testsuite/gcc.dg/dfp/composite-type.c b/gcc/testsuite/gcc.dg/dfp/composite-type.c
index 6d461c76e29..ce7d5c1a0a0 100644
--- a/gcc/testsuite/gcc.dg/dfp/composite-type.c
+++ b/gcc/testsuite/gcc.dg/dfp/composite-type.c
@@ -53,3 +53,6 @@ int main()
 
   return 0;
 }
+
+/* The invalid function redeclarations might also trigger:
+   { dg-prune-output "-Warray-parameter" } */
diff --git a/gcc/testsuite/gcc.dg/torture/pr57147-1.c b/gcc/testsuite/gcc.dg/torture/pr57147-1.c
index 5c2a30e91e6..5fd4ee715ee 100644
--- a/gcc/testsuite/gcc.dg/torture/pr57147-1.c
+++ b/gcc/testsuite/gcc.dg/torture/pr57147-1.c
@@ -2,11 +2,11 @@
 /* { dg-options "-fdump-tree-optimized" } */
 /* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } { "" } } */
 
-struct __jmp_buf_tag {};
+struct __jmp_buf_tag { int mask; };
 typedef struct __jmp_buf_tag jmp_buf[1];
 extern int _setjmp (struct __jmp_buf_tag __env[1]);
 
-jmp_buf g_return_jmp_buf;
+extern jmp_buf g_return_jmp_buf;
 
 void SetNaClSwitchExpectations (void)
 {
diff --git a/gcc/testsuite/gcc.dg/torture/pr57147-3.c b/gcc/testsuite/gcc.dg/torture/pr57147-3.c
index 7a5926a1630..699c7f97a54 100644
--- a/gcc/testsuite/gcc.dg/torture/pr57147-3.c
+++ b/gcc/testsuite/gcc.dg/torture/pr57147-3.c
@@ -1,8 +1,7 @@
 /* { dg-do compile } */
 
 typedef char * ptr_t;
-struct __jmp_buf_tag   {
-};
+struct __jmp_buf_tag { int mask; };
 typedef struct __jmp_buf_tag sigjmp_buf[1];
 sigjmp_buf GC_jmp_buf;
 int __sigsetjmp (sigjmp_buf, int);
diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c
index 0447bb2f3fb..7ed16866afa 100644
--- a/gcc/tree-ssa-uninit.c
+++ b/gcc/tree-ssa-uninit.c
@@ -562,7 +562,7 @@ maybe_warn_pass_by_reference (gimple *stmt, wlimits &wlims)
 	  if (fndecl)
 	    {
 	      location_t loc (DECL_SOURCE_LOCATION (fndecl));
-	      inform (loc, "by argument %u of type %<%s%> to %qD "
+	      inform (loc, "by argument %u of type %s to %qD "
 		      "declared here",
 		      argno, argtypestr.c_str (), fndecl);
 	    }
@@ -570,7 +570,7 @@ maybe_warn_pass_by_reference (gimple *stmt, wlimits &wlims)
 	    {
 	      /* Handle calls through function pointers.  */
 	      location_t loc (gimple_location (stmt));
-	      inform (loc, "by argument %u of type %<%s%> to %qT",
+	      inform (loc, "by argument %u of type %s to %qT",
 		      argno, argtypestr.c_str (), fntype);
 	    }
 	}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-09-19 23:57 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-19 23:57 [gcc r11-3305] Extend -Wstringop-overflow to detect out-of-bounds accesses to array parameters Martin Sebor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).