Move more warning code to gimple-ssa-warn-access etc.
authorMartin Sebor <msebor@redhat.com>
Tue, 17 Aug 2021 20:49:05 +0000 (14:49 -0600)
committerMartin Sebor <msebor@redhat.com>
Tue, 17 Aug 2021 20:49:05 +0000 (14:49 -0600)
Also resolves:
PR middle-end/101854 - Invalid warning -Wstringop-overflow wrong argument

gcc/ChangeLog:

PR middle-end/101854
* builtins.c (expand_builtin_alloca): Move warning code to check_alloca
in gimple-ssa-warn-access.cc.
* calls.c (alloc_max_size): Move code to check_alloca.
(get_size_range): Move to pointer-query.cc.
(maybe_warn_alloc_args_overflow): Move to gimple-ssa-warn-access.cc.
(get_attr_nonstring_decl): Move to tree.c.
(fntype_argno_type): Move to gimple-ssa-warn-access.cc.
(append_attrname): Same.
(maybe_warn_rdwr_sizes): Same.
(initialize_argument_information): Move code to
gimple-ssa-warn-access.cc.
* calls.h (maybe_warn_alloc_args_overflow): Move to
gimple-ssa-warn-access.h.
(get_attr_nonstring_decl): Move to tree.h.
(maybe_warn_nonstring_arg):  Move to gimple-ssa-warn-access.h.
(enum size_range_flags): Move to pointer-query.h.
(get_size_range): Same.
* gimple-ssa-warn-access.cc (has_location): Remove unused overload
to avoid Clang -Wunused-function.
(get_size_range): Declare static.
(maybe_emit_free_warning): Rename...
(maybe_check_dealloc_call): ...to this for consistency.
(class pass_waccess): Add members.
(pass_waccess::~pass_waccess): Defined.
(alloc_max_size): Move here from calls.c.
(maybe_warn_alloc_args_overflow): Same.
(check_alloca): New function.
(check_alloc_size_call): New function.
(check_strncat): Handle another warning flag.
(pass_waccess::check_builtin): Handle alloca.
(fntype_argno_type): Move here from calls.c.
(append_attrname): Same.
(maybe_warn_rdwr_sizes): Same.
(pass_waccess::check_call): Define.
(check_nonstring_args): New function.
(pass_waccess::check): Call new member functions.
(pass_waccess::execute): Enable ranger.
* gimple-ssa-warn-access.h (get_size_range): Move here from calls.h.
(maybe_warn_nonstring_arg): Same.
* gimple-ssa-warn-restrict.c: Remove #include.
* pointer-query.cc (get_size_range): Move here from calls.c.
* pointer-query.h (enum size_range_flags): Same.
(get_size_range): Same.
* tree.c (get_attr_nonstring_decl): Move here from calls.c.
* tree.h (get_attr_nonstring_decl): Move here from calls.h.

gcc/testsuite/ChangeLog:

* gcc.dg/attr-alloc_size-5.c: Adjust optimization to -O1.
* gcc.dg/attr-alloc_size-7.c: Use #pragmas to adjust optimization.
* gcc.dg/attr-alloc_size-8.c: Adjust optimization to -O1.

PR middle-end/101854
* gcc.dg/Wstringop-overflow-72.c: New test.

14 files changed:
gcc/builtins.c
gcc/calls.c
gcc/calls.h
gcc/gimple-ssa-warn-access.cc
gcc/gimple-ssa-warn-access.h
gcc/gimple-ssa-warn-restrict.c
gcc/pointer-query.cc
gcc/pointer-query.h
gcc/testsuite/gcc.dg/Wstringop-overflow-72.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-alloc_size-5.c
gcc/testsuite/gcc.dg/attr-alloc_size-7.c
gcc/testsuite/gcc.dg/attr-alloc_size-8.c
gcc/tree.c
gcc/tree.h

index d2be807..9954862 100644 (file)
@@ -43,7 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "alias.h"
 #include "fold-const.h"
 #include "fold-const-call.h"
-#include "gimple-ssa-warn-restrict.h"
+#include "gimple-ssa-warn-access.h"
 #include "stor-layout.h"
 #include "calls.h"
 #include "varasm.h"
@@ -81,7 +81,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "demangle.h"
 #include "gimple-range.h"
 #include "pointer-query.h"
-#include "gimple-ssa-warn-access.h"
 
 struct target_builtins default_target_builtins;
 #if SWITCHABLE_TARGET
@@ -4896,25 +4895,6 @@ expand_builtin_alloca (tree exp)
   if (!valid_arglist)
     return NULL_RTX;
 
-  if ((alloca_for_var
-       && warn_vla_limit >= HOST_WIDE_INT_MAX
-       && warn_alloc_size_limit < warn_vla_limit)
-      || (!alloca_for_var
-         && warn_alloca_limit >= HOST_WIDE_INT_MAX
-         && warn_alloc_size_limit < warn_alloca_limit
-         ))
-    {
-      /* -Walloca-larger-than and -Wvla-larger-than settings of
-        less than HOST_WIDE_INT_MAX override the more general
-        -Walloc-size-larger-than so unless either of the former
-        options is smaller than the last one (wchich would imply
-        that the call was already checked), check the alloca
-        arguments for overflow.  */
-      tree args[] = { CALL_EXPR_ARG (exp, 0), NULL_TREE };
-      int idx[] = { 0, -1 };
-      maybe_warn_alloc_args_overflow (fndecl, exp, args, idx);
-    }
-
   /* Compute the argument.  */
   op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
 
index fcb0d6d..e50d3fc 100644 (file)
@@ -17,7 +17,6 @@ You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
-#define INCLUDE_STRING
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
@@ -50,7 +49,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl-iter.h"
 #include "tree-vrp.h"
 #include "tree-ssanames.h"
-#include "tree-ssa-strlen.h"
 #include "intl.h"
 #include "stringpool.h"
 #include "hash-map.h"
@@ -60,8 +58,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "attr-fnspec.h"
 #include "value-query.h"
-#include "pointer-query.h"
-#include "gimple-ssa-warn-access.h"
 #include "tree-pretty-print.h"
 
 /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
@@ -1223,397 +1219,6 @@ store_unaligned_arguments_into_pseudos (struct arg_data *args, int num_actuals)
       }
 }
 
