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:
parent
3a391adf7a
commit
a2c2cee92e
20 changed files with 1204 additions and 377 deletions
752
gcc/builtins.c
752
gcc/builtins.c
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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" }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
252
gcc/testsuite/gcc.dg/Wstringop-overflow-34.c
Normal file
252
gcc/testsuite/gcc.dg/Wstringop-overflow-34.c
Normal 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" }
|
||||
}
|
76
gcc/testsuite/gcc.dg/Wstringop-overflow-35.c
Normal file
76
gcc/testsuite/gcc.dg/Wstringop-overflow-35.c
Normal 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");
|
||||
}
|
24
gcc/testsuite/gcc.dg/Wstringop-overflow-36.c
Normal file
24
gcc/testsuite/gcc.dg/Wstringop-overflow-36.c
Normal 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;
|
||||
}
|
229
gcc/testsuite/gcc.dg/Wstringop-overflow-37.c
Normal file
229
gcc/testsuite/gcc.dg/Wstringop-overflow-37.c
Normal 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);
|
||||
}
|
||||
|
||||
}
|
94
gcc/testsuite/gcc.dg/Wstringop-overflow-38.c
Normal file
94
gcc/testsuite/gcc.dg/Wstringop-overflow-38.c
Normal 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\]+ " }
|
||||
}
|
|
@ -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" } */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue