aboutsummaryrefslogtreecommitdiff
path: root/gcc/builtins.c
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2021-07-28 15:28:10 -0600
committerMartin Sebor <msebor@redhat.com>2021-07-28 16:02:17 -0600
commit2a837de28ee94b4ec201059a9a7aaa852e6808da (patch)
tree69c62e5581139c45d20f695344749058b7ae0978 /gcc/builtins.c
parentf471739e636734c2b4933cc4cb4ebad6cb2083ad (diff)
downloadgcc-2a837de28ee94b4ec201059a9a7aaa852e6808da.zip
gcc-2a837de28ee94b4ec201059a9a7aaa852e6808da.tar.gz
gcc-2a837de28ee94b4ec201059a9a7aaa852e6808da.tar.bz2
Add new gimple-ssa-warn-access pass.
gcc/ChangeLog: * Makefile.in (OBJS): Add gimple-ssa-warn-access.o and pointer-query.o. * attribs.h (fndecl_dealloc_argno): Move fndecl_dealloc_argno to tree.h. * builtins.c (compute_objsize_r): Move to pointer-query.cc. (access_ref::access_ref): Same. (access_ref::phi): Same. (access_ref::get_ref): Same. (access_ref::size_remaining): Same. (access_ref::offset_in_range): Same. (access_ref::add_offset): Same. (access_ref::inform_access): Same. (ssa_name_limit_t::visit_phi): Same. (ssa_name_limit_t::leave_phi): Same. (ssa_name_limit_t::next): Same. (ssa_name_limit_t::next_phi): Same. (ssa_name_limit_t::~ssa_name_limit_t): Same. (pointer_query::pointer_query): Same. (pointer_query::get_ref): Same. (pointer_query::put_ref): Same. (pointer_query::flush_cache): Same. (warn_string_no_nul): Move to gimple-ssa-warn-access.cc. (check_nul_terminated_array): Same. (unterminated_array): Same. (maybe_warn_for_bound): Same. (check_read_access): Same. (warn_for_access): Same. (get_size_range): Same. (check_access): Same. (gimple_call_alloc_size): Move to tree.c. (gimple_parm_array_size): Move to pointer-query.cc. (get_offset_range): Same. (gimple_call_return_array): Same. (handle_min_max_size): Same. (handle_array_ref): Same. (handle_mem_ref): Same. (compute_objsize): Same. (gimple_call_alloc_p): Move to gimple-ssa-warn-access.cc. (call_dealloc_argno): Same. (fndecl_dealloc_argno): Same. (new_delete_mismatch_p): Same. (matching_alloc_calls_p): Same. (warn_dealloc_offset): Same. (maybe_emit_free_warning): Same. * builtins.h (check_nul_terminated_array): Move to gimple-ssa-warn-access.h. (check_nul_terminated_array): Same. (warn_string_no_nul): Same. (unterminated_array): Same. (class ssa_name_limit_t): Same. (class pointer_query): Same. (struct access_ref): Same. (class range_query): Same. (struct access_data): Same. (gimple_call_alloc_size): Same. (gimple_parm_array_size): Same. (compute_objsize): Same. (class access_data): Same. (maybe_emit_free_warning): Same. * calls.c (initialize_argument_information): Remove call to maybe_emit_free_warning. * gimple-array-bounds.cc: Include new header.. * gimple-fold.c: Same. * gimple-ssa-sprintf.c: Same. * gimple-ssa-warn-restrict.c: Same. * passes.def: Add pass_warn_access. * tree-pass.h (make_pass_warn_access): Declare. * tree-ssa-strlen.c: Include new headers. * tree.c (fndecl_dealloc_argno): Move here from builtins.c. * tree.h (fndecl_dealloc_argno): Move here from attribs.h. * gimple-ssa-warn-access.cc: New file. * gimple-ssa-warn-access.h: New file. * pointer-query.cc: New file. * pointer-query.h: New file. gcc/cp/ChangeLog: * init.c: Include new header.
Diffstat (limited to 'gcc/builtins.c')
-rw-r--r--gcc/builtins.c3519
1 files changed, 2 insertions, 3517 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 170d776..845a8bb 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -80,6 +80,8 @@ along with GCC; see the file COPYING3. If not see
#include "attr-fnspec.h"
#include "demangle.h"
#include "gimple-range.h"
+#include "pointer-query.h"
+#include "gimple-ssa-warn-access.h"
struct target_builtins default_target_builtins;
#if SWITCHABLE_TARGET
@@ -185,8 +187,6 @@ static void maybe_emit_chk_warning (tree, enum built_in_function);
static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
static tree fold_builtin_object_size (tree, tree);
static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
-static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
- pointer_query *);
unsigned HOST_WIDE_INT target_newline;
unsigned HOST_WIDE_INT target_percent;
@@ -199,565 +199,6 @@ static tree do_mpfr_remquo (tree, tree, tree);
static tree do_mpfr_lgamma_r (tree, tree, tree);
static void expand_builtin_sync_synchronize (void);
-access_ref::access_ref (tree bound /* = NULL_TREE */,
- bool minaccess /* = false */)
-: ref (), eval ([](tree x){ return x; }), deref (), trail1special (true),
- base0 (true), parmarray ()
-{
- /* Set to valid. */
- offrng[0] = offrng[1] = 0;
- offmax[0] = offmax[1] = 0;
- /* Invalidate. */
- sizrng[0] = sizrng[1] = -1;
-
- /* Set the default bounds of the access and adjust below. */
- bndrng[0] = minaccess ? 1 : 0;
- bndrng[1] = HOST_WIDE_INT_M1U;
-
- /* When BOUND is nonnull and a range can be extracted from it,
- set the bounds of the access to reflect both it and MINACCESS.
- BNDRNG[0] is the size of the minimum access. */
- tree rng[2];
- if (bound && get_size_range (bound, rng, SR_ALLOW_ZERO))
- {
- bndrng[0] = wi::to_offset (rng[0]);
- bndrng[1] = wi::to_offset (rng[1]);
- bndrng[0] = bndrng[0] > 0 && minaccess ? 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 */,
- pointer_query *qry /* = 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. */
- pointer_query empty_qry;
- if (!qry)
- qry = &empty_qry;
-
- 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_r (arg, ostype, &phi_arg_ref, *psnlim, qry)
- || 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 (TREE_CODE (arg) == SSA_NAME)
- qry->put_ref (arg, phi_arg_ref);
-
- 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 (!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;
- }
-
- 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;
- }
-
- /* 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. */
-
-offset_int
-access_ref::size_remaining (offset_int *pmin /* = NULL */) const
-{
- offset_int minbuf;
- if (!pmin)
- pmin = &minbuf;
-
- /* add_offset() ensures the offset range isn't inverted. */
- gcc_checking_assert (offrng[0] <= offrng[1]);
-
- if (base0)
- {
- /* The offset into referenced object is zero-based (i.e., it's
- not referenced by a pointer into middle of some unknown object). */
- if (offrng[0] < 0 && offrng[1] < 0)
- {
- /* If the offset is negative the remaining size is zero. */
- *pmin = 0;
- return 0;
- }
-
- if (sizrng[1] <= offrng[0])
- {
- /* If the starting offset is greater than or equal to the upper
- bound on the size of the object, the space remaining is zero.
- As a special case, if it's equal, set *PMIN to -1 to let
- the caller know the offset is valid and just past the end. */
- *pmin = sizrng[1] == offrng[0] ? -1 : 0;
- return 0;
- }
-
- /* Otherwise return the size minus the lower bound of the offset. */
- offset_int or0 = offrng[0] < 0 ? 0 : offrng[0];
-
- *pmin = sizrng[0] - or0;
- return sizrng[1] - or0;
- }
-
- /* The offset to the referenced object isn't zero-based (i.e., it may
- refer to a byte other than the first. The size of such an object
- is constrained only by the size of the address space (the result
- of max_object_size()). */
- if (sizrng[1] <= offrng[0])
- {
- *pmin = 0;
- return 0;
- }
-
- offset_int or0 = offrng[0] < 0 ? 0 : offrng[0];
-
- *pmin = sizrng[0] - or0;
- return sizrng[1] - or0;
-}
-
-/* Return true if the offset and object size are in range for SIZE. */
-
-bool
-access_ref::offset_in_range (const offset_int &size) const
-{
- if (size_remaining () < size)
- return false;
-
- if (base0)
- return offmax[0] >= 0 && offmax[1] <= sizrng[1];
-
- offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
- return offmax[0] > -maxoff && offmax[1] < maxoff;
-}
-
-/* Add the range [MIN, MAX] to the offset range. For known objects (with
- zero-based offsets) at least one of whose offset's bounds is in range,
- constrain the other (or both) to the bounds of the object (i.e., zero
- and the upper bound of its size). This improves the quality of
- diagnostics. */
-
-void access_ref::add_offset (const offset_int &min, const offset_int &max)
-{
- if (min <= max)
- {
- /* To add an ordinary range just add it to the bounds. */
- offrng[0] += min;
- offrng[1] += max;
- }
- else if (!base0)
- {
- /* To add an inverted range to an offset to an unknown object
- expand it to the maximum. */
- add_max_offset ();
- return;
- }
- else
- {
- /* To add an inverted range to an offset to an known object set
- the upper bound to the maximum representable offset value
- (which may be greater than MAX_OBJECT_SIZE).
- The lower bound is either the sum of the current offset and
- MIN when abs(MAX) is greater than the former, or zero otherwise.
- Zero because then then inverted range includes the negative of
- the lower bound. */
- offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
- offrng[1] = maxoff;
-
- if (max >= 0)
- {
- offrng[0] = 0;
- if (offmax[0] > 0)
- offmax[0] = 0;
- return;
- }
-
- offset_int absmax = wi::abs (max);
- if (offrng[0] < absmax)
- {
- offrng[0] += min;
- /* Cap the lower bound at the upper (set to MAXOFF above)
- to avoid inadvertently recreating an inverted range. */
- if (offrng[1] < offrng[0])
- offrng[0] = offrng[1];
- }
- else
- offrng[0] = 0;
- }
-
- /* Set the minimum and maximmum computed so far. */
- if (offrng[1] < 0 && offrng[1] < offmax[0])
- offmax[0] = offrng[1];
- if (offrng[0] > 0 && offrng[0] > offmax[1])
- offmax[1] = offrng[0];
-
- if (!base0)
- return;
-
- /* When referencing a known object check to see if the offset computed
- so far is in bounds... */
- offset_int remrng[2];
- remrng[1] = size_remaining (remrng);
- if (remrng[1] > 0 || remrng[0] < 0)
- {
- /* ...if so, constrain it so that neither bound exceeds the size of
- the object. Out of bounds offsets are left unchanged, and, for
- better or worse, become in bounds later. They should be detected
- and diagnosed at the point they first become invalid by
- -Warray-bounds. */
- if (offrng[0] < 0)
- offrng[0] = 0;
- if (offrng[1] > sizrng[1])
- offrng[1] = sizrng[1];
- }
-}
-
-/* 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);
-}
-
-/* Default ctor. Initialize object with pointers to the range_query
- and cache_type instances to use or null. */
-
-pointer_query::pointer_query (range_query *qry /* = NULL */,
- cache_type *cache /* = NULL */)
-: rvals (qry), var_cache (cache), hits (), misses (),
- failures (), depth (), max_depth ()
-{
- /* No op. */
-}
-
-/* Return a pointer to the cached access_ref instance for the SSA_NAME
- PTR if it's there or null otherwise. */
-
-const access_ref *
-pointer_query::get_ref (tree ptr, int ostype /* = 1 */) const
-{
- if (!var_cache)
- {
- ++misses;
- return NULL;
- }
-
- unsigned version = SSA_NAME_VERSION (ptr);
- unsigned idx = version << 1 | (ostype & 1);
- if (var_cache->indices.length () <= idx)
- {
- ++misses;
- return NULL;
- }
-
- unsigned cache_idx = var_cache->indices[idx];
- if (var_cache->access_refs.length () <= cache_idx)
- {
- ++misses;
- return NULL;
- }
-
- access_ref &cache_ref = var_cache->access_refs[cache_idx];
- if (cache_ref.ref)
- {
- ++hits;
- return &cache_ref;
- }
-
- ++misses;
- return NULL;
-}
-
-/* Retrieve the access_ref instance for a variable from the cache if it's
- there or compute it and insert it into the cache if it's nonnonull. */
-
-bool
-pointer_query::get_ref (tree ptr, access_ref *pref, int ostype /* = 1 */)
-{
- const unsigned version
- = TREE_CODE (ptr) == SSA_NAME ? SSA_NAME_VERSION (ptr) : 0;
-
- if (var_cache && version)
- {
- unsigned idx = version << 1 | (ostype & 1);
- if (idx < var_cache->indices.length ())
- {
- unsigned cache_idx = var_cache->indices[idx] - 1;
- if (cache_idx < var_cache->access_refs.length ()
- && var_cache->access_refs[cache_idx].ref)
- {
- ++hits;
- *pref = var_cache->access_refs[cache_idx];
- return true;
- }
- }
-
- ++misses;
- }
-
- if (!compute_objsize (ptr, ostype, pref, this))
- {
- ++failures;
- return false;
- }
-
- return true;
-}
-
-/* Add a copy of the access_ref REF for the SSA_NAME to the cache if it's
- nonnull. */
-
-void
-pointer_query::put_ref (tree ptr, const access_ref &ref, int ostype /* = 1 */)
-{
- /* Only add populated/valid entries. */
- if (!var_cache || !ref.ref || ref.sizrng[0] < 0)
- return;
-
- /* Add REF to the two-level cache. */
- unsigned version = SSA_NAME_VERSION (ptr);
- unsigned idx = version << 1 | (ostype & 1);
-
- /* Grow INDICES if necessary. An index is valid if it's nonzero.
- Its value minus one is the index into ACCESS_REFS. Not all
- entries are valid. */
- if (var_cache->indices.length () <= idx)
- var_cache->indices.safe_grow_cleared (idx + 1);
-
- if (!var_cache->indices[idx])
- var_cache->indices[idx] = var_cache->access_refs.length () + 1;
-
- /* Grow ACCESS_REF cache if necessary. An entry is valid if its
- REF member is nonnull. All entries except for the last two
- are valid. Once nonnull, the REF value must stay unchanged. */
- unsigned cache_idx = var_cache->indices[idx];
- if (var_cache->access_refs.length () <= cache_idx)
- var_cache->access_refs.safe_grow_cleared (cache_idx + 1);
-
- access_ref cache_ref = var_cache->access_refs[cache_idx - 1];
- if (cache_ref.ref)
- {
- gcc_checking_assert (cache_ref.ref == ref.ref);
- return;
- }
-
- cache_ref = ref;
-}
-
-/* Flush the cache if it's nonnull. */
-
-void
-pointer_query::flush_cache ()
-{
- if (!var_cache)
- return;
- var_cache->indices.release ();
- var_cache->access_refs.release ();
-}
-
/* Return true if NAME starts with __builtin_ or __sync_. */
static bool
@@ -1106,218 +547,6 @@ string_length (const void *ptr, unsigned eltsize, unsigned maxelts)
return n;
}
-/* For a call EXPR at LOC to a function FNAME that expects a string
- in the argument ARG, issue a diagnostic due to it being a called
- with an argument that is a character array with no terminating
- NUL. SIZE is the EXACT size of the array, and BNDRNG the number
- of characters in which the NUL is expected. Either EXPR or FNAME
- may be null but noth both. SIZE may be null when BNDRNG is null. */
-
-void
-warn_string_no_nul (location_t loc, tree expr, const char *fname,
- tree arg, tree decl, tree size /* = NULL_TREE */,
- bool exact /* = false */,
- const wide_int bndrng[2] /* = NULL */)
-{
- const opt_code opt = OPT_Wstringop_overread;
- if ((expr && warning_suppressed_p (expr, opt))
- || warning_suppressed_p (arg, opt))
- return;
-
- loc = expansion_point_location_if_in_system_header (loc);
- bool warned;
-
- /* Format the bound range as a string to keep the nuber of messages
- from exploding. */
- char bndstr[80];
- *bndstr = 0;
- if (bndrng)
- {
- if (bndrng[0] == bndrng[1])
- sprintf (bndstr, "%llu", (unsigned long long) bndrng[0].to_uhwi ());
- else
- sprintf (bndstr, "[%llu, %llu]",
- (unsigned long long) bndrng[0].to_uhwi (),
- (unsigned long long) bndrng[1].to_uhwi ());
- }
-
- const tree maxobjsize = max_object_size ();
- const wide_int maxsiz = wi::to_wide (maxobjsize);
- if (expr)
- {
- tree func = get_callee_fndecl (expr);
- if (bndrng)
- {
- if (wi::ltu_p (maxsiz, bndrng[0]))
- warned = warning_at (loc, opt,
- "%qD specified bound %s exceeds "
- "maximum object size %E",
- func, bndstr, maxobjsize);
- else
- {
- bool maybe = wi::to_wide (size) == bndrng[0];
- warned = warning_at (loc, opt,
- exact
- ? G_("%qD specified bound %s exceeds "
- "the size %E of unterminated array")
- : (maybe
- ? G_("%qD specified bound %s may "
- "exceed the size of at most %E "
- "of unterminated array")
- : G_("%qD specified bound %s exceeds "
- "the size of at most %E "
- "of unterminated array")),
- func, bndstr, size);
- }
- }
- else
- warned = warning_at (loc, opt,
- "%qD argument missing terminating nul",
- func);
- }
- else
- {
- if (bndrng)
- {
- if (wi::ltu_p (maxsiz, bndrng[0]))
- warned = warning_at (loc, opt,
- "%qs specified bound %s exceeds "
- "maximum object size %E",
- fname, bndstr, maxobjsize);
- else
- {
- bool maybe = wi::to_wide (size) == bndrng[0];
- warned = warning_at (loc, opt,
- exact
- ? G_("%qs specified bound %s exceeds "
- "the size %E of unterminated array")
- : (maybe
- ? G_("%qs specified bound %s may "
- "exceed the size of at most %E "
- "of unterminated array")
- : G_("%qs specified bound %s exceeds "
- "the size of at most %E "
- "of unterminated array")),
- fname, bndstr, size);
- }
- }
- else
- warned = warning_at (loc, opt,
- "%qs argument missing terminating nul",
- fname);
- }
-
- if (warned)
- {
- inform (DECL_SOURCE_LOCATION (decl),
- "referenced argument declared here");
- suppress_warning (arg, opt);
- if (expr)
- suppress_warning (expr, opt);
- }
-}
-
-/* For a call EXPR (which may be null) that expects a string argument
- SRC as an argument, returns false if SRC is a character array with
- no terminating NUL. When nonnull, BOUND is the number of characters
- in which to expect the terminating NUL. RDONLY is true for read-only
- accesses such as strcmp, false for read-write such as strcpy. When
- EXPR is also issues a warning. */
-
-bool
-check_nul_terminated_array (tree expr, tree src,
- tree bound /* = NULL_TREE */)
-{
- /* The constant size of the array SRC points to. The actual size
- may be less of EXACT is true, but not more. */
- tree size;
- /* True if SRC involves a non-constant offset into the array. */
- bool exact;
- /* The unterminated constant array SRC points to. */
- tree nonstr = unterminated_array (src, &size, &exact);
- if (!nonstr)
- return true;
-
- /* NONSTR refers to the non-nul terminated constant array and SIZE
- is the constant size of the array in bytes. EXACT is true when
- SIZE is exact. */
-
- wide_int bndrng[2];
- if (bound)
- {
- value_range r;
-
- get_global_range_query ()->range_of_expr (r, bound);
-
- if (r.kind () != VR_RANGE)
- return true;
-
- bndrng[0] = r.lower_bound ();
- bndrng[1] = r.upper_bound ();
-
- if (exact)
- {
- if (wi::leu_p (bndrng[0], wi::to_wide (size)))
- return true;
- }
- else if (wi::lt_p (bndrng[0], wi::to_wide (size), UNSIGNED))
- return true;
- }
-
- if (expr)
- warn_string_no_nul (EXPR_LOCATION (expr), expr, NULL, src, nonstr,
- size, exact, bound ? bndrng : NULL);
-
- return false;
-}
-
-/* If EXP refers to an unterminated constant character array return
- the declaration of the object of which the array is a member or
- element and if SIZE is not null, set *SIZE to the size of
- the unterminated array and set *EXACT if the size is exact or
- clear it otherwise. Otherwise return null. */
-
-tree
-unterminated_array (tree exp, tree *size /* = NULL */, bool *exact /* = NULL */)
-{
- /* C_STRLEN will return NULL and set DECL in the info
- structure if EXP references a unterminated array. */
- c_strlen_data lendata = { };
- tree len = c_strlen (exp, 1, &lendata);
- if (len == NULL_TREE && lendata.minlen && lendata.decl)
- {
- if (size)
- {
- len = lendata.minlen;
- if (lendata.off)
- {
- /* Constant offsets are already accounted for in LENDATA.MINLEN,
- but not in a SSA_NAME + CST expression. */
- if (TREE_CODE (lendata.off) == INTEGER_CST)
- *exact = true;
- else if (TREE_CODE (lendata.off) == PLUS_EXPR
- && TREE_CODE (TREE_OPERAND (lendata.off, 1)) == INTEGER_CST)
- {
- /* Subtract the offset from the size of the array. */
- *exact = false;
- tree temp = TREE_OPERAND (lendata.off, 1);
- temp = fold_convert (ssizetype, temp);
- len = fold_build2 (MINUS_EXPR, ssizetype, len, temp);
- }
- else
- *exact = false;
- }
- else
- *exact = true;
-
- *size = len;
- }
- return lendata.decl;
- }
-
- return NULL_TREE;
-}
-
/* Compute the length of a null-terminated character string or wide
character string handling character sizes of 1, 2, and 4 bytes.
TREE_STRING_LENGTH is not the right way because it evaluates to
@@ -3969,996 +3198,6 @@ determine_block_size (tree len, rtx len_rtx,
GET_MODE_MASK (GET_MODE (len_rtx)));
}
-/* Issue a warning OPT for a bounded call EXP with a bound in RANGE
- accessing an object with SIZE. */
-
-static bool
-maybe_warn_for_bound (opt_code opt, location_t loc, tree exp, tree func,
- tree bndrng[2], tree size, const access_data *pad = NULL)
-{
- if (!bndrng[0] || warning_suppressed_p (exp, opt))
- return false;
-
- tree maxobjsize = max_object_size ();
-
- bool warned = false;
-
- 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,
- (maybe
- ? G_("%qD specified bound %E may "
- "exceed maximum object size %E")
- : G_("%qD specified bound %E "
- "exceeds maximum object size %E")),
- func, bndrng[0], maxobjsize)
- : warning_at (loc, opt,
- (maybe
- ? G_("specified bound %E may "
- "exceed maximum object size %E")
- : G_("specified bound %E "
- "exceeds maximum object size %E")),
- bndrng[0], maxobjsize));
- else
- warned = (func
- ? warning_at (loc, opt,
- (maybe
- ? G_("%qD specified bound [%E, %E] may "
- "exceed maximum object size %E")
- : G_("%qD specified bound [%E, %E] "
- "exceeds maximum object size %E")),
- func, bndrng[0], bndrng[1], maxobjsize)
- : warning_at (loc, opt,
- (maybe
- ? G_("specified bound [%E, %E] may "
- "exceed maximum object size %E")
- : G_("specified bound [%E, %E] "
- "exceeds maximum object size %E")),
- bndrng[0], bndrng[1], maxobjsize));
- }
- else if (!size || tree_int_cst_le (bndrng[0], size))
- return false;
- else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
- warned = (func
- ? warning_at (loc, opt,
- (maybe
- ? G_("%qD specified bound %E may exceed "
- "source size %E")
- : G_("%qD specified bound %E exceeds "
- "source size %E")),
- func, bndrng[0], size)
- : warning_at (loc, opt,
- (maybe
- ? G_("specified bound %E may exceed "
- "source size %E")
- : G_("specified bound %E exceeds "
- "source size %E")),
- bndrng[0], size));
- else
- warned = (func
- ? warning_at (loc, opt,
- (maybe
- ? G_("%qD specified bound [%E, %E] may "
- "exceed source size %E")
- : G_("%qD specified bound [%E, %E] exceeds "
- "source size %E")),
- func, bndrng[0], bndrng[1], size)
- : warning_at (loc, opt,
- (maybe
- ? G_("specified bound [%E, %E] may exceed "
- "source size %E")
- : G_("specified bound [%E, %E] exceeds "
- "source size %E")),
- bndrng[0], bndrng[1], size));
- if (warned)
- {
- if (pad && pad->src.ref)
- {
- if (DECL_P (pad->src.ref))
- inform (DECL_SOURCE_LOCATION (pad->src.ref),
- "source object declared here");
- else if (EXPR_HAS_LOCATION (pad->src.ref))
- inform (EXPR_LOCATION (pad->src.ref),
- "source object allocated here");
- }
- suppress_warning (exp, opt);
- }
-
- 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,
- (maybe
- ? G_("%qD specified size %E may "
- "exceed maximum object size %E")
- : G_("%qD specified size %E "
- "exceeds maximum object size %E")),
- func, bndrng[0], maxobjsize)
- : warning_at (loc, opt,
- (maybe
- ? G_("specified size %E may exceed "
- "maximum object size %E")
- : G_("specified size %E exceeds "
- "maximum object size %E")),
- bndrng[0], maxobjsize));
- else
- warned = (func
- ? warning_at (loc, opt,
- (maybe
- ? G_("%qD specified size between %E and %E "
- "may exceed maximum object size %E")
- : G_("%qD specified size between %E and %E "
- "exceeds maximum object size %E")),
- func, bndrng[0], bndrng[1], maxobjsize)
- : warning_at (loc, opt,
- (maybe
- ? G_("specified size between %E and %E "
- "may exceed maximum object size %E")
- : G_("specified size between %E and %E "
- "exceeds maximum object size %E")),
- bndrng[0], bndrng[1], maxobjsize));
- }
- else if (!size || tree_int_cst_le (bndrng[0], size))
- return false;
- else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
- warned = (func
- ? warning_at (loc, opt,
- (maybe
- ? G_("%qD specified bound %E may exceed "
- "destination size %E")
- : G_("%qD specified bound %E exceeds "
- "destination size %E")),
- func, bndrng[0], size)
- : warning_at (loc, opt,
- (maybe
- ? G_("specified bound %E may exceed "
- "destination size %E")
- : G_("specified bound %E exceeds "
- "destination size %E")),
- bndrng[0], size));
- else
- warned = (func
- ? warning_at (loc, opt,
- (maybe
- ? G_("%qD specified bound [%E, %E] may exceed "
- "destination size %E")
- : G_("%qD specified bound [%E, %E] exceeds "
- "destination size %E")),
- func, bndrng[0], bndrng[1], size)
- : warning_at (loc, opt,
- (maybe
- ? G_("specified bound [%E, %E] exceeds "
- "destination size %E")
- : G_("specified bound [%E, %E] exceeds "
- "destination size %E")),
- bndrng[0], bndrng[1], size));
-
- if (warned)
- {
- if (pad && pad->dst.ref)
- {
- if (DECL_P (pad->dst.ref))
- inform (DECL_SOURCE_LOCATION (pad->dst.ref),
- "destination object declared here");
- else if (EXPR_HAS_LOCATION (pad->dst.ref))
- inform (EXPR_LOCATION (pad->dst.ref),
- "destination object allocated here");
- }
- suppress_warning (exp, opt);
- }
-
- return warned;
-}
-
-/* For an expression EXP issue an access warning controlled by option OPT
- with access to a region SIZE bytes in size in the RANGE of sizes.
- WRITE is true for a write access, READ for a read access, neither for
- call that may or may not perform an access but for which the range
- is expected to valid.
- Returns true when a warning has been issued. */
-
-static bool
-warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
- tree size, bool write, bool read, bool maybe)
-{
- bool warned = false;
-
- if (write && read)
- {
- if (tree_int_cst_equal (range[0], range[1]))
- warned = (func
- ? warning_n (loc, opt, tree_to_uhwi (range[0]),
- (maybe
- ? G_("%qD may access %E byte in a region "
- "of size %E")
- : G_("%qD accessing %E byte in a region "
- "of size %E")),
- (maybe
- ? G_ ("%qD may access %E bytes in a region "
- "of size %E")
- : G_ ("%qD accessing %E bytes in a region "
- "of size %E")),
- func, range[0], size)
- : warning_n (loc, opt, tree_to_uhwi (range[0]),
- (maybe
- ? G_("may access %E byte in a region "
- "of size %E")
- : G_("accessing %E byte in a region "
- "of size %E")),
- (maybe
- ? G_("may access %E bytes in a region "
- "of size %E")
- : G_("accessing %E bytes in a region "
- "of size %E")),
- 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,
- (maybe
- ? G_("%qD may access %E or more bytes "
- "in a region of size %E")
- : G_("%qD accessing %E or more bytes "
- "in a region of size %E")),
- func, range[0], size)
- : warning_at (loc, opt,
- (maybe
- ? G_("may access %E or more bytes "
- "in a region of size %E")
- : G_("accessing %E or more bytes "
- "in a region of size %E")),
- range[0], size));
- }
- else
- warned = (func
- ? warning_at (loc, opt,
- (maybe
- ? G_("%qD may access between %E and %E "
- "bytes in a region of size %E")
- : G_("%qD accessing between %E and %E "
- "bytes in a region of size %E")),
- func, range[0], range[1], size)
- : warning_at (loc, opt,
- (maybe
- ? G_("may access between %E and %E bytes "
- "in a region of size %E")
- : G_("accessing between %E and %E bytes "
- "in a region of size %E")),
- range[0], range[1], size));
- return warned;
- }
-
- if (write)
- {
- if (tree_int_cst_equal (range[0], range[1]))
- warned = (func
- ? warning_n (loc, opt, tree_to_uhwi (range[0]),
- (maybe
- ? G_("%qD may write %E byte into a region "
- "of size %E")
- : G_("%qD writing %E byte into a region "
- "of size %E overflows the destination")),
- (maybe
- ? G_("%qD may write %E bytes into a region "
- "of size %E")
- : G_("%qD writing %E bytes into a region "
- "of size %E overflows the destination")),
- func, range[0], size)
- : warning_n (loc, opt, tree_to_uhwi (range[0]),
- (maybe
- ? G_("may write %E byte into a region "
- "of size %E")
- : G_("writing %E byte into a region "
- "of size %E overflows the destination")),
- (maybe
- ? G_("may write %E bytes into a region "
- "of size %E")
- : G_("writing %E bytes into a region "
- "of size %E overflows the destination")),
- 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,
- (maybe
- ? G_("%qD may write %E or more bytes "
- "into a region of size %E")
- : G_("%qD writing %E or more bytes "
- "into a region of size %E overflows "
- "the destination")),
- func, range[0], size)
- : warning_at (loc, opt,
- (maybe
- ? G_("may write %E or more bytes into "
- "a region of size %E")
- : G_("writing %E or more bytes into "
- "a region of size %E overflows "
- "the destination")),
- range[0], size));
- }
- else
- warned = (func
- ? warning_at (loc, opt,
- (maybe
- ? G_("%qD may write between %E and %E bytes "
- "into a region of size %E")
- : G_("%qD writing between %E and %E bytes "
- "into a region of size %E overflows "
- "the destination")),
- func, range[0], range[1], size)
- : warning_at (loc, opt,
- (maybe
- ? G_("may write between %E and %E bytes "
- "into a region of size %E")
- : G_("writing between %E and %E bytes "
- "into a region of size %E overflows "
- "the destination")),
- range[0], range[1], size));
- return warned;
- }
-
- if (read)
- {
- if (tree_int_cst_equal (range[0], range[1]))
- warned = (func
- ? warning_n (loc, OPT_Wstringop_overread,
- tree_to_uhwi (range[0]),
- (maybe
- ? G_("%qD may read %E byte from a region "
- "of size %E")
- : G_("%qD reading %E byte from a region "
- "of size %E")),
- (maybe
- ? G_("%qD may read %E bytes from a region "
- "of size %E")
- : G_("%qD reading %E bytes from a region "
- "of size %E")),
- func, range[0], size)
- : warning_n (loc, OPT_Wstringop_overread,
- tree_to_uhwi (range[0]),
- (maybe
- ? G_("may read %E byte from a region "
- "of size %E")
- : G_("reading %E byte from a region "
- "of size %E")),
- (maybe
- ? G_("may read %E bytes from a region "
- "of size %E")
- : G_("reading %E bytes from a region "
- "of size %E")),
- 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,
- (maybe
- ? G_("%qD may read %E or more bytes "
- "from a region of size %E")
- : G_("%qD reading %E or more bytes "
- "from a region of size %E")),
- func, range[0], size)
- : warning_at (loc, OPT_Wstringop_overread,
- (maybe
- ? G_("may read %E or more bytes "
- "from a region of size %E")
- : G_("reading %E or more bytes "
- "from a region of size %E")),
- range[0], size));
- }
- else
- warned = (func
- ? warning_at (loc, OPT_Wstringop_overread,
- (maybe
- ? G_("%qD may read between %E and %E bytes "
- "from a region of size %E")
- : G_("%qD reading between %E and %E bytes "
- "from a region of size %E")),
- func, range[0], range[1], size)
- : warning_at (loc, opt,
- (maybe
- ? G_("may read between %E and %E bytes "
- "from a region of size %E")
- : G_("reading between %E and %E bytes "
- "from a region of size %E")),
- range[0], range[1], size));
-
- if (warned)
- suppress_warning (exp, OPT_Wstringop_overread);
-
- return warned;
- }
-
- if (tree_int_cst_equal (range[0], range[1])
- || tree_int_cst_sign_bit (range[1]))
- warned = (func
- ? warning_n (loc, OPT_Wstringop_overread,
- tree_to_uhwi (range[0]),
- "%qD expecting %E byte in a region of size %E",
- "%qD expecting %E bytes in a region of size %E",
- func, range[0], size)
- : warning_n (loc, OPT_Wstringop_overread,
- tree_to_uhwi (range[0]),
- "expecting %E byte in a region of size %E",
- "expecting %E bytes in a region of size %E",
- 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,
- "%qD expecting %E or more bytes in a region "
- "of size %E",
- func, range[0], size)
- : warning_at (loc, OPT_Wstringop_overread,
- "expecting %E or more bytes in a region "
- "of size %E",
- range[0], size));
- }
- else
- warned = (func
- ? warning_at (loc, OPT_Wstringop_overread,
- "%qD expecting between %E and %E bytes in "
- "a region of size %E",
- func, range[0], range[1], size)
- : warning_at (loc, OPT_Wstringop_overread,
- "expecting between %E and %E bytes in "
- "a region of size %E",
- range[0], range[1], size));
-
- if (warned)
- suppress_warning (exp, OPT_Wstringop_overread);
-
- return warned;
-}
-
-/* Issue one inform message describing each target of an access REF.
- WRITE is set for a write access and clear for a read access. */
-
-void
-access_ref::inform_access (access_mode mode) const
-{
- 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 (aref.offrng[0]))
- minoff = aref.offrng[0].to_shwi ();
- else
- minoff = aref.offrng[0] < 0 ? diff_min : diff_max;
-
- 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
- of the maximum of ptrdiff_t. */
- maxoff = minoff;
-
- /* Convert size range and always include it since all sizes are
- meaningful. */
- unsigned long long minsize = 0, maxsize = 0;
- if (wi::fits_shwi_p (aref.sizrng[0])
- && wi::fits_shwi_p (aref.sizrng[1]))
- {
- 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];
- 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);
-
- 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);
- if (SSA_NAME_IDENTIFIER (ref))
- {
- 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);
- }
-
- 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 (*offstr)
- inform (loc, "at offset %s into destination object %qE of size %s",
- offstr, ref, sizestr);
- else
- inform (loc, "destination object %qE of size %s", ref, sizestr);
- return;
- }
-
- if (*offstr)
- inform (loc,
- "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 (mode == access_read_only)
- {
- if (allocfn == NULL_TREE)
- {
- if (*offstr)
- inform (loc, "at offset %s into source object %qE of size %s",
- offstr, ref, sizestr);
- else
- inform (loc, "source object %qE of size %s", ref, sizestr);
-
- return;
- }
-
- if (*offstr)
- inform (loc,
- "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);
- return;
- }
-
- if (allocfn == NULL_TREE)
- {
- if (*offstr)
- inform (loc, "at offset %s into object %qE of size %s",
- offstr, ref, sizestr);
- else
- inform (loc, "object %qE of size %s", ref, sizestr);
-
- return;
- }
-
- if (*offstr)
- inform (loc,
- "at offset %s into object of size %s allocated by %qE",
- offstr, sizestr, allocfn);
- else
- inform (loc, "object of size %s allocated by %qE",
- sizestr, allocfn);
-}
-
-/* Helper to set RANGE to the range of BOUND if it's nonnull, bounded
- by BNDRNG if nonnull and valid. */
-
-static void
-get_size_range (tree bound, tree range[2], const offset_int bndrng[2])
-{
- if (bound)
- get_size_range (bound, range);
-
- if (!bndrng || (bndrng[0] == 0 && bndrng[1] == HOST_WIDE_INT_M1U))
- return;
-
- if (range[0] && TREE_CODE (range[0]) == INTEGER_CST)
- {
- offset_int r[] =
- { wi::to_offset (range[0]), wi::to_offset (range[1]) };
- if (r[0] < bndrng[0])
- range[0] = wide_int_to_tree (sizetype, bndrng[0]);
- if (bndrng[1] < r[1])
- range[1] = wide_int_to_tree (sizetype, bndrng[1]);
- }
- else
- {
- range[0] = wide_int_to_tree (sizetype, bndrng[0]);
- range[1] = wide_int_to_tree (sizetype, bndrng[1]);
- }
-}
-
-/* Try to verify that the sizes and lengths of the arguments to a string
- manipulation function given by EXP are within valid bounds and that
- the operation does not lead to buffer overflow or read past the end.
- Arguments other than EXP may be null. When non-null, the arguments
- have the following meaning:
- DST is the destination of a copy call or NULL otherwise.
- SRC is the source of a copy call or NULL otherwise.
- DSTWRITE is the number of bytes written into the destination obtained
- from the user-supplied size argument to the function (such as in
- memcpy(DST, SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
- MAXREAD is the user-supplied bound on the length of the source sequence
- (such as in strncat(d, s, N). It specifies the upper limit on the number
- of bytes to write. If NULL, it's taken to be the same as DSTWRITE.
- SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
- expression EXP is a string function call (as opposed to a memory call
- like memcpy). As an exception, SRCSTR can also be an integer denoting
- the precomputed size of the source string or object (for functions like
- memcpy).
- DSTSIZE is the size of the destination object.
-
- When DSTWRITE is null LEN is checked to verify that it doesn't exceed
- SIZE_MAX.
-
- WRITE is true for write accesses, READ is true for reads. Both are
- false for simple size checks in calls to functions that neither read
- from nor write to the region.
-
- When nonnull, PAD points to a more detailed description of the access.
-
- If the call is successfully verified as safe return true, otherwise
- return false. */
-
-bool
-check_access (tree exp, tree dstwrite,
- tree maxread, tree srcstr, tree dstsize,
- access_mode mode, const access_data *pad /* = NULL */)
-{
- /* The size of the largest object is half the address space, or
- PTRDIFF_MAX. (This is way too permissive.) */
- tree maxobjsize = max_object_size ();
-
- /* Either an approximate/minimum the length of the source string for
- string functions or the size of the source object for raw memory
- functions. */
- tree slen = NULL_TREE;
-
- /* The range of the access in bytes; first set to the write access
- for functions that write and then read for those that also (or
- just) read. */
- tree range[2] = { NULL_TREE, NULL_TREE };
-
- /* Set to true when the exact number of bytes written by a string
- function like strcpy is not known and the only thing that is
- known is that it must be at least one (for the terminating nul). */
- bool at_least_one = false;
- if (srcstr)
- {
- /* SRCSTR is normally a pointer to string but as a special case
- it can be an integer denoting the length of a string. */
- if (POINTER_TYPE_P (TREE_TYPE (srcstr)))
- {
- if (!check_nul_terminated_array (exp, srcstr, maxread))
- return false;
- /* Try to determine the range of lengths the source string
- refers to. If it can be determined and is less than
- the upper bound given by MAXREAD add one to it for
- the terminating nul. Otherwise, set it to one for
- the same reason, or to MAXREAD as appropriate. */
- c_strlen_data lendata = { };
- get_range_strlen (srcstr, &lendata, /* eltsize = */ 1);
- range[0] = lendata.minlen;
- range[1] = lendata.maxbound ? lendata.maxbound : lendata.maxlen;
- if (range[0]
- && TREE_CODE (range[0]) == INTEGER_CST
- && TREE_CODE (range[1]) == INTEGER_CST
- && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
- {
- if (maxread && tree_int_cst_le (maxread, range[0]))
- range[0] = range[1] = maxread;
- else
- range[0] = fold_build2 (PLUS_EXPR, size_type_node,
- range[0], size_one_node);
-
- if (maxread && tree_int_cst_le (maxread, range[1]))
- range[1] = maxread;
- else if (!integer_all_onesp (range[1]))
- range[1] = fold_build2 (PLUS_EXPR, size_type_node,
- range[1], size_one_node);
-
- slen = range[0];
- }
- else
- {
- at_least_one = true;
- slen = size_one_node;
- }
- }
- else
- slen = srcstr;
- }
-
- if (!dstwrite && !maxread)
- {
- /* When the only available piece of data is the object size
- there is nothing to do. */
- if (!slen)
- return true;
-
- /* Otherwise, when the length of the source sequence is known
- (as with strlen), set DSTWRITE to it. */
- if (!range[0])
- dstwrite = slen;
- }
-
- if (!dstsize)
- dstsize = maxobjsize;
-
- /* Set RANGE to that of DSTWRITE if non-null, bounded by PAD->DST.BNDRNG
- if valid. */
- get_size_range (dstwrite, range, pad ? pad->dst.bndrng : NULL);
-
- tree func = get_callee_fndecl (exp);
- /* Read vs write access by built-ins can be determined from the const
- qualifiers on the pointer argument. In the absence of attribute
- access, non-const qualified pointer arguments to user-defined
- functions are assumed to both read and write the objects. */
- const bool builtin = func ? fndecl_built_in_p (func) : false;
-
- /* First check the number of bytes to be written against the maximum
- object size. */
- if (range[0]
- && TREE_CODE (range[0]) == INTEGER_CST
- && tree_int_cst_lt (maxobjsize, range[0]))
- {
- location_t loc = EXPR_LOCATION (exp);
- maybe_warn_for_bound (OPT_Wstringop_overflow_, loc, exp, func, range,
- NULL_TREE, pad);
- return false;
- }
-
- /* The number of bytes to write is "exact" if DSTWRITE is non-null,
- constant, and in range of unsigned HOST_WIDE_INT. */
- bool exactwrite = dstwrite && tree_fits_uhwi_p (dstwrite);
-
- /* Next check the number of bytes to be written against the destination
- object size. */
- if (range[0] || !exactwrite || integer_all_onesp (dstwrite))
- {
- if (range[0]
- && TREE_CODE (range[0]) == INTEGER_CST
- && ((tree_fits_uhwi_p (dstsize)
- && tree_int_cst_lt (dstsize, range[0]))
- || (dstwrite
- && tree_fits_uhwi_p (dstwrite)
- && tree_int_cst_lt (dstwrite, range[0]))))
- {
- const opt_code opt = OPT_Wstringop_overflow_;
- if (warning_suppressed_p (exp, opt)
- || (pad && pad->dst.ref
- && warning_suppressed_p (pad->dst.ref, opt)))
- return false;
-
- location_t loc = EXPR_LOCATION (exp);
- bool warned = false;
- if (dstwrite == slen && at_least_one)
- {
- /* This is a call to strcpy with a destination of 0 size
- and a source of unknown length. The call will write
- at least one byte past the end of the destination. */
- warned = (func
- ? warning_at (loc, opt,
- "%qD writing %E or more bytes into "
- "a region of size %E overflows "
- "the destination",
- func, range[0], dstsize)
- : warning_at (loc, opt,
- "writing %E or more bytes into "
- "a region of size %E overflows "
- "the destination",
- range[0], dstsize));
- }
- else
- {
- const bool read
- = 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, maybe);
- }
-
- if (warned)
- {
- suppress_warning (exp, OPT_Wstringop_overflow_);
- if (pad)
- pad->dst.inform_access (pad->mode);
- }
-
- /* Return error when an overflow has been detected. */
- return false;
- }
- }
-
- /* Check the maximum length of the source sequence against the size
- of the destination object if known, or against the maximum size
- of an object. */
- if (maxread)
- {
- /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
- PAD is nonnull and BNDRNG is valid. */
- get_size_range (maxread, range, pad ? pad->src.bndrng : NULL);
-
- location_t loc = EXPR_LOCATION (exp);
- tree size = dstsize;
- if (pad && pad->mode == access_read_only)
- size = wide_int_to_tree (sizetype, pad->src.sizrng[1]);
-
- if (range[0] && maxread && tree_fits_uhwi_p (size))
- {
- if (tree_int_cst_lt (maxobjsize, range[0]))
- {
- maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
- range, size, pad);
- return false;
- }
-
- if (size != maxobjsize && tree_int_cst_lt (size, range[0]))
- {
- opt_code opt = (dstwrite || mode != access_read_only
- ? OPT_Wstringop_overflow_
- : OPT_Wstringop_overread);
- maybe_warn_for_bound (opt, loc, exp, func, range, size, pad);
- return false;
- }
- }
-
- maybe_warn_nonstring_arg (func, exp);
- }
-
- /* Check for reading past the end of SRC. */
- bool overread = (slen
- && slen == srcstr
- && dstwrite
- && range[0]
- && TREE_CODE (slen) == INTEGER_CST
- && tree_int_cst_lt (slen, range[0]));
- /* If none is determined try to get a better answer based on the details
- in PAD. */
- if (!overread
- && pad
- && pad->src.sizrng[1] >= 0
- && pad->src.offrng[0] >= 0
- && (pad->src.offrng[1] < 0
- || pad->src.offrng[0] <= pad->src.offrng[1]))
- {
- /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
- PAD is nonnull and BNDRNG is valid. */
- get_size_range (maxread, range, pad ? pad->src.bndrng : NULL);
- /* Set OVERREAD for reads starting just past the end of an object. */
- overread = pad->src.sizrng[1] - pad->src.offrng[0] < pad->src.bndrng[0];
- range[0] = wide_int_to_tree (sizetype, pad->src.bndrng[0]);
- slen = size_zero_node;
- }
-
- if (overread)
- {
- const opt_code opt = OPT_Wstringop_overread;
- if (warning_suppressed_p (exp, opt)
- || (srcstr && warning_suppressed_p (srcstr, opt))
- || (pad && pad->src.ref
- && warning_suppressed_p (pad->src.ref, opt)))
- return false;
-
- location_t loc = EXPR_LOCATION (exp);
- 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, range, slen, false, read,
- maybe))
- {
- suppress_warning (exp, opt);
- if (pad)
- pad->src.inform_access (access_read_only);
- }
- return false;
- }
-
- return true;
-}
-
/* A convenience wrapper for check_access above to check access
by a read-only function like puts. */
@@ -4978,1050 +3217,6 @@ check_read_access (tree exp, tree src, tree bound /* = NULL_TREE */,
&data);
}
-/* If STMT is a call to an allocation function, returns the constant
- maximum size of the object allocated by the call represented as
- sizetype. If nonnull, sets RNG1[] to the range of the size.
- When nonnull, uses RVALS for range information, otherwise gets global
- range info.
- Returns null when STMT is not a call to a valid allocation function. */
-
-tree
-gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
- range_query * /* = NULL */)
-{
- if (!stmt || !is_gimple_call (stmt))
- return NULL_TREE;
-
- tree allocfntype;
- if (tree fndecl = gimple_call_fndecl (stmt))
- allocfntype = TREE_TYPE (fndecl);
- else
- allocfntype = gimple_call_fntype (stmt);
-
- if (!allocfntype)
- return NULL_TREE;
-
- unsigned argidx1 = UINT_MAX, argidx2 = UINT_MAX;
- tree at = lookup_attribute ("alloc_size", TYPE_ATTRIBUTES (allocfntype));
- if (!at)
- {
- if (!gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
- return NULL_TREE;
-
- argidx1 = 0;
- }
-
- unsigned nargs = gimple_call_num_args (stmt);
-
- if (argidx1 == UINT_MAX)
- {
- tree atval = TREE_VALUE (at);
- if (!atval)
- return NULL_TREE;
-
- argidx1 = TREE_INT_CST_LOW (TREE_VALUE (atval)) - 1;
- if (nargs <= argidx1)
- return NULL_TREE;
-
- atval = TREE_CHAIN (atval);
- if (atval)
- {
- argidx2 = TREE_INT_CST_LOW (TREE_VALUE (atval)) - 1;
- if (nargs <= argidx2)
- return NULL_TREE;
- }
- }
-
- tree size = gimple_call_arg (stmt, argidx1);
-
- wide_int rng1_buf[2];
- /* If RNG1 is not set, use the buffer. */
- if (!rng1)
- rng1 = rng1_buf;
-
- /* Use maximum precision to avoid overflow below. */
- const int prec = ADDR_MAX_PRECISION;
-
- {
- tree r[2];
- /* Determine the largest valid range size, including zero. */
- if (!get_size_range (size, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
- return NULL_TREE;
- rng1[0] = wi::to_wide (r[0], prec);
- rng1[1] = wi::to_wide (r[1], prec);
- }
-
- if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST)
- return fold_convert (sizetype, size);
-
- /* To handle ranges do the math in wide_int and return the product
- of the upper bounds as a constant. Ignore anti-ranges. */
- tree n = argidx2 < nargs ? gimple_call_arg (stmt, argidx2) : integer_one_node;
- wide_int rng2[2];
- {
- tree r[2];
- /* As above, use the full non-negative range on failure. */
- if (!get_size_range (n, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
- return NULL_TREE;
- rng2[0] = wi::to_wide (r[0], prec);
- rng2[1] = wi::to_wide (r[1], prec);
- }
-
- /* Compute products of both bounds for the caller but return the lesser
- of SIZE_MAX and the product of the upper bounds as a constant. */
- rng1[0] = rng1[0] * rng2[0];
- rng1[1] = rng1[1] * rng2[1];
-
- const tree size_max = TYPE_MAX_VALUE (sizetype);
- if (wi::gtu_p (rng1[1], wi::to_wide (size_max, prec)))
- {
- rng1[1] = wi::to_wide (size_max, prec);
- return size_max;
- }
-
- return wide_int_to_tree (sizetype, rng1[1]);
-}
-
-/* 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],
- 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
- related). */
- tree var = SSA_NAME_VAR (ptr);
- if (TREE_CODE (var) != PARM_DECL)
- return NULL_TREE;
-
- const unsigned prec = TYPE_PRECISION (sizetype);
-
- rdwr_map rdwr_idx;
- attr_access *access = get_parm_access (rdwr_idx, var);
- if (!access)
- return NULL_TREE;
-
- if (access->sizarg != UINT_MAX)
- {
- /* TODO: Try to extract the range from the argument based on
- those of subsequent assertions or based on known calls to
- the current function. */
- return NULL_TREE;
- }
-
- if (!access->minsize)
- return NULL_TREE;
-
- /* Only consider ordinary array bound at level 2 (or above if it's
- ever added). */
- 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
- of what the pointer argument to which it decays points to. */
- tree eltype = TREE_TYPE (TREE_TYPE (ptr));
- tree size = TYPE_SIZE_UNIT (eltype);
- if (!size || TREE_CODE (size) != INTEGER_CST)
- return NULL_TREE;
-
- rng[1] *= wi::to_wide (size, prec);
- return var;
-}
-
-/* Wrapper around the wide_int overload of get_range that accepts
- offset_int instead. For middle end expressions returns the same
- result. For a subset of nonconstamt expressions emitted by the front
- end determines a more precise range than would be possible otherwise. */
-
-static bool
-get_offset_range (tree x, gimple *stmt, offset_int r[2], range_query *rvals)
-{
- offset_int add = 0;
- if (TREE_CODE (x) == PLUS_EXPR)
- {
- /* Handle constant offsets in pointer addition expressions seen
- n the front end IL. */
- tree op = TREE_OPERAND (x, 1);
- if (TREE_CODE (op) == INTEGER_CST)
- {
- op = fold_convert (signed_type_for (TREE_TYPE (op)), op);
- add = wi::to_offset (op);
- x = TREE_OPERAND (x, 0);
- }
- }
-
- if (TREE_CODE (x) == NOP_EXPR)
- /* Also handle conversions to sizetype seen in the front end IL. */
- x = TREE_OPERAND (x, 0);
-
- tree type = TREE_TYPE (x);
- if (!INTEGRAL_TYPE_P (type) && !POINTER_TYPE_P (type))
- return false;
-
- if (TREE_CODE (x) != INTEGER_CST
- && TREE_CODE (x) != SSA_NAME)
- {
- if (TYPE_UNSIGNED (type)
- && TYPE_PRECISION (type) == TYPE_PRECISION (sizetype))
- type = signed_type_for (type);
-
- r[0] = wi::to_offset (TYPE_MIN_VALUE (type)) + add;
- r[1] = wi::to_offset (TYPE_MAX_VALUE (type)) + add;
- return x;
- }
-
- wide_int wr[2];
- if (!get_range (x, stmt, wr, rvals))
- return false;
-
- signop sgn = SIGNED;
- /* Only convert signed integers or unsigned sizetype to a signed
- offset and avoid converting large positive values in narrower
- types to negative offsets. */
- if (TYPE_UNSIGNED (type)
- && wr[0].get_precision () < TYPE_PRECISION (sizetype))
- sgn = UNSIGNED;
-
- r[0] = offset_int::from (wr[0], sgn);
- r[1] = offset_int::from (wr[1], sgn);
- return true;
-}
-
-/* Return the argument that the call STMT to a built-in function returns
- or null if it doesn't. On success, set OFFRNG[] to the range of offsets
- from the argument reflected in the value returned by the built-in if it
- can be determined, otherwise to 0 and HWI_M1U respectively. Set
- *PAST_END for functions like mempcpy that might return a past the end
- pointer (most functions return a dereferenceable pointer to an existing
- element of an array). */
-
-static tree
-gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
- range_query *rvals)
-{
- /* Clear and set below for the rare function(s) that might return
- a past-the-end pointer. */
- *past_end = false;
-
- {
- /* Check for attribute fn spec to see if the function returns one
- of its arguments. */
- attr_fnspec fnspec = gimple_call_fnspec (as_a <gcall *>(stmt));
- unsigned int argno;
- if (fnspec.returns_arg (&argno))
- {
- /* Functions return the first argument (not a range). */
- offrng[0] = offrng[1] = 0;
- return gimple_call_arg (stmt, argno);
- }
- }
-
- if (gimple_call_num_args (stmt) < 1)
- return NULL_TREE;
-
- tree fn = gimple_call_fndecl (stmt);
- if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
- {
- /* See if this is a call to placement new. */
- if (!fn
- || !DECL_IS_OPERATOR_NEW_P (fn)
- || DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fn))
- return NULL_TREE;
-
- /* Check the mangling, keeping in mind that operator new takes
- a size_t which could be unsigned int or unsigned long. */
- tree fname = DECL_ASSEMBLER_NAME (fn);
- if (!id_equal (fname, "_ZnwjPv") // ordinary form
- && !id_equal (fname, "_ZnwmPv") // ordinary form
- && !id_equal (fname, "_ZnajPv") // array form
- && !id_equal (fname, "_ZnamPv")) // array form
- return NULL_TREE;
-
- if (gimple_call_num_args (stmt) != 2)
- return NULL_TREE;
-
- /* Allocation functions return a pointer to the beginning. */
- offrng[0] = offrng[1] = 0;
- return gimple_call_arg (stmt, 1);
- }
-
- switch (DECL_FUNCTION_CODE (fn))
- {
- case BUILT_IN_MEMCPY:
- case BUILT_IN_MEMCPY_CHK:
- case BUILT_IN_MEMMOVE:
- case BUILT_IN_MEMMOVE_CHK:
- case BUILT_IN_MEMSET:
- case BUILT_IN_STRCAT:
- case BUILT_IN_STRCAT_CHK:
- case BUILT_IN_STRCPY:
- case BUILT_IN_STRCPY_CHK:
- case BUILT_IN_STRNCAT:
- case BUILT_IN_STRNCAT_CHK:
- case BUILT_IN_STRNCPY:
- case BUILT_IN_STRNCPY_CHK:
- /* Functions return the first argument (not a range). */
- offrng[0] = offrng[1] = 0;
- return gimple_call_arg (stmt, 0);
-
- case BUILT_IN_MEMPCPY:
- case BUILT_IN_MEMPCPY_CHK:
- {
- /* The returned pointer is in a range constrained by the smaller
- of the upper bound of the size argument and the source object
- size. */
- offrng[0] = 0;
- offrng[1] = HOST_WIDE_INT_M1U;
- tree off = gimple_call_arg (stmt, 2);
- bool off_valid = get_offset_range (off, stmt, offrng, rvals);
- if (!off_valid || offrng[0] != offrng[1])
- {
- /* If the offset is either indeterminate or in some range,
- try to constrain its upper bound to at most the size
- of the source object. */
- access_ref aref;
- tree src = gimple_call_arg (stmt, 1);
- if (compute_objsize (src, 1, &aref, rvals)
- && aref.sizrng[1] < offrng[1])
- offrng[1] = aref.sizrng[1];
- }
-
- /* Mempcpy may return a past-the-end pointer. */
- *past_end = true;
- return gimple_call_arg (stmt, 0);
- }
-
- case BUILT_IN_MEMCHR:
- {
- tree off = gimple_call_arg (stmt, 2);
- if (get_offset_range (off, stmt, offrng, rvals))
- offrng[1] -= 1;
- else
- offrng[1] = HOST_WIDE_INT_M1U;
-
- offrng[0] = 0;
- return gimple_call_arg (stmt, 0);
- }
-
- case BUILT_IN_STRCHR:
- case BUILT_IN_STRRCHR:
- case BUILT_IN_STRSTR:
- offrng[0] = 0;
- offrng[1] = HOST_WIDE_INT_M1U;
- return gimple_call_arg (stmt, 0);
-
- case BUILT_IN_STPCPY:
- case BUILT_IN_STPCPY_CHK:
- {
- access_ref aref;
- tree src = gimple_call_arg (stmt, 1);
- if (compute_objsize (src, 1, &aref, rvals))
- offrng[1] = aref.sizrng[1] - 1;
- else
- offrng[1] = HOST_WIDE_INT_M1U;
-
- offrng[0] = 0;
- return gimple_call_arg (stmt, 0);
- }
-
- case BUILT_IN_STPNCPY:
- case BUILT_IN_STPNCPY_CHK:
- {
- /* The returned pointer is in a range between the first argument
- and it plus the smaller of the upper bound of the size argument
- and the source object size. */
- offrng[1] = HOST_WIDE_INT_M1U;
- tree off = gimple_call_arg (stmt, 2);
- if (!get_offset_range (off, stmt, offrng, rvals)
- || offrng[0] != offrng[1])
- {
- /* If the offset is either indeterminate or in some range,
- try to constrain its upper bound to at most the size
- of the source object. */
- access_ref aref;
- tree src = gimple_call_arg (stmt, 1);
- if (compute_objsize (src, 1, &aref, rvals)
- && aref.sizrng[1] < offrng[1])
- offrng[1] = aref.sizrng[1];
- }
-
- /* When the source is the empty string the returned pointer is
- a copy of the argument. Otherwise stpcpy can also return
- a past-the-end pointer. */
- offrng[0] = 0;
- *past_end = true;
- return gimple_call_arg (stmt, 0);
- }
-
- default:
- break;
- }
-
- return NULL_TREE;
-}
-
-/* A helper of compute_objsize_r() 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, pointer_query *qry)
-{
- 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_r (ptr, ostype, &aref[0], snlim, qry))
- {
- 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_r (ptr, ostype, &aref[1], snlim, qry))
- {
- 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;
-}
-
-/* A helper of compute_objsize_r() to determine the size from ARRAY_REF
- AREF. ADDR is true if PTR is the operand of ADDR_EXPR. Return true
- on success and false on failure. */
-
-static bool
-handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref,
- ssa_name_limit_t &snlim, pointer_query *qry)
-{
- gcc_assert (TREE_CODE (aref) == ARRAY_REF);
-
- ++pref->deref;
-
- tree arefop = TREE_OPERAND (aref, 0);
- tree reftype = TREE_TYPE (arefop);
- if (!addr && TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE)
- /* Avoid arrays of pointers. FIXME: Hande pointers to arrays
- of known bound. */
- return false;
-
- if (!compute_objsize_r (arefop, ostype, pref, snlim, qry))
- return false;
-
- offset_int orng[2];
- tree off = pref->eval (TREE_OPERAND (aref, 1));
- range_query *const rvals = qry ? qry->rvals : NULL;
- if (!get_offset_range (off, NULL, orng, rvals))
- {
- /* Set ORNG to the maximum offset representable in ptrdiff_t. */
- orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
- orng[0] = -orng[1] - 1;
- }
-
- /* Convert the array index range determined above to a byte
- offset. */
- tree lowbnd = array_ref_low_bound (aref);
- if (!integer_zerop (lowbnd) && tree_fits_uhwi_p (lowbnd))
- {
- /* Adjust the index by the low bound of the array domain
- (normally zero but 1 in Fortran). */
- unsigned HOST_WIDE_INT lb = tree_to_uhwi (lowbnd);
- orng[0] -= lb;
- orng[1] -= lb;
- }
-
- tree eltype = TREE_TYPE (aref);
- tree tpsize = TYPE_SIZE_UNIT (eltype);
- if (!tpsize || TREE_CODE (tpsize) != INTEGER_CST)
- {
- pref->add_max_offset ();
- return true;
- }
-
- offset_int sz = wi::to_offset (tpsize);
- orng[0] *= sz;
- orng[1] *= sz;
-
- if (ostype && TREE_CODE (eltype) == ARRAY_TYPE)
- {
- /* Except for the permissive raw memory functions which use
- the size of the whole object determined above, use the size
- of the referenced array. Because the overall offset is from
- the beginning of the complete array object add this overall
- offset to the size of array. */
- offset_int sizrng[2] =
- {
- pref->offrng[0] + orng[0] + sz,
- pref->offrng[1] + orng[1] + sz
- };
- if (sizrng[1] < sizrng[0])
- std::swap (sizrng[0], sizrng[1]);
- if (sizrng[0] >= 0 && sizrng[0] <= pref->sizrng[0])
- pref->sizrng[0] = sizrng[0];
- if (sizrng[1] >= 0 && sizrng[1] <= pref->sizrng[1])
- pref->sizrng[1] = sizrng[1];
- }
-
- pref->add_offset (orng[0], orng[1]);
- return true;
-}
-
-/* A helper of compute_objsize_r() to determine the size from MEM_REF
- MREF. Return true on success and false on failure. */
-
-static bool
-handle_mem_ref (tree mref, int ostype, access_ref *pref,
- ssa_name_limit_t &snlim, pointer_query *qry)
-{
- gcc_assert (TREE_CODE (mref) == MEM_REF);
-
- ++pref->deref;
-
- if (VECTOR_TYPE_P (TREE_TYPE (mref)))
- {
- /* Hack: Handle MEM_REFs of vector types as those to complete
- objects; those may be synthesized from multiple assignments
- to consecutive data members (see PR 93200 and 96963).
- FIXME: Vectorized assignments should only be present after
- vectorization so this hack is only necessary after it has
- run and could be avoided in calls from prior passes (e.g.,
- tree-ssa-strlen.c).
- FIXME: Deal with this more generally, e.g., by marking up
- such MEM_REFs at the time they're created. */
- ostype = 0;
- }
-
- tree mrefop = TREE_OPERAND (mref, 0);
- if (!compute_objsize_r (mrefop, ostype, pref, snlim, qry))
- return false;
-
- offset_int orng[2];
- tree off = pref->eval (TREE_OPERAND (mref, 1));
- range_query *const rvals = qry ? qry->rvals : NULL;
- if (!get_offset_range (off, NULL, orng, rvals))
- {
- /* Set ORNG to the maximum offset representable in ptrdiff_t. */
- orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
- orng[0] = -orng[1] - 1;
- }
-
- pref->add_offset (orng[0], orng[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).
- On success, sets PREF->REF to the DECL of the referenced object
- if it's unique, otherwise to null, PREF->OFFRNG to the range of
- offsets into it, and PREF->SIZRNG to the range of sizes of
- the object(s).
- 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.
-
- The function is intended for diagnostics and should not be used
- to influence code generation or optimization. */
-
-static bool
-compute_objsize_r (tree ptr, int ostype, access_ref *pref,
- ssa_name_limit_t &snlim, pointer_query *qry)
-{
- STRIP_NOPS (ptr);
-
- const bool addr = TREE_CODE (ptr) == ADDR_EXPR;
- if (addr)
- {
- --pref->deref;
- ptr = TREE_OPERAND (ptr, 0);
- }
-
- if (DECL_P (ptr))
- {
- pref->ref = ptr;
-
- if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
- {
- /* Set the maximum size if the reference is to the pointer
- itself (as opposed to what it points to), and clear
- BASE0 since the offset isn't necessarily zero-based. */
- pref->set_max_size_range ();
- pref->base0 = false;
- return true;
- }
-
- if (tree size = decl_init_size (ptr, false))
- if (TREE_CODE (size) == INTEGER_CST)
- {
- pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
- return true;
- }
-
- pref->set_max_size_range ();
- return true;
- }
-
- const tree_code code = TREE_CODE (ptr);
- range_query *const rvals = qry ? qry->rvals : NULL;
-
- if (code == BIT_FIELD_REF)
- {
- tree ref = TREE_OPERAND (ptr, 0);
- if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
- return false;
-
- offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
- pref->add_offset (off / BITS_PER_UNIT);
- return true;
- }
-
- 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_r (ref, ostype, pref, snlim, qry))
- return false;
-
- /* Otherwise, use the size of the enclosing object and add
- the offset of the member to the offset computed so far. */
- tree offset = byte_position (field);
- if (TREE_CODE (offset) == INTEGER_CST)
- 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
- itself (as opposed to what it points to). */
- pref->set_max_size_range ();
- return true;
- }
-
- /* SAM is set for array members that might need special treatment. */
- special_array_member sam;
- tree size = component_ref_size (ptr, &sam);
- if (sam == special_array_member::int_0)
- pref->sizrng[0] = pref->sizrng[1] = 0;
- else if (!pref->trail1special && sam == special_array_member::trail_1)
- pref->sizrng[0] = pref->sizrng[1] = 1;
- else if (size && TREE_CODE (size) == INTEGER_CST)
- pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
- else
- {
- /* When the size of the member is unknown it's either a flexible
- array member or a trailing special array member (either zero
- length or one-element). Set the size to the maximum minus
- the constant size of the type. */
- pref->sizrng[0] = 0;
- pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
- if (tree recsize = TYPE_SIZE_UNIT (TREE_TYPE (ref)))
- if (TREE_CODE (recsize) == INTEGER_CST)
- pref->sizrng[1] -= wi::to_offset (recsize);
- }
- return true;
- }
-
- if (code == ARRAY_REF)
- return handle_array_ref (ptr, addr, ostype, pref, snlim, qry);
-
- if (code == MEM_REF)
- return handle_mem_ref (ptr, ostype, pref, snlim, qry);
-
- if (code == TARGET_MEM_REF)
- {
- tree ref = TREE_OPERAND (ptr, 0);
- if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
- return false;
-
- /* TODO: Handle remaining operands. Until then, add maximum offset. */
- pref->ref = ptr;
- pref->add_max_offset ();
- return true;
- }
-
- if (code == INTEGER_CST)
- {
- /* Pointer constants other than null are most likely the result
- of erroneous null pointer addition/subtraction. Set size to
- zero. For null pointers, set size to the maximum for now
- since those may be the result of jump threading. */
- if (integer_zerop (ptr))
- pref->set_max_size_range ();
- else
- pref->sizrng[0] = pref->sizrng[1] = 0;
- pref->ref = ptr;
-
- return true;
- }
-
- 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_r (ref, ostype, pref, snlim, qry))
- return false;
-
- /* Clear DEREF since the offset is being applied to the target
- of the dereference. */
- pref->deref = 0;
-
- offset_int orng[2];
- tree off = pref->eval (TREE_OPERAND (ptr, 1));
- if (get_offset_range (off, NULL, orng, rvals))
- pref->add_offset (orng[0], orng[1]);
- else
- pref->add_max_offset ();
- return true;
- }
-
- if (code == VIEW_CONVERT_EXPR)
- {
- ptr = TREE_OPERAND (ptr, 0);
- return compute_objsize_r (ptr, ostype, pref, snlim, qry);
- }
-
- if (code == SSA_NAME)
- {
- if (!snlim.next ())
- return false;
-
- /* Only process an SSA_NAME if the recursion limit has not yet
- been reached. */
- if (qry)
- {
- if (++qry->depth)
- qry->max_depth = qry->depth;
- if (const access_ref *cache_ref = qry->get_ref (ptr))
- {
- /* If the pointer is in the cache set *PREF to what it refers
- to and return success. */
- *pref = *cache_ref;
- return true;
- }
- }
-
- gimple *stmt = SSA_NAME_DEF_STMT (ptr);
- if (is_gimple_call (stmt))
- {
- /* If STMT is a call to an allocation function get the size
- from its argument(s). If successful, also set *PREF->REF
- to PTR for the caller to include in diagnostics. */
- wide_int wr[2];
- if (gimple_call_alloc_size (stmt, wr, rvals))
- {
- pref->ref = ptr;
- pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
- pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
- /* Constrain both bounds to a valid size. */
- offset_int maxsize = wi::to_offset (max_object_size ());
- if (pref->sizrng[0] > maxsize)
- pref->sizrng[0] = maxsize;
- if (pref->sizrng[1] > maxsize)
- pref->sizrng[1] = maxsize;
- }
- else
- {
- /* For functions known to return one of their pointer arguments
- try to determine what the returned pointer points to, and on
- success add OFFRNG which was set to the offset added by
- the function (e.g., memchr or stpcpy) to the overall offset.
- */
- bool past_end;
- offset_int offrng[2];
- if (tree ret = gimple_call_return_array (stmt, offrng,
- &past_end, rvals))
- {
- if (!compute_objsize_r (ret, ostype, pref, snlim, qry))
- return false;
-
- /* Cap OFFRNG[1] to at most the remaining size of
- the object. */
- offset_int remrng[2];
- remrng[1] = pref->size_remaining (remrng);
- if (remrng[1] != 0 && !past_end)
- /* Decrement the size for functions that never return
- a past-the-end pointer. */
- remrng[1] -= 1;
-
- if (remrng[1] < offrng[1])
- offrng[1] = remrng[1];
- pref->add_offset (offrng[0], offrng[1]);
- }
- else
- {
- /* For other calls that might return arbitrary pointers
- including into the middle of objects set the size
- range to maximum, clear PREF->BASE0, and also set
- PREF->REF to include in diagnostics. */
- pref->set_max_size_range ();
- pref->base0 = false;
- pref->ref = ptr;
- }
- }
- qry->put_ref (ptr, *pref);
- return true;
- }
-
- if (gimple_nop_p (stmt))
- {
- /* For a function argument try to determine the byte size
- of the array from the current function declaratation
- (e.g., attribute access or related). */
- wide_int wr[2];
- 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;
- qry->put_ref (ptr, *pref);
- return true;
- }
-
- pref->set_max_size_range ();
- pref->base0 = false;
- pref->ref = ptr;
- qry->put_ref (ptr, *pref);
- return true;
- }
-
- if (gimple_code (stmt) == GIMPLE_PHI)
- {
- pref->ref = ptr;
- access_ref phi_ref = *pref;
- if (!pref->get_ref (NULL, &phi_ref, ostype, &snlim, qry))
- return false;
- *pref = phi_ref;
- pref->ref = ptr;
- qry->put_ref (ptr, *pref);
- return true;
- }
-
- if (!is_gimple_assign (stmt))
- {
- /* Clear BASE0 since the assigned pointer might point into
- the middle of the object, set the maximum size range and,
- if the SSA_NAME refers to a function argumnent, set
- PREF->REF to it. */
- pref->base0 = false;
- pref->set_max_size_range ();
- pref->ref = ptr;
- return true;
- }
-
- tree_code code = gimple_assign_rhs_code (stmt);
-
- if (code == MAX_EXPR || code == MIN_EXPR)
- {
- if (!handle_min_max_size (stmt, ostype, pref, snlim, qry))
- return false;
- qry->put_ref (ptr, *pref);
- return true;
- }
-
- tree rhs = gimple_assign_rhs1 (stmt);
-
- if (code == ASSERT_EXPR)
- {
- rhs = TREE_OPERAND (rhs, 0);
- return compute_objsize_r (rhs, ostype, pref, snlim, qry);
- }
-
- if (code == POINTER_PLUS_EXPR
- && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE)
- {
- /* Compute the size of the object first. */
- if (!compute_objsize_r (rhs, ostype, pref, snlim, qry))
- return false;
-
- offset_int orng[2];
- tree off = gimple_assign_rhs2 (stmt);
- if (get_offset_range (off, stmt, orng, rvals))
- pref->add_offset (orng[0], orng[1]);
- else
- pref->add_max_offset ();
- qry->put_ref (ptr, *pref);
- return true;
- }
-
- if (code == ADDR_EXPR
- || code == SSA_NAME)
- return compute_objsize_r (rhs, ostype, pref, snlim, qry);
-
- /* (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 = 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 ();
- if (TREE_CODE (ptr) == SSA_NAME)
- qry->put_ref (ptr, *pref);
- return true;
-}
-
-/* A "public" wrapper around the above. Clients should use this overload
- instead. */
-
-tree
-compute_objsize (tree ptr, int ostype, access_ref *pref,
- range_query *rvals /* = NULL */)
-{
- pointer_query qry;
- qry.rvals = rvals;
- ssa_name_limit_t snlim;
- if (!compute_objsize_r (ptr, ostype, pref, snlim, &qry))
- return NULL_TREE;
-
- offset_int maxsize = pref->size_remaining ();
- if (pref->base0 && pref->offrng[0] < 0 && pref->offrng[1] >= 0)
- pref->offrng[0] = 0;
- return wide_int_to_tree (sizetype, maxsize);
-}
-
-/* Transitional wrapper. The function should be removed once callers
- transition to the pointer_query API. */
-
-tree
-compute_objsize (tree ptr, int ostype, access_ref *pref, pointer_query *ptr_qry)
-{
- pointer_query qry;
- if (ptr_qry)
- ptr_qry->depth = 0;
- else
- ptr_qry = &qry;
-
- ssa_name_limit_t snlim;
- if (!compute_objsize_r (ptr, ostype, pref, snlim, ptr_qry))
- return NULL_TREE;
-
- offset_int maxsize = pref->size_remaining ();
- if (pref->base0 && pref->offrng[0] < 0 && pref->offrng[1] >= 0)
- pref->offrng[0] = 0;
- return wide_int_to_tree (sizetype, maxsize);
-}
-
-/* Legacy wrapper around the above. The function should be removed
- once callers transition to one of the two above. */
-
-tree
-compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */,
- tree *poff /* = NULL */, range_query *rvals /* = NULL */)
-{
- /* Set the initial offsets to zero and size to negative to indicate
- none has been computed yet. */
- access_ref ref;
- tree size = compute_objsize (ptr, ostype, &ref, rvals);
- if (!size || !ref.base0)
- return NULL_TREE;
-
- if (pdecl)
- *pdecl = ref.ref;
-
- if (poff)
- *poff = wide_int_to_tree (ptrdiff_type_node, ref.offrng[ref.offrng[0] < 0]);
-
- return size;
-}
-
/* Helper to determine and check the sizes of the source and the destination
of calls to __builtin_{bzero,memcpy,mempcpy,memset} calls. EXP is the
call expression, DEST is the destination argument, SRC is the source
@@ -13352,716 +10547,6 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
access_write_only);
}
-/* Return true if STMT is a call to an allocation function. Unless
- ALL_ALLOC is set, consider only functions that return dynmamically
- allocated objects. Otherwise return true even for all forms of
- alloca (including VLA). */
-
-static bool
-fndecl_alloc_p (tree fndecl, bool all_alloc)
-{
- if (!fndecl)
- return false;
-
- /* A call to operator new isn't recognized as one to a built-in. */
- if (DECL_IS_OPERATOR_NEW_P (fndecl))
- return true;
-
- if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
- {
- switch (DECL_FUNCTION_CODE (fndecl))
- {
- case BUILT_IN_ALLOCA:
- case BUILT_IN_ALLOCA_WITH_ALIGN:
- return all_alloc;
- case BUILT_IN_ALIGNED_ALLOC:
- case BUILT_IN_CALLOC:
- case BUILT_IN_GOMP_ALLOC:
- case BUILT_IN_MALLOC:
- case BUILT_IN_REALLOC:
- case BUILT_IN_STRDUP:
- case BUILT_IN_STRNDUP:
- return true;
- default:
- break;
- }
- }
-
- /* A function is considered an allocation function if it's declared
- with attribute malloc with an argument naming its associated
- deallocation function. */
- tree attrs = DECL_ATTRIBUTES (fndecl);
- if (!attrs)
- return false;
-
- for (tree allocs = attrs;
- (allocs = lookup_attribute ("malloc", allocs));
- allocs = TREE_CHAIN (allocs))
- {
- tree args = TREE_VALUE (allocs);
- if (!args)
- continue;
-
- if (TREE_VALUE (args))
- return true;
- }
-
- return false;
-}
-
-/* Return true if STMT is a call to an allocation function. A wrapper
- around fndecl_alloc_p. */
-
-static bool
-gimple_call_alloc_p (gimple *stmt, bool all_alloc = false)
-{
- return fndecl_alloc_p (gimple_call_fndecl (stmt), all_alloc);
-}
-
-/* Return the zero-based number corresponding to the argument being
- deallocated if STMT is a call to a deallocation function or UINT_MAX
- if it isn't. */
-
-static unsigned
-call_dealloc_argno (tree exp)
-{
- tree fndecl = get_callee_fndecl (exp);
- if (!fndecl)
- return UINT_MAX;
-
- return fndecl_dealloc_argno (fndecl);
-}
-
-/* Return the zero-based number corresponding to the argument being
- deallocated if FNDECL is a deallocation function or UINT_MAX
- if it isn't. */
-
-unsigned
-fndecl_dealloc_argno (tree fndecl)
-{
- /* A call to operator delete isn't recognized as one to a built-in. */
- if (DECL_IS_OPERATOR_DELETE_P (fndecl))
- {
- if (DECL_IS_REPLACEABLE_OPERATOR (fndecl))
- return 0;
-
- /* Avoid placement delete that's not been inlined. */
- tree fname = DECL_ASSEMBLER_NAME (fndecl);
- if (id_equal (fname, "_ZdlPvS_") // ordinary form
- || id_equal (fname, "_ZdaPvS_")) // array form
- return UINT_MAX;
- return 0;
- }
-
- /* TODO: Handle user-defined functions with attribute malloc? Handle
- known non-built-ins like fopen? */
- if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
- {
- switch (DECL_FUNCTION_CODE (fndecl))
- {
- case BUILT_IN_FREE:
- case BUILT_IN_REALLOC:
- return 0;
- default:
- break;
- }
- return UINT_MAX;
- }
-
- tree attrs = DECL_ATTRIBUTES (fndecl);
- if (!attrs)
- return UINT_MAX;
-
- for (tree atfree = attrs;
- (atfree = lookup_attribute ("*dealloc", atfree));
- atfree = TREE_CHAIN (atfree))
- {
- tree alloc = TREE_VALUE (atfree);
- if (!alloc)
- continue;
-
- tree pos = TREE_CHAIN (alloc);
- if (!pos)
- return 0;
-
- pos = TREE_VALUE (pos);
- return TREE_INT_CST_LOW (pos) - 1;
- }
-
- return UINT_MAX;
-}
-
-/* Return true if DELC doesn't refer to an operator delete that's
- suitable to call with a pointer returned from the operator new
- described by NEWC. */
-
-static bool
-new_delete_mismatch_p (const demangle_component &newc,
- const demangle_component &delc)
-{
- if (newc.type != delc.type)
- return true;
-
- switch (newc.type)
- {
- case DEMANGLE_COMPONENT_NAME:
- {
- int len = newc.u.s_name.len;
- const char *news = newc.u.s_name.s;
- const char *dels = delc.u.s_name.s;
- if (len != delc.u.s_name.len || memcmp (news, dels, len))
- return true;
-
- if (news[len] == 'n')
- {
- if (news[len + 1] == 'a')
- return dels[len] != 'd' || dels[len + 1] != 'a';
- if (news[len + 1] == 'w')
- return dels[len] != 'd' || dels[len + 1] != 'l';
- }
- return false;
- }
-
- case DEMANGLE_COMPONENT_OPERATOR:
- /* Operator mismatches are handled above. */
- return false;
-
- case DEMANGLE_COMPONENT_EXTENDED_OPERATOR:
- if (newc.u.s_extended_operator.args != delc.u.s_extended_operator.args)
- return true;
- return new_delete_mismatch_p (*newc.u.s_extended_operator.name,
- *delc.u.s_extended_operator.name);
-
- case DEMANGLE_COMPONENT_FIXED_TYPE:
- if (newc.u.s_fixed.accum != delc.u.s_fixed.accum
- || newc.u.s_fixed.sat != delc.u.s_fixed.sat)
- return true;
- return new_delete_mismatch_p (*newc.u.s_fixed.length,
- *delc.u.s_fixed.length);
-
- case DEMANGLE_COMPONENT_CTOR:
- if (newc.u.s_ctor.kind != delc.u.s_ctor.kind)
- return true;
- return new_delete_mismatch_p (*newc.u.s_ctor.name,
- *delc.u.s_ctor.name);
-
- case DEMANGLE_COMPONENT_DTOR:
- if (newc.u.s_dtor.kind != delc.u.s_dtor.kind)
- return true;
- return new_delete_mismatch_p (*newc.u.s_dtor.name,
- *delc.u.s_dtor.name);
-
- case DEMANGLE_COMPONENT_BUILTIN_TYPE:
- {
- /* The demangler API provides no better way to compare built-in
- types except to by comparing their demangled names. */
- size_t nsz, dsz;
- demangle_component *pnc = const_cast<demangle_component *>(&newc);
- demangle_component *pdc = const_cast<demangle_component *>(&delc);
- char *nts = cplus_demangle_print (0, pnc, 16, &nsz);
- char *dts = cplus_demangle_print (0, pdc, 16, &dsz);
- if (!nts != !dts)
- return true;
- bool mismatch = strcmp (nts, dts);
- free (nts);
- free (dts);
- return mismatch;
- }
-
- case DEMANGLE_COMPONENT_SUB_STD:
- if (newc.u.s_string.len != delc.u.s_string.len)
- return true;
- return memcmp (newc.u.s_string.string, delc.u.s_string.string,
- newc.u.s_string.len);
-
- case DEMANGLE_COMPONENT_FUNCTION_PARAM:
- case DEMANGLE_COMPONENT_TEMPLATE_PARAM:
- return newc.u.s_number.number != delc.u.s_number.number;
-
- case DEMANGLE_COMPONENT_CHARACTER:
- return newc.u.s_character.character != delc.u.s_character.character;
-
- case DEMANGLE_COMPONENT_DEFAULT_ARG:
- case DEMANGLE_COMPONENT_LAMBDA:
- if (newc.u.s_unary_num.num != delc.u.s_unary_num.num)
- return true;
- return new_delete_mismatch_p (*newc.u.s_unary_num.sub,
- *delc.u.s_unary_num.sub);
- default:
- break;
- }
-
- if (!newc.u.s_binary.left != !delc.u.s_binary.left)
- return true;
-
- if (!newc.u.s_binary.left)
- return false;
-
- if (new_delete_mismatch_p (*newc.u.s_binary.left, *delc.u.s_binary.left)
- || !newc.u.s_binary.right != !delc.u.s_binary.right)
- return true;
-
- if (newc.u.s_binary.right)
- return new_delete_mismatch_p (*newc.u.s_binary.right,
- *delc.u.s_binary.right);
- return false;
-}
-
-/* Return true if DELETE_DECL is an operator delete that's not suitable
- to call with a pointer returned fron NEW_DECL. */
-
-static bool
-new_delete_mismatch_p (tree new_decl, tree delete_decl)
-{
- tree new_name = DECL_ASSEMBLER_NAME (new_decl);
- tree delete_name = DECL_ASSEMBLER_NAME (delete_decl);
-
- /* valid_new_delete_pair_p() returns a conservative result (currently
- it only handles global operators). A true result is reliable but
- a false result doesn't necessarily mean the operators don't match. */
- if (valid_new_delete_pair_p (new_name, delete_name))
- return false;
-
- /* For anything not handled by valid_new_delete_pair_p() such as member
- operators compare the individual demangled components of the mangled
- name. */
- const char *new_str = IDENTIFIER_POINTER (new_name);
- const char *del_str = IDENTIFIER_POINTER (delete_name);
-
- void *np = NULL, *dp = NULL;
- demangle_component *ndc = cplus_demangle_v3_components (new_str, 0, &np);
- demangle_component *ddc = cplus_demangle_v3_components (del_str, 0, &dp);
- bool mismatch = new_delete_mismatch_p (*ndc, *ddc);
- free (np);
- free (dp);
- return mismatch;
-}
-
-/* ALLOC_DECL and DEALLOC_DECL are pair of allocation and deallocation
- functions. Return true if the latter is suitable to deallocate objects
- allocated by calls to the former. */
-
-static bool
-matching_alloc_calls_p (tree alloc_decl, tree dealloc_decl)
-{
- /* Set to alloc_kind_t::builtin if ALLOC_DECL is associated with
- a built-in deallocator. */
- enum class alloc_kind_t { none, builtin, user }
- alloc_dealloc_kind = alloc_kind_t::none;
-
- if (DECL_IS_OPERATOR_NEW_P (alloc_decl))
- {
- if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
- /* Return true iff both functions are of the same array or
- singleton form and false otherwise. */
- return !new_delete_mismatch_p (alloc_decl, dealloc_decl);
-
- /* Return false for deallocation functions that are known not
- to match. */
- if (fndecl_built_in_p (dealloc_decl, BUILT_IN_FREE)
- || fndecl_built_in_p (dealloc_decl, BUILT_IN_REALLOC))
- return false;
- /* Otherwise proceed below to check the deallocation function's
- "*dealloc" attributes to look for one that mentions this operator
- new. */
- }
- else if (fndecl_built_in_p (alloc_decl, BUILT_IN_NORMAL))
- {
- switch (DECL_FUNCTION_CODE (alloc_decl))
- {
- case BUILT_IN_ALLOCA:
- case BUILT_IN_ALLOCA_WITH_ALIGN:
- return false;
-
- case BUILT_IN_ALIGNED_ALLOC:
- case BUILT_IN_CALLOC:
- case BUILT_IN_GOMP_ALLOC:
- case BUILT_IN_MALLOC:
- case BUILT_IN_REALLOC:
- case BUILT_IN_STRDUP:
- case BUILT_IN_STRNDUP:
- if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
- return false;
-
- if (fndecl_built_in_p (dealloc_decl, BUILT_IN_FREE)
- || fndecl_built_in_p (dealloc_decl, BUILT_IN_REALLOC))
- return true;
-
- alloc_dealloc_kind = alloc_kind_t::builtin;
- break;
-
- default:
- break;
- }
- }
-
- /* Set if DEALLOC_DECL both allocates and deallocates. */
- alloc_kind_t realloc_kind = alloc_kind_t::none;
-
- if (fndecl_built_in_p (dealloc_decl, BUILT_IN_NORMAL))
- {
- built_in_function dealloc_code = DECL_FUNCTION_CODE (dealloc_decl);
- if (dealloc_code == BUILT_IN_REALLOC)
- realloc_kind = alloc_kind_t::builtin;
-
- for (tree amats = DECL_ATTRIBUTES (alloc_decl);
- (amats = lookup_attribute ("malloc", amats));
- amats = TREE_CHAIN (amats))
- {
- tree args = TREE_VALUE (amats);
- if (!args)
- continue;
-
- tree fndecl = TREE_VALUE (args);
- if (!fndecl || !DECL_P (fndecl))
- continue;
-
- if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)
- && dealloc_code == DECL_FUNCTION_CODE (fndecl))
- return true;
- }
- }
-
- const bool alloc_builtin = fndecl_built_in_p (alloc_decl, BUILT_IN_NORMAL);
- alloc_kind_t realloc_dealloc_kind = alloc_kind_t::none;
-
- /* If DEALLOC_DECL has an internal "*dealloc" attribute scan the list
- of its associated allocation functions for ALLOC_DECL.
- If the corresponding ALLOC_DECL is found they're a matching pair,
- otherwise they're not.
- With DDATS set to the Deallocator's *Dealloc ATtributes... */
- for (tree ddats = DECL_ATTRIBUTES (dealloc_decl);
- (ddats = lookup_attribute ("*dealloc", ddats));
- ddats = TREE_CHAIN (ddats))
- {
- tree args = TREE_VALUE (ddats);
- if (!args)
- continue;
-
- tree alloc = TREE_VALUE (args);
- if (!alloc)
- continue;
-
- if (alloc == DECL_NAME (dealloc_decl))
- realloc_kind = alloc_kind_t::user;
-
- if (DECL_P (alloc))
- {
- gcc_checking_assert (fndecl_built_in_p (alloc, BUILT_IN_NORMAL));
-
- switch (DECL_FUNCTION_CODE (alloc))
- {
- case BUILT_IN_ALIGNED_ALLOC:
- case BUILT_IN_CALLOC:
- case BUILT_IN_GOMP_ALLOC:
- case BUILT_IN_MALLOC:
- case BUILT_IN_REALLOC:
- case BUILT_IN_STRDUP:
- case BUILT_IN_STRNDUP:
- realloc_dealloc_kind = alloc_kind_t::builtin;
- break;
- default:
- break;
- }
-
- if (!alloc_builtin)
- continue;
-
- if (DECL_FUNCTION_CODE (alloc) != DECL_FUNCTION_CODE (alloc_decl))
- continue;
-
- return true;
- }
-
- if (alloc == DECL_NAME (alloc_decl))
- return true;
- }
-
- if (realloc_kind == alloc_kind_t::none)
- return false;
-
- hash_set<tree> common_deallocs;
- /* Special handling for deallocators. Iterate over both the allocator's
- and the reallocator's associated deallocator functions looking for
- the first one in common. If one is found, the de/reallocator is
- a match for the allocator even though the latter isn't directly
- associated with the former. This simplifies declarations in system
- headers.
- With AMATS set to the Allocator's Malloc ATtributes,
- and RMATS set to Reallocator's Malloc ATtributes... */
- for (tree amats = DECL_ATTRIBUTES (alloc_decl),
- rmats = DECL_ATTRIBUTES (dealloc_decl);
- (amats = lookup_attribute ("malloc", amats))
- || (rmats = lookup_attribute ("malloc", rmats));
- amats = amats ? TREE_CHAIN (amats) : NULL_TREE,
- rmats = rmats ? TREE_CHAIN (rmats) : NULL_TREE)
- {
- if (tree args = amats ? TREE_VALUE (amats) : NULL_TREE)
- if (tree adealloc = TREE_VALUE (args))
- {
- if (DECL_P (adealloc)
- && fndecl_built_in_p (adealloc, BUILT_IN_NORMAL))
- {
- built_in_function fncode = DECL_FUNCTION_CODE (adealloc);
- if (fncode == BUILT_IN_FREE || fncode == BUILT_IN_REALLOC)
- {
- if (realloc_kind == alloc_kind_t::builtin)
- return true;
- alloc_dealloc_kind = alloc_kind_t::builtin;
- }
- continue;
- }
-
- common_deallocs.add (adealloc);
- }
-
- if (tree args = rmats ? TREE_VALUE (rmats) : NULL_TREE)
- if (tree ddealloc = TREE_VALUE (args))
- {
- if (DECL_P (ddealloc)
- && fndecl_built_in_p (ddealloc, BUILT_IN_NORMAL))
- {
- built_in_function fncode = DECL_FUNCTION_CODE (ddealloc);
- if (fncode == BUILT_IN_FREE || fncode == BUILT_IN_REALLOC)
- {
- if (alloc_dealloc_kind == alloc_kind_t::builtin)
- return true;
- realloc_dealloc_kind = alloc_kind_t::builtin;
- }
- continue;
- }
-
- if (common_deallocs.add (ddealloc))
- return true;
- }
- }
-
- /* Succeed only if ALLOC_DECL and the reallocator DEALLOC_DECL share
- a built-in deallocator. */
- return (alloc_dealloc_kind == alloc_kind_t::builtin
- && realloc_dealloc_kind == alloc_kind_t::builtin);
-}
-
-/* Return true if DEALLOC_DECL is a function suitable to deallocate
- objectes allocated by the ALLOC call. */
-
-static bool
-matching_alloc_calls_p (gimple *alloc, tree dealloc_decl)
-{
- tree alloc_decl = gimple_call_fndecl (alloc);
- if (!alloc_decl)
- return true;
-
- return matching_alloc_calls_p (alloc_decl, dealloc_decl);
-}
-
-/* Diagnose a call EXP to deallocate a pointer referenced by AREF if it
- includes a nonzero offset. Such a pointer cannot refer to the beginning
- of an allocated object. A negative offset may refer to it only if
- the target pointer is unknown. */
-
-static bool
-warn_dealloc_offset (location_t loc, tree exp, const access_ref &aref)
-{
- if (aref.deref || aref.offrng[0] <= 0 || aref.offrng[1] <= 0)
- return false;
-
- tree dealloc_decl = get_callee_fndecl (exp);
- if (!dealloc_decl)
- return false;
-
- if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
- && !DECL_IS_REPLACEABLE_OPERATOR (dealloc_decl))
- {
- /* A call to a user-defined operator delete with a pointer plus offset
- may be valid if it's returned from an unknown function (i.e., one
- that's not operator new). */
- if (TREE_CODE (aref.ref) == SSA_NAME)
- {
- gimple *def_stmt = SSA_NAME_DEF_STMT (aref.ref);
- if (is_gimple_call (def_stmt))
- {
- tree alloc_decl = gimple_call_fndecl (def_stmt);
- if (!alloc_decl || !DECL_IS_OPERATOR_NEW_P (alloc_decl))
- return false;
- }
- }
- }
-
- char offstr[80];
- offstr[0] = '\0';
- if (wi::fits_shwi_p (aref.offrng[0]))
- {
- if (aref.offrng[0] == aref.offrng[1]
- || !wi::fits_shwi_p (aref.offrng[1]))
- sprintf (offstr, " %lli",
- (long long)aref.offrng[0].to_shwi ());
- else
- sprintf (offstr, " [%lli, %lli]",
- (long long)aref.offrng[0].to_shwi (),
- (long long)aref.offrng[1].to_shwi ());
- }
-
- if (!warning_at (loc, OPT_Wfree_nonheap_object,
- "%qD called on pointer %qE with nonzero offset%s",
- dealloc_decl, aref.ref, offstr))
- return false;
-
- if (DECL_P (aref.ref))
- inform (DECL_SOURCE_LOCATION (aref.ref), "declared here");
- else if (TREE_CODE (aref.ref) == SSA_NAME)
- {
- gimple *def_stmt = SSA_NAME_DEF_STMT (aref.ref);
- if (is_gimple_call (def_stmt))
- {
- location_t def_loc = gimple_location (def_stmt);
- tree alloc_decl = gimple_call_fndecl (def_stmt);
- if (alloc_decl)
- inform (def_loc,
- "returned from %qD", alloc_decl);
- else if (tree alloc_fntype = gimple_call_fntype (def_stmt))
- inform (def_loc,
- "returned from %qT", alloc_fntype);
- else
- inform (def_loc, "obtained here");
- }
- }
-
- return true;
-}
-
-/* Issue a warning if a deallocation function such as free, realloc,
- or C++ operator delete is called with an argument not returned by
- a matching allocation function such as malloc or the corresponding
- form of C++ operatorn new. */
-
-void
-maybe_emit_free_warning (tree exp)
-{
- tree fndecl = get_callee_fndecl (exp);
- if (!fndecl)
- return;
-
- unsigned argno = call_dealloc_argno (exp);
- if ((unsigned) call_expr_nargs (exp) <= argno)
- return;
-
- tree ptr = CALL_EXPR_ARG (exp, argno);
- if (integer_zerop (ptr))
- return;
-
- access_ref aref;
- if (!compute_objsize (ptr, 0, &aref))
- return;
-
- tree ref = aref.ref;
- if (integer_zerop (ref))
- return;
-
- tree dealloc_decl = get_callee_fndecl (exp);
- location_t loc = EXPR_LOCATION (exp);
-
- if (DECL_P (ref) || EXPR_P (ref))
- {
- /* Diagnose freeing a declared object. */
- if (aref.ref_declared ()
- && warning_at (loc, OPT_Wfree_nonheap_object,
- "%qD called on unallocated object %qD",
- dealloc_decl, ref))
- {
- loc = (DECL_P (ref)
- ? DECL_SOURCE_LOCATION (ref)
- : EXPR_LOCATION (ref));
- inform (loc, "declared here");
- return;
- }
-
- /* Diagnose freeing a pointer that includes a positive offset.
- Such a pointer cannot refer to the beginning of an allocated
- object. A negative offset may refer to it. */
- if (aref.sizrng[0] != aref.sizrng[1]
- && warn_dealloc_offset (loc, exp, aref))
- return;
- }
- else if (CONSTANT_CLASS_P (ref))
- {
- if (warning_at (loc, OPT_Wfree_nonheap_object,
- "%qD called on a pointer to an unallocated "
- "object %qE", dealloc_decl, ref))
- {
- if (TREE_CODE (ptr) == SSA_NAME)
- {
- gimple *def_stmt = SSA_NAME_DEF_STMT (ptr);
- if (is_gimple_assign (def_stmt))
- {
- location_t loc = gimple_location (def_stmt);
- inform (loc, "assigned here");
- }
- }
- return;
- }
- }
- else if (TREE_CODE (ref) == SSA_NAME)
- {
- /* Also warn if the pointer argument refers to the result
- of an allocation call like alloca or VLA. */
- gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
- if (is_gimple_call (def_stmt))
- {
- bool warned = false;
- if (gimple_call_alloc_p (def_stmt))
- {
- if (matching_alloc_calls_p (def_stmt, dealloc_decl))
- {
- if (warn_dealloc_offset (loc, exp, aref))
- return;
- }
- else
- {
- tree alloc_decl = gimple_call_fndecl (def_stmt);
- const opt_code opt =
- (DECL_IS_OPERATOR_NEW_P (alloc_decl)
- || DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
- ? OPT_Wmismatched_new_delete
- : OPT_Wmismatched_dealloc);
- warned = warning_at (loc, opt,
- "%qD called on pointer returned "
- "from a mismatched allocation "
- "function", dealloc_decl);
- }
- }
- else if (gimple_call_builtin_p (def_stmt, BUILT_IN_ALLOCA)
- || gimple_call_builtin_p (def_stmt,
- BUILT_IN_ALLOCA_WITH_ALIGN))
- warned = warning_at (loc, OPT_Wfree_nonheap_object,
- "%qD called on pointer to "
- "an unallocated object",
- dealloc_decl);
- else if (warn_dealloc_offset (loc, exp, aref))
- return;
-
- if (warned)
- {
- tree fndecl = gimple_call_fndecl (def_stmt);
- inform (gimple_location (def_stmt),
- "returned from %qD", fndecl);
- return;
- }
- }
- else if (gimple_nop_p (def_stmt))
- {
- ref = SSA_NAME_VAR (ref);
- /* Diagnose freeing a pointer that includes a positive offset. */
- if (TREE_CODE (ref) == PARM_DECL
- && !aref.deref
- && aref.sizrng[0] != aref.sizrng[1]
- && aref.offrng[0] > 0 && aref.offrng[1] > 0
- && warn_dealloc_offset (loc, exp, aref))
- return;
- }
- }
-}
-
/* Fold a call to __builtin_object_size with arguments PTR and OST,
if possible. */