-/* The limit set by -Walloc-larger-than=.  */
-static GTY(()) tree alloc_object_size_limit;
-
-/* Initialize ALLOC_OBJECT_SIZE_LIMIT based on the -Walloc-size-larger-than=
-   setting if the option is specified, or to the maximum object size if it
-   is not.  Return the initialized value.  */
-
-static tree
-alloc_max_size (void)
-{
-  if (alloc_object_size_limit)
-    return alloc_object_size_limit;
-
-  HOST_WIDE_INT limit = warn_alloc_size_limit;
-  if (limit == HOST_WIDE_INT_MAX)
-    limit = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node));
-
-  alloc_object_size_limit = build_int_cst (size_type_node, limit);
-
-  return alloc_object_size_limit;
-}
-
-/* Return true when EXP's range can be determined and set RANGE[] to it
-   after adjusting it if necessary to make EXP a represents a valid size
-   of object, or a valid size argument to an allocation function declared
-   with attribute alloc_size (whose argument may be signed), or to a string
-   manipulation function like memset.
-   When ALLOW_ZERO is set in FLAGS, allow returning a range of [0, 0] for
-   a size in an anti-range [1, N] where N > PTRDIFF_MAX.  A zero range is
-   a (nearly) invalid argument to allocation functions like malloc but it
-   is a valid argument to functions like memset.
-   When USE_LARGEST is set in FLAGS set RANGE to the largest valid subrange
-   in a multi-range, otherwise to the smallest valid subrange.  */
-
-bool
-get_size_range (range_query *query, tree exp, gimple *stmt, tree range[2],
-               int flags /* = 0 */)
-{
-  if (!exp)
-    return false;
-
-  if (tree_fits_uhwi_p (exp))
-    {
-      /* EXP is a constant.  */
-      range[0] = range[1] = exp;
-      return true;
-    }
-
-  tree exptype = TREE_TYPE (exp);
-  bool integral = INTEGRAL_TYPE_P (exptype);
-
-  wide_int min, max;
-  enum value_range_kind range_type;
-
-  if (!query)
-    query = get_global_range_query ();
-
-  if (integral)
-    {
-      value_range vr;
-
-      query->range_of_expr (vr, exp, stmt);
-
-      if (vr.undefined_p ())
-       vr.set_varying (TREE_TYPE (exp));
-      range_type = vr.kind ();
-      min = wi::to_wide (vr.min ());
-      max = wi::to_wide (vr.max ());
-    }
-  else
-    range_type = VR_VARYING;
-
-  if (range_type == VR_VARYING)
-    {
-      if (integral)
-       {       
-         /* Use the full range of the type of the expression when
-            no value range information is available.  */
-         range[0] = TYPE_MIN_VALUE (exptype);
-         range[1] = TYPE_MAX_VALUE (exptype);
-         return true;
-       }
-
-      range[0] = NULL_TREE;
-      range[1] = NULL_TREE;
-      return false;
-    }
-
-  unsigned expprec = TYPE_PRECISION (exptype);
-
-  bool signed_p = !TYPE_UNSIGNED (exptype);
-
-  if (range_type == VR_ANTI_RANGE)
-    {
-      if (signed_p)
-       {
-         if (wi::les_p (max, 0))
-           {
-             /* EXP is not in a strictly negative range.  That means
-                it must be in some (not necessarily strictly) positive
-                range which includes zero.  Since in signed to unsigned
-                conversions negative values end up converted to large
-                positive values, and otherwise they are not valid sizes,
-                the resulting range is in both cases [0, TYPE_MAX].  */
-             min = wi::zero (expprec);
-             max = wi::to_wide (TYPE_MAX_VALUE (exptype));
-           }
-         else if (wi::les_p (min - 1, 0))
-           {
-             /* EXP is not in a negative-positive range.  That means EXP
-                is either negative, or greater than max.  Since negative
-                sizes are invalid make the range [MAX + 1, TYPE_MAX].  */
-             min = max + 1;
-             max = wi::to_wide (TYPE_MAX_VALUE (exptype));
-           }
-         else
-           {
-             max = min - 1;
-             min = wi::zero (expprec);
-           }
-       }
-      else
-       {
-         wide_int maxsize = wi::to_wide (max_object_size ());
-         min = wide_int::from (min, maxsize.get_precision (), UNSIGNED);
-         max = wide_int::from (max, maxsize.get_precision (), UNSIGNED);
-         if (wi::eq_p (0, min - 1))
-           {
-             /* EXP is unsigned and not in the range [1, MAX].  That means
-                it's either zero or greater than MAX.  Even though 0 would
-                normally be detected by -Walloc-zero, unless ALLOW_ZERO
-                is set, set the range to [MAX, TYPE_MAX] so that when MAX
-                is greater than the limit the whole range is diagnosed.  */
-             wide_int maxsize = wi::to_wide (max_object_size ());
-             if (flags & SR_ALLOW_ZERO)
-               {
-                 if (wi::leu_p (maxsize, max + 1)
-                     || !(flags & SR_USE_LARGEST))
-                   min = max = wi::zero (expprec);
-                 else
-                   {
-                     min = max + 1;
-                     max = wi::to_wide (TYPE_MAX_VALUE (exptype));
-                   }
-               }
-             else
-               {
-                 min = max + 1;
-                 max = wi::to_wide (TYPE_MAX_VALUE (exptype));
-               }
-           }
-         else if ((flags & SR_USE_LARGEST)
-                  && wi::ltu_p (max + 1, maxsize))
-           {
-             /* When USE_LARGEST is set and the larger of the two subranges
-                is a valid size, use it...  */
-             min = max + 1;
-             max = maxsize;
-           }
-         else
-           {
-             /* ...otherwise use the smaller subrange.  */
-             max = min - 1;
-             min = wi::zero (expprec);
-           }
-       }
-    }
-
-  range[0] = wide_int_to_tree (exptype, min);
-  range[1] = wide_int_to_tree (exptype, max);
-
-  return true;
-}
-
-bool
-get_size_range (tree exp, tree range[2], int flags /* = 0 */)
-{
-  return get_size_range (/*query=*/NULL, exp, /*stmt=*/NULL, range, flags);
-}
-
-/* Diagnose a call EXP to function FN decorated with attribute alloc_size
-   whose argument numbers given by IDX with values given by ARGS exceed
-   the maximum object size or cause an unsigned oveflow (wrapping) when
-   multiplied.  FN is null when EXP is a call via a function pointer.
-   When ARGS[0] is null the function does nothing.  ARGS[1] may be null
-   for functions like malloc, and non-null for those like calloc that
-   are decorated with a two-argument attribute alloc_size.  */
-
-void
-maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
-{
-  /* The range each of the (up to) two arguments is known to be in.  */
-  tree argrange[2][2] = { { NULL_TREE, NULL_TREE }, { NULL_TREE, NULL_TREE } };
-
-  /* Maximum object size set by -Walloc-size-larger-than= or SIZE_MAX / 2.  */
-  tree maxobjsize = alloc_max_size ();
-
-  location_t loc = EXPR_LOCATION (exp);
-
-  tree fntype = fn ? TREE_TYPE (fn) : TREE_TYPE (TREE_TYPE (exp));
-  bool warned = false;
-
-  /* Validate each argument individually.  */
-  for (unsigned i = 0; i != 2 && args[i]; ++i)
-    {
-      if (TREE_CODE (args[i]) == INTEGER_CST)
-       {
-         argrange[i][0] = args[i];
-         argrange[i][1] = args[i];
-
-         if (tree_int_cst_lt (args[i], integer_zero_node))
-           {
-             warned = warning_at (loc, OPT_Walloc_size_larger_than_,
-                                  "argument %i value %qE is negative",
-                                  idx[i] + 1, args[i]);
-           }
-         else if (integer_zerop (args[i]))
-           {
-             /* Avoid issuing -Walloc-zero for allocation functions other
-                than __builtin_alloca that are declared with attribute
-                returns_nonnull because there's no portability risk.  This
-                avoids warning for such calls to libiberty's xmalloc and
-                friends.
-                Also avoid issuing the warning for calls to function named
-                "alloca".  */
-             if (fn && fndecl_built_in_p (fn, BUILT_IN_ALLOCA)
-                 ? IDENTIFIER_LENGTH (DECL_NAME (fn)) != 6
-                 : !lookup_attribute ("returns_nonnull",
-                                      TYPE_ATTRIBUTES (fntype)))
-               warned = warning_at (loc, OPT_Walloc_zero,
-                                    "argument %i value is zero",
-                                    idx[i] + 1);
-           }
-         else if (tree_int_cst_lt (maxobjsize, args[i]))
-           {
-             /* G++ emits calls to ::operator new[](SIZE_MAX) in C++98
-                mode and with -fno-exceptions as a way to indicate array
-                size overflow.  There's no good way to detect C++98 here
-                so avoid diagnosing these calls for all C++ modes.  */
-             if (i == 0
-                 && fn
-                 && !args[1]
-                 && lang_GNU_CXX ()
-                 && DECL_IS_OPERATOR_NEW_P (fn)
-                 && integer_all_onesp (args[i]))
-               continue;
-
-             warned = warning_at (loc, OPT_Walloc_size_larger_than_,
-                                  "argument %i value %qE exceeds "
-                                  "maximum object size %E",
-                                  idx[i] + 1, args[i], maxobjsize);
-           }
-       }
-      else if (TREE_CODE (args[i]) == SSA_NAME
-              && get_size_range (args[i], argrange[i]))
-       {
-         /* Verify that the argument's range is not negative (including
-            upper bound of zero).  */
-         if (tree_int_cst_lt (argrange[i][0], integer_zero_node)
-             && tree_int_cst_le (argrange[i][1], integer_zero_node))
-           {
-             warned = warning_at (loc, OPT_Walloc_size_larger_than_,
-                                  "argument %i range [%E, %E] is negative",
-                                  idx[i] + 1,
-                                  argrange[i][0], argrange[i][1]);
-           }
-         else if (tree_int_cst_lt (maxobjsize, argrange[i][0]))
-           {
-             warned = warning_at (loc, OPT_Walloc_size_larger_than_,
-                                  "argument %i range [%E, %E] exceeds "
-                                  "maximum object size %E",
-                                  idx[i] + 1,
-                                  argrange[i][0], argrange[i][1],
-                                  maxobjsize);
-           }
-       }
-    }
-
-  if (!argrange[0])
-    return;
-
-  /* For a two-argument alloc_size, validate the product of the two
-     arguments if both of their values or ranges are known.  */
-  if (!warned && tree_fits_uhwi_p (argrange[0][0])
-      && argrange[1][0] && tree_fits_uhwi_p (argrange[1][0])
-      && !integer_onep (argrange[0][0])
-      && !integer_onep (argrange[1][0]))
-    {
-      /* Check for overflow in the product of a function decorated with
-        attribute alloc_size (X, Y).  */
-      unsigned szprec = TYPE_PRECISION (size_type_node);
-      wide_int x = wi::to_wide (argrange[0][0], szprec);
-      wide_int y = wi::to_wide (argrange[1][0], szprec);
-
-      wi::overflow_type vflow;
-      wide_int prod = wi::umul (x, y, &vflow);
-
-      if (vflow)
-       warned = warning_at (loc, OPT_Walloc_size_larger_than_,
-                            "product %<%E * %E%> of arguments %i and %i "
-                            "exceeds %<SIZE_MAX%>",
-                            argrange[0][0], argrange[1][0],
-                            idx[0] + 1, idx[1] + 1);
-      else if (wi::ltu_p (wi::to_wide (maxobjsize, szprec), prod))
-       warned = warning_at (loc, OPT_Walloc_size_larger_than_,
-                            "product %<%E * %E%> of arguments %i and %i "
-                            "exceeds maximum object size %E",
-                            argrange[0][0], argrange[1][0],
-                            idx[0] + 1, idx[1] + 1,
-                            maxobjsize);
-
-      if (warned)
-       {
-         /* Print the full range of each of the two arguments to make
-            it clear when it is, in fact, in a range and not constant.  */
-         if (argrange[0][0] != argrange [0][1])
-           inform (loc, "argument %i in the range [%E, %E]",
-                   idx[0] + 1, argrange[0][0], argrange[0][1]);
-         if (argrange[1][0] != argrange [1][1])
-           inform (loc, "argument %i in the range [%E, %E]",
-                   idx[1] + 1, argrange[1][0], argrange[1][1]);
-       }
-    }
-
-  if (warned && fn)
-    {
-      location_t fnloc = DECL_SOURCE_LOCATION (fn);
-
-      if (DECL_IS_UNDECLARED_BUILTIN (fn))
-       inform (loc,
-               "in a call to built-in allocation function %qD", fn);
-      else
-       inform (fnloc,
-               "in a call to allocation function %qD declared here", fn);
-    }
-}
-
-/* If EXPR refers to a character array or pointer declared attribute
-   nonstring return a decl for that array or pointer and set *REF to
-   the referenced enclosing object or pointer.  Otherwise returns
-   null.  */
-
-tree
-get_attr_nonstring_decl (tree expr, tree *ref)
-{
-  tree decl = expr;
-  tree var = NULL_TREE;
-  if (TREE_CODE (decl) == SSA_NAME)
-    {
-      gimple *def = SSA_NAME_DEF_STMT (decl);
-
-      if (is_gimple_assign (def))
-       {
-         tree_code code = gimple_assign_rhs_code (def);
-         if (code == ADDR_EXPR
-             || code == COMPONENT_REF
-             || code == VAR_DECL)
-           decl = gimple_assign_rhs1 (def);
-       }
-      else
-       var = SSA_NAME_VAR (decl);
-    }
-
-  if (TREE_CODE (decl) == ADDR_EXPR)
-    decl = TREE_OPERAND (decl, 0);
-
-  /* To simplify calling code, store the referenced DECL regardless of
-     the attribute determined below, but avoid storing the SSA_NAME_VAR
-     obtained above (it's not useful for dataflow purposes).  */
-  if (ref)
-    *ref = decl;
-
-  /* Use the SSA_NAME_VAR that was determined above to see if it's
-     declared nonstring.  Otherwise drill down into the referenced
-     DECL.  */
-  if (var)
-    decl = var;
-  else if (TREE_CODE (decl) == ARRAY_REF)
-    decl = TREE_OPERAND (decl, 0);
-  else if (TREE_CODE (decl) == COMPONENT_REF)
-    decl = TREE_OPERAND (decl, 1);
-  else if (TREE_CODE (decl) == MEM_REF)
-    return get_attr_nonstring_decl (TREE_OPERAND (decl, 0), ref);
-
-  if (DECL_P (decl)
-      && lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl)))
-    return decl;
-
-  return NULL_TREE;
-}
-
 /* Issue an error if CALL_EXPR was flagged as requiring
    tall-call optimization.  */
 
@@ -1627,310 +1232,6 @@ maybe_complain_about_tail_call (tree call_expr, const char *reason)
   error_at (EXPR_LOCATION (call_expr), "cannot tail-call: %s", reason);
 }
 
-/* Returns the type of the argument ARGNO to function with type FNTYPE
-   or null when the typoe cannot be determined or no such argument exists.  */
-
-static tree
-fntype_argno_type (tree fntype, unsigned argno)
-{
-  if (!prototype_p (fntype))
-    return NULL_TREE;
-
-  tree argtype;
-  function_args_iterator it;
-  FOREACH_FUNCTION_ARGS (fntype, argtype, it)
-    if (argno-- == 0)
-      return argtype;
-
-  return NULL_TREE;
-}
-
-/* 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)
-{
-  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
-   arguments and diagnose past-the-end accesses and related problems
-   in the function call EXP.  */
-
-static void
-maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
-{
-  auto_diagnostic_group adg;
-
-  /* Set if a warning has been issued for any argument (used to decide
-     whether to emit an informational note at the end).  */
-  opt_code opt_warned = N_OPTS;
-
-  /* A string describing the attributes that the warnings issued by this
-     function apply to.  Used to print one informational note per function
-     call, rather than one per warning.  That reduces clutter.  */
-  char attrstr[80];
-  attrstr[0] = 0;
-
-  for (rdwr_map::iterator it = rwm->begin (); it != rwm->end (); ++it)
-    {
-      std::pair<int, attr_access> access = *it;
-
-      /* Get the function call arguments corresponding to the attribute's
-        positional arguments.  When both arguments have been specified
-        there will be two entries in *RWM, one for each.  They are
-        cross-referenced by their respective argument numbers in
-        ACCESS.PTRARG and ACCESS.SIZARG.  */
-      const int ptridx = access.second.ptrarg;
-      const int sizidx = access.second.sizarg;
-
-      gcc_assert (ptridx != -1);
-      gcc_assert (access.first == ptridx || access.first == sizidx);
-
-      /* The pointer is set to null for the entry corresponding to
-        the size argument.  Skip it.  It's handled when the entry
-        corresponding to the pointer argument comes up.  */
-      if (!access.second.ptr)
-       continue;
-
-      tree ptrtype = fntype_argno_type (fntype, ptridx);
-      tree argtype = TREE_TYPE (ptrtype);
-
-      /* 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 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
-       access_size = rwm->get (sizidx)->size;
-
-      /* Format the value or range to avoid an explosion of messages.  */
-      char sizstr[80];
-      tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
-      if (get_size_range (access_size, sizrng, true))
-       {
-         char *s0 = print_generic_expr_to_str (sizrng[0]);
-         if (tree_int_cst_equal (sizrng[0], sizrng[1]))
-           {
-             gcc_checking_assert (strlen (s0) < sizeof sizstr);
-             strcpy (sizstr, s0);
-           }
-         else
-           {
-             char *s1 = print_generic_expr_to_str (sizrng[1]);
-             gcc_checking_assert (strlen (s0) + strlen (s1)
-                                  < sizeof sizstr - 4);
-             sprintf (sizstr, "[%s, %s]", s0, s1);
-             free (s1);
-           }
-         free (s0);
-       }
-      else
-       *sizstr = '\0';
-
-      /* Set if a warning has been issued for the current argument.  */
-      opt_code arg_warned = no_warning;
-      location_t loc = EXPR_LOCATION (exp);
-      tree ptr = access.second.ptr;
-      if (*sizstr
-         && tree_int_cst_sgn (sizrng[0]) < 0
-         && tree_int_cst_sgn (sizrng[1]) < 0)
-       {
-         /* Warn about negative sizes.  */
-         if (access.second.internal_p)
-           {
-             const std::string argtypestr
-               = access.second.array_as_string (ptrtype);
-
-             if (warning_at (loc, OPT_Wstringop_overflow_,
-                             "bound argument %i value %s is "
-                             "negative for a variable length array "
-                             "argument %i of type %s",
-                             sizidx + 1, sizstr,
-                             ptridx + 1, argtypestr.c_str ()))
-               arg_warned = OPT_Wstringop_overflow_;
-           }
-         else if (warning_at (loc, OPT_Wstringop_overflow_,
-                              "argument %i value %s is negative",
-                              sizidx + 1, sizstr))
-           arg_warned = OPT_Wstringop_overflow_;
-
-         if (arg_warned != no_warning)
-           {
-             append_attrname (access, attrstr, sizeof attrstr);
-             /* Remember a warning has been issued and avoid warning
-                again below for the same attribute.  */
-             opt_warned = arg_warned;
-             continue;
-           }
-       }
-
-      if (tree_int_cst_sgn (sizrng[0]) >= 0)
-       {
-         if (COMPLETE_TYPE_P (argtype))
-           {
-             /* Multiply ACCESS_SIZE by the size of the type the pointer
-                argument points to.  If it's incomplete the size is used
-                as is.  */
-             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);
-                   access_size = wide_int_to_tree (sizetype, minsize);
-                 }
-           }
-       }
-      else
-       access_size = NULL_TREE;
-
-      if (integer_zerop (ptr))
-       {
-         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 (access.second.internal_p)
-               {
-                 const std::string argtypestr
-                   = access.second.array_as_string (ptrtype);
-
-                 if (warning_at (loc, OPT_Wnonnull,
-                                 "argument %i of variable length "
-                                 "array %s is null but "
-                                 "the corresponding bound argument "
-                                 "%i value is %s",
-                                 ptridx + 1, argtypestr.c_str (),
-                                 sizidx + 1, sizstr))
-                   arg_warned = OPT_Wnonnull;
-               }
-             else if (warning_at (loc, OPT_Wnonnull,
-                                  "argument %i is null but "
-                                  "the corresponding size argument "
-                                  "%i value is %s",
-                                  ptridx + 1, sizidx + 1, sizstr))
-               arg_warned = OPT_Wnonnull;
-           }
-         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.  */
-             if (warning_at (loc, OPT_Wnonnull,
-                             "argument %i to %<%T[static %E]%> "
-                             "is null where non-null expected",
-                             ptridx + 1, argtype, access_size))
-               arg_warned = OPT_Wnonnull;              
-           }
-
-         if (arg_warned != no_warning)
-           {
-             append_attrname (access, attrstr, sizeof attrstr);
-             /* Remember a warning has been issued and avoid warning
-                again below for the same attribute.  */
-             opt_warned = OPT_Wnonnull;
-             continue;
-           }
-       }
-
-      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);
-
-      /* 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 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)
-           {
-             /* For a read-only attribute there is no destination so
-                clear OBJSIZE.  This emits "reading N bytes" kind of
-                diagnostics instead of the "writing N bytes" kind,
-                unless MODE is none.  */
-             objsize = NULL_TREE;
-           }
-       }
-      else
-       dstsize = objsize;
-
-      /* Clear the no-warning bit in case it was set by check_access
-        in a prior iteration so that accesses via different arguments
-        are diagnosed.  */
-      suppress_warning (exp, OPT_Wstringop_overflow_, false);
-      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 (warning_suppressed_p (exp, OPT_Wstringop_overflow_))
-       opt_warned = OPT_Wstringop_overflow_;
-      if (opt_warned != N_OPTS)
-       {
-         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 (*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 (opt_warned != N_OPTS)
-    {
-      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.  */
-  if (opt_warned != N_OPTS)
-    suppress_warning (exp, opt_warned);
-}
-
 /* Fill in ARGS_SIZE and ARGS array based on the parameters found in
    CALL_EXPR EXP.
 
@@ -2030,27 +1331,6 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 
   bitmap_obstack_release (NULL);
 
-  tree fntypeattrs = TYPE_ATTRIBUTES (fntype);
-  /* Extract attribute alloc_size from the type of the called expression
-     (which could be a function or a function pointer) and if set, store
-     the indices of the corresponding arguments in ALLOC_IDX, and then
-     the actual argument(s) at those indices in ALLOC_ARGS.  */
-  int alloc_idx[2] = { -1, -1 };
-  if (tree alloc_size = lookup_attribute ("alloc_size", fntypeattrs))
-    {
-      tree args = TREE_VALUE (alloc_size);
-      alloc_idx[0] = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
-      if (TREE_CHAIN (args))
-       alloc_idx[1] = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
-    }
-
-  /* Array for up to the two attribute alloc_size arguments.  */
-  tree alloc_args[] = { NULL_TREE, NULL_TREE };
-
-  /* Map of attribute accewss specifications for function arguments.  */
-  rdwr_map rdwr_idx;
-  init_attr_rdwr_indices (&rdwr_idx, fntypeattrs);
-
   /* I counts args in order (to be) pushed; ARGPOS counts in order written.  */
   for (argpos = 0; argpos < num_actuals; i--, argpos++)
     {
@@ -2283,44 +1563,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
         does pass the promoted mode.  */
       arg.mode = TYPE_MODE (type);
       targetm.calls.function_arg_advance (args_so_far, arg);
-
-      /* Store argument values for functions decorated with attribute
-        alloc_size.  */
-      if (argpos == alloc_idx[0])
-       alloc_args[0] = args[i].tree_value;
-      else if (argpos == alloc_idx[1])
-       alloc_args[1] = args[i].tree_value;
-
-      /* Save the actual argument that corresponds to the access attribute
-        operand for later processing.  */
-      if (attr_access *access = rdwr_idx.get (argpos))
-       {
-         if (POINTER_TYPE_P (type))
-           {
-             access->ptr = args[i].tree_value;
-             // A nonnull ACCESS->SIZE contains VLA bounds.  */
-           }
-         else
-           {
-             access->size = args[i].tree_value;
-             gcc_assert (access->ptr == NULL_TREE);
-           }
-       }
-    }
-
-  if (alloc_args[0])
-    {
-      /* Check the arguments of functions decorated with attribute
-        alloc_size.  */
-      maybe_warn_alloc_args_overflow (fndecl, exp, alloc_args, alloc_idx);
     }
-
-  /* Detect passing non-string arguments to functions expecting
-     nul-terminated strings.  */
-  maybe_warn_nonstring_arg (fndecl, exp);
-
-  /* Check attribute access arguments.  */
-  maybe_warn_rdwr_sizes (&rdwr_idx, fndecl, fntype, exp);
 }
 
 /* Update ARGS_SIZE to contain the total size for the argument block.
@@ -6020,6 +5263,3 @@ cxx17_empty_base_field_p (const_tree field)
          && RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))
          && !lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (field)));
 }
-
-/* Tell the garbage collector about GTY markers in this source file.  */
-#include "gt-calls.h"
index 4a018f6..9a98f5d 100644 (file)
@@ -130,21 +130,8 @@ extern bool apply_pass_by_reference_rules (CUMULATIVE_ARGS *,
                                           function_arg_info &);
 extern bool reference_callee_copied (CUMULATIVE_ARGS *,
                                     const function_arg_info &);
-extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
-extern tree get_attr_nonstring_decl (tree, tree * = NULL);
-extern bool maybe_warn_nonstring_arg (tree, tree);
 extern void maybe_complain_about_tail_call (tree, const char *);
-enum size_range_flags
-  {
-   /* Set to consider zero a valid range.  */
-   SR_ALLOW_ZERO = 1,
-   /* Set to use the largest subrange of a set of ranges as opposed
-      to the smallest.  */
-   SR_USE_LARGEST = 2
-  };
-extern bool get_size_range (tree, tree[2], int = 0);
-extern bool get_size_range (class range_query *, tree, gimple *,
-                           tree[2], int = 0);
+
 extern rtx rtx_for_static_chain (const_tree, bool);
 extern bool cxx17_empty_base_field_p (const_tree);
 
index 93f43b7..f3efe56 100644 (file)
@@ -20,6 +20,7 @@
    along with GCC; see the file COPYING3.  If not see
    <http://www.gnu.org/licenses/>.  */
 
+#define INCLUDE_STRING
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
@@ -36,6 +37,7 @@
 #include "fold-const.h"
 #include "gimple-fold.h"
 #include "gimple-iterator.h"
+#include "langhooks.h"
 #include "tree-dfa.h"
 #include "tree-ssa.h"
 #include "tree-cfg.h"
 #include "demangle.h"
 #include "pointer-query.h"
 
-/* Return true if STMT has an associated location.  */
-
-static inline location_t
-has_location (const gimple *stmt)
-{
-  return gimple_has_location (stmt);
-}
-
 /* Return true if tree node X has an associated location.  */
 
 static inline location_t
@@ -1177,7 +1171,7 @@ warn_for_access (location_t loc, tree func, tree expr, int opt,
 /* Helper to set RANGE to the range of BOUND if it's nonnull, bounded
    by BNDRNG if nonnull and valid.  */
 
-void
+static void
 get_size_range (tree bound, tree range[2], const offset_int bndrng[2])
 {
   if (bound)
@@ -2105,14 +2099,14 @@ warn_dealloc_offset (location_t loc, gimple *call, const access_ref &aref)
    form of C++ operatorn new.  */
 
 static void
-maybe_emit_free_warning (gcall *call)
+maybe_check_dealloc_call (gcall *call)
 {
   tree fndecl = gimple_call_fndecl (call);
   if (!fndecl)
     return;
 
   unsigned argno = fndecl_dealloc_argno (fndecl);
-  if ((unsigned) gimple_call_num_args (call) <= argno)
+  if ((unsigned) call_nargs (call) <= argno)
     return;
 
   tree ptr = gimple_call_arg (call, argno);
@@ -2246,9 +2240,9 @@ const pass_data pass_data_waccess = {
 class pass_waccess : public gimple_opt_pass
 {
  public:
-  pass_waccess (gcc::context *ctxt)
-    : gimple_opt_pass (pass_data_waccess, ctxt), m_ranger ()
-    { }
+  pass_waccess (gcc::context *);
+
+  ~pass_waccess ();
 
   opt_pass *clone () { return new pass_waccess (m_ctxt); }
 
@@ -2258,6 +2252,9 @@ class pass_waccess : public gimple_opt_pass
   /* Check a call to a built-in function.  */
   bool check_builtin (gcall *);
 
+  /* Check a call to an ordinary function.  */
+  bool check_call (gcall *);
+
   /* Check statements in a basic block.  */
   void check (basic_block);
 
@@ -2265,9 +2262,35 @@ class pass_waccess : public gimple_opt_pass
  void check (gcall *);
 
 private:
+  /* Not copyable or assignable.  */
+  pass_waccess (pass_waccess &) = delete;
+  void operator= (pass_waccess &) = delete;
+
+  /* A pointer_query object and its cache to store information about
+     pointers and their targets in.  */
+  pointer_query ptr_qry;
+  pointer_query::cache_type var_cache;
+
   gimple_ranger *m_ranger;
 };
 
+/* Construct the pass.  */
+
+pass_waccess::pass_waccess (gcc::context *ctxt)
+  : gimple_opt_pass (pass_data_waccess, ctxt),
+    ptr_qry (m_ranger, &var_cache),
+    var_cache (),
+    m_ranger ()
+{
+}
+
+/* Release pointer_query cache.  */
+
+pass_waccess::~pass_waccess ()
+{
+  ptr_qry.flush_cache ();
+}
+
 /* Return true when any checks performed by the pass are enabled.  */
 
 bool
@@ -2278,12 +2301,256 @@ pass_waccess::gate (function *)
          || warn_mismatched_new_delete);
 }
 
+/* Initialize ALLOC_OBJECT_SIZE_LIMIT based on the -Walloc-size-larger-than=
+   setting if the option is specified, or to the maximum object size if it
+   is not.  Return the initialized value.  */
+
+static tree
+alloc_max_size (void)
+{
+  HOST_WIDE_INT limit = warn_alloc_size_limit;
+  if (limit == HOST_WIDE_INT_MAX)
+    limit = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node));
+
+  return build_int_cst (size_type_node, limit);
+}
+
+/* Diagnose a call EXP to function FN decorated with attribute alloc_size
+   whose argument numbers given by IDX with values given by ARGS exceed
+   the maximum object size or cause an unsigned oveflow (wrapping) when
+   multiplied.  FN is null when EXP is a call via a function pointer.
+   When ARGS[0] is null the function does nothing.  ARGS[1] may be null
+   for functions like malloc, and non-null for those like calloc that
+   are decorated with a two-argument attribute alloc_size.  */
+
+void
+maybe_warn_alloc_args_overflow (gimple *stmt, const tree args[2],
+                               const int idx[2])
+{
+  /* The range each of the (up to) two arguments is known to be in.  */
+  tree argrange[2][2] = { { NULL_TREE, NULL_TREE }, { NULL_TREE, NULL_TREE } };
+
+  /* Maximum object size set by -Walloc-size-larger-than= or SIZE_MAX / 2.  */
+  tree maxobjsize = alloc_max_size ();
+
+  location_t loc = get_location (stmt);
+
+  tree fn = gimple_call_fndecl (stmt);
+  tree fntype = fn ? TREE_TYPE (fn) : gimple_call_fntype (stmt);
+  bool warned = false;
+
+  /* Validate each argument individually.  */
+  for (unsigned i = 0; i != 2 && args[i]; ++i)
+    {
+      if (TREE_CODE (args[i]) == INTEGER_CST)
+       {
+         argrange[i][0] = args[i];
+         argrange[i][1] = args[i];
+
+         if (tree_int_cst_lt (args[i], integer_zero_node))
+           {
+             warned = warning_at (loc, OPT_Walloc_size_larger_than_,
+                                  "argument %i value %qE is negative",
+                                  idx[i] + 1, args[i]);
+           }
+         else if (integer_zerop (args[i]))
+           {
+             /* Avoid issuing -Walloc-zero for allocation functions other
+                than __builtin_alloca that are declared with attribute
+                returns_nonnull because there's no portability risk.  This
+                avoids warning for such calls to libiberty's xmalloc and
+                friends.
+                Also avoid issuing the warning for calls to function named
+                "alloca".  */
+             if (fn && fndecl_built_in_p (fn, BUILT_IN_ALLOCA)
+                 ? IDENTIFIER_LENGTH (DECL_NAME (fn)) != 6
+                 : !lookup_attribute ("returns_nonnull",
+                                      TYPE_ATTRIBUTES (fntype)))
+               warned = warning_at (loc, OPT_Walloc_zero,
+                                    "argument %i value is zero",
+                                    idx[i] + 1);
+           }
+         else if (tree_int_cst_lt (maxobjsize, args[i]))
+           {
+             /* G++ emits calls to ::operator new[](SIZE_MAX) in C++98
+                mode and with -fno-exceptions as a way to indicate array
+                size overflow.  There's no good way to detect C++98 here
+                so avoid diagnosing these calls for all C++ modes.  */
+             if (i == 0
+                 && fn
+                 && !args[1]
+                 && lang_GNU_CXX ()
+                 && DECL_IS_OPERATOR_NEW_P (fn)
+                 && integer_all_onesp (args[i]))
+               continue;
+
+             warned = warning_at (loc, OPT_Walloc_size_larger_than_,
+                                  "argument %i value %qE exceeds "
+                                  "maximum object size %E",
+                                  idx[i] + 1, args[i], maxobjsize);
+           }
+       }
+      else if (TREE_CODE (args[i]) == SSA_NAME
+              && get_size_range (args[i], argrange[i]))
+       {
+         /* Verify that the argument's range is not negative (including
+            upper bound of zero).  */
+         if (tree_int_cst_lt (argrange[i][0], integer_zero_node)
+             && tree_int_cst_le (argrange[i][1], integer_zero_node))
+           {
+             warned = warning_at (loc, OPT_Walloc_size_larger_than_,
+                                  "argument %i range [%E, %E] is negative",
+                                  idx[i] + 1,
+                                  argrange[i][0], argrange[i][1]);
+           }
+         else if (tree_int_cst_lt (maxobjsize, argrange[i][0]))
+           {
+             warned = warning_at (loc, OPT_Walloc_size_larger_than_,
+                                  "argument %i range [%E, %E] exceeds "
+                                  "maximum object size %E",
+                                  idx[i] + 1,
+                                  argrange[i][0], argrange[i][1],
+                                  maxobjsize);
+           }
+       }
+    }
+
+  if (!argrange[0])
+    return;
+
+  /* For a two-argument alloc_size, validate the product of the two
+     arguments if both of their values or ranges are known.  */
+  if (!warned && tree_fits_uhwi_p (argrange[0][0])
+      && argrange[1][0] && tree_fits_uhwi_p (argrange[1][0])
+      && !integer_onep (argrange[0][0])
+      && !integer_onep (argrange[1][0]))
+    {
+      /* Check for overflow in the product of a function decorated with
+        attribute alloc_size (X, Y).  */
+      unsigned szprec = TYPE_PRECISION (size_type_node);
+      wide_int x = wi::to_wide (argrange[0][0], szprec);
+      wide_int y = wi::to_wide (argrange[1][0], szprec);
+
+      wi::overflow_type vflow;
+      wide_int prod = wi::umul (x, y, &vflow);
+
+      if (vflow)
+       warned = warning_at (loc, OPT_Walloc_size_larger_than_,
+                            "product %<%E * %E%> of arguments %i and %i "
+                            "exceeds %<SIZE_MAX%>",
+                            argrange[0][0], argrange[1][0],
+                            idx[0] + 1, idx[1] + 1);
+      else if (wi::ltu_p (wi::to_wide (maxobjsize, szprec), prod))
+       warned = warning_at (loc, OPT_Walloc_size_larger_than_,
+                            "product %<%E * %E%> of arguments %i and %i "
+                            "exceeds maximum object size %E",
+                            argrange[0][0], argrange[1][0],
+                            idx[0] + 1, idx[1] + 1,
+                            maxobjsize);
+
+      if (warned)
+       {
+         /* Print the full range of each of the two arguments to make
+            it clear when it is, in fact, in a range and not constant.  */
+         if (argrange[0][0] != argrange [0][1])
+           inform (loc, "argument %i in the range [%E, %E]",
+                   idx[0] + 1, argrange[0][0], argrange[0][1]);
+         if (argrange[1][0] != argrange [1][1])
+           inform (loc, "argument %i in the range [%E, %E]",
+                   idx[1] + 1, argrange[1][0], argrange[1][1]);
+       }
+    }
+
+  if (warned && fn)
+    {
+      location_t fnloc = DECL_SOURCE_LOCATION (fn);
+
+      if (DECL_IS_UNDECLARED_BUILTIN (fn))
+       inform (loc,
+               "in a call to built-in allocation function %qD", fn);
+      else
+       inform (fnloc,
+               "in a call to allocation function %qD declared here", fn);
+    }
+}
+
+/* Check a call to an alloca function for an excessive size.  */
+
+static void
+check_alloca (gimple *stmt)
+{
+  if ((warn_vla_limit >= HOST_WIDE_INT_MAX
+       && warn_alloc_size_limit < warn_vla_limit)
+      || (warn_alloca_limit >= HOST_WIDE_INT_MAX
+         && warn_alloc_size_limit < warn_alloca_limit))
+    {
+      /* -Walloca-larger-than and -Wvla-larger-than settings of less
+        than  HWI_MAX override the more general -Walloc-size-larger-than
+        so unless either of the former options is smaller than the last
+        one (wchich would imply that the call was already checked), check
+        the alloca arguments for overflow.  */
+      const tree alloc_args[] = { call_arg (stmt, 0), NULL_TREE };
+      const int idx[] = { 0, -1 };
+      maybe_warn_alloc_args_overflow (stmt, alloc_args, idx);
+    }
+}
+
+/* Check a call to an allocation function for an excessive size.  */
+
+static void
+check_alloc_size_call (gimple *stmt)
+{
+  if (gimple_call_num_args (stmt) < 1)
+    /* Avoid invalid calls to functions without a prototype.  */
+    return;
+
+  tree fndecl = gimple_call_fndecl (stmt);
+  if (fndecl && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+    {
+      /* Alloca is handled separately.  */
+      switch (DECL_FUNCTION_CODE (fndecl))
+       {
+       case BUILT_IN_ALLOCA:
+       case BUILT_IN_ALLOCA_WITH_ALIGN:
+       case BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX:
+         return;
+       default:
+         break;
+       }
+    }
+
+  tree fntype = gimple_call_fntype (stmt);
+  tree fntypeattrs = TYPE_ATTRIBUTES (fntype);
+
+  tree alloc_size = lookup_attribute ("alloc_size", fntypeattrs);
+  if (!alloc_size)
+    return;
+
+  /* Extract attribute alloc_size from the type of the called expression
+     (which could be a function or a function pointer) and if set, store
+     the indices of the corresponding arguments in ALLOC_IDX, and then
+     the actual argument(s) at those indices in ALLOC_ARGS.  */
+  int idx[2] = { -1, -1 };
+  tree alloc_args[] = { NULL_TREE, NULL_TREE };
+
+  tree args = TREE_VALUE (alloc_size);
+  idx[0] = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
+  alloc_args[0] = call_arg (stmt, idx[0]);
+  if (TREE_CHAIN (args))
+    {
+      idx[1] = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
+      alloc_args[1] = call_arg (stmt, idx[1]);
+    }
+
+  maybe_warn_alloc_args_overflow (stmt, alloc_args, idx);
+}
+
 /* Check a call STMT to strcat() for overflow and warn if it does.  */
 
 static void
 check_strcat (gimple *stmt)
 {
-  if (!warn_stringop_overflow)
+  if (!warn_stringop_overflow && !warn_stringop_overread)
     return;
 
   tree dest = call_arg (stmt, 0);
@@ -2308,7 +2575,7 @@ check_strcat (gimple *stmt)
 static void
 check_strncat (gimple *stmt)
 {
-  if (!warn_stringop_overflow)
+  if (!warn_stringop_overflow && !warn_stringop_overread)
     return;
 
   tree dest = call_arg (stmt, 0);
@@ -2537,6 +2804,12 @@ pass_waccess::check_builtin (gcall *stmt)
 
   switch (DECL_FUNCTION_CODE (callee))
     {
+    case BUILT_IN_ALLOCA:
+    case BUILT_IN_ALLOCA_WITH_ALIGN:
+    case BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX:
+      check_alloca (stmt);
+      return true;
+
     case BUILT_IN_GETTEXT:
     case BUILT_IN_PUTS:
     case BUILT_IN_PUTS_UNLOCKED:
@@ -2639,16 +2912,384 @@ pass_waccess::check_builtin (gcall *stmt)
   return true;
 }
 
+/* Returns the type of the argument ARGNO to function with type FNTYPE
+   or null when the typoe cannot be determined or no such argument exists.  */
+
+static tree
+fntype_argno_type (tree fntype, unsigned argno)
+{
+  if (!prototype_p (fntype))
+    return NULL_TREE;
+
+  tree argtype;
+  function_args_iterator it;
+  FOREACH_FUNCTION_ARGS (fntype, argtype, it)
+    if (argno-- == 0)
+      return argtype;
+
+  return NULL_TREE;
+}
+
+/* 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)
+{
+  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
+   arguments and diagnose past-the-end accesses and related problems
+   in the function call EXP.  */
+
+static void
+maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, gimple *stmt)
+{
+  auto_diagnostic_group adg;
+
+  /* Set if a warning has been issued for any argument (used to decide
+     whether to emit an informational note at the end).  */
+  opt_code opt_warned = no_warning;
+
+  /* A string describing the attributes that the warnings issued by this
+     function apply to.  Used to print one informational note per function
+     call, rather than one per warning.  That reduces clutter.  */
+  char attrstr[80];
+  attrstr[0] = 0;
+
+  for (rdwr_map::iterator it = rwm->begin (); it != rwm->end (); ++it)
+    {
+      std::pair<int, attr_access> access = *it;
+
+      /* Get the function call arguments corresponding to the attribute's
+        positional arguments.  When both arguments have been specified
+        there will be two entries in *RWM, one for each.  They are
+        cross-referenced by their respective argument numbers in
+        ACCESS.PTRARG and ACCESS.SIZARG.  */
+      const int ptridx = access.second.ptrarg;
+      const int sizidx = access.second.sizarg;
+
+      gcc_assert (ptridx != -1);
+      gcc_assert (access.first == ptridx || access.first == sizidx);
+
+      /* The pointer is set to null for the entry corresponding to
+        the size argument.  Skip it.  It's handled when the entry
+        corresponding to the pointer argument comes up.  */
+      if (!access.second.ptr)
+       continue;
+
+      tree ptrtype = fntype_argno_type (fntype, ptridx);
+      tree argtype = TREE_TYPE (ptrtype);
+
+      /* 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 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
+       access_size = rwm->get (sizidx)->size;
+
+      /* Format the value or range to avoid an explosion of messages.  */
+      char sizstr[80];
+      tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
+      if (get_size_range (access_size, sizrng, true))
+       {
+         char *s0 = print_generic_expr_to_str (sizrng[0]);
+         if (tree_int_cst_equal (sizrng[0], sizrng[1]))
+           {
+             gcc_checking_assert (strlen (s0) < sizeof sizstr);
+             strcpy (sizstr, s0);
+           }
+         else
+           {
+             char *s1 = print_generic_expr_to_str (sizrng[1]);
+             gcc_checking_assert (strlen (s0) + strlen (s1)
+                                  < sizeof sizstr - 4);
+             sprintf (sizstr, "[%s, %s]", s0, s1);
+             free (s1);
+           }
+         free (s0);
+       }
+      else
+       *sizstr = '\0';
+
+      /* Set if a warning has been issued for the current argument.  */
+      opt_code arg_warned = no_warning;
+      location_t loc = get_location (stmt);
+      tree ptr = access.second.ptr;
+      if (*sizstr
+         && tree_int_cst_sgn (sizrng[0]) < 0
+         && tree_int_cst_sgn (sizrng[1]) < 0)
+       {
+         /* Warn about negative sizes.  */
+         if (access.second.internal_p)
+           {
+             const std::string argtypestr
+               = access.second.array_as_string (ptrtype);
+
+             if (warning_at (loc, OPT_Wstringop_overflow_,
+                             "bound argument %i value %s is "
+                             "negative for a variable length array "
+                             "argument %i of type %s",
+                             sizidx + 1, sizstr,
+                             ptridx + 1, argtypestr.c_str ()))
+               arg_warned = OPT_Wstringop_overflow_;
+           }
+         else if (warning_at (loc, OPT_Wstringop_overflow_,
+                              "argument %i value %s is negative",
+                              sizidx + 1, sizstr))
+           arg_warned = OPT_Wstringop_overflow_;
+
+         if (arg_warned != no_warning)
+           {
+             append_attrname (access, attrstr, sizeof attrstr);
+             /* Remember a warning has been issued and avoid warning
+                again below for the same attribute.  */
+             opt_warned = arg_warned;
+             continue;
+           }
+       }
+
+      if (tree_int_cst_sgn (sizrng[0]) >= 0)
+       {
+         if (COMPLETE_TYPE_P (argtype))
+           {
+             /* Multiply ACCESS_SIZE by the size of the type the pointer
+                argument points to.  If it's incomplete the size is used
+                as is.  */
+             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);
+                   access_size = wide_int_to_tree (sizetype, minsize);
+                 }
+           }
+       }
+      else
+       access_size = NULL_TREE;
+
+      if (integer_zerop (ptr))
+       {
+         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 (access.second.internal_p)
+               {
+                 const std::string argtypestr
+                   = access.second.array_as_string (ptrtype);
+
+                 if (warning_at (loc, OPT_Wnonnull,
+                                 "argument %i of variable length "
+                                 "array %s is null but "
+                                 "the corresponding bound argument "
+                                 "%i value is %s",
+                                 ptridx + 1, argtypestr.c_str (),
+                                 sizidx + 1, sizstr))
+                   arg_warned = OPT_Wnonnull;
+               }
+             else if (warning_at (loc, OPT_Wnonnull,
+                                  "argument %i is null but "
+                                  "the corresponding size argument "
+                                  "%i value is %s",
+                                  ptridx + 1, sizidx + 1, sizstr))
+               arg_warned = OPT_Wnonnull;
+           }
+         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.  */
+             if (warning_at (loc, OPT_Wnonnull,
+                             "argument %i to %<%T[static %E]%> "
+                             "is null where non-null expected",
+                             ptridx + 1, argtype, access_size))
+               arg_warned = OPT_Wnonnull;
+           }
+
+         if (arg_warned != no_warning)
+           {
+             append_attrname (access, attrstr, sizeof attrstr);
+             /* Remember a warning has been issued and avoid warning
+                again below for the same attribute.  */
+             opt_warned = OPT_Wnonnull;
+             continue;
+           }
+       }
+
+      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);
+
+      /* 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 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)
+           {
+             /* For a read-only attribute there is no destination so
+                clear OBJSIZE.  This emits "reading N bytes" kind of
+                diagnostics instead of the "writing N bytes" kind,
+                unless MODE is none.  */
+             objsize = NULL_TREE;
+           }
+       }
+      else
+       dstsize = objsize;
+
+      /* Clear the no-warning bit in case it was set by check_access
+        in a prior iteration so that accesses via different arguments
+        are diagnosed.  */
+      suppress_warning (stmt, OPT_Wstringop_overflow_, false);
+      access_mode mode = data.mode;
+      if (mode == access_deferred)
+       mode = TYPE_READONLY (argtype) ? access_read_only : access_read_write;
+      check_access (stmt, access_size, /*maxread=*/ NULL_TREE, srcsize,
+                   dstsize, mode, &data);
+
+      if (warning_suppressed_p (stmt, OPT_Wstringop_overflow_))
+       opt_warned = OPT_Wstringop_overflow_;
+      if (opt_warned != no_warning)
+       {
+         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 (*attrstr)
+    {
+      if (fndecl)
+       inform (get_location (fndecl),
+               "in a call to function %qD declared with attribute %qs",
+               fndecl, attrstr);
+      else
+       inform (get_location (stmt),
+               "in a call with type %qT and attribute %qs",
+               fntype, attrstr);
+    }
+  else if (opt_warned != no_warning)
+    {
+      if (fndecl)
+       inform (get_location (fndecl),
+               "in a call to function %qD", fndecl);
+      else
+       inform (get_location (stmt),
+               "in a call with type %qT", fntype);
+    }
+
+  /* Set the bit in case if was cleared and not set above.  */
+  if (opt_warned != no_warning)
+    suppress_warning (stmt, opt_warned);
+}
+
+/* Check call STMT to an ordinary (non-built-in) function for invalid
+   accesses.  Return true if a call has been handled.  */
+
+bool
+pass_waccess::check_call (gcall *stmt)
+{
+  tree fntype = gimple_call_fntype (stmt);
+  if (!fntype)
+    return false;
+
+  tree fntypeattrs = TYPE_ATTRIBUTES (fntype);
+  if (!fntypeattrs)
+    return false;
+
+  /* Map of attribute accewss specifications for function arguments.  */
+  rdwr_map rdwr_idx;
+  init_attr_rdwr_indices (&rdwr_idx, fntypeattrs);
+
+  unsigned nargs = call_nargs (stmt);
+  for (unsigned i = 0; i != nargs; ++i)
+    {
+      tree arg = call_arg (stmt, i);
+
+      /* Save the actual argument that corresponds to the access attribute
+        operand for later processing.  */
+      if (attr_access *access = rdwr_idx.get (i))
+       {
+         if (POINTER_TYPE_P (TREE_TYPE (arg)))
+           {
+             access->ptr = arg;
+             // A nonnull ACCESS->SIZE contains VLA bounds.  */
+           }
+         else
+           {
+             access->size = arg;
+             gcc_assert (access->ptr == NULL_TREE);
+           }
+       }
+    }
+
+  /* Check attribute access arguments.  */
+  tree fndecl = gimple_call_fndecl (stmt);
+  maybe_warn_rdwr_sizes (&rdwr_idx, fndecl, fntype, stmt);
+
+  check_alloc_size_call (stmt);
+  return true;
+}
+
+/* Check arguments in a call STMT for attribute nonstring.  */
+
+static void
+check_nonstring_args (gcall *stmt)
+{
+  tree fndecl = gimple_call_fndecl (stmt);
+
+  /* Detect passing non-string arguments to functions expecting
+     nul-terminated strings.  */
+  maybe_warn_nonstring_arg (fndecl, stmt);
+}
+
 /* Check call STMT for invalid accesses.  */
 
 void
 pass_waccess::check (gcall *stmt)
 {
-  if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
-      && check_builtin (stmt))
-    return;
+  if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+    check_builtin (stmt);
 
-  maybe_emit_free_warning (stmt);
+  if (is_gimple_call (stmt))
+    check_call (stmt);
+
+  maybe_check_dealloc_call (stmt);
+
+  check_nonstring_args (stmt);
 }
 
 /* Check basic block BB for invalid accesses.  */
@@ -2669,6 +3310,8 @@ pass_waccess::check (basic_block bb)
 unsigned
 pass_waccess::execute (function *fun)
 {
+  m_ranger = enable_ranger (fun);
+
   basic_block bb;
   FOR_EACH_BB_FN (bb, fun)
     check (bb);
index 8b33ecb..1cd3a28 100644 (file)
@@ -31,7 +31,9 @@ extern void warn_string_no_nul (location_t, tree, const char *, tree,
                                tree, tree = NULL_TREE, bool = false,
                                const wide_int[2] = NULL);
 extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
-extern void get_size_range (tree, tree[2], const offset_int[2]);
+
+extern bool maybe_warn_nonstring_arg (tree, gimple *);
+extern bool maybe_warn_nonstring_arg (tree, tree);
 
 class access_data;
 extern bool maybe_warn_for_bound (opt_code, location_t, gimple *, tree,
index 404acb0..d1df9ca 100644 (file)
@@ -29,6 +29,7 @@
 #include "pointer-query.h"
 #include "ssa.h"
 #include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-access.h"
 #include "gimple-ssa-warn-restrict.h"
 #include "diagnostic-core.h"
 #include "fold-const.h"
index bc7cac0..99caf78 100644 (file)
 #include "system.h"
 #include "coretypes.h"
 #include "backend.h"
-#include "target.h"
-#include "rtl.h"
 #include "tree.h"
-#include "memmodel.h"
 #include "gimple.h"
-#include "predict.h"
-#include "tm_p.h"
 #include "stringpool.h"
 #include "tree-vrp.h"
-#include "tree-ssanames.h"
-#include "expmed.h"
-#include "optabs.h"
-#include "emit-rtl.h"
-#include "recog.h"
 #include "diagnostic-core.h"
-#include "alias.h"
 #include "fold-const.h"
-#include "fold-const-call.h"
-#include "gimple-ssa-warn-restrict.h"
-#include "stor-layout.h"
-#include "calls.h"
-#include "varasm.h"
 #include "tree-object-size.h"
 #include "tree-ssa-strlen.h"
-#include "realmpfr.h"
-#include "cfgrtl.h"
-#include "except.h"
-#include "dojump.h"
-#include "explow.h"
-#include "stmt.h"
-#include "expr.h"
-#include "libfuncs.h"
-#include "output.h"
-#include "typeclass.h"
 #include "langhooks.h"
-#include "value-prof.h"
-#include "builtins.h"
 #include "stringpool.h"
 #include "attribs.h"
-#include "asan.h"
-#include "internal-fn.h"
-#include "case-cfn-macros.h"
 #include "gimple-fold.h"
 #include "intl.h"
-#include "tree-dfa.h"
-#include "gimple-iterator.h"
-#include "gimple-ssa.h"
-#include "tree-ssa-live.h"
-#include "tree-outof-ssa.h"
 #include "attr-fnspec.h"
 #include "gimple-range.h"
-
 #include "pointer-query.h"
 
 static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
@@ -311,6 +274,164 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
   return NULL_TREE;
 }
 
+/* Return true when EXP's range can be determined and set RANGE[] to it
+   after adjusting it if necessary to make EXP a represents a valid size
+   of object, or a valid size argument to an allocation function declared
+   with attribute alloc_size (whose argument may be signed), or to a string
+   manipulation function like memset.
+   When ALLOW_ZERO is set in FLAGS, allow returning a range of [0, 0] for
+   a size in an anti-range [1, N] where N > PTRDIFF_MAX.  A zero range is
+   a (nearly) invalid argument to allocation functions like malloc but it
+   is a valid argument to functions like memset.
+   When USE_LARGEST is set in FLAGS set RANGE to the largest valid subrange
+   in a multi-range, otherwise to the smallest valid subrange.  */
+
+bool
+get_size_range (range_query *query, tree exp, gimple *stmt, tree range[2],
+               int flags /* = 0 */)
+{
+  if (!exp)
+    return false;
+
+  if (tree_fits_uhwi_p (exp))
+    {
+      /* EXP is a constant.  */
+      range[0] = range[1] = exp;
+      return true;
+    }
+
+  tree exptype = TREE_TYPE (exp);
+  bool integral = INTEGRAL_TYPE_P (exptype);
+
+  wide_int min, max;
+  enum value_range_kind range_type;
+
+  if (!query)
+    query = get_global_range_query ();
+
+  if (integral)
+    {
+      value_range vr;
+
+      query->range_of_expr (vr, exp, stmt);
+
+      if (vr.undefined_p ())
+       vr.set_varying (TREE_TYPE (exp));
+      range_type = vr.kind ();
+      min = wi::to_wide (vr.min ());
+      max = wi::to_wide (vr.max ());
+    }
+  else
+    range_type = VR_VARYING;
+
+  if (range_type == VR_VARYING)
+    {
+      if (integral)
+       {       
+         /* Use the full range of the type of the expression when
+            no value range information is available.  */
+         range[0] = TYPE_MIN_VALUE (exptype);
+         range[1] = TYPE_MAX_VALUE (exptype);
+         return true;
+       }
+
+      range[0] = NULL_TREE;
+      range[1] = NULL_TREE;
+      return false;
+    }
+
+  unsigned expprec = TYPE_PRECISION (exptype);
+
+  bool signed_p = !TYPE_UNSIGNED (exptype);
+
+  if (range_type == VR_ANTI_RANGE)
+    {
+      if (signed_p)
+       {
+         if (wi::les_p (max, 0))
+           {
+             /* EXP is not in a strictly negative range.  That means
+                it must be in some (not necessarily strictly) positive
+                range which includes zero.  Since in signed to unsigned
+                conversions negative values end up converted to large
+                positive values, and otherwise they are not valid sizes,
+                the resulting range is in both cases [0, TYPE_MAX].  */
+             min = wi::zero (expprec);
+             max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+           }
+         else if (wi::les_p (min - 1, 0))
+           {
+             /* EXP is not in a negative-positive range.  That means EXP
+                is either negative, or greater than max.  Since negative
+                sizes are invalid make the range [MAX + 1, TYPE_MAX].  */
+             min = max + 1;
+             max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+           }
+         else
+           {
+             max = min - 1;
+             min = wi::zero (expprec);
+           }
+       }
+      else
+       {
+         wide_int maxsize = wi::to_wide (max_object_size ());
+         min = wide_int::from (min, maxsize.get_precision (), UNSIGNED);
+         max = wide_int::from (max, maxsize.get_precision (), UNSIGNED);
+         if (wi::eq_p (0, min - 1))
+           {
+             /* EXP is unsigned and not in the range [1, MAX].  That means
+                it's either zero or greater than MAX.  Even though 0 would
+                normally be detected by -Walloc-zero, unless ALLOW_ZERO
+                is set, set the range to [MAX, TYPE_MAX] so that when MAX
+                is greater than the limit the whole range is diagnosed.  */
+             wide_int maxsize = wi::to_wide (max_object_size ());
+             if (flags & SR_ALLOW_ZERO)
+               {
+                 if (wi::leu_p (maxsize, max + 1)
+                     || !(flags & SR_USE_LARGEST))
+                   min = max = wi::zero (expprec);
+                 else
+                   {
+                     min = max + 1;
+                     max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+                   }
+               }
+             else
+               {
+                 min = max + 1;
+                 max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+               }
+           }
+         else if ((flags & SR_USE_LARGEST)
+                  && wi::ltu_p (max + 1, maxsize))
+           {
+             /* When USE_LARGEST is set and the larger of the two subranges
+                is a valid size, use it...  */
+             min = max + 1;
+             max = maxsize;
+           }
+         else
+           {
+             /* ...otherwise use the smaller subrange.  */
+             max = min - 1;
+             min = wi::zero (expprec);
+           }
+       }
+    }
+
+  range[0] = wide_int_to_tree (exptype, min);
+  range[1] = wide_int_to_tree (exptype, max);
+
+  return true;
+}
+
+bool
+get_size_range (tree exp, tree range[2], int flags /* = 0 */)
+{
+  return get_size_range (/*query=*/NULL, exp, /*stmt=*/NULL, range, flags);
+}
+
 /* If STMT is a call to an allocation function, returns the constant
    maximum size of the object allocated by the call represented as
    sizetype.  If nonnull, sets RNG1[] to the range of the size.
index 8bd538a..eb7e90d 100644 (file)
@@ -230,6 +230,17 @@ struct access_data
   access_mode mode;
 };
 
+enum size_range_flags
+  {
+   /* Set to consider zero a valid range.  */
+   SR_ALLOW_ZERO = 1,
+   /* Set to use the largest subrange of a set of ranges as opposed
+      to the smallest.  */
+   SR_USE_LARGEST = 2
+  };
+extern bool get_size_range (tree, tree[2], int = 0);
+extern bool get_size_range (range_query *, tree, gimple *, tree[2], int = 0);
+
 class range_query;
 extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
                                    range_query * = NULL);
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-72.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-72.c
new file mode 100644 (file)
index 0000000..c10773e
--- /dev/null
@@ -0,0 +1,13 @@
+/* PR middle-end/101854 - Invalid warning -Wstringop-overflow wrong argument
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+struct A { int a[5]; };
+
+struct A g (int*, int[6][8]);
+
+struct A f (void)
+{
+  int a[2];
+  return g (a, 0);            // { dg-bogus "-Wstringop-overflow" }
+}
index 7aa7cbf..4eea625 100644 (file)
@@ -4,7 +4,7 @@
    zero bytes.  For standard allocation functions the return value is
    implementation-defined and so relying on it may be a source of bugs.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -Wall -Walloc-zero" } */
