mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-12-19 00:55:13 +08:00
Make full use of context-sensitive ranges in access warnings.
gcc/ChangeLog: * builtins.c (check_strncat_sizes): Pass access_data ctor additional arguments. (expand_builtin_memcmp): Move code to gimple-ssa-warn-access.cc. (expand_builtin_fork_or_exec): Same. * gimple-array-bounds.cc (array_bounds_checker::check_mem_ref): Pass compute_objsize additional arguments. (inbounds_memaccess_p): Same. (array_bounds_checker::check_array_bounds): Add an assert. Stash statement in a member. (check_array_bounds_dom_walker::before_dom_children): Same. * gimple-array-bounds.h (array_bounds_checker::m_stmt): New member. * gimple-ssa-sprintf.c (get_destination_size): Add an argument. (handle_printf_call): Pass a new argument. * gimple-ssa-warn-access.cc (get_size_range): Add an argument. (check_access): Add an argument and pass it along to callees. (check_read_access): Make a member function. (pass_waccess::check_strcat): Pass access_data ctor additional arguments. (pass_waccess::check_strncat): Same. (pass_waccess::check_stxcpy): Same. (pass_waccess::check_stxncpy): Same. (pass_waccess::check_strncmp): Same. (pass_waccess::check_read_access): Same. (pass_waccess::check_builtin): Same. (pass_waccess::maybe_check_access_sizes): Same. (pass_waccess::maybe_check_dealloc_call): Same. * gimple-ssa-warn-access.h (check_read_access): Declare a new member function. * pointer-query.cc (compute_objsize_r): Add an argument. (gimple_call_return_array): Same. (gimple_call_alloc_size): Same. (access_ref::access_ref): Same. (access_ref::get_ref): Same. (pointer_query::get_ref): Same. (handle_min_max_size): Pass an arguments to callees. (handle_array_ref): Add an argument. (handle_mem_ref): Same. (compute_objsize): Same. * pointer-query.h (struct access_ref): Adjust signatures. (struct access_data): Same. (gimple_call_alloc_size): Add an argument. (gimple_parm_array_size): Same. (compute_objsize): Same. * tree-ssa-strlen.c (strlen_pass::adjust_last_stmt): Pass an additional argument to compute_objsize. (strlen_pass::maybe_warn_overflow): Same. (maybe_diag_stxncpy_trunc): Same. gcc/testsuite/ChangeLog: * gcc.dg/Wstringop-overflow-22.c: Correct typos. * gcc.dg/Wstringop-overflow-81.c: New test. libstdc++-v3/ChangeLog: * testsuite/21_strings/basic_string/capacity/1.cc: Also suppress -Wstringop-overread. * testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc: Same.
This commit is contained in:
parent
88b504b7a8
commit
9a27acc30a
@ -3600,7 +3600,7 @@ check_strncat_sizes (tree exp, tree objsize)
|
||||
/* Try to verify that the destination is big enough for the shortest
|
||||
string. */
|
||||
|
||||
access_data data (exp, access_read_write, maxread, true);
|
||||
access_data data (nullptr, exp, access_read_write, maxread, true);
|
||||
if (!objsize && warn_stringop_overflow)
|
||||
{
|
||||
/* If it hasn't been provided by __strncat_chk, try to determine
|
||||
@ -4260,12 +4260,6 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
|
||||
tree arg2 = CALL_EXPR_ARG (exp, 1);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
|
||||
/* Diagnose calls where the specified length exceeds the size of either
|
||||
object. */
|
||||
if (!check_read_access (exp, arg1, len, 0)
|
||||
|| !check_read_access (exp, arg2, len, 0))
|
||||
return NULL_RTX;
|
||||
|
||||
/* Due to the performance benefit, always inline the calls first
|
||||
when result_eq is false. */
|
||||
rtx result = NULL_RTX;
|
||||
@ -5486,27 +5480,6 @@ expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore)
|
||||
tree id, decl;
|
||||
tree call;
|
||||
|
||||
if (DECL_FUNCTION_CODE (fn) != BUILT_IN_FORK)
|
||||
{
|
||||
tree path = CALL_EXPR_ARG (exp, 0);
|
||||
/* Detect unterminated path. */
|
||||
if (!check_read_access (exp, path))
|
||||
return NULL_RTX;
|
||||
|
||||
/* Also detect unterminated first argument. */
|
||||
switch (DECL_FUNCTION_CODE (fn))
|
||||
{
|
||||
case BUILT_IN_EXECL:
|
||||
case BUILT_IN_EXECLE:
|
||||
case BUILT_IN_EXECLP:
|
||||
if (!check_read_access (exp, path))
|
||||
return NULL_RTX;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* If we are not profiling, just call the function. */
|
||||
if (!profile_arc_flag)
|
||||
return NULL_RTX;
|
||||
|
@ -426,7 +426,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
|
||||
axssize = wi::to_offset (access_size);
|
||||
|
||||
access_ref aref;
|
||||
if (!compute_objsize (ref, 0, &aref, ranges))
|
||||
if (!compute_objsize (ref, m_stmt, 0, &aref, ranges))
|
||||
return false;
|
||||
|
||||
if (aref.offset_in_range (axssize))
|
||||
@ -667,7 +667,7 @@ array_bounds_checker::check_addr_expr (location_t location, tree t,
|
||||
problems discussed in pr98266 and pr97595. */
|
||||
|
||||
static bool
|
||||
inbounds_memaccess_p (tree t)
|
||||
inbounds_memaccess_p (tree t, gimple *stmt)
|
||||
{
|
||||
if (TREE_CODE (t) != COMPONENT_REF)
|
||||
return false;
|
||||
@ -686,7 +686,7 @@ inbounds_memaccess_p (tree t)
|
||||
allocated). */
|
||||
access_ref aref; // unused
|
||||
tree refop = TREE_OPERAND (mref, 0);
|
||||
tree refsize = compute_objsize (refop, 1, &aref);
|
||||
tree refsize = compute_objsize (refop, stmt, 1, &aref);
|
||||
if (!refsize || TREE_CODE (refsize) != INTEGER_CST)
|
||||
return false;
|
||||
|
||||
@ -724,6 +724,7 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
|
||||
{
|
||||
tree t = *tp;
|
||||
struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
|
||||
|
||||
location_t location;
|
||||
|
||||
if (EXPR_HAS_LOCATION (t))
|
||||
@ -735,6 +736,8 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
|
||||
|
||||
bool warned = false;
|
||||
array_bounds_checker *checker = (array_bounds_checker *) wi->info;
|
||||
gcc_assert (checker->m_stmt == wi->stmt);
|
||||
|
||||
if (TREE_CODE (t) == ARRAY_REF)
|
||||
warned = checker->check_array_ref (location, t, wi->stmt,
|
||||
false/*ignore_off_by_one*/);
|
||||
@ -746,7 +749,7 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
|
||||
checker->check_addr_expr (location, t, wi->stmt);
|
||||
*walk_subtree = false;
|
||||
}
|
||||
else if (inbounds_memaccess_p (t))
|
||||
else if (inbounds_memaccess_p (t, wi->stmt))
|
||||
/* Hack: Skip MEM_REF checks in accesses to a member of a base class
|
||||
at an offset that's within the bounds of the enclosing object.
|
||||
See pr98266 and pr97595. */
|
||||
@ -794,14 +797,13 @@ check_array_bounds_dom_walker::before_dom_children (basic_block bb)
|
||||
for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
|
||||
{
|
||||
gimple *stmt = gsi_stmt (si);
|
||||
struct walk_stmt_info wi;
|
||||
if (!gimple_has_location (stmt)
|
||||
|| is_gimple_debug (stmt))
|
||||
continue;
|
||||
|
||||
memset (&wi, 0, sizeof (wi));
|
||||
|
||||
struct walk_stmt_info wi{ };
|
||||
wi.info = checker;
|
||||
checker->m_stmt = stmt;
|
||||
|
||||
walk_gimple_op (stmt, array_bounds_checker::check_array_bounds, &wi);
|
||||
}
|
||||
|
@ -36,8 +36,12 @@ private:
|
||||
void check_addr_expr (location_t, tree, gimple *);
|
||||
const value_range *get_value_range (const_tree op, gimple *);
|
||||
|
||||
/* Current function. */
|
||||
struct function *fun;
|
||||
/* Ranger instance. */
|
||||
range_query *ranges;
|
||||
/* Current statement. */
|
||||
gimple *m_stmt;
|
||||
};
|
||||
|
||||
#endif // GCC_GIMPLE_ARRAY_BOUNDS_H
|
||||
|
@ -4030,11 +4030,11 @@ compute_format_length (call_info &info, format_result *res, range_query *query)
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Return the size of the object referenced by the expression DEST if
|
||||
available, or the maximum possible size otherwise. */
|
||||
/* Return the size of the object referenced by the expression DEST in
|
||||
statement STMT, if available, or the maximum possible size otherwise. */
|
||||
|
||||
static unsigned HOST_WIDE_INT
|
||||
get_destination_size (tree dest, pointer_query &ptr_qry)
|
||||
get_destination_size (tree dest, gimple *stmt, pointer_query &ptr_qry)
|
||||
{
|
||||
/* When there is no destination return the maximum. */
|
||||
if (!dest)
|
||||
@ -4042,7 +4042,7 @@ get_destination_size (tree dest, pointer_query &ptr_qry)
|
||||
|
||||
/* Use compute_objsize to determine the size of the destination object. */
|
||||
access_ref aref;
|
||||
if (!ptr_qry.get_ref (dest, &aref))
|
||||
if (!ptr_qry.get_ref (dest, stmt, &aref))
|
||||
return HOST_WIDE_INT_MAX;
|
||||
|
||||
offset_int remsize = aref.size_remaining ();
|
||||
@ -4516,7 +4516,7 @@ handle_printf_call (gimple_stmt_iterator *gsi, pointer_query &ptr_qry)
|
||||
/* 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, ptr_qry);
|
||||
dstsize = get_destination_size (dstptr, info.callstmt, ptr_qry);
|
||||
}
|
||||
else if (tree size = gimple_call_arg (info.callstmt, idx_dstsize))
|
||||
{
|
||||
|
@ -1190,11 +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 (range_query *query, tree bound, tree range[2],
|
||||
get_size_range (range_query *query, tree bound, gimple *stmt, tree range[2],
|
||||
const offset_int bndrng[2])
|
||||
{
|
||||
if (bound)
|
||||
get_size_range (query, bound, NULL, range);
|
||||
get_size_range (query, bound, stmt, range);
|
||||
|
||||
if (!bndrng || (bndrng[0] == 0 && bndrng[1] == HOST_WIDE_INT_M1U))
|
||||
return;
|
||||
@ -1251,7 +1251,8 @@ template <class GimpleOrTree>
|
||||
static bool
|
||||
check_access (GimpleOrTree exp, tree dstwrite,
|
||||
tree maxread, tree srcstr, tree dstsize,
|
||||
access_mode mode, const access_data *pad /* = NULL */)
|
||||
access_mode mode, const access_data *pad,
|
||||
range_query *rvals)
|
||||
{
|
||||
/* The size of the largest object is half the address space, or
|
||||
PTRDIFF_MAX. (This is way too permissive.) */
|
||||
@ -1338,7 +1339,8 @@ 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 (NULL, dstwrite, range, pad ? pad->dst.bndrng : NULL);
|
||||
gimple *stmt = pad ? pad->stmt : nullptr;
|
||||
get_size_range (rvals, dstwrite, stmt, 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
|
||||
@ -1432,7 +1434,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 (NULL, maxread, range, pad ? pad->src.bndrng : NULL);
|
||||
get_size_range (rvals, maxread, stmt, range, pad ? pad->src.bndrng : NULL);
|
||||
|
||||
location_t loc = get_location (exp);
|
||||
tree size = dstsize;
|
||||
@ -1479,7 +1481,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 (NULL, maxread, range, pad ? pad->src.bndrng : NULL);
|
||||
get_size_range (rvals, maxread, stmt, 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]);
|
||||
@ -1512,13 +1514,14 @@ check_access (GimpleOrTree exp, tree dstwrite,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
static bool
|
||||
check_access (gimple *stmt, tree dstwrite,
|
||||
tree maxread, tree srcstr, tree dstsize,
|
||||
access_mode mode, const access_data *pad /* = NULL */)
|
||||
access_mode mode, const access_data *pad,
|
||||
range_query *rvals)
|
||||
{
|
||||
return check_access<gimple *>(stmt, dstwrite, maxread, srcstr, dstsize,
|
||||
mode, pad);
|
||||
return check_access<gimple *> (stmt, dstwrite, maxread, srcstr, dstsize,
|
||||
mode, pad, rvals);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1526,45 +1529,8 @@ check_access (tree expr, tree dstwrite,
|
||||
tree maxread, tree srcstr, tree dstsize,
|
||||
access_mode mode, const access_data *pad /* = NULL */)
|
||||
{
|
||||
return check_access<tree>(expr, dstwrite, maxread, srcstr, dstsize,
|
||||
mode, pad);
|
||||
}
|
||||
|
||||
/* A convenience wrapper for check_access above to check access
|
||||
by a read-only function like puts. */
|
||||
|
||||
template <class GimpleOrTree>
|
||||
static bool
|
||||
check_read_access (GimpleOrTree expr, tree src, tree bound, int ost)
|
||||
{
|
||||
if (!warn_stringop_overread)
|
||||
return true;
|
||||
|
||||
if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound)))
|
||||
bound = fold_convert (size_type_node, bound);
|
||||
|
||||
tree fndecl = get_callee_fndecl (expr);
|
||||
maybe_warn_nonstring_arg (fndecl, expr);
|
||||
|
||||
access_data data (expr, access_read_only, NULL_TREE, false, bound, true);
|
||||
compute_objsize (src, ost, &data.src);
|
||||
return check_access (expr, /*dstwrite=*/ NULL_TREE, /*maxread=*/ bound,
|
||||
/*srcstr=*/ src, /*dstsize=*/ NULL_TREE, data.mode,
|
||||
&data);
|
||||
}
|
||||
|
||||
bool
|
||||
check_read_access (gimple *stmt, tree src, tree bound /* = NULL_TREE */,
|
||||
int ost /* = 1 */)
|
||||
{
|
||||
return check_read_access<gimple *>(stmt, src, bound, ost);
|
||||
}
|
||||
|
||||
bool
|
||||
check_read_access (tree expr, tree src, tree bound /* = NULL_TREE */,
|
||||
int ost /* = 1 */)
|
||||
{
|
||||
return check_read_access<tree>(expr, src, bound, ost);
|
||||
return check_access<tree> (expr, dstwrite, maxread, srcstr, dstsize,
|
||||
mode, pad, nullptr);
|
||||
}
|
||||
|
||||
/* Return true if STMT is a call to an allocation function. Unless
|
||||
@ -2133,6 +2099,7 @@ private:
|
||||
void check_stxncpy (gcall *);
|
||||
void check_strncmp (gcall *);
|
||||
void check_memop_access (gimple *, tree, tree, tree);
|
||||
void check_read_access (gimple *, tree, tree = NULL_TREE, int = 1);
|
||||
|
||||
void maybe_check_dealloc_call (gcall *);
|
||||
void maybe_check_access_sizes (rdwr_map *, tree, tree, gimple *);
|
||||
@ -2428,14 +2395,14 @@ pass_waccess::check_strcat (gcall *stmt)
|
||||
the destination to which the SRC string is being appended so
|
||||
just diagnose cases when the souce string is longer than
|
||||
the destination object. */
|
||||
access_data data (stmt, access_read_write, NULL_TREE, true,
|
||||
NULL_TREE, true);
|
||||
access_data data (m_ptr_qry.rvals, 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, &m_ptr_qry);
|
||||
tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
|
||||
compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
|
||||
tree destsize = compute_objsize (dest, stmt, ost, &data.dst, &m_ptr_qry);
|
||||
|
||||
check_access (stmt, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
|
||||
src, destsize, data.mode, &data);
|
||||
src, destsize, data.mode, &data, m_ptr_qry.rvals);
|
||||
}
|
||||
|
||||
/* Check a call STMT to strcat() for overflow and warn if it does. */
|
||||
@ -2469,12 +2436,12 @@ pass_waccess::check_strncat (gcall *stmt)
|
||||
maxlen = lendata.maxbound;
|
||||
}
|
||||
|
||||
access_data data (stmt, access_read_write);
|
||||
access_data data (m_ptr_qry.rvals, stmt, access_read_write);
|
||||
/* 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. */
|
||||
const int ost = warn_stringop_overflow - 1;
|
||||
tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
|
||||
tree destsize = compute_objsize (dest, stmt, ost, &data.dst, &m_ptr_qry);
|
||||
|
||||
/* Add one for the terminating nul. */
|
||||
tree srclen = (maxlen
|
||||
@ -2503,7 +2470,7 @@ pass_waccess::check_strncat (gcall *stmt)
|
||||
srclen = maxread;
|
||||
|
||||
check_access (stmt, /*dstwrite=*/NULL_TREE, maxread, srclen,
|
||||
destsize, data.mode, &data);
|
||||
destsize, data.mode, &data, m_ptr_qry.rvals);
|
||||
}
|
||||
|
||||
/* Check a call STMT to stpcpy() or strcpy() for overflow and warn
|
||||
@ -2527,14 +2494,14 @@ pass_waccess::check_stxcpy (gcall *stmt)
|
||||
|
||||
if (warn_stringop_overflow)
|
||||
{
|
||||
access_data data (stmt, access_read_write, NULL_TREE, true,
|
||||
NULL_TREE, true);
|
||||
access_data data (m_ptr_qry.rvals, 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, &m_ptr_qry);
|
||||
tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
|
||||
compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
|
||||
tree dstsize = compute_objsize (dst, stmt, ost, &data.dst, &m_ptr_qry);
|
||||
check_access (stmt, /*dstwrite=*/ NULL_TREE,
|
||||
/*maxread=*/ NULL_TREE, /*srcstr=*/ src,
|
||||
dstsize, data.mode, &data);
|
||||
dstsize, data.mode, &data, m_ptr_qry.rvals);
|
||||
}
|
||||
|
||||
/* Check to see if the argument was declared attribute nonstring
|
||||
@ -2558,13 +2525,14 @@ pass_waccess::check_stxncpy (gcall *stmt)
|
||||
/* The number of bytes to write (not the maximum). */
|
||||
tree len = call_arg (stmt, 2);
|
||||
|
||||
access_data data (stmt, access_read_write, len, true, len, true);
|
||||
access_data data (m_ptr_qry.rvals, 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, &m_ptr_qry);
|
||||
tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
|
||||
compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
|
||||
tree dstsize = compute_objsize (dst, stmt, ost, &data.dst, &m_ptr_qry);
|
||||
|
||||
check_access (stmt, /*dstwrite=*/len,
|
||||
/*maxread=*/len, src, dstsize, data.mode, &data);
|
||||
check_access (stmt, /*dstwrite=*/len, /*maxread=*/len, src, dstsize,
|
||||
data.mode, &data, m_ptr_qry.rvals);
|
||||
}
|
||||
|
||||
/* Check a call STMT to stpncpy() or strncpy() for overflow and warn
|
||||
@ -2597,6 +2565,11 @@ pass_waccess::check_strncmp (gcall *stmt)
|
||||
tree len1 = c_strlen (arg1, 1, &lendata1);
|
||||
tree len2 = c_strlen (arg2, 1, &lendata2);
|
||||
|
||||
if (len1 && TREE_CODE (len1) != INTEGER_CST)
|
||||
len1 = NULL_TREE;
|
||||
if (len2 && TREE_CODE (len2) != INTEGER_CST)
|
||||
len2 = NULL_TREE;
|
||||
|
||||
if (len1 && len2)
|
||||
/* If the length of both arguments was computed they must both be
|
||||
nul-terminated and no further checking is necessary regardless
|
||||
@ -2609,13 +2582,15 @@ pass_waccess::check_strncmp (gcall *stmt)
|
||||
if (maybe_warn_nonstring_arg (get_callee_fndecl (stmt), stmt))
|
||||
return;
|
||||
|
||||
access_data adata1 (stmt, access_read_only, NULL_TREE, false, bound, true);
|
||||
access_data adata2 (stmt, access_read_only, NULL_TREE, false, bound, true);
|
||||
access_data adata1 (m_ptr_qry.rvals, stmt, access_read_only, NULL_TREE, false,
|
||||
bound, true);
|
||||
access_data adata2 (m_ptr_qry.rvals, stmt, access_read_only, NULL_TREE, false,
|
||||
bound, true);
|
||||
|
||||
/* 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 (m_ptr_qry.rvals, bound, bndrng, adata1.src.bndrng);
|
||||
get_size_range (m_ptr_qry.rvals, bound, stmt, bndrng, adata1.src.bndrng);
|
||||
if (!bndrng[0] || integer_zerop (bndrng[0]))
|
||||
return;
|
||||
|
||||
@ -2626,8 +2601,8 @@ pass_waccess::check_strncmp (gcall *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, &m_ptr_qry)
|
||||
|| !compute_objsize (arg2, 1, &adata2.src, &m_ptr_qry))
|
||||
if (!compute_objsize (arg1, stmt, 1, &adata1.src, &m_ptr_qry)
|
||||
|| !compute_objsize (arg2, stmt, 1, &adata2.src, &m_ptr_qry))
|
||||
return;
|
||||
|
||||
/* Compute the size of the remaining space in each array after
|
||||
@ -2675,15 +2650,41 @@ pass_waccess::check_memop_access (gimple *stmt, tree dest, tree src, tree size)
|
||||
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);
|
||||
access_data data (m_ptr_qry.rvals, 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);
|
||||
= src ? compute_objsize (src, stmt, 0, &data.src, &m_ptr_qry) : NULL_TREE;
|
||||
tree dstsize = compute_objsize (dest, stmt, 0, &data.dst, &m_ptr_qry);
|
||||
|
||||
check_access (stmt, size, /*maxread=*/NULL_TREE,
|
||||
srcsize, dstsize, data.mode, &data);
|
||||
check_access (stmt, size, /*maxread=*/NULL_TREE, srcsize, dstsize,
|
||||
data.mode, &data, m_ptr_qry.rvals);
|
||||
}
|
||||
|
||||
/* A convenience wrapper for check_access to check access by a read-only
|
||||
function like puts or strcmp. */
|
||||
|
||||
void
|
||||
pass_waccess::check_read_access (gimple *stmt, tree src,
|
||||
tree bound /* = NULL_TREE */,
|
||||
int ost /* = 1 */)
|
||||
{
|
||||
if (!warn_stringop_overread)
|
||||
return;
|
||||
|
||||
if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound)))
|
||||
bound = fold_convert (size_type_node, bound);
|
||||
|
||||
tree fndecl = get_callee_fndecl (stmt);
|
||||
maybe_warn_nonstring_arg (fndecl, stmt);
|
||||
|
||||
access_data data (m_ptr_qry.rvals, stmt, access_read_only, NULL_TREE,
|
||||
false, bound, true);
|
||||
compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
|
||||
check_access (stmt, /*dstwrite=*/ NULL_TREE, /*maxread=*/ bound,
|
||||
/*srcstr=*/ src, /*dstsize=*/ NULL_TREE, data.mode,
|
||||
&data, m_ptr_qry.rvals);
|
||||
}
|
||||
|
||||
|
||||
/* Check a call STMT to an atomic or sync built-in. */
|
||||
|
||||
bool
|
||||
@ -2783,6 +2784,15 @@ pass_waccess::check_builtin (gcall *stmt)
|
||||
check_alloca (stmt);
|
||||
return true;
|
||||
|
||||
case BUILT_IN_EXECL:
|
||||
case BUILT_IN_EXECLE:
|
||||
case BUILT_IN_EXECLP:
|
||||
case BUILT_IN_EXECV:
|
||||
case BUILT_IN_EXECVE:
|
||||
case BUILT_IN_EXECVP:
|
||||
check_read_access (stmt, call_arg (stmt, 0));
|
||||
return true;
|
||||
|
||||
case BUILT_IN_GETTEXT:
|
||||
case BUILT_IN_PUTS:
|
||||
case BUILT_IN_PUTS_UNLOCKED:
|
||||
@ -2805,8 +2815,12 @@ pass_waccess::check_builtin (gcall *stmt)
|
||||
|
||||
case BUILT_IN_STRNDUP:
|
||||
case BUILT_IN_STRNLEN:
|
||||
check_read_access (stmt, call_arg (stmt, 0), call_arg (stmt, 1));
|
||||
return true;
|
||||
{
|
||||
tree str = call_arg (stmt, 0);
|
||||
tree len = call_arg (stmt, 1);
|
||||
check_read_access (stmt, str, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
case BUILT_IN_STRCAT:
|
||||
check_strcat (stmt);
|
||||
@ -2985,7 +2999,7 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
|
||||
/* 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 (m_ptr_qry.rvals, access_size, NULL, sizrng, 1))
|
||||
if (get_size_range (m_ptr_qry.rvals, access_size, stmt, sizrng, 1))
|
||||
{
|
||||
char *s0 = print_generic_expr_to_str (sizrng[0]);
|
||||
if (tree_int_cst_equal (sizrng[0], sizrng[1]))
|
||||
@ -3113,11 +3127,11 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
|
||||
}
|
||||
}
|
||||
|
||||
access_data data (ptr, access.second.mode, NULL_TREE, false,
|
||||
NULL_TREE, false);
|
||||
access_data data (m_ptr_qry.rvals, stmt, access.second.mode,
|
||||
NULL_TREE, false, NULL_TREE, false);
|
||||
access_ref* const pobj = (access.second.mode == access_write_only
|
||||
? &data.dst : &data.src);
|
||||
tree objsize = compute_objsize (ptr, 1, pobj, &m_ptr_qry);
|
||||
tree objsize = compute_objsize (ptr, stmt, 1, pobj, &m_ptr_qry);
|
||||
|
||||
/* The size of the destination or source object. */
|
||||
tree dstsize = NULL_TREE, srcsize = NULL_TREE;
|
||||
@ -3149,7 +3163,7 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
|
||||
if (mode == access_deferred)
|
||||
mode = TYPE_READONLY (argtype) ? access_read_only : access_read_write;
|
||||
check_access (stmt, access_size, /*maxread=*/ NULL_TREE, srcsize,
|
||||
dstsize, mode, &data);
|
||||
dstsize, mode, &data, m_ptr_qry.rvals);
|
||||
|
||||
if (warning_suppressed_p (stmt, OPT_Wstringop_overflow_))
|
||||
opt_warned = OPT_Wstringop_overflow_;
|
||||
@ -3272,7 +3286,7 @@ pass_waccess::maybe_check_dealloc_call (gcall *call)
|
||||
return;
|
||||
|
||||
access_ref aref;
|
||||
if (!compute_objsize (ptr, 0, &aref, &m_ptr_qry))
|
||||
if (!compute_objsize (ptr, call, 0, &aref, &m_ptr_qry))
|
||||
return;
|
||||
|
||||
tree ref = aref.ref;
|
||||
|
@ -45,7 +45,4 @@ class access_data;
|
||||
extern bool check_access (tree, tree, tree, tree, tree, access_mode,
|
||||
const access_data * = NULL);
|
||||
|
||||
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);
|
||||
|
||||
#endif // GCC_GIMPLE_SSA_WARN_ACCESS_H
|
||||
|
@ -43,8 +43,8 @@
|
||||
#include "tree-ssanames.h"
|
||||
#include "target.h"
|
||||
|
||||
static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
|
||||
pointer_query *);
|
||||
static bool compute_objsize_r (tree, gimple *, int, access_ref *,
|
||||
ssa_name_limit_t &, pointer_query *);
|
||||
|
||||
/* Wrapper around the wide_int overload of get_range that accepts
|
||||
offset_int instead. For middle end expressions returns the same
|
||||
@ -115,7 +115,7 @@ get_offset_range (tree x, gimple *stmt, offset_int r[2], range_query *rvals)
|
||||
|
||||
static tree
|
||||
gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
|
||||
range_query *rvals)
|
||||
ssa_name_limit_t &snlim, pointer_query *qry)
|
||||
{
|
||||
/* Clear and set below for the rare function(s) that might return
|
||||
a past-the-end pointer. */
|
||||
@ -191,7 +191,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
|
||||
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);
|
||||
bool off_valid = get_offset_range (off, stmt, offrng, qry->rvals);
|
||||
if (!off_valid || offrng[0] != offrng[1])
|
||||
{
|
||||
/* If the offset is either indeterminate or in some range,
|
||||
@ -199,7 +199,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
|
||||
of the source object. */
|
||||
access_ref aref;
|
||||
tree src = gimple_call_arg (stmt, 1);
|
||||
if (compute_objsize (src, 1, &aref, rvals)
|
||||
if (compute_objsize (src, stmt, 1, &aref, qry)
|
||||
&& aref.sizrng[1] < offrng[1])
|
||||
offrng[1] = aref.sizrng[1];
|
||||
}
|
||||
@ -212,7 +212,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
|
||||
case BUILT_IN_MEMCHR:
|
||||
{
|
||||
tree off = gimple_call_arg (stmt, 2);
|
||||
if (get_offset_range (off, stmt, offrng, rvals))
|
||||
if (get_offset_range (off, stmt, offrng, qry->rvals))
|
||||
offrng[1] -= 1;
|
||||
else
|
||||
offrng[1] = HOST_WIDE_INT_M1U;
|
||||
@ -233,7 +233,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
|
||||
{
|
||||
access_ref aref;
|
||||
tree src = gimple_call_arg (stmt, 1);
|
||||
if (compute_objsize (src, 1, &aref, rvals))
|
||||
if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry))
|
||||
offrng[1] = aref.sizrng[1] - 1;
|
||||
else
|
||||
offrng[1] = HOST_WIDE_INT_M1U;
|
||||
@ -250,7 +250,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
|
||||
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)
|
||||
if (!get_offset_range (off, stmt, offrng, qry->rvals)
|
||||
|| offrng[0] != offrng[1])
|
||||
{
|
||||
/* If the offset is either indeterminate or in some range,
|
||||
@ -258,7 +258,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
|
||||
of the source object. */
|
||||
access_ref aref;
|
||||
tree src = gimple_call_arg (stmt, 1);
|
||||
if (compute_objsize (src, 1, &aref, rvals)
|
||||
if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry)
|
||||
&& aref.sizrng[1] < offrng[1])
|
||||
offrng[1] = aref.sizrng[1];
|
||||
}
|
||||
@ -445,7 +445,7 @@ get_size_range (tree exp, tree range[2], int flags /* = 0 */)
|
||||
|
||||
tree
|
||||
gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
|
||||
range_query * /* = NULL */)
|
||||
range_query *qry /* = NULL */)
|
||||
{
|
||||
if (!stmt || !is_gimple_call (stmt))
|
||||
return NULL_TREE;
|
||||
@ -503,7 +503,7 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
|
||||
{
|
||||
tree r[2];
|
||||
/* Determine the largest valid range size, including zero. */
|
||||
if (!get_size_range (size, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
|
||||
if (!get_size_range (qry, size, stmt, 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);
|
||||
@ -519,7 +519,7 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
|
||||
{
|
||||
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))
|
||||
if (!get_size_range (qry, n, stmt, 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);
|
||||
@ -546,7 +546,7 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
|
||||
Set STATIC_ARRAY if the array parameter has been declared [static].
|
||||
Return the function parameter on success and null otherwise. */
|
||||
|
||||
tree
|
||||
static tree
|
||||
gimple_parm_array_size (tree ptr, wide_int rng[2],
|
||||
bool *static_array /* = NULL */)
|
||||
{
|
||||
@ -596,10 +596,17 @@ gimple_parm_array_size (tree ptr, wide_int rng[2],
|
||||
return var;
|
||||
}
|
||||
|
||||
access_ref::access_ref (tree bound /* = NULL_TREE */,
|
||||
/* Given a statement STMT, set the bounds of the reference to at most
|
||||
as many bytes as BOUND or unknown when null, and at least one when
|
||||
the MINACCESS is true unless BOUND is a constant zero. STMT is
|
||||
used for context to get accurate range info. */
|
||||
|
||||
access_ref::access_ref (range_query *qry /* = nullptr */,
|
||||
tree bound /* = NULL_TREE */,
|
||||
gimple *stmt /* = nullptr */,
|
||||
bool minaccess /* = false */)
|
||||
: ref (), eval ([](tree x){ return x; }), deref (), trail1special (true),
|
||||
base0 (true), parmarray ()
|
||||
: ref (), eval ([](tree x){ return x; }), deref (), trail1special (true),
|
||||
base0 (true), parmarray ()
|
||||
{
|
||||
/* Set to valid. */
|
||||
offrng[0] = offrng[1] = 0;
|
||||
@ -615,7 +622,7 @@ access_ref::access_ref (tree bound /* = NULL_TREE */,
|
||||
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))
|
||||
if (bound && get_size_range (qry, bound, stmt, rng, SR_ALLOW_ZERO))
|
||||
{
|
||||
bndrng[0] = wi::to_offset (rng[0]);
|
||||
bndrng[1] = wi::to_offset (rng[1]);
|
||||
@ -696,7 +703,8 @@ 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_r (arg, ostype, &phi_arg_ref, *psnlim, qry)
|
||||
if (!compute_objsize_r (arg, phi_stmt, ostype, &phi_arg_ref, *psnlim,
|
||||
qry)
|
||||
|| phi_arg_ref.sizrng[0] < 0)
|
||||
/* A PHI with all null pointer arguments. */
|
||||
return NULL_TREE;
|
||||
@ -1312,7 +1320,7 @@ pointer_query::get_ref (tree ptr, int ostype /* = 1 */) const
|
||||
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 */)
|
||||
pointer_query::get_ref (tree ptr, gimple *stmt, access_ref *pref, int ostype /* = 1 */)
|
||||
{
|
||||
const unsigned version
|
||||
= TREE_CODE (ptr) == SSA_NAME ? SSA_NAME_VERSION (ptr) : 0;
|
||||
@ -1335,7 +1343,7 @@ pointer_query::get_ref (tree ptr, access_ref *pref, int ostype /* = 1 */)
|
||||
++misses;
|
||||
}
|
||||
|
||||
if (!compute_objsize (ptr, ostype, pref, this))
|
||||
if (!compute_objsize (ptr, stmt, ostype, pref, this))
|
||||
{
|
||||
++failures;
|
||||
return false;
|
||||
@ -1502,7 +1510,7 @@ static bool
|
||||
handle_min_max_size (tree ptr, int ostype, access_ref *pref,
|
||||
ssa_name_limit_t &snlim, pointer_query *qry)
|
||||
{
|
||||
const gimple *stmt = SSA_NAME_DEF_STMT (ptr);
|
||||
gimple *stmt = SSA_NAME_DEF_STMT (ptr);
|
||||
const tree_code code = gimple_assign_rhs_code (stmt);
|
||||
|
||||
/* In a valid MAX_/MIN_EXPR both operands must refer to the same array.
|
||||
@ -1512,7 +1520,7 @@ handle_min_max_size (tree ptr, int ostype, access_ref *pref,
|
||||
for the expression. */
|
||||
access_ref aref[2] = { *pref, *pref };
|
||||
tree arg1 = gimple_assign_rhs1 (stmt);
|
||||
if (!compute_objsize_r (arg1, ostype, &aref[0], snlim, qry))
|
||||
if (!compute_objsize_r (arg1, stmt, ostype, &aref[0], snlim, qry))
|
||||
{
|
||||
aref[0].base0 = false;
|
||||
aref[0].offrng[0] = aref[0].offrng[1] = 0;
|
||||
@ -1521,7 +1529,7 @@ handle_min_max_size (tree ptr, int ostype, access_ref *pref,
|
||||
}
|
||||
|
||||
tree arg2 = gimple_assign_rhs2 (stmt);
|
||||
if (!compute_objsize_r (arg2, ostype, &aref[1], snlim, qry))
|
||||
if (!compute_objsize_r (arg2, stmt, ostype, &aref[1], snlim, qry))
|
||||
{
|
||||
aref[1].base0 = false;
|
||||
aref[1].offrng[0] = aref[1].offrng[1] = 0;
|
||||
@ -1589,8 +1597,9 @@ handle_min_max_size (tree ptr, int ostype, access_ref *pref,
|
||||
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)
|
||||
handle_array_ref (tree aref, gimple *stmt, bool addr, int ostype,
|
||||
access_ref *pref, ssa_name_limit_t &snlim,
|
||||
pointer_query *qry)
|
||||
{
|
||||
gcc_assert (TREE_CODE (aref) == ARRAY_REF);
|
||||
|
||||
@ -1603,7 +1612,7 @@ handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref,
|
||||
of known bound. */
|
||||
return false;
|
||||
|
||||
if (!compute_objsize_r (arefop, ostype, pref, snlim, qry))
|
||||
if (!compute_objsize_r (arefop, stmt, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
offset_int orng[2];
|
||||
@ -1668,7 +1677,7 @@ handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref,
|
||||
MREF. Return true on success and false on failure. */
|
||||
|
||||
static bool
|
||||
handle_mem_ref (tree mref, int ostype, access_ref *pref,
|
||||
handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref,
|
||||
ssa_name_limit_t &snlim, pointer_query *qry)
|
||||
{
|
||||
gcc_assert (TREE_CODE (mref) == MEM_REF);
|
||||
@ -1690,7 +1699,7 @@ handle_mem_ref (tree mref, int ostype, access_ref *pref,
|
||||
}
|
||||
|
||||
tree mrefop = TREE_OPERAND (mref, 0);
|
||||
if (!compute_objsize_r (mrefop, ostype, pref, snlim, qry))
|
||||
if (!compute_objsize_r (mrefop, stmt, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
offset_int orng[2];
|
||||
@ -1723,7 +1732,7 @@ handle_mem_ref (tree mref, int ostype, access_ref *pref,
|
||||
to influence code generation or optimization. */
|
||||
|
||||
static bool
|
||||
compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
compute_objsize_r (tree ptr, gimple *stmt, int ostype, access_ref *pref,
|
||||
ssa_name_limit_t &snlim, pointer_query *qry)
|
||||
{
|
||||
STRIP_NOPS (ptr);
|
||||
@ -1774,7 +1783,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
if (code == BIT_FIELD_REF)
|
||||
{
|
||||
tree ref = TREE_OPERAND (ptr, 0);
|
||||
if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
|
||||
if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
|
||||
@ -1796,7 +1805,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
/* 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))
|
||||
if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
/* Otherwise, use the size of the enclosing object and add
|
||||
@ -1850,15 +1859,15 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
}
|
||||
|
||||
if (code == ARRAY_REF)
|
||||
return handle_array_ref (ptr, addr, ostype, pref, snlim, qry);
|
||||
return handle_array_ref (ptr, stmt, addr, ostype, pref, snlim, qry);
|
||||
|
||||
if (code == MEM_REF)
|
||||
return handle_mem_ref (ptr, ostype, pref, snlim, qry);
|
||||
return handle_mem_ref (ptr, stmt, ostype, pref, snlim, qry);
|
||||
|
||||
if (code == TARGET_MEM_REF)
|
||||
{
|
||||
tree ref = TREE_OPERAND (ptr, 0);
|
||||
if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
|
||||
if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
/* TODO: Handle remaining operands. Until then, add maximum offset. */
|
||||
@ -1903,7 +1912,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
if (code == POINTER_PLUS_EXPR)
|
||||
{
|
||||
tree ref = TREE_OPERAND (ptr, 0);
|
||||
if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
|
||||
if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
/* Clear DEREF since the offset is being applied to the target
|
||||
@ -1922,7 +1931,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
if (code == VIEW_CONVERT_EXPR)
|
||||
{
|
||||
ptr = TREE_OPERAND (ptr, 0);
|
||||
return compute_objsize_r (ptr, ostype, pref, snlim, qry);
|
||||
return compute_objsize_r (ptr, stmt, ostype, pref, snlim, qry);
|
||||
}
|
||||
|
||||
if (code == SSA_NAME)
|
||||
@ -1951,7 +1960,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
}
|
||||
}
|
||||
|
||||
gimple *stmt = SSA_NAME_DEF_STMT (ptr);
|
||||
stmt = SSA_NAME_DEF_STMT (ptr);
|
||||
if (is_gimple_call (stmt))
|
||||
{
|
||||
/* If STMT is a call to an allocation function get the size
|
||||
@ -1979,9 +1988,9 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
bool past_end;
|
||||
offset_int offrng[2];
|
||||
if (tree ret = gimple_call_return_array (stmt, offrng,
|
||||
&past_end, rvals))
|
||||
&past_end, snlim, qry))
|
||||
{
|
||||
if (!compute_objsize_r (ret, ostype, pref, snlim, qry))
|
||||
if (!compute_objsize_r (ret, stmt, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
/* Cap OFFRNG[1] to at most the remaining size of
|
||||
@ -2076,14 +2085,14 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
if (code == ASSERT_EXPR)
|
||||
{
|
||||
rhs = TREE_OPERAND (rhs, 0);
|
||||
return compute_objsize_r (rhs, ostype, pref, snlim, qry);
|
||||
return compute_objsize_r (rhs, stmt, 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))
|
||||
if (!compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
offset_int orng[2];
|
||||
@ -2099,7 +2108,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
|
||||
if (code == ADDR_EXPR || code == SSA_NAME)
|
||||
{
|
||||
if (!compute_objsize_r (rhs, ostype, pref, snlim, qry))
|
||||
if (!compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
qry->put_ref (ptr, *pref);
|
||||
return true;
|
||||
@ -2128,31 +2137,8 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
||||
instead. */
|
||||
|
||||
tree
|
||||
compute_objsize (tree ptr, int ostype, access_ref *pref,
|
||||
range_query *rvals /* = NULL */)
|
||||
{
|
||||
pointer_query qry;
|
||||
qry.rvals = rvals;
|
||||
|
||||
/* Clear and invalidate in case *PREF is being reused. */
|
||||
pref->offrng[0] = pref->offrng[1] = 0;
|
||||
pref->sizrng[0] = pref->sizrng[1] = -1;
|
||||
|
||||
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)
|
||||
compute_objsize (tree ptr, gimple *stmt, int ostype, access_ref *pref,
|
||||
pointer_query *ptr_qry)
|
||||
{
|
||||
pointer_query qry;
|
||||
if (ptr_qry)
|
||||
@ -2165,7 +2151,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, pointer_query *ptr_qry)
|
||||
pref->sizrng[0] = pref->sizrng[1] = -1;
|
||||
|
||||
ssa_name_limit_t snlim;
|
||||
if (!compute_objsize_r (ptr, ostype, pref, snlim, ptr_qry))
|
||||
if (!compute_objsize_r (ptr, stmt, ostype, pref, snlim, ptr_qry))
|
||||
return NULL_TREE;
|
||||
|
||||
offset_int maxsize = pref->size_remaining ();
|
||||
@ -2174,6 +2160,18 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, pointer_query *ptr_qry)
|
||||
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, gimple *stmt, int ostype, access_ref *pref,
|
||||
range_query *rvals /* = NULL */)
|
||||
{
|
||||
pointer_query qry;
|
||||
qry.rvals = rvals;
|
||||
return compute_objsize (ptr, stmt, ostype, pref, &qry);
|
||||
}
|
||||
|
||||
/* Legacy wrapper around the above. The function should be removed
|
||||
once callers transition to one of the two above. */
|
||||
|
||||
@ -2184,7 +2182,7 @@ compute_objsize (tree ptr, int ostype, tree *pdecl /* = 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);
|
||||
tree size = compute_objsize (ptr, nullptr, ostype, &ref, rvals);
|
||||
if (!size || !ref.base0)
|
||||
return NULL_TREE;
|
||||
|
||||
|
@ -60,18 +60,16 @@ class pointer_query;
|
||||
/* Describes a reference to an object used in an access. */
|
||||
struct access_ref
|
||||
{
|
||||
/* Set the bounds of the reference to at most as many bytes
|
||||
as the first argument or unknown when null, and at least
|
||||
one when the second argument is true unless the first one
|
||||
is a constant zero. */
|
||||
access_ref (tree = NULL_TREE, bool = false);
|
||||
/* Set the bounds of the reference. */
|
||||
access_ref (range_query *query = nullptr, tree = NULL_TREE,
|
||||
gimple * = nullptr, bool = false);
|
||||
|
||||
/* Return the PHI node REF refers to or null if it doesn't. */
|
||||
gphi *phi () const;
|
||||
|
||||
/* Return the object to which REF refers. */
|
||||
tree get_ref (vec<access_ref> *, access_ref * = NULL, int = 1,
|
||||
ssa_name_limit_t * = NULL, pointer_query * = NULL) const;
|
||||
tree get_ref (vec<access_ref> *, access_ref * = nullptr, int = 1,
|
||||
ssa_name_limit_t * = nullptr, pointer_query * = nullptr) const;
|
||||
|
||||
/* Return true if OFFRNG is the constant zero. */
|
||||
bool offset_zero () const
|
||||
@ -85,7 +83,7 @@ struct access_ref
|
||||
|
||||
/* Return the maximum amount of space remaining and if non-null, set
|
||||
argument to the minimum. */
|
||||
offset_int size_remaining (offset_int * = NULL) const;
|
||||
offset_int size_remaining (offset_int * = nullptr) const;
|
||||
|
||||
/* Return true if the offset and object size are in range for SIZE. */
|
||||
bool offset_in_range (const offset_int &) const;
|
||||
@ -172,13 +170,13 @@ public:
|
||||
};
|
||||
|
||||
/* Construct an object with the given Ranger instance and cache. */
|
||||
explicit pointer_query (range_query * = NULL, cache_type * = NULL);
|
||||
explicit pointer_query (range_query * = nullptr, cache_type * = nullptr);
|
||||
|
||||
/* Retrieve the access_ref for a variable from cache if it's there. */
|
||||
const access_ref* get_ref (tree, int = 1) const;
|
||||
|
||||
/* Retrieve the access_ref for a variable from cache or compute it. */
|
||||
bool get_ref (tree, access_ref*, int = 1);
|
||||
bool get_ref (tree, gimple *, access_ref*, int = 1);
|
||||
|
||||
/* Add an access_ref for the SSA_NAME to the cache. */
|
||||
void put_ref (tree, const access_ref&, int = 1);
|
||||
@ -208,19 +206,23 @@ struct access_data
|
||||
{
|
||||
/* Set the access to at most MAXWRITE and MAXREAD bytes, and
|
||||
at least 1 when MINWRITE or MINREAD, respectively, is set. */
|
||||
access_data (gimple *stmt, access_mode mode,
|
||||
access_data (range_query *query, gimple *stmt, access_mode mode,
|
||||
tree maxwrite = NULL_TREE, bool minwrite = false,
|
||||
tree maxread = NULL_TREE, bool minread = false)
|
||||
: stmt (stmt), call (),
|
||||
dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { }
|
||||
dst (query, maxwrite, stmt, minwrite),
|
||||
src (query, maxread, stmt, minread),
|
||||
mode (mode) { }
|
||||
|
||||
/* Set the access to at most MAXWRITE and MAXREAD bytes, and
|
||||
at least 1 when MINWRITE or MINREAD, respectively, is set. */
|
||||
access_data (tree expr, access_mode mode,
|
||||
access_data (range_query *query, tree expr, access_mode mode,
|
||||
tree maxwrite = NULL_TREE, bool minwrite = false,
|
||||
tree maxread = NULL_TREE, bool minread = false)
|
||||
: stmt (), call (expr),
|
||||
dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { }
|
||||
dst (query, maxwrite, nullptr, minwrite),
|
||||
src (query, maxread, nullptr, minread),
|
||||
mode (mode) { }
|
||||
|
||||
/* Access statement. */
|
||||
gimple *stmt;
|
||||
@ -245,14 +247,23 @@ extern bool get_size_range (tree, tree[2], int = 0);
|
||||
extern bool get_size_range (range_query *, tree, gimple *, tree[2], int = 0);
|
||||
|
||||
class range_query;
|
||||
extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
|
||||
range_query * = NULL);
|
||||
extern tree gimple_parm_array_size (tree, wide_int[2], bool * = NULL);
|
||||
extern tree gimple_call_alloc_size (gimple *, wide_int[2] = nullptr,
|
||||
range_query * = nullptr);
|
||||
|
||||
/* Compute the size of an object referenced by the first argument in
|
||||
a statement given by second argument, using Object Size Type given
|
||||
by third argument. Store result in an access_ref. */
|
||||
extern tree compute_objsize (tree, gimple *, int, access_ref *,
|
||||
range_query * = nullptr);
|
||||
extern tree compute_objsize (tree, gimple *, int, access_ref *,
|
||||
pointer_query *);
|
||||
inline tree compute_objsize (tree ptr, int ostype, access_ref *pref)
|
||||
{
|
||||
return compute_objsize (ptr, nullptr, ostype, pref, (range_query *)nullptr);
|
||||
}
|
||||
|
||||
extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
|
||||
/* Legacy/transitional API. Should not be used in new code. */
|
||||
extern tree compute_objsize (tree, int, access_ref *, pointer_query *);
|
||||
extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
|
||||
range_query * = NULL);
|
||||
extern tree compute_objsize (tree, int, tree * = nullptr, tree * = nullptr,
|
||||
range_query * = nullptr);
|
||||
|
||||
#endif // GCC_POINTER_QUERY_H
|
||||
|
@ -260,13 +260,12 @@ T (puts_unlocked, a); // { dg-warning "missing terminating nul" "puts_unlo
|
||||
|
||||
// Exerise exec functions.
|
||||
T (execl, a, s, NULL); // { dg-warning "missing terminating nul" "execl" }
|
||||
T (execl, a, s, NULL); // { dg-warning "missing terminating nul" "execl" }
|
||||
T (execle, a, s, NULL, NULL); // { dg-warning "missing terminating nul" "execl" }
|
||||
T (execlp, a, s, NULL); // { dg-warning "missing terminating nul" "execl" }
|
||||
T (execle, a, s, NULL, NULL); // { dg-warning "missing terminating nul" "execle" }
|
||||
T (execlp, a, s, NULL); // { dg-warning "missing terminating nul" "execlp" }
|
||||
|
||||
T (execv, a, &d); // { dg-warning "missing terminating nul" "execl" }
|
||||
T (execve, a, &d, &d); // { dg-warning "missing terminating nul" "execl" }
|
||||
T (execvp, a, &d); // { dg-warning "missing terminating nul" "execl" }
|
||||
T (execv, a, &d); // { dg-warning "missing terminating nul" "execv" }
|
||||
T (execve, a, &d, &d); // { dg-warning "missing terminating nul" "execve" }
|
||||
T (execvp, a, &d); // { dg-warning "missing terminating nul" "execvp" }
|
||||
|
||||
T (gettext, a); // { dg-warning "missing terminating nul" "gettext" }
|
||||
|
||||
|
38
gcc/testsuite/gcc.dg/Wstringop-overflow-81.c
Normal file
38
gcc/testsuite/gcc.dg/Wstringop-overflow-81.c
Normal file
@ -0,0 +1,38 @@
|
||||
/* Verify that -Wstringop-overflow uses context-sensitive range info
|
||||
even at -O0.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O0 -Wall" } */
|
||||
|
||||
extern void* memset (void*, int, __SIZE_TYPE__);
|
||||
|
||||
char a[8];
|
||||
|
||||
void warn_offset_range (int i)
|
||||
{
|
||||
if (i < 4)
|
||||
i = 4;
|
||||
memset (a + i, 0, 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
|
||||
}
|
||||
|
||||
void warn_size_range (int i, int n)
|
||||
{
|
||||
if (n < 5)
|
||||
n = 5;
|
||||
|
||||
memset (a + 4, 1, n); // { dg-warning "writing between 5 and \\d+ bytes into a region of size 4 " }
|
||||
}
|
||||
|
||||
void warn_offset_and_size_range (int i, int n)
|
||||
{
|
||||
if (n < 5)
|
||||
n = 5;
|
||||
|
||||
if (i < 4)
|
||||
{
|
||||
if (n < 9)
|
||||
n = 9;
|
||||
memset (a + i, 1, n); // { dg-warning "writing between 9 and \\d+ bytes into a region of size 8 " }
|
||||
}
|
||||
else
|
||||
memset (a + i, 0, n); // { dg-warning "writing between 5 and \\d+ bytes into a region of size 4 " }
|
||||
}
|
@ -1833,7 +1833,7 @@ strlen_pass::adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
|
||||
tree dst = gimple_call_arg (last.stmt, 0);
|
||||
|
||||
access_ref aref;
|
||||
tree size = compute_objsize (dst, 1, &aref, &ptr_qry);
|
||||
tree size = compute_objsize (dst, stmt, 1, &aref, &ptr_qry);
|
||||
if (size && tree_int_cst_lt (size, len))
|
||||
return;
|
||||
}
|
||||
@ -2035,7 +2035,7 @@ strlen_pass::maybe_warn_overflow (gimple *stmt, bool call_lhs, 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, ostype, &aref, &ptr_qry);
|
||||
tree destsize = compute_objsize (dest, stmt, ostype, &aref, &ptr_qry);
|
||||
|
||||
if (!destsize)
|
||||
{
|
||||
@ -3115,7 +3115,7 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
|
||||
}
|
||||
|
||||
access_ref aref;
|
||||
if (tree dstsize = compute_objsize (dst, 1, &aref, ptr_qry))
|
||||
if (tree dstsize = compute_objsize (dst, stmt, 1, &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.
|
||||
@ -3130,7 +3130,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, &aref, ptr_qry))
|
||||
if (tree srcsize = compute_objsize (src, stmt, 1, &aref, ptr_qry))
|
||||
if (wi::to_wide (srcsize) == cntrange[1])
|
||||
return false;
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
// { dg-options "-Wno-stringop-overflow" }
|
||||
// { dg-options "-Wno-stringop-overflow -Wno-stringop-overread" }
|
||||
|
||||
// 21.3.3 string capacity
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
// { dg-options "-fchar8_t" }
|
||||
// { dg-options "-fchar8_t -Wno-stringop-overread" }
|
||||
// { dg-do run { target c++17 } }
|
||||
|
||||
#include <filesystem>
|
||||
@ -36,6 +36,7 @@ test01()
|
||||
p = fs::u8path(u8"\xf0\x9d\x84\x9e");
|
||||
VERIFY( p.u8string() == u8"\U0001D11E" );
|
||||
|
||||
// The following triggers -Wstringop-overread. See PR 102958.
|
||||
std::u8string s1 = u8"filename2";
|
||||
p = fs::u8path(s1);
|
||||
VERIFY( p.u8string() == u8"filename2" );
|
||||
|
Loading…
Reference in New Issue
Block a user