PR middle-end/97373 - missing warning on sprintf into allocated destination
gcc/ChangeLog:
PR middle-end/97373
* builtins.c (compute_objsize): Rename...
(compute_objsize_r): to this. Change order and types of arguments.
Use new argument. Adjust calls to self.
(access_ref::get_ref): New member function.
(pointer_query::pointer_query): New member function.
(pointer_query::get_ref): Same.
(pointer_query::put_ref): Same.
(handle_min_max_size): Change order and types of arguments.
(maybe_emit_free_warning): Add a test.
* builtins.h (class pointer_query): New class.
(compute_objsize): Declare an overload.
* gimple-ssa-sprintf.c (get_destination_size):
(handle_printf_call):
* tree-ssa-strlen.c (adjust_last_stmt): Add an argument and use it.
(maybe_warn_overflow): Same.
(handle_builtin_strcpy): Same.
(maybe_diag_stxncpy_trunc): Same.
(handle_builtin_memcpy): Change argument type. Adjust calls.
(handle_builtin_strcat): Same.
(handle_builtin_memset): Same.
(handle_store): Same.
(strlen_check_and_optimize_call): Same.
(check_and_optimize_stmt): Same.
(strlen_dom_walker): Add new data members.
(strlen_dom_walker::before_dom_children): Use new member.
(printf_strlen_execute): Dump cache performance counters.
* tree-ssa-strlen.h (maybe_diag_stxncpy_trunc): Add argument.
(handle_printf_call): Change argument type.
gcc/testsuite/ChangeLog:
PR middle-end/97373
* gcc.dg/warn-strnlen-no-nul.c:
* g++.dg/warn/Wmismatched-new-delete-2.C: New test.
* gcc.dg/tree-ssa/builtin-sprintf-warn-25.c: New test.
@@ -185,7 +185,7 @@ 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 (tree, int, access_ref *, bitmap *, range_query *);
+static bool compute_objsize_r (tree, access_ref *, pointer_query *, bitmap *);
unsigned HOST_WIDE_INT target_newline;
unsigned HOST_WIDE_INT target_percent;
@@ -241,15 +241,14 @@ access_ref::phi () const
/* Determine and return the largest object to which REF refers. If REF
refers to a PHI and PREF is nonnull, fill *PREF with the details of
- the object determined by compute_objsize(ARG, OSTYPE) for each PHI
- argument ARG. */
+ the object determined by compute_objsize(ARG, ...) for each PHI argument
+ ARG. */
tree
access_ref::get_ref (vec<access_ref> *all_refs,
access_ref *pref /* = NULL */,
- int ostype /* = 1 */,
- bitmap *visited /* = NULL */,
- range_query *rvals /* = NULL */) const
+ pointer_query *qry /* = NULL */,
+ bitmap *visited /* = NULL */) const
{
gphi *phi_stmt = this->phi ();
if (!phi_stmt)
@@ -266,6 +265,10 @@ access_ref::get_ref (vec<access_ref> *all_refs,
/* Have compute_objsize() fail and propagate the failure below. */
return NULL_TREE;
+ pointer_query empty_qry;
+ if (!qry)
+ qry = &empty_qry;
+
access_ref phi_ref;
if (pref)
phi_ref = *pref;
@@ -283,15 +286,17 @@ access_ref::get_ref (vec<access_ref> *all_refs,
{
access_ref phi_arg_ref;
tree arg = gimple_phi_arg_def (phi_stmt, i);
- if (!compute_objsize (arg, ostype, &phi_arg_ref, visited, rvals))
+ if (!compute_objsize_r (arg, &phi_arg_ref, qry, visited))
return NULL_TREE;
- /* The call to compute_objsize() above will fail when the ARG has
- already been VISITED. */
+ /* The call to compute_objsize_r() above will fail when the ARG
+ has already been VISITED. */
gcc_checking_assert (phi_arg_ref.sizrng[0] >= 0);
/* Add PREF's offset to that of the argument. */
phi_arg_ref.add_offset (orng[0], orng[1]);
+ if (TREE_CODE (arg) == SSA_NAME)
+ qry->put_ref (arg, phi_arg_ref);
if (all_refs)
all_refs->safe_push (phi_arg_ref);
@@ -479,6 +484,103 @@ void access_ref::add_offset (const offset_int &min, const offset_int &max)
}
}
+/* 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), ostype (1), 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) const
+{
+ if (!var_cache)
+ return NULL;
+
+ unsigned version = SSA_NAME_VERSION (ptr);
+ if (var_cache->length () <= version)
+ return NULL;
+
+ access_ref &cache_ref = (*var_cache)[version];
+ 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)
+{
+ const unsigned version
+ = TREE_CODE (ptr) == SSA_NAME ? SSA_NAME_VERSION (ptr) : 0;
+
+ access_ref *cache_ref = NULL;
+ if (var_cache && version)
+ {
+ if (version < var_cache->length ())
+ {
+ if ((*var_cache)[version].ref)
+ {
+ ++hits;
+ *pref = (*var_cache)[version];
+ return true;
+ }
+ }
+ else
+ var_cache->safe_grow_cleared (version + 1);
+
+ ++misses;
+ cache_ref = &(*var_cache)[version];
+ }
+
+ if (!compute_objsize (ptr, pref, this))
+ {
+ ++failures;
+ return false;
+ }
+
+ if (cache_ref)
+ *cache_ref = *pref;
+
+ 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)
+{
+ /* Only add populated/valid entries. */
+ if (!var_cache || !ref.ref || ref.sizrng[0] < 0)
+ return;
+
+ unsigned version = SSA_NAME_VERSION (ptr);
+ if (var_cache->length () <= version)
+ var_cache->safe_grow_cleared (version + 1);
+ else if ((*var_cache)[version].ref)
+ {
+ gcc_checking_assert ((*var_cache)[version].ref == ref.ref);
+ return;
+ }
+
+ (*var_cache)[version] = ref;
+}
+
/* Return true if NAME starts with __builtin_ or __sync_. */
static bool
@@ -4956,8 +5058,8 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2],
statement STMT with the RHS of either MIN_EXPR or MAX_EXPR. */
static bool
-handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
- bitmap *visited, range_query *rvals)
+handle_min_max_size (gimple *stmt, access_ref *pref, pointer_query *qry,
+ bitmap *visited)
{
tree_code code = gimple_assign_rhs_code (stmt);
@@ -4969,7 +5071,7 @@ handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
determined from the other instead, adjusted up or down as appropriate
for the expression. */
access_ref aref[2] = { *pref, *pref };
- if (!compute_objsize (ptr, ostype, &aref[0], visited, rvals))
+ if (!compute_objsize_r (ptr, &aref[0], qry, visited))
{
aref[0].base0 = false;
aref[0].offrng[0] = aref[0].offrng[1] = 0;
@@ -4978,7 +5080,7 @@ handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
}
ptr = gimple_assign_rhs2 (stmt);
- if (!compute_objsize (ptr, ostype, &aref[1], visited, rvals))
+ if (!compute_objsize_r (ptr, &aref[1], qry, visited))
{
aref[1].base0 = false;
aref[1].offrng[0] = aref[1].offrng[1] = 0;
@@ -5038,7 +5140,8 @@ handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
offsets into it, and PREF->SIZRNG to the range of sizes of
the object(s).
VISITED is used to avoid visiting the same PHI operand multiple
- times, and, when nonnull, RVALS to determine range information.
+ times, and, when nonnull, QRY to use to query and/or update
+ the pointer cache and to determine range information.
Returns true on success, false when a meaningful size (or range)
cannot be determined.
@@ -5046,8 +5149,8 @@ handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
to influence code generation or optimization. */
static bool
-compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
- range_query *rvals)
+compute_objsize_r (tree ptr, access_ref *pref, pointer_query *qry,
+ bitmap *visited)
{
STRIP_NOPS (ptr);
@@ -5082,11 +5185,12 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
}
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 (ref, ostype, pref, visited, rvals))
+ if (!compute_objsize_r (ref, pref, qry, visited))
return false;
offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
@@ -5097,18 +5201,21 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
if (code == COMPONENT_REF)
{
tree ref = TREE_OPERAND (ptr, 0);
+ const int save_ostype = qry->ostype;
if (TREE_CODE (TREE_TYPE (ref)) == UNION_TYPE)
/* In accesses through union types consider the entire unions
rather than just their members. */
- ostype = 0;
+ qry->ostype = 0;
tree field = TREE_OPERAND (ptr, 1);
- if (ostype == 0)
+ if (qry->ostype == 0)
{
/* In OSTYPE zero (for raw memory functions like memcpy), use
the maximum size instead if the identity of the enclosing
object cannot be determined. */
- if (!compute_objsize (ref, ostype, pref, visited, rvals))
+ bool success = compute_objsize_r (ref, pref, qry, visited);
+ qry->ostype = save_ostype;
+ if (!success)
return false;
/* Otherwise, use the size of the enclosing object and add
@@ -5179,7 +5286,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
return false;
}
- if (!compute_objsize (ref, ostype, pref, visited, rvals))
+ if (!compute_objsize_r (ref, pref, qry, visited))
return false;
offset_int orng[2];
@@ -5217,7 +5324,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
orng[0] *= sz;
orng[1] *= sz;
- if (ostype && TREE_CODE (eltype) == ARRAY_TYPE)
+ if (qry->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
@@ -5245,7 +5352,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
if (code == TARGET_MEM_REF)
{
tree ref = TREE_OPERAND (ptr, 0);
- if (!compute_objsize (ref, ostype, pref, visited, rvals))
+ if (!compute_objsize_r (ref, pref, qry, visited))
return false;
/* TODO: Handle remaining operands. Until then, add maximum offset. */
@@ -5279,7 +5386,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
if (code == POINTER_PLUS_EXPR)
{
tree ref = TREE_OPERAND (ptr, 0);
- if (!compute_objsize (ref, ostype, pref, visited, rvals))
+ if (!compute_objsize_r (ref, pref, qry, visited))
return false;
/* Clear DEREF since the offset is being applied to the target
@@ -5298,11 +5405,24 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
if (code == VIEW_CONVERT_EXPR)
{
ptr = TREE_OPERAND (ptr, 0);
- return compute_objsize (ptr, ostype, pref, visited, rvals);
+ return compute_objsize_r (ptr, pref, qry, visited);
}
if (TREE_CODE (ptr) == SSA_NAME)
{
+ 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))
{
@@ -5331,7 +5451,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
offset_int offrng[2];
if (tree ret = gimple_call_return_array (stmt, offrng, rvals))
{
- if (!compute_objsize (ret, ostype, pref, visited, rvals))
+ if (!compute_objsize_r (ret, pref, qry, visited))
return false;
/* Cap OFFRNG[1] to at most the remaining size of
@@ -5353,6 +5473,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
pref->ref = ptr;
}
}
+ qry->put_ref (ptr, *pref);
return true;
}
@@ -5369,12 +5490,14 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
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;
}
@@ -5382,10 +5505,11 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
{
pref->ref = ptr;
access_ref phi_ref = *pref;
- if (!pref->get_ref (NULL, &phi_ref, ostype, visited, rvals))
+ if (!pref->get_ref (NULL, &phi_ref, qry, visited))
return false;
*pref = phi_ref;
pref->ref = ptr;
+ qry->put_ref (ptr, *pref);
return true;
}
@@ -5397,21 +5521,27 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
PREF->REF to it. */
pref->base0 = false;
pref->set_max_size_range ();
+ qry->put_ref (ptr, *pref);
return true;
}
tree_code code = gimple_assign_rhs_code (stmt);
if (code == MAX_EXPR || code == MIN_EXPR)
- return handle_min_max_size (stmt, ostype, pref, visited, rvals);
+ {
+ if (!handle_min_max_size (stmt, pref, qry, visited))
+ return false;
+ qry->put_ref (ptr, *pref);
+ return true;
+ }
- ptr = gimple_assign_rhs1 (stmt);
+ tree rhs = gimple_assign_rhs1 (stmt);
if (code == POINTER_PLUS_EXPR
- && TREE_CODE (TREE_TYPE (ptr)) == POINTER_TYPE)
+ && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE)
{
/* Compute the size of the object first. */
- if (!compute_objsize (ptr, ostype, pref, visited, rvals))
+ if (!compute_objsize_r (rhs, pref, qry, visited))
return false;
offset_int orng[2];
@@ -5420,22 +5550,25 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
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 (ptr, ostype, pref, visited, rvals);
+ return compute_objsize_r (rhs, pref, qry, visited);
/* This could be an assignment from a nonlocal pointer. Save PTR
to mention in diagnostics but otherwise treat it as a pointer
to an unknown object. */
- pref->ref = ptr;
+ pref->ref = rhs;
}
/* Assume all other expressions point into an unknown object
of the maximum valid size. */
pref->base0 = false;
pref->set_max_size_range ();
+ if (TREE_CODE (ptr) == SSA_NAME)
+ qry->put_ref (ptr, *pref);
return true;
}
@@ -5446,10 +5579,44 @@ tree
compute_objsize (tree ptr, int ostype, access_ref *pref,
range_query *rvals /* = NULL */)
{
+ pointer_query qry;
+ qry.rvals = rvals;
+ qry.ostype = ostype;
+
+ bitmap visited = NULL;
+
+ bool success = compute_objsize_r (ptr, pref, &qry, &visited);
+
+ if (visited)
+ BITMAP_FREE (visited);
+
+ if (!success)
+ 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, access_ref *pref, pointer_query *ptr_qry)
+{
+ pointer_query qry;
+ if (ptr_qry)
+ ptr_qry->depth = 0;
+ else
+ {
+ qry.ostype = 1;
+ ptr_qry = &qry;
+ }
+
bitmap visited = NULL;
- bool success
- = compute_objsize (ptr, ostype, pref, &visited, rvals);
+ bool success = compute_objsize_r (ptr, pref, ptr_qry, &visited);
if (visited)
BITMAP_FREE (visited);
@@ -5463,7 +5630,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
return wide_int_to_tree (sizetype, maxsize);
}
-/* Transitional wrapper around the above. The function should be removed
+/* Legacy wrapper around the above. The function should be removed
once callers transition to one of the two above. */
tree
@@ -12822,7 +12989,7 @@ maybe_emit_free_warning (tree exp)
return;
access_ref aref;
- if (!compute_objsize (ptr, 0, &aref))
+ if (!compute_objsize (ptr, 0, &aref) || !aref.ref)
return;
tree ref = aref.ref;
@@ -153,6 +153,7 @@ extern void warn_string_no_nul (location_t, tree, const char *, tree,
extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
extern bool builtin_with_linkage_p (tree);
+class pointer_query;
class range_query;
/* Describes a reference to an object used in an access. */
@@ -168,8 +169,8 @@ struct access_ref
gphi *phi () const;
/* Return the object to which REF refers. */
- tree get_ref (vec<access_ref> *, access_ref * = NULL, int = 1,
- bitmap * = NULL, range_query * = NULL) const;
+ tree get_ref (vec<access_ref> *, access_ref * = NULL,
+ pointer_query * = NULL, bitmap * = NULL) const;
/* Return true if OFFRNG is the constant zero. */
bool offset_zero () const
@@ -246,6 +247,41 @@ struct access_ref
bool parmarray;
};
+/* Queries and caches compute_objsize results. */
+class pointer_query
+{
+ DISABLE_COPY_AND_ASSIGN (pointer_query);
+
+public:
+ typedef vec<access_ref> cache_type;
+
+ /* Construct an object with the given Ranger instance and cache. */
+ explicit pointer_query (range_query * = NULL, cache_type * = NULL);
+
+ /* Retrieve the access_ref for a variable from cache if it's there. */
+ const access_ref* get_ref (tree) const;
+
+ /* Retrieve the access_ref for a variable from cache or compute it. */
+ bool get_ref (tree, access_ref*);
+
+ /* Add an access_ref for the SSA_NAME to the cache. */
+ void put_ref (tree, const access_ref&);
+
+ /* A Ranger instance. May be null to use global ranges. */
+ range_query *rvals;
+ /* Cache of SSA_NAMEs. May be null to disable caching. */
+ cache_type *var_cache;
+ /* The Object Size Type used for size computation. */
+ int ostype;
+
+ /* Performance counters. */
+ mutable unsigned hits;
+ mutable unsigned misses;
+ mutable unsigned failures;
+ mutable unsigned depth;
+ mutable unsigned max_depth;
+};
+
/* Describes a pair of references used in an access by built-in
functions like memcpy. */
struct access_data
@@ -270,7 +306,11 @@ struct access_data
extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
range_query * = NULL);
extern tree gimple_parm_array_size (tree, wide_int[2], bool * = NULL);
-extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
+
+extern tree compute_objsize (tree, int, access_ref *,
+ range_query *rvals = NULL);
+/* Legacy/transitional API. Should not be used in new code. */
+extern tree compute_objsize (tree, access_ref *, pointer_query *);
extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
range_query * = NULL);
extern bool check_access (tree, tree, tree, tree, tree,
@@ -4032,26 +4032,22 @@ compute_format_length (call_info &info, format_result *res, range_query *query)
available, or the maximum possible size otherwise. */
static unsigned HOST_WIDE_INT
-get_destination_size (tree dest)
+get_destination_size (tree dest, pointer_query &ptr_qry)
{
/* When there is no destination return the maximum. */
if (!dest)
return HOST_WIDE_INT_MAX;
- /* Initialize object size info before trying to compute it. */
- init_object_sizes ();
+ /* Use compute_objsize to determine the size of the destination object. */
+ access_ref aref;
+ if (!ptr_qry.get_ref (dest, &aref))
+ return HOST_WIDE_INT_MAX;
- /* Use __builtin_object_size to determine the size of the destination
- object. When optimizing, determine the smallest object (such as
- a member array as opposed to the whole enclosing object), otherwise
- use type-zero object size to determine the size of the enclosing
- object (the function fails without optimization in this type). */
- int ost = optimize > 0;
- unsigned HOST_WIDE_INT size;
- if (compute_builtin_object_size (dest, ost, &size))
- return size;
+ offset_int remsize = aref.size_remaining ();
+ if (!wi::fits_uhwi_p (remsize))
+ return HOST_WIDE_INT_MAX;
- return HOST_WIDE_INT_MAX;
+ return remsize.to_uhwi ();
}
/* Return true if the call described by INFO with result RES safe to
@@ -4296,7 +4292,7 @@ get_user_idx_format (tree fndecl, unsigned *idx_args)
gsi_next should not be performed in the caller. */
bool
-handle_printf_call (gimple_stmt_iterator *gsi, range_query *query)
+handle_printf_call (gimple_stmt_iterator *gsi, pointer_query &ptr_qry)
{
init_target_to_host_charmap ();
@@ -4519,7 +4515,7 @@ handle_printf_call (gimple_stmt_iterator *gsi, range_query *query)
/* For non-bounded functions like sprintf, determine the size
of the destination from the object or pointer passed to it
as the first argument. */
- dstsize = get_destination_size (dstptr);
+ dstsize = get_destination_size (dstptr, ptr_qry);
}
else if (tree size = gimple_call_arg (info.callstmt, idx_dstsize))
{
@@ -4566,7 +4562,7 @@ handle_printf_call (gimple_stmt_iterator *gsi, range_query *query)
and use the greater of the two at level 1 and the smaller
of them at level 2. */
value_range vr;
- query->range_of_expr (vr, size, info.callstmt);
+ ptr_qry.rvals->range_of_expr (vr, size, info.callstmt);
if (!vr.undefined_p ())
{
@@ -4683,7 +4679,7 @@ handle_printf_call (gimple_stmt_iterator *gsi, range_query *query)
never set to true again). */
res.posunder4k = posunder4k && dstptr;
- bool success = compute_format_length (info, &res, query);
+ bool success = compute_format_length (info, &res, ptr_qry.rvals);
if (res.warned)
gimple_set_no_warning (info.callstmt, true);
new file mode 100644
@@ -0,0 +1,67 @@
+typedef __SIZE_TYPE__ size_t;
+
+extern "C" {
+ void free (void*);
+ void* malloc (size_t);
+}
+
+struct HasNew
+{
+ void* operator new (size_t);
+};
+
+struct HasDelete
+{
+ void operator delete (void*);
+};
+
+struct HasNewDelete
+{
+ void* operator new (size_t);
+ void operator delete (void*);
+};
+
+void sink (void*);
+
+void nowarn_new_delete ()
+{
+ {
+ HasNew *p = new HasNew;
+ sink (p);
+ delete p;
+ }
+
+ {
+ HasNewDelete *p = new HasNewDelete;
+ sink (p);
+ delete p;
+ }
+
+ {
+ HasDelete *p = new HasDelete;
+ sink (p);
+ delete p;
+ }
+}
+
+
+void nowarn_new_free ()
+{
+ {
+ HasNew *p = new HasNew;
+ sink (p);
+ free (p);
+ }
+
+ {
+ HasNewDelete *p = new HasNewDelete;
+ sink (p);
+ delete p;
+ }
+
+ {
+ HasDelete *p = new HasDelete;
+ sink (p);
+ delete p;
+ }
+}
new file mode 100644
@@ -0,0 +1,76 @@
+/* PR middle-end/97373 - missing warning on sprintf into allocated destination
+ { dg-do compile }
+ { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#include "../range.h"
+
+extern void* alloca (size_t);
+extern void* malloc (size_t);
+
+extern int sprintf (char*, const char*, ...);
+#define sprintf(d, ...) (sprintf (d, __VA_ARGS__), sink (d))
+
+void sink (void*, ...);
+
+void test_alloca_range (void)
+{
+ int n1_2 = UR (1, 2);
+ int n5_9 = UR (5, 9);
+
+ char *d = (char*)alloca (n5_9);
+
+ sprintf (d, "%i", 12345);
+
+ d += n1_2;
+ sprintf (d, "%i", 12345);
+
+ d += n1_2;
+ sprintf (d, "%i", 12345);
+
+ d += n1_2;
+ sprintf (d, "%i", 12345);
+
+ d += n1_2;
+ sprintf (d, "%i", 12345); // { dg-warning "writing a terminating nul past the end of the destination" }
+
+ d += n1_2;
+ sprintf (d, "%i", 12345); // { dg-warning "'%i' directive writing 5 bytes into a region of size 4" }
+}
+
+
+void test_malloc_range (void)
+{
+ int n2_3 = UR (2, 3);
+ int n5_9 = UR (5, 9);
+
+ char *d = (char*)malloc (n5_9);
+
+ sprintf (d, "%i", 12345);
+
+ d += n2_3;
+ sprintf (d, "%i", 12345);
+
+ d += n2_3;
+ sprintf (d, "%i", 12345); // { dg-warning "writing a terminating nul past the end of the destination" }
+
+ d += n2_3;
+ sprintf (d, "%i", 12345); // { dg-warning "'%i' directive writing 5 bytes into a region of size 3" }
+}
+
+
+void test_vla_range (void)
+{
+ int n3_4 = UR (3, 4);
+ int n5_9 = UR (5, 9);
+
+ char vla[n5_9];
+ char *d = vla;
+
+ sprintf (d, "%i", 12345);
+
+ d += n3_4;
+ sprintf (d, "%i", 12345);
+
+ d += n3_4;
+ sprintf (d, "%i", 12345); // { dg-warning "'%i' directive writing 5 bytes into a region of size 3" }
+}
@@ -180,7 +180,7 @@ T (v0 ? b[0] : b[2], bsz);
T (v0 ? b[2] : b[3], bsz);
T (v0 ? b[3] : b[2], bsz);
-T (v0 ? "1234" : b[3], bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
+T (v0 ? "1234" : b[3], bsz + 1); /* { dg-warning "bound 6 may exceed source size 5" "pr86937" } */
T (v0 ? "1234" : b[i3], bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
T (v0 ? b[3] : "1234", bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
T (v0 ? b[i3] : "1234", bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
@@ -46,7 +46,6 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssa-propagate.h"
#include "tree-ssa-strlen.h"
#include "tree-hash-traits.h"
-#include "tree-object-size.h"
#include "builtins.h"
#include "target.h"
#include "diagnostic-core.h"
@@ -1667,7 +1666,8 @@ valid_builtin_call (gimple *stmt)
strinfo. */
static void
-adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
+adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat,
+ pointer_query &ptr_qry)
{
tree vuse, callee, len;
struct laststmt_struct last = laststmt;
@@ -1754,7 +1754,9 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
/* Don't fold away an out of bounds access, as this defeats proper
warnings. */
tree dst = gimple_call_arg (last.stmt, 0);
- tree size = compute_objsize (dst, 0);
+
+ access_ref aref;
+ tree size = compute_objsize (dst, &aref, &ptr_qry);
if (size && tree_int_cst_lt (size, len))
return;
}
@@ -1912,8 +1914,7 @@ maybe_set_strlen_range (tree lhs, tree src, tree bound)
to allow accesses across subobject boundaries. */
static void
-maybe_warn_overflow (gimple *stmt, tree len,
- range_query *rvals = NULL,
+maybe_warn_overflow (gimple *stmt, tree len, pointer_query &ptr_qry,
strinfo *si = NULL, bool plus_one = false,
bool rawmem = false)
{
@@ -1939,6 +1940,12 @@ maybe_warn_overflow (gimple *stmt, tree len,
if (TREE_NO_WARNING (dest))
return;
+ /* Hack: temporarily replace PTR_QRY.OSTYPE. OSTYPE should disappear
+ as soon as compute_objsize() computes sizes for both the subobject
+ (type 1) and the enclosing object (type 0). */
+ const int save_ostype = ptr_qry.ostype;
+ ptr_qry.ostype = rawmem ? 0 : 1;
+
/* Use maximum precision to avoid overflow in the addition below.
Make sure all operands have the same precision to keep wide_int
from ICE'ing. */
@@ -1946,7 +1953,11 @@ maybe_warn_overflow (gimple *stmt, tree len,
access_ref aref;
/* The size of the destination region (which is smaller than
the destination object for stores at a non-zero offset). */
- tree destsize = compute_objsize (dest, rawmem ? 0 : 1, &aref, rvals);
+ tree destsize = compute_objsize (dest, &aref, &ptr_qry);
+
+ /* Restore PTR_QRY.OSTYPE. */
+ ptr_qry.ostype = save_ostype;
+
if (!destsize)
{
aref.sizrng[0] = 0;
@@ -1962,7 +1973,7 @@ maybe_warn_overflow (gimple *stmt, tree len,
return;
wide_int rng[2];
- if (!get_range (len, stmt, rng, rvals))
+ if (!get_range (len, stmt, rng, ptr_qry.rvals))
return;
widest_int lenrng[2] =
@@ -2091,10 +2102,10 @@ maybe_warn_overflow (gimple *stmt, tree len,
static inline void
maybe_warn_overflow (gimple *stmt, unsigned HOST_WIDE_INT len,
- range_query *rvals = NULL, strinfo *si = NULL,
+ pointer_query &ptr_qry, strinfo *si = NULL,
bool plus_one = false, bool rawmem = false)
{
- maybe_warn_overflow (stmt, build_int_cst (size_type_node, len), rvals,
+ maybe_warn_overflow (stmt, build_int_cst (size_type_node, len), ptr_qry,
si, plus_one, rawmem);
}
@@ -2394,7 +2405,7 @@ handle_builtin_strchr (gimple_stmt_iterator *gsi)
static void
handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
- range_query *rvals)
+ pointer_query &ptr_qry)
{
int idx, didx;
tree src, dst, srclen, len, lhs, type, fn, oldlen;
@@ -2420,7 +2431,7 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
return;
if (olddsi != NULL)
- adjust_last_stmt (olddsi, stmt, false);
+ adjust_last_stmt (olddsi, stmt, false, ptr_qry);
srclen = NULL_TREE;
if (si != NULL)
@@ -2428,10 +2439,10 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
else if (idx < 0)
srclen = build_int_cst (size_type_node, ~idx);
- maybe_warn_overflow (stmt, srclen, rvals, olddsi, true);
+ maybe_warn_overflow (stmt, srclen, ptr_qry, olddsi, true);
if (olddsi != NULL)
- adjust_last_stmt (olddsi, stmt, false);
+ adjust_last_stmt (olddsi, stmt, false, ptr_qry);
loc = gimple_location (stmt);
if (srclen == NULL_TREE)
@@ -2776,7 +2787,8 @@ is_strlen_related_p (tree src, tree len)
*/
bool
-maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
+maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
+ pointer_query *ptr_qry /* = NULL */)
{
gimple *stmt = gsi_stmt (gsi);
if (gimple_no_warning_p (stmt))
@@ -3039,7 +3051,8 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
}
}
- if (tree dstsize = compute_objsize (dst, 1))
+ access_ref aref;
+ if (tree dstsize = compute_objsize (dst, &aref, ptr_qry))
{
/* The source length is unknown. Try to determine the destination
size and see if it matches the specified bound. If not, bail.
@@ -3054,7 +3067,7 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
/* Avoid warning for strncpy(a, b, N) calls where the following
equalities hold:
N == sizeof a && N == sizeof b */
- if (tree srcsize = compute_objsize (src, 1))
+ if (tree srcsize = compute_objsize (src, &aref, ptr_qry))
if (wi::to_wide (srcsize) == cntrange[1])
return false;
@@ -3197,7 +3210,7 @@ handle_builtin_stxncpy_strncat (bool append_p, gimple_stmt_iterator *gsi)
static void
handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
- range_query *rvals)
+ pointer_query &ptr_qry)
{
tree lhs, oldlen, newlen;
gimple *stmt = gsi_stmt (*gsi);
@@ -3217,8 +3230,8 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
if (olddsi != NULL
&& !integer_zerop (len))
{
- maybe_warn_overflow (stmt, len, rvals, olddsi, false, false);
- adjust_last_stmt (olddsi, stmt, false);
+ maybe_warn_overflow (stmt, len, ptr_qry, olddsi, false, false);
+ adjust_last_stmt (olddsi, stmt, false, ptr_qry);
}
int idx = get_stridx (src);
@@ -3295,7 +3308,7 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
}
if (olddsi != NULL && TREE_CODE (len) == SSA_NAME)
- adjust_last_stmt (olddsi, stmt, false);
+ adjust_last_stmt (olddsi, stmt, false, ptr_qry);
if (didx == 0)
{
@@ -3377,7 +3390,8 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
is known. */
static void
-handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
+handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi,
+ pointer_query &ptr_qry)
{
int idx, didx;
tree srclen, args, type, fn, objsz, endptr;
@@ -3605,7 +3619,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
computed by transforming this strcpy into stpcpy. */
if (srclen == NULL_TREE && dsi->dont_invalidate)
dsi->stmt = stmt;
- adjust_last_stmt (dsi, stmt, true);
+ adjust_last_stmt (dsi, stmt, true, ptr_qry);
if (srclen != NULL_TREE)
{
laststmt.stmt = stmt;
@@ -3662,13 +3676,13 @@ handle_alloc_call (enum built_in_function bcode, gimple_stmt_iterator *gsi)
static bool
handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write,
- range_query *rvals)
+ pointer_query &ptr_qry)
{
gimple *memset_stmt = gsi_stmt (*gsi);
tree ptr = gimple_call_arg (memset_stmt, 0);
/* Set to the non-constant offset added to PTR. */
wide_int offrng[2];
- int idx1 = get_stridx (ptr, offrng, rvals);
+ int idx1 = get_stridx (ptr, offrng, ptr_qry.rvals);
if (idx1 <= 0)
return false;
strinfo *si1 = get_strinfo (idx1);
@@ -3684,7 +3698,7 @@ handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write,
tree memset_size = gimple_call_arg (memset_stmt, 2);
/* Check for overflow. */
- maybe_warn_overflow (memset_stmt, memset_size, rvals, NULL, false, false);
+ maybe_warn_overflow (memset_stmt, memset_size, ptr_qry, NULL, false, false);
/* Bail when there is no statement associated with the destination
(the statement may be null even when SI1->ALLOC is not). */
@@ -4728,7 +4742,7 @@ count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
static bool
handle_store (gimple_stmt_iterator *gsi, bool *zero_write,
- range_query *rvals)
+ pointer_query &ptr_qry)
{
int idx = -1;
strinfo *si = NULL;
@@ -4736,6 +4750,8 @@ handle_store (gimple_stmt_iterator *gsi, bool *zero_write,
tree ssaname = NULL_TREE, lhs = gimple_assign_lhs (stmt);
tree rhs = gimple_assign_rhs1 (stmt);
+ range_query *const rvals = ptr_qry.rvals;
+
/* The offset of the first byte in LHS modified by the store. */
unsigned HOST_WIDE_INT offset = 0;
@@ -4762,7 +4778,7 @@ handle_store (gimple_stmt_iterator *gsi, bool *zero_write,
unsigned lenrange[] = { UINT_MAX, 0, 0 };
if (count_nonzero_bytes (rhs, lenrange, &dummy, &dummy, &dummy,
rvals))
- maybe_warn_overflow (stmt, lenrange[2], rvals);
+ maybe_warn_overflow (stmt, lenrange[2], ptr_qry);
return true;
}
@@ -4802,7 +4818,7 @@ handle_store (gimple_stmt_iterator *gsi, bool *zero_write,
storing_nonzero_p = lenrange[1] > 0;
*zero_write = storing_all_zeros_p;
- maybe_warn_overflow (stmt, lenrange[2], rvals);
+ maybe_warn_overflow (stmt, lenrange[2], ptr_qry);
}
else
{
@@ -4920,7 +4936,7 @@ handle_store (gimple_stmt_iterator *gsi, bool *zero_write,
/* We're overwriting the nul terminator with a nonzero or
unknown character. If the previous stmt was a memcpy,
its length may be decreased. */
- adjust_last_stmt (si, stmt, false);
+ adjust_last_stmt (si, stmt, false, ptr_qry);
si = unshare_strinfo (si);
if (storing_nonzero_p)
{
@@ -5138,7 +5154,7 @@ is_char_type (tree type)
static bool
strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, bool *zero_write,
- range_query *rvals)
+ pointer_query &ptr_qry)
{
gimple *stmt = gsi_stmt (*gsi);
@@ -5155,7 +5171,7 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, bool *zero_write,
if (!flag_optimize_strlen
|| !strlen_optimize
|| !valid_builtin_call (stmt))
- return !handle_printf_call (gsi, rvals);
+ return !handle_printf_call (gsi, ptr_qry);
tree callee = gimple_call_fndecl (stmt);
switch (DECL_FUNCTION_CODE (callee))
@@ -5171,7 +5187,7 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, bool *zero_write,
case BUILT_IN_STRCPY_CHK:
case BUILT_IN_STPCPY:
case BUILT_IN_STPCPY_CHK:
- handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi, rvals);
+ handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi, ptr_qry);
break;
case BUILT_IN_STRNCAT:
@@ -5190,11 +5206,11 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, bool *zero_write,
case BUILT_IN_MEMCPY_CHK:
case BUILT_IN_MEMPCPY:
case BUILT_IN_MEMPCPY_CHK:
- handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi, rvals);
+ handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi, ptr_qry);
break;
case BUILT_IN_STRCAT:
case BUILT_IN_STRCAT_CHK:
- handle_builtin_strcat (DECL_FUNCTION_CODE (callee), gsi);
+ handle_builtin_strcat (DECL_FUNCTION_CODE (callee), gsi, ptr_qry);
break;
case BUILT_IN_ALLOCA:
case BUILT_IN_ALLOCA_WITH_ALIGN:
@@ -5203,7 +5219,7 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, bool *zero_write,
handle_alloc_call (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_MEMSET:
- if (handle_builtin_memset (gsi, zero_write, rvals))
+ if (handle_builtin_memset (gsi, zero_write, ptr_qry))
return false;
break;
case BUILT_IN_MEMCMP:
@@ -5212,11 +5228,11 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, bool *zero_write,
break;
case BUILT_IN_STRCMP:
case BUILT_IN_STRNCMP:
- if (handle_builtin_string_cmp (gsi, rvals))
+ if (handle_builtin_string_cmp (gsi, ptr_qry.rvals))
return false;
break;
default:
- if (handle_printf_call (gsi, rvals))
+ if (handle_printf_call (gsi, ptr_qry))
return false;
break;
}
@@ -5374,7 +5390,7 @@ handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh,
static bool
check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
- range_query *rvals)
+ pointer_query &ptr_qry)
{
gimple *stmt = gsi_stmt (*gsi);
@@ -5384,7 +5400,7 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
if (is_gimple_call (stmt))
{
- if (!strlen_check_and_optimize_call (gsi, &zero_write, rvals))
+ if (!strlen_check_and_optimize_call (gsi, &zero_write, ptr_qry))
return false;
}
else if (!flag_optimize_strlen || !strlen_optimize)
@@ -5409,7 +5425,7 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
}
else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (lhs_type))
/* Handle assignment to a character. */
- handle_integral_assign (gsi, cleanup_eh, rvals);
+ handle_integral_assign (gsi, cleanup_eh, ptr_qry.rvals);
else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
{
tree type = TREE_TYPE (lhs);
@@ -5440,7 +5456,7 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
}
/* Handle a single or multibyte assignment. */
- if (is_char_store && !handle_store (gsi, &zero_write, rvals))
+ if (is_char_store && !handle_store (gsi, &zero_write, ptr_qry))
return false;
}
}
@@ -5514,8 +5530,9 @@ public:
strlen_dom_walker (cdi_direction direction)
: dom_walker (direction),
evrp (false),
+ ptr_qry (&evrp, &var_cache),
m_cleanup_cfg (false)
- {}
+ { }
virtual edge before_dom_children (basic_block);
virtual void after_dom_children (basic_block);
@@ -5524,6 +5541,11 @@ public:
to track strlen results across integer variable assignments. */
evrp_range_analyzer evrp;
+ /* A pointer_query object and its cache to store information about
+ pointers and their targets in. */
+ pointer_query ptr_qry;
+ auto_vec<access_ref> var_cache;
+
/* Flag that will trigger TODO_cleanup_cfg to be returned in strlen
execute function. */
bool m_cleanup_cfg;
@@ -5617,7 +5639,10 @@ strlen_dom_walker::before_dom_children (basic_block bb)
can be used by printf argument processing. */
evrp.record_ranges_from_stmt (stmt, false);
- if (check_and_optimize_stmt (&gsi, &cleanup_eh, &evrp))
+ /* Reset search depth preformance counter. */
+ ptr_qry.depth = 0;
+
+ if (check_and_optimize_stmt (&gsi, &cleanup_eh, ptr_qry))
gsi_next (&gsi);
}
@@ -5685,6 +5710,17 @@ printf_strlen_execute (function *fun, bool warn_only)
strlen_dom_walker walker (CDI_DOMINATORS);
walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "pointer_query counters\n"
+ " cache size: %u\n"
+ " hits: %u\n"
+ " misses: %u\n"
+ " failures: %u\n"
+ " max_depth: %u\n",
+ walker.ptr_qry.var_cache->length (),
+ walker.ptr_qry.hits, walker.ptr_qry.misses,
+ walker.ptr_qry.failures, walker.ptr_qry.max_depth);
+
ssa_ver_to_stridx.release ();
strinfo_pool.release ();
if (decl_to_stridxlist_htab)
@@ -5710,9 +5746,6 @@ printf_strlen_execute (function *fun, bool warn_only)
loop_optimizer_finalize ();
}
- /* Clean up object size info. */
- fini_object_sizes ();
-
return walker.m_cleanup_cfg ? TODO_cleanup_cfg : 0;
}
@@ -21,8 +21,11 @@
#ifndef GCC_TREE_SSA_STRLEN_H
#define GCC_TREE_SSA_STRLEN_H
+class pointer_query;
+
extern bool is_strlen_related_p (tree, tree);
-extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree);
+extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree,
+ pointer_query * = NULL);
extern tree set_strlen_range (tree, wide_int, wide_int, tree = NULL_TREE);
extern tree get_range (tree, gimple *, wide_int[2],
@@ -33,6 +36,6 @@ extern void get_range_strlen_dynamic (tree, gimple *, c_strlen_data *,
class range_query *);
/* APIs internal to strlen pass. Defined in gimple-ssa-sprintf.c. */
-extern bool handle_printf_call (gimple_stmt_iterator *, class range_query *);
+extern bool handle_printf_call (gimple_stmt_iterator *, pointer_query &);
#endif // GCC_TREE_SSA_STRLEN_H