+/* { dg-options "-O1 -Wall -Walloc-zero" } */
 
 #define SCHAR_MAX  __SCHAR_MAX__
 #define SCHAR_MIN  (-SCHAR_MAX - 1)
index 68602ec..3adde5c 100644 (file)
@@ -4,7 +4,7 @@
    of the maximum specified by -Walloc-size-larger-than=maximum.  */
 /* { dg-do compile } */
 /* { dg-require-effective-target alloca } */
-/* { dg-options "-O2 -Wall -Walloc-size-larger-than=12345" } */
+/* { dg-options "-O1 -Wall -Walloc-size-larger-than=12345" } */
 
 #define SIZE_MAX   __SIZE_MAX__
 #define MAXOBJSZ   12345
@@ -13,15 +13,40 @@ typedef __SIZE_TYPE__ size_t;
 
 void sink (void*);
 
-static size_t maxobjsize (void)
+#pragma GCC push_options
+/* Verify that constant evaluation takes place even at -O0.  */
+#pragma GCC optimize ("0")
+
+void test_cst (void *p)
 {
-  return MAXOBJSZ;
+  enum { max = MAXOBJSZ };
+
+  sink (__builtin_aligned_alloc (1, max));
+  sink (__builtin_aligned_alloc (1, max + 1));   /* { dg-warning "argument 2 value .12346\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_alloca (max));
+  sink (__builtin_alloca (max + 2));   /* { dg-warning "argument 1 value .12347\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_calloc (1, max));
+  sink (__builtin_calloc (max, 1));
+
+  sink (__builtin_calloc (max / 2, 3));   /* { dg-warning "product .6172\[lu\]* \\* 3\[lu\]*. of arguments 1 and 2 exceeds maximum object size 12345" } */
+  sink (__builtin_calloc (4, max / 3));   /* { dg-warning "product .4\[lu\]* \\* 4115\[lu\]*. of arguments 1 and 2 exceeds maximum object size 12345" } */
+
+  sink (__builtin_malloc (max));
+  sink (__builtin_malloc (max + 3));   /* { dg-warning "argument 1 value .12348\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_realloc (p, max));
+  sink (__builtin_realloc (p, max + 4));  /* { dg-warning "argument 2 value .12349\[lu\]*. exceeds maximum object size 12345" } */
 }
 
 
