public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-2976] Move more warning code to gimple-ssa-warn-access etc.
@ 2021-08-17 20:51 Martin Sebor
0 siblings, 0 replies; only message in thread
From: Martin Sebor @ 2021-08-17 20:51 UTC (permalink / raw)
To: gcc-cvs
https://gcc.gnu.org/g:b48d4e6818674898f90d9358378c127511ef0f9f
commit r12-2976-gb48d4e6818674898f90d9358378c127511ef0f9f
Author: Martin Sebor <msebor@redhat.com>
Date: Tue Aug 17 14:49:05 2021 -0600
Move more warning code to gimple-ssa-warn-access etc.
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.
Diff:
---
gcc/builtins.c | 22 +-
gcc/calls.c | 760 ---------------------------
gcc/calls.h | 15 +-
gcc/gimple-ssa-warn-access.cc | 683 +++++++++++++++++++++++-
gcc/gimple-ssa-warn-access.h | 4 +-
gcc/gimple-ssa-warn-restrict.c | 1 +
gcc/pointer-query.cc | 195 +++++--
gcc/pointer-query.h | 11 +
gcc/testsuite/gcc.dg/Wstringop-overflow-72.c | 13 +
gcc/testsuite/gcc.dg/attr-alloc_size-5.c | 2 +-
gcc/testsuite/gcc.dg/attr-alloc_size-7.c | 45 +-
gcc/testsuite/gcc.dg/attr-alloc_size-8.c | 2 +-
gcc/tree.c | 54 ++
gcc/tree.h | 6 +
14 files changed, 952 insertions(+), 861 deletions(-)
diff --git a/gcc/builtins.c b/gcc/builtins.c
index d2be807f1d6..99548627761 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -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));
diff --git a/gcc/calls.c b/gcc/calls.c
index fcb0d6dec69..e50d3fc3b62 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -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"
diff --git a/gcc/calls.h b/gcc/calls.h
index 4a018f6ae85..9a98f5de75f 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -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);
diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 93f43b711e2..f3efe564af0 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -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"
@@ -50,14 +52,6 @@
#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);
diff --git a/gcc/gimple-ssa-warn-access.h b/gcc/gimple-ssa-warn-access.h
index 8b33ecbd4fb..1cd3a28c421 100644
--- a/gcc/gimple-ssa-warn-access.h
+++ b/gcc/gimple-ssa-warn-access.h
@@ -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,
diff --git a/gcc/gimple-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c
index 404acb03195..d1df9ca8d4f 100644
--- a/gcc/gimple-ssa-warn-restrict.c
+++ b/gcc/gimple-ssa-warn-restrict.c
@@ -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"
diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index bc7cac092f5..99caf78bfa7 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -22,58 +22,21 @@
#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.
diff --git a/gcc/pointer-query.h b/gcc/pointer-query.h
index 8bd538a3ac2..eb7e90dde03 100644
--- a/gcc/pointer-query.h
+++ b/gcc/pointer-query.h
@@ -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
index 00000000000..c10773e90a3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-72.c
@@ -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" }
+}
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-5.c b/gcc/testsuite/gcc.dg/attr-alloc_size-5.c
index 7aa7cbf0c72..4eea625a966 100644
--- a/gcc/testsuite/gcc.dg/attr-alloc_size-5.c
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-5.c
@@ -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)
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-7.c b/gcc/testsuite/gcc.dg/attr-alloc_size-7.c
index 68602ec37d9..3adde5c2270 100644
--- a/gcc/testsuite/gcc.dg/attr-alloc_size-7.c
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-7.c
@@ -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
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-8.c b/gcc/testsuite/gcc.dg/attr-alloc_size-8.c
index 91d7eb58e29..7b47b045b11 100644
--- a/gcc/testsuite/gcc.dg/attr-alloc_size-8.c
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-8.c
@@ -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;
diff --git a/gcc/tree.c b/gcc/tree.c
index 6ec8a9738c8..cba3bca41b3 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -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 {
diff --git a/gcc/tree.h b/gcc/tree.h
index 78d8a049d29..905417fd17b 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -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 */
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2021-08-17 20:51 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-17 20:51 [gcc r12-2976] Move more warning code to gimple-ssa-warn-access etc Martin Sebor
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).