PR middle-end/95353 - spurious -Wstringop-overflow writing to a trailing array plus offset

Also resolves:
PR middle-end/92939 - missing -Wstringop-overflow on negative index from the end of array

gcc/ChangeLog:

	PR middle-end/95353
	PR middle-end/92939
	* builtins.c (inform_access): New function.
	(check_access): Call it.  Add argument.
	(addr_decl_size): Remove.
	(get_range): New function.
	(compute_objsize): New overload.  Only use compute_builtin_object_size
	with raw memory function.
	(check_memop_access): Pass new argument to compute_objsize and
	check_access.
	(expand_builtin_memchr, expand_builtin_strcat): Same.
	(expand_builtin_strcpy, expand_builtin_stpcpy_1): Same.
	(expand_builtin_stpncpy, check_strncat_sizes): Same.
	(expand_builtin_strncat, expand_builtin_strncpy): Same.
	(expand_builtin_memcmp): Same.
	* builtins.h (check_nul_terminated_array): Declare extern.
	(check_access): Add argument.
	(struct access_ref, struct access_data): New structs.
	* gimple-ssa-warn-restrict.c (clamp_offset): New helper.
	(builtin_access::overlap): Call it.
	* tree-object-size.c (decl_init_size): Declare extern.
	(addr_object_size): Correct offset computation.
	* tree-object-size.h (decl_init_size): Declare.
	* tree-ssa-strlen.c (handle_integral_assign): Remove a call
	to maybe_warn_overflow when assigning to an SSA_NAME.

gcc/testsuite/ChangeLog:

	PR middle-end/95353
	PR middle-end/92939
	* c-c++-common/Wstringop-truncation.c: Remove an xfail.
	* gcc.dg/Warray-bounds-46.c: Remove a bogus warning.
	* gcc.dg/Wrestrict-9.c: Disable -Wstringop-overflow.
	* gcc.dg/Wstringop-overflow-12.c: Remove xfails.
	* gcc.dg/Wstringop-overflow-28.c: Same.
	* gcc.dg/builtin-stringop-chk-4.c: Same.
	* gcc.dg/builtin-stringop-chk-5.c: Same.
	* gcc.dg/builtin-stringop-chk-8.c: Same.
	* gcc.dg/strlenopt-74.c: Avoid buffer overflow.
	* gcc.dg/Wstringop-overflow-34.c: New test.
	* gcc.dg/Wstringop-overflow-35.c: New test.
	* gcc.dg/Wstringop-overflow-36.c: New test.
	* gcc.dg/Wstringop-overflow-37.c: New test.
	* gcc.dg/Wstringop-overflow-38.c: New test.
This commit is contained in:
Martin Sebor 2020-06-10 12:00:08 -06:00
parent 3a391adf7a
commit a2c2cee92e
20 changed files with 1204 additions and 377 deletions

View file