-void test_var (void *p)
+/* Variable evaluation needs -O1.  */
+#pragma GCC pop_options
+
+__attribute__ ((noipa)) void test_var (void *p)
 {
-  size_t max = maxobjsize ();
+  size_t max = MAXOBJSZ;
 
   sink (__builtin_aligned_alloc (1, max));
   sink (__builtin_aligned_alloc (1, max + 1));   /* { dg-warning "argument 2 value .12346\[lu\]*. exceeds maximum object size 12345" } */
@@ -43,7 +68,15 @@ void test_var (void *p)
 }
 
 
-void test_range (void *p, size_t range)
+/* Value range evaluation (apparently) needs -O2 here.  */
+#pragma GCC optimize ("2")
+
+static size_t maxobjsize (void)
+{
+  return MAXOBJSZ;
+}
+
+__attribute__ ((noipa)) void test_range (void *p, size_t range)
 {
   /* Make sure the variable is at least as large as the maximum object
      size but also make sure that it's guaranteed not to be too big to
index 91d7eb5..7b47b04 100644 (file)
@@ -4,7 +4,7 @@
    two more specific options override the more general latter option.  */
 /* { dg-do compile } */
 /* { dg-require-effective-target alloca } */
-/* { dg-options "-O2 -Walloc-size-larger-than=123 -Walloca-larger-than=234 -Wvla-larger-than=345" } */
+/* { dg-options "-O -Walloc-size-larger-than=123 -Walloca-larger-than=234 -Wvla-larger-than=345" } */
 
 typedef __SIZE_TYPE__ size_t;
 
index 6ec8a97..cba3bca 100644 (file)
@@ -14464,6 +14464,60 @@ fndecl_dealloc_argno (tree fndecl)
   return UINT_MAX;
 }
 
