The attached patch extends compute_objsize() to handle conditional
expressions represented either as PHIs or MIN_EXPR and MAX_EXPR.
To simplify the handling of the -Wstringop-overflow/-overread
warnings the change factors this code out of tree-ssa-strlen.c
and into inform_access() in builtins.c, making it a member of
access_ref. Besides eliminating a decent amount of code
duplication this also improves the consistency of the warnings.
Finally, the change introduces a distinction between the definite
kinds of -Wstringop-overflow (and -Wstringop-overread) warnings
and the maybe kind. The latter are currently only being issued
for function array parameters but I expect to make use of them
more extensively in the future.
Besides the usual GCC bootstrap/regtest I have tested the change
with Binutils/GDB and Glibc and verified that it doesn't introduce
any false positives.
Martin
PR middle-end/92936 - missing warning on a past-the-end store to a PHI
PR middle-end/92940 - incorrect offset and size in -Wstringop-overflow for out-of-bounds store into VLA and two offset ranges
PR middle-end/89428 - missing -Wstringop-overflow on a PHI with variable offset
gcc/ChangeLog:
PR middle-end/92936
PR middle-end/92940
PR middle-end/89428
* builtins.c (access_ref::access_ref): Initialize member.
(access_ref::phi): New function.
(access_ref::get_ref): New function.
(access_ref::add_offset): Remove duplicate assignment.
(maybe_warn_for_bound): Add "maybe" kind of warning messages.
(warn_for_access): Same.
(inform_access): Rename...
(access_ref::inform_access): ...to this. Print PHI arguments. Format
offset the same as size and simplify. Improve printing of allocation
functions and VLAs.
(check_access): Adjust to the above.
(gimple_parm_array_size): Change argument.
(handle_min_max_size): New function.
* builtins.h (struct access_ref): Declare new members.
(gimple_parm_array_size): Change argument.
* tree-ssa-strlen.c (maybe_warn_overflow): Use access_ref and simplify.
(handle_builtin_memcpy): Correct argument passed to maybe_warn_overflow.
(handle_builtin_memset): Same.
gcc/testsuite/ChangeLog:
PR middle-end/92936
PR middle-end/92940
PR middle-end/89428
* c-c++-common/Wstringop-overflow-2.c: Adjust text of expected
informational notes.
* gcc.dg/Wstringop-overflow-11.c: Remove xfails.
* gcc.dg/Wstringop-overflow-12.c: Same.
* gcc.dg/Wstringop-overflow-17.c: Adjust text of expected messages.
* gcc.dg/Wstringop-overflow-27.c: Same. Remove xfails.
* gcc.dg/Wstringop-overflow-28.c: Adjust text of expected messages.
* gcc.dg/Wstringop-overflow-29.c: Same.
* gcc.dg/Wstringop-overflow-37.c: Same.
* gcc.dg/Wstringop-overflow-46.c: Same.
* gcc.dg/Wstringop-overflow-47.c: Same.
* gcc.dg/Wstringop-overflow-54.c: Same.
* gcc.dg/warn-strnlen-no-nul.c: Add expected warning.
* gcc.dg/Wstringop-overflow-58.c: New test.
* gcc.dg/Wstringop-overflow-59.c: New test.
* gcc.dg/Wstringop-overflow-60.c: New test.
* gcc.dg/Wstringop-overflow-61.c: New test.
* gcc.dg/Wstringop-overflow-62.c: New test.
diff --git a/gcc/builtins.c b/gcc/builtins.c
index da25343beb1..5d60eab6ba2 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -185,6 +185,7 @@ static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
static void maybe_emit_free_warning (tree);
static tree fold_builtin_object_size (tree, tree);
static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
+static bool compute_objsize (tree, int, access_ref *, bitmap *, range_query *);
unsigned HOST_WIDE_INT target_newline;
unsigned HOST_WIDE_INT target_percent;
@@ -199,7 +200,8 @@ static void expand_builtin_sync_synchronize (void);
access_ref::access_ref (tree bound /* = NULL_TREE */,
bool minaccess /* = false */)
-: ref (), eval ([](tree x){ return x; }), trail1special (true), base0 (true)
+: ref (), eval ([](tree x){ return x; }), trail1special (true), base0 (true),
+ parmarray ()
{
/* Set to valid. */
offrng[0] = offrng[1] = 0;
@@ -222,6 +224,131 @@ access_ref::access_ref (tree bound /* = NULL_TREE */,
}
}
+/* Return the PHI node REF refers to or null if it doesn't. */
+
+gphi *
+access_ref::phi () const
+{
+ if (!ref || TREE_CODE (ref) != SSA_NAME)
+ return NULL;
+
+ gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
+ if (gimple_code (def_stmt) != GIMPLE_PHI)
+ return NULL;
+
+ return as_a <gphi *> (def_stmt);
+}
+
+/* Determine and return the largest object to which REF refers. If REF
+ refers to a PHI and PREF is nonnull, fill *PREF with the details of
+ the object determined by compute_objsize(ARG, OSTYPE) for each PHI
+ argument ARG. */
+
+tree
+access_ref::get_ref (vec<access_ref> *all_refs,
+ access_ref *pref /* = NULL */,
+ int ostype /* = 1 */,
+ bitmap *visited /* = NULL */,
+ range_query *rvals /* = NULL */) const
+{
+ gphi *phi_stmt = this->phi ();
+ if (!phi_stmt)
+ return ref;
+
+ bitmap visited_phis = NULL;
+ if (!visited)
+ visited = &visited_phis;
+
+ if (!*visited)
+ *visited = BITMAP_ALLOC (NULL);
+
+ if (!bitmap_set_bit (*visited, SSA_NAME_VERSION (ref)))
+ /* Have compute_objsize() fail and propagate the failure below. */
+ return NULL_TREE;
+
+ access_ref phi_ref;
+ if (pref)
+ phi_ref = *pref;
+
+ /* Set if any argument is a function array (or VLA) parameter not
+ declared [static]. */
+ bool parmarray = false;
+ /* The size of the smallest object referenced by the PHI arguments. */
+ offset_int minsize = 0;
+ const offset_int maxobjsize = wi::to_offset (max_object_size ());
+ const offset_int orng[2] = { phi_ref.offrng[0], phi_ref.offrng[1] };
+
+ const unsigned nargs = gimple_phi_num_args (phi_stmt);
+ for (unsigned i = 0; i < nargs; ++i)
+ {
+ access_ref phi_arg_ref;
+ tree arg = gimple_phi_arg_def (phi_stmt, i);
+ if (!compute_objsize (arg, ostype, &phi_arg_ref, visited, rvals))
+ return NULL_TREE;
+
+ /* The call to compute_objsize() above will fail when the ARG has
+ already been VISITED. */
+ gcc_checking_assert (phi_arg_ref.sizrng[0] >= 0);
+
+ /* Add PREF's offset to that of the argument. */
+ phi_arg_ref.add_offset (orng[0], orng[1]);
+
+ if (all_refs)
+ all_refs->safe_push (phi_arg_ref);
+
+ const bool arg_known_size = (phi_arg_ref.sizrng[0] != 0
+ || phi_arg_ref.sizrng[1] != maxobjsize);
+
+ parmarray |= phi_arg_ref.parmarray;
+
+ if (phi_ref.sizrng[0] < 0)
+ {
+ phi_ref = phi_arg_ref;
+ if (arg_known_size)
+ minsize = phi_arg_ref.sizrng[0];
+ continue;
+ }
+
+ const bool phi_known_size = (phi_ref.sizrng[0] != 0
+ || phi_ref.sizrng[1] != maxobjsize);
+
+ if (phi_known_size && phi_arg_ref.sizrng[0] < minsize)
+ minsize = phi_arg_ref.sizrng[0];
+
+ /* Determine the amount of remaining space in the argument. */
+ offset_int argrem[2];
+ argrem[1] = phi_arg_ref.size_remaining (argrem);
+
+ /* Determine the amount of remaining space computed so far and
+ if the remaining space in the argument is more use it instead. */
+ offset_int phirem[2];
+ phirem[1] = phi_ref.size_remaining (phirem);
+
+ if (phirem[1] < argrem[1])
+ /* Use the argument with the most space remaining as the result. */
+ phi_ref = phi_arg_ref;
+ }
+
+ if (phi_ref.sizrng[0] < 0)
+ /* Fail if none of the PHI's arguments resulted in updating PHI_REF
+ (perhaps because they have all been already visited by prior
+ recursive calls). */
+ return NULL_TREE;
+
+ /* Replace the lower bound of the largest argument with the size of
+ the smallest argument, and set PARMARRAY if any argument was one. */
+ phi_ref.sizrng[0] = minsize;
+ phi_ref.parmarray = parmarray;
+
+ if (visited == &visited_phis)
+ BITMAP_FREE (visited_phis);
+
+ if (pref && pref != this)
+ *pref = phi_ref;
+
+ return phi_ref.ref;
+}
+
/* Return the maximum amount of space remaining and if non-null, set
argument to the minimum. */
@@ -318,7 +445,6 @@ void access_ref::add_offset (const offset_int &min, const offset_int &max)
return;
}
- offrng[1] = maxoff;
offset_int absmax = wi::abs (max);
if (offrng[0] < absmax)
{
@@ -3560,28 +3686,42 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
if (opt == OPT_Wstringop_overread)
{
+ bool maybe = pad && pad->src.phi ();
+
if (tree_int_cst_lt (maxobjsize, bndrng[0]))
{
if (bndrng[0] == bndrng[1])
warned = (func
? warning_at (loc, opt,
- "%K%qD specified bound %E "
- "exceeds maximum object size %E",
+ (maybe
+ ? G_("%K%qD specified bound %E may "
+ "exceed maximum object size %E")
+ : G_("%K%qD specified bound %E "
+ "exceeds maximum object size %E")),
exp, func, bndrng[0], maxobjsize)
: warning_at (loc, opt,
- "%Kspecified bound %E "
- "exceeds maximum object size %E",
+ (maybe
+ ? G_("%Kspecified bound %E may "
+ "exceed maximum object size %E")
+ : G_("%Kspecified bound %E "
+ "exceeds maximum object size %E")),
exp, bndrng[0], maxobjsize));
else
warned = (func
? warning_at (loc, opt,
- "%K%qD specified bound [%E, %E] "
- "exceeds maximum object size %E",
+ (maybe
+ ? G_("%K%qD specified bound [%E, %E] may "
+ "exceed maximum object size %E")
+ : G_("%K%qD specified bound [%E, %E] "
+ "exceeds maximum object size %E")),
exp, func,
bndrng[0], bndrng[1], maxobjsize)
: warning_at (loc, opt,
- "%Kspecified bound [%E, %E] "
- "exceeds maximum object size %E",
+ (maybe
+ ? G_("%Kspecified bound [%E, %E] may "
+ "exceed maximum object size %E")
+ : G_("%Kspecified bound [%E, %E] "
+ "exceeds maximum object size %E")),
exp, bndrng[0], bndrng[1], maxobjsize));
}
else if (!size || tree_int_cst_le (bndrng[0], size))
@@ -3589,22 +3729,34 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
warned = (func
? warning_at (loc, opt,
- "%K%qD specified bound %E exceeds "
- "source size %E",
+ (maybe
+ ? G_("%K%qD specified bound %E may exceed "
+ "source size %E")
+ : G_("%K%qD specified bound %E exceeds "
+ "source size %E")),
exp, func, bndrng[0], size)
: warning_at (loc, opt,
- "%Kspecified bound %E exceeds "
- "source size %E",
+ (maybe
+ ? G_("%Kspecified bound %E may exceed "
+ "source size %E")
+ : G_("%Kspecified bound %E exceeds "
+ "source size %E")),
exp, bndrng[0], size));
else
warned = (func
? warning_at (loc, opt,
- "%K%qD specified bound [%E, %E] exceeds "
- "source size %E",
+ (maybe
+ ? G_("%K%qD specified bound [%E, %E] may "
+ "exceed source size %E")
+ : G_("%K%qD specified bound [%E, %E] exceeds "
+ "source size %E")),
exp, func, bndrng[0], bndrng[1], size)
: warning_at (loc, opt,
- "%Kspecified bound [%E, %E] exceeds "
- "source size %E",
+ (maybe
+ ? G_("%Kspecified bound [%E, %E] may exceed "
+ "source size %E")
+ : G_("%Kspecified bound [%E, %E] exceeds "
+ "source size %E")),
exp, bndrng[0], bndrng[1], size));
if (warned)
{
@@ -3623,28 +3775,41 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
return warned;
}
+ bool maybe = pad && pad->dst.phi ();
if (tree_int_cst_lt (maxobjsize, bndrng[0]))
{
if (bndrng[0] == bndrng[1])
warned = (func
? warning_at (loc, opt,
- "%K%qD specified size %E "
- "exceeds maximum object size %E",
+ (maybe
+ ? G_("%K%qD specified size %E may "
+ "exceed maximum object size %E")
+ : G_("%K%qD specified size %E "
+ "exceeds maximum object size %E")),
exp, func, bndrng[0], maxobjsize)
: warning_at (loc, opt,
- "%Kspecified size %E "
- "exceeds maximum object size %E",
+ (maybe
+ ? G_("%Kspecified size %E may exceed "
+ "maximum object size %E")
+ : G_("%Kspecified size %E exceeds "
+ "maximum object size %E")),
exp, bndrng[0], maxobjsize));
else
warned = (func
? warning_at (loc, opt,
- "%K%qD specified size between %E and %E "
- "exceeds maximum object size %E",
+ (maybe
+ ? G_("%K%qD specified size between %E and %E "
+ "may exceed maximum object size %E")
+ : G_("%K%qD specified size between %E and %E "
+ "exceeds maximum object size %E")),
exp, func,
bndrng[0], bndrng[1], maxobjsize)
: warning_at (loc, opt,
- "%Kspecified size between %E and %E "
- "exceeds maximum object size %E",
+ (maybe
+ ? G_("%Kspecified size between %E and %E "
+ "may exceed maximum object size %E")
+ : G_("%Kspecified size between %E and %E "
+ "exceeds maximum object size %E")),
exp, bndrng[0], bndrng[1], maxobjsize));
}
else if (!size || tree_int_cst_le (bndrng[0], size))
@@ -3652,22 +3817,34 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
warned = (func
? warning_at (loc, OPT_Wstringop_overflow_,
- "%K%qD specified bound %E exceeds "
- "destination size %E",
+ (maybe
+ ? G_("%K%qD specified bound %E may exceed "
+ "destination size %E")
+ : G_("%K%qD specified bound %E exceeds "
+ "destination size %E")),
exp, func, bndrng[0], size)
: warning_at (loc, OPT_Wstringop_overflow_,
- "%Kspecified bound %E exceeds "
- "destination size %E",
+ (maybe
+ ? G_("%Kspecified bound %E may exceed "
+ "destination size %E")
+ : G_("%Kspecified bound %E exceeds "
+ "destination size %E")),
exp, bndrng[0], size));
else
warned = (func
? warning_at (loc, OPT_Wstringop_overflow_,
- "%K%qD specified bound [%E, %E] exceeds "
- "destination size %E",
+ (maybe
+ ? G_("%K%qD specified bound [%E, %E] may exceed "
+ "destination size %E")
+ : G_("%K%qD specified bound [%E, %E] exceeds "
+ "destination size %E")),
exp, func, bndrng[0], bndrng[1], size)
: warning_at (loc, OPT_Wstringop_overflow_,
- "%Kspecified bound [%E, %E] exceeds "
- "destination size %E",
+ (maybe
+ ? G_("%Kspecified bound [%E, %E] exceeds "
+ "destination size %E")
+ : G_("%Kspecified bound [%E, %E] exceeds "
+ "destination size %E")),
exp, bndrng[0], bndrng[1], size));
if (warned)
@@ -3696,7 +3873,7 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
static bool
warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
- tree size, bool write, bool read)
+ tree size, bool write, bool read, bool maybe)
{
bool warned = false;
@@ -3705,40 +3882,64 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
if (tree_int_cst_equal (range[0], range[1]))
warned = (func
? warning_n (loc, opt, tree_to_uhwi (range[0]),
- "%K%qD accessing %E byte in a region "
- "of size %E",
- "%K%qD accessing %E bytes in a region "
- "of size %E",
+ (maybe
+ ? G_("%K%qD may access %E byte in a region "
+ "of size %E")
+ : G_("%K%qD accessing %E byte in a region "
+ "of size %E")),
+ (maybe
+ ? G_ ("%K%qD may access %E bytes in a region "
+ "of size %E")
+ : G_ ("%K%qD accessing %E bytes in a region "
+ "of size %E")),
exp, func, range[0], size)
: warning_n (loc, opt, tree_to_uhwi (range[0]),
- "%Kaccessing %E byte in a region "
- "of size %E",
- "%Kaccessing %E bytes in a region "
- "of size %E",
+ (maybe
+ ? G_("%Kmay access %E byte in a region "
+ "of size %E")
+ : G_("%Kaccessing %E byte in a region "
+ "of size %E")),
+ (maybe
+ ? G_("%Kmay access %E bytes in a region "
+ "of size %E")
+ : G_("%Kaccessing %E bytes in a region "
+ "of size %E")),
exp, range[0], size));
else if (tree_int_cst_sign_bit (range[1]))
{
/* Avoid printing the upper bound if it's invalid. */
warned = (func
? warning_at (loc, opt,
- "%K%qD accessing %E or more bytes in "
- "a region of size %E",
+ (maybe
+ ? G_("%K%qD may access %E or more bytes "
+ "in a region of size %E")
+ : G_("%K%qD accessing %E or more bytes "
+ "in a region of size %E")),
exp, func, range[0], size)
: warning_at (loc, opt,
- "%Kaccessing %E or more bytes in "
- "a region of size %E",
+ (maybe
+ ? G_("%Kmay access %E or more bytes "
+ "in a region of size %E")
+ : G_("%Kaccessing %E or more bytes "
+ "in a region of size %E")),
exp, range[0], size));
}
else
warned = (func
? warning_at (loc, opt,
- "%K%qD accessing between %E and %E bytes "
- "in a region of size %E",
+ (maybe
+ ? G_("%K%qD may access between %E and %E "
+ "bytes in a region of size %E")
+ : G_("%K%qD accessing between %E and %E "
+ "bytes in a region of size %E")),
exp, func, range[0], range[1],
size)
: warning_at (loc, opt,
- "%Kaccessing between %E and %E bytes "
- "in a region of size %E",
+ (maybe
+ ? G_("%Kmay access between %E and %E bytes "
+ "in a region of size %E")
+ : G_("%Kaccessing between %E and %E bytes "
+ "in a region of size %E")),
exp, range[0], range[1],
size));
return warned;
@@ -3749,44 +3950,69 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
if (tree_int_cst_equal (range[0], range[1]))
warned = (func
? warning_n (loc, opt, tree_to_uhwi (range[0]),
- "%K%qD writing %E byte into a region "
- "of size %E overflows the destination",
- "%K%qD writing %E bytes into a region "
- "of size %E overflows the destination",
+ (maybe
+ ? G_("%K%qD may write %E byte into a region "
+ "of size %E")
+ : G_("%K%qD writing %E byte into a region "
+ "of size %E overflows the destination")),
+ (maybe
+ ? G_("%K%qD may write %E bytes into a region "
+ "of size %E")
+ : G_("%K%qD writing %E bytes into a region "
+ "of size %E overflows the destination")),
exp, func, range[0], size)
: warning_n (loc, opt, tree_to_uhwi (range[0]),
- "%Kwriting %E byte into a region "
- "of size %E overflows the destination",
- "%Kwriting %E bytes into a region "
- "of size %E overflows the destination",
+ (maybe
+ ? G_("%Kmay write %E byte into a region "
+ "of size %E")
+ : G_("%Kwriting %E byte into a region "
+ "of size %E overflows the destination")),
+ (maybe
+ ? G_("%Kmay write %E bytes into a region "
+ "of size %E")
+ : G_("%Kwriting %E bytes into a region "
+ "of size %E overflows the destination")),
exp, range[0], size));
else if (tree_int_cst_sign_bit (range[1]))
{
/* Avoid printing the upper bound if it's invalid. */
warned = (func
? warning_at (loc, opt,
- "%K%qD writing %E or more bytes into "
- "a region of size %E overflows "
- "the destination",
+ (maybe
+ ? G_("%K%qD may write %E or more bytes "
+ "into a region of size %E "
+ "the destination")
+ : G_("%K%qD writing %E or more bytes "
+ "into a region of size %E overflows "
+ "the destination")),
exp, func, range[0], size)
: warning_at (loc, opt,
- "%Kwriting %E or more bytes into "
- "a region of size %E overflows "
- "the destination",
+ (maybe
+ ? G_("%Kmay write %E or more bytes into "
+ "a region of size %E")
+ : G_("%Kwriting %E or more bytes into "
+ "a region of size %E overflows "
+ "the destination")),
exp, range[0], size));
}
else
warned = (func
? warning_at (loc, opt,
- "%K%qD writing between %E and %E bytes "
- "into a region of size %E overflows "
- "the destination",
+ (maybe
+ ? G_("%K%qD may write between %E and %E bytes "
+ "into a region of size %E")
+ : G_("%K%qD writing between %E and %E bytes "
+ "into a region of size %E overflows "
+ "the destination")),
exp, func, range[0], range[1],
size)
: warning_at (loc, opt,
- "%Kwriting between %E and %E bytes "
- "into a region of size %E overflows "
- "the destination",
+ (maybe
+ ? G_("%Kmay write between %E and %E bytes "
+ "into a region of size %E")
+ : G_("%Kwriting between %E and %E bytes "
+ "into a region of size %E overflows "
+ "the destination")),
exp, range[0], range[1],
size));
return warned;
@@ -3798,35 +4024,64 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
warned = (func
? warning_n (loc, OPT_Wstringop_overread,
tree_to_uhwi (range[0]),
- "%K%qD reading %E byte from a region of size %E",
- "%K%qD reading %E bytes from a region of size %E", exp, func, range[0], size)
+ (maybe
+ ? G_("%K%qD may reade %E byte from a region "
+ "of size %E")
+ : G_("%K%qD reading %E byte from a region "
+ "of size %E")),
+ (maybe
+ ? G_("%K%qD may read %E bytes from a region "
+ "of size %E")
+ : G_("%K%qD reading %E bytes from a region "
+ "of size %E")),
+ exp, func, range[0], size)
: warning_n (loc, OPT_Wstringop_overread,
tree_to_uhwi (range[0]),
- "%Kreading %E byte from a region of size %E",
- "%Kreading %E bytes from a region of size %E",
+ (maybe
+ ? G_("%Kmay read %E byte from a region "
+ "of size %E")
+ : G_("%Kreading %E byte from a region "
+ "of size %E")),
+ (maybe
+ ? G_("%Kmay read %E bytes from a region "
+ "of size %E")
+ : G_("%Kreading %E bytes from a region "
+ "of size %E")),
exp, range[0], size));
else if (tree_int_cst_sign_bit (range[1]))
{
/* Avoid printing the upper bound if it's invalid. */
warned = (func
? warning_at (loc, OPT_Wstringop_overread,
- "%K%qD reading %E or more bytes from "
- "a region of size %E",
+ (maybe
+ ? G_("%K%qD may read %E or more bytes "
+ "from a region of size %E")
+ : G_("%K%qD reading %E or more bytes "
+ "from a region of size %E")),
exp, func, range[0], size)
: warning_at (loc, OPT_Wstringop_overread,
- "%Kreading %E or more bytes from a region "
- "of size %E",
+ (maybe
+ ? G_("%Kmay read %E or more bytes "
+ "from a region of size %E")
+ : G_("%Kreading %E or more bytes "
+ "from a region of size %E")),
exp, range[0], size));
}
else
warned = (func
? warning_at (loc, OPT_Wstringop_overread,
- "%K%qD reading between %E and %E bytes from "
- "a region of size %E",
+ (maybe
+ ? G_("%K%qD may read between %E and %E bytes "
+ "from a region of size %E")
+ : G_("%K%qD reading between %E and %E bytes "
+ "from a region of size %E")),
exp, func, range[0], range[1], size)
: warning_at (loc, opt,
- "%K reading between %E and %E bytes from "
- "a region of size %E",
+ (maybe
+ ? G_("%Kmay read between %E and %E bytes "
+ "from a region of size %E")
+ : G_("%Kreading between %E and %E bytes "
+ "from a region of size %E")),
exp, range[0], range[1], size));
if (warned)
@@ -3878,28 +4133,57 @@ 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.
+/* Issue one inform message describing each 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, access_mode mode)
+void
+access_ref::inform_access (access_mode mode) const
{
- if (!ref.ref)
+ const access_ref &aref = *this;
+ if (!aref.ref)
return;
+ if (aref.phi ())
+ {
+ /* Set MAXREF to refer to the largest object and fill ALL_REFS
+ with data for all objects referenced by the PHI arguments. */
+ access_ref maxref;
+ auto_vec<access_ref> all_refs;
+ if (!get_ref (&all_refs, &maxref))
+ return;
+
+ /* Except for MAXREF, the rest of the arguments' offsets need not
+ reflect one added to the PHI itself. Determine the latter from
+ MAXREF on which the result is based. */
+ const offset_int orng[] =
+ {
+ offrng[0] - maxref.offrng[0],
+ wi::smax (offrng[1] - maxref.offrng[1], offrng[0]),
+ };
+
+ /* Add the final PHI's offset to that of each of the arguments
+ and recurse to issue an inform message for it. */
+ for (unsigned i = 0; i != all_refs.length (); ++i)
+ {
+ all_refs[i].add_offset (orng[0], orng[1]);
+ all_refs[i].inform_access (mode);
+ }
+ return;
+ }
+
/* Convert offset range and avoid including a zero range since it
isn't necessarily meaningful. */
HOST_WIDE_INT diff_min = tree_to_shwi (TYPE_MIN_VALUE (ptrdiff_type_node));
HOST_WIDE_INT diff_max = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node));
HOST_WIDE_INT minoff;
HOST_WIDE_INT maxoff = diff_max;
- if (wi::fits_shwi_p (ref.offrng[0]))
- minoff = ref.offrng[0].to_shwi ();
+ if (wi::fits_shwi_p (aref.offrng[0]))
+ minoff = aref.offrng[0].to_shwi ();
else
- minoff = ref.offrng[0] < 0 ? diff_min : diff_max;
+ minoff = aref.offrng[0] < 0 ? diff_min : diff_max;
- if (wi::fits_shwi_p (ref.offrng[1]))
- maxoff = ref.offrng[1].to_shwi ();
+ if (wi::fits_shwi_p (aref.offrng[1]))
+ maxoff = aref.offrng[1].to_shwi ();
if (maxoff <= diff_min || maxoff >= diff_max)
/* Avoid mentioning an upper bound that's equal to or in excess
@@ -3909,110 +4193,127 @@ inform_access (const access_ref &ref, access_mode mode)
/* 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]))
+ if (wi::fits_shwi_p (aref.sizrng[0])
+ && wi::fits_shwi_p (aref.sizrng[1]))
{
- minsize = ref.sizrng[0].to_shwi ();
- maxsize = ref.sizrng[1].to_shwi ();
+ minsize = aref.sizrng[0].to_shwi ();
+ maxsize = aref.sizrng[1].to_shwi ();
}
+ /* SIZRNG doesn't necessarily have the same range as the allocation
+ size determined by gimple_call_alloc_size (). */
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);
+
+ char offstr[80];
+ if (minoff == 0
+ && (maxoff == 0 || aref.sizrng[1] <= maxoff))
+ offstr[0] = '\0';
+ else if (minoff == maxoff)
+ sprintf (offstr, "%lli", (long long) minoff);
+ else
+ sprintf (offstr, "[%lli, %lli]", (long long) minoff, (long long) maxoff);
- if (minsize == maxsize)
- sprintf (sizestr, "%llu", minsize);
- else
- sprintf (sizestr, "[%llu, %llu]", minsize, maxsize);
+ location_t loc = UNKNOWN_LOCATION;
+ tree ref = this->ref;
+ tree allocfn = NULL_TREE;
+ if (TREE_CODE (ref) == SSA_NAME)
+ {
+ gimple *stmt = SSA_NAME_DEF_STMT (ref);
+ if (is_gimple_call (stmt))
+ {
+ loc = gimple_location (stmt);
+ if (gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
+ {
+ /* Strip the SSA_NAME suffix from the variable name and
+ recreate an identifier with the VLA's original name. */
+ ref = gimple_call_lhs (stmt);
+ ref = SSA_NAME_IDENTIFIER (ref);
+ const char *id = IDENTIFIER_POINTER (ref);
+ size_t len = strcspn (id, ".$");
+ if (!len)
+ len = strlen (id);
+ ref = get_identifier_with_length (id, len);
+ }
+ else
+ {
+ /* Except for VLAs, retrieve the allocation function. */
+ allocfn = gimple_call_fndecl (stmt);
+ if (!allocfn)
+ allocfn = gimple_call_fn (stmt);
+ if (TREE_CODE (allocfn) == SSA_NAME)
+ {
+ /* For an ALLOC_CALL via a function pointer make a small
+ effort to determine the destination of the pointer. */
+ gimple *def = SSA_NAME_DEF_STMT (allocfn);
+ if (gimple_assign_single_p (def))
+ {
+ tree rhs = gimple_assign_rhs1 (def);
+ if (DECL_P (rhs))
+ allocfn = rhs;
+ else if (TREE_CODE (rhs) == COMPONENT_REF)
+ allocfn = TREE_OPERAND (rhs, 1);
+ }
+ }
+ }
+ }
+ else if (gimple_nop_p (stmt))
+ /* Handle DECL_PARM below. */
+ ref = SSA_NAME_VAR (ref);
}
- else if (DECL_P (ref.ref))
- loc = DECL_SOURCE_LOCATION (ref.ref);
- else if (EXPR_P (ref.ref) && EXPR_HAS_LOCATION (ref.ref))
- loc = EXPR_LOCATION (ref.ref);
- else
+
+ if (DECL_P (ref))
+ loc = DECL_SOURCE_LOCATION (ref);
+ else if (EXPR_P (ref) && EXPR_HAS_LOCATION (ref))
+ loc = EXPR_LOCATION (ref);
+ else if (TREE_CODE (ref) != IDENTIFIER_NODE
+ && TREE_CODE (ref) != SSA_NAME)
return;
if (mode == access_read_write || mode == access_write_only)
{
if (allocfn == NULL_TREE)
{
- if (minoff == maxoff)
- {
- if (minoff == 0)
- inform (loc, "destination object %qE", ref.ref);
- else
- inform (loc, "at offset %wi into destination object %qE",
- minoff, ref.ref);
- }
+ if (*offstr)
+ inform (loc, "at offset %s into destination object %qE of size %s",
+ offstr, ref, sizestr);
else
- inform (loc, "at offset [%wi, %wi] into destination object %qE",
- minoff, maxoff, ref.ref);
+ inform (loc, "destination object %qE of size %s", ref, sizestr);
return;
}
- if (minoff == maxoff)
- {
- if (minoff == 0)
- inform (loc, "destination object of size %s allocated by %qE",
- sizestr, allocfn);
- else
- inform (loc,
- "at offset %wi into destination object of size %s "
- "allocated by %qE", minoff, sizestr, allocfn);
- }
- else
+ if (*offstr)
inform (loc,
- "at offset [%wi, %wi] into destination object of size %s "
- "allocated by %qE",
- minoff, maxoff, sizestr, allocfn);
-
+ "at offset %s into destination object of size %s "
+ "allocated by %qE", offstr, sizestr, allocfn);
+ else
+ inform (loc, "destination object of size %s allocated by %qE",
+ sizestr, allocfn);
return;
}
- if (DECL_P (ref.ref))
+ if (DECL_P (ref))
{
- if (minoff == maxoff)
- {
- if (minoff == 0)
- inform (loc, "source object %qD", ref.ref);
- else
- inform (loc, "at offset %wi into source object %qD",
- minoff, ref.ref);
- }
+ if (*offstr)
+ inform (loc, "at offset %s into source object %qD of size %s",
+ offstr, ref, sizestr);
else
- inform (loc, "at offset [%wi, %wi] into source object %qD",
- minoff, maxoff, ref.ref);
+ inform (loc, "source object %qD of size %s", ref, sizestr);
+
return;
}
- if (minoff == maxoff)
- {
- if (minoff == 0)
- inform (loc, "source object of size %s allocated by %qE",
- sizestr, allocfn);
- else
- inform (loc,
- "at offset %wi into source object of size %s "
- "allocated by %qE", minoff, sizestr, allocfn);
- }
- else
+ if (*offstr)
inform (loc,
- "at offset [%wi, %wi] into source object of size %s "
- "allocated by %qE",
- minoff, maxoff, sizestr, allocfn);
+ "at offset %s into source object of size %s allocated by %qE",
+ offstr, sizestr, allocfn);
+ else
+ inform (loc, "source object of size %s allocated by %qE",
+ sizestr, allocfn);
}
/* Helper to set RANGE to the range of BOUND if it's nonnull, bounded
@@ -4232,17 +4533,18 @@ check_access (tree exp, tree dstwrite,
= mode == access_read_only || mode == access_read_write;
const bool write
= mode == access_write_only || mode == access_read_write;
+ const bool maybe = pad && pad->dst.parmarray;
warned = warn_for_access (loc, func, exp,
OPT_Wstringop_overflow_,
range, dstsize,
- write, read && !builtin);
+ write, read && !builtin, maybe);
}
if (warned)
{
TREE_NO_WARNING (exp) = true;
if (pad)
- inform_access (pad->dst, pad->mode);
+ pad->dst.inform_access (pad->mode);
}
/* Return error when an overflow has been detected. */
@@ -4325,12 +4627,13 @@ check_access (tree exp, tree dstwrite,
const bool read
= mode == access_read_only || mode == access_read_write;
+ const bool maybe = pad && pad->dst.parmarray;
if (warn_for_access (loc, func, exp, OPT_Wstringop_overread, range,
- slen, false, read))
+ slen, false, read, maybe))
{
TREE_NO_WARNING (exp) = true;
if (pad)
- inform_access (pad->src, access_read_only);
+ pad->src.inform_access (access_read_only);
}
return false;
}
@@ -4462,11 +4765,12 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
/* For an access to an object referenced to by the function parameter PTR
of pointer type, and set RNG[] to the range of sizes of the object
obtainedfrom the attribute access specification for the current function.
+ Set STATIC_ARRAY if the array parameter has been declared [static].
Return the function parameter on success and null otherwise. */
tree
gimple_parm_array_size (tree ptr, wide_int rng[2],
- range_query * /* = NULL */)
+ bool *static_array /* = NULL */)
{
/* For a function argument try to determine the byte size of the array
from the current function declaratation (e.g., attribute access or
@@ -4498,6 +4802,9 @@ gimple_parm_array_size (tree ptr, wide_int rng[2],
if (warn_array_parameter < 2 && !access->static_p)
return NULL_TREE;
+ if (static_array)
+ *static_array = access->static_p;
+
rng[0] = wi::zero (prec);
rng[1] = wi::uhwi (access->minsize, prec);
/* Multiply the array bound encoded in the attribute by the size
@@ -4645,6 +4952,84 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2],
return NULL_TREE;
}
+/* A helper of compute_objsize() to determine the size from an assignment
+ statement STMT with the RHS of either MIN_EXPR or MAX_EXPR. */
+
+static bool
+handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
+ bitmap *visited, range_query *rvals)
+{
+ tree_code code = gimple_assign_rhs_code (stmt);
+
+ tree ptr = gimple_assign_rhs1 (stmt);
+
+ /* In a valid MAX_/MIN_EXPR both operands must refer to the same array.
+ Determine the size/offset of each and use the one with more or less
+ space remaining, respectively. If either fails, use the information
+ determined from the other instead, adjusted up or down as appropriate
+ for the expression. */
+ access_ref aref[2] = { *pref, *pref };
+ if (!compute_objsize (ptr, ostype, &aref[0], visited, rvals))
+ {
+ aref[0].base0 = false;
+ aref[0].offrng[0] = aref[0].offrng[1] = 0;
+ aref[0].add_max_offset ();
+ aref[0].set_max_size_range ();
+ }
+
+ ptr = gimple_assign_rhs2 (stmt);
+ if (!compute_objsize (ptr, ostype, &aref[1], visited, rvals))
+ {
+ aref[1].base0 = false;
+ aref[1].offrng[0] = aref[1].offrng[1] = 0;
+ aref[1].add_max_offset ();
+ aref[1].set_max_size_range ();
+ }
+
+ if (!aref[0].ref && !aref[1].ref)
+ /* Fail if the identity of neither argument could be determined. */
+ return false;
+
+ bool i0 = false;
+ if (aref[0].ref && aref[0].base0)
+ {
+ if (aref[1].ref && aref[1].base0)
+ {
+ /* If the object referenced by both arguments has been determined
+ set *PREF to the one with more or less space remainng, whichever
+ is appopriate for CODE.
+ TODO: Indicate when the objects are distinct so it can be
+ diagnosed. */
+ i0 = code == MAX_EXPR;
+ const bool i1 = !i0;
+
+ if (aref[i0].size_remaining () < aref[i1].size_remaining ())
+ *pref = aref[i1];
+ else
+ *pref = aref[i0];
+ return true;
+ }
+
+ /* If only the object referenced by one of the arguments could be
+ determined, use it and... */
+ *pref = aref[0];
+ i0 = true;
+ }
+ else
+ *pref = aref[1];
+
+ const bool i1 = !i0;
+ /* ...see if the offset obtained from the other pointer can be used
+ to tighten up the bound on the offset obtained from the first. */
+ if ((code == MAX_EXPR && aref[i1].offrng[1] < aref[i0].offrng[0])
+ || (code == MIN_EXPR && aref[i0].offrng[0] < aref[i1].offrng[1]))
+ {
+ pref->offrng[0] = aref[i0].offrng[0];
+ pref->offrng[1] = aref[i0].offrng[1];
+ }
+ return true;
+}
+
/* 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).
@@ -4709,6 +5094,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
if (code == COMPONENT_REF)
{
tree ref = TREE_OPERAND (ptr, 0);
+ if (TREE_CODE (TREE_TYPE (ref)) == UNION_TYPE)
+ /* In accesses through union types consider the entire unions
+ rather than just their members. */
+ ostype = 0;
tree field = TREE_OPERAND (ptr, 1);
if (ostype == 0)
@@ -4963,8 +5352,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
of the array from the current function declaratation
(e.g., attribute access or related). */
wide_int wr[2];
- if (tree ref = gimple_parm_array_size (ptr, wr, rvals))
+ bool static_array = false;
+ if (tree ref = gimple_parm_array_size (ptr, wr, &static_array))
{
+ pref->parmarray = !static_array;
pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
pref->ref = ref;
@@ -4974,14 +5365,19 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
pref->set_max_size_range ();
pref->base0 = false;
pref->ref = ptr;
- if (tree var = SSA_NAME_VAR (ptr))
- if (TREE_CODE (var) == PARM_DECL)
- pref->ref = var;
-
return true;
}
- /* TODO: Handle PHI. */
+ if (gimple_code (stmt) == GIMPLE_PHI)
+ {
+ pref->ref = ptr;
+ access_ref phi_ref = *pref;
+ if (!pref->get_ref (NULL, &phi_ref, ostype, visited, rvals))
+ return false;
+ *pref = phi_ref;
+ pref->ref = ptr;
+ return true;
+ }
if (!is_gimple_assign (stmt))
{
@@ -4991,16 +5387,16 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
PREF->REF to it. */
pref->base0 = false;
pref->set_max_size_range ();
- if (tree var = SSA_NAME_VAR (ptr))
- if (TREE_CODE (var) == PARM_DECL)
- pref->ref = var;
return true;
}
- ptr = gimple_assign_rhs1 (stmt);
-
tree_code code = gimple_assign_rhs_code (stmt);
+ if (code == MAX_EXPR || code == MIN_EXPR)
+ return handle_min_max_size (stmt, ostype, pref, visited, rvals);
+
+ ptr = gimple_assign_rhs1 (stmt);
+
if (code == POINTER_PLUS_EXPR
&& TREE_CODE (TREE_TYPE (ptr)) == POINTER_TYPE)
{
diff --git a/gcc/builtins.h b/gcc/builtins.h
index c09f36da02b..4a2e34351be 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -153,6 +153,8 @@ extern void warn_string_no_nul (location_t, tree, const char *, tree,
extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
extern bool builtin_with_linkage_p (tree);
+class range_query;
+
/* Describes a reference to an object used in an access. */
struct access_ref
{
@@ -162,17 +164,12 @@ struct access_ref
is a constant zero. */
access_ref (tree = NULL_TREE, bool = false);
- /* Reference to the accessed object(s). */
- tree ref;
+ /* Return the PHI node REF refers to or null if it doesn't. */
+ gphi *phi () const;
- /* Range of byte offsets into and sizes of the object(s). */
- offset_int offrng[2];
- offset_int sizrng[2];
- /* Range of the bound of the access: denotes that the access
- is at least BNDRNG[0] bytes but no more than BNDRNG[1].
- For string functions the size of the actual access is
- further constrained by the length of the string. */
- offset_int bndrng[2];
+ /* Return the object to which REF refers. */
+ tree get_ref (vec<access_ref> *, access_ref * = NULL, int = 1,
+ bitmap * = NULL, range_query * = NULL) const;
/* Return true if OFFRNG is the constant zero. */
bool offset_zero () const
@@ -211,6 +208,22 @@ struct access_ref
add_offset (-maxoff - 1, maxoff);
}
+ /* Issue an informational message describing the target of an access
+ with the given mode. */
+ void inform_access (access_mode) const;
+
+ /* Reference to the accessed object(s). */
+ tree ref;
+
+ /* Range of byte offsets into and sizes of the object(s). */
+ offset_int offrng[2];
+ offset_int sizrng[2];
+ /* Range of the bound of the access: denotes that the access
+ is at least BNDRNG[0] bytes but no more than BNDRNG[1].
+ For string functions the size of the actual access is
+ further constrained by the length of the string. */
+ offset_int bndrng[2];
+
/* Used to fold integer expressions when called from front ends. */
tree (*eval)(tree);
/* Set if trailing one-element arrays should be treated as flexible
@@ -219,6 +232,9 @@ struct access_ref
/* Set if valid offsets must start at zero (for declared and allocated
objects but not for others referenced by pointers). */
bool base0;
+ /* Set if REF refers to a function array parameter not declared
+ static. */
+ bool parmarray;
};
/* Describes a pair of references used in an access by built-in
@@ -242,10 +258,9 @@ struct access_data
access_mode mode;
};
-class range_query;
extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
range_query * = NULL);
-extern tree gimple_parm_array_size (tree, wide_int[2], range_query * = NULL);
+extern tree gimple_parm_array_size (tree, wide_int[2], bool * = NULL);
extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
range_query * = NULL);
diff --git a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
index 7c7932e3cf0..676643168cc 100644
--- a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
+++ b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
@@ -10,7 +10,7 @@ void sink (void*);
struct Ax
{
char n;
- char a[]; // { dg-message "declared here" }
+ char a[]; // { dg-message "destination object" "note" }
};
// Verify warning for a definition with no initializer.
@@ -91,7 +91,7 @@ void gaxx (void)
struct A0
{
char n;
- char a[0]; // { dg-message "declared here" }
+ char a[0]; // { dg-message "destination object" "note" }
};
// Verify warning for a definition with no initializer.
@@ -158,7 +158,7 @@ void ga0x (void)
struct A1
{
char n;
- char a[1]; // { dg-message "declared here" }
+ char a[1]; // { dg-message "destination object" "note" }
};
// Verify warning for a definition with no initializer.
@@ -256,7 +256,7 @@ void ga1x (void)
struct A1i
{
char n;
- char a[1]; // { dg-message "declared here" }
+ char a[1]; // { dg-message "destination object" }
char x;
};
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-11.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-11.c
index f5dac458d1e..ec3c97e8102 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-11.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-11.c
@@ -72,7 +72,7 @@ void test_memset_array_range_cst_off (void)
{
T (SR (-7, 7), 1, 7);
T (SR (-1, 1), 1, 7);
- T (SR (-1, 1), 1, 9); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+ T (SR (-1, 1), 1, 9); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" } */
T (SR ( 1, 2), 1, 1);
T (SR ( 1, 2), 1, 5);
@@ -147,7 +147,7 @@ void test_memcpy_array_range_cst_off (const void *s)
{
T (SR (-7, 7), 1, 7);
T (SR (-1, 1), 1, 7);
- T (SR (-1, 1), 1, 9); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+ T (SR (-1, 1), 1, 9); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" } */
T (SR ( 1, 2), 1, 1);
T (SR ( 1, 2), 1, 5);
@@ -224,7 +224,7 @@ void test_strcpy_array_range_cst_off (const char *s)
{
T (SR (-7, 7), 1, 6);
T (SR (-1, 1), 1, 6);
- T (SR (-1, 1), 1, 8); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+ T (SR (-1, 1), 1, 8); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" } */
T (SR ( 1, 2), 1, 0);
T (SR ( 1, 2), 1, 1);
T (SR ( 1, 2), 1, 4);
@@ -290,7 +290,7 @@ void test_strncpy_array_range_cst_off (const char *s)
{
T (SR (-7, 7), 1, 7);
T (SR (-1, 1), 1, 7);
- T (SR (-1, 1), 1, 9); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+ T (SR (-1, 1), 1, 9); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" } */
T (SR ( 1, 2), 1, 1);
T (SR ( 1, 2), 1, 5);
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-12.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-12.c
index 1e67b5fd928..7c3dc8c0544 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-12.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-12.c
@@ -25,7 +25,9 @@ void test_memcpy_array_cst_range_off (const void *s)
T (d + UR (1, 2), 5);
T (d + UR (0, 1), 6);
- T (d + UR (0, 1), 7); /* { dg-warning ".memcpy. writing 6 bytes into a region of size 5 overflows the destination" "pr89428" { xfail *-*-* } } */
+ /* The warning below should be "writing" but the [0, 1] range
+ is somehow lost and get_range_info() returns VR_VARYING. */
+ T (d + UR (0, 1), 7); /* { dg-warning ".memcpy. writing 7 bytes into a region of size 6 overflows the destination" "pr89428" { xfail *-*-* } } */
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 " } */
@@ -48,7 +50,8 @@ void test_memcpy_array_range_range_off (const void *s)
char *d = ga7 + UR (0, 1);
T (d + SR (-1, 0), 1);
T (d + SR (-1, 0), 7);
- T (d + SR (-1, 0), 9); /* { dg-warning "writing 1 byte into a region of size 0 " "pr89350" { xfail *-*-* } } */
+ T (d + SR (-1, 0), 8); /* { dg-warning "writing 8 bytes into a region of size 7 " } */
+ T (d + SR (-1, 0), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " "pr89350" } */
}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-17.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-17.c
index fb81420c933..9c05d04f90c 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-17.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-17.c
@@ -13,7 +13,7 @@ void sink (void*);
void call_copy_n (const char *s)
{
- char a[7]; // { dg-message "declared here" }
+ char a[7]; // { dg-message "at offset 7 into destination object 'a'" }
copy_n (a, "1234567", 7);
sink (a);
}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
index 37c1ca29713..607c27989a3 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
@@ -261,8 +261,7 @@ void test_strcpy_warn (const char *s)
that the conversion from signed int to size_t doesn't prevent
the detection. */
int n = strlen (a);
- char *t = (char*)calloc (n, 1); // { dg-message "at offset 0 to an object with size 3 allocated by 'calloc' here" "calloc note 1" { xfail *-*-* } }
- // { dg-message "at offset 0 to an object with size at most 3 allocated by 'calloc' here" "calloc note 2" { target *-*-* } .-1 }
+ char *t = (char*)calloc (n, 1); // { dg-message "destination object of size 3 allocated by 'calloc'" "note" }
strcpy (t, a); // { dg-warning "writing 4 bytes into a region of size (between 0 and )?3 " }
sink (t);
@@ -271,8 +270,7 @@ void test_strcpy_warn (const char *s)
{
const char a[] = "1234";
size_t n = strlen (a);
- char *t = (char*)malloc (n); // { dg-message "at offset 0 to an object with size 4 allocated by 'malloc' here" "malloc note 1" { xfail *-*-* } }
- // { dg-message "at offset 0 to an object with size at most 4 allocated by 'malloc' here" "malloc note 2" { target *-*-* } .-1 }
+ char *t = (char*)malloc (n); // { dg-message "destination object of size 4 allocated by 'malloc'" "note" }
strcpy (t, a); // { dg-warning "writing 5 bytes into a region of size (between 0 and )?4 " }
sink (t);
}
@@ -280,14 +278,14 @@ void test_strcpy_warn (const char *s)
// Exercise PR middle-end/85484.
{
size_t len = strlen (s);
- char vla[len]; // { dg-message "at offset 0 to an object declared here" "vla note" }
+ char vla[len]; // { dg-message "destination object 'vla'" "vla note" }
strcpy (vla, s); // { dg-warning "writing one too many bytes into a region of a size that depends on 'strlen'" }
sink (vla);
}
{
size_t n = strlen (s);
- char *t = (char*)malloc (n); // { dg-message "at offset 0 to an object allocated by 'malloc' here" "malloc note" }
+ char *t = (char*)malloc (n); // { dg-message "allocated by 'malloc'" "malloc note" }
strcpy (t, s); // { dg-warning "writing one too many bytes into a region of a size that depends on 'strlen'" }
sink (t);
}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c
index be7f51ad3a5..5009fb5763a 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c
@@ -40,27 +40,27 @@ void same_size_and_offset_idx_cst (void)
const size_t n = UR (2, 3);
T (n, n, -4); // { dg-warning "writing 1 byte into a region of size 0" }
- // { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset \\\[-2, -1] into destination object of size \\\[2, 3] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
T (n, n, -3);
T (n, n, -2);
T (n, n, -1);
T (n, n, 0);
T (n, n, 1); // { dg-warning "writing 1 byte into a region of size 0" }
- // { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset 3 into destination object of size \\\[2, 3] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
}
{
const size_t n = UR (3, 4);
T (n, n, -5); // { dg-warning "writing 1 byte into a region of size 0" }
- // { dg-message "at offset \\\[-2, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset \\\[-2, -1] into destination object of size \\\[3, 4] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
T (n, n, -4);
T (n, n, -3);
T (n, n, -2);
T (n, n, -1);
T (n, n, 0);
T (n, n, 1); // { dg-warning "writing 1 byte into a region of size 0" }
- // { dg-message "at offset \\\[4, 5] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset 4 into destination object of size \\\[3, 4] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
}
{
@@ -84,15 +84,15 @@ void different_size_and_offset_idx_cst (void)
const size_t i = UR (1, 2);
T (n, i, -4); // { dg-warning "writing 1 byte into a region of size 0" }
- // { dg-message "at offset \\\[-3, -2] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset \\\[-3, -2] into destination object of size \\\[2, 3] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
T (n, i, -3); // { dg-warning "writing 1 byte into a region of size 0" }
- // { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset \\\[-2, -1] into destination object of size \\\[2, 3] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
T (n, i, -2);
T (n, i, -1);
T (n, i, 0);
T (n, i, 1);
T (n, i, 2); // { dg-warning "writing 1 byte into a region of size 0" }
- // { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset 3 into destination object of size \\\[2, 3] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
}
{
@@ -100,20 +100,20 @@ void different_size_and_offset_idx_cst (void)
const size_t i = UR (2, 5);
T (n, i, -6); // { dg-warning "writing 1 byte into a region of size 0" }
- // { dg-message "at offset \\\[-4, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset \\\[-4, -2] into destination object of size \\\[3, 4] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
- /* The offsets -5 and -4 are both necessarily invalid even if the sum
- (i - 5) and (i - 4) are (or could be) in bounds because they imply
- that the intermediate offset (p + i) is out of bounds. */
- T (n, i, -5); // { dg-warning "" "intermediate offset" { xfail *-*-* } }
- T (n, i, -4); // { dg-warning "" "intermediate offset" { xfail *-*-* } }
+ /* The offset -5 is necessarily invalid even if the sum (i - 5) is (or
+ could be) in bounds because it implies that the intermediate offset
+ (p + i) is out of bounds. */
+ T (n, i, -5); // { dg-warning "writing 1 byte into a region of size 0 " }
+ T (n, i, -4);
T (n, i, -3);
T (n, i, -2);
T (n, i, -1);
T (n, i, 0);
T (n, i, 1);
T (n, i, 2); // { dg-warning "writing 1 byte into a region of size 0" }
- // { dg-message "at offset \\\[4, 7] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset 4 into destination object of size \\\[3, 4] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
}
}
@@ -133,11 +133,8 @@ void different_size_and_offset_idx_var (void)
T (n, i, SR ( 0, 1));
T (n, i, SR ( 1, 2));
T (n, i, SR ( 2, 3));
- /* The warning is issued below but the offset and the size in
- the note are wrong. See the FIXME in compute_objsize(). */
T (n, i, SR ( 3, 4)); // { dg-warning "\\\[-Wstringop-overflow" }
- // { dg-message "at offset 4 to an object with size between 3 and 4 allocated by 'alloc1'" "pr92940 note: offset addition" { xfail *-*-* } .-1 }
- // { dg-message "at offset . to an object with size . allocated by 'alloc1'" "note: offset addition" { target *-*-* } .-2 }
+ // { dg-message "at offset 4 into destination object of size \\\[3, 4] allocated by 'alloc1'" "pr92940 note: offset addition" { target *-*-* } .-1 }
}
}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-29.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-29.c
index c011d05e89f..f13abbd7ca0 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-29.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-29.c
@@ -11,7 +11,7 @@ void sink (void*);
void direct_call (void)
{
- char *q = allocfn (0); // { dg-message "at offset 0 to an object with size 0 allocated by 'allocfn'" }
+ char *q = allocfn (0); // { dg-message "object of size 0 allocated by 'allocfn'" "note" }
q[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
sink (q);
}
@@ -20,7 +20,7 @@ void direct_call (void)
void local_ptr_call (void)
{
allocfn_t *ptr = allocfn;
- char *q = ptr (1); // { dg-message "at offset -1 to an object with size 1 allocated by 'allocfn'" }
+ char *q = ptr (1); // { dg-message "at offset -1 into destination object of size 1 allocated by 'allocfn'" "note" }
q[0] = 0;
q[-1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
sink (q);
@@ -32,7 +32,7 @@ void global_ptr_call (void)
extern allocfn_t *ptralloc;
allocfn_t *ptr = ptralloc;
- char *q = ptr (2); // { dg-message "at offset 3 to an object with size 2 allocated by 'ptralloc'" }
+ char *q = ptr (2); // { dg-message "at offset 3 into destination object of size 2 allocated by 'ptralloc'" "note" }
q[0] = 0;
q[1] = 1;
q[3] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
@@ -44,7 +44,7 @@ void global_ptr_array_call (void)
extern allocfn_t * (arralloc[]);
allocfn_t *ptr = arralloc[0];
- char *q = ptr (2); // { dg-message "at offset 3 to an object with size 2 allocated by 'ptr'" }
+ char *q = ptr (2); // { dg-message "at offset 3 into destination object of size 2 allocated by 'ptr'" "note" }
q[0] = 1;
q[1] = 2;
q[3] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
@@ -56,7 +56,7 @@ struct S { allocfn_t *ptralloc; };
void member_ptr_call (struct S *p)
{
- char *q = p->ptralloc (3); // { dg-message "at offset 5 to an object with size 3 allocated by 'ptralloc' here" }
+ char *q = p->ptralloc (3); // { dg-message "at offset 5 into destination object of size 3 allocated by 'ptralloc'" "note" }
q[0] = 0;
q[1] = 1;
q[2] = 2;
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c
index 46f8fed79f3..d9cf32d8784 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c
@@ -67,7 +67,7 @@ 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'" }
+ char *p = (char*)malloc (n); // { dg-message "at offset 1 into destination object of size \\\[3, 5] allocated by 'malloc'" "note" }
// 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;
@@ -89,7 +89,7 @@ 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'" }
+ char *p = (char*)usr_alloc (n, 3); // { dg-message "at offset 1 into destination object of size \\\[9, 15] allocated by 'usr_alloc'" "note" }
// 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;
@@ -179,67 +179,67 @@ void test_note (const char *s)
extern void sink (void*);
{
- char a[1][1][2]; // { dg-message "destination object" }
+ char a[1][1][2]; // { dg-message "destination object" "note" }
strncpy (a[0][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
sink (a);
}
{
- char a[1][1][2]; // { dg-message "at offset 2 into " }
+ char a[1][1][2]; // { dg-message "at offset 2 into " "note" }
strncpy (a[0][1], s, 3); // { dg-warning "writing 3 bytes into a region of size 0 " }
sink (a);
}
{
- char a[1][2][2]; // { dg-message "destination object" }
+ char a[1][2][2]; // { dg-message "destination object" "note" }
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 " }
+ char a[1][2][2]; // { dg-message "at offset 2 into " "note" }
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 " }
+ char a[1][2][2]; // { dg-message "at offset 4 into " "note" }
strncpy (a[1][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 0 " }
sink (a);
}
{
- char a[2][1][2]; // { dg-message "at offset 2 into " }
+ char a[2][1][2]; // { dg-message "at offset 2 into " "note" }
strncpy (a[0][1], s, 3); // { dg-warning "writing 3 bytes into a region of size 0 " }
sink (a);
}
{
- char a[2][1][2]; // { dg-message "at offset 2 into " }
+ char a[2][1][2]; // { dg-message "at offset 2 into " "note" }
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 " }
+ char a[2][2][3]; // { dg-message "at offset 9 into " "note" }
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 " }
+ char a[2][3][3]; // { dg-message "at offset 12 into " "note" }
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 " }
+ char a[2][3][3]; // { dg-message "at offset 12 into " "note" }
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 " }
+ char a[2][3][3]; // { dg-message "at offset 15 into " "note" }
strncpy (a[1][2], s, 7); // { dg-warning "writing 7 bytes into a region of size 3 " }
sink (a);
}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c
index a4d78b21cd1..b126fcbdcae 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c
@@ -53,7 +53,7 @@ void nowarn_memchr_anti_range_memset_cst (const void *s, size_t n)
void warn_memchr_cst_memset_cst (const void *s)
{
- char *p = malloc (4); // { dg-message "at offset \\\[0, 4] into destination object of size 4 " "note" }
+ char *p = malloc (4); // { dg-message "destination object of size 4 " "note" }
sink (p);
p = memchr (p, '1', 4);
@@ -62,7 +62,7 @@ void warn_memchr_cst_memset_cst (const void *s)
void warn_memchr_var_memset_cst (const void *s, unsigned n)
{
- char *p = malloc (4); // { dg-message "at offset \\\[0, 4] into destination object of size 4 " "note" }
+ char *p = malloc (4); // { dg-message "destination object of size 4 " "note" }
sink (p);
p = memchr (p, '1', n);
@@ -79,9 +79,9 @@ void warn_memchr_var_memset_range (const void *s, unsigned n)
as in the first two notes. The exact value probably isn't too
important. */
char *p0 = malloc (UR (5, 7));
- // { dg-message "at offset \\\[0, 7] into destination object of size \\\[5, 7]" "note" { target *-*-* } .-1 }
- // { dg-message "at offset \\\[1, 7] into destination object of size \\\[5, 7]" "note" { target *-*-* } .-2 }
- // { dg-message "at offset \\\[2, 7] into destination object of size \\\[5, 7]" "note" { target *-*-* } .-3 }
+ // { dg-message ": destination object of size \\\[5, 7]" "note 1" { target *-*-* } .-1 }
+ // { dg-message "at offset \\\[1, 7] into destination object of size \\\[5, 7]" "note 2" { target *-*-* } .-2 }
+ // { dg-message "at offset \\\[2, 7] into destination object of size \\\[5, 7]" "note 3" { target *-*-* } .-3 }
sink (p0);
char *p1 = memchr (p0, '1', n);
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c
index 02b14ee2eda..cb2c329aa84 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c
@@ -26,7 +26,7 @@ void nowarn_c32 (char c)
void warn_c32 (char c)
{
- extern char warn_a32[32]; // { dg-message "at offset 32 to object 'warn_a32' with size 32" }
+ extern char warn_a32[32]; // { dg-message "at offset 32 into destination object 'warn_a32' of size 32" "note" }
void *p = warn_a32 + 1;
*(C32*)p = (C32){ c }; // { dg-warning "writing 1 byte into a region of size 0" }
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-50.s b/gcc/testsuite/gcc.dg/Wstringop-overflow-50.s
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c
index 26568f8366d..f5929c9e7d6 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c
@@ -15,7 +15,7 @@ void sink (void*);
void char_flexarray_cst_off_cst_size (void)
{
extern struct { char n, a[]; }
- caxcc; // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'caxcc'" }
+ caxcc; // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'caxcc'" "note" }
char *p = caxcc.a;
size_t idx = DIFF_MAX - 4;
@@ -38,7 +38,7 @@ void char_flexarray_cst_off_cst_size (void)
void char_flexarray_var_off_cst_size (ptrdiff_t idx)
{
extern struct { char n, a[]; }
- caxvc; // { dg-message "destination object 'caxvc'" }
+ caxvc; // { dg-message "destination object 'caxvc'" "note" }
char *p = caxvc.a;
@@ -55,7 +55,7 @@ void char_flexarray_var_off_cst_size (ptrdiff_t idx)
void char_flexarray_var_off_var_size (size_t n, ptrdiff_t idx)
{
extern struct { char n, a[]; }
- caxvv; // { dg-message "destination object 'caxvv'" }
+ caxvv; // { dg-message "destination object 'caxvv'" "note" }
char *p = caxvv.a;
@@ -76,7 +76,7 @@ void char_flexarray_var_off_var_size (size_t n, ptrdiff_t idx)
void alloc_array_var_off_cst_size (size_t n, ptrdiff_t idx)
{
struct { char n, a[]; }
- *p = __builtin_malloc (n); // { dg-message "at offset \\d+ into destination object" }
+ *p = __builtin_malloc (n); // { dg-message "at offset \\d+ into destination object" "note" }
if (idx < DIFF_MAX - 4)
idx = DIFF_MAX - 4;
@@ -91,7 +91,7 @@ void alloc_array_var_off_cst_size (size_t n, ptrdiff_t idx)
void int_array_cst_off_cst_size (void)
{
extern struct { int n, a[]; }
- iaxc; // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'iaxc'" }
+ iaxc; // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'iaxc'" "note" }
int *p = iaxc.a;
size_t idx = DIFF_MAX / sizeof *p - 1;
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c
new file mode 100644
index 00000000000..b81186cfb94
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c
@@ -0,0 +1,260 @@
+/* PR middle-end/92936 - missing warning on a past-the-end store to a PHI
+ Exercise warnings for writing into one of two or more declared objects.
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX __INT_MAX__
+
+extern void* memset (void*, int, size_t);
+#define memset(d, c, n) sink (memset (d, c, n))
+
+void sink (int, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+volatile int cond1, cond2;
+
+extern char ca0[0], ca1[1], ca2[2], ca3[3], ca4[4],
+ ca5[5], ca6[6], ca7[7], ca8[8], ca9[9], cax[];
+
+#define CHOOSE_DECL_2(n1, n2) \
+ (cond1 ? ca ## n1 : ca ## n2)
+#define CHOOSE_DECL_3(n1, n2, n3) \
+ (cond1 < 0 ? ca ## n1 : 0 < cond1 ? ca ## n2 : ca ## n3)
+
+
+void memset_decl_2 (void)
+{
+ {
+ char *p0_1 = CHOOSE_DECL_2 (0, 1);
+
+ memset (p0_1, 0, 0);
+ /* Writing more than the smallest destination should trigger a "may
+ write" warning if the access is unconditionally reachable from
+ the block where the pointer to either object is assigned. */
+ memset (p0_1, 0, 1);
+ memset (p0_1, 0, 2); // { dg-warning "memset' writing 2 bytes into a region of size 1 " }
+ memset (p0_1, 0, 9); // { dg-warning "memset' writing 9 bytes into a region of size 1 " }
+ }
+
+ {
+ char *p0_x = CHOOSE_DECL_2 (0, x);
+
+ memset (p0_x, 0, 0);
+ memset (p0_x, 0, 1);
+ memset (p0_x, 0, 2);
+ memset (p0_x, 0, 9);
+ }
+
+ {
+ char *p3_5 = CHOOSE_DECL_2 (3, 5);
+
+ memset (p3_5, 0, 1);
+ memset (p3_5, 0, 3);
+ memset (p3_5, 0, 4);
+ memset (p3_5, 0, 5);
+ memset (p3_5, 0, 6); // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+ }
+
+ {
+ char *p5_3 = CHOOSE_DECL_2 (5, 3);
+
+ memset (p5_3, 0, 3);
+ memset (p5_3, 0, 4);
+ memset (p5_3, 0, 5);
+ memset (p5_3, 0, 6); // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+ }
+
+ {
+ char *px_3 = CHOOSE_DECL_2 (x, 3);
+
+ memset (px_3, 0, 1);
+ memset (px_3, 0, 3);
+ memset (px_3, 0, 4);
+ memset (px_3, 0, 1234);
+ }
+
+ {
+ char *p5_x = CHOOSE_DECL_2 (5, x);
+
+ memset (p5_x, 0, 1);
+ memset (p5_x, 0, 5);
+ memset (p5_x, 0, 6);
+ memset (p5_x, 0, 1234);
+ }
+
+}
+
+
+void memset_decl_3 (void)
+{
+ {
+ char *p0_1_2 = CHOOSE_DECL_3 (0, 1, 2);
+ memset (p0_1_2, 0, 0);
+ memset (p0_1_2, 0, 1);
+ memset (p0_1_2, 0, 2);
+ memset (p0_1_2, 0, 3); // { dg-warning "memset' writing 3 bytes into a region of size 2 " }
+ memset (p0_1_2, 0, 9); // { dg-warning "memset' writing 9 bytes into a region of size 2 " }
+ }
+
+ {
+ char *p0_2_x = CHOOSE_DECL_3 (0, 2, x);
+
+ memset (p0_2_x, 0, 0);
+ memset (p0_2_x, 0, 1);
+ memset (p0_2_x, 0, 3);
+ memset (p0_2_x, 0, 9);
+ }
+
+ {
+ char *p3_4_5 = CHOOSE_DECL_3 (3, 4, 5);
+
+ memset (p3_4_5, 0, 3);
+ memset (p3_4_5, 0, 4);
+ memset (p3_4_5, 0, 5);
+ memset (p3_4_5, 0, 6); // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+ }
+
+ {
+ char *p5_3_4 = CHOOSE_DECL_3 (5, 3, 4);
+
+ memset (p5_3_4, 0, 3);
+ memset (p5_3_4, 0, 4);
+ memset (p5_3_4, 0, 5);
+ memset (p5_3_4, 0, 6); // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+ }
+
+ {
+ char *p9_8_7 = CHOOSE_DECL_3 (9, 8, 7);
+
+ memset (p9_8_7, 0, 7);
+ memset (p9_8_7, 0, 8);
+ memset (p9_8_7, 0, 9);
+ memset (p9_8_7, 0, 10); // { dg-warning "memset' writing 10 bytes into a region of size 9 " }
+ }
+}
+
+
+/* Verify conditionally writing into one of two objects with the same
+ size. */
+
+void memset_decl_2_same_size (int i)
+{
+ {
+ char a4_1[4], a4_2[4];
+ char *p4 = cond1 ? a4_1 : a4_2;
+
+ memset (p4, 0, 1);
+ memset (p4, 0, 2);
+ memset (p4, 0, 3);
+ memset (p4, 0, 4);
+ memset (p4, 0, 5); // { dg-warning "memset' writing 5 bytes into a region of size 4" }
+ }
+
+ {
+ char a4_1[4]; // { dg-message "destination object 'a4_1" "note" }
+ char a4_2[4]; // { dg-message "destination object 'a4_2" "note" }
+ char *p4 = cond1 ? a4_1 : a4_2;
+ char *p4_i = p4 + i;
+
+ memset (p4_i, 0, 5); // { dg-warning "memset' writing 5 bytes into a region of size 4" }
+ }
+
+ {
+ if (i < 1)
+ i = 1;
+
+ char a4_1[4]; // { dg-message "at offset \\\[1, 4] into destination object 'a4_1" "note" }
+ char a4_2[4]; // { dg-message "at offset \\\[1, 4] into destination object 'a4_2" "note" }
+ char *p4 = cond1 ? a4_1 : a4_2;
+ char *p4_i = p4 + i;
+
+ memset (p4_i, 0, 3);
+ memset (p4_i, 0, 4); // { dg-warning "memset' writing 4 bytes into a region of size 3 " }
+ }
+}
+
+
+void memset_decl_2_off (void)
+{
+ int i1 = SR (1, INT_MAX);
+ int i2 = SR (2, INT_MAX);
+
+ {
+ char a5[5]; // { dg-warning "at offset [1, 5] into destination object 'a5'
+ char a7[7]; // { dg-warning "at offset [2, 7] into destination object 'a7'
+ char *p5_p1 = a5 + i1;
+ char *p7_p2 = a7 + i2;
+ char *p5_7 = cond1 ? p5_p1 : p7_p2;
+
+ memset (p5_7, 0, 1);
+ memset (p5_7, 0, 2);
+ memset (p5_7, 0, 3);
+ memset (p5_7, 0, 4);
+ memset (p5_7, 0, 5);
+ memset (p5_7, 0, 6); // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+ }
+
+ int i3 = SR (3, INT_MAX);
+
+ {
+ char a5[5];
+ // { dg-message "at offset \\\[3, 5] into destination object 'a5'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset \\\[2, 5] into destination object 'a5'" "note" { target *-*-* } .-2 }
+ // { dg-message "at offset \\\[1, 5] into destination object 'a5'" "note" { target *-*-* } .-3 }
+ // { dg-message ": destination object 'a5'" "note" { target *-*-* } .-4 }
+ char a9[9];
+ // { dg-message "at offset \\\[4, 9] into destination object 'a9'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset \\\[3, 9] into destination object 'a9'" "note" { target *-*-* } .-2 }
+ // { dg-message "at offset \\\[2, 9] into destination object 'a9'" "note" { target *-*-* } .-3 }
+ // { dg-message ": destination object 'a9'" "note" { target *-*-* } .-4 }
+ char *p5_p2 = a5 + i2; // 3 bytes left
+ char *p9_p3 = a9 + i3; // 6 bytes left
+ char *p =
+ cond1 ? p5_p2 : p9_p3; // [3 - 6] bytes left
+ char *q = p + i1; // [2 - 5] bytes left
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3);
+ memset (q, 0, 4);
+ memset (q, 0, 5);
+ memset (q, 0, 6); // { dg-warning "memset' writing 6 bytes into a region of size 5" }
+
+ --q; // [3 - 6] bytes left
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3);
+ memset (q, 0, 4);
+ memset (q, 0, 5);
+ memset (q, 0, 6);
+ memset (q, 0, 7); // { dg-warning "memset' writing 7 bytes into a region of size 6" }
+
+ --q; // [4 - 7] bytes left
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3);
+ memset (q, 0, 4);
+ memset (q, 0, 5);
+ memset (q, 0, 6);
+ memset (q, 0, 7);
+ memset (q, 0, 8); // { dg-warning "memset' writing 8 bytes into a region of size 7" }
+
+ int m1_x = SR (-1, INT_MAX);
+ int m2_x = SR (-2, INT_MAX);
+
+ q += cond2 ? m1_x : m2_x; // [5 - 9] bytes left
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3);
+ memset (q, 0, 4);
+ memset (q, 0, 5);
+ memset (q, 0, 6);
+ memset (q, 0, 7);
+ memset (q, 0, 8);
+ memset (q, 0, 9);
+ memset (q, 0, 10); // { dg-warning "memset' writing 10 bytes into a region of size 9" }
+ }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c
new file mode 100644
index 00000000000..c45a92d21e1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c
@@ -0,0 +1,267 @@
+/* PR middle-end/92936 - missing warning on a past-the-end store to a PHI
+ Exercise warnings for writing into one of two or more allocated objects.
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX __INT_MAX__
+
+extern void* malloc (size_t);
+extern void* memset (void*, int, size_t);
+#define memset(d, c, n) sink (memset (d, c, n))
+
+void sink (int, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+volatile int cond1, cond2, x;
+
+#define CHOOSE_MALLOC_2(n1, n2) \
+ (cond1 ? malloc (n1) : malloc (n2))
+#define CHOOSE_MALLOC_3(n1, n2, n3) \
+ (cond1 < 0 ? malloc (n1) : 0 < cond1 ? malloc (n2) : malloc (n3))
+
+
+void memset_malloc_2 (void)
+{
+ {
+ char *p0_1 = CHOOSE_MALLOC_2 (0, 1);
+
+ memset (p0_1, 0, 0);
+ /* Writing more than the smallest destination should trigger a "may
+ write" warning if the access is unconditionally reachable from
+ the block where the pointer to either object is assigned. */
+ memset (p0_1, 0, 1);
+ memset (p0_1, 0, 2); // { dg-warning "memset' writing 2 bytes into a region of size 1 " }
+ memset (p0_1, 0, 9); // { dg-warning "memset' writing 9 bytes into a region of size 1 " }
+ }
+
+ {
+ char *p0_x = CHOOSE_MALLOC_2 (0, x);
+
+ memset (p0_x, 0, 0);
+ memset (p0_x, 0, 1);
+ memset (p0_x, 0, 2);
+ memset (p0_x, 0, 12345);
+ }
+
+ {
+ char *px_x = CHOOSE_MALLOC_2 (x, x);
+
+ memset (px_x, 0, 0);
+ memset (px_x, 0, 1);
+ memset (px_x, 0, 2);
+ memset (px_x, 0, 12345);
+ }
+
+ {
+ char *p3_5 = CHOOSE_MALLOC_2 (3, 5);
+
+ memset (p3_5, 0, 1);
+ memset (p3_5, 0, 3);
+ memset (p3_5, 0, 4);
+ memset (p3_5, 0, 5);
+ memset (p3_5, 0, 6); // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+ }
+
+ {
+ char *p5_3 = CHOOSE_MALLOC_2 (5, 3);
+
+ memset (p5_3, 0, 3);
+ memset (p5_3, 0, 4);
+ memset (p5_3, 0, 5);
+ memset (p5_3, 0, 6); // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+ }
+
+ {
+ char *px_3 = CHOOSE_MALLOC_2 (x, 3);
+
+ memset (px_3, 0, 1);
+ memset (px_3, 0, 3);
+ memset (px_3, 0, 4);
+ memset (px_3, 0, 1234);
+ }
+
+ {
+ char *p5_x = CHOOSE_MALLOC_2 (5, x);
+
+ memset (p5_x, 0, 1);
+ memset (p5_x, 0, 5);
+ memset (p5_x, 0, 6);
+ memset (p5_x, 0, 1234);
+ }
+
+}
+
+
+void memset_malloc_3 (void)
+{
+ {
+ char *p0_1_2 = CHOOSE_MALLOC_3 (0, 1, 2);
+ memset (p0_1_2, 0, 0);
+ memset (p0_1_2, 0, 1);
+ memset (p0_1_2, 0, 2);
+ memset (p0_1_2, 0, 3); // { dg-warning "memset' writing 3 bytes into a region of size 2 " }
+ memset (p0_1_2, 0, 9); // { dg-warning "memset' writing 9 bytes into a region of size 2 " }
+ }
+
+ {
+ char *p0_2_x = CHOOSE_MALLOC_3 (0, 2, x);
+
+ memset (p0_2_x, 0, 0);
+ memset (p0_2_x, 0, 1);
+ memset (p0_2_x, 0, 3);
+ memset (p0_2_x, 0, 9);
+ }
+
+ {
+ char *p3_4_5 = CHOOSE_MALLOC_3 (3, 4, 5);
+
+ memset (p3_4_5, 0, 3);
+ memset (p3_4_5, 0, 4);
+ memset (p3_4_5, 0, 5);
+ memset (p3_4_5, 0, 6); // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+ }
+
+ {
+ char *p5_3_4 = CHOOSE_MALLOC_3 (5, 3, 4);
+
+ memset (p5_3_4, 0, 3);
+ memset (p5_3_4, 0, 4);
+ memset (p5_3_4, 0, 5);
+ memset (p5_3_4, 0, 6); // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+ }
+
+ {
+ char *p9_8_7 = CHOOSE_MALLOC_3 (9, 8, 7);
+
+ memset (p9_8_7, 0, 7);
+ memset (p9_8_7, 0, 8);
+ memset (p9_8_7, 0, 9);
+ memset (p9_8_7, 0, 10); // { dg-warning "memset' writing 10 bytes into a region of size 9 " }
+ }
+}
+
+
+/* Verify conditionally writing into one of two objects with the same
+ size. */
+
+void memset_malloc_2_same_size (int i)
+{
+ {
+ char a4_1[4], a4_2[4];
+ char *p4 = cond1 ? a4_1 : a4_2;
+
+ memset (p4, 0, 1);
+ memset (p4, 0, 2);
+ memset (p4, 0, 3);
+ memset (p4, 0, 4);
+ memset (p4, 0, 5); // { dg-warning "memset' writing 5 bytes into a region of size 4" }
+ }
+
+ {
+ char a4_1[4]; // { dg-message "destination object 'a4_1" "note" }
+ char a4_2[4]; // { dg-message "destination object 'a4_2" "note" }
+ char *p4 = cond1 ? a4_1 : a4_2;
+ char *p4_i = p4 + i;
+
+ memset (p4_i, 0, 5); // { dg-warning "memset' writing 5 bytes into a region of size 4" }
+ }
+
+ {
+ if (i < 1)
+ i = 1;
+
+ char a4_1[4]; // { dg-message "at offset \\\[1, 4] into destination object 'a4_1" "note" }
+ char a4_2[4]; // { dg-message "at offset \\\[1, 4] into destination object 'a4_2" "note" }
+ char *p4 = cond1 ? a4_1 : a4_2;
+ char *p4_i = p4 + i;
+
+ memset (p4_i, 0, 3);
+ memset (p4_i, 0, 4); // { dg-warning "memset' writing 4 bytes into a region of size 3 " }
+ }
+}
+
+
+void memset_malloc_2_off (void)
+{
+ int i1 = SR (1, INT_MAX);
+ int i2 = SR (2, INT_MAX);
+
+ {
+ char a5[5]; // { dg-warning "at offset [1, 5] into destination object 'a5'
+ char a7[7]; // { dg-warning "at offset [2, 7] into destination object 'a7'
+ char *p5_p1 = a5 + i1;
+ char *p7_p2 = a7 + i2;
+ char *p5_7 = cond1 ? p5_p1 : p7_p2;
+
+ memset (p5_7, 0, 1);
+ memset (p5_7, 0, 2);
+ memset (p5_7, 0, 3);
+ memset (p5_7, 0, 4);
+ memset (p5_7, 0, 5);
+ memset (p5_7, 0, 6); // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+ }
+
+ int i3 = SR (3, INT_MAX);
+
+ {
+ char a5[5];
+ // { dg-message "at offset \\\[3, 5] into destination object 'a5'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset \\\[2, 5] into destination object 'a5'" "note" { target *-*-* } .-2 }
+ // { dg-message "at offset \\\[1, 5] into destination object 'a5'" "note" { target *-*-* } .-3 }
+ // { dg-message ": destination object 'a5'" "note" { target *-*-* } .-4 }
+ char a9[9];
+ // { dg-message "at offset \\\[4, 9] into destination object 'a9'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset \\\[3, 9] into destination object 'a9'" "note" { target *-*-* } .-2 }
+ // { dg-message "at offset \\\[2, 9] into destination object 'a9'" "note" { target *-*-* } .-3 }
+ // { dg-message ": destination object 'a9'" "note" { target *-*-* } .-4 }
+ char *p5_p2 = a5 + i2; // 3 bytes left
+ char *p9_p3 = a9 + i3; // 6 bytes left
+ char *p =
+ cond1 ? p5_p2 : p9_p3; // [3 - 6] bytes left
+ char *q = p + i1; // [2 - 5] bytes left
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3);
+ memset (q, 0, 4);
+ memset (q, 0, 5);
+ memset (q, 0, 6); // { dg-warning "memset' writing 6 bytes into a region of size 5" }
+
+ --q; // [3 - 6] bytes left
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3);
+ memset (q, 0, 4);
+ memset (q, 0, 5);
+ memset (q, 0, 6);
+ memset (q, 0, 7); // { dg-warning "memset' writing 7 bytes into a region of size 6" }
+
+ --q; // [4 - 7] bytes left
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3);
+ memset (q, 0, 4);
+ memset (q, 0, 5);
+ memset (q, 0, 6);
+ memset (q, 0, 7);
+ memset (q, 0, 8); // { dg-warning "memset' writing 8 bytes into a region of size 7" }
+
+ int m1_x = SR (-1, INT_MAX);
+ int m2_x = SR (-2, INT_MAX);
+
+ q += cond2 ? m1_x : m2_x; // [5 - 9] bytes left
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3);
+ memset (q, 0, 4);
+ memset (q, 0, 5);
+ memset (q, 0, 6);
+ memset (q, 0, 7);
+ memset (q, 0, 8);
+ memset (q, 0, 9);
+ memset (q, 0, 10); // { dg-warning "memset' writing 10 bytes into a region of size 9" }
+ }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-60.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-60.c
new file mode 100644
index 00000000000..8c9de20e9bc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-60.c
@@ -0,0 +1,72 @@
+/* Test derived from Glibc's getifaddrs_internal. The code could be
+ rewritten to avoid the warning for the memcpy call but since unions
+ are designed to have their members treated as interchangeable there
+ isn't a whole lot to be gained from issuing one.
+ { dg-do compile }
+ { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void*, const void*, size_t);
+
+struct sockaddr
+{
+ short sa_family;
+ char sa_data[14];
+};
+
+struct in_addr
+{
+ int s_addr;
+};
+
+struct in6_addr
+{
+ union
+ {
+ char __u6_addr8[16];
+ short __u6_addr16[8];
+ int __u6_addr32[4];
+ } __in6_u;
+};
+
+struct sockaddr_in
+{
+ short sin_family;
+ short sin_port;
+ struct in_addr sin_addr;
+ unsigned char sin_zero[sizeof (struct sockaddr) -
+ (sizeof (short)) -
+ sizeof (short) -
+ sizeof (struct in_addr)];
+};
+
+struct sockaddr_in6
+{
+ short sin6_family;
+ short sin6_port;
+ int sin6_flowinfo;
+ struct in6_addr sin6_addr;
+ int sin6_scope_id;
+};
+
+union
+{
+ struct sockaddr sa;
+ struct sockaddr_in s4;
+ struct sockaddr_in6 s6;
+} u1, u2;
+
+struct sockaddr *sa;
+
+void test_unconditional (void *p)
+{
+ sa = &u1.sa;
+ memcpy (&((struct sockaddr_in6 *) sa)->sin6_addr, p, 16);
+}
+
+void test_conditional (void *p, int i)
+{
+ sa = i ? &u1.sa : &u2.sa;
+ memcpy (&((struct sockaddr_in6 *) sa)->sin6_addr, p, 16);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-61.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-61.c
new file mode 100644
index 00000000000..7601679fac3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-61.c
@@ -0,0 +1,88 @@
+/* { dg-do compile }
+ { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* malloc (size_t);
+void* memcpy (void*, const void*, size_t);
+size_t strlen (const char *);
+
+// Test case reduced from gcc/attribs.c.
+
+char* sorted_attr_string (char *argv[])
+{
+ size_t n = 0;
+ unsigned int i;
+
+ for (i = 0; argv[i]; ++i)
+ n += strlen (argv[i]);
+
+ char *s = (char*)malloc (n);
+ n = 0;
+ for (i = 0; argv[i]; ++i)
+ {
+ const char *str = argv[i];
+ size_t len = strlen (str);
+ memcpy (s + n, str, len);
+ n += len + 1;
+ }
+
+ /* Replace "=,-" with "_". */
+ for (i = 0; i < strlen (s); i++)
+ if (s[i] == '=')
+ s[i] = '_'; // { dg-bogus "\\\[-Wstringop-overflow" }
+
+ return s;
+}
+
+
+void f (void*);
+
+void nowarn_cond_escape (int c, int *x)
+{
+ extern char a3[3], a5[5];
+
+ char *p;
+ if (c)
+ {
+ p = a3;
+ *x = 2;
+ }
+ else
+ {
+ p = a5;
+ *x = 4;
+ }
+
+ f (p); // may modify *x
+
+ if (*x == 2)
+ p[2] = 0;
+ else if (*x == 4)
+ p[4] = 0; // { dg-bogus "\\\[-Wstringop-overflow" }
+}
+
+void warn_cond_escape (int c, int *x)
+{
+ extern char a3_2[3];
+ extern char a5_2[5]; // { dg-message "at offset 5 into destination object 'a5_2'" }
+
+ char *p;
+ if (c)
+ {
+ p = a3_2;
+ *x = 2;
+ }
+ else
+ {
+ p = a5_2;
+ *x = 5;
+ }
+
+ f (p); // may modify *x
+
+ if (*x == 2)
+ p[2] = 0;
+ else if (*x == 5)
+ p[5] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c
new file mode 100644
index 00000000000..318d9bd1f94
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c
@@ -0,0 +1,363 @@
+/* Test for MIN and MAX expressions involving pointers.
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX __INT_MAX__
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) ((x) < (y) ? (y) : (x))
+
+typedef __SIZE_TYPE__ size_t;
+
+void* memset (void*, int, size_t);
+#define memset(...) sink (memset (__VA_ARGS__))
+
+void sink (void*, ...);
+
+volatile int cond, vi;
+char* volatile ptr;
+
+void test_min (void)
+{
+ const int i1 = SR (1, INT_MAX);
+ const int i2 = SR (2, INT_MAX);
+
+ {
+ /* Exercise both pointers pointing to a different unknown object plus
+ positive constant offset. Since PTR is volatile P1 and P2 cannot
+ normally be considered to point to the same object. It can only
+ be inferred from the MIN expression. */
+ char *p1 = ptr + 1;
+ char *p2 = ptr + 2;
+
+ char *q = MIN (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, INT_MAX);
+ // { dg-warning "writing 2147483647 bytes into a region of size 2147483646" "ilp32" { target ilp32 } .-1 }
+ memset (q, 0, DIFF_MAX - 2);
+ memset (q, 0, DIFF_MAX);
+ // { dg-warning "writing 2147483647 bytes into a region of size 2147483646" "ilp32" { target ilp32 } .-1 }
+ // { dg-warning "writing 9223372036854775807 bytes into a region of size 9223372036854775806" "lp64" { target lp64 } .-2 }
+ }
+
+ {
+ /* Exercise both pointers pointing to a different unknown object plus
+ variable offset. */
+ char *p1 = ptr + vi;
+ char *p2 = ptr + vi;
+
+ char *q = MIN (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, INT_MAX);
+ }
+
+ {
+ /* Exercise both pointers pointing to the same object plus constant
+ offset. */
+ char a2[2]; // { dg-message "at offset 1 into destination object 'a2' of size 2" "note" }
+ char *p1 = a2 + 1;
+ char *p2 = a2 + 2;
+
+ char *q = MIN (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2); // { dg-warning "writing 2 bytes into a region of size 1 " }
+ }
+
+ {
+ /* Exercise both pointers pointing to the same object plus offset
+ in a known range. */
+ char a3[3]; // { dg-message "at offset \\\[1, 3] into destination object 'a3'" "note" }
+ char *pi = a3 + i1;
+ char *pj = a3 + i2;
+
+ char *q = MIN (pi, pj);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
+ }
+
+ {
+ /* Exercise both pointers pointing to the same object plus variable
+ offset. Verify that no offset is mentioned in the note (since
+ its unknown, printing its full range is unnecessary). */
+ char a4[4]; // { dg-message ": destination object 'a4'" "note" }
+ char *pi = a4 + vi;
+ char *pj = a4 + vi;
+
+ char *q = MIN (pi, pj);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3);
+ memset (q, 0, 4);
+ memset (q, 0, 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
+ }
+
+ {
+ /* Exercise a pointer pointing to a known object with one pointing
+ to an unknown object. */
+ char a5[5]; // { dg-message ": destination object 'a5'" "note" }
+ char *p = ptr;
+ char *q = MIN (p, a5);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 5);
+ memset (q, 0, 6); // { dg-warning "writing 6 bytes into a region of size 5 " }
+ }
+
+ {
+ /* Exercise a pointer pointing to a known object plus constant offset
+ with one pointing to an unknown object. */
+ char a6[6]; // { dg-message ": destination object 'a6'" "note" }
+ char *p1 = ptr;
+ char *p2 = a6 + 1;
+ char *q = MIN (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 6);
+ memset (q, 0, 7); // { dg-warning "writing 7 bytes into a region of size 6 " }
+ }
+
+ {
+ /* Exercise a pointer pointing to a known object with one pointing
+ to an unknown object plus constant offset. */
+ char a7[7]; // { dg-message ": destination object 'a7'" "note" }
+ char *p1 = a7;
+ char *p2 = ptr + 1;
+ /* Since p1 points to a7[0] it must be less than any pointer to a7
+ plus positive offset, and so Q == P1. */
+ char *q = MIN (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3);
+ memset (q, 0, 7);
+ memset (q, 0, 8); // { dg-warning "writing 8 bytes into a region of size 7 " }
+ }
+
+ {
+ /* Exercise a pointer pointing to a known object plus constant offset
+ with one pointing to an unknown object plus a different constant
+ offset. */
+ char a8[8]; // { dg-message "at offset 1 into destination object 'a8'" "note" }
+ char *p1 = a8 + 1;
+ char *p2 = ptr + 2;
+ /* Since P1 points to A8[1] it must be less than or equal to any
+ pointer to A8 plus positive offset. Either way, Q must point
+ to A8[1]. */
+ char *q = MIN (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 7);
+ memset (q, 0, 8); // { dg-warning "writing 8 bytes into a region of size 7 " }
+ }
+
+ {
+ /* Same as above but with larger offsets. */
+ char a9[9]; // { dg-message "at offset 3 into destination object 'a9'" "note" }
+ char *p1 = a9 + 3;
+ char *p2 = ptr + 4;
+ /* Since P1 points to A9[3] it must be less than or equal to any
+ pointer anywhere into A9 plus 4, so Q must point to A9[3]. */
+ char *q = MIN (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 6);
+ memset (q, 0, 7); // { dg-warning "writing 7 bytes into a region of size 6 " }
+ }
+
+ {
+ /* Same as above but with the offsets reversed. */
+ char a10[10]; // { dg-message "at offset 5 into destination object 'a10'" "note" }
+ char *p1 = a10 + 10;
+ char *p2 = ptr + 5;
+ /* Since P1 points just past the end of A10 it could be either less
+ or equal to another pointer anywhere into A10 plus 3 because
+ the other pointer itself could start at a non-zero offset that's
+ not reflected in the determined offset). */
+ char *q = MIN (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 5);
+ memset (q, 0, 6); // { dg-warning "writing 6 bytes into a region of size 5 " }
+ }
+
+ {
+ char a3[3]; // { dg-message ": destination object 'a3'" "note" }
+ char *p1 = ptr;
+ char *p2 = a3 + i1;
+ char *q = MIN (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3);
+ memset (q, 0, 4); // { dg-warning "writing 4 bytes into a region of size 3 " }
+ }
+}
+
+
+void test_max (void)
+{
+ const int i1 = SR (1, INT_MAX);
+ const int i2 = SR (2, INT_MAX);
+
+ {
+ /* Exercise both pointers pointing to the same object plus constant
+ offset. */
+ char a2[2]; // { dg-message "at offset 1 into destination object 'a2' of size 2" "note" }
+ char *pi = a2 + 1;
+ char *pj = a2 + 2;
+
+ char *q = MAX (pi, pj);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2); // { dg-warning "writing 2 bytes into a region of size 1 " }
+ }
+
+ {
+ /* Exercise both pointers pointing to the same object plus offset
+ in a known range. */
+ char a3[3]; // { dg-message "at offset \\\[1, 3] into destination object 'a3'" "note" }
+ char *pi = a3 + i1;
+ char *pj = a3 + i2;
+
+ char *q = MAX (pi, pj);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
+ }
+
+ {
+ /* Exercise both pointers pointing to the same object plus variable
+ offset. Verify that no offset is mentioned in the note (since
+ its unknown, printing its full range is unnecessary). */
+ char a4[4]; // { dg-message ": destination object 'a4'" "note" }
+ char *pi = a4 + vi;
+ char *pj = a4 + vi;
+
+ char *q = MAX (pi, pj);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 3);
+ memset (q, 0, 4);
+ memset (q, 0, 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
+ }
+
+ {
+ /* Exercise a pointer pointing to a known object with one pointing
+ to an unknown object. */
+ char a5[5]; // { dg-message ": destination object 'a5'" "note" }
+ char *p = ptr;
+ char *q = MAX (p, a5);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 5);
+ memset (q, 0, 6); // { dg-warning "writing 6 bytes into a region of size 5 " }
+ }
+
+ {
+ /* Exercise a pointer pointing to a known object plus constant offset
+ with one pointing to an unknown object. */
+ char a6[6]; // { dg-message "at offset 1 into destination object 'a6'" "note" }
+ char *p1 = ptr;
+ char *p2 = a6 + 1;
+ char *q = MAX (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 5);
+ memset (q, 0, 6); // { dg-warning "writing 6 bytes into a region of size 5 " }
+ memset (q, 0, 7); // { dg-warning "writing 7 bytes into a region of size 5 " }
+ }
+
+ {
+ /* Exercise a pointer pointing to a known object with one pointing
+ to an unknown object plus constant offset. */
+ char a7[7]; // { dg-message "at offset 1 into destination object 'a7'" "note" }
+ char *p1 = a7;
+ char *p2 = ptr + 1;
+ /* Since p1 points to a7[0] it must be less than any pointer to a7
+ plus positive offset, and so Q == P2. */
+ char *q = MAX (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 6);
+ memset (q, 0, 7); // { dg-warning "writing 7 bytes into a region of size 6 " }
+ memset (q, 0, 8); // { dg-warning "writing 8 bytes into a region of size 6 " }
+ }
+
+ {
+ /* Exercise a pointer pointing to a known object plus constant offset
+ with one pointing to an unknown object plus a different constant
+ offset. */
+ char a8[8]; // { dg-message "at offset 2 into destination object 'a8'" "note" }
+ char *p1 = a8 + 1;
+ char *p2 = ptr + 2;
+ /* Since P1 points to A8[1] it must be less than or equal to any
+ pointer to A8 plus positive offset. Either way, Q must point
+ to A8[2]. */
+ char *q = MAX (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 6);
+ memset (q, 0, 7); // { dg-warning "writing 7 bytes into a region of size 6 " }
+ memset (q, 0, 8); // { dg-warning "writing 8 bytes into a region of size 6 " }
+ }
+
+ {
+ /* Same as above but with larger offsets. */
+ char a9[9]; // { dg-message "at offset 4 into destination object 'a9'" "note" }
+ char *p1 = a9 + 3;
+ char *p2 = ptr + 4;
+ /* Since P1 points to A9[3] it must be less than or equal to any
+ pointer anywhere into A9 plus 4, so Q must point to A9[4]. */
+ char *q = MAX (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 5);
+ memset (q, 0, 6); // { dg-warning "writing 6 bytes into a region of size 5 " }
+ }
+
+ {
+ /* Same as above but with the offsets reversed. */
+ char a10[10]; // { dg-message "at offset 10 into destination object 'a10'" "note" }
+ char *p1 = a10 + 10;
+ char *p2 = ptr + 5;
+ /* Since P1 points just past the end of A10 it could be either less
+ or equal to another pointer anywhere into A10 plus 3 because
+ the other pointer itself could start at a non-zero offset that's
+ not reflected in the determaxed offset). */
+ char *q = MAX (p1, p2);
+
+ memset (q, 0, 1); // { dg-warning "writing 1 byte into a region of size 0 " }
+ }
+
+ {
+ char a11[11]; // { dg-message "at offset \\\[1, 11] into destination object 'a11'" "note" }
+ char *p1 = ptr;
+ char *p2 = a11 + i1;
+ char *q = MAX (p1, p2);
+
+ memset (q, 0, 1);
+ memset (q, 0, 2);
+ memset (q, 0, 10);
+ memset (q, 0, 11); // { dg-warning "writing 11 bytes into a region of size 10 " }
+ }
+}
+
diff --git a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
index 02f6f3d5342..82555b0fbcc 100644
--- a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
+++ b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
@@ -143,11 +143,11 @@ T (v0 ? b[1] : "", bsz);
T (v0 ? b[2] : "", bsz);
T (v0 ? b[3] : "", bsz);
-T (v0 ? "" : b[0], bsz + 1);
+T (v0 ? "" : b[0], bsz + 1); /* { dg-warning "bound 6 may exceed source size 5" } */
T (v0 ? "" : b[1], bsz + 1);
T (v0 ? "" : b[2], bsz + 1);
T (v0 ? "" : b[3], bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? b[0] : "", bsz + 1);
+T (v0 ? b[0] : "", bsz + 1); /* { dg-warning "bound 6 may exceed source size 5" } */
T (v0 ? b[1] : "", bsz + 1);
T (v0 ? b[2] : "", bsz + 1);
T (v0 ? b[3] : "", bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
@@ -185,8 +185,8 @@ T (v0 ? "1234" : b[i3], bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfa
T (v0 ? b[3] : "1234", bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
T (v0 ? b[i3] : "1234", bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? a : b[3], bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? b[0] : b[2], bsz + 1);
+T (v0 ? a : b[3], bsz + 1); /* { dg-warning "bound 6 may exceed source size 5" "pr86937" } */
+T (v0 ? b[0] : b[2], bsz + 1); /* { dg-warning "bound 6 may exceed source size 5" "pr86937" } */
T (v0 ? b[2] : b[3], bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
T (v0 ? b[3] : b[2], bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index ebb17cd852c..7de2daa2b54 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -1933,6 +1933,8 @@ maybe_warn_overflow (gimple *stmt, tree len,
dest = gimple_call_arg (stmt, 0);
writefn = gimple_call_fndecl (stmt);
}
+ else
+ return;
if (TREE_NO_WARNING (dest))
return;
@@ -1941,148 +1943,22 @@ maybe_warn_overflow (gimple *stmt, tree len,
Make sure all operands have the same precision to keep wide_int
from ICE'ing. */
- /* Convenience constants. */
- const widest_int diff_min
- = wi::to_widest (TYPE_MIN_VALUE (ptrdiff_type_node));
- const widest_int diff_max
- = wi::to_widest (TYPE_MAX_VALUE (ptrdiff_type_node));
- const widest_int size_max
- = wi::to_widest (TYPE_MAX_VALUE (size_type_node));
-
- /* The offset into the destination object computed below and not
- reflected in DESTSIZE. */
- widest_int offrng[2] = { 0, 0 };
-
- if (!si)
- {
- /* If no destination STRINFO was provided try to get it from
- the DEST argument. */
- tree ref = dest;
- if (TREE_CODE (ref) == ARRAY_REF)
- {
- /* Handle stores to VLAs (represented as
- ARRAY_REF (MEM_REF (vlaptr, 0), N]. */
- tree off = TREE_OPERAND (ref, 1);
- ref = TREE_OPERAND (ref, 0);
- wide_int rng[2];
- if (get_range (off, stmt, rng, rvals))
- {
- /* Convert offsets to the maximum precision. */
- offrng[0] = widest_int::from (rng[0], SIGNED);
- offrng[1] = widest_int::from (rng[1], SIGNED);
- }
- else
- {
- offrng[0] = diff_min;
- offrng[1] = diff_max;
- }
- }
-
- if (TREE_CODE (ref) == MEM_REF)
- {
- tree mem_off = TREE_OPERAND (ref, 1);
- ref = TREE_OPERAND (ref, 0);
- wide_int rng[2];
- if (get_range (mem_off, stmt, rng, rvals))
- {
- offrng[0] += widest_int::from (rng[0], SIGNED);
- offrng[1] += widest_int::from (rng[1], SIGNED);
- }
- else
- {
- offrng[0] = diff_min;
- offrng[1] = diff_max;
- }
- }
-
- wide_int rng[2];
- if (int idx = get_stridx (ref, rng, rvals))
- {
- si = get_strinfo (idx);
- offrng[0] += widest_int::from (rng[0], SIGNED);
- offrng[1] += widest_int::from (rng[1], SIGNED);
- }
- }
-
- /* The allocation call if the destination object was allocated
- by one. */
- gimple *alloc_call = NULL;
- /* The DECL of the destination object if known and not dynamically
- allocated. */
- tree destdecl = NULL_TREE;
- /* The offset into the destination object set by compute_objsize
- but already reflected in DESTSIZE. */
- tree destoff = NULL_TREE;
+ access_ref aref;
/* The size of the destination region (which is smaller than
the destination object for stores at a non-zero offset). */
- tree destsize = NULL_TREE;
-
- /* Compute the range of sizes of the destination object. The range
- is constant for declared objects but may be a range for allocated
- objects. */
- widest_int sizrng[2] = { 0, 0 };
- if (si)
- {
- wide_int rng[2];
- destsize = gimple_call_alloc_size (si->alloc, rng, rvals);
- if (destsize)
- {
- sizrng[0] = widest_int::from (rng[0], UNSIGNED);
- sizrng[1] = widest_int::from (rng[1], UNSIGNED);
- }
- alloc_call = si->alloc;
- }
- else
- offrng[0] = offrng[1] = 0;
-
+ tree destsize = compute_objsize (dest, rawmem ? 0 : 1, &aref, rvals);
if (!destsize)
{
- /* If there is no STRINFO for DEST, fall back on compute_objsize. */
- tree off = NULL_TREE;
- destsize = compute_objsize (dest, rawmem ? 0 : 1, &destdecl, &off, rvals);
- if (destsize)
- {
- /* Remember OFF but clear OFFRNG that may have been set above. */
- destoff = off;
- offrng[0] = offrng[1] = 0;
-
- if (destdecl && TREE_CODE (destdecl) == SSA_NAME)
- {
- gimple *stmt = SSA_NAME_DEF_STMT (destdecl);
- if (is_gimple_call (stmt))
- alloc_call = stmt;
- destdecl = NULL_TREE;
- }
-
- wide_int rng[2];
- if (get_range (destsize, stmt, rng, rvals))
- {
- sizrng[0] = widest_int::from (rng[0], UNSIGNED);
- sizrng[1] = widest_int::from (rng[1], UNSIGNED);
- }
- else
- {
- /* On failure, rather than failing, set the maximum range
- so that overflow in allocated objects whose size depends
- on the strlen of the source can still be diagnosed
- below. */
- sizrng[0] = 0;
- sizrng[1] = size_max;
- }
- }
+ aref.sizrng[0] = 0;
+ aref.sizrng[1] = wi::to_offset (max_object_size ());
}
- if (!destsize)
- {
- sizrng[0] = 0;
- sizrng[1] = size_max;
- };
-
/* Return early if the DESTSIZE size expression is the same as LEN
and the offset into the destination is zero. This might happen
in the case of a pair of malloc and memset calls to allocate
an object and clear it as if by calloc. */
- if (destsize == len && !plus_one && offrng[0] == 0 && offrng[0] == offrng[1])
+ if (destsize == len && !plus_one
+ && aref.offrng[0] == 0 && aref.offrng[0] == aref.offrng[1])
return;
wide_int rng[2];
@@ -2100,38 +1976,13 @@ maybe_warn_overflow (gimple *stmt, tree len,
/* The size of the remaining space in the destination computed
as the size of the latter minus the offset into it. */
- widest_int spcrng[2] = { sizrng[0], sizrng[1] };
- if (wi::neg_p (offrng[0]) && wi::neg_p (offrng[1]))
- {
- /* When the offset is negative and the size of the destination
- object unknown there is little to do.
- FIXME: Detect offsets that are necessarily invalid regardless
- of the size of the object. */
- if (!destsize)
- return;
-
- /* The remaining space is necessarily zero. */
- spcrng[0] = spcrng[1] = 0;
- }
- else if (wi::neg_p (offrng[0]))
- {
- /* When the lower bound of the offset is negative but the upper
- bound is not, reduce the upper bound of the remaining space
- by the upper bound of the offset but leave the lower bound
- unchanged. If that makes the upper bound of the space less
- than the lower bound swap the two. */
- spcrng[1] -= wi::ltu_p (offrng[1], spcrng[1]) ? offrng[1] : spcrng[1];
- if (wi::ltu_p (spcrng[1], spcrng[0]))
- std::swap (spcrng[1], spcrng[0]);
- }
- else
- {
- /* When the offset is positive reduce the remaining space by
- the lower bound of the offset or clear it if the offset is
- greater. */
- spcrng[0] -= wi::ltu_p (offrng[0], spcrng[0]) ? offrng[0] : spcrng[0];
- spcrng[1] -= wi::ltu_p (offrng[0], spcrng[1]) ? offrng[0] : spcrng[1];
- }
+ widest_int spcrng[2];
+ {
+ offset_int remrng[2];
+ remrng[1] = aref.size_remaining (remrng);
+ spcrng[0] = remrng[0] == -1 ? 0 : widest_int::from (remrng[0], UNSIGNED);
+ spcrng[1] = widest_int::from (remrng[1], UNSIGNED);
+ }
if (wi::leu_p (lenrng[0], spcrng[0])
&& wi::leu_p (lenrng[1], spcrng[1]))
@@ -2233,112 +2084,7 @@ maybe_warn_overflow (gimple *stmt, tree len,
gimple_set_no_warning (stmt, true);
- /* If DESTOFF is not null, use it to format the offset value/range. */
- if (destoff)
- {
- wide_int rng[2];
- if (get_range (destoff, stmt, rng))
- {
- offrng[0] = widest_int::from (rng[0], SIGNED);
- offrng[1] = widest_int::from (rng[1], SIGNED);
- }
- else
- offrng[0] = offrng[1] = 0;
- }
-
- /* Format the offset to keep the number of inform calls from growing
- out of control. */
- char offstr[64];
- if (offrng[0] == offrng[1])
- sprintf (offstr, "%lli", (long long) offrng[0].to_shwi ());
- else
- sprintf (offstr, "[%lli, %lli]",
- (long long) offrng[0].to_shwi (), (long long) offrng[1].to_shwi ());
-
- if (destdecl && DECL_P (destdecl))
- {
- if (tree size = DECL_SIZE_UNIT (destdecl))
- inform (DECL_SOURCE_LOCATION (destdecl),
- "at offset %s to object %qD with size %E declared here",
- offstr, destdecl, size);
- else
- inform (DECL_SOURCE_LOCATION (destdecl),
- "at offset %s to object %qD declared here",
- offstr, destdecl);
- return;
- }
-
- if (!alloc_call)
- return;
-
- tree allocfn = gimple_call_fndecl (alloc_call);
- if (!allocfn)
- {
- /* For an ALLOC_CALL via a function pointer make a small effort
- to determine the destination of the pointer. */
- allocfn = gimple_call_fn (alloc_call);
- if (TREE_CODE (allocfn) == SSA_NAME)
- {
- gimple *def = SSA_NAME_DEF_STMT (allocfn);
- if (gimple_assign_single_p (def))
- {
- tree rhs = gimple_assign_rhs1 (def);
- if (DECL_P (rhs))
- allocfn = rhs;
- else if (TREE_CODE (rhs) == COMPONENT_REF)
- allocfn = TREE_OPERAND (rhs, 1);
- }
- }
- }
-
- if (gimple_call_builtin_p (alloc_call, BUILT_IN_ALLOCA_WITH_ALIGN))
- {
- if (sizrng[0] == sizrng[1])
- inform (gimple_location (alloc_call),
- "at offset %s to an object with size %wu declared here",
- offstr, sizrng[0].to_uhwi ());
- else if (sizrng[0] == 0)
- {
- /* Avoid printing impossible sizes. */
- if (wi::ltu_p (sizrng[1], diff_max - 2))
- inform (gimple_location (alloc_call),
- "at offset %s to an object with size at most %wu "
- "declared here",
- offstr, sizrng[1].to_uhwi ());
- else
- inform (gimple_location (alloc_call),
- "at offset %s to an object declared here", offstr);
- }
- else
- inform (gimple_location (alloc_call),
- "at offset %s to an object with size between %wu and %wu "
- "declared here",
- offstr, sizrng[0].to_uhwi (), sizrng[1].to_uhwi ());
- return;
- }
-
- if (sizrng[0] == sizrng[1])
- inform (gimple_location (alloc_call),
- "at offset %s to an object with size %wu allocated by %qE here",
- offstr, sizrng[0].to_uhwi (), allocfn);
- else if (sizrng[0] == 0)
- {
- /* Avoid printing impossible sizes. */
- if (wi::ltu_p (sizrng[1], diff_max - 2))
- inform (gimple_location (alloc_call),
- "at offset %s to an object with size at most %wu allocated "
- "by %qD here",
- offstr, sizrng[1].to_uhwi (), allocfn);
- else
- inform (gimple_location (alloc_call),
- "at offset %s to an object allocated by %qE here",
- offstr, allocfn);
- }
- else
- inform (gimple_location (alloc_call),
- "at offset %s to an object with size between %wu and %wu "
- "allocated by %qE here",
- offstr, sizrng[0].to_uhwi (), sizrng[1].to_uhwi (), allocfn);
+ aref.inform_access (access_write_only);
}
/* Convenience wrapper for the above. */
@@ -3471,7 +3217,7 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
if (olddsi != NULL
&& !integer_zerop (len))
{
- maybe_warn_overflow (stmt, len, rvals, olddsi, false, true);
+ maybe_warn_overflow (stmt, len, rvals, olddsi, false, false);
adjust_last_stmt (olddsi, stmt, false);
}
@@ -3938,7 +3684,7 @@ handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write,
tree memset_size = gimple_call_arg (memset_stmt, 2);
/* Check for overflow. */
- maybe_warn_overflow (memset_stmt, memset_size, rvals, NULL, false, true);
+ maybe_warn_overflow (memset_stmt, memset_size, rvals, NULL, false, false);
/* Bail when there is no statement associated with the destination
(the statement may be null even when SI1->ALLOC is not). */