@ -3394,6 +3394,130 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
return warned;
}
/* Issue an inform message describing the target of an access REF.
WRITE is set for a write access and clear for a read access. */
static void
inform_access (const access_ref &ref, bool write)
{
if (!ref.ref)
return;
/* Convert offset range and avoid including a zero range since it isn't
necessarily meaningful. */
long long minoff = 0, maxoff = 0;
if (wi::fits_shwi_p (ref.offrng[0])
&& wi::fits_shwi_p (ref.offrng[1]))
{
minoff = ref.offrng[0].to_shwi ();
maxoff = ref.offrng[1].to_shwi ();
}
/* Convert size range and always include it since all sizes are
meaningful. */
unsigned long long minsize = 0, maxsize = 0;
if (wi::fits_shwi_p (ref.sizrng[0])
&& wi::fits_shwi_p (ref.sizrng[1]))
{
minsize = ref.sizrng[0].to_shwi ();
maxsize = ref.sizrng[1].to_shwi ();
}
char sizestr[80];
location_t loc;
tree allocfn = NULL_TREE;
if (TREE_CODE (ref.ref) == SSA_NAME)
{
gimple *stmt = SSA_NAME_DEF_STMT (ref.ref);
gcc_assert (is_gimple_call (stmt));
loc = gimple_location (stmt);
allocfn = gimple_call_fndecl (stmt);
if (!allocfn)
/* Handle calls through pointers to functions. */
allocfn = gimple_call_fn (stmt);
/* SIZRNG doesn't necessarily have the same range as the allocation
size determined by gimple_call_alloc_size (). */
if (minsize == maxsize)
sprintf (sizestr, "%llu", minsize);
else
sprintf (sizestr, "[%llu, %llu]", minsize, maxsize);
}
else
loc = DECL_SOURCE_LOCATION (ref.ref);
if (write)
{
if (DECL_P (ref.ref))
{
if (minoff == maxoff)
{
if (minoff == 0)
inform (loc, "destination object %qD", ref.ref);
else
inform (loc, "at offset %lli into destination object %qD",
minoff, ref.ref);
}
else
inform (loc, "at offset [%lli, %lli] into destination object %qD",
minoff, maxoff, ref.ref);
return;
}
if (minoff == maxoff)
{
if (minoff == 0)
inform (loc, "destination object of size %s allocated by %qE",
sizestr, allocfn);
else
inform (loc,
"at offset %lli into destination object of size %s "
"allocated by %qE", minoff, sizestr, allocfn);
}
else
inform (loc,
"at offset [%lli, %lli] into destination object of size %s "
"allocated by %qE",
minoff, maxoff, sizestr, allocfn);
return;
}
if (DECL_P (ref.ref))
{
if (minoff == maxoff)
{
if (minoff == 0)
inform (loc, "source object %qD", ref.ref);
else
inform (loc, "at offset %lli into source object %qD",
minoff, ref.ref);
}
else
inform (loc, "at offset [%lli, %lli] into source object %qD",
minoff, maxoff, ref.ref);
return;
}
if (minoff == maxoff)
{
if (minoff == 0)
inform (loc, "source object of size %s allocated by %qE",
sizestr, allocfn);
else
inform (loc,
"at offset %lli into source object of size %s "
"allocated by %qE", minoff, sizestr, allocfn);
}
else
inform (loc,
"at offset [%lli, %lli] into source object of size %s "
"allocated by %qE",
minoff, maxoff, sizestr, allocfn);
}
/* Try to verify that the sizes and lengths of the arguments to a string
manipulation function given by EXP are within valid bounds and that
the operation does not lead to buffer overflow or read past the end.
@ -3423,13 +3547,16 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
ACCESS is true for accesses, false for simple size checks in calls
to functions that neither read from nor write to the region.
When nonnull, PAD points to a more detailed description of the access.
If the call is successfully verified as safe return true, otherwise
return false. */
bool
check_access (tree exp, tree, tree, tree dstwrite,
tree maxread, tree srcstr, tree dstsize,
bool access /* = true */)
bool access /* = true */,
const access_data *pad /* = NULL */)
{
int opt = OPT_Wstringop_overflow_;
@ -3633,7 +3760,11 @@ check_access (tree exp, tree, tree, tree dstwrite,
exp, range[0], range[1],
dstsize));
if (warned)
TREE_NO_WARNING (exp) = true;
{
TREE_NO_WARNING (exp) = true;
if (pad)
inform_access (pad->dst, true);
}
/* Return error when an overflow has been detected. */
return false;
@ -3741,8 +3872,11 @@ check_access (tree exp, tree, tree, tree dstwrite,
loc = expansion_point_location_if_in_system_header (loc);
if (warn_for_access (loc, func, exp, opt, range, slen, access))
TREE_NO_WARNING (exp) = true;
{
TREE_NO_WARNING (exp) = true;
if (pad)
inform_access (pad->src, false);
}
return false;
}
@ -3841,185 +3975,135 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
return wide_int_to_tree (sizetype, rng1[1]);
}
/* Helper for compute_objsize. Returns the constant size of the DEST
if it refers to a variable or field and sets *PDECL to the DECL and
*POFF to zero. Otherwise returns null for other nodes. */
/* Wrapper around the wide_int overload of get_range. Returns the same
result but accepts offset_int instead. */
static tree
addr_decl_size (tree dest, tree *pdecl, tree *poff)
static bool
get_range (tree x, signop sgn, offset_int r[2],
const vr_values *rvals /* = NULL */)
{
if (TREE_CODE (dest) == ADDR_EXPR)
dest = TREE_OPERAND (dest, 0);
wide_int wr[2];
if (!get_range (x, wr, rvals))
return false;
if (DECL_P (dest))
{
*pdecl = dest;
*poff = integer_zero_node;
if (tree size = DECL_SIZE_UNIT (dest))
return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
}
if (TREE_CODE (dest) == COMPONENT_REF)
{
*pdecl = TREE_OPERAND (dest, 1);
*poff = integer_zero_node;
/* Only return constant sizes for now while callers depend on it. */
if (tree size = component_ref_size (dest))
return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
}
return NULL_TREE;
r[0] = offset_int::from (wr[0], sgn);
r[1] = offset_int::from (wr[1], sgn);
return true;
}
/* Helper to compute the size of the object referenced by the DEST
/* Helper to compute the size of the object referenced by the PTR
expression which must have pointer type, using Object Size type
OSTYPE (only the least significant 2 bits are used).
Returns an estimate of the size of the object represented as
a sizetype constant if successful or NULL when the size cannot
be determined.
When the referenced object involves a non-constant offset in some
range the returned value represents the largest size given the
smallest non-negative offset in the range.
If nonnull, sets *PDECL to the decl of the referenced subobject
if it can be determined, or to null otherwise. Likewise, when
POFF is nonnull *POFF is set to the offset into *PDECL.
On success, sets PREF->REF to the DECL of the referenced object
if it's unique, otherwise to null, PREF->OFFRNG to the range of
offsets into it, and PREF->SIZRNG to the range of sizes of
the object(s).
VISITED is used to avoid visiting the same PHI operand multiple
times, and, when nonnull, RVALS to determine range information.
Returns true on success, false when the size cannot be determined.
The function is intended for diagnostics and should not be used
to influence code generation or optimization. */
tree
compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
tree *poff /* = NULL */, const vr_values *rvals /* = NULL */)
static bool
compute_objsize (tree ptr, int ostype, access_ref *pref,
bitmap *visited, const vr_values *rvals /* = NULL */)
{
tree dummy_decl = NULL_TREE;
if (!pdecl)
pdecl = &dummy_decl;
tree dummy_off = NULL_TREE;
if (!poff)
poff = &dummy_off;
/* Only the two least significant bits are meaningful. */
ostype &= 3;
if (ostype)
/* Except for overly permissive calls to memcpy and other raw
memory functions with zero OSTYPE, detect the size from simple
DECLs first to more reliably than compute_builtin_object_size
set *PDECL and *POFF. */
if (tree size = addr_decl_size (dest, pdecl, poff))
return size;
unsigned HOST_WIDE_INT size;
if (compute_builtin_object_size (dest, ostype, &size, pdecl, poff))
return build_int_cst (sizetype, size);
if (TREE_CODE (dest) == SSA_NAME)
if (ostype == 0)
{
gimple *stmt = SSA_NAME_DEF_STMT (dest);
if (is_gimple_call (stmt))
/* Use BOS only for raw memory functions like memcpy to get
the size of the largest enclosing object. */
tree off = NULL_TREE;
unsigned HOST_WIDE_INT size;
if (compute_builtin_object_size (ptr, ostype, &size, &pref->ref, &off))
{
/* If STMT is a call to an allocation function get the size
from its argument(s). If successful, also set *PDECL to
DEST for the caller to include in diagnostics. */
if (tree size = gimple_call_alloc_size (stmt))
if (off)
{
*pdecl = dest;
*poff = integer_zero_node;
return size;
offset_int offset = wi::to_offset (off);
pref->offrng[0] += offset;
pref->offrng[1] += offset;
/* compute_builtin_object_size() returns the remaining
size in PTR. Add the offset to it to get the full
size. */
pref->sizrng[0] = pref->sizrng[1] = size + offset;
}
return NULL_TREE;
else
pref->sizrng[0] = pref->sizrng[1] = size;
return true;
}
if (!is_gimple_assign (stmt))
return NULL_TREE;
dest = gimple_assign_rhs1 (stmt);
tree_code code = gimple_assign_rhs_code (stmt);
if (code == POINTER_PLUS_EXPR)
{
/* compute_builtin_object_size fails for addresses with
non-constant offsets. Try to determine the range of
such an offset here and use it to adjust the constant
size. */
tree off = gimple_assign_rhs2 (stmt);
if (TREE_CODE (off) == INTEGER_CST)
{
if (tree size = compute_objsize (dest, ostype, pdecl, poff))
{
wide_int wioff = wi::to_wide (off);
wide_int wisiz = wi::to_wide (size);
/* Ignore negative offsets for now. For others,
use the lower bound as the most optimistic
estimate of the (remaining) size. */
if (wi::neg_p (wioff))
;
else
{
if (*poff)
{
*poff = fold_convert (ptrdiff_type_node, *poff);
off = fold_convert (ptrdiff_type_node, *poff);
*poff = size_binop (PLUS_EXPR, *poff, off);
}
else
*poff = off;
if (wi::ltu_p (wioff, wisiz))
return wide_int_to_tree (TREE_TYPE (size),
wi::sub (wisiz, wioff));
return size_zero_node;
}
}
}
else if (TREE_CODE (off) == SSA_NAME
&& INTEGRAL_TYPE_P (TREE_TYPE (off)))
{
wide_int min, max;
enum value_range_kind rng = get_range_info (off, &min, &max);
if (rng == VR_RANGE)
if (tree size = compute_objsize (dest, ostype, pdecl, poff))
{
wide_int wisiz = wi::to_wide (size);
/* Ignore negative offsets for now. For others,
use the lower bound as the most optimistic
estimate of the (remaining)size. */
if (wi::neg_p (min) || wi::neg_p (max))
;
else
{
/* FIXME: For now, since the offset is non-constant,
clear *POFF to keep it from being "misused."
Eventually *POFF will need to become a range that
can be properly added to the outer offset if it
too is one. */
*poff = NULL_TREE;
if (wi::ltu_p (min, wisiz))
return wide_int_to_tree (TREE_TYPE (size),
wi::sub (wisiz, min));
return size_zero_node;
}
}
}
}
else if (code != ADDR_EXPR)
return NULL_TREE;
}
/* Unless computing the largest size (for memcpy and other raw memory
functions), try to determine the size of the object from its type. */
if (!ostype)
return NULL_TREE;
const bool addr = TREE_CODE (ptr) == ADDR_EXPR;
if (addr)
ptr = TREE_OPERAND (ptr, 0);
if (TREE_CODE (dest) == ARRAY_REF
|| TREE_CODE (dest) == MEM_REF)
if (DECL_P (ptr))
{
tree ref = TREE_OPERAND (dest, 0);
/* Bail if the reference is to the pointer itself (as opposed
to what it points to). */
if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
return false;
tree size = decl_init_size (ptr, false);
if (!size || TREE_CODE (size) != INTEGER_CST)
return false;
pref->ref = ptr;
pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
return true;
}
const tree_code code = TREE_CODE (ptr);
if (code == COMPONENT_REF)
{
if (ostype == 0)
{
/* For raw memory functions like memcpy bail if the size
of the enclosing object cannot be determined. */
access_ref tmpref;
tree ref = TREE_OPERAND (ptr, 0);
if (!compute_objsize (ref, ostype, &tmpref, visited, rvals)
|| !tmpref.ref)
return false;
}
tree field = TREE_OPERAND (ptr, 1);
/* Bail if the reference is to the pointer itself (as opposed
to what it points to). */
if (!addr && POINTER_TYPE_P (TREE_TYPE (field)))
return false;
pref->ref = field;
/* Only return constant sizes for now while callers depend
on it. INT0LEN is true for interior zero-length arrays. */
bool int0len = false;
tree size = component_ref_size (ptr, &int0len);
if (int0len)
{
pref->sizrng[0] = pref->sizrng[1] = 0;
return true;
}
if (!size || TREE_CODE (size) != INTEGER_CST)
return false;
pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
return true;
}
if (code == ARRAY_REF || code == MEM_REF)
{
tree ref = TREE_OPERAND (ptr, 0);
tree reftype = TREE_TYPE (ref);
if (TREE_CODE (dest) == MEM_REF && TREE_CODE (reftype) == POINTER_TYPE)
if (code == ARRAY_REF
&& TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE)
/* Avoid arrays of pointers. FIXME: Hande pointers to arrays
of known bound. */
return false;
if (code == MEM_REF && TREE_CODE (reftype) == POINTER_TYPE)
{
/* Give up for MEM_REFs of vector types; those may be synthesized
from multiple assignments to consecutive data members. See PR
@ -4028,158 +4112,175 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
MEM_REFs at the time they're created. */
reftype = TREE_TYPE (reftype);
if (TREE_CODE (reftype) == VECTOR_TYPE)
return NULL_TREE;
return false;
}
tree off = TREE_OPERAND (dest, 1);
if (tree size = compute_objsize (ref, ostype, pdecl, poff))
if (!compute_objsize (ref, ostype, pref, visited, rvals))
return false;
offset_int orng[2];
tree off = TREE_OPERAND (ptr, 1);
if (!get_range (off, SIGNED, orng, rvals))
/* Fail unless the size of the object is zero. */
return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1];
if (TREE_CODE (ptr) == ARRAY_REF)
{
/* If the declaration of the destination object is known
to have zero size, return zero. */
if (integer_zerop (size)
&& *pdecl && DECL_P (*pdecl)
&& *poff && integer_zerop (*poff))
return size_zero_node;
/* A valid offset into a declared object cannot be negative.
A zero size with a zero "inner" offset is still zero size
regardless of the "other" offset OFF. */
if (*poff
&& ((integer_zerop (*poff) && integer_zerop (size))
|| (TREE_CODE (*poff) == INTEGER_CST
&& tree_int_cst_sgn (*poff) < 0)))
return size_zero_node;
wide_int offrng[2];
if (!get_range (off, offrng, rvals))
return NULL_TREE;
/* Convert to the same precision to keep wide_int from "helpfully"
crashing whenever it sees other arguments. */
const unsigned sizprec = TYPE_PRECISION (sizetype);
offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED);
offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED);
/* Adjust SIZE either up or down by the sum of *POFF and OFF
above. */
if (TREE_CODE (dest) == ARRAY_REF)
/* Convert the array index range determined above to a byte
offset. */
tree lowbnd = array_ref_low_bound (ptr);
if (!integer_zerop (lowbnd) && tree_fits_uhwi_p (lowbnd))
{
tree lowbnd = array_ref_low_bound (dest);
if (!integer_zerop (lowbnd) && tree_fits_uhwi_p (lowbnd))
{
/* Adjust the offset by the low bound of the array
domain (normally zero but 1 in Fortran). */
unsigned HOST_WIDE_INT lb = tree_to_uhwi (lowbnd);
offrng[0] -= lb;
offrng[1] -= lb;
}
/* Convert the array index into a byte offset. */
tree eltype = TREE_TYPE (dest);
tree tpsize = TYPE_SIZE_UNIT (eltype);
if (tpsize && TREE_CODE (tpsize) == INTEGER_CST)
{
wide_int wsz = wi::to_wide (tpsize, offrng->get_precision ());
offrng[0] *= wsz;
offrng[1] *= wsz;
}
else
return NULL_TREE;
/* Adjust the index by the low bound of the array domain
(normally zero but 1 in Fortran). */
unsigned HOST_WIDE_INT lb = tree_to_uhwi (lowbnd);
orng[0] -= lb;
orng[1] -= lb;
}
wide_int wisize = wi::to_wide (size);
tree eltype = TREE_TYPE (ptr);
tree tpsize = TYPE_SIZE_UNIT (eltype);
if (!tpsize || TREE_CODE (tpsize) != INTEGER_CST)
return false;
if (!*poff)
offset_int sz = wi::to_offset (tpsize);
orng[0] *= sz;
orng[1] *= sz;
if (TREE_CODE (eltype) == ARRAY_TYPE)
{
/* If the "inner" offset is unknown and the "outer" offset
is either negative or less than SIZE, return the size
minus the offset. This may be overly optimistic in
the first case if the inner offset happens to be less
than the absolute value of the outer offset. */
if (wi::neg_p (offrng[0]))
return size;
if (wi::ltu_p (offrng[0], wisize))
return build_int_cst (sizetype, (wisize - offrng[0]).to_uhwi ());
return size_zero_node;
pref->sizrng[0] = pref->offrng[0] + orng[0] + sz;
pref->sizrng[1] = pref->offrng[1] + orng[1] + sz;
}
/* Convert to the same precision to keep wide_int from "helpfuly"
crashing whenever it sees other argumments. */
offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED);
offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED);
tree dstoff = *poff;
if (integer_zerop (*poff))
*poff = off;
else if (!integer_zerop (off))
{
*poff = fold_convert (ptrdiff_type_node, *poff);
off = fold_convert (ptrdiff_type_node, off);
*poff = size_binop (PLUS_EXPR, *poff, off);
}
if (!wi::neg_p (offrng[0]))
{
if (TREE_CODE (size) != INTEGER_CST)
return NULL_TREE;
/* Return the difference between the size and the offset
or zero if the offset is greater. */
wide_int wisize = wi::to_wide (size, sizprec);
if (wi::ltu_p (wisize, offrng[0]))
return size_zero_node;
return wide_int_to_tree (sizetype, wisize - offrng[0]);
}
wide_int dstoffrng[2];
if (TREE_CODE (dstoff) == INTEGER_CST)
dstoffrng[0] = dstoffrng[1] = wi::to_wide (dstoff);
else if (TREE_CODE (dstoff) == SSA_NAME)
{
enum value_range_kind rng
= get_range_info (dstoff, dstoffrng, dstoffrng + 1);
if (rng != VR_RANGE)
return NULL_TREE;
}
else
return NULL_TREE;
dstoffrng[0] = wide_int::from (dstoffrng[0], sizprec, SIGNED);
dstoffrng[1] = wide_int::from (dstoffrng[1], sizprec, SIGNED);
if (!wi::neg_p (dstoffrng[0]))
wisize += dstoffrng[0];
offrng[1] += dstoffrng[1];
if (wi::neg_p (offrng[1]))
return size_zero_node;
return wide_int_to_tree (sizetype, wisize);
}
return NULL_TREE;
pref->offrng[0] += orng[0];
pref->offrng[1] += orng[1];
return true;
}
/* Try simple DECLs not handled above. */
if (tree size = addr_decl_size (dest, pdecl, poff))
return size;
if (TREE_CODE (ptr) == SSA_NAME)
{
gimple *stmt = SSA_NAME_DEF_STMT (ptr);
if (is_gimple_call (stmt))
{
/* If STMT is a call to an allocation function get the size
from its argument(s). If successful, also set *PDECL to
PTR for the caller to include in diagnostics. */
wide_int wr[2];
if (gimple_call_alloc_size (stmt, wr, rvals))
{
pref->ref = ptr;
pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
return true;
}
return false;
}
tree type = TREE_TYPE (dest);
if (TREE_CODE (type) == POINTER_TYPE)
type = TREE_TYPE (type);
/* TODO: Handle PHI. */
if (!is_gimple_assign (stmt))
return false;
ptr = gimple_assign_rhs1 (stmt);
tree_code code = gimple_assign_rhs_code (stmt);
if (TREE_CODE (TREE_TYPE (ptr)) != POINTER_TYPE)
/* Avoid conversions from non-pointers. */
return false;
if (code == POINTER_PLUS_EXPR)
{
/* If the the offset in the expression can be determined use
it to adjust the overall offset. Otherwise, set the overall
offset to the maximum. */
offset_int orng[2];
tree off = gimple_assign_rhs2 (stmt);
if (!get_range (off, SIGNED, orng, rvals)
|| !wi::les_p (orng[0], orng[1]))
{
orng[0] = wi::to_offset (TYPE_MIN_VALUE (ptrdiff_type_node));
orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
}
pref->offrng[0] += orng[0];
pref->offrng[1] += orng[1];
}
else if (code != ADDR_EXPR)
return false;
return compute_objsize (ptr, ostype, pref, visited, rvals);
}
tree type = TREE_TYPE (ptr);
type = TYPE_MAIN_VARIANT (type);
if (TREE_CODE (dest) == ADDR_EXPR)
dest = TREE_OPERAND (dest, 0);
if (TREE_CODE (ptr) == ADDR_EXPR)
ptr = TREE_OPERAND (ptr, 0);
if (TREE_CODE (type) == ARRAY_TYPE
&& !array_at_struct_end_p (dest))
&& !array_at_struct_end_p (ptr))
{
if (tree size = TYPE_SIZE_UNIT (type))
return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
return get_range (size, UNSIGNED, pref->sizrng, rvals);
}
return NULL_TREE;
return false;
}
/* Convenience wrapper around the above. */
static tree
compute_objsize (tree ptr, int ostype, access_ref *pref,
const vr_values *rvals = NULL)
{
bitmap visited = NULL;
bool success
= compute_objsize (ptr, ostype, pref, &visited, rvals);
if (visited)
BITMAP_FREE (visited);
if (!success)
return NULL_TREE;
if (pref->offrng[0] < 0)
{
if (pref->offrng[1] < 0)
return size_zero_node;
pref->offrng[0] = 0;
}
if (pref->sizrng[1] < pref->offrng[0])
return size_zero_node;
return wide_int_to_tree (sizetype, pref->sizrng[1] - pref->offrng[0]);
}
/* Transitional wrapper around the above. The function should be removed
once callers transition to one of the two above. */
tree
compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */,
tree *poff /* = NULL */, const vr_values *rvals /* = NULL */)
{
/* Set the initial offsets to zero and size to negative to indicate
none has been computed yet. */
access_ref ref;
tree size = compute_objsize (ptr, ostype, &ref, rvals);
if (!size)
return NULL_TREE;
if (pdecl)
*pdecl = ref.ref;
if (poff)
*poff = wide_int_to_tree (ptrdiff_type_node, ref.offrng[ref.offrng[0] < 0]);
return size;
}
/* Helper to determine and check the sizes of the source and the destination
@ -4196,11 +4297,12 @@ check_memop_access (tree exp, tree dest, tree src, tree size)
try to determine the size of the largest source and destination
object using type-0 Object Size regardless of the object size
type specified by the option. */
tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
tree dstsize = compute_objsize (dest, 0);
access_data data;
tree srcsize = src ? compute_objsize (src, 0, &data.src) : NULL_TREE;
tree dstsize = compute_objsize (dest, 0, &data.dst);
return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE,
srcsize, dstsize);
srcsize, dstsize, true, &data);
}
/* Validate memchr arguments without performing any expansion.
@ -4220,9 +4322,11 @@ expand_builtin_memchr (tree exp, rtx)
of the object. */
if (warn_stringop_overflow)
{
tree size = compute_objsize (arg1, 0);
access_data data;
tree size = compute_objsize (arg1, 0, &data.src);
check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
/*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
/*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE,
true, &data);
}
return NULL_RTX;
@ -4497,10 +4601,11 @@ expand_builtin_strcat (tree exp)
just diagnose cases when the souce string is longer than
the destination object. */
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
access_data data;
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
destsize);
destsize, true, &data);
return NULL_RTX;
}
@ -4521,9 +4626,11 @@ expand_builtin_strcpy (tree exp, rtx target)
if (warn_stringop_overflow)
{
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
access_data data;
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1,
&data.dst);
check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
src, destsize);
src, destsize, true, &data);
}
if (rtx ret = expand_builtin_strcpy_args (exp, dest, src, target))
@ -4579,9 +4686,11 @@ expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
if (warn_stringop_overflow)
{
tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
access_data data;
tree destsize = compute_objsize (dst, warn_stringop_overflow - 1,
&data.dst);
check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
src, destsize);
src, destsize, true, &data);
}
/* If return value is ignored, transform stpcpy into strcpy. */
@ -4690,10 +4799,12 @@ expand_builtin_stpncpy (tree exp, rtx)
if (!check_nul_terminated_array (exp, src, len))
return NULL_RTX;
access_data data;
/* The size of the destination object. */
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize,
true, &data);
return NULL_RTX;
}
@ -4733,12 +4844,13 @@ check_strncat_sizes (tree exp, tree objsize)
/* Try to verify that the destination is big enough for the shortest
string. */
access_data data;
if (!objsize && warn_stringop_overflow)
{
/* If it hasn't been provided by __strncat_chk, try to determine
the size of the destination object into which the source is
being copied. */
objsize = compute_objsize (dest, warn_stringop_overflow - 1);
objsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
}
/* Add one for the terminating nul. */
@ -4769,10 +4881,10 @@ check_strncat_sizes (tree exp, tree objsize)
&& tree_int_cst_lt (maxread, srclen)))
srclen = maxread;
/* The number of bytes to write is LEN but check_access will also
/* The number of bytes to write is LEN but check_access will alsoa
check SRCLEN if LEN's value isn't known. */
return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
objsize);
objsize, true, &data);
}
/* Similar to expand_builtin_strcat, do some very basic size validation
@ -4810,10 +4922,11 @@ expand_builtin_strncat (tree exp, rtx)
maxlen = lendata.maxbound;
}
access_data data;
/* Try to verify that the destination is big enough for the shortest
string. First try to determine the size of the destination object
into which the source is being copied. */
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
/* Add one for the terminating nul. */
tree srclen = (maxlen
@ -4843,8 +4956,8 @@ expand_builtin_strncat (tree exp, rtx)
&& tree_int_cst_lt (maxread, srclen)))
srclen = maxread;
/* The number of bytes to write is SRCLEN. */
check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize);
check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize,
true, &data);
return NULL_RTX;
}
@ -4873,13 +4986,14 @@ expand_builtin_strncpy (tree exp, rtx target)
if (warn_stringop_overflow)
{
tree destsize = compute_objsize (dest,
warn_stringop_overflow - 1);
access_data data;
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1,
&data.dst);
/* The number of bytes to write is LEN but check_access will also
check SLEN if LEN's value isn't known. */
check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
destsize);
destsize, true, &data);
}
/* We must be passed a constant len and src parameter. */
@ -5192,16 +5306,18 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
/* Diagnose calls where the specified length exceeds the size of either
object. */
tree size = compute_objsize (arg1, 0);
access_data data;
tree size = compute_objsize (arg1, 0, &data.src);
no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
len, /*maxread=*/NULL_TREE, size,
/*objsize=*/NULL_TREE);
/*objsize=*/NULL_TREE, true, &data);
if (no_overflow)
{
size = compute_objsize (arg2, 0);
access_data data;
size = compute_objsize (arg2, 0, &data.src);
no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
len, /*maxread=*/NULL_TREE, size,
/*objsize=*/NULL_TREE);
/*objsize=*/NULL_TREE, true, &data);
}
/* If the specified length exceeds the size of either object,

View file

@ -152,12 +152,39 @@ extern bool target_char_cst_p (tree t, char *p);
extern internal_fn associated_internal_fn (tree);
extern internal_fn replacement_internal_fn (gcall *);
bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
extern bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
extern void warn_string_no_nul (location_t, const char *, tree, tree);
extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
extern bool builtin_with_linkage_p (tree);
extern bool check_access (tree, tree, tree, tree, tree, tree, tree,
bool = true);
/* Describes a reference to an object used in an access. */
struct access_ref
{
access_ref (): ref ()
{
/* Set to valid. */
offrng[0] = offrng[1] = 0;
/* Invalidate. */
sizrng[0] = sizrng[1] = -1;
}
/* Reference to the object. */
tree ref;
/* Range of offsets into and sizes of the object(s). */
offset_int offrng[2];
offset_int sizrng[2];
};
/* Describes a pair of references used in an access by built-in
functions like memcpy. */
struct access_data
{
/* Destination and source of the access. */
access_ref dst, src;
};
extern bool check_access (tree, tree, tree, tree, tree, tree, tree,
bool = true, const access_data * = NULL);
#endif /* GCC_BUILTINS_H */

