Enable ranger and caching in pass_waccess.
gcc/ChangeLog: * gimple-ssa-warn-access.cc (get_size_range): Add argument. (check_access): Pass additional argument. (check_memop_access): Remove template and make a member function. (maybe_check_dealloc_call): Make a pass_waccess member function. (class pass_waccess): Add, rename, and remove members. (pass_waccess::pass_waccess): Adjust to name change. (pass_waccess::~pass_waccess): Same. (check_alloca): Make a member function. (check_alloc_size_call): Same. (check_strcat): Same. (check_strncat): Same. (check_stxcpy): Same. (check_stxncpy): Same. (check_strncmp): Same. (maybe_warn_rdwr_sizes): Rename... (pass_waccess::maybe_check_access_sizes): ...to this. (pass_waccess::check_call): Adjust to name changes. (pass_waccess::maybe_check_dealloc_call): Make a pass_waccess member function. (pass_waccess::execute): Adjust to name changes. * gimple-ssa-warn-access.h (check_memop_access): Remove. * pointer-query.cc (access_ref::phi): Handle null pointer. (access_ref::inform_access): Same. (pointer_query::put_ref): Modify a cached value, not a copy of it. (pointer_query::dump): New function. (compute_objsize_r): Avoid overwriting access_ref::bndrng. Cache more results. * pointer-query.h (pointer_query::dump): Declare. * tree-ssa-strlen.c (get_range): Simplify. Use function query. (dump_strlen_info): Use function query. (printf_strlen_execute): Factor code out into pointer_query::put_ref. gcc/testsuite/ChangeLog: * gcc.dg/Wstringop-overflow-11.c: Remove xfails. * gcc.dg/Wstringop-overflow-12.c: Same. * gcc.dg/Wstringop-overflow-43.c: Add xfails. * gcc.dg/Wstringop-overflow-73.c: New test.
This commit is contained in:
parent
ac6e77aacf
commit
ece28da924
@ -1190,10 +1190,11 @@ warn_for_access (location_t loc, tree func, tree expr, int opt,
|
||||
by BNDRNG if nonnull and valid. */
|
||||
|
||||
static void
|
||||
get_size_range (tree bound, tree range[2], const offset_int bndrng[2])
|
||||
get_size_range (range_query *query, tree bound, tree range[2],
|
||||
const offset_int bndrng[2])
|
||||
{
|
||||
if (bound)
|
||||
get_size_range (bound, range);
|
||||
get_size_range (query, bound, NULL, range);
|
||||
|
||||
if (!bndrng || (bndrng[0] == 0 && bndrng[1] == HOST_WIDE_INT_M1U))
|
||||
return;
|
||||
@ -1337,7 +1338,7 @@ check_access (GimpleOrTree exp, tree dstwrite,
|
||||
|
||||
/* 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);
|
||||
get_size_range (NULL, 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
|
||||
@ -1431,7 +1432,7 @@ check_access (GimpleOrTree exp, tree dstwrite,
|
||||
{
|
||||
/* 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);
|
||||
get_size_range (NULL, maxread, range, pad ? pad->src.bndrng : NULL);
|
||||
|
||||
location_t loc = get_location (exp);
|
||||
tree size = dstsize;
|
||||
@ -1478,7 +1479,7 @@ check_access (GimpleOrTree exp, tree dstwrite,
|
||||
{
|
||||
/* 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);
|
||||
get_size_range (NULL, 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]);
|
||||
@ -1529,41 +1530,6 @@ check_access (tree expr, tree dstwrite,
|
||||
mode, pad);
|
||||
}
|
||||
|
||||
/* 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
|
||||
argument or null, and LEN is the number of bytes. Use Object Size type-0
|
||||
regardless of the OPT_Wstringop_overflow_ setting. Return true on success
|
||||
(no overflow or invalid sizes), false otherwise. */
|
||||
|
||||
template <class GimpleOrTree>
|
||||
static bool
|
||||
check_memop_access (GimpleOrTree expr, tree dest, tree src, tree size)
|
||||
{
|
||||
/* For functions like memset and memcpy that operate on raw memory
|
||||
try to determine the size of the largest source and destination
|
||||
object using type-0 Object Size regardless of the object size
|
||||
type specified by the option. */
|
||||
access_data data (expr, access_read_write);
|
||||
tree srcsize = src ? compute_objsize (src, 0, &data.src) : NULL_TREE;
|
||||
tree dstsize = compute_objsize (dest, 0, &data.dst);
|
||||
|
||||
return check_access (expr, size, /*maxread=*/NULL_TREE,
|
||||
srcsize, dstsize, data.mode, &data);
|
||||
}
|
||||
|
||||
bool
|
||||
check_memop_access (gimple *stmt, tree dest, tree src, tree size)
|
||||
{
|
||||
return check_memop_access<gimple *>(stmt, dest, src, size);
|
||||
}
|
||||
|
||||
bool
|
||||
check_memop_access (tree expr, tree dest, tree src, tree size)
|
||||
{
|
||||
return check_memop_access<tree>(expr, dest, src, size);
|
||||
}
|
||||
|
||||
/* A convenience wrapper for check_access above to check access
|
||||
by a read-only function like puts. */
|
||||
|
||||
@ -2111,135 +2077,6 @@ warn_dealloc_offset (location_t loc, gimple *call, const access_ref &aref)
|
||||
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. */
|
||||
|
||||
static void
|
||||
maybe_check_dealloc_call (gcall *call)
|
||||
{
|
||||
tree fndecl = gimple_call_fndecl (call);
|
||||
if (!fndecl)
|
||||
return;
|
||||
|
||||
unsigned argno = fndecl_dealloc_argno (fndecl);
|
||||
if ((unsigned) call_nargs (call) <= argno)
|
||||
return;
|
||||
|
||||
tree ptr = gimple_call_arg (call, 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 = fndecl;
|
||||
location_t loc = gimple_location (call);
|
||||
|
||||
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))
|
||||
{
|
||||
inform (get_location (ref), "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, call, 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, call, 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, call, 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, call, aref))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const pass_data pass_data_waccess = {
|
||||
@ -2267,6 +2104,11 @@ class pass_waccess : public gimple_opt_pass
|
||||
virtual bool gate (function *);
|
||||
virtual unsigned int execute (function *);
|
||||
|
||||
private:
|
||||
/* Not copyable or assignable. */
|
||||
pass_waccess (pass_waccess &) = delete;
|
||||
void operator= (pass_waccess &) = delete;
|
||||
|
||||
/* Check a call to a built-in function. */
|
||||
bool check_builtin (gcall *);
|
||||
|
||||
@ -2277,28 +2119,33 @@ class pass_waccess : public gimple_opt_pass
|
||||
void check (basic_block);
|
||||
|
||||
/* Check a call to a function. */
|
||||
void check (gcall *);
|
||||
void check (gcall *);
|
||||
|
||||
private:
|
||||
/* Not copyable or assignable. */
|
||||
pass_waccess (pass_waccess &) = delete;
|
||||
void operator= (pass_waccess &) = delete;
|
||||
/* Check a call to the named built-in function. */
|
||||
void check_alloca (gcall *);
|
||||
void check_alloc_size_call (gcall *);
|
||||
void check_strcat (gcall *);
|
||||
void check_strncat (gcall *);
|
||||
void check_stxcpy (gcall *);
|
||||
void check_stxncpy (gcall *);
|
||||
void check_strncmp (gcall *);
|
||||
void check_memop_access (gimple *, tree, tree, tree);
|
||||
|
||||
void maybe_check_dealloc_call (gcall *);
|
||||
void maybe_check_access_sizes (rdwr_map *, tree, tree, gimple *);
|
||||
|
||||
/* A pointer_query object and its cache to store information about
|
||||
pointers and their targets in. */
|
||||
pointer_query ptr_qry;
|
||||
pointer_query::cache_type var_cache;
|
||||
|
||||
gimple_ranger *m_ranger;
|
||||
pointer_query m_ptr_qry;
|
||||
pointer_query::cache_type m_var_cache;
|
||||
};
|
||||
|
||||
/* Construct the pass. */
|
||||
|
||||
pass_waccess::pass_waccess (gcc::context *ctxt)
|
||||
: gimple_opt_pass (pass_data_waccess, ctxt),
|
||||
ptr_qry (m_ranger, &var_cache),
|
||||
var_cache (),
|
||||
m_ranger ()
|
||||
m_ptr_qry (NULL, &m_var_cache),
|
||||
m_var_cache ()
|
||||
{
|
||||
}
|
||||
|
||||
@ -2306,7 +2153,7 @@ pass_waccess::pass_waccess (gcc::context *ctxt)
|
||||
|
||||
pass_waccess::~pass_waccess ()
|
||||
{
|
||||
ptr_qry.flush_cache ();
|
||||
m_ptr_qry.flush_cache ();
|
||||
}
|
||||
|
||||
/* Return true when any checks performed by the pass are enabled. */
|
||||
@ -2494,8 +2341,8 @@ maybe_warn_alloc_args_overflow (gimple *stmt, const tree args[2],
|
||||
|
||||
/* Check a call to an alloca function for an excessive size. */
|
||||
|
||||
static void
|
||||
check_alloca (gimple *stmt)
|
||||
void
|
||||
pass_waccess::check_alloca (gcall *stmt)
|
||||
{
|
||||
if ((warn_vla_limit >= HOST_WIDE_INT_MAX
|
||||
&& warn_alloc_size_limit < warn_vla_limit)
|
||||
@ -2515,8 +2362,8 @@ check_alloca (gimple *stmt)
|
||||
|
||||
/* Check a call to an allocation function for an excessive size. */
|
||||
|
||||
static void
|
||||
check_alloc_size_call (gimple *stmt)
|
||||
void
|
||||
pass_waccess::check_alloc_size_call (gcall *stmt)
|
||||
{
|
||||
if (gimple_call_num_args (stmt) < 1)
|
||||
/* Avoid invalid calls to functions without a prototype. */
|
||||
@ -2565,8 +2412,8 @@ check_alloc_size_call (gimple *stmt)
|
||||
|
||||
/* Check a call STMT to strcat() for overflow and warn if it does. */
|
||||
|
||||
static void
|
||||
check_strcat (gimple *stmt)
|
||||
void
|
||||
pass_waccess::check_strcat (gcall *stmt)
|
||||
{
|
||||
if (!warn_stringop_overflow && !warn_stringop_overread)
|
||||
return;
|
||||
@ -2581,8 +2428,8 @@ check_strcat (gimple *stmt)
|
||||
access_data data (stmt, access_read_write, NULL_TREE, true,
|
||||
NULL_TREE, true);
|
||||
const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
|
||||
compute_objsize (src, ost, &data.src);
|
||||
tree destsize = compute_objsize (dest, ost, &data.dst);
|
||||
compute_objsize (src, ost, &data.src, &m_ptr_qry);
|
||||
tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
|
||||
|
||||
check_access (stmt, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
|
||||
src, destsize, data.mode, &data);
|
||||
@ -2590,8 +2437,8 @@ check_strcat (gimple *stmt)
|
||||
|
||||
/* Check a call STMT to strcat() for overflow and warn if it does. */
|
||||
|
||||
static void
|
||||
check_strncat (gimple *stmt)
|
||||
void
|
||||
pass_waccess::check_strncat (gcall *stmt)
|
||||
{
|
||||
if (!warn_stringop_overflow && !warn_stringop_overread)
|
||||
return;
|
||||
@ -2623,7 +2470,8 @@ check_strncat (gimple *stmt)
|
||||
/* Try to verify that the destination is big enough for the shortest
|
||||
string. First try to determine the size of the destination object
|
||||
into which the source is being copied. */
|
||||
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
|
||||
const int ost = warn_stringop_overflow - 1;
|
||||
tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
|
||||
|
||||
/* Add one for the terminating nul. */
|
||||
tree srclen = (maxlen
|
||||
@ -2658,8 +2506,8 @@ check_strncat (gimple *stmt)
|
||||
/* Check a call STMT to stpcpy() or strcpy() for overflow and warn
|
||||
if it does. */
|
||||
|
||||
static void
|
||||
check_stxcpy (gimple *stmt)
|
||||
void
|
||||
pass_waccess::check_stxcpy (gcall *stmt)
|
||||
{
|
||||
tree dst = call_arg (stmt, 0);
|
||||
tree src = call_arg (stmt, 1);
|
||||
@ -2679,8 +2527,8 @@ check_stxcpy (gimple *stmt)
|
||||
access_data data (stmt, access_read_write, NULL_TREE, true,
|
||||
NULL_TREE, true);
|
||||
const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
|
||||
compute_objsize (src, ost, &data.src);
|
||||
tree dstsize = compute_objsize (dst, ost, &data.dst);
|
||||
compute_objsize (src, ost, &data.src, &m_ptr_qry);
|
||||
tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
|
||||
check_access (stmt, /*dstwrite=*/ NULL_TREE,
|
||||
/*maxread=*/ NULL_TREE, /*srcstr=*/ src,
|
||||
dstsize, data.mode, &data);
|
||||
@ -2696,8 +2544,8 @@ check_stxcpy (gimple *stmt)
|
||||
/* Check a call STMT to stpncpy() or strncpy() for overflow and warn
|
||||
if it does. */
|
||||
|
||||
static void
|
||||
check_stxncpy (gimple *stmt)
|
||||
void
|
||||
pass_waccess::check_stxncpy (gcall *stmt)
|
||||
{
|
||||
if (!warn_stringop_overflow)
|
||||
return;
|
||||
@ -2709,8 +2557,8 @@ check_stxncpy (gimple *stmt)
|
||||
|
||||
access_data data (stmt, access_read_write, len, true, len, true);
|
||||
const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
|
||||
compute_objsize (src, ost, &data.src);
|
||||
tree dstsize = compute_objsize (dst, ost, &data.dst);
|
||||
compute_objsize (src, ost, &data.src, &m_ptr_qry);
|
||||
tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
|
||||
|
||||
check_access (stmt, /*dstwrite=*/len,
|
||||
/*maxread=*/len, src, dstsize, data.mode, &data);
|
||||
@ -2719,8 +2567,8 @@ check_stxncpy (gimple *stmt)
|
||||
/* Check a call STMT to stpncpy() or strncpy() for overflow and warn
|
||||
if it does. */
|
||||
|
||||
static void
|
||||
check_strncmp (gimple *stmt)
|
||||
void
|
||||
pass_waccess::check_strncmp (gcall *stmt)
|
||||
{
|
||||
if (!warn_stringop_overread)
|
||||
return;
|
||||
@ -2764,7 +2612,7 @@ check_strncmp (gimple *stmt)
|
||||
/* Determine the range of the bound first and bail if it fails; it's
|
||||
cheaper than computing the size of the objects. */
|
||||
tree bndrng[2] = { NULL_TREE, NULL_TREE };
|
||||
get_size_range (bound, bndrng, adata1.src.bndrng);
|
||||
get_size_range (m_ptr_qry.rvals, bound, bndrng, adata1.src.bndrng);
|
||||
if (!bndrng[0] || integer_zerop (bndrng[0]))
|
||||
return;
|
||||
|
||||
@ -2775,8 +2623,8 @@ check_strncmp (gimple *stmt)
|
||||
|
||||
/* compute_objsize almost never fails (and ultimately should never
|
||||
fail). Don't bother to handle the rare case when it does. */
|
||||
if (!compute_objsize (arg1, 1, &adata1.src)
|
||||
|| !compute_objsize (arg2, 1, &adata2.src))
|
||||
if (!compute_objsize (arg1, 1, &adata1.src, &m_ptr_qry)
|
||||
|| !compute_objsize (arg2, 1, &adata2.src, &m_ptr_qry))
|
||||
return;
|
||||
|
||||
/* Compute the size of the remaining space in each array after
|
||||
@ -2810,6 +2658,29 @@ check_strncmp (gimple *stmt)
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine and check the sizes of the source and the destination
|
||||
of calls to __builtin_{bzero,memcpy,mempcpy,memset} calls. STMT is
|
||||
the call statement, DEST is the destination argument, SRC is the source
|
||||
argument or null, and SIZE is the number of bytes being accessed. Use
|
||||
Object Size type-0 regardless of the OPT_Wstringop_overflow_ setting.
|
||||
Return true on success (no overflow or invalid sizes), false otherwise. */
|
||||
|
||||
void
|
||||
pass_waccess::check_memop_access (gimple *stmt, tree dest, tree src, tree size)
|
||||
{
|
||||
/* For functions like memset and memcpy that operate on raw memory
|
||||
try to determine the size of the largest source and destination
|
||||
object using type-0 Object Size regardless of the object size
|
||||
type specified by the option. */
|
||||
access_data data (stmt, access_read_write);
|
||||
tree srcsize
|
||||
= src ? compute_objsize (src, 0, &data.src, &m_ptr_qry) : NULL_TREE;
|
||||
tree dstsize = compute_objsize (dest, 0, &data.dst, &m_ptr_qry);
|
||||
|
||||
check_access (stmt, size, /*maxread=*/NULL_TREE,
|
||||
srcsize, dstsize, data.mode, &data);
|
||||
}
|
||||
|
||||
/* Check call STMT to a built-in function for invalid accesses. Return
|
||||
true if a call has been handled. */
|
||||
|
||||
@ -2968,8 +2839,9 @@ append_attrname (const std::pair<int, attr_access> &access,
|
||||
arguments and diagnose past-the-end accesses and related problems
|
||||
in the function call EXP. */
|
||||
|
||||
static void
|
||||
maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, gimple *stmt)
|
||||
void
|
||||
pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
|
||||
gimple *stmt)
|
||||
{
|
||||
auto_diagnostic_group adg;
|
||||
|
||||
@ -3028,7 +2900,7 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, gimple *stmt)
|
||||
/* Format the value or range to avoid an explosion of messages. */
|
||||
char sizstr[80];
|
||||
tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
|
||||
if (get_size_range (access_size, sizrng, true))
|
||||
if (get_size_range (m_ptr_qry.rvals, access_size, NULL, sizrng, 1))
|
||||
{
|
||||
char *s0 = print_generic_expr_to_str (sizrng[0]);
|
||||
if (tree_int_cst_equal (sizrng[0], sizrng[1]))
|
||||
@ -3160,7 +3032,7 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, gimple *stmt)
|
||||
NULL_TREE, false);
|
||||
access_ref* const pobj = (access.second.mode == access_write_only
|
||||
? &data.dst : &data.src);
|
||||
tree objsize = compute_objsize (ptr, 1, pobj);
|
||||
tree objsize = compute_objsize (ptr, 1, pobj, &m_ptr_qry);
|
||||
|
||||
/* The size of the destination or source object. */
|
||||
tree dstsize = NULL_TREE, srcsize = NULL_TREE;
|
||||
@ -3276,7 +3148,7 @@ pass_waccess::check_call (gcall *stmt)
|
||||
|
||||
/* Check attribute access arguments. */
|
||||
tree fndecl = gimple_call_fndecl (stmt);
|
||||
maybe_warn_rdwr_sizes (&rdwr_idx, fndecl, fntype, stmt);
|
||||
maybe_check_access_sizes (&rdwr_idx, fndecl, fntype, stmt);
|
||||
|
||||
check_alloc_size_call (stmt);
|
||||
return true;
|
||||
@ -3294,6 +3166,138 @@ check_nonstring_args (gcall *stmt)
|
||||
maybe_warn_nonstring_arg (fndecl, stmt);
|
||||
}
|
||||
|
||||
/* 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
|
||||
pass_waccess::maybe_check_dealloc_call (gcall *call)
|
||||
{
|
||||
tree fndecl = gimple_call_fndecl (call);
|
||||
if (!fndecl)
|
||||
return;
|
||||
|
||||
unsigned argno = fndecl_dealloc_argno (fndecl);
|
||||
if ((unsigned) call_nargs (call) <= argno)
|
||||
return;
|
||||
|
||||
tree ptr = gimple_call_arg (call, argno);
|
||||
if (integer_zerop (ptr))
|
||||
return;
|
||||
|
||||
access_ref aref;
|
||||
if (!compute_objsize (ptr, 0, &aref, &m_ptr_qry))
|
||||
return;
|
||||
|
||||
tree ref = aref.ref;
|
||||
if (integer_zerop (ref))
|
||||
return;
|
||||
|
||||
tree dealloc_decl = fndecl;
|
||||
location_t loc = gimple_location (call);
|
||||
|
||||
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))
|
||||
{
|
||||
inform (get_location (ref), "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, call, 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 (!def_stmt)
|
||||
return;
|
||||
|
||||
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, call, 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, call, 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, call, aref))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check call STMT for invalid accesses. */
|
||||
|
||||
void
|
||||
@ -3329,14 +3333,21 @@ unsigned
|
||||
pass_waccess::execute (function *fun)
|
||||
{
|
||||
/* Create a new ranger instance and associate it with FUN. */
|
||||
m_ranger = enable_ranger (fun);
|
||||
m_ptr_qry.rvals = enable_ranger (fun);
|
||||
|
||||
basic_block bb;
|
||||
FOR_EACH_BB_FN (bb, fun)
|
||||
check (bb);
|
||||
|
||||
/* Release the ranger instance and replace it with a global ranger. */
|
||||
if (dump_file)
|
||||
m_ptr_qry.dump (dump_file, (dump_flags & TDF_DETAILS) != 0);
|
||||
|
||||
m_ptr_qry.flush_cache ();
|
||||
|
||||
/* Release the ranger instance and replace it with a global ranger.
|
||||
Also reset the pointer since calling disable_ranger() deletes it. */
|
||||
disable_ranger (fun);
|
||||
m_ptr_qry.rvals = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -45,7 +45,6 @@ class access_data;
|
||||
extern bool check_access (tree, tree, tree, tree, tree, access_mode,
|
||||
const access_data * = NULL);
|
||||
|
||||
extern bool check_memop_access (tree, tree, tree, tree);
|
||||
extern bool check_read_access (gimple *, tree, tree = NULL_TREE, int ost = 1);
|
||||
extern bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
|
||||
|
||||
|
@ -34,10 +34,13 @@
|
||||
#include "stringpool.h"
|
||||
#include "attribs.h"
|
||||
#include "gimple-fold.h"
|
||||
#include "gimple-ssa.h"
|
||||
#include "intl.h"
|
||||
#include "attr-fnspec.h"
|
||||
#include "gimple-range.h"
|
||||
#include "pointer-query.h"
|
||||
#include "tree-pretty-print.h"
|
||||
#include "tree-ssanames.h"
|
||||
|
||||
static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
|
||||
pointer_query *);
|
||||
@ -307,7 +310,7 @@ get_size_range (range_query *query, tree exp, gimple *stmt, tree range[2],
|
||||
enum value_range_kind range_type;
|
||||
|
||||
if (!query)
|
||||
query = get_global_range_query ();
|
||||
query = get_range_query (cfun);
|
||||
|
||||
if (integral)
|
||||
{
|
||||
@ -628,7 +631,7 @@ access_ref::phi () const
|
||||
return NULL;
|
||||
|
||||
gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
|
||||
if (gimple_code (def_stmt) != GIMPLE_PHI)
|
||||
if (!def_stmt || gimple_code (def_stmt) != GIMPLE_PHI)
|
||||
return NULL;
|
||||
|
||||
return as_a <gphi *> (def_stmt);
|
||||
@ -1038,6 +1041,9 @@ access_ref::inform_access (access_mode mode) const
|
||||
if (TREE_CODE (ref) == SSA_NAME)
|
||||
{
|
||||
gimple *stmt = SSA_NAME_DEF_STMT (ref);
|
||||
if (!stmt)
|
||||
return;
|
||||
|
||||
if (is_gimple_call (stmt))
|
||||
{
|
||||
loc = gimple_location (stmt);
|
||||
@ -1339,7 +1345,7 @@ pointer_query::put_ref (tree ptr, const access_ref &ref, int ostype /* = 1 */)
|
||||
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];
|
||||
access_ref &cache_ref = var_cache->access_refs[cache_idx];
|
||||
if (cache_ref.ref)
|
||||
{
|
||||
gcc_checking_assert (cache_ref.ref == ref.ref);
|
||||
@ -1360,6 +1366,102 @@ pointer_query::flush_cache ()
|
||||
var_cache->access_refs.release ();
|
||||
}
|
||||
|
||||
/* Dump statistics and, optionally, cache contents to DUMP_FILE. */
|
||||
|
||||
void
|
||||
pointer_query::dump (FILE *dump_file, bool contents /* = false */)
|
||||
{
|
||||
unsigned nused = 0, nrefs = 0;
|
||||
unsigned nidxs = var_cache->indices.length ();
|
||||
for (unsigned i = 0; i != nidxs; ++i)
|
||||
{
|
||||
unsigned ari = var_cache->indices[i];
|
||||
if (!ari)
|
||||
continue;
|
||||
|
||||
++nused;
|
||||
|
||||
const access_ref &aref = var_cache->access_refs[ari];
|
||||
if (!aref.ref)
|
||||
continue;
|
||||
|
||||
++nrefs;
|
||||
}
|
||||
|
||||
fprintf (dump_file, "pointer_query counters:\n"
|
||||
" index cache size: %u\n"
|
||||
" index entries: %u\n"
|
||||
" access cache size: %u\n"
|
||||
" access entries: %u\n"
|
||||
" hits: %u\n"
|
||||
" misses: %u\n"
|
||||
" failures: %u\n"
|
||||
" max_depth: %u\n",
|
||||
nidxs, nused,
|
||||
var_cache->access_refs.length (), nrefs,
|
||||
hits, misses, failures, max_depth);
|
||||
|
||||
if (!contents || !nidxs)
|
||||
return;
|
||||
|
||||
fputs ("\npointer_query cache contents:\n", dump_file);
|
||||
|
||||
for (unsigned i = 0; i != nidxs; ++i)
|
||||
{
|
||||
unsigned ari = var_cache->indices[i];
|
||||
if (!ari)
|
||||
continue;
|
||||
|
||||
const access_ref &aref = var_cache->access_refs[ari];
|
||||
if (!aref.ref)
|
||||
continue;
|
||||
|
||||
/* The level-1 cache index corresponds to the SSA_NAME_VERSION
|
||||
shifted left by one and ORed with the Object Size Type in
|
||||
the lowest bit. Print the two separately. */
|
||||
unsigned ver = i >> 1;
|
||||
unsigned ost = i & 1;
|
||||
|
||||
fprintf (dump_file, " %u.%u[%u]: ", ver, ost, ari);
|
||||
if (tree name = ssa_name (ver))
|
||||
{
|
||||
print_generic_expr (dump_file, name);
|
||||
fputs (" = ", dump_file);
|
||||
}
|
||||
else
|
||||
fprintf (dump_file, " _%u = ", ver);
|
||||
|
||||
if (gphi *phi = aref.phi ())
|
||||
{
|
||||
fputs ("PHI <", dump_file);
|
||||
unsigned nargs = gimple_phi_num_args (phi);
|
||||
for (unsigned i = 0; i != nargs; ++i)
|
||||
{
|
||||
tree arg = gimple_phi_arg_def (phi, i);
|
||||
print_generic_expr (dump_file, arg);
|
||||
if (i + 1 < nargs)
|
||||
fputs (", ", dump_file);
|
||||
}
|
||||
fputc ('>', dump_file);
|
||||
}
|
||||
else
|
||||
print_generic_expr (dump_file, aref.ref);
|
||||
|
||||
if (aref.offrng[0] != aref.offrng[1])
|
||||
fprintf (dump_file, " + [%lli, %lli]",
|
||||
(long long) aref.offrng[0].to_shwi (),
|
||||
(long long) aref.offrng[1].to_shwi ());
|
||||
else if (aref.offrng[0] != 0)
|
||||
fprintf (dump_file, " %c %lli",
|
||||
aref.offrng[0] < 0 ? '-' : '+',
|
||||
(long long) aref.offrng[0].to_shwi ());
|
||||
|
||||
fputc ('\n', dump_file);
|
||||
}
|
||||
|
||||
fputc ('\n', dump_file);
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
|
||||
@ -1782,8 +1884,14 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
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. */
|
||||
to and return success.
|
||||
FIXME: BNDRNG is determined by each access and so it doesn't
|
||||
belong in access_ref. Until the design is changed, keep it
|
||||
unchanged here. */
|
||||
const offset_int bndrng[2] = { pref->bndrng[0], pref->bndrng[1] };
|
||||
*pref = *cache_ref;
|
||||
pref->bndrng[0] = bndrng[0];
|
||||
pref->bndrng[1] = bndrng[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1928,13 +2036,18 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
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);
|
||||
if (code == ADDR_EXPR || code == SSA_NAME)
|
||||
{
|
||||
if (!compute_objsize_r (rhs, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
qry->put_ref (ptr, *pref);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (This could also be an assignment from a nonlocal pointer.) Save
|
||||
PTR to mention in diagnostics but otherwise treat it as a pointer
|
||||
|
@ -186,6 +186,9 @@ public:
|
||||
/* Flush the cache. */
|
||||
void flush_cache ();
|
||||
|
||||
/* Dump statistics and optionally cache contents to DUMP_FILE. */
|
||||
void dump (FILE *, bool = false);
|
||||
|
||||
/* A Ranger instance. May be null to use global ranges. */
|
||||
range_query *rvals;
|
||||
/* Cache of SSA_NAMEs. May be null to disable caching. */
|
||||
|
@ -56,7 +56,7 @@ void test_memset_array_cst_range_off (void)
|
||||
T (2, SR ( 1, 2), 4);
|
||||
T (2, SR ( 1, 2), 5); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
|
||||
|
||||
T (2, SR ( 0, 1), 6); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-* } } */
|
||||
T (2, SR ( 0, 1), 6); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" } */
|
||||
T (2, UR ( 1, 2), 7); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
|
||||
T (7, UR (-7, 0), 7);
|
||||
T (7, UR (-7, 0), 9); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
|
||||
@ -134,7 +134,7 @@ void test_memcpy_array_cst_range_off (const void *s)
|
||||
T (2, SR ( 1, 2), 4);
|
||||
T (2, SR ( 1, 2), 5); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
|
||||
|
||||
T (2, SR ( 0, 1), 6); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-* } } */
|
||||
T (2, SR ( 0, 1), 6); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" } */
|
||||
T (2, UR ( 1, 2), 7); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
|
||||
T (7, UR (-7, 0), 7);
|
||||
T (7, UR (-7, 0), 9); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
|
||||
@ -211,7 +211,7 @@ void test_strcpy_array_cst_range_off (void)
|
||||
T (2, SR ( 1, 2), 3);
|
||||
T (2, SR ( 1, 2), 4); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
|
||||
|
||||
T (2, SR ( 0, 1), 5); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-* } } */
|
||||
T (2, SR ( 0, 1), 5); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" } */
|
||||
T (2, UR ( 1, 2), 6); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
|
||||
T (7, UR (-7, 0), 6);
|
||||
T (7, UR (-7, 0), 8); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
|
||||
@ -277,7 +277,7 @@ void test_strncpy_array_cst_range_off (const char *s)
|
||||
T (2, SR ( 1, 2), 4);
|
||||
T (2, SR ( 1, 2), 5); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
|
||||
|
||||
T (2, SR ( 0, 1), 6); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-* } } */
|
||||
T (2, SR ( 0, 1), 6); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" } */
|
||||
T (2, UR ( 1, 2), 7); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
|
||||
T (7, UR (-7, 0), 7);
|
||||
T (7, UR (-7, 0), 9); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
|
||||
|
@ -25,9 +25,7 @@ void test_memcpy_array_cst_range_off (const void *s)
|
||||
T (d + UR (1, 2), 5);
|
||||
|
||||
T (d + UR (0, 1), 6);
|
||||
/* The warning below should be "writing" but the [0, 1] range
|
||||
is somehow lost and get_range_info() returns VR_VARYING. */
|
||||
T (d + UR (0, 1), 7); /* { dg-warning ".memcpy. writing 7 bytes into a region of size 6 overflows the destination" "pr89428" { xfail *-*-* } } */
|
||||
T (d + UR (0, 1), 7); /* { dg-warning ".memcpy. writing 7 bytes into a region of size 6 overflows the destination" "pr89428" } */
|
||||
T (d + UR (1, 2), 6); /* { dg-warning ".memcpy. writing 6 bytes into a region of size 5 overflows the destination" } */
|
||||
T (d + UR (1, 2), 7); /* { dg-warning "writing 7 bytes into a region of size 5 " } */
|
||||
|
||||
@ -66,7 +64,7 @@ void test_memset_array_unsigned_off (void)
|
||||
T (d + UR (1, 2), 5);
|
||||
|
||||
T (d + UR (0, 1), 6);
|
||||
T (d + UR (0, 1), 7); /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" "pr89428" { xfail *-*-* } } */
|
||||
T (d + UR (0, 1), 7); /* { dg-warning ".memset. writing 7 bytes into a region of size 6 overflows the destination" "pr89428" } */
|
||||
T (d + UR (1, 2), 6); /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" } */
|
||||
T (d + UR (1, 2), 7); /* { dg-warning "writing 7 bytes into a region of size 5 " } */
|
||||
|
||||
|
@ -161,8 +161,11 @@ void warn_memset_reversed_range (void)
|
||||
|
||||
/* Since the offset is excessive, either starting before &a11[0]
|
||||
ot just past &a[11], the region size in the warning should
|
||||
probably be zero, but accept other sizes too. */
|
||||
T1 (p, SAR (INT_MIN, -11), n11); // { dg-warning "writing 11 or more bytes into a region of size \\d+" }
|
||||
probably be zero, but accept other sizes too.
|
||||
|
||||
The problem isn't detected anymore because the offset is in
|
||||
the anti-range ~[INT_MIN, -11] which isn't handled. */
|
||||
T1 (p, SAR (INT_MIN, -11), n11); // { dg-warning "writing 11 or more bytes into a region of size \\d+" "" { xfail *-*-* } }
|
||||
|
||||
/* The following are represented as ordinary ranges with reversed bounds
|
||||
and those are handled. */
|
||||
@ -170,7 +173,7 @@ void warn_memset_reversed_range (void)
|
||||
T1 (p, SAR (INT_MIN, 1), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" }
|
||||
T1 (p, SAR (INT_MIN, 0), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" }
|
||||
/* Also represented as a true anti-range. */
|
||||
T1 (p, SAR ( -12, -11), n11); // { dg-warning "writing 11 or more bytes into a region of size \\d+" }
|
||||
T1 (p, SAR ( -12, -11), n11); // { dg-warning "writing 11 or more bytes into a region of size \\d+" "" { xfail *-*-* } }
|
||||
T1 (p, SAR ( -12, -1), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" }
|
||||
T1 (p, SAR ( -11, 0), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" }
|
||||
T1 (p, SAR ( -11, 11), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" }
|
||||
|
35
gcc/testsuite/gcc.dg/Wstringop-overflow-73.c
Normal file
35
gcc/testsuite/gcc.dg/Wstringop-overflow-73.c
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
{ dg-do compile }
|
||||
{ dg-options "-Wall" } */
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
int memcmp (const void*, const void*, size_t);
|
||||
int strncmp (const char*, const char*, size_t);
|
||||
char* stpncpy (char*, const char*, size_t);
|
||||
char* strncpy (char*, const char*, size_t);
|
||||
|
||||
extern char a4[4], b5[5];
|
||||
|
||||
struct A { char a4[4]; };
|
||||
|
||||
extern volatile int i;
|
||||
extern void* volatile ptr;
|
||||
|
||||
void test_stpncpy (struct A *p)
|
||||
{
|
||||
ptr = stpncpy (a4, b5, 4);
|
||||
ptr = stpncpy (a4, b5, 5); // { dg-warning "writing 5 bytes" }
|
||||
|
||||
ptr = stpncpy (p->a4, b5, 4);
|
||||
ptr = stpncpy (p->a4, b5, 5); // { dg-warning "writing 5 bytes" }
|
||||
}
|
||||
|
||||
void test_strncpy (struct A *p)
|
||||
{
|
||||
ptr = strncpy (a4, b5, 4);
|
||||
ptr = strncpy (a4, b5, 5); // { dg-warning "writing 5 bytes" }
|
||||
|
||||
ptr = strncpy (p->a4, b5, 4);
|
||||
ptr = strncpy (p->a4, b5, 5); // { dg-warning "writing 5 bytes" }
|
||||
}
|
@ -200,61 +200,29 @@ static bool handle_assign (gimple_stmt_iterator *, tree, bool *,
|
||||
/* Sets MINMAX to either the constant value or the range VAL is in
|
||||
and returns either the constant value or VAL on success or null
|
||||
when the range couldn't be determined. Uses RVALS when nonnull
|
||||
to determine the range, otherwise uses global range info. */
|
||||
to determine the range, otherwise uses CFUN or global range info,
|
||||
whichever is nonnull. */
|
||||
|
||||
tree
|
||||
get_range (tree val, gimple *stmt, wide_int minmax[2],
|
||||
range_query *rvals /* = NULL */)
|
||||
{
|
||||
if (TREE_CODE (val) == INTEGER_CST)
|
||||
{
|
||||
minmax[0] = minmax[1] = wi::to_wide (val);
|
||||
return val;
|
||||
}
|
||||
|
||||
if (TREE_CODE (val) != SSA_NAME)
|
||||
return NULL_TREE;
|
||||
if (!rvals)
|
||||
rvals = get_range_query (cfun);
|
||||
|
||||
value_range vr;
|
||||
if (rvals && stmt)
|
||||
{
|
||||
if (!rvals->range_of_expr (vr, val, stmt))
|
||||
return NULL_TREE;
|
||||
value_range_kind rng = vr.kind ();
|
||||
if (rng != VR_RANGE)
|
||||
return NULL_TREE;
|
||||
if (!rvals->range_of_expr (vr, val, stmt))
|
||||
return NULL_TREE;
|
||||
|
||||
value_range_kind rng = vr.kind ();
|
||||
if (rng == VR_RANGE)
|
||||
{
|
||||
/* Only handle straight ranges. */
|
||||
minmax[0] = wi::to_wide (vr.min ());
|
||||
minmax[1] = wi::to_wide (vr.max ());
|
||||
return val;
|
||||
}
|
||||
|
||||
// ?? This entire function should use get_range_query or get_global_range_query (),
|
||||
// instead of doing something different for RVALS and global ranges.
|
||||
|
||||
if (!get_global_range_query ()->range_of_expr (vr, val) || vr.undefined_p ())
|
||||
return NULL_TREE;
|
||||
|
||||
minmax[0] = wi::to_wide (vr.min ());
|
||||
minmax[1] = wi::to_wide (vr.max ());
|
||||
value_range_kind rng = vr.kind ();
|
||||
if (rng == VR_RANGE)
|
||||
/* This may be an inverted range whose MINMAX[1] < MINMAX[0]. */
|
||||
return val;
|
||||
|
||||
if (rng == VR_ANTI_RANGE)
|
||||
{
|
||||
/* An anti-range is the same as an ordinary range with inverted
|
||||
bounds (where MINMAX[1] < MINMAX[0] is true) that may result
|
||||
from the conversion of a signed anti-range to unsigned. */
|
||||
wide_int tmp = minmax[0];
|
||||
minmax[0] = minmax[1] + 1;
|
||||
minmax[1] = wi::sub (tmp, 1);
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Do not handle anti-ranges and instead make use of the on-demand
|
||||
VRP if/when it becomes available (hopefully in GCC 11). */
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
@ -943,8 +911,8 @@ dump_strlen_info (FILE *fp, gimple *stmt, range_query *rvals)
|
||||
else
|
||||
{
|
||||
value_range vr;
|
||||
get_global_range_query ()->range_of_expr (vr,
|
||||
si->nonzero_chars);
|
||||
get_range_query (cfun)
|
||||
->range_of_expr (vr, si->nonzero_chars);
|
||||
rng = vr.kind ();
|
||||
if (!vr.undefined_p ())
|
||||
{
|
||||
@ -5820,27 +5788,7 @@ printf_strlen_execute (function *fun, bool warn_only)
|
||||
walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
|
||||
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
unsigned nused = 0;
|
||||
unsigned nidxs = walker.ptr_qry.var_cache->indices.length ();
|
||||
for (unsigned i = 0; i != nidxs; ++i)
|
||||
if (walker.ptr_qry.var_cache->indices[i])
|
||||
++nused;
|
||||
|
||||
fprintf (dump_file, "pointer_query counters\n"
|
||||
" index cache size: %u\n"
|
||||
" utilization: %u%%\n"
|
||||
" access cache size: %u\n"
|
||||
" hits: %u\n"
|
||||
" misses: %u\n"
|
||||
" failures: %u\n"
|
||||
" max_depth: %u\n",
|
||||
nidxs,
|
||||
nidxs == 0 ? 0 : (nused * 100) / nidxs,
|
||||
walker.ptr_qry.var_cache->access_refs.length (),
|
||||
walker.ptr_qry.hits, walker.ptr_qry.misses,
|
||||
walker.ptr_qry.failures, walker.ptr_qry.max_depth);
|
||||
}
|
||||
walker.ptr_qry.dump (dump_file);
|
||||
|
||||
ssa_ver_to_stridx.release ();
|
||||
strinfo_pool.release ();
|
||||
|
Loading…
Reference in New Issue
Block a user