Extend -Warray-bounds to detect out-of-bounds accesses to array parameters.
gcc/ChangeLog: PR middle-end/82608 PR middle-end/94195 PR c/50584 PR middle-end/84051 * gimple-array-bounds.cc (get_base_decl): New function. (get_ref_size): New function. (trailing_array): New function. (array_bounds_checker::check_array_ref): Call them. Handle arrays declared in function parameters. (array_bounds_checker::check_mem_ref): Same. Handle references to dynamically allocated arrays. gcc/testsuite/ChangeLog: PR middle-end/82608 PR middle-end/94195 PR c/50584 PR middle-end/84051 * c-c++-common/Warray-bounds.c: Adjust. * gcc.dg/Wbuiltin-declaration-mismatch-9.c: Adjust. * gcc.dg/Warray-bounds-63.c: New test. * gcc.dg/Warray-bounds-64.c: New test. * gcc.dg/Warray-bounds-65.c: New test. * gcc.dg/Warray-bounds-66.c: New test. * gcc.dg/Warray-bounds-67.c: New test.
This commit is contained in:
parent
baad4c48a8
commit
3f9a497d1b
@ -36,6 +36,8 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "vr-values.h"
|
||||
#include "domwalk.h"
|
||||
#include "tree-cfg.h"
|
||||
#include "attribs.h"
|
||||
#include "builtins.h"
|
||||
|
||||
// This purposely returns a value_range, not a value_range_equiv, to
|
||||
// break the dependency on equivalences for this pass.
|
||||
@ -46,19 +48,137 @@ array_bounds_checker::get_value_range (const_tree op)
|
||||
return ranges->get_value_range (op);
|
||||
}
|
||||
|
||||
/* Try to determine the DECL that REF refers to. Return the DECL or
|
||||
the expression closest to it. Used in informational notes pointing
|
||||
to referenced objects or function parameters. */
|
||||
|
||||
static tree
|
||||
get_base_decl (tree ref)
|
||||
{
|
||||
tree base = get_base_address (ref);
|
||||
if (DECL_P (base))
|
||||
return base;
|
||||
|
||||
if (TREE_CODE (base) == MEM_REF)
|
||||
base = TREE_OPERAND (base, 0);
|
||||
|
||||
if (TREE_CODE (base) != SSA_NAME)
|
||||
return base;
|
||||
|
||||
do
|
||||
{
|
||||
gimple *def = SSA_NAME_DEF_STMT (base);
|
||||
if (gimple_assign_single_p (def))
|
||||
{
|
||||
base = gimple_assign_rhs1 (def);
|
||||
if (TREE_CODE (base) != ASSERT_EXPR)
|
||||
return base;
|
||||
|
||||
base = TREE_OPERAND (base, 0);
|
||||
if (TREE_CODE (base) != SSA_NAME)
|
||||
return base;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!gimple_nop_p (def))
|
||||
return base;
|
||||
|
||||
break;
|
||||
} while (true);
|
||||
|
||||
tree var = SSA_NAME_VAR (base);
|
||||
if (TREE_CODE (var) != PARM_DECL)
|
||||
return base;
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
/* Return the constant byte size of the object or type referenced by
|
||||
the MEM_REF ARG. On success, set *PREF to the DECL or expression
|
||||
ARG refers to. Otherwise return null. */
|
||||
|
||||
static tree
|
||||
get_ref_size (tree arg, tree *pref)
|
||||
{
|
||||
if (TREE_CODE (arg) != MEM_REF)
|
||||
return NULL_TREE;
|
||||
|
||||
arg = TREE_OPERAND (arg, 0);
|
||||
tree type = TREE_TYPE (arg);
|
||||
if (!POINTER_TYPE_P (type))
|
||||
return NULL_TREE;
|
||||
|
||||
type = TREE_TYPE (type);
|
||||
if (TREE_CODE (type) != ARRAY_TYPE)
|
||||
return NULL_TREE;
|
||||
|
||||
tree nbytes = TYPE_SIZE_UNIT (type);
|
||||
if (!nbytes || TREE_CODE (nbytes) != INTEGER_CST)
|
||||
return NULL_TREE;
|
||||
|
||||
*pref = get_base_decl (arg);
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
/* Return true if REF is (likely) an ARRAY_REF to a trailing array member
|
||||
of a struct. It refines array_at_struct_end_p by detecting a pointer
|
||||
to an array and an array parameter declared using the [N] syntax (as
|
||||
opposed to a pointer) and returning false. Set *PREF to the decl or
|
||||
expression REF refers to. */
|
||||
|
||||
static bool
|
||||
trailing_array (tree arg, tree *pref)
|
||||
{
|
||||
tree ref = arg;
|
||||
tree base = get_base_decl (arg);
|
||||
while (TREE_CODE (ref) == ARRAY_REF || TREE_CODE (ref) == MEM_REF)
|
||||
ref = TREE_OPERAND (ref, 0);
|
||||
|
||||
if (TREE_CODE (ref) == COMPONENT_REF)
|
||||
{
|
||||
*pref = TREE_OPERAND (ref, 1);
|
||||
tree type = TREE_TYPE (*pref);
|
||||
if (TREE_CODE (type) == ARRAY_TYPE)
|
||||
{
|
||||
/* A multidimensional trailing array is not considered special
|
||||
no matter what its major bound is. */
|
||||
type = TREE_TYPE (type);
|
||||
if (TREE_CODE (type) == ARRAY_TYPE)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
*pref = base;
|
||||
|
||||
tree basetype = TREE_TYPE (base);
|
||||
if (TREE_CODE (base) == PARM_DECL
|
||||
&& POINTER_TYPE_P (basetype))
|
||||
{
|
||||
tree ptype = TREE_TYPE (basetype);
|
||||
if (TREE_CODE (ptype) == ARRAY_TYPE)
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_at_struct_end_p (arg);
|
||||
}
|
||||
|
||||
/* Checks one ARRAY_REF in REF, located at LOCUS. Ignores flexible
|
||||
arrays and "struct" hacks. If VRP can determine that the array
|
||||
subscript is a constant, check if it is outside valid range. If
|
||||
the array subscript is a RANGE, warn if it is non-overlapping with
|
||||
valid range. IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside
|
||||
a ADDR_EXPR. Returns true if a warning has been issued. */
|
||||
a ADDR_EXPR. Return true if a warning has been issued or if
|
||||
no-warning is set. */
|
||||
|
||||
bool
|
||||
array_bounds_checker::check_array_ref (location_t location, tree ref,
|
||||
bool ignore_off_by_one)
|
||||
{
|
||||
if (TREE_NO_WARNING (ref))
|
||||
return false;
|
||||
/* Return true to have the caller prevent warnings for enclosing
|
||||
refs. */
|
||||
return true;
|
||||
|
||||
tree low_sub = TREE_OPERAND (ref, 1);
|
||||
tree up_sub = low_sub;
|
||||
@ -74,8 +194,7 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
|
||||
|
||||
if (!up_bound
|
||||
|| TREE_CODE (up_bound) != INTEGER_CST
|
||||
|| (warn_array_bounds < 2
|
||||
&& array_at_struct_end_p (ref)))
|
||||
|| (warn_array_bounds < 2 && trailing_array (ref, &decl)))
|
||||
{
|
||||
/* Accesses to trailing arrays via pointers may access storage
|
||||
beyond the types array bounds. For such arrays, or for flexible
|
||||
@ -116,7 +235,14 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
|
||||
poly_int64 off;
|
||||
if (tree base = get_addr_base_and_unit_offset (arg, &off))
|
||||
{
|
||||
if (!compref && DECL_P (base))
|
||||
if (TREE_CODE (base) == MEM_REF)
|
||||
{
|
||||
/* Try to determine the size from a pointer to
|
||||
an array if BASE is one. */
|
||||
if (tree size = get_ref_size (base, &decl))
|
||||
maxbound = size;
|
||||
}
|
||||
else if (!compref && DECL_P (base))
|
||||
if (tree basesize = DECL_SIZE_UNIT (base))
|
||||
if (TREE_CODE (basesize) == INTEGER_CST)
|
||||
{
|
||||
@ -217,7 +343,13 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
|
||||
fprintf (dump_file, "\n");
|
||||
}
|
||||
|
||||
ref = decl ? decl : TREE_OPERAND (ref, 0);
|
||||
/* Avoid more warnings when checking more significant subscripts
|
||||
of the same expression. */
|
||||
ref = TREE_OPERAND (ref, 0);
|
||||
TREE_NO_WARNING (ref) = 1;
|
||||
|
||||
if (decl)
|
||||
ref = decl;
|
||||
|
||||
tree rec = NULL_TREE;
|
||||
if (TREE_CODE (ref) == COMPONENT_REF)
|
||||
@ -235,8 +367,6 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
|
||||
inform (DECL_SOURCE_LOCATION (ref), "while referencing %qD", ref);
|
||||
if (rec && DECL_P (rec))
|
||||
inform (DECL_SOURCE_LOCATION (rec), "defined here %qD", rec);
|
||||
|
||||
TREE_NO_WARNING (ref) = 1;
|
||||
}
|
||||
|
||||
return warned;
|
||||
@ -266,8 +396,8 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
|
||||
|
||||
const offset_int maxobjsize = tree_to_shwi (max_object_size ());
|
||||
|
||||
/* The array or string constant bounds in bytes. Initially set
|
||||
to [-MAXOBJSIZE - 1, MAXOBJSIZE] until a tighter bound is
|
||||
/* 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 };
|
||||
|
||||
@ -275,7 +405,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
|
||||
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 enbaled at -Warray-bounds=2
|
||||
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));
|
||||
@ -284,7 +414,10 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
|
||||
/* The range of the byte offset into the reference. */
|
||||
offset_int offrange[2] = { 0, 0 };
|
||||
|
||||
const value_range *vr = NULL;
|
||||
/* 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;
|
||||
|
||||
/* Determine the offsets and increment OFFRANGE for the bounds of each.
|
||||
The loop computes the range of the final offset for expressions such
|
||||
@ -294,6 +427,35 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
|
||||
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;
|
||||
|
||||
@ -316,7 +478,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
|
||||
if (TREE_CODE (varoff) != SSA_NAME)
|
||||
break;
|
||||
|
||||
vr = get_value_range (varoff);
|
||||
const value_range* const vr = get_value_range (varoff);
|
||||
if (!vr || vr->undefined_p () || vr->varying_p ())
|
||||
break;
|
||||
|
||||
@ -366,79 +528,104 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
|
||||
offrange[1] = arrbounds[1];
|
||||
}
|
||||
|
||||
if (TREE_CODE (arg) == ADDR_EXPR)
|
||||
tree reftype = NULL_TREE;
|
||||
offset_int eltsize = -1;
|
||||
if (arrbounds[0] >= 0)
|
||||
{
|
||||
/* 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;
|
||||
reftype = build_array_type_nelts (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;
|
||||
}
|
||||
else
|
||||
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. */
|
||||
tree reftype = TREE_TYPE (arg);
|
||||
if (POINTER_TYPE_P (reftype)
|
||||
|| !COMPLETE_TYPE_P (reftype)
|
||||
|| TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST)
|
||||
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;
|
||||
/* 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;
|
||||
|
||||
arrbounds[0] = 0;
|
||||
/* FIXME: Should this be 1 for Fortran? */
|
||||
arrbounds[0] = 0;
|
||||
|
||||
offset_int eltsize;
|
||||
if (TREE_CODE (reftype) == ARRAY_TYPE)
|
||||
{
|
||||
eltsize = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (reftype)));
|
||||
if (tree dom = TYPE_DOMAIN (reftype))
|
||||
if (TREE_CODE (reftype) == ARRAY_TYPE)
|
||||
{
|
||||
tree bnds[] = { TYPE_MIN_VALUE (dom), TYPE_MAX_VALUE (dom) };
|
||||
if (TREE_CODE (arg) == COMPONENT_REF)
|
||||
/* 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))
|
||||
{
|
||||
offset_int size = maxobjsize;
|
||||
if (tree fldsize = component_ref_size (arg))
|
||||
size = wi::to_offset (fldsize);
|
||||
arrbounds[1] = wi::lrshift (size, wi::floor_log2 (eltsize));
|
||||
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 if (array_at_struct_end_p (arg) || !bnds[0] || !bnds[1])
|
||||
arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize));
|
||||
else
|
||||
arrbounds[1] = (wi::to_offset (bnds[1]) - wi::to_offset (bnds[0])
|
||||
+ 1) * eltsize;
|
||||
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
|
||||
arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize));
|
||||
{
|
||||
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;
|
||||
|
||||
/* 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));
|
||||
arrbounds[1] = wi::to_offset (size);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
return false;
|
||||
|
||||
offrange[0] += ioff;
|
||||
offrange[1] += ioff;
|
||||
@ -448,11 +635,25 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
|
||||
of an array) but always use the stricter bound in diagnostics. */
|
||||
offset_int ubound = arrbounds[1];
|
||||
if (ignore_off_by_one)
|
||||
ubound += 1;
|
||||
ubound += eltsize;
|
||||
|
||||
if (arrbounds[0] == arrbounds[1]
|
||||
|| offrange[0] >= ubound
|
||||
|| offrange[1] < arrbounds[0])
|
||||
bool warned = false;
|
||||
/* Set if the lower bound of the subscript is out of bounds. */
|
||||
const bool lboob = (arrbounds[0] == arrbounds[1]
|
||||
|| offrange[0] >= ubound
|
||||
|| offrange[1] < arrbounds[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)
|
||||
{
|
||||
/* Treat a reference to a non-array object as one to an array
|
||||
of a single element. */
|
||||
@ -471,8 +672,10 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
|
||||
offrange[0] = offrange[0] / wi::to_offset (size);
|
||||
offrange[1] = offrange[1] / wi::to_offset (size);
|
||||
}
|
||||
}
|
||||
|
||||
bool warned;
|
||||
if (lboob)
|
||||
{
|
||||
if (offrange[0] == offrange[1])
|
||||
warned = warning_at (location, OPT_Warray_bounds,
|
||||
"array subscript %wi is outside array bounds "
|
||||
@ -484,12 +687,66 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
|
||||
"array bounds of %qT",
|
||||
offrange[0].to_shwi (),
|
||||
offrange[1].to_shwi (), reftype);
|
||||
if (warned && DECL_P (arg))
|
||||
inform (DECL_SOURCE_LOCATION (arg), "while referencing %qD", arg);
|
||||
}
|
||||
else if (uboob && !ignore_off_by_one)
|
||||
{
|
||||
tree backtype = reftype;
|
||||
if (alloc_stmt)
|
||||
/* 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 ());
|
||||
|
||||
if (warned)
|
||||
TREE_NO_WARNING (ref) = 1;
|
||||
return warned;
|
||||
warned = warning_at (location, OPT_Warray_bounds,
|
||||
"array subscript %<%T[%wi]%> is partly "
|
||||
"outside array bounds of %qT",
|
||||
axstype, offrange[0].to_shwi (), backtype);
|
||||
}
|
||||
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (warn_array_bounds < 2)
|
||||
|
@ -2,7 +2,7 @@
|
||||
large index
|
||||
{ dg-do compile }
|
||||
{ dg-require-effective-target alloca }
|
||||
{ dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" } */
|
||||
{ dg-options "-O2 -Warray-bounds -Wno-stringop-overread -ftrack-macro-expansion=0" } */
|
||||
|
||||
#include "../gcc.dg/range.h"
|
||||
|
||||
|
53
gcc/testsuite/gcc.dg/Warray-bounds-63.c
Normal file
53
gcc/testsuite/gcc.dg/Warray-bounds-63.c
Normal file
@ -0,0 +1,53 @@
|
||||
/* PR middle-end/94195 - missing warning reading a smaller object via
|
||||
an lvalue of a larger type
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
typedef __INT16_TYPE__ int16_t;
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
void* alloca (size_t);
|
||||
|
||||
void sink (void*);
|
||||
|
||||
|
||||
void byte_store_to_decl (void)
|
||||
{
|
||||
struct S6 { char a[6]; } s; // { dg-message "referencing 's'" }
|
||||
|
||||
char *p = (char*)&s;
|
||||
|
||||
p[0] = 0; p[1] = 1; p[2] = 2; p[3] = 3; p[4] = 4; p[5] = 5;
|
||||
p[6] = 6; // { dg-warning "array subscript 6 is outside array bounds of 'struct S6\\\[1]" }
|
||||
|
||||
sink (&s);
|
||||
}
|
||||
|
||||
|
||||
void word_store_to_decl (void)
|
||||
{
|
||||
struct S6 { char a[6]; } s; // { dg-message "referencing 's'" }
|
||||
|
||||
char *p = (char*)&s;
|
||||
|
||||
int16_t *q = (int16_t*)(p + 1);
|
||||
|
||||
q[0] = 0; q[1] = 1;
|
||||
q[2] = 2; // { dg-warning "array subscript 'int16_t {aka short int}\\\[2]' is partly outside array bounds of 'struct S6\\\[1]'" }
|
||||
|
||||
sink (&s);
|
||||
}
|
||||
|
||||
|
||||
void word_store_to_alloc (void)
|
||||
{
|
||||
struct S6 { char a[6]; } *p;
|
||||
p = alloca (sizeof *p); // { dg-message "referencing an object of size 6 allocated by 'alloca'" }
|
||||
|
||||
int16_t *q = (int16_t*)((char*)p + 1);
|
||||
|
||||
q[0] = 0; q[1] = 1;
|
||||
q[2] = 2; // { dg-warning "array subscript 'int16_t {aka short int}\\\[2]' is partly outside array bounds of 'unsigned char\\\[6]'" }
|
||||
|
||||
sink (p);
|
||||
}
|
60
gcc/testsuite/gcc.dg/Warray-bounds-64.c
Normal file
60
gcc/testsuite/gcc.dg/Warray-bounds-64.c
Normal file
@ -0,0 +1,60 @@
|
||||
/* PR c/50584 - No warning for passing small array to C99 static array
|
||||
declarator
|
||||
|
||||
Verify that out-of-bounds accesses to array arguments are diagnosed,
|
||||
both to ordinary array parameters with constant bounds and to array
|
||||
parameters declared static. This is the converse of what PR 50584
|
||||
asks for.
|
||||
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Warray-parameter -Wno-vla-paramater" } */
|
||||
|
||||
#define NOIPA __attribute__ ((noipa))
|
||||
|
||||
void sink (void*, ...);
|
||||
|
||||
#define T(...) sink (0, __VA_ARGS__)
|
||||
|
||||
|
||||
NOIPA void fca1 (char a[1])
|
||||
{
|
||||
T (a[0]);
|
||||
T (a[1]); // { dg-warning "-Warray-bounds" }
|
||||
}
|
||||
|
||||
NOIPA void fcas1 (char a[static 1])
|
||||
{
|
||||
T (a[0]);
|
||||
T (a[1]); // { dg-warning "-Warray-bounds" }
|
||||
}
|
||||
|
||||
NOIPA void fca2 (char a[2])
|
||||
{
|
||||
T (a[0]); T (a[1]);
|
||||
T (a[2]); // { dg-warning "-Warray-bounds" }
|
||||
}
|
||||
|
||||
NOIPA void fcas2 (char a[static 2])
|
||||
{
|
||||
T (a[0]); T (a[1]);
|
||||
T (a[2]); // { dg-warning "-Warray-bounds" }
|
||||
}
|
||||
|
||||
NOIPA void fca3 (char a[3])
|
||||
{
|
||||
T (a[0]); T (a[1]); T (a[2]);
|
||||
T (a[3]); // { dg-warning "-Warray-bounds" }
|
||||
}
|
||||
|
||||
NOIPA void fcas3 (char a[static 3])
|
||||
{
|
||||
T (a[0]); T (a[1]); T (a[2]);
|
||||
T (a[3]); // { dg-warning "-Warray-bounds" }
|
||||
}
|
||||
|
||||
|
||||
NOIPA void fca1_1 (char a[1][1])
|
||||
{
|
||||
T (a[0][0]);
|
||||
T (a[0][1]); // { dg-warning "-Warray-bounds" }
|
||||
}
|
202
gcc/testsuite/gcc.dg/Warray-bounds-65.c
Normal file
202
gcc/testsuite/gcc.dg/Warray-bounds-65.c
Normal file
@ -0,0 +1,202 @@
|
||||
/* PR middle-end/84051 - missing -Warray-bounds on an out-of-bounds access
|
||||
via an array pointer
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
|
||||
|
||||
void sink (void*, ...);
|
||||
#define T(x) sink (0, x)
|
||||
|
||||
void
|
||||
test_note (int (*pia3)[3]) // { dg-message "while referencing 'pia3'" }
|
||||
{
|
||||
int i = 0;
|
||||
T ((*pia3)[i++]);
|
||||
T ((*pia3)[i++]);
|
||||
T ((*pia3)[i++]);
|
||||
T ((*pia3)[i++]); // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]'" }
|
||||
T ((*pia3)[i++]); // { dg-warning "array subscript 4 is (above|outside) array bounds of 'int\\\[3]'" }
|
||||
|
||||
{
|
||||
/* Regrettably, the following isn't diagnosed because it's represented
|
||||
the same as the possibly valid access below:
|
||||
MEM[(int *)a_1(D) + 36B] = 0; */
|
||||
int *p0 = pia3[0];
|
||||
T (p0[3]); // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]'" "pr?????" { xfail *-*-* } }
|
||||
|
||||
int *p1 = pia3[3];
|
||||
T (p1[0]); // okay
|
||||
}
|
||||
}
|
||||
|
||||
void test_a1_cst (_Bool (*pba0)[0], char (*pca1)[1],
|
||||
short (*psa2)[2], int (*pia3)[3])
|
||||
{
|
||||
T ((*pba0)[-1]); // { dg-warning "array subscript -1 is (above|outside) array bounds of '_Bool\\\[0]'" }
|
||||
T ((*pba0)[0]); // { dg-warning "array subscript 0 is (above|outside) array bounds of '_Bool\\\[0]'" }
|
||||
T ((*pba0)[1]); // { dg-warning "array subscript 1 is (above|outside) array bounds of '_Bool\\\[0]'" }
|
||||
T ((*pba0)[2]); // { dg-warning "array subscript 2 is (above|outside) array bounds of '_Bool\\\[0]'" }
|
||||
T ((*pba0)[12]); // { dg-warning "array subscript 12 is (above|outside) array bounds of '_Bool\\\[0]'" }
|
||||
|
||||
T ((*pca1)[-1]); // { dg-warning "array subscript -1 is (below|outside) array bounds of 'char\\\[1]'" }
|
||||
T ((*pca1)[0]);
|
||||
T ((*pca1)[1]); // { dg-warning "array subscript 1 is (above|outside) array bounds of 'char\\\[1]'" }
|
||||
T ((*pca1)[2]); // { dg-warning "array subscript 2 is (above|outside) array bounds of 'char\\\[1]'" }
|
||||
T ((*pca1)[123]); // { dg-warning "array subscript 123 is (above|outside) array bounds of 'char\\\[1]'" }
|
||||
|
||||
T ((*psa2)[-1]); // { dg-warning "array subscript -1 is (below|outside) array bounds of 'short int\\\[2]'" }
|
||||
T ((*psa2)[0]);
|
||||
T ((*psa2)[1]);
|
||||
T ((*psa2)[2]); // { dg-warning "array subscript 2 is (above|outside) array bounds of 'short int\\\[2]'" }
|
||||
T ((*psa2)[1234]); // { dg-warning "array subscript 1234 is (above|outside) array bounds of 'short int\\\[2]'" }
|
||||
|
||||
T ((*pia3)[-1]); // { dg-warning "array subscript -1 is (below|outside) array bounds of 'int\\\[3]'" }
|
||||
T ((*pia3)[0]);
|
||||
T ((*pia3)[1]);
|
||||
T ((*pia3)[2]);
|
||||
T ((*pia3)[3]); // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]'" }
|
||||
T ((*pia3)[12345]); // { dg-warning "array subscript 12345 is (above|outside) array bounds of 'int\\\[3]'" }
|
||||
}
|
||||
|
||||
|
||||
void test_a2_cst (_Bool (*pba0_1)[0][1], char (*pca1_2)[1][2],
|
||||
short (*psa2_3)[2][3], int (*pia3_4)[3][4])
|
||||
{
|
||||
T ((*pba0_1)[-1][-1]); // { dg-warning "array subscript -1 is (below|outside) array bounds of '_Bool\\\[1]'" }
|
||||
T ((*pba0_1)[-1][0]); // { dg-warning "array subscript -1 is (above|outside) array bounds of '_Bool\\\[0]\\\[1]'" }
|
||||
|
||||
T ((*pba0_1)[0][-1]); // { dg-warning "array subscript -1 is (below|outside) array bounds of '_Bool\\\[1]'" }
|
||||
T ((*pba0_1)[0][0]); // { dg-warning "array subscript 0 is (above|outside) array bounds of '_Bool\\\[0]\\\[1]'" }
|
||||
T ((*pba0_1)[0][1]); // { dg-warning "array subscript 1 is (above|outside) array bounds of '_Bool\\\[1]'" }
|
||||
T ((*pba0_1)[0][2]); // { dg-warning "array subscript 2 is (above|outside) array bounds of '_Bool\\\[1]'" }
|
||||
T ((*pba0_1)[0][12]); // { dg-warning "array subscript 12 is (above|outside) array bounds of '_Bool\\\[1]'" }
|
||||
|
||||
T ((*pba0_1)[1][-1]); // { dg-warning "array subscript -1 is (below|outside) array bounds of '_Bool\\\[1]'" }
|
||||
T ((*pba0_1)[1][0]); // { dg-warning "array subscript 1 is (above|outside) array bounds of '_Bool\\\[0]\\\[1]'" }
|
||||
T ((*pba0_1)[1][1]); // { dg-warning "array subscript 1 is (above|outside) array bounds of '_Bool\\\[1]'" }
|
||||
T ((*pba0_1)[1][2]); // { dg-warning "array subscript 2 is (above|outside) array bounds of '_Bool\\\[1]'" }
|
||||
T ((*pba0_1)[1][12]); // { dg-warning "array subscript 12 is (above|outside) array bounds of '_Bool\\\[1]'" }
|
||||
|
||||
|
||||
T ((*pca1_2)[0][0]);
|
||||
T ((*pca1_2)[0][1]);
|
||||
T ((*pca1_2)[0][2]); // { dg-warning "array subscript 2 is (above|outside) array bounds of 'char\\\[2]'" }
|
||||
|
||||
T ((*pca1_2)[1][0]); // { dg-warning "array subscript 1 is (above|outside) array bounds of 'char\\\[1]\\\[2]'" }
|
||||
T ((*pca1_2)[1][1]); // { dg-warning "array subscript 1 is (above|outside) array bounds of 'char\\\[1]\\\[2]'" }
|
||||
T ((*pca1_2)[1][2]); // { dg-warning "array subscript 2 is (above|outside) array bounds of 'char\\\[2]'" }
|
||||
|
||||
|
||||
T ((*psa2_3)[0][0]);
|
||||
T ((*psa2_3)[0][1]);
|
||||
T ((*psa2_3)[0][2]);
|
||||
T ((*psa2_3)[0][3]); // { dg-warning "array subscript 3 is (above|outside) array bounds of 'short int\\\[3]'" }
|
||||
|
||||
T ((*psa2_3)[1][0]);
|
||||
T ((*psa2_3)[1][1]);
|
||||
T ((*psa2_3)[1][2]);
|
||||
T ((*psa2_3)[1][3]); // { dg-warning "array subscript 3 is (above|outside) array bounds of 'short int\\\[3]'" }
|
||||
|
||||
T ((*psa2_3)[2][0]); // { dg-warning "array subscript 2 is (above|outside) array bounds of 'short int\\\[2]\\\[3]'" }
|
||||
T ((*psa2_3)[2][1]); // { dg-warning "array subscript 2 is (above|outside) array bounds of 'short int\\\[2]\\\[3]'" }
|
||||
T ((*psa2_3)[2][2]); // { dg-warning "array subscript 2 is (above|outside) array bounds of 'short int\\\[2]\\\[3]'" }
|
||||
T ((*psa2_3)[2][3]); // { dg-warning "array subscript 3 is (above|outside) array bounds of 'short int\\\[3]'" }
|
||||
|
||||
|
||||
T ((*pia3_4)[0][0]);
|
||||
T ((*pia3_4)[0][1]);
|
||||
T ((*pia3_4)[0][2]);
|
||||
T ((*pia3_4)[0][3]);
|
||||
T ((*pia3_4)[0][4]); // { dg-warning "array subscript 4 is (above|outside) array bounds of 'int\\\[4]'" }
|
||||
|
||||
T ((*pia3_4)[1][0]);
|
||||
T ((*pia3_4)[1][1]);
|
||||
T ((*pia3_4)[1][2]);
|
||||
T ((*pia3_4)[1][3]);
|
||||
T ((*pia3_4)[1][4]); // { dg-warning "array subscript 4 is (above|outside) array bounds of 'int\\\[4]'" }
|
||||
|
||||
T ((*pia3_4)[2][0]);
|
||||
T ((*pia3_4)[2][1]);
|
||||
T ((*pia3_4)[2][2]);
|
||||
T ((*pia3_4)[2][3]);
|
||||
T ((*pia3_4)[2][4]); // { dg-warning "array subscript 4 is (above|outside) array bounds of 'int\\\[4]'" }
|
||||
|
||||
T ((*pia3_4)[3][0]); // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]\\\[4]'" }
|
||||
T ((*pia3_4)[3][1]); // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]\\\[4]'" }
|
||||
T ((*pia3_4)[3][2]); // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]\\\[4]'" }
|
||||
T ((*pia3_4)[3][3]); // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]\\\[4]'" }
|
||||
T ((*pia3_4)[3][4]); // { dg-warning "array subscript 4 is (above|outside) array bounds of 'int\\\[4]'" }
|
||||
}
|
||||
|
||||
|
||||
typedef int IA4[4];
|
||||
typedef IA4 IA3_4[3];
|
||||
|
||||
void test_a2_var (IA3_4 *pia3_4)
|
||||
{
|
||||
{
|
||||
IA4 *pia4 = &(*pia3_4)[0];
|
||||
|
||||
T ((*pia4)[-1]); // { dg-warning "array subscript -1 is (below|outside) array bounds of 'IA4'" }
|
||||
T ((*pia4)[0]);
|
||||
T ((*pia4)[1]);
|
||||
T ((*pia4)[2]);
|
||||
T ((*pia4)[3]);
|
||||
T ((*pia4)[4]); // { dg-warning "array subscript 4 is (above|outside) array bounds of 'IA4'" }
|
||||
}
|
||||
|
||||
{
|
||||
IA4 *pia4 = &(*pia3_4)[1];
|
||||
|
||||
T ((*pia4)[-1]); // { dg-warning "array subscript -1 is (below|outside) array bounds of 'IA4'" }
|
||||
T ((*pia4)[0]);
|
||||
T ((*pia4)[1]);
|
||||
T ((*pia4)[2]);
|
||||
T ((*pia4)[3]);
|
||||
T ((*pia4)[4]); // { dg-warning "array subscript 4 is (above|outside) array bounds of 'IA4'" }
|
||||
}
|
||||
|
||||
{
|
||||
IA4 *pia4 = &(*pia3_4)[2];
|
||||
|
||||
T ((*pia4)[-1]); // { dg-warning "array subscript -1 is (below|outside) array bounds of 'IA4'" }
|
||||
T ((*pia4)[0]);
|
||||
T ((*pia4)[1]);
|
||||
T ((*pia4)[2]);
|
||||
T ((*pia4)[3]);
|
||||
T ((*pia4)[4]); // { dg-warning "array subscript 4 is (above|outside) array bounds of 'IA4'" }
|
||||
}
|
||||
|
||||
{
|
||||
IA4 *pia4 = &(*pia3_4)[3];
|
||||
|
||||
T ((*pia4)[-1]); // { dg-warning "\\\[-Warray-bounds" }
|
||||
/* The following aren't diagnosed unless N itself is out of bounds
|
||||
because thanks to the MEM_REF they're indistinguishable from
|
||||
possibly valid accesses:
|
||||
MEM[(int[4] *)pia3_4_2(D) + 48B][N]; */
|
||||
T ((*pia4)[0]); // { dg-warning "\\\[-Warray-bounds" "pr?????" { xfail *-*-* } }
|
||||
T ((*pia4)[1]); // { dg-warning "\\\[-Warray-bounds" "pr?????" { xfail *-*-* } }
|
||||
T ((*pia4)[2]); // { dg-warning "\\\[-Warray-bounds" "pr?????" { xfail *-*-* } }
|
||||
T ((*pia4)[3]); // { dg-warning "\\\[-Warray-bounds" "pr?????" { xfail *-*-* } }
|
||||
T ((*pia4)[4]); // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct S { IA3_4 *pia3_4; };
|
||||
typedef struct S S5[5];
|
||||
typedef S5 S5_7[7];
|
||||
|
||||
void test_s5_7 (S5_7 *ps5_7)
|
||||
{
|
||||
{
|
||||
S5 *ps5 = &(*ps5_7)[0];
|
||||
T ((*ps5)[0]);
|
||||
T ((*(*ps5)[0].pia3_4)[0][0]);
|
||||
T ((*(*ps5)[0].pia3_4)[2][3]);
|
||||
T ((*(*ps5)[0].pia3_4)[2][4]); // { dg-warning "array subscript 4 is above array bounds of 'IA4'" }
|
||||
|
||||
T ((*(*ps5)[1].pia3_4)[2][3]);
|
||||
T ((*(*ps5)[5].pia3_4)[2][3]); // { dg-warning "array subscript 5 is above array bounds of 'S5'" }
|
||||
}
|
||||
}
|
256
gcc/testsuite/gcc.dg/Warray-bounds-66.c
Normal file
256
gcc/testsuite/gcc.dg/Warray-bounds-66.c
Normal file
@ -0,0 +1,256 @@
|
||||
/* PR middle-end/82608 - missing -Warray-bounds on an out-of-bounds VLA index
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wno-uninitialized -ftrack-macro-expansion=0" } */
|
||||
|
||||
#include "range.h"
|
||||
|
||||
typedef __INT16_TYPE__ int16_t;
|
||||
|
||||
#define alloca(n) __builtin_alloca (n)
|
||||
|
||||
void* calloc (size_t, size_t);
|
||||
void* malloc (size_t);
|
||||
|
||||
void sink (void*, ...);
|
||||
#define sink(...) sink (0, __VA_ARGS__)
|
||||
|
||||
#define T(x) (sink (x))
|
||||
|
||||
__attribute__ ((alloc_size (1))) void* alloc (size_t);
|
||||
|
||||
|
||||
void test_alloca_cst (void)
|
||||
{
|
||||
{
|
||||
char *p = alloca (1);
|
||||
sink (p);
|
||||
T (p[0]);
|
||||
T (p[1]); // { dg-warning "subscript 1 is outside array bounds of 'char\\\[1\\\]'" }
|
||||
}
|
||||
|
||||
{
|
||||
char *p = alloca (2);
|
||||
sink (p);
|
||||
T (p[0]), T (p[1]);
|
||||
T (p[2]); // { dg-warning "subscript 2 is outside array bounds of 'char\\\[2\\\]'" }
|
||||
}
|
||||
|
||||
{
|
||||
char *p = alloca (3);
|
||||
sink (p);
|
||||
T (p[0]), T (p[1]), T (p[2]);
|
||||
T (p[3]); // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test_alloca_char_range (int i, unsigned n, size_t sz)
|
||||
{
|
||||
{
|
||||
// Be sure to exercise signed as well as unsigned arguments.
|
||||
char *p = alloca (i);
|
||||
sink (p);
|
||||
T (p[0]), T (p[1]), T (p[12345]);
|
||||
T (p[-1]); // { dg-warning "subscript -1 is outside array bounds of 'char\\\[" }
|
||||
}
|
||||
|
||||
{
|
||||
char *p = alloca (n);
|
||||
sink (p);
|
||||
T (p[0]), T (p[1]), T (p[12345]);
|
||||
T (p[-1]); // { dg-warning "subscript -1 is outside array bounds of 'char\\\[" }
|
||||
}
|
||||
|
||||
{
|
||||
char *p = alloca (sz);
|
||||
sink (p);
|
||||
T (p[0]), T (p[1]), T (p[23456]);
|
||||
T (p[-1]); // { dg-warning "subscript -1 is outside array bounds of 'char\\\[" }
|
||||
}
|
||||
|
||||
{
|
||||
char *p = alloca (UR (0, 1));
|
||||
sink (p);
|
||||
T (p[0]);
|
||||
T (p[1]); // { dg-warning "subscript 1 is outside array bounds of 'char\\\[1\\\]'" }
|
||||
}
|
||||
|
||||
{
|
||||
char *p = alloca (UR (0, 2));
|
||||
sink (p);
|
||||
sink (p[0], p[1]);
|
||||
sink (p[2]); // { dg-warning "subscript 2 is outside array bounds of 'char\\\[2\\\]'" }
|
||||
}
|
||||
|
||||
{
|
||||
char *p = alloca (UR (0, 3));
|
||||
sink (p);
|
||||
T (p[0]), T (p[1]), T (p[2]);
|
||||
T (p[3]); // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" }
|
||||
}
|
||||
|
||||
{
|
||||
char *p = alloca (UR (1, 3));
|
||||
sink (p);
|
||||
T (p[0]), T (p[1]), T (p[2]);
|
||||
T (p[3]); // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" }
|
||||
}
|
||||
|
||||
{
|
||||
char *p = alloca (UR (2, 3));
|
||||
sink (p);
|
||||
T (p[0]), T (p[1]), T (p[2]);
|
||||
T (p[3]); // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test_alloca_int16_range (unsigned n)
|
||||
{
|
||||
int16_t *p;
|
||||
{
|
||||
p = alloca (n); // { dg-message "allocated by " }
|
||||
sink (p);
|
||||
T (p[0]), T (p[1]), T (p[12345]);
|
||||
T (p[-1]); // { dg-warning "subscript -1 is outside array bounds of 'int16_t\\\[" }
|
||||
}
|
||||
|
||||
{
|
||||
p = alloca (UR (0, 1)); // { dg-message "object of size between 0 and 1 allocated by '__builtin_alloca'" }
|
||||
sink (p);
|
||||
T (p[0]); // { dg-warning "subscript 'int16_t {aka short int}\\\[0\\\]' is partly outside array bounds of 'unsigned char\\\[1]'" }
|
||||
T (p[1]); // { dg-warning "subscript 1 is outside array bounds of 'int16_t\\\[0]'" }
|
||||
}
|
||||
|
||||
{
|
||||
p = alloca (UR (0, 2)); // { dg-message "object of size between 0 and 2 allocated by '__builtin_alloca'" }
|
||||
sink (p);
|
||||
sink (p[0]);
|
||||
sink (p[1]); // { dg-warning "subscript 1 is outside array bounds of 'int16_t\\\[1]'" }
|
||||
sink (p[2]); // { dg-warning "subscript 2 is outside array bounds of 'int16_t\\\[1\\\]'" }
|
||||
}
|
||||
|
||||
{
|
||||
p = alloca (UR (0, 3)); // { dg-message "object of size between 0 and 3 allocated by '__builtin_alloca'" }
|
||||
sink (p);
|
||||
T (p[0]);
|
||||
T (p[1]); // { dg-warning "subscript 'int16_t {aka short int}\\\[1\\\]' is partly outside array bounds of 'unsigned char\\\[3]'" }
|
||||
T (p[2]); // { dg-warning "subscript 2 is outside array bounds of 'int16_t\\\[1\\\]'" }
|
||||
T (p[3]); // { dg-warning "subscript 3 is outside array bounds of 'int16_t\\\[1\\\]'" }
|
||||
}
|
||||
|
||||
{
|
||||
p = alloca (UR (1, 3)); // { dg-message "object of size between 1 and 3 allocated by '__builtin_alloca'" }
|
||||
sink (p);
|
||||
T (p[0]);
|
||||
T (p[1]); // { dg-warning "subscript 'int16_t {aka short int}\\\[1\\\]' is partly outside array bounds of 'unsigned char\\\[3]'" }
|
||||
T (p[2]); // { dg-warning "subscript 2 is outside array bounds of 'int16_t\\\[1\\\]'" }
|
||||
T (p[3]); // { dg-warning "subscript 3 is outside array bounds of 'int16_t\\\[1\\\]'" }
|
||||
}
|
||||
|
||||
{
|
||||
p = alloca (UR (2, 3)); // { dg-message "object of size between 2 and 3 allocated by '__builtin_alloca'" }
|
||||
sink (p);
|
||||
T (p[0]);
|
||||
T (p[1]); // { dg-warning "subscript 'int16_t {aka short int}\\\[1\\\]' is partly outside array bounds of 'unsigned char\\\[3]'" }
|
||||
T (p[2]); // { dg-warning "subscript 2 is outside array bounds of 'int16_t\\\[1\\\]'" }
|
||||
T (p[3]); // { dg-warning "subscript 3 is outside array bounds of 'int16_t\\\[1\\\]'" }
|
||||
}
|
||||
|
||||
{
|
||||
p = alloca (UR (3, 4)); // { dg-message "object of size between 3 and 4 allocated by '__builtin_alloca'" }
|
||||
sink (p);
|
||||
T (p[0]);
|
||||
T (p[1]);
|
||||
T (p[2]); // { dg-warning "subscript 2 is outside array bounds of 'int16_t\\\[2\\\]'" }
|
||||
T (p[3]); // { dg-warning "subscript 3 is outside array bounds of 'int16_t\\\[2\\\]'" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test_vla_cst (void)
|
||||
{
|
||||
int n = 1;
|
||||
{
|
||||
char a[n];
|
||||
sink (a);
|
||||
T (a[0]);
|
||||
T (a[1]); // { dg-warning "subscript 1 is (above|outside) array bounds " }
|
||||
}
|
||||
|
||||
{
|
||||
n = 2;
|
||||
char a[n];
|
||||
sink (a);
|
||||
T (a[0]), T (a[1]);
|
||||
T (a[2]); // { dg-warning "subscript 2 is (above|outside) array bounds " }
|
||||
}
|
||||
|
||||
{
|
||||
n = 3;
|
||||
char a[n], *p = a;
|
||||
sink (p);
|
||||
T (p[0]), T (p[1]), T (p[2]);
|
||||
T (p[3]); // { dg-warning "subscript 3 is (above|outside) array bounds " }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test_vla_char_range (int i, unsigned n, size_t sz)
|
||||
{
|
||||
{
|
||||
char a[i];
|
||||
sink (a);
|
||||
T (a[0]), T (a[1]), T (a[12345]);
|
||||
T (a[-1]); // { dg-warning "subscript -1 is (below|outside) array bounds of 'char\\\[" }
|
||||
}
|
||||
|
||||
{
|
||||
char a[n];
|
||||
sink (a);
|
||||
T (a[0]), T (a[1]), T (a[12345]);
|
||||
T (a[-1]); // { dg-warning "subscript -1 is (below|outside) array bounds of 'char\\\[" }
|
||||
}
|
||||
|
||||
{
|
||||
char a[sz];
|
||||
sink (a);
|
||||
T (a[0]), T (a[1]), T (a[23456]);
|
||||
T (a[-1]); // { dg-warning "subscript -1 is (below|outside) array bounds of 'char\\\[" }
|
||||
}
|
||||
|
||||
{
|
||||
char a[UR (0, 1)];
|
||||
sink (a);
|
||||
T (a[0]);
|
||||
T (a[1]); // { dg-warning "subscript 1 is outside array bounds of 'char\\\[1\\\]'" "pr82608" { xfail *-*-* } }
|
||||
}
|
||||
|
||||
{
|
||||
char a[UR (0, 2)];
|
||||
sink (a);
|
||||
sink (a[0], a[1]);
|
||||
sink (a[2]); // { dg-warning "subscript 2 is outside array bounds of 'char\\\[2\\\]'" "pr82608" { xfail *-*-* } }
|
||||
}
|
||||
|
||||
{
|
||||
char a[UR (0, 3)];
|
||||
sink (a);
|
||||
T (a[0]), T (a[1]), T (a[2]);
|
||||
T (a[3]); // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" "pr82608" { xfail *-*-* } }
|
||||
}
|
||||
|
||||
{
|
||||
char a[UR (1, 3)];
|
||||
sink (a);
|
||||
T (a[0]), T (a[1]), T (a[2]);
|
||||
T (a[3]); // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" "pr82608" { xfail *-*-* } }
|
||||
}
|
||||
|
||||
{
|
||||
char a[UR (2, 3)];
|
||||
sink (a);
|
||||
T (a[0]), T (a[1]), T (a[2]);
|
||||
T (a[3]); // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" "pr82608" { xfail *-*-* } }
|
||||
}
|
||||
}
|
36
gcc/testsuite/gcc.dg/Warray-bounds-67.c
Normal file
36
gcc/testsuite/gcc.dg/Warray-bounds-67.c
Normal file
@ -0,0 +1,36 @@
|
||||
/* Verify warnings fpr accesses to trailing one-element array members
|
||||
of a struct that's a member of either a struct or a union. Both
|
||||
are obviously undefined but GCC relies on these hacks so the test
|
||||
verifies that -Warray-bounds doesn't trigger for it.
|
||||
{ do-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
|
||||
typedef union tree_node *tree;
|
||||
|
||||
struct tree_exp { int i; tree operands[1]; };
|
||||
|
||||
union tree_node
|
||||
{
|
||||
struct tree_exp exp;
|
||||
};
|
||||
|
||||
tree test_nowarn (tree t)
|
||||
{
|
||||
return t->exp.operands[3]; // { dg-bogus "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
|
||||
typedef struct shrub_node *shrub;
|
||||
|
||||
struct shrub_exp { int i; shrub operands[1]; };
|
||||
|
||||
struct shrub_node
|
||||
{
|
||||
struct shrub_exp exp;
|
||||
};
|
||||
|
||||
shrub test_warn (shrub s)
|
||||
{
|
||||
return s->exp.operands[3]; // { dg-warning "\\\[-Warray-bounds" "pr96346" { xfail *-*-* } }
|
||||
}
|
@ -11,5 +11,5 @@ void a (void)
|
||||
);
|
||||
}
|
||||
|
||||
/* The scanf call may also trigger:
|
||||
{ dg-prune-output "-Wstringop-overflow" } */
|
||||
/* The invalid scanf call may also trigger:
|
||||
{ dg-prune-output "accessing 4 bytes in a region of size 1" } */
|
||||
|
Loading…
Reference in New Issue
Block a user