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 *, ssa_name_limit_t &,
+ range_query *);
unsigned HOST_WIDE_INT target_newline;
unsigned HOST_WIDE_INT target_percent;
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;
}
}
+/* 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 *THIS. If *THIS
+ 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 */,
+ ssa_name_limit_t *psnlim /* = NULL */,
+ range_query *rvals /* = NULL */) const
+{
+ gphi *phi_stmt = this->phi ();
+ if (!phi_stmt)
+ return ref;
+
+ /* FIXME: Calling get_ref() with a null PSNLIM is dangerous and might
+ cause unbounded recursion. */
+ ssa_name_limit_t snlim_buf;
+ if (!psnlim)
+ psnlim = &snlim_buf;
+
+ if (!psnlim->visit_phi (ref))
+ return NULL_TREE;
+
+ /* Reflects the range of offsets of all PHI arguments refer to the same
+ object (i.e., have the same REF). */
+ access_ref same_ref;
+ /* The conservative result of the PHI reflecting the offset and size
+ of the largest PHI argument, regardless of whether or not they all
+ refer to the same object. */
+ access_ref phi_ref;
+ if (pref)
+ {
+ phi_ref = *pref;
+ same_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 ());
+ /* The offset of the PHI, not reflecting those of its arguments. */
+ 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, *psnlim, rvals)
+ || phi_arg_ref.sizrng[0] < 0)
+ /* A PHI with all null pointer arguments. */
+ return NULL_TREE;
+
+ /* 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;
+
+ const bool nullp = integer_zerop (arg) && (i || i + 1 < nargs);
+
+ if (phi_ref.sizrng[0] < 0)
+ {
+ if (!nullp)
+ same_ref = phi_arg_ref;
+ 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];
+
+ /* Disregard null pointers in PHIs with two or more arguments.
+ TODO: Handle this better! */
+ if (nullp)
+ continue;
+
+ /* 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 (phi_arg_ref.ref != same_ref.ref)
+ same_ref.ref = NULL_TREE;
+
+ if (phirem[1] < argrem[1]
+ || (phirem[1] == argrem[1]
+ && phi_ref.sizrng[1] < phi_arg_ref.sizrng[1]))
+ /* Use the argument with the most space remaining as the result,
+ or the larger one if the space is equal. */
+ phi_ref = phi_arg_ref;
+
+ /* Set SAME_REF.OFFRNG to the maximum range of all arguments. */
+ if (phi_arg_ref.offrng[0] < same_ref.offrng[0])
+ same_ref.offrng[0] = phi_arg_ref.offrng[0];
+ if (same_ref.offrng[1] < phi_arg_ref.offrng[1])
+ same_ref.offrng[1] = phi_arg_ref.offrng[1];
+ }
+
+ 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). */
+ psnlim->leave_phi (ref);
+ return NULL_TREE;
+ }
+
+ if (!same_ref.ref && same_ref.offrng[0] != 0)
+ /* Clear BASE0 if not all the arguments refer to the same object and
+ if not all their offsets are zero-based. This allows the final
+ PHI offset to out of bounds for some arguments but not for others
+ (or negative even of all the arguments are BASE0), which is overly
+ permissive. */
+ phi_ref.base0 = false;
+
+ if (same_ref.ref)
+ phi_ref = same_ref;
+ else
+ {
+ /* 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;
+ }
+
+ /* Avoid changing *THIS. */
+ if (pref && pref != this)
+ *pref = phi_ref;
+
+ psnlim->leave_phi (ref);
+
+ return phi_ref.ref;
+}
+
/* Return the maximum amount of space remaining and if non-null, set
argument to the minimum. */
return;
}
- offrng[1] = maxoff;
offset_int absmax = wi::abs (max);
if (offrng[0] < absmax)
{
}
}
+/* Set a bit for the PHI in VISITED and return true if it wasn't
+ already set. */
+
+bool
+ssa_name_limit_t::visit_phi (tree ssa_name)
+{
+ if (!visited)
+ visited = BITMAP_ALLOC (NULL);
+
+ /* Return false if SSA_NAME has already been visited. */
+ return bitmap_set_bit (visited, SSA_NAME_VERSION (ssa_name));
+}
+
+/* Clear a bit for the PHI in VISITED. */
+
+void
+ssa_name_limit_t::leave_phi (tree ssa_name)
+{
+ /* Return false if SSA_NAME has already been visited. */
+ bitmap_clear_bit (visited, SSA_NAME_VERSION (ssa_name));
+}
+
+/* Return false if the SSA_NAME chain length counter has reached
+ the limit, otherwise increment the counter and return true. */
+
+bool
+ssa_name_limit_t::next ()
+{
+ /* Return a negative value to let caller avoid recursing beyond
+ the specified limit. */
+ if (ssa_def_max == 0)
+ return false;
+
+ --ssa_def_max;
+
+ return true;
+}
+
+/* If the SSA_NAME has already been "seen" return a positive value.
+ Otherwise add it to VISITED. If the SSA_NAME limit has been
+ reached, return a negative value. Otherwise return zero. */
+
+int
+ssa_name_limit_t::next_phi (tree ssa_name)
+{
+ {
+ gimple *def_stmt = SSA_NAME_DEF_STMT (ssa_name);
+ /* Return a positive value if the PHI has already been visited. */
+ if (gimple_code (def_stmt) == GIMPLE_PHI
+ && !visit_phi (ssa_name))
+ return 1;
+ }
+
+ /* Return a negative value to let caller avoid recursing beyond
+ the specified limit. */
+ if (ssa_def_max == 0)
+ return -1;
+
+ --ssa_def_max;
+
+ return 0;
+}
+
+ssa_name_limit_t::~ssa_name_limit_t ()
+{
+ if (visited)
+ BITMAP_FREE (visited);
+}
+
/* Return true if NAME starts with __builtin_ or __sync_. */
static bool
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))
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)
{
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))
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)
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;
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;
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;
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)
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)
+ {
+ /* Skip any PHIs; those could lead to infinite recursion. */
+ if (all_refs[i].phi ())
+ continue;
+
+ 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
/* 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
= 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. */
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;
}
/* 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
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
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,
+ ssa_name_limit_t &snlim, 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], snlim, 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], snlim, 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).
if it's unique, otherwise to null, PREF->OFFRNG to the range of
offsets into it, and PREF->SIZRNG to the range of sizes of
the object(s).
- VISITED is used to avoid visiting the same PHI operand multiple
+ SNLIM is used to avoid visiting the same PHI operand multiple
times, and, when nonnull, RVALS to determine range information.
Returns true on success, false when a meaningful size (or range)
cannot be determined.
to influence code generation or optimization. */
static bool
-compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
- range_query *rvals)
+compute_objsize (tree ptr, int ostype, access_ref *pref,
+ ssa_name_limit_t &snlim, range_query *rvals)
{
STRIP_NOPS (ptr);
if (code == BIT_FIELD_REF)
{
tree ref = TREE_OPERAND (ptr, 0);
- if (!compute_objsize (ref, ostype, pref, visited, rvals))
+ if (!compute_objsize (ref, ostype, pref, snlim, rvals))
return false;
offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
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)
/* In OSTYPE zero (for raw memory functions like memcpy), use
the maximum size instead if the identity of the enclosing
object cannot be determined. */
- if (!compute_objsize (ref, ostype, pref, visited, rvals))
+ if (!compute_objsize (ref, ostype, pref, snlim, rvals))
return false;
/* Otherwise, use the size of the enclosing object and add
pref->add_offset (wi::to_offset (offset));
else
pref->add_max_offset ();
+
+ if (!pref->ref)
+ /* REF may have been already set to an SSA_NAME earlier
+ to provide better context for diagnostics. In that case,
+ leave it unchanged. */
+ pref->ref = ref;
return true;
}
+ pref->ref = field;
+
if (!addr && POINTER_TYPE_P (TREE_TYPE (field)))
{
/* Set maximum size if the reference is to the pointer member
return true;
}
- pref->ref = field;
-
/* SAM is set for array members that might need special treatment. */
special_array_member sam;
tree size = component_ref_size (ptr, &sam);
{
tree ref = TREE_OPERAND (ptr, 0);
tree reftype = TREE_TYPE (ref);
- if (code == ARRAY_REF
+ if (!addr && code == ARRAY_REF
&& TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE)
/* Avoid arrays of pointers. FIXME: Hande pointers to arrays
of known bound. */
return false;
}
- if (!compute_objsize (ref, ostype, pref, visited, rvals))
+ if (!compute_objsize (ref, ostype, pref, snlim, rvals))
return false;
offset_int orng[2];
if (code == TARGET_MEM_REF)
{
tree ref = TREE_OPERAND (ptr, 0);
- if (!compute_objsize (ref, ostype, pref, visited, rvals))
+ if (!compute_objsize (ref, ostype, pref, snlim, rvals))
return false;
/* TODO: Handle remaining operands. Until then, add maximum offset. */
if (code == STRING_CST)
{
pref->sizrng[0] = pref->sizrng[1] = TREE_STRING_LENGTH (ptr);
+ pref->ref = ptr;
return true;
}
if (code == POINTER_PLUS_EXPR)
{
tree ref = TREE_OPERAND (ptr, 0);
- if (!compute_objsize (ref, ostype, pref, visited, rvals))
+ if (!compute_objsize (ref, ostype, pref, snlim, rvals))
return false;
offset_int orng[2];
if (code == VIEW_CONVERT_EXPR)
{
ptr = TREE_OPERAND (ptr, 0);
- return compute_objsize (ptr, ostype, pref, visited, rvals);
+ return compute_objsize (ptr, ostype, pref, snlim, rvals);
}
- if (TREE_CODE (ptr) == SSA_NAME)
+ if (code == SSA_NAME)
{
+ if (!snlim.next ())
+ return false;
+
+ /* Only process an SSA_NAME if the recursion limit has not yet
+ been reached. */
gimple *stmt = SSA_NAME_DEF_STMT (ptr);
if (is_gimple_call (stmt))
{
offset_int offrng[2];
if (tree ret = gimple_call_return_array (stmt, offrng, rvals))
{
- if (!compute_objsize (ret, ostype, pref, visited, rvals))
+ if (!compute_objsize (ret, ostype, pref, snlim, rvals))
return false;
/* Cap OFFRNG[1] to at most the remaining size of
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;
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, &snlim, rvals))
+ return false;
+ *pref = phi_ref;
+ pref->ref = ptr;
+ return true;
+ }
if (!is_gimple_assign (stmt))
{
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;
+ pref->ref = ptr;
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, snlim, rvals);
+
+ tree rhs = gimple_assign_rhs1 (stmt);
+
if (code == POINTER_PLUS_EXPR
- && TREE_CODE (TREE_TYPE (ptr)) == POINTER_TYPE)
+ && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE)
{
/* Compute the size of the object first. */
- if (!compute_objsize (ptr, ostype, pref, visited, rvals))
+ if (!compute_objsize (rhs, ostype, pref, snlim, rvals))
return false;
offset_int orng[2];
return true;
}
- if (code == ADDR_EXPR)
- return compute_objsize (ptr, ostype, pref, visited, rvals);
+ if (code == ADDR_EXPR
+ || code == SSA_NAME)
+ return compute_objsize (rhs, ostype, pref, snlim, rvals);
- /* This could be an assignment from a nonlocal pointer. Save PTR
- to mention in diagnostics but otherwise treat it as a pointer
+ /* (This could also be an assignment from a nonlocal pointer.) Save
+ PTR to mention in diagnostics but otherwise treat it as a pointer
to an unknown object. */
- pref->ref = ptr;
+ pref->ref = rhs;
+ pref->base0 = false;
+ pref->set_max_size_range ();
+ return true;
}
/* Assume all other expressions point into an unknown object
of the maximum valid size. */
+ pref->ref = ptr;
pref->base0 = false;
pref->set_max_size_range ();
return true;
compute_objsize (tree ptr, int ostype, access_ref *pref,
range_query *rvals /* = NULL */)
{
- bitmap visited = NULL;
-
- bool success
- = compute_objsize (ptr, ostype, pref, &visited, rvals);
-
- if (visited)
- BITMAP_FREE (visited);
-
- if (!success)
+ ssa_name_limit_t snlim;
+ if (!compute_objsize (ptr, ostype, pref, snlim, rvals))
return NULL_TREE;
offset_int maxsize = pref->size_remaining ();
extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
extern bool builtin_with_linkage_p (tree);
+/* Describes recursion limits used by functions that follow use-def
+ chains of SSA_NAMEs. */
+
+class ssa_name_limit_t
+{
+ bitmap visited; /* Bitmap of visited SSA_NAMEs. */
+ unsigned ssa_def_max; /* Longest chain of SSA_NAMEs to follow. */
+
+ /* Not copyable or assignable. */
+ DISABLE_COPY_AND_ASSIGN (ssa_name_limit_t);
+
+public:
+
+ ssa_name_limit_t ()
+ : visited (),
+ ssa_def_max (param_ssa_name_def_chain_limit) { }
+
+ /* Set a bit for the PHI in VISITED and return true if it wasn't
+ already set. */
+ bool visit_phi (tree);
+ /* Clear a bit for the PHI in VISITED. */
+ void leave_phi (tree);
+ /* Return false if the SSA_NAME chain length counter has reached
+ the limit, otherwise increment the counter and return true. */
+ bool next ();
+
+ /* If the SSA_NAME has already been "seen" return a positive value.
+ Otherwise add it to VISITED. If the SSA_NAME limit has been
+ reached, return a negative value. Otherwise return zero. */
+ int next_phi (tree);
+
+ ~ssa_name_limit_t ();
+};
+
+class range_query;
+
/* Describes a reference to an object used in an access. */
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,
+ ssa_name_limit_t * = NULL, range_query * = NULL) const;
/* Return true if OFFRNG is the constant zero. */
bool offset_zero () const
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
/* 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
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);
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.
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.
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.
struct A1i
{
char n;
- char a[1]; // { dg-message "declared here" }
+ char a[1]; // { dg-message "destination object" }
char x;
};
new (&uac2.c) int32_t; // { dg-warning "placement" }
new (&uac3.c) int32_t; // { dg-warning "placement" }
- // Diagnose the following even though the size of uac4.c could be
- // expected to extend to the end of the union (as it is by Built-in
- // Object Size and so isn't diagnosed in calls to functions like
- // memset(&uac4.c, 0, sizeof(int32_t)) when _FORTIFY_SOURCE is non-zero. */
- new (&uac4.c) int32_t; // { dg-warning "placement" }
+ /* The following isn't diagnosed (anymore) for consistency with
+ the middle end where members of unions are considered to extend
+ to the end of the enclosing object.
+ See gcc.dg/Wstringop-overflow-60.c for the middle end test. */
+ new (&uac4.c) int32_t;
new (&uac4.c + 1) int32_t; // { dg-warning "placement" }
}
struct Ax
{
char n;
- char a[]; // { dg-message "at offset \[0-2\] to object 'Ax::a' declared here" "note: flexarray" }
+ char a[]; // { dg-message "destination object 'Ax::a' of size 0" "note: flexarray" }
};
// Verify warning for a definition with no initializer.
struct A0
{
char n;
- char a[0]; // { dg-message "at offset \[0-2\] to object 'A0::a' with size 0 declared here" "note: trailing zero-length array" }
+ char a[0]; // { dg-message "destination object 'A0::a' of size 0" "note: trailing zero-length array" }
};
// Verify warning for a definition with no initializer.
struct A1
{
char n;
- char a[1]; // { dg-message "at offset \[1-9\] to object 'A1::a' with size 1 declared here" "note: trailing one-element array" }
+ char a[1]; // { dg-message "at offset \[1-2\] into destination object 'A1::a' of size 1" "note: trailing one-element array" }
};
// Verify warning for a definition with no initializer.
struct A1i
{
char n;
- char a[1]; // { dg-message "at offset \[1-9\] to object 'A1i::a' with size 1 declared here" "note: interior one-element array" }
+ char a[1]; // { dg-message "at offset \[1-2\] into destination object 'A1i::a' of size 1" "note: interior one-element array" }
char x;
};
struct Bx
{
char n;
- char a[]; // { dg-message "at offset 0 to object 'Bx::a' declared here" "note: flexarray class member" }
+ char a[]; // { dg-message "destination object 'Bx::a' of size 0" "note: flexarray class member" }
// Verify the warning for a constant.
Bx () { a[0] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
struct B0
{
char n;
- char a[0]; // { dg-message "at offset 0 to object 'B0::a' with size 0 declared here" "note: zero-length trailing array class member" }
+ char a[0]; // { dg-message "destination object 'B0::a' of size 0" "note: zero-length trailing array class member" }
B0 () { a[0] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
struct B1
{
char n;
- char a[1]; // { dg-message "at offset 1 to object 'B1::a' with size 1 declared here" "note: one-element trailing array class member" }
+ char a[1]; // { dg-message "at offset 1 into destination object 'B1::a' of size 1" "note: one-element trailing array class member" }
B1 () { a[1] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
struct B123
{
- char a[123]; // { dg-message "at offset 123 to object 'B123::a' with size 123 declared here" "note: large trailing array class member" }
+ char a[123]; // { dg-message "at offset 123 into destination object 'B123::a' of size 123" "note: large trailing array class member" }
B123 () { a[123] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
struct B234
{
- char a[234]; // { dg-message "at offset 234 to object 'B234::a' with size 234 declared here" "note: large trailing array class member" }
+ char a[234]; // { dg-message "at offset 234 into destination object 'B234::a' of size 234" "note: large trailing array class member" }
B234 (int i) { a[i] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
#define NOIPA __attribute__ ((noipa))
-const char a0[] = "";
-const char a1[] = "1";
-const char a2[] = "12";
-const char a3[] = "123";
-const char a4[] = "1234";
-const char a5[] = "12345";
-const char a6[] = "123456";
-const char a7[] = "1234567";
-const char a8[] = "12345678";
const char a9[] = "123456789";
-void f (const char*, ...);
-
-int i0, i1, i2, i3, i4, i5, i6, i7, i8;
+void sink (const char*, ...);
NOIPA int g2 (int i)
{
const char *p1 = p0 + i;
const char *p2 = p1 + i;
- f (p0, p1, p2);
+ sink (p0, p1, p2);
return p2[8]; // { dg-warning "\\\[-Warray-bounds]" }
}
const char *p2 = p1 + i;
const char *p3 = p2 + i;
- f (p0, p1, p2, p3);
+ sink (p0, p1, p2, p3);
return p3[7]; // { dg-warning "\\\[-Warray-bounds]" }
}
const char *p3 = p2 + i;
const char *p4 = p3 + i;
- f (p0, p1, p2, p3, p4);
+ sink (p0, p1, p2, p3, p4);
return p4[6]; // { dg-warning "\\\[-Warray-bounds]" }
}
const char *p4 = p3 + i;
const char *p5 = p4 + i;
- f (p0, p1, p2, p3, p4, p5);
+ sink (p0, p1, p2, p3, p4, p5);
return p5[5];
}
const char *p5 = p4 + i;
const char *p6 = p5 + i;
- f (p0, p1, p2, p3, p4, p5, p6);
+ sink (p0, p1, p2, p3, p4, p5, p6);
return p6[4];
}
const char *p6 = p5 + i;
const char *p7 = p6 + i;
- f (p0, p1, p2, p3, p4, p5, p6, p7);
+ sink (p0, p1, p2, p3, p4, p5, p6, p7);
return p7[3];
}
const char *p7 = p6 + i;
const char *p8 = p7 + i;
- f (p0, p1, p2, p3, p4, p5, p6, p7, p8);
+ sink (p0, p1, p2, p3, p4, p5, p6, p7, p8);
return p8[2];
}
{
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);
{
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);
{
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);
{
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);
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 " } */
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" } */
}
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);
}
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);
{
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);
}
// 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);
}
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 }
}
{
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 }
}
{
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 }
}
}
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 }
}
}
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);
}
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);
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" }
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" }
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;
{
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;
{
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;
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);
}
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);
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);
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);
void warn_c32 (char c)
{
- extern char warn_a32[32]; // { dg-message "at offset 32 to object 'warn_a32' with size 32" "note" }
+ 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" }
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;
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;
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;
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;
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;
--- /dev/null
+/* 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" }
+ }
+}
--- /dev/null
+/* 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" }
+ }
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* { 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" }
+}
--- /dev/null
+/* 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 " }
+ }
+}
+
--- /dev/null
+/* PR middle-end/92936 - missing warning on a past-the-end store to a PHI
+ Test case derived from gcc/opts-common.c.
+ { dg-do compile }
+ { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+char* f (const void*, ...);
+
+const char *
+candidates_list_and_hint (const char *arg, char **str, const char *a[])
+{
+ size_t len = 0;
+ int i;
+
+ for (i = 0; a[i]; ++i)
+ len += __builtin_strlen (a[i]) + 1;
+
+ char *p = (char*)__builtin_malloc (len);
+ *str = p;
+
+ for (i = 0; a[i]; ++i)
+ {
+ len = __builtin_strlen (a[i]);
+ __builtin_memcpy (p, a[i], len);
+ p[len] = ' ';
+ p += len + 1;
+ }
+
+ p[-1] = '\0'; // { dg-bogus "\\\[-Wstringop-overflow" }
+
+ return f (arg, &a);
+}
--- /dev/null
+/* PR middle-end/92936 - missing warning on a past-the-end store to a PHI
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* malloc (size_t);
+void* memset (void*, int, size_t);
+
+extern char a3[3], a5[5], a9[9];
+
+extern int cnd[];
+
+void* f2 (void)
+{
+ char *p0 = cnd[0] ? a3 : 0;
+ char *p1 = cnd[1] ? a5 : p0;
+
+ return memset (p1, 0, 6); // { dg-warning "writing 6 bytes into a region of size 5" }
+}
+
+void* f3 (void)
+{
+ char *p0 = cnd[0] ? a3 : 0;
+ char *p1 = cnd[1] ? a5 : 0;
+ char *p2 = cnd[2] ? p0 : p1;
+
+ return memset (p2, 0, 6); // { dg-warning "writing 6 bytes into a region of size 5" }
+}
+
+void* f3_2 (void)
+{
+ char *p0 = cnd[0] ? a3 : 0;
+ char *p1 = cnd[1] ? a5 : 0;
+ char *p2 = cnd[2] ? p1 : p0;
+
+ return memset (p2, 0, 6); // { dg-warning "writing 6 bytes into a region of size 5" }
+}
+
+void* f3_3 (void)
+{
+ char *p0 = cnd[0] ? a5 : 0;
+ char *p1 = cnd[1] ? p0 : a5;
+ char *p2 = cnd[2] ? p1 : p0;
+
+ return memset (p2, 0, 6); // { dg-warning "writing 6 bytes into a region of size 5" }
+}
+
+void* f4 (void)
+{
+ char *p0 = cnd[0] ? a3 : 0;
+ char *p1 = cnd[1] ? a5 : 0;
+ char *p2 = cnd[2] ? p0 : 0;
+ char *p3 = cnd[3] ? p1 : p2;
+
+ return memset (p3, 0, 6); // { dg-warning "writing 6 bytes into a region of size 5" }
+}
+
+void* f9 (void)
+{
+ char *p0 = cnd[0] ? a5 : 0;
+ char *p1 = cnd[1] ? a5 + 1 : 0;
+ char *p2 = cnd[2] ? a5 + 2 : 0;
+ char *p3 = cnd[3] ? a5 + 3 : 0;
+ char *p4 = cnd[4] ? a5 + 4 : 0;
+
+ char *p5 = cnd[5] ? p0 : p1;
+ char *p6 = cnd[6] ? p5 : p2;
+ char *p7 = cnd[7] ? p6 : p3;
+ char *p8 = cnd[8] ? p7 : p4;
+ char *p9 = cnd[9] ? p8 : p5;
+
+ return memset (p9, 0, 6); // { dg-warning "writing 6 bytes into a region of size 5" }
+}
--- /dev/null
+/* Test to verify that --param ssa_name_def_chain_limit can be used to
+ limit the maximum number of SSA_NAME assignments the warning follows.
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds --param ssa-name-def-chain-limit=5" } */
+
+#define NOIPA __attribute__ ((noipa))
+
+void* memset (void*, int, __SIZE_TYPE__);
+
+char a9[9];
+
+void sink (const char*, ...);
+
+NOIPA void g2 (int i)
+{
+ if (i < 1) i = 1;
+
+ char *p0 = a9;
+ char *p1 = p0 + i;
+ char *p2 = p1 + i;
+
+ sink (p0, p1, p2);
+
+ memset (p2, 0, 8); // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+NOIPA void g3 (int i)
+{
+ if (i < 1) i = 1;
+
+ char *p0 = a9;
+ char *p1 = p0 + i;
+ char *p2 = p1 + i;
+ char *p3 = p2 + i;
+
+ sink (p0, p1, p2, p3);
+
+ memset (p3, 0, 7); // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+NOIPA void g4 (int i)
+{
+ if (i < 1) i = 1;
+
+ char *p0 = a9;
+ char *p1 = p0 + i;
+ char *p2 = p1 + i;
+ char *p3 = p2 + i;
+ char *p4 = p3 + i;
+
+ sink (p0, p1, p2, p3, p4);
+
+ memset (p4, 0, 6); // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+NOIPA void g5 (int i)
+{
+ if (i < 1) i = 1;
+
+ char *p0 = a9;
+ char *p1 = p0 + i;
+ char *p2 = p1 + i;
+ char *p3 = p2 + i;
+ char *p4 = p3 + i;
+ char *p5 = p4 + i;
+
+ sink (p0, p1, p2, p3, p4, p5);
+
+ memset (p5, 0, 5); // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+NOIPA void g6 (int i)
+{
+ if (i < 1) i = 1;
+
+ char *p0 = a9;
+ char *p1 = p0 + i;
+ char *p2 = p1 + i;
+ char *p3 = p2 + i;
+ char *p4 = p3 + i;
+ char *p5 = p4 + i;
+ char *p6 = p5 + i;
+
+ sink (p0, p1, p2, p3, p4, p5, p6);
+
+ memset (p6, 0, 4);
+}
+
+NOIPA void g7 (int i)
+{
+ if (i < 1) i = 1;
+
+ char *p0 = a9;
+ char *p1 = p0 + i;
+ char *p2 = p1 + i;
+ char *p3 = p2 + i;
+ char *p4 = p3 + i;
+ char *p5 = p4 + i;
+ char *p6 = p5 + i;
+ char *p7 = p6 + i;
+
+ sink (p0, p1, p2, p3, p4, p5, p6, p7);
+
+ memset (p7, 0, 4);
+}
+
+NOIPA void g8 (int i)
+{
+ if (i < 1) i = 1;
+
+ char *p0 = a9;
+ char *p1 = p0 + i;
+ char *p2 = p1 + i;
+ char *p3 = p2 + i;
+ char *p4 = p3 + i;
+ char *p5 = p4 + i;
+ char *p6 = p5 + i;
+ char *p7 = p6 + i;
+ char *p8 = p7 + i;
+
+ sink (p0, p1, p2, p3, p4, p5, p6, p7, p8);
+
+ memset (p8, 0, 2);
+}
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 *-*-* } } */
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" { xfail *-*-*} } */
+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 *-*-* } } */
dest = gimple_call_arg (stmt, 0);
writefn = gimple_call_fndecl (stmt);
}
+ else
+ return;
if (TREE_NO_WARNING (dest))
return;
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];
/* 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]))
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. */
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);
}
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). */
}
}
-/* Describes recursion limits used by count_nonzero_bytes. */
-
-class ssa_name_limit_t
-{
- bitmap visited; /* Bitmap of visited SSA_NAMEs. */
- unsigned ssa_def_max; /* Longest chain of SSA_NAMEs to follow. */
-
- /* Not copyable or assignable. */
- ssa_name_limit_t (ssa_name_limit_t&);
- void operator= (ssa_name_limit_t&);
-
- public:
-
- ssa_name_limit_t ()
- : visited (NULL),
- ssa_def_max (param_ssa_name_def_chain_limit) { }
-
- int next_ssa_name (tree);
-
- ~ssa_name_limit_t ()
- {
- if (visited)
- BITMAP_FREE (visited);
- }
-};
-
-/* If the SSA_NAME has already been "seen" return a positive value.
- Otherwise add it to VISITED. If the SSA_NAME limit has been
- reached, return a negative value. Otherwise return zero. */
-
-int ssa_name_limit_t::next_ssa_name (tree ssa_name)
-{
- if (!visited)
- visited = BITMAP_ALLOC (NULL);
-
- /* Return a positive value if SSA_NAME has already been visited. */
- if (!bitmap_set_bit (visited, SSA_NAME_VERSION (ssa_name)))
- return 1;
-
- /* Return a negative value to let caller avoid recursing beyond
- the specified limit. */
- if (ssa_def_max == 0)
- return -1;
-
- --ssa_def_max;
-
- return 0;
-}
-
static bool
count_nonzero_bytes_addr (tree, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT,
unsigned [3], bool *, bool *, bool *,
/* Avoid processing an SSA_NAME that has already been visited
or if an SSA_NAME limit has been reached. Indicate success
if the former and failure if the latter. */
- if (int res = snlim.next_ssa_name (exp))
+ if (int res = snlim.next_phi (exp))
return res > 0;
/* Determine the minimum and maximum from the PHI arguments. */
/* Avoid processing an SSA_NAME that has already been visited
or if an SSA_NAME limit has been reached. Indicate success
if the former and failure if the latter. */
- if (int res = snlim.next_ssa_name (exp))
+ if (int res = snlim.next_phi (exp))
return res > 0;
/* Determine the minimum and maximum from the PHI arguments. */