View file

@ -1274,6 +1274,27 @@ builtin_access::strcpy_overlap ()
return generic_overlap ();
}
/* For a BASE of array type, clamp REFOFF to at most [0, BASE_SIZE]
if known, or [0, MAXOBJSIZE] otherwise. */
static void
clamp_offset (tree base, offset_int refoff[2], offset_int maxobjsize)
{
if (!base || TREE_CODE (TREE_TYPE (base)) != ARRAY_TYPE)
return;
if (refoff[0] < 0 && refoff[1] >= 0)
refoff[0] = 0;
if (refoff[1] < refoff[0])
{
offset_int maxsize = maxobjsize;
if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (base)))
maxsize = wi::to_offset (size);
refoff[1] = wi::umin (refoff[1], maxsize);
}
}
/* Return true if DSTREF and SRCREF describe accesses that either overlap
one another or that, in order not to overlap, would imply that the size
@ -1312,35 +1333,12 @@ builtin_access::overlap ()
/* If the base object is an array adjust the bounds of the offset
to be non-negative and within the bounds of the array if possible. */
if (dstref->base
&& TREE_CODE (TREE_TYPE (dstref->base)) == ARRAY_TYPE)
{
if (acs.dstoff[0] < 0 && acs.dstoff[1] >= 0)
acs.dstoff[0] = 0;
if (acs.dstoff[1] < acs.dstoff[0])
{
if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (dstref->base)))
acs.dstoff[1] = wi::umin (acs.dstoff[1], wi::to_offset (size));
else
acs.dstoff[1] = wi::umin (acs.dstoff[1], maxobjsize);
}
}
clamp_offset (dstref->base, acs.dstoff, maxobjsize);
acs.srcoff[0] = srcref->offrange[0];
acs.srcoff[1] = srcref->offrange[1];
if (srcref->base
&& TREE_CODE (TREE_TYPE (srcref->base)) == ARRAY_TYPE)
{
if (acs.srcoff[0] < 0 && acs.srcoff[1] >= 0)
acs.srcoff[0] = 0;
if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (srcref->base)))
acs.srcoff[1] = wi::umin (acs.srcoff[1], wi::to_offset (size));
else if (acs.srcoff[1] < acs.srcoff[0])
acs.srcoff[1] = wi::umin (acs.srcoff[1], maxobjsize);
}
clamp_offset (srcref->base, acs.srcoff, maxobjsize);
/* When the upper bound of the offset is less than the lower bound
the former is the result of a negative offset being represented

View file

@ -269,7 +269,7 @@ void test_strncpy_array (Dest *pd, int i, const char* s)
CPY (dst7, s, 7); /* { dg-warning "specified bound 7 equals destination size" } */
CPY (dst7, s, sizeof dst7); /* { dg-warning "specified bound 7 equals destination size" } */
CPY (dst2_5[0], s, sizeof dst2_5[0]); /* { dg-warning "specified bound 5 equals destination size" "bug 77293" { xfail *-*-* } } */
CPY (dst2_5[0], s, sizeof dst2_5[0]); /* { dg-warning "specified bound 5 equals destination size" "bug 77293" } */
CPY (dst2_5[1], s, sizeof dst2_5[1]); /* { dg-warning "specified bound 5 equals destination size" } */
/* Verify that copies that nul-terminate are not diagnosed. */

