aboutsummaryrefslogtreecommitdiff
path: root/gcc/gimple-array-bounds.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/gimple-array-bounds.cc')
-rw-r--r--gcc/gimple-array-bounds.cc378
1 files changed, 80 insertions, 298 deletions
diff --git a/gcc/gimple-array-bounds.cc b/gcc/gimple-array-bounds.cc
index 54f3205..0517e5d 100644
--- a/gcc/gimple-array-bounds.cc
+++ b/gcc/gimple-array-bounds.cc
@@ -37,15 +37,15 @@ along with GCC; see the file COPYING3. If not see
#include "domwalk.h"
#include "tree-cfg.h"
#include "attribs.h"
-#include "builtins.h"
+#include "pointer-query.h"
// This purposely returns a value_range, not a value_range_equiv, to
// break the dependency on equivalences for this pass.
const value_range *
-array_bounds_checker::get_value_range (const_tree op)
+array_bounds_checker::get_value_range (const_tree op, gimple *stmt)
{
- return ranges->get_value_range (op);
+ return ranges->get_value_range (op, stmt);
}
/* Try to determine the DECL that REF refers to. Return the DECL or
@@ -173,9 +173,9 @@ trailing_array (tree arg, tree *pref)
bool
array_bounds_checker::check_array_ref (location_t location, tree ref,
- bool ignore_off_by_one)
+ gimple *stmt, bool ignore_off_by_one)
{
- if (TREE_NO_WARNING (ref))
+ if (warning_suppressed_p (ref, OPT_Warray_bounds))
/* Return true to have the caller prevent warnings for enclosing
refs. */
return true;
@@ -287,7 +287,7 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
const value_range *vr = NULL;
if (TREE_CODE (low_sub) == SSA_NAME)
{
- vr = get_value_range (low_sub);
+ vr = get_value_range (low_sub, stmt);
if (!vr->undefined_p () && !vr->varying_p ())
{
low_sub = vr->kind () == VR_RANGE ? vr->max () : vr->min ();
@@ -346,7 +346,7 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
/* Avoid more warnings when checking more significant subscripts
of the same expression. */
ref = TREE_OPERAND (ref, 0);
- TREE_NO_WARNING (ref) = 1;
+ suppress_warning (ref, OPT_Warray_bounds);
if (decl)
ref = decl;
@@ -411,272 +411,82 @@ bool
array_bounds_checker::check_mem_ref (location_t location, tree ref,
bool ignore_off_by_one)
{
- if (TREE_NO_WARNING (ref))
+ if (warning_suppressed_p (ref, OPT_Warray_bounds))
return false;
- tree arg = TREE_OPERAND (ref, 0);
- /* The constant and variable offset of the reference. */
- tree cstoff = TREE_OPERAND (ref, 1);
- tree varoff = NULL_TREE;
-
- const offset_int maxobjsize = tree_to_shwi (max_object_size ());
-
- /* The zero-based array or string constant bounds in bytes. Initially
- set to [-MAXOBJSIZE - 1, MAXOBJSIZE] until a tighter bound is
- determined. */
- offset_int arrbounds[2] = { -maxobjsize - 1, maxobjsize };
-
- /* The minimum and maximum intermediate offset. For a reference
- to be valid, not only does the final offset/subscript must be
- in bounds but all intermediate offsets should be as well.
- GCC may be able to deal gracefully with such out-of-bounds
- offsets so the checking is only enabled at -Warray-bounds=2
- where it may help detect bugs in uses of the intermediate
- offsets that could otherwise not be detectable. */
- offset_int ioff = wi::to_offset (fold_convert (ptrdiff_type_node, cstoff));
- offset_int extrema[2] = { 0, wi::abs (ioff) };
-
- /* The range of the byte offset into the reference. */
- offset_int offrange[2] = { 0, 0 };
-
/* The statement used to allocate the array or null. */
gimple *alloc_stmt = NULL;
/* For an allocation statement, the low bound of the size range. */
offset_int minbound = 0;
+ /* The type and size of the access. */
+ tree axstype = TREE_TYPE (ref);
+ offset_int axssize = 0;
+ if (tree access_size = TYPE_SIZE_UNIT (axstype))
+ if (TREE_CODE (access_size) == INTEGER_CST)
+ axssize = wi::to_offset (access_size);
- /* Determine the offsets and increment OFFRANGE for the bounds of each.
- The loop computes the range of the final offset for expressions such
- as (A + i0 + ... + iN)[CSTOFF] where i0 through iN are SSA_NAMEs in
- some range. */
- const unsigned limit = param_ssa_name_def_chain_limit;
- for (unsigned n = 0; TREE_CODE (arg) == SSA_NAME && n < limit; ++n)
- {
- gimple *def = SSA_NAME_DEF_STMT (arg);
- if (is_gimple_call (def))
- {
- /* Determine the byte size of the array from an allocation call. */
- wide_int sizrng[2];
- if (gimple_call_alloc_size (def, sizrng))
- {
- arrbounds[0] = 0;
- arrbounds[1] = offset_int::from (sizrng[1], UNSIGNED);
- minbound = offset_int::from (sizrng[0], UNSIGNED);
- alloc_stmt = def;
- }
- break;
- }
-
- if (gimple_nop_p (def))
- {
- /* For a function argument try to determine the byte size
- of the array from the current function declaratation
- (e.g., attribute access or related). */
- wide_int wr[2];
- tree ref = gimple_parm_array_size (arg, wr);
- if (!ref)
- break;
- arrbounds[0] = offset_int::from (wr[0], UNSIGNED);
- arrbounds[1] = offset_int::from (wr[1], UNSIGNED);
- arg = ref;
- break;
- }
-
- if (!is_gimple_assign (def))
- break;
-
- tree_code code = gimple_assign_rhs_code (def);
- if (code == POINTER_PLUS_EXPR)
- {
- arg = gimple_assign_rhs1 (def);
- varoff = gimple_assign_rhs2 (def);
- }
- else if (code == ASSERT_EXPR)
- {
- arg = TREE_OPERAND (gimple_assign_rhs1 (def), 0);
- continue;
- }
- else
- return false;
-
- /* VAROFF should always be a SSA_NAME here (and not even
- INTEGER_CST) but there's no point in taking chances. */
- if (TREE_CODE (varoff) != SSA_NAME)
- break;
-
- const value_range* const vr = get_value_range (varoff);
- if (!vr || vr->undefined_p () || vr->varying_p ())
- break;
+ access_ref aref;
+ if (!compute_objsize (ref, 0, &aref, ranges))
+ return false;
- if (!vr->constant_p ())
- break;
+ if (aref.offset_in_range (axssize))
+ return false;
- if (vr->kind () == VR_RANGE)
- {
- offset_int min
- = wi::to_offset (fold_convert (ptrdiff_type_node, vr->min ()));
- offset_int max
- = wi::to_offset (fold_convert (ptrdiff_type_node, vr->max ()));
- if (min < max)
- {
- offrange[0] += min;
- offrange[1] += max;
- }
- else
- {
- /* When MIN >= MAX, the offset is effectively in a union
- of two ranges: [-MAXOBJSIZE -1, MAX] and [MIN, MAXOBJSIZE].
- Since there is no way to represent such a range across
- additions, conservatively add [-MAXOBJSIZE -1, MAXOBJSIZE]
- to OFFRANGE. */
- offrange[0] += arrbounds[0];
- offrange[1] += arrbounds[1];
- }
- }
- else
+ if (TREE_CODE (aref.ref) == SSA_NAME)
+ {
+ gimple *def = SSA_NAME_DEF_STMT (aref.ref);
+ if (is_gimple_call (def))
{
- /* For an anti-range, analogously to the above, conservatively
- add [-MAXOBJSIZE -1, MAXOBJSIZE] to OFFRANGE. */
- offrange[0] += arrbounds[0];
- offrange[1] += arrbounds[1];
+ /* Save the allocation call and the low bound on the size. */
+ alloc_stmt = def;
+ minbound = aref.sizrng[0];
}
-
- /* Keep track of the minimum and maximum offset. */
- if (offrange[1] < 0 && offrange[1] < extrema[0])
- extrema[0] = offrange[1];
- if (offrange[0] > 0 && offrange[0] > extrema[1])
- extrema[1] = offrange[0];
-
- if (offrange[0] < arrbounds[0])
- offrange[0] = arrbounds[0];
-
- if (offrange[1] > arrbounds[1])
- offrange[1] = arrbounds[1];
}
-
- tree reftype = NULL_TREE;
- offset_int eltsize = -1;
- if (arrbounds[0] >= 0)
+
+ /* The range of the byte offset into the reference. Adjusted below. */
+ offset_int offrange[2] = { aref.offrng[0], aref.offrng[1] };
+
+ /* The type of the referenced object. */
+ tree reftype = TREE_TYPE (aref.ref);
+ /* The size of the referenced array element. */
+ offset_int eltsize = 1;
+ if (POINTER_TYPE_P (reftype))
+ reftype = TREE_TYPE (reftype);
+
+ if (TREE_CODE (reftype) == FUNCTION_TYPE)
+ /* Restore the original (pointer) type and avoid trying to create
+ an array of functions (done below). */
+ reftype = TREE_TYPE (aref.ref);
+ else
{
/* The byte size of the array has already been determined above
based on a pointer ARG. Set ELTSIZE to the size of the type
it points to and REFTYPE to the array with the size, rounded
down as necessary. */
- reftype = TREE_TYPE (TREE_TYPE (arg));
if (TREE_CODE (reftype) == ARRAY_TYPE)
reftype = TREE_TYPE (reftype);
if (tree refsize = TYPE_SIZE_UNIT (reftype))
if (TREE_CODE (refsize) == INTEGER_CST)
eltsize = wi::to_offset (refsize);
- if (eltsize < 0)
- return false;
-
- offset_int nelts = arrbounds[1] / eltsize;
+ const offset_int nelts = aref.sizrng[1] / eltsize;
reftype = build_printable_array_type (reftype, nelts.to_uhwi ());
}
- else if (TREE_CODE (arg) == ADDR_EXPR)
- {
- arg = TREE_OPERAND (arg, 0);
- if (TREE_CODE (arg) != STRING_CST
- && TREE_CODE (arg) != PARM_DECL
- && TREE_CODE (arg) != VAR_DECL)
- return false;
-
- /* The type of the object being referred to. It can be an array,
- string literal, or a non-array type when the MEM_REF represents
- a reference/subscript via a pointer to an object that is not
- an element of an array. Incomplete types are excluded as well
- because their size is not known. */
- reftype = TREE_TYPE (arg);
- if (POINTER_TYPE_P (reftype)
- || !COMPLETE_TYPE_P (reftype)
- || TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST)
- return false;
-
- /* Except in declared objects, references to trailing array members
- of structs and union objects are excluded because MEM_REF doesn't
- make it possible to identify the member where the reference
- originated. */
- if (RECORD_OR_UNION_TYPE_P (reftype)
- && (!VAR_P (arg)
- || (DECL_EXTERNAL (arg) && array_at_struct_end_p (ref))))
- return false;
-
- /* FIXME: Should this be 1 for Fortran? */
- arrbounds[0] = 0;
-
- if (TREE_CODE (reftype) == ARRAY_TYPE)
- {
- /* Set to the size of the array element (and adjust below). */
- eltsize = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (reftype)));
- /* Use log2 of size to convert the array byte size in to its
- upper bound in elements. */
- const offset_int eltsizelog2 = wi::floor_log2 (eltsize);
- if (tree dom = TYPE_DOMAIN (reftype))
- {
- tree bnds[] = { TYPE_MIN_VALUE (dom), TYPE_MAX_VALUE (dom) };
- if (TREE_CODE (arg) == COMPONENT_REF)
- {
- offset_int size = maxobjsize;
- if (tree fldsize = component_ref_size (arg))
- size = wi::to_offset (fldsize);
- arrbounds[1] = wi::lrshift (size, eltsizelog2);
- }
- else if (array_at_struct_end_p (arg) || !bnds[0] || !bnds[1])
- arrbounds[1] = wi::lrshift (maxobjsize, eltsizelog2);
- else
- arrbounds[1] = (wi::to_offset (bnds[1]) - wi::to_offset (bnds[0])
- + 1) * eltsize;
- }
- else
- arrbounds[1] = wi::lrshift (maxobjsize, eltsizelog2);
-
- /* Determine a tighter bound of the non-array element type. */
- tree eltype = TREE_TYPE (reftype);
- while (TREE_CODE (eltype) == ARRAY_TYPE)
- eltype = TREE_TYPE (eltype);
- eltsize = wi::to_offset (TYPE_SIZE_UNIT (eltype));
- }
- else
- {
- eltsize = 1;
- tree size = TYPE_SIZE_UNIT (reftype);
- if (VAR_P (arg))
- if (tree initsize = DECL_SIZE_UNIT (arg))
- if (tree_int_cst_lt (size, initsize))
- size = initsize;
-
- arrbounds[1] = wi::to_offset (size);
- }
- }
- else
- return false;
-
- offrange[0] += ioff;
- offrange[1] += ioff;
/* Compute the more permissive upper bound when IGNORE_OFF_BY_ONE
is set (when taking the address of the one-past-last element
of an array) but always use the stricter bound in diagnostics. */
- offset_int ubound = arrbounds[1];
+ offset_int ubound = aref.sizrng[1];
if (ignore_off_by_one)
ubound += eltsize;
- bool warned = false;
/* Set if the lower bound of the subscript is out of bounds. */
- const bool lboob = (arrbounds[0] == arrbounds[1]
+ const bool lboob = (aref.sizrng[1] == 0
|| offrange[0] >= ubound
- || offrange[1] < arrbounds[0]);
+ || offrange[1] < 0);
/* Set if only the upper bound of the subscript is out of bounds.
This can happen when using a bigger type to index into an array
of a smaller type, as is common with unsigned char. */
- tree axstype = TREE_TYPE (ref);
- offset_int axssize = 0;
- if (TREE_CODE (axstype) != UNION_TYPE)
- if (tree access_size = TYPE_SIZE_UNIT (axstype))
- if (TREE_CODE (access_size) == INTEGER_CST)
- axssize = wi::to_offset (access_size);
-
const bool uboob = !lboob && offrange[0] + axssize > ubound;
if (lboob || uboob)
{
@@ -689,9 +499,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
to compute the index to print in the diagnostic; arrays
in MEM_REF don't mean anything. A type with no size like
void is as good as having a size of 1. */
- tree type = TREE_TYPE (ref);
- while (TREE_CODE (type) == ARRAY_TYPE)
- type = TREE_TYPE (type);
+ tree type = strip_array_types (TREE_TYPE (ref));
if (tree size = TYPE_SIZE_UNIT (type))
{
offrange[0] = offrange[0] / wi::to_offset (size);
@@ -699,6 +507,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
}
}
+ bool warned = false;
if (lboob)
{
if (offrange[0] == offrange[1])
@@ -720,7 +529,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
/* If the memory was dynamically allocated refer to it as if
it were an untyped array of bytes. */
backtype = build_array_type_nelts (unsigned_char_type_node,
- arrbounds[1].to_uhwi ());
+ aref.sizrng[1].to_uhwi ());
warned = warning_at (location, OPT_Warray_bounds,
"array subscript %<%T[%wi]%> is partly "
@@ -730,47 +539,9 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
if (warned)
{
- if (DECL_P (arg))
- inform (DECL_SOURCE_LOCATION (arg), "while referencing %qD", arg);
- else if (alloc_stmt)
- {
- location_t loc = gimple_location (alloc_stmt);
- if (gimple_call_builtin_p (alloc_stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
- {
- if (minbound == arrbounds[1])
- inform (loc, "referencing a variable length array "
- "of size %wu", minbound.to_uhwi ());
- else
- inform (loc, "referencing a variable length array "
- "of size between %wu and %wu",
- minbound.to_uhwi (), arrbounds[1].to_uhwi ());
- }
- else if (tree fndecl = gimple_call_fndecl (alloc_stmt))
- {
- if (minbound == arrbounds[1])
- inform (loc, "referencing an object of size %wu "
- "allocated by %qD",
- minbound.to_uhwi (), fndecl);
- else
- inform (loc, "referencing an object of size between "
- "%wu and %wu allocated by %qD",
- minbound.to_uhwi (), arrbounds[1].to_uhwi (), fndecl);
- }
- else
- {
- tree fntype = gimple_call_fntype (alloc_stmt);
- if (minbound == arrbounds[1])
- inform (loc, "referencing an object of size %wu "
- "allocated by %qT",
- minbound.to_uhwi (), fntype);
- else
- inform (loc, "referencing an object of size between "
- "%wu and %wu allocated by %qT",
- minbound.to_uhwi (), arrbounds[1].to_uhwi (), fntype);
- }
- }
-
- TREE_NO_WARNING (ref) = 1;
+ /* TODO: Determine the access from the statement and use it. */
+ aref.inform_access (access_none);
+ suppress_warning (ref, OPT_Warray_bounds);
return true;
}
@@ -779,15 +550,15 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
/* At level 2 check also intermediate offsets. */
int i = 0;
- if (extrema[i] < -arrbounds[1] || extrema[i = 1] > ubound)
+ if (aref.offmax[i] < -aref.sizrng[1] || aref.offmax[i = 1] > ubound)
{
- HOST_WIDE_INT tmpidx = extrema[i].to_shwi () / eltsize.to_shwi ();
+ HOST_WIDE_INT tmpidx = aref.offmax[i].to_shwi () / eltsize.to_shwi ();
if (warning_at (location, OPT_Warray_bounds,
"intermediate array offset %wi is outside array bounds "
"of %qT", tmpidx, reftype))
{
- TREE_NO_WARNING (ref) = 1;
+ suppress_warning (ref, OPT_Warray_bounds);
return true;
}
}
@@ -799,7 +570,8 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
address of an ARRAY_REF, and call check_array_ref on it. */
void
-array_bounds_checker::check_addr_expr (location_t location, tree t)
+array_bounds_checker::check_addr_expr (location_t location, tree t,
+ gimple *stmt)
{
/* For the most significant subscript only, accept taking the address
of the just-past-the-end element. */
@@ -811,14 +583,14 @@ array_bounds_checker::check_addr_expr (location_t location, tree t)
bool warned = false;
if (TREE_CODE (t) == ARRAY_REF)
{
- warned = check_array_ref (location, t, ignore_off_by_one);
+ warned = check_array_ref (location, t, stmt, ignore_off_by_one);
ignore_off_by_one = false;
}
else if (TREE_CODE (t) == MEM_REF)
warned = check_mem_ref (location, t, ignore_off_by_one);
if (warned)
- TREE_NO_WARNING (t) = true;
+ suppress_warning (t, OPT_Warray_bounds);
t = TREE_OPERAND (t, 0);
}
@@ -826,7 +598,7 @@ array_bounds_checker::check_addr_expr (location_t location, tree t)
if (TREE_CODE (t) != MEM_REF
|| TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR
- || TREE_NO_WARNING (t))
+ || warning_suppressed_p (t, OPT_Warray_bounds))
return;
tree tem = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
@@ -886,7 +658,7 @@ array_bounds_checker::check_addr_expr (location_t location, tree t)
if (DECL_P (t))
inform (DECL_SOURCE_LOCATION (t), "while referencing %qD", t);
- TREE_NO_WARNING (t) = 1;
+ suppress_warning (t, OPT_Warray_bounds);
}
}
@@ -895,7 +667,7 @@ array_bounds_checker::check_addr_expr (location_t location, tree t)
problems discussed in pr98266 and pr97595. */
static bool
-inbounds_vbase_memaccess_p (tree t)
+inbounds_memaccess_p (tree t)
{
if (TREE_CODE (t) != COMPONENT_REF)
return false;
@@ -928,10 +700,19 @@ inbounds_vbase_memaccess_p (tree t)
object by adding its offset computed above to the MEM_REF offset. */
tree refoff = TREE_OPERAND (mref, 1);
tree fldoff = int_const_binop (PLUS_EXPR, fldpos, refoff);
+ /* Return false if the member offset is greater or equal to the size
+ of the complete object. */
+ if (!tree_int_cst_lt (fldoff, refsize))
+ return false;
+
+ tree fldsiz = DECL_SIZE_UNIT (fld);
+ if (!fldsiz || TREE_CODE (fldsiz) != INTEGER_CST)
+ return false;
- /* Return true if the member offset is less than the size of the complete
- object. */
- return tree_int_cst_lt (fldoff, refsize);
+ /* Return true if the offset just past the end of the member is less
+ than or equal to the size of the complete object. */
+ tree fldend = int_const_binop (PLUS_EXPR, fldoff, fldsiz);
+ return tree_int_cst_le (fldend, refsize);
}
/* Callback for walk_tree to check a tree for out of bounds array
@@ -955,25 +736,26 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
bool warned = false;
array_bounds_checker *checker = (array_bounds_checker *) wi->info;
if (TREE_CODE (t) == ARRAY_REF)
- warned = checker->check_array_ref (location, t,
+ warned = checker->check_array_ref (location, t, wi->stmt,
false/*ignore_off_by_one*/);
else if (TREE_CODE (t) == MEM_REF)
warned = checker->check_mem_ref (location, t,
false /*ignore_off_by_one*/);
else if (TREE_CODE (t) == ADDR_EXPR)
{
- checker->check_addr_expr (location, t);
+ checker->check_addr_expr (location, t, wi->stmt);
*walk_subtree = false;
}
- else if (inbounds_vbase_memaccess_p (t))
+ else if (inbounds_memaccess_p (t))
/* Hack: Skip MEM_REF checks in accesses to a member of a base class
at an offset that's within the bounds of the enclosing object.
See pr98266 and pr97595. */
*walk_subtree = false;
- /* Propagate the no-warning bit to the outer expression. */
+ /* Propagate the no-warning bit to the outer statement to avoid also
+ issuing -Wstringop-overflow/-overread for the out-of-bounds accesses. */
if (warned)
- TREE_NO_WARNING (t) = true;
+ suppress_warning (wi->stmt, OPT_Warray_bounds);
return NULL_TREE;
}