+/* If EXPR refers to a character array or pointer declared attribute
+   nonstring, return a decl for that array or pointer and set *REF
+   to the referenced enclosing object or pointer.  Otherwise return
+   null.  */
+
+tree
+get_attr_nonstring_decl (tree expr, tree *ref)
+{
+  tree decl = expr;
+  tree var = NULL_TREE;
+  if (TREE_CODE (decl) == SSA_NAME)
+    {
+      gimple *def = SSA_NAME_DEF_STMT (decl);
+
+      if (is_gimple_assign (def))
+       {
+         tree_code code = gimple_assign_rhs_code (def);
+         if (code == ADDR_EXPR
+             || code == COMPONENT_REF
+             || code == VAR_DECL)
+           decl = gimple_assign_rhs1 (def);
+       }
+      else
+       var = SSA_NAME_VAR (decl);
+    }
+
+  if (TREE_CODE (decl) == ADDR_EXPR)
+    decl = TREE_OPERAND (decl, 0);
+
+  /* To simplify calling code, store the referenced DECL regardless of
+     the attribute determined below, but avoid storing the SSA_NAME_VAR
+     obtained above (it's not useful for dataflow purposes).  */
+  if (ref)
+    *ref = decl;
+
+  /* Use the SSA_NAME_VAR that was determined above to see if it's
+     declared nonstring.  Otherwise drill down into the referenced
+     DECL.  */
+  if (var)
+    decl = var;
+  else if (TREE_CODE (decl) == ARRAY_REF)
+    decl = TREE_OPERAND (decl, 0);
+  else if (TREE_CODE (decl) == COMPONENT_REF)
+    decl = TREE_OPERAND (decl, 1);
+  else if (TREE_CODE (decl) == MEM_REF)
+    return get_attr_nonstring_decl (TREE_OPERAND (decl, 0), ref);
+
+  if (DECL_P (decl)
+      && lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl)))
+    return decl;
+
+  return NULL_TREE;
+}
+
 #if CHECKING_P
 
 namespace selftest {
index 78d8a04..905417f 100644 (file)
@@ -6488,4 +6488,10 @@ extern void copy_warning (tree, const_tree);
    value if it isn't.  */
 extern unsigned fndecl_dealloc_argno (tree);
 
+/* If an expression refers to a character array or pointer declared
+   attribute nonstring, return a decl for that array or pointer and
+   if nonnull, set the second argument to the referenced enclosing
+   object or pointer.  Otherwise return null.  */
+extern tree get_attr_nonstring_decl (tree, tree * = NULL);
+
 #endif  /* GCC_TREE_H  */