Extend -Wstringop-overflow to detect out-of-bounds accesses to array parameters.
authorMartin Sebor <msebor@redhat.com>
Sat, 19 Sep 2020 23:37:05 +0000 (17:37 -0600)
committerMartin Sebor <msebor@redhat.com>
Sat, 19 Sep 2020 23:37:05 +0000 (17:37 -0600)
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.

15 files changed:
gcc/builtins.c
gcc/builtins.h
gcc/calls.c
gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
gcc/testsuite/g++.dg/ext/attr-access.C
gcc/testsuite/gcc.dg/Warray-bounds-30.c
gcc/testsuite/gcc.dg/Wstringop-overflow-23.c
gcc/testsuite/gcc.dg/Wstringop-overflow-24.c
gcc/testsuite/gcc.dg/Wstringop-overflow-40.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-access-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-access-none.c
gcc/testsuite/gcc.dg/dfp/composite-type.c
gcc/testsuite/gcc.dg/torture/pr57147-1.c
gcc/testsuite/gcc.dg/torture/pr57147-3.c
gcc/tree-ssa-uninit.c

index 8b9a4a4d948b37a2aa438bc2a59d66b398850413..45efc1c3992a1b99b25da6be13b4073947c9bcd2 100644 (file)
@@ -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;
 }
 
index 94ff96b1292258b131b74eabdfc9d3cc3ef14e97..8136b768750bd60ed35ad298f2ca752b0512d453 100644 (file)
@@ -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 */
index b56069ded4de2ab499203892c479bc41a70520b9..0e5c696c4638febea5a854b210ae615831eb3523 100644 (file)
@@ -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))
            {
-             /* MultiplSIZE 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;
index c4127b805abd73ca10bbae45c723f788a6a9e10e..38e06ba5e6295f5da0928f49a47cedaade62b37e 100644 (file)
@@ -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;
index 3b9c1a36e30369102de4ac2eecf2d1b2c53eb863..b7b2a5fc4bfc338c6be6dcc3f05a18d9a7a2f1a9 100644 (file)
@@ -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)
index b99656821017626fe1d03fcaec8f7689f715c82e..048a95d6dcffc340f899748b6c399d380e750499 100644 (file)
@@ -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]." } */
 
index 69c71cc4c3036155a7591efbdadcef4156a0f9c6..bbc12102d1406294e2a9fb0e5c304b84498cfc61 100644 (file)
@@ -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)
 {
index a21a1f17a2b38375e62e0f4c48e060382e35a5d2..049d1c6981c44fead4aad805ec3547e33becaec2 100644 (file)
@@ -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 (file)
index 0000000..386c92d
--- /dev/null
@@ -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 (file)
index 0000000..7476261
--- /dev/null
@@ -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 }
index d983f2fac06e77b5fcb8ac46b5500905dac89279..dc06d057479d32a28e00c27c70eba2eafaaeca0f 100644 (file)
@@ -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)
 {
index 6d461c76e2980ef0250c487902d35d0726564d7e..ce7d5c1a0a0fde78d2ee5808c12f0512ae62f3a8 100644 (file)
@@ -53,3 +53,6 @@ int main()
 
   return 0;
 }
+
+/* The invalid function redeclarations might also trigger:
+   { dg-prune-output "-Warray-parameter" } */
index 5c2a30e91e6a7168ff4d15355ecb6c0c674f05d2..5fd4ee715ee542994a34bebd13ff788ca2df7adb 100644 (file)
@@ -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)
 {
index 7a5926a16304e6387ac58c4991c178d7bc8e818e..699c7f97a54789714d13ef9f45dccf71e8bab1b5 100644 (file)
@@ -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);
index 0447bb2f3fbf1d8435ed1685ea5f31a77fca2f55..7ed16866afa60c1b78e38af648db7f7baf4d1c36 100644 (file)
@@ -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);
            }
        }