View file

@ -66,8 +66,10 @@ void strcpy_global (void)
T (gma.a17, 17); // { dg-warning "'strcpy' offset 157 from the object at 'gma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" }
SA (__builtin_offsetof (struct MA17, ax) == 157);
T (gma.ax, 0); // { dg-warning "'strcpy' offset 157 from the object at 'gma' is out of the bounds of referenced subobject 'ax' with type 'char[]' at offset 157|'strcpy' offset 157 is out of the bounds \\\[0, 157] of object 'gma' with type 'struct MA17'" }
// GCC allows static initialization of flexible array members of
// non-local objects. Verify that writing into one that may be
// initialized in another translation unit isn't diagnosed. */
T (gma.ax, 0); // { dg-bogus "\\\[-Warray-bounds" }
}

View file

@ -1,7 +1,7 @@
/* PR tree-optimization/84095 - false-positive -Wrestrict warnings for
strcpy within array
{ dg-do compile }
{ dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
{ dg-options "-O2 -Wrestrict -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
typedef __SIZE_TYPE__ size_t;

View file

@ -29,15 +29,15 @@ void test_memcpy_array_cst_range_off (const void *s)
T (d + UR (1, 2), 6); /* { dg-warning ".memcpy. writing 6 bytes into a region of size 5 overflows the destination" } */
T (d + UR (1, 2), 7); /* { dg-warning "writing 7 bytes into a region of size 5 " } */
T (d + SR (-3, -2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " "pr85350" { xfail *-*-* } } */
T (d + SR (-3, -2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " } */
T (d + SR (-2, -1), 1);
T (d + SR (-2, -1), 2); /* { dg-warning "writing 2 bytes into a region of size 7 " "pr89428" { xfail *-*-* } } */
T (d + SR (-2, -1), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " "pr85350" { xfail *-*-* } } */
T (d + SR (-2, -1), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " } */
d = ga7 + 7;
T (d + SR (-7, -6), 1);
T (d + SR (-7, -1), 1);
T (d + SR (-2, -1), 3); /* { dg-warning "writing 3 bytes into a region of size 2 " "pr85350" { xfail *-*-* } } */
T (d + SR (-2, -1), 3); /* { dg-warning "writing 3 bytes into a region of size 2 " } */
T (d + UR (1, 2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " } */
}
@ -67,15 +67,15 @@ void test_memset_array_unsigned_off (void)
T (d + UR (1, 2), 6); /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" } */
T (d + UR (1, 2), 7); /* { dg-warning "writing 7 bytes into a region of size 5 " } */
T (d + SR (-3, -2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " "pr85350" { xfail *-*-* } } */
T (d + SR (-3, -2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " } */
T (d + SR (-2, -1), 1);
T (d + SR (-2, -1), 2); /* { dg-warning "writing 2 bytes into a region of size 7 " "pr89428" { xfail *-*-* } } */
T (d + SR (-2, -1), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " "pr85350" { xfail *-*-* } } */
T (d + SR (-2, -1), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " } */
d = ga7 + 7;
T (d + SR (-7, -6), 1);
T (d + SR (-7, -1), 1);
T (d + SR (-2, -1), 3); /* { dg-warning "writing 3 bytes into a region of size 2 " "pr85350" { xfail *-*-* } } */
T (d + SR (-2, -1), 3); /* { dg-warning "writing 3 bytes into a region of size 2 " } */
T (d + UR (1, 2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " } */
}
@ -110,8 +110,8 @@ void test_memcpy_array_signed_off (const void *s)
T (d + SR (-7, 7), 7);
T (d + SR (-1, 1), 7);
T (d + SR (-1, 1), 9); /* { dg-warning "writing 9 bytes into a region of size " "pr89428" { xfail *-*-* } } */
T (d + SR (-1, 2), 9); /* { dg-warning "writing 9 bytes into a region of size " "pr89428" { xfail *-*-* } } */
T (d + SR (-1, 1), 9); /* { dg-warning "writing 9 bytes into a region of size " } */
T (d + SR (-1, 2), 9); /* { dg-warning "writing 9 bytes into a region of size " } */
T (d + SR (1, 2), 1);
T (d + SR (1, 2), 5);

View file

@ -224,7 +224,7 @@ void ptr_sub_from_end (int n, int i0, int i1, int i2, int i3)
q += n; // N=1 N=2
q[-1] = 0; // p[0] p[1]
q[-2] = 1; // p[-1] p[0]
q[-3] = 2; // p[-2] p[-1] // { dg-warning "\\\[-Wstringop-overflow" "pr92939: negative offset from end" { xfail *-*-* } }
q[-3] = 2; // p[-2] p[-1] // { dg-warning "\\\[-Wstringop-overflow" "pr92939: negative offset from end" }
/* The following isn't diagnosed because the warning doesn't recognize
the index below as necessarily having the same value as the size

View file

@ -0,0 +1,252 @@
/* PR middle-end/95353 - spurious -Wstringop-overflow writing to a trailing
array plus offset
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
typedef __SIZE_TYPE__ size_t;
struct S0 { char n, a[0]; };
void s0_nowarn_cstidx (struct S0 *p)
{
char *q = p->a;
q[1] = __LINE__;
q[9] = __LINE__;
}
void s0_nowarn_cstoff_cstidx (struct S0 *p)
{
char *q = p->a + 1;
q[1] = __LINE__;
q[9] = __LINE__;
}
void s0_nowarn_varoff_cstdix (struct S0 *p, int i)
{
char *q = p->a + i;
q[1] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" }
q[9] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" }
}
void s0_nowarn_cstoff_varidx (struct S0 *p, int i)
{
char *q = p->a + 1;
q[i] = __LINE__;
}
void s0_nowarn_varoff_varidx (struct S0 *p, int i, int j)
{
char *q = p->a + i;
q[j] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" }
}
/* Accesses past the end of a trailing array with one element is
discouraged but still reluctantly not diagnosed. This should
change. */
struct S1 { char n, a[1]; };
void s1_nowarn_cstidx (struct S1 *p)
{
char *q = p->a;
q[1] = __LINE__;
q[9] = __LINE__;
}
void s1_nowarn_cstoff_cstidx (struct S1 *p)
{
char *q = p->a + 1;
q[1] = __LINE__;
q[9] = __LINE__;
}
void s1_nowarn_varoff_cstdix (struct S1 *p, int i)
{
char *q = p->a + i;
q[1] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" }
q[9] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" }
}
void s1_nowarn_cstoff_varidx (struct S1 *p, int i)
{
char *q = p->a + 1;
q[i] = __LINE__;
}
void s1_nowarn_varoff_varidx (struct S1 *p, int i, int j)
{
char *q = p->a + i;
q[j] = __LINE__;
}
/* Accesses past the end of a trailing array with more than one
element should be diagnosed but aren't yet because the MEM_REF
makes the out-of-bounds accesses indistinguishable from valid
ones to subsequent elements of the array pointed by P. */
struct S2 { char n, a[2]; };
void s2_warn_cstidx (struct S2 *p)
{
char *q = p->a;
/* The following invalid store is represented as
MEM[(char *)p_1(D) + 3B] = __LINE__;
which is indistinguishable from the valid
q = &p[1].n; q[0] = __LINE__;
*/
q[2] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
}
void s2_warn_cstoff_cstidx (struct S2 *p)
{
char *q = p->a + 1;
q[1] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
}
void s2_warn_varoff_cstdix (struct S2 *p, int i)
{
char *q = p->a + i;
q[2] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
}
void s2_warn_cstoff_varidx (struct S2 *p, int i)
{
char *q = p->a + 1;
q[i] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
}
void s2_warn_varoff_varidx (struct S2 *p, int i, int j)
{
char *q = p->a + i;
q[j] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
}
/* Verify that none of these triggers a bogus warning (not tested
elsewhere but triggered during bootstrap). */
void s2_nowarn_varidx_int (struct S2 *p, int i)
{
extern struct S2 s2;
extern struct S2 s2a[];
s2.a[i - 1] = __LINE__;
s2.a[i] = __LINE__;
s2.a[i + 1] = __LINE__;
s2a[i].a[i - 1] = __LINE__;
s2a[i].a[i] = __LINE__;
s2a[i].a[i + 1] = __LINE__;
p[i].a[i - 1] = __LINE__;
p[i].a[i] = __LINE__;
p[i].a[i + 1] = __LINE__;
char *q = p->a;
q[i - 1] = __LINE__;
q[i] = __LINE__;
q[i + 1] = __LINE__;
}
/* Same as above but with a size_t index in range [1, SIZE_MAX]. */
void* s2_nowarn_varidx_size (struct S2 *p, size_t i, size_t j)
{
extern struct S2 s2;
extern struct S2 s2a[];
struct S2 *ps2 = __builtin_malloc (3 * sizeof *ps2);
s2.a[i - 1] = __LINE__;
s2.a[i] = __LINE__;
s2.a[i + 1] = __LINE__;
s2a[i].a[i - 1] = __LINE__;
s2a[i].a[i] = __LINE__;
s2a[i].a[i + 1] = __LINE__;
p[i].a[i - 1] = __LINE__;
p[i].a[i] = __LINE__;
p[i].a[i + 1] = __LINE__;
ps2->a[i - 1] = __LINE__;
ps2->a[i] = __LINE__;
ps2->a[i + 1] = __LINE__;
char *q = p->a;
q[i - 1] = __LINE__;
q[i] = __LINE__;
q[i + 1] = __LINE__;
if (j == 0)
return ps2;
s2.a[j - 1] = __LINE__;
s2.a[j] = __LINE__;
s2.a[j + 1] = __LINE__;
s2a[j].a[j - 1] = __LINE__;
s2a[j].a[j] = __LINE__;
s2a[j].a[j + 1] = __LINE__;
p[j].a[j - 1] = __LINE__;
p[j].a[j] = __LINE__;
p[j].a[j + 1] = __LINE__;
ps2->a[j - 1] = __LINE__;
ps2->a[j] = __LINE__;
ps2->a[j + 1] = __LINE__;
q = p->a;
q[j - 1] = __LINE__;
q[j] = __LINE__;
q[j + 1] = __LINE__;
return ps2;
}
/* Verify that accesses to an interior zero-length array are diagnosed. */
struct Si0 { char c, a[0], d; };
void si0_warn_cstidx (struct Si0 *p)
{
// These are indistinguishable from valid accesses to p->d:
// MEM[(char *)p_1(D) + 1B] = 0;
char *q = p->a;
q[1] = __LINE__; // { dg-warning "writing 1 byte into a region of size 0" "pr?????" { xfail *-*-* } }
q[9] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
}
void si0_warn_cstoff_cstidx (struct Si0 *p)
{
// Like those above, these too are indistinguishable from valid accesses
// to p->d.
char *q = p->a + 1;
q[1] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
q[9] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
}
void si0_warn_varoff_cstdix (struct Si0 *p, int i)
{
char *q = p->a + i;
q[1] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" }
q[9] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" }
}
void si0_warn_cstoff_varidx (struct Si0 *p, int i)
{
char *q = p->a + 1;
q[i] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
}
void si0_warn_varoff_varidx (struct Si0 *p, int i, int j)
{
char *q = p->a + i;
q[j] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" }
}

View file

@ -0,0 +1,76 @@
/* Verify that calls to strcpy to write to an element of an array of pointers
are not diagnosed (due to mistakenly using the size of the array as that
of the destination).
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
typedef char A1[1];
typedef char A2[2];
typedef char A3[3];
typedef char A4[4];
typedef char A5[5];
typedef char A6[6];
typedef char A7[7];
typedef char A8[8];
typedef char A9[9];
typedef char A10[10];
A1* pa1[3];
A2* pa2[3];
A3* pa3[3];
A4* pa4[3];
A5* pa5[3];
A6* pa6[3];
A7* pa7[3];
A8* pa8[3];
A9* pa9[3];
A10* pa10[3];
void nowarn_a1_1 (int i)
{
__builtin_strcpy (*pa1[0], "");
__builtin_strcpy (*pa1[1], "");
__builtin_strcpy (*pa1[i], "");
}
void nowarn_a2_2 (int i)
{
__builtin_strcpy (*pa2[0], "1");
__builtin_strcpy (*pa2[1], "2");
__builtin_strcpy (*pa2[i], "3");
}
void nowarn_a3_3 (int i)
{
__builtin_strcpy (*pa3[0], "12");
__builtin_strcpy (*pa3[1], "23");
__builtin_strcpy (*pa3[i], "34");
}
void nowarn_a4_4 (int i)
{
__builtin_strcpy (*pa4[0], "123");
__builtin_strcpy (*pa4[1], "234");
__builtin_strcpy (*pa4[i], "345");
}
void nowarn_a5_5 (int i)
{
__builtin_strcpy (*pa5[0], "1234");
__builtin_strcpy (*pa5[1], "2345");
__builtin_strcpy (*pa5[i], "3456");
}
void nowarn_a6_6 (int i)
{
__builtin_strcpy (*pa6[0], "12345");
__builtin_strcpy (*pa6[1], "23456");
__builtin_strcpy (*pa6[1], "34567");
}
void nowarn_a10_10 (int i)
{
__builtin_strcpy (*pa10[0], "0123456789");
__builtin_strcpy (*pa10[1], "1234567890");
__builtin_strcpy (*pa10[i], "2345678909");
}

View file

@ -0,0 +1,24 @@
/* Verify that casts between pointers and integers don't trigger false
positives. Test derived from Glibc's _dl_allocate_tls_storage() in
dl-tls.c.
{ dg-do compile }
{ dg-options "-O2 -Wall -Wno-array-bounds" } */
typedef __SIZE_TYPE__ size_t;
typedef __UINTPTR_TYPE__ uintptr_t;
size_t a;
size_t s;
void* _dl_allocate_tls_storage (void)
{
void *p = __builtin_malloc (s + a + sizeof (void *));
char *q = (char *)(__builtin_constant_p (a) && (((a - 1) & a) == 0)
? ((((uintptr_t)p) + a - 1) & ~(a - 1))
: (((((uintptr_t)p) + (a - 1)) / a) * a));
char *r = q + s - sizeof (int[4]);
__builtin_memset (r, '\0', sizeof (int[4]));
return r;
}

View file

@ -0,0 +1,229 @@
/* Verify that -Wstringop-overflow detects writing past the end of each
individual element of a multidimensional array.
{ dg-do compile }
{ dg-options "-O2 -Wall -Wno-array-bounds -Wno-stringop-truncation" } */
#define CONCAT(x, y) x ## y
#define CAT(name, line) CONCAT (name, line)
#define UNIQ_NAME(name) CAT (name, __LINE__)
typedef __SIZE_TYPE__ size_t;
extern void* malloc (size_t);
extern char* strncpy (char*, const char*, size_t);
extern char a2_2_8[2][2][8];
void nowarn_a2_2_8 (const char *s)
{
// The following trigger -Wstringop-truncation.
strncpy (a2_2_8[0][0], s, 8);
strncpy (a2_2_8[0][1], s, 8);
strncpy (a2_2_8[1][0], s, 8);
strncpy (a2_2_8[1][1], s, 8);
}
void warn_a2_2_8 (const char *s)
{
strncpy (a2_2_8[0][0], s, 9); // { dg-warning "writing 9 bytes into a region of size 8 " }
strncpy (a2_2_8[0][1], s, 9); // { dg-warning "writing 9 bytes into a region of size 8 " }
strncpy (a2_2_8[1][0], s, 9); // { dg-warning "writing 9 bytes into a region of size 8 " }
strncpy (a2_2_8[1][1], s, 9); // { dg-warning "writing 9 bytes into a region of size 8 " }
}
extern char a2_3_5[2][3][5];
void nowarn_a2_3_5 (const char *s)
{
// The following trigger -Wstringop-truncation.
strncpy (a2_3_5[0][0], s, 5);
strncpy (a2_3_5[0][1], s, 5);
strncpy (a2_3_5[0][2], s, 5);
strncpy (a2_3_5[1][0], s, 5);
strncpy (a2_3_5[1][1], s, 5);
strncpy (a2_3_5[1][2], s, 5);
}
void warn_a2_3_5 (const char *s)
{
strncpy (a2_3_5[0][0], s, 6); // { dg-warning "writing 6 bytes into a region of size 5 " }
strncpy (a2_3_5[0][1], s, 7); // { dg-warning "writing 7 bytes into a region of size 5 " }
strncpy (a2_3_5[1][0], s, 8); // { dg-warning "writing 8 bytes into a region of size 5 " }
strncpy (a2_3_5[1][1], s, 9); // { dg-warning "writing 9 bytes into a region of size 5 " }
}
void* nowarn_malloc_3_5 (const char *s, unsigned n)
{
if (n < 3 || 5 < n)
n = 3;
char *p = (char*)malloc (n);
strncpy (p + 1, s, 4);
return p;
}
void* warn_malloc_3_5 (const char *s, unsigned n)
{
if (n < 3 || 5 < n)
n = 3;
char *p = (char*)malloc (n); // { dg-message "at offset 1 into destination object of size \\\[3, 5] allocated by 'malloc'" }
// The size below should be a range like the one above.
strncpy (p + 1, s, 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
return p;
}
typedef __attribute__ ((alloc_size (1, 2))) void* UsrAlloc (int, int);
void* nowarn_use_alloc_3_5 (UsrAlloc *usr_alloc, const char *s, unsigned n)
{
if (n < 3 || 5 < n)
n = 3;
char *p = (char*)usr_alloc (n, 3);
strncpy (p + 1, s, 14);
return p;
}
void* warn_usr_alloc_3_5 (UsrAlloc *usr_alloc, const char *s, unsigned n)
{
if (n < 3 || 5 < n)
n = 3;
char *p = (char*)usr_alloc (n, 3); // { dg-message "at offset 1 into destination object of size \\\[9, 15] allocated by 'usr_alloc'" }
// The size below should be a range like the one above.
strncpy (p + 1, s, 15); // { dg-warning "writing 15 bytes into a region of size 14 " }
return p;
}
struct S
{
char a2_3_4[2][3][4];
char a3_4_5[3][4][5];
};
extern struct S sa[];
void nowarn_sa_cstidx_cstsize (const char* const s[])
{
strncpy (sa[0].a2_3_4[0][0], s[0], 4);
strncpy (sa[0].a2_3_4[0][1], s[1], 4);
strncpy (sa[0].a2_3_4[0][2], s[2], 4);
strncpy (sa[0].a2_3_4[1][0], s[3], 4);
strncpy (sa[0].a2_3_4[1][1], s[4], 4);
strncpy (sa[0].a2_3_4[1][2], s[5], 4);
strncpy (sa[1].a2_3_4[0][0], s[6], 4);
strncpy (sa[1].a2_3_4[0][1], s[7], 4);
strncpy (sa[1].a2_3_4[0][2], s[8], 4);
strncpy (sa[1].a2_3_4[1][0], s[9], 4);
strncpy (sa[1].a2_3_4[1][1], s[10], 4);
strncpy (sa[1].a2_3_4[1][2], s[11], 4);
}
void warn_sa_cstidx_cstsize (const char* const s[])
{
strncpy (sa[0].a2_3_4[0][0], s[0], 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
strncpy (sa[0].a2_3_4[0][1], s[1], 6); // { dg-warning "writing 6 bytes into a region of size 4 " }
strncpy (sa[0].a2_3_4[0][2], s[2], 7); // { dg-warning "writing 7 bytes into a region of size 4 " }
strncpy (sa[0].a2_3_4[1][0], s[3], 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
strncpy (sa[0].a2_3_4[1][1], s[4], 6); // { dg-warning "writing 6 bytes into a region of size 4 " }
strncpy (sa[0].a2_3_4[1][2], s[5], 7); // { dg-warning "writing 7 bytes into a region of size 4 " }
strncpy (sa[1].a2_3_4[0][0], s[6], 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
strncpy (sa[1].a2_3_4[0][1], s[7], 6); // { dg-warning "writing 6 bytes into a region of size 4 " }
strncpy (sa[1].a2_3_4[0][2], s[8], 7); // { dg-warning "writing 7 bytes into a region of size 4 " }
strncpy (sa[1].a2_3_4[1][0], s[9], 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
strncpy (sa[1].a2_3_4[1][1], s[10], 6); // { dg-warning "writing 6 bytes into a region of size 4 " }
strncpy (sa[1].a2_3_4[1][2], s[11], 7); // { dg-warning "writing 7 bytes into a region of size 4 " }
}
void nowarn_sa_cstidx_varsize (const char* const s[], unsigned n)
{
strncpy (sa[0].a2_3_4[0][0], s[0], n);
strncpy (sa[0].a2_3_4[0][1], s[1], n);
strncpy (sa[0].a2_3_4[0][2], s[2], n);
strncpy (sa[0].a2_3_4[1][0], s[3], n);
strncpy (sa[0].a2_3_4[1][1], s[4], n);
strncpy (sa[0].a2_3_4[1][2], s[5], n);
strncpy (sa[1].a2_3_4[0][0], s[6], n);
strncpy (sa[1].a2_3_4[0][1], s[7], n);
strncpy (sa[1].a2_3_4[0][2], s[8], n);
strncpy (sa[1].a2_3_4[1][0], s[9], n);
strncpy (sa[1].a2_3_4[1][1], s[10], n);
strncpy (sa[1].a2_3_4[1][2], s[11], n);
}
void nowarn_sa_loop (const char* const s[], unsigned n)
{
for (unsigned i0 = 0; i0 != 5; ++i0)
for (unsigned i1 = 0; i1 != 3; ++i1)
for (unsigned i2 = 0; i2 != 2; ++i2)
strncpy (sa[i0].a2_3_4[i1][i2], s[i2], n);
}
/* Verify that a note after the warning points to the accessed object
and mentions the starting offset of the access. Another alternative
might be for the offset to be the starting offset of the overflow.
As it is, it's not clear to which of the two the offset refers. */
void test_note (const char *s)
{
extern void sink (void*);
{
char a[1][1][2]; // { dg-message "destination object" }
strncpy (a[0][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
sink (a);
}
{
char a[1][2][2]; // { dg-message "at offset 2 into " }
strncpy (a[0][1], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
sink (a);
}
{
char a[1][2][2]; // { dg-message "at offset 4 into " }
strncpy (a[1][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
sink (a);
}
{
char a[2][1][2]; // { dg-message "at offset 2 into " }
strncpy (a[1][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
sink (a);
}
{
char a[2][2][3]; // { dg-message "at offset 9 into " }
strncpy (a[1][1], s, 4); // { dg-warning "writing 4 bytes into a region of size 3 " }
sink (a);
}
{
char a[2][3][3]; // { dg-message "at offset 12 into " }
strncpy (a[1][1], s, 5); // { dg-warning "writing 5 bytes into a region of size 3 " }
sink (a);
}
{
char a[2][3][3]; // { dg-message "at offset 12 into " }
strncpy (a[1][1], s, 6); // { dg-warning "writing 6 bytes into a region of size 3 " }
sink (a);
}
{
char a[2][3][3]; // { dg-message "at offset 15 into " }
strncpy (a[1][2], s, 7); // { dg-warning "writing 7 bytes into a region of size 3 " }
sink (a);
}
}

View file

@ -0,0 +1,94 @@
/* Verify that non-constant offsets don't suppress warnings in cases
when the size of the access alone makes it invalid, regardless of
the offset value.
Also verify that writing into unknown objects through pointers
(or pointer members) doesn't trigger warnings.
{ dg-do compile }
{ dg-options "-O1 -Wall -Wno-array-bounds" } */
typedef __SIZE_TYPE__ size_t;
extern void* memcpy (void*, const void*, size_t);
extern void* memset (void*, int, size_t);
struct Xp { char *p; } xp;
struct Xa { char *a[2]; } xa;
void nowarn_copy_read_pointed_obj_plus_cst (void *d)
{
int i = 0;
memcpy (d, xp.p + i, 9);
memcpy (d, xa.a[i], 9);
}
void nowarn_copy_read_pointed_obj_plus_var (void *d, int i)
{
memcpy (d, xp.p + i, 9);
memcpy (d, xa.a[i], 9);
}
void warn_copy_read_pointer_plus_cst (void *d)
{
int i = 0;
memcpy (d, &xp.p + i, 9); // { dg-warning "reading 9 bytes from a region of size . " }
}
void warn_copy_read_pointer_plus_var (void *d, int i)
{
memcpy (d, &xp.p + i, 9); // { dg-warning "reading 9 bytes from a region of size . " }
}
void nowarn_copy_write_pointed_obj_plus_cst (const void *s)
{
int i = 0;
memcpy (xp.p + i, s, 9);
memcpy (xa.a[i], s, 9);
}
void nowarn_copy_write_pointed_obj_plus_var (const void *s, int i)
{
memcpy (xp.p + i, s, 9);
memcpy (xa.a[i], s, 9);
}
void warn_copy_write_pointer_plus_cst (const void *s)
{
int i = 0;
memcpy (&xp.p + i, s, 9); // { dg-warning "writing 9 bytes into a region of size . " }
}
void warn_copy_write_pointer_plus_var (const void *s, int i)
{
memcpy (&xp.p + i, s, 9); // { dg-warning "writing 9 bytes into a region of size . " }
}
void nowarn_set_pointed_obj_plus_cst (void)
{
int i = 0;
memset (xp.p + i, 0, 9);
memset (xa.a[i], 0, 9);
}
void nowarn_set_pointed_obj_plus_var (int i)
{
memset (xp.p + i, 0, 9);
memset (xa.a[i], 0, 9);
}
void warn_set_pointer_plus_cst (void)
{
int i = 0;
memset (&xp.p + i, 0, 9); // { dg-warning "writing 9 bytes into a region of size . " }
}
void warn_set_pointer_plus_var (int i)
{
memset (&xp.p + i, 0, 9); // { dg-warning "writing 9 bytes into a region of size . " }
memset (&xa.a[i], 0, 17); // { dg-warning "writing 17 bytes into a region of size \[0-9\]+ " }
}

View file

@ -99,7 +99,7 @@ void test_memcpy_range (void *d, const void *s)
memcpy (buf + 5, s, UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" } */
memcpy (buf + size_max, s, UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" "excessive pointer offset" { xfail *-*-* } } */
memcpy (buf + size_max, s, UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" "excessive pointer offset" } */
memcpy (buf, s, UR (ssize_max, size_max)); /* { dg-warning "writing \[0-9\]+ or more bytes into a region of size 5 overflows the destination" } */
memcpy (buf, s, UR (ssize_max + 1, size_max)); /* { dg-warning "specified \(bound|size\) between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */

View file

@ -1,6 +1,6 @@
/* Test exercising -Wstringop-overflow warnings. */
/* { dg-do compile } */
/* { dg-options "-O2 -Wstringop-overflow=1" } */
/* { dg-options "-O2 -Wstringop-overflow=1 -Wno-array-bounds" } */
#define offsetof(type, mem) __builtin_offsetof (type, mem)
@ -120,7 +120,7 @@ void test_memop_warn_alloc (const void *src)
/* Verify the same as above but by writing into the first mmeber
of the first element of the array. */
memcpy (&a[0].a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
memcpy (&a[0].a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size " "memcpy into allocated" } */
escape (a, src);
n = range (12, 32);
@ -133,33 +133,33 @@ void test_memop_warn_alloc (const void *src)
/* The following idiom of clearing multiple members of a struct is
used in a few places in the Linux kernel. Verify that a warning
is issued for it when it writes past the end of the array object. */
memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size " "memcpy into allocated" } */
escape (b);
memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size " "memcpy into allocated" } */
escape (b);
memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size " "memcpy into allocated" } */
escape (b);
memset (&b->c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
memset (&b->c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size " "memcpy into allocated" } */
escape (b);
memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size " "memcpy into allocated" } */
escape (b);
memset (&b->d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
memset (&b->d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size " "memcpy into allocated" } */
escape (b);
/* Same as above but clearing just elements of the second element
of the array. */
memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1); /* { dg-warning "writing 4 bytes into a region of size 3" "memcpy into allocated" { xfail *-*-*} } */
memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1); /* { dg-warning "writing 4 bytes into a region of size " "memcpy into allocated" } */
escape (b);
memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1); /* { dg-warning "writing 3 bytes into a region of size 2" "memcpy into allocated" { xfail *-*-*} } */
memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1); /* { dg-warning "writing 3 bytes into a region of size " "memcpy into allocated" } */
escape (b);
memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1); /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" { xfail *-*-*} } */
memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1); /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" } */
escape (b);
}

View file

@ -106,7 +106,7 @@ void test_memop_warn_alloc (void *p)
memcpy (p, &a[0], n); /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" } */
memcpy (p, &a[0].a, n); /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" { xfail *-*-*} } */
memcpy (p, &a[0].a, n); /* { dg-warning "reading between 8 and 32 bytes from a region of size " "memcpy from allocated" } */
n = range (12, 32);

View file

@ -75,7 +75,7 @@ VERIFY (i0 ? (a8 + 2) : (b8 + 2), 7, 6);
VERIFY (i0 ? (a8 + 0) : (c4 + 0), 9, 4);
VERIFY (i0 ? (a8 + 0) : (c4 + 1), 9, 3);
VERIFY (i0 ? (a8 + 0) : (c4 + 3), 9, 1);
VERIFY (i0 ? (a8 + 0) : (c4 + 4), 9, 0);
VERIFY (i0 ? (a8 + 0) : (c4 + 4), 8, 0);
VERIFY (i0 ? (a8 + 1) : (c4 + 0), 8, 4);
VERIFY (i0 ? (a8 + 1) : (c4 + 1), 8, 3);
VERIFY (i0 ? (a8 + 1) : (c4 + 2), 8, 2);

View file

@ -172,7 +172,7 @@ compute_object_offset (const_tree expr, const_tree var)
is true, else null. An object's initializer affects the object's
size if it's a struct type with a flexible array member. */
static tree
tree
decl_init_size (tree decl, bool min)
{
tree size = DECL_SIZE_UNIT (decl);
@ -259,6 +259,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
offset_int mem_offset;
if (mem_ref_offset (pt_var).is_constant (&mem_offset))
{
if (*poff)
*poff = wide_int_to_tree (ptrdiff_type_node,
mem_offset + wi::to_offset (*poff));
else
*poff = wide_int_to_tree (ptrdiff_type_node, mem_offset);
offset_int dsz = wi::sub (sz, mem_offset);
if (wi::neg_p (dsz))
sz = 0;
@ -413,12 +418,12 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
if (bytes != error_mark_node)
{
*poff = bytes;
if (TREE_CODE (bytes) == INTEGER_CST
&& tree_int_cst_lt (var_size, bytes))
bytes = size_zero_node;
else
bytes = size_binop (MINUS_EXPR, var_size, bytes);
*poff = bytes;
}
if (var != pt_var
&& pt_var_size
@ -441,7 +446,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
else if (!pt_var_size)
return false;
else
bytes = pt_var_size;
{
bytes = pt_var_size;
if (!*poff)
*poff = size_zero_node;
}
if (tree_fits_uhwi_p (bytes))
{

View file

@ -24,5 +24,6 @@ extern void init_object_sizes (void);
extern void fini_object_sizes (void);
extern bool compute_builtin_object_size (tree, int, unsigned HOST_WIDE_INT *,
tree * = NULL, tree * = NULL);
extern tree decl_init_size (tree, bool);
#endif // GCC_TREE_OBJECT_SIZE_H

View file

@ -5589,7 +5589,6 @@ handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh,
set_strinfo (idx, si);
si->writable = true;
si->dont_invalidate = true;
maybe_warn_overflow (stmt, lenrange[2], rvals);
}
}
}