PR middle-end/71924 - missing -Wreturn-local-addr returning alloca result
PR middle-end/71924 - missing -Wreturn-local-addr returning alloca result PR middle-end/90549 - missing -Wreturn-local-addr maybe returning an address of a local array plus offset gcc/ChangeLog: PR middle-end/71924 PR middle-end/90549 * gimple-ssa-isolate-paths.c (isolate_path): Add attribute. Update comment. (args_loc_t): New type. (args_loc_t, locmap_t): same. (diag_returned_locals): New function. (is_addr_local): Same. (handle_return_addr_local_phi_arg, warn_return_addr_local): Same. (find_implicit_erroneous_behavior): Call warn_return_addr_local_phi_arg. (find_explicit_erroneous_behavior): Call warn_return_addr_local. gcc/testsuite/ChangeLog: PR middle-end/71924 PR middle-end/90549 * gcc.c-torture/execute/return-addr.c: New test. * gcc.dg/Wreturn-local-addr-2.c: New test. * gcc.dg/Wreturn-local-addr-4.c: New test. * gcc.dg/Wreturn-local-addr-5.c: New test. * gcc.dg/Wreturn-local-addr-6.c: New test. * gcc.dg/Wreturn-local-addr-7.c: New test. * gcc.dg/Wreturn-local-addr-8.c: New test. * gcc.dg/Wreturn-local-addr-9.c: New test. * gcc.dg/Wreturn-local-addr-10.c: New test. * gcc.dg/Walloca-4.c: Handle expected warnings. * gcc.dg/pr41551.c: Same. * gcc.dg/pr59523.c: Same. * gcc.dg/tree-ssa/pr88775-2.c: Same. * gcc.dg/tree-ssa/alias-37.c: Same. * gcc.dg/winline-7.c: Same. From-SVN: r273261
This commit is contained in:
parent
7d64aec499
commit
aac9480da1
@ -1,3 +1,17 @@
|
||||
2019-07-08 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR middle-end/71924
|
||||
PR middle-end/90549
|
||||
* gimple-ssa-isolate-paths.c (isolate_path): Add attribute. Update
|
||||
comment.
|
||||
(args_loc_t): New type.
|
||||
(args_loc_t, locmap_t): same.
|
||||
(diag_returned_locals): New function.
|
||||
(is_addr_local): Same.
|
||||
(handle_return_addr_local_phi_arg, warn_return_addr_local): Same.
|
||||
(find_implicit_erroneous_behavior): Call warn_return_addr_local_phi_arg.
|
||||
(find_explicit_erroneous_behavior): Call warn_return_addr_local.
|
||||
|
||||
2019-07-08 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
* tree-vect-stmts.c (scan_operand_equal_p): Look through MEM_REF
|
||||
|
@ -128,9 +128,9 @@ insert_trap (gimple_stmt_iterator *si_p, tree op)
|
||||
|
||||
DUPLICATE is a pre-existing duplicate, use it as BB' if it exists.
|
||||
|
||||
Return BB'. */
|
||||
Return BB' (which may be equal to DUPLICATE). */
|
||||
|
||||
basic_block
|
||||
ATTRIBUTE_RETURNS_NONNULL basic_block
|
||||
isolate_path (basic_block bb, basic_block duplicate,
|
||||
edge e, gimple *stmt, tree op, bool ret_zero)
|
||||
{
|
||||
@ -341,6 +341,322 @@ stmt_uses_0_or_null_in_undefined_way (gimple *stmt)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Describes the property of a return statement that may return
|
||||
the address of one or more local variables. The type must
|
||||
be safely assignable and copyable so that it can be stored in
|
||||
a hash_map. */
|
||||
class args_loc_t
|
||||
{
|
||||
public:
|
||||
|
||||
args_loc_t (): nargs (), locvec (), ptr (&ptr)
|
||||
{
|
||||
locvec.create (4);
|
||||
}
|
||||
|
||||
args_loc_t (const args_loc_t &rhs)
|
||||
: nargs (rhs.nargs), locvec (rhs.locvec.copy ()), ptr (&ptr) { }
|
||||
|
||||
args_loc_t& operator= (const args_loc_t &rhs)
|
||||
{
|
||||
nargs = rhs.nargs;
|
||||
locvec.release ();
|
||||
locvec = rhs.locvec.copy ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
~args_loc_t ()
|
||||
{
|
||||
locvec.release ();
|
||||
gcc_assert (ptr == &ptr);
|
||||
}
|
||||
|
||||
/* For a PHI in a return statement its number of arguments. When greater
|
||||
than LOCVEC.LENGTH () implies that an address of one of the locals in
|
||||
LOCVEC may but need not be returned by the statement. Otherwise,
|
||||
unless both are zero, it implies it definitely is returned. */
|
||||
unsigned nargs;
|
||||
/* The locations of local variables/alloca calls returned by the return
|
||||
statement. Avoid using auto_vec here since it's not safe to copy due
|
||||
to pr90904. */
|
||||
vec <location_t> locvec;
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
/* A mapping from a return statement to the locations of local variables
|
||||
whose addresses it may return. */
|
||||
typedef hash_map <gimple *, args_loc_t> locmap_t;
|
||||
|
||||
/* Given the LOCMAP mapping, issue diagnostics about returning addresses
|
||||
of local variables. When MAYBE is set, all diagnostics will be of
|
||||
the "may return" kind. Otherwise each will be determined based on
|
||||
the equality of the corresponding NARGS and LOCVEC.LENGTH () values. */
|
||||
|
||||
static void
|
||||
diag_returned_locals (bool maybe, const locmap_t &locmap)
|
||||
{
|
||||
for (locmap_t::iterator it = locmap.begin (); it != locmap.end (); ++it)
|
||||
{
|
||||
gimple *stmt = (*it).first;
|
||||
const args_loc_t &argsloc = (*it).second;
|
||||
location_t stmtloc = gimple_location (stmt);
|
||||
|
||||
auto_diagnostic_group d;
|
||||
unsigned nargs = argsloc.locvec.length ();
|
||||
if (warning_at (stmtloc, OPT_Wreturn_local_addr,
|
||||
(maybe || argsloc.nargs > nargs
|
||||
? G_("function may return address of local variable")
|
||||
: G_("function returns address of local variable"))))
|
||||
{
|
||||
for (unsigned i = 0; i != nargs; ++i)
|
||||
inform (argsloc.locvec[i], "declared here");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return true if EXPR is an expression of pointer type that refers
|
||||
to the address of one or more variables with automatic storage
|
||||
duration. If so, add an entry to *PLOCMAP and insert into
|
||||
PLOCMAP->LOCVEC the locations of the corresponding local variables
|
||||
whose address is returned by the RETURN_STMT (which may be set to
|
||||
(gimple*)-1 as a placeholder for such a statement). VISITED is
|
||||
a bitmap of PHI nodes already visited by recursive calls. When
|
||||
null, PHI expressions are not considered. */
|
||||
|
||||
static bool
|
||||
is_addr_local (gimple *return_stmt, tree exp, locmap_t *plocmap,
|
||||
hash_set<gphi *> *visited)
|
||||
{
|
||||
if (TREE_CODE (exp) == ADDR_EXPR)
|
||||
{
|
||||
tree baseaddr = get_base_address (TREE_OPERAND (exp, 0));
|
||||
if (TREE_CODE (baseaddr) == MEM_REF)
|
||||
return is_addr_local (return_stmt, TREE_OPERAND (baseaddr, 0),
|
||||
plocmap, visited);
|
||||
|
||||
if ((!VAR_P (baseaddr)
|
||||
|| is_global_var (baseaddr))
|
||||
&& TREE_CODE (baseaddr) != PARM_DECL)
|
||||
return false;
|
||||
|
||||
args_loc_t &argsloc = plocmap->get_or_insert (return_stmt);
|
||||
argsloc.locvec.safe_push (DECL_SOURCE_LOCATION (baseaddr));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!POINTER_TYPE_P (TREE_TYPE (exp)))
|
||||
return false;
|
||||
|
||||
if (TREE_CODE (exp) == SSA_NAME)
|
||||
{
|
||||
gimple *def_stmt = SSA_NAME_DEF_STMT (exp);
|
||||
enum gimple_code code = gimple_code (def_stmt);
|
||||
|
||||
if (is_gimple_assign (def_stmt))
|
||||
{
|
||||
tree type = TREE_TYPE (gimple_assign_lhs (def_stmt));
|
||||
if (POINTER_TYPE_P (type))
|
||||
{
|
||||
tree_code code = gimple_assign_rhs_code (def_stmt);
|
||||
tree ptr1 = NULL_TREE, ptr2 = NULL_TREE;
|
||||
|
||||
/* Set to the number of arguments examined that should
|
||||
be added to ARGSLOC->NARGS to identify expressions
|
||||
only some but not all of whose operands refer to local
|
||||
addresses. */
|
||||
unsigned nargs = 0;
|
||||
if (code == COND_EXPR)
|
||||
{
|
||||
ptr1 = gimple_assign_rhs2 (def_stmt);
|
||||
ptr2 = gimple_assign_rhs3 (def_stmt);
|
||||
nargs = 2;
|
||||
}
|
||||
else if (code == MAX_EXPR || code == MIN_EXPR)
|
||||
{
|
||||
ptr1 = gimple_assign_rhs1 (def_stmt);
|
||||
ptr2 = gimple_assign_rhs2 (def_stmt);
|
||||
nargs = 2;
|
||||
}
|
||||
else if (code == ADDR_EXPR
|
||||
|| code == NOP_EXPR
|
||||
|| code == POINTER_PLUS_EXPR)
|
||||
/* Leave NARGS at zero and let the recursive call set it. */
|
||||
ptr1 = gimple_assign_rhs1 (def_stmt);
|
||||
|
||||
/* Avoid short-circuiting the logical OR result in case
|
||||
both operands refer to local variables, in which case
|
||||
both should be considered and identified in the warning. */
|
||||
bool res1 = false, res2 = false;
|
||||
if (ptr1)
|
||||
res1 = is_addr_local (return_stmt, ptr1, plocmap, visited);
|
||||
if (ptr2)
|
||||
res2 = is_addr_local (return_stmt, ptr2, plocmap, visited);
|
||||
|
||||
if (nargs)
|
||||
if (args_loc_t *argsloc = plocmap->get (return_stmt))
|
||||
argsloc->nargs += nargs;
|
||||
|
||||
return res1 || res2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (code == GIMPLE_CALL
|
||||
&& gimple_call_builtin_p (def_stmt))
|
||||
{
|
||||
/* Handle alloca and friends that return pointers to automatic
|
||||
storage. */
|
||||
tree fn = gimple_call_fndecl (def_stmt);
|
||||
int code = DECL_FUNCTION_CODE (fn);
|
||||
if (code == BUILT_IN_ALLOCA
|
||||
|| code == BUILT_IN_ALLOCA_WITH_ALIGN
|
||||
|| code == BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX)
|
||||
{
|
||||
args_loc_t &argsloc = plocmap->get_or_insert (return_stmt);
|
||||
argsloc.locvec.safe_push (gimple_location (def_stmt));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gimple_call_num_args (def_stmt) < 1)
|
||||
return false;
|
||||
|
||||
/* Recursively examine the first argument of calls to built-ins
|
||||
that return it. */
|
||||
switch (code)
|
||||
{
|
||||
case BUILT_IN_MEMCPY:
|
||||
case BUILT_IN_MEMCPY_CHK:
|
||||
case BUILT_IN_MEMPCPY:
|
||||
case BUILT_IN_MEMPCPY_CHK:
|
||||
case BUILT_IN_MEMMOVE:
|
||||
case BUILT_IN_MEMMOVE_CHK:
|
||||
case BUILT_IN_STPCPY:
|
||||
case BUILT_IN_STPCPY_CHK:
|
||||
case BUILT_IN_STPNCPY:
|
||||
case BUILT_IN_STPNCPY_CHK:
|
||||
case BUILT_IN_STRCAT:
|
||||
case BUILT_IN_STRCAT_CHK:
|
||||
case BUILT_IN_STRCHR:
|
||||
case BUILT_IN_STRCPY:
|
||||
case BUILT_IN_STRCPY_CHK:
|
||||
case BUILT_IN_STRNCAT:
|
||||
case BUILT_IN_STRNCAT_CHK:
|
||||
case BUILT_IN_STRNCPY:
|
||||
case BUILT_IN_STRNCPY_CHK:
|
||||
case BUILT_IN_STRRCHR:
|
||||
case BUILT_IN_STRSTR:
|
||||
return is_addr_local (return_stmt,
|
||||
gimple_call_arg (def_stmt, 0),
|
||||
plocmap, visited);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (code == GIMPLE_PHI && visited)
|
||||
{
|
||||
gphi *phi_stmt = as_a <gphi *> (def_stmt);
|
||||
if (visited->add (phi_stmt))
|
||||
return false;
|
||||
|
||||
unsigned count = 0;
|
||||
unsigned nargs = gimple_phi_num_args (phi_stmt);
|
||||
args_loc_t &argsloc = plocmap->get_or_insert (return_stmt);
|
||||
/* Bump up the number of operands examined by the number of
|
||||
operands of this PHI. */
|
||||
argsloc.nargs += nargs;
|
||||
for (unsigned i = 0; i < gimple_phi_num_args (phi_stmt); ++i)
|
||||
{
|
||||
tree arg = gimple_phi_arg_def (phi_stmt, i);
|
||||
if (is_addr_local (return_stmt, arg, plocmap, visited))
|
||||
++count;
|
||||
}
|
||||
return count != 0;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Detect returning the address of a local variable in a PHI result LHS
|
||||
and argument ARG and PHI edge E in basic block BB. Add an entry for
|
||||
each use to LOCMAP, setting its NARGS member to the NARGS argument
|
||||
(the number of PHI operands) plus the number of arguments in binary
|
||||
expressions refereced by ARG. Call isolate_path for each returned
|
||||
address and set *ISOLATED to true if called.
|
||||
Return either DUPLICATE or the most recent result of isolate_path. */
|
||||
|
||||
static basic_block
|
||||
handle_return_addr_local_phi_arg (basic_block bb, basic_block duplicate,
|
||||
tree lhs, tree arg, edge e, locmap_t &locmap,
|
||||
unsigned nargs, bool *isolated)
|
||||
{
|
||||
/* Use (gimple*)-1 as a temporary placeholder and replace it with
|
||||
the return statement below once it is known. Using a null doesn't
|
||||
work because it's used by the hash_map to mean "no-entry." Pass
|
||||
null instead of a visited_phis bitmap to avoid descending into
|
||||
PHIs since they are being processed by the caller. Those that
|
||||
remain will be checked again later. */
|
||||
if (!is_addr_local ((gimple*)-1, arg, &locmap, NULL))
|
||||
{
|
||||
/* Remove the placeholder regardless of success or failure. */
|
||||
locmap.remove ((gimple*)-1);
|
||||
return duplicate;
|
||||
}
|
||||
|
||||
const args_loc_t* const placeargsloc = locmap.get ((gimple*)-1);
|
||||
const unsigned nlocs = placeargsloc->locvec.length ();
|
||||
gcc_assert (nlocs);
|
||||
|
||||
/* Add to the number of PHI arguments determined by the caller
|
||||
the number of operands of the expressions referenced by ARG.
|
||||
This lets the caller determine whether it's dealing with
|
||||
a "may return" or "definitely returns." */
|
||||
nargs += placeargsloc->nargs;
|
||||
|
||||
/* Set to true if any expressions referenced by ARG involve
|
||||
multiple addresses only some of which are those of locals. */
|
||||
bool maybe = placeargsloc->nargs > placeargsloc->locvec.length ();
|
||||
|
||||
gimple *use_stmt;
|
||||
imm_use_iterator iter;
|
||||
|
||||
/* Look for uses of the PHI result LHS in return statements. */
|
||||
FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
|
||||
{
|
||||
greturn *return_stmt = dyn_cast <greturn *> (use_stmt);
|
||||
if (!return_stmt)
|
||||
continue;
|
||||
|
||||
if (gimple_return_retval (return_stmt) != lhs)
|
||||
continue;
|
||||
|
||||
/* Add an entry for the return statement and the locations
|
||||
oof the PHI arguments obtained above to the map. */
|
||||
args_loc_t &argsloc = locmap.get_or_insert (use_stmt);
|
||||
argsloc.nargs = nargs;
|
||||
unsigned nelts = argsloc.locvec.length () + nlocs;
|
||||
argsloc.locvec.reserve (nelts);
|
||||
argsloc.locvec.splice (placeargsloc->locvec);
|
||||
|
||||
if (!maybe
|
||||
&& (flag_isolate_erroneous_paths_dereference
|
||||
|| flag_isolate_erroneous_paths_attribute)
|
||||
&& gimple_bb (use_stmt) == bb)
|
||||
{
|
||||
duplicate = isolate_path (bb, duplicate, e,
|
||||
use_stmt, lhs, true);
|
||||
|
||||
/* Let caller know the path has been isolated. */
|
||||
*isolated = true;
|
||||
}
|
||||
}
|
||||
|
||||
locmap.remove ((gimple*)-1);
|
||||
|
||||
return duplicate;
|
||||
}
|
||||
|
||||
/* Look for PHI nodes which feed statements in the same block where
|
||||
the value of the PHI node implies the statement is erroneous.
|
||||
|
||||
@ -352,6 +668,8 @@ stmt_uses_0_or_null_in_undefined_way (gimple *stmt)
|
||||
static void
|
||||
find_implicit_erroneous_behavior (void)
|
||||
{
|
||||
locmap_t locmap;
|
||||
|
||||
basic_block bb;
|
||||
|
||||
FOR_EACH_BB_FN (bb, cfun)
|
||||
@ -388,70 +706,46 @@ find_implicit_erroneous_behavior (void)
|
||||
gphi *phi = si.phi ();
|
||||
tree lhs = gimple_phi_result (phi);
|
||||
|
||||
/* Initial number of PHI arguments. The result may change
|
||||
from one iteration of the loop below to the next in
|
||||
response to changes to the CFG but only the initial
|
||||
value is stored below for use by diagnostics. */
|
||||
unsigned nargs = gimple_phi_num_args (phi);
|
||||
|
||||
/* PHI produces a pointer result. See if any of the PHI's
|
||||
arguments are NULL.
|
||||
|
||||
When we remove an edge, we want to reprocess the current
|
||||
index, hence the ugly way we update I for each iteration. */
|
||||
index since the argument at that index will have been
|
||||
removed, hence the ugly way we update I for each iteration. */
|
||||
basic_block duplicate = NULL;
|
||||
for (unsigned i = 0, next_i = 0;
|
||||
i < gimple_phi_num_args (phi);
|
||||
i = next_i)
|
||||
i < gimple_phi_num_args (phi); i = next_i)
|
||||
{
|
||||
tree op = gimple_phi_arg_def (phi, i);
|
||||
tree arg = gimple_phi_arg_def (phi, i);
|
||||
edge e = gimple_phi_arg_edge (phi, i);
|
||||
imm_use_iterator iter;
|
||||
gimple *use_stmt;
|
||||
|
||||
/* Advance the argument index unless a path involving
|
||||
the current argument has been isolated. */
|
||||
next_i = i + 1;
|
||||
|
||||
if (TREE_CODE (op) == ADDR_EXPR)
|
||||
bool isolated = false;
|
||||
duplicate = handle_return_addr_local_phi_arg (bb, duplicate, lhs,
|
||||
arg, e, locmap,
|
||||
nargs, &isolated);
|
||||
if (isolated)
|
||||
{
|
||||
tree valbase = get_base_address (TREE_OPERAND (op, 0));
|
||||
if ((VAR_P (valbase) && !is_global_var (valbase))
|
||||
|| TREE_CODE (valbase) == PARM_DECL)
|
||||
{
|
||||
FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
|
||||
{
|
||||
greturn *return_stmt
|
||||
= dyn_cast <greturn *> (use_stmt);
|
||||
if (!return_stmt)
|
||||
continue;
|
||||
|
||||
if (gimple_return_retval (return_stmt) != lhs)
|
||||
continue;
|
||||
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
if (warning_at (gimple_location (use_stmt),
|
||||
OPT_Wreturn_local_addr,
|
||||
"function may return address "
|
||||
"of local variable"))
|
||||
inform (DECL_SOURCE_LOCATION(valbase),
|
||||
"declared here");
|
||||
}
|
||||
|
||||
if ((flag_isolate_erroneous_paths_dereference
|
||||
|| flag_isolate_erroneous_paths_attribute)
|
||||
&& gimple_bb (use_stmt) == bb)
|
||||
{
|
||||
duplicate = isolate_path (bb, duplicate, e,
|
||||
use_stmt, lhs, true);
|
||||
|
||||
/* When we remove an incoming edge, we need to
|
||||
reprocess the Ith element. */
|
||||
next_i = i;
|
||||
cfg_altered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
cfg_altered = true;
|
||||
next_i = i;
|
||||
}
|
||||
|
||||
if (!integer_zerop (op))
|
||||
if (!integer_zerop (arg))
|
||||
continue;
|
||||
|
||||
location_t phi_arg_loc = gimple_phi_arg_location (phi, i);
|
||||
|
||||
imm_use_iterator iter;
|
||||
gimple *use_stmt;
|
||||
|
||||
/* We've got a NULL PHI argument. Now see if the
|
||||
PHI's result is dereferenced within BB. */
|
||||
FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
|
||||
@ -480,6 +774,57 @@ find_implicit_erroneous_behavior (void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diag_returned_locals (false, locmap);
|
||||
}
|
||||
|
||||
/* Detect and diagnose returning the address of a local variable
|
||||
in RETURN_STMT in basic block BB. This only becomes undefined
|
||||
behavior if the result is used, so we do not insert a trap and
|
||||
only return NULL instead. */
|
||||
|
||||
static void
|
||||
warn_return_addr_local (basic_block bb, greturn *return_stmt)
|
||||
{
|
||||
tree val = gimple_return_retval (return_stmt);
|
||||
if (!val)
|
||||
return;
|
||||
|
||||
locmap_t locmap;
|
||||
hash_set<gphi *> visited_phis;
|
||||
if (!is_addr_local (return_stmt, val, &locmap, &visited_phis))
|
||||
return;
|
||||
|
||||
/* We only need it for this particular case. */
|
||||
calculate_dominance_info (CDI_POST_DOMINATORS);
|
||||
|
||||
const args_loc_t *argsloc = locmap.get (return_stmt);
|
||||
gcc_assert (argsloc);
|
||||
|
||||
bool maybe = argsloc->nargs > argsloc->locvec.length ();
|
||||
if (!maybe)
|
||||
maybe = !dominated_by_p (CDI_POST_DOMINATORS,
|
||||
single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb);
|
||||
|
||||
diag_returned_locals (maybe, locmap);
|
||||
|
||||
/* Bail if the statement isn't certain to return the address
|
||||
of a local (e.g., if it involves a conditional expression
|
||||
that wasn't trasnformed into a PHI or if it involves
|
||||
a MAX_EXPR or MIN_EXPR only one of whose operands is a local
|
||||
(even though such an expression isn't valid in C or has
|
||||
defined semantics in C++). */
|
||||
if (maybe)
|
||||
return;
|
||||
|
||||
/* Do not modify code if the user only asked for warnings. */
|
||||
if (flag_isolate_erroneous_paths_dereference
|
||||
|| flag_isolate_erroneous_paths_attribute)
|
||||
{
|
||||
tree zero = build_zero_cst (TREE_TYPE (val));
|
||||
gimple_return_set_retval (return_stmt, zero);
|
||||
update_stmt (return_stmt);
|
||||
}
|
||||
}
|
||||
|
||||
/* Look for statements which exhibit erroneous behavior. For example
|
||||
@ -525,49 +870,10 @@ find_explicit_erroneous_behavior (void)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Detect returning the address of a local variable. This only
|
||||
becomes undefined behavior if the result is used, so we do not
|
||||
insert a trap and only return NULL instead. */
|
||||
/* Look for a return statement that returns the address
|
||||
of a local variable or the result of alloca. */
|
||||
if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
|
||||
{
|
||||
tree val = gimple_return_retval (return_stmt);
|
||||
if (val && TREE_CODE (val) == ADDR_EXPR)
|
||||
{
|
||||
tree valbase = get_base_address (TREE_OPERAND (val, 0));
|
||||
if ((VAR_P (valbase) && !is_global_var (valbase))
|
||||
|| TREE_CODE (valbase) == PARM_DECL)
|
||||
{
|
||||
/* We only need it for this particular case. */
|
||||
calculate_dominance_info (CDI_POST_DOMINATORS);
|
||||
const char* msg;
|
||||
bool always_executed = dominated_by_p
|
||||
(CDI_POST_DOMINATORS,
|
||||
single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb);
|
||||
if (always_executed)
|
||||
msg = N_("function returns address of local variable");
|
||||
else
|
||||
msg = N_("function may return address of "
|
||||
"local variable");
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
if (warning_at (gimple_location (stmt),
|
||||
OPT_Wreturn_local_addr, msg))
|
||||
inform (DECL_SOURCE_LOCATION(valbase),
|
||||
"declared here");
|
||||
}
|
||||
|
||||
/* Do not modify code if the user only asked for
|
||||
warnings. */
|
||||
if (flag_isolate_erroneous_paths_dereference
|
||||
|| flag_isolate_erroneous_paths_attribute)
|
||||
{
|
||||
tree zero = build_zero_cst (TREE_TYPE (val));
|
||||
gimple_return_set_retval (return_stmt, zero);
|
||||
update_stmt (stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
warn_return_addr_local (bb, return_stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,23 @@
|
||||
2019-07-08 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR middle-end/71924
|
||||
PR middle-end/90549
|
||||
* gcc.c-torture/execute/return-addr.c: New test.
|
||||
* gcc.dg/Wreturn-local-addr-2.c: New test.
|
||||
* gcc.dg/Wreturn-local-addr-4.c: New test.
|
||||
* gcc.dg/Wreturn-local-addr-5.c: New test.
|
||||
* gcc.dg/Wreturn-local-addr-6.c: New test.
|
||||
* gcc.dg/Wreturn-local-addr-7.c: New test.
|
||||
* gcc.dg/Wreturn-local-addr-8.c: New test.
|
||||
* gcc.dg/Wreturn-local-addr-9.c: New test.
|
||||
* gcc.dg/Wreturn-local-addr-10.c: New test.
|
||||
* gcc.dg/Walloca-4.c: Handle expected warnings.
|
||||
* gcc.dg/pr41551.c: Same.
|
||||
* gcc.dg/pr59523.c: Same.
|
||||
* gcc.dg/tree-ssa/pr88775-2.c: Same.
|
||||
* gcc.dg/tree-ssa/alias-37.c: Same.
|
||||
* gcc.dg/winline-7.c: Same.
|
||||
|
||||
2019-07-08 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
* g++.dg/vect/simd-6.cc: Replace xfail with target x86.
|
||||
|
122
gcc/testsuite/gcc.c-torture/execute/return-addr.c
Normal file
122
gcc/testsuite/gcc.c-torture/execute/return-addr.c
Normal file
@ -0,0 +1,122 @@
|
||||
/* Test to verify that a function that returns either the address
|
||||
of a local variable or a non-local via a MAX_EXPR or MIN_EXPR
|
||||
doesn't return null when the result of the expression is
|
||||
the latter. */
|
||||
|
||||
#define NOIPA __attribute__ ((noclone, noinline, noipa))
|
||||
|
||||
#define A(expr) \
|
||||
((expr) \
|
||||
? (void)0 \
|
||||
: (__builtin_printf ("assertion failed on line %i: %s\n", \
|
||||
__LINE__, #expr), \
|
||||
__builtin_abort ()))
|
||||
|
||||
|
||||
typedef __UINTPTR_TYPE__ uintptr_t;
|
||||
|
||||
/* Return a bigger value than P. The address still points (just
|
||||
past) the local variable pointed to by P so the caller does
|
||||
return the address of a local variable but that's hidden from
|
||||
GCC by the attribute and the point of the test is to verify
|
||||
that the address in the return statement in the caller isn't
|
||||
replaced by null when GCC cannot prove the address doesn't
|
||||
reference a non-local variable. */
|
||||
|
||||
NOIPA char* get_max_2 (char *p)
|
||||
{
|
||||
return p + 1;
|
||||
}
|
||||
|
||||
NOIPA char* get_max_3 (char *p, char *q)
|
||||
{
|
||||
return p < q ? q + 1 : p + 1;
|
||||
}
|
||||
|
||||
/* Analogous to the above. The expressions are undefined because
|
||||
they form an address prior to the beginning of the object but
|
||||
it's hidden from GCC by the attributes. */
|
||||
|
||||
NOIPA char* get_min_2 (char *p)
|
||||
{
|
||||
return p - 1;
|
||||
}
|
||||
|
||||
NOIPA char* get_min_3 (char *p, char *q)
|
||||
{
|
||||
return p < q ? p - 1 : q - 1;
|
||||
}
|
||||
|
||||
|
||||
NOIPA void* test_max_2 (void)
|
||||
{
|
||||
char c;
|
||||
|
||||
char *p = get_max_2 (&c);
|
||||
|
||||
void *q = p > &c ? p : &c; /* MAX_EXPR */
|
||||
return q;
|
||||
}
|
||||
|
||||
NOIPA void* test_max_3 (void)
|
||||
{
|
||||
char c;
|
||||
char d;
|
||||
|
||||
char *p = get_max_3 (&c, &d);
|
||||
|
||||
void *q = p < &c ? &c < &d ? &d : &c : p;
|
||||
return q;
|
||||
}
|
||||
|
||||
NOIPA void* test_min_2 (void)
|
||||
{
|
||||
char c;
|
||||
|
||||
char *p = get_min_2 (&c);
|
||||
|
||||
void *q = p < &c ? p : &c; /* MIN_EXPR" */
|
||||
return q;
|
||||
}
|
||||
|
||||
NOIPA void* test_min_3 (void)
|
||||
{
|
||||
char c;
|
||||
char d;
|
||||
|
||||
char *p = get_min_3 (&c, &d);
|
||||
|
||||
void *q = p > &c ? &c > &d ? &d : &c : p;
|
||||
return q;
|
||||
}
|
||||
|
||||
NOIPA void* test_min_3_phi (int i)
|
||||
{
|
||||
char a, b;
|
||||
|
||||
char *p0 = &a;
|
||||
char *p1 = &b;
|
||||
char *p2 = get_min_3 (&a, &b);
|
||||
char *p3 = get_min_3 (&a, &b);
|
||||
|
||||
char *p4 = p2 < p0 ? p2 : p0;
|
||||
char *p5 = p3 < p1 ? p3 : p1;
|
||||
|
||||
__builtin_printf ("%p %p %p %p\n", p2, p3, p4, p5);
|
||||
|
||||
if (i == 1)
|
||||
return p4;
|
||||
else
|
||||
return p5;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
A (0 != test_max_2 ());
|
||||
A (0 != test_max_3 ());
|
||||
|
||||
A (0 != test_min_2 ());
|
||||
A (0 != test_min_3 ());
|
||||
|
||||
A (0 != test_min_3_phi (0));
|
||||
}
|
@ -7,11 +7,12 @@
|
||||
{
|
||||
|
||||
char *src;
|
||||
_Bool
|
||||
use_alloca = (((rear_ptr - w) * sizeof (char)) < 4096U);
|
||||
if (use_alloca)
|
||||
_Bool use_alloca = (((rear_ptr - w) * sizeof (char)) < 4096U);
|
||||
if (use_alloca)
|
||||
src = (char *) __builtin_alloca ((rear_ptr - w) * sizeof (char));
|
||||
else
|
||||
src = (char *) __builtin_malloc ((rear_ptr - w) * sizeof (char));
|
||||
return src;
|
||||
}
|
||||
|
||||
/* { dg-prune-output "-Wreturn-local-addr" } */
|
||||
|
56
gcc/testsuite/gcc.dg/Wreturn-local-addr-10.c
Normal file
56
gcc/testsuite/gcc.dg/Wreturn-local-addr-10.c
Normal file
@ -0,0 +1,56 @@
|
||||
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
|
||||
Test reduced from libstdc++-v3/testsuite/ext/ext_pointer/1.cc.
|
||||
It verifies that iteration in find_implicit_erroneous_behavior
|
||||
in gimple-ssa-isolate-path.c terminates under specific conditions.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
typedef __UINTPTR_TYPE__ uintptr_t;
|
||||
|
||||
struct A { int i; };
|
||||
struct P { uintptr_t d; };
|
||||
|
||||
static inline struct A* get (const struct P *p)
|
||||
{
|
||||
if (p->d == 1)
|
||||
return 0;
|
||||
|
||||
return (struct A*)((uintptr_t)p + p->d);
|
||||
}
|
||||
|
||||
static inline void set (struct P *p, struct A* q)
|
||||
{
|
||||
/* The basic block below would cause an infinite loop in
|
||||
find_implicit_erroneous_behavior due to assuming the DUPLICATE
|
||||
pointer returned from isolate_path would distinct from the one
|
||||
passed to it. (Replacing the if statement with the ternary ?:
|
||||
expression did not have this effect (it gets optimized early
|
||||
on).
|
||||
<bb 4> [local count: 1073741823]:
|
||||
# _14 = PHI <0B(2), &MEM <struct A[2]> [(void *)&a + 4B](3)>
|
||||
_2 = _14->i;
|
||||
if (_2 != 2)
|
||||
goto <bb 5>; [0.00%]
|
||||
else
|
||||
goto <bb 6>; [100.00%]
|
||||
*/
|
||||
if (!q)
|
||||
p->d = 1;
|
||||
else
|
||||
p->d = (uintptr_t)(q) - (uintptr_t)(p);
|
||||
}
|
||||
|
||||
void f (void)
|
||||
{
|
||||
struct A a[2] = { { 1 }, { 2 } };
|
||||
|
||||
struct P p, q;
|
||||
set (&p, a);
|
||||
set (&q, get (&p));
|
||||
|
||||
set (&q, get (&q) + 0);
|
||||
set (&q, get (&q) + 1);
|
||||
|
||||
if (get (&q)[0].i != get (&p)[1].i)
|
||||
__builtin_abort ();
|
||||
}
|
293
gcc/testsuite/gcc.dg/Wreturn-local-addr-2.c
Normal file
293
gcc/testsuite/gcc.dg/Wreturn-local-addr-2.c
Normal file
@ -0,0 +1,293 @@
|
||||
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
#define ATTR(...) __attribute__ ((__VA_ARGS__))
|
||||
|
||||
struct A { int a, b, c; };
|
||||
struct B { int a, b, c[]; };
|
||||
|
||||
void sink (void*, ...);
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_alloca (int n)
|
||||
{
|
||||
void *p = __builtin_alloca (n);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_alloca_index_cst (int n)
|
||||
{
|
||||
int *p = (int*)__builtin_alloca (n);
|
||||
p = &p[1];
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_alloca_plus_cst (int n)
|
||||
{
|
||||
int *p = (int*)__builtin_alloca (n);
|
||||
p += 1;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_alloca_plus_var (int n, int i)
|
||||
{
|
||||
char *p = (char*)__builtin_alloca (n);
|
||||
p += i;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_alloca_member_1 (int n)
|
||||
{
|
||||
struct A *p = (struct A*)__builtin_alloca (n);
|
||||
sink (&p->a);
|
||||
return &p->a; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_alloca_member_2 (int n)
|
||||
{
|
||||
struct A *p = (struct A*)__builtin_alloca (n);
|
||||
sink (&p->b);
|
||||
return &p->b; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_alloca_flexarray (int n)
|
||||
{
|
||||
struct B *p = (struct B*)__builtin_alloca (n);
|
||||
sink (p->c);
|
||||
return p->c; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_array (void)
|
||||
{
|
||||
int a[32];
|
||||
void *p = a;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_array_index_cst (void)
|
||||
{
|
||||
int a[32];
|
||||
void *p = &a[2];
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_array_plus_cst (void)
|
||||
{
|
||||
int a[32];
|
||||
void *p = a + 2;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_array_plus_var (int i)
|
||||
{
|
||||
int a[32];
|
||||
void *p = a + i;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_array_member_1 (void)
|
||||
{
|
||||
struct A a[2];
|
||||
int *p = &a[1].a;
|
||||
sink (a, p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_array_member_2 (void)
|
||||
{
|
||||
struct A a[32];
|
||||
int *p = &a[1].b;
|
||||
sink (a, p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_vla (int n)
|
||||
{
|
||||
char a[n];
|
||||
void *p = a;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_vla_index_cst (int n)
|
||||
{
|
||||
char a[n];
|
||||
char *p = &a[3];
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_vla_plus_cst (int n)
|
||||
{
|
||||
char a[n];
|
||||
char *p = a + 3;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_vla_index_var (int n, int i)
|
||||
{
|
||||
char a[n];
|
||||
char *p = &a[i];
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_vla_plus_var (int n, int i)
|
||||
{
|
||||
char a[n];
|
||||
char *p = a + i;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_vla_member_1 (int n, int i)
|
||||
{
|
||||
struct A a[n];
|
||||
void *p = &a[i].a;
|
||||
sink (a, p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_vla_member_2 (int n, int i)
|
||||
{
|
||||
struct A a[n];
|
||||
void *p = &a[i].b;
|
||||
sink (a, p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_alloca_or_alloca (int n, int i)
|
||||
{
|
||||
void *p = i ? __builtin_alloca (n * i) : __builtin_alloca (n);
|
||||
sink (p);
|
||||
/* The warning here should really be "function returns". */
|
||||
return p; /* { dg-warning "function (returns|may return) address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_alloca_or_alloca_2 (int n, int i)
|
||||
{
|
||||
void *p0 = __builtin_alloca (n);
|
||||
void *p1 = __builtin_alloca (n * 2);
|
||||
void *p = i ? p0 : p1;
|
||||
sink (p0, p1, p);
|
||||
/* Same as above. */
|
||||
return p; /* { dg-warning "function (returns|may return) address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_array_or_array (int i)
|
||||
{
|
||||
int a[5];
|
||||
int b[7];
|
||||
void *p = i ? a : b;
|
||||
sink (a, b, p);
|
||||
/* The warning here should really be "function returns". */
|
||||
return p; /* { dg-warning "function (returns|may return) address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_array_or_array_plus_var (int i, int j)
|
||||
{
|
||||
int a[5];
|
||||
int b[7];
|
||||
|
||||
void *p0 = a + i;
|
||||
void *p1 = b + j;
|
||||
|
||||
void *p = i < j ? p0 : p1;
|
||||
sink (a, b, p0, p1, p);
|
||||
/* The warning here should really be "function returns". */
|
||||
return p; /* { dg-warning "function (returns|may return) address of local" } */
|
||||
}
|
||||
|
||||
extern int global[32];
|
||||
|
||||
ATTR (noipa) void*
|
||||
may_return_global_or_alloca (int n, int i)
|
||||
{
|
||||
void *p = i ? global : __builtin_alloca (n);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
|
||||
ATTR (noipa) void*
|
||||
may_return_global_or_alloca_plus_cst (int n, int i)
|
||||
{
|
||||
int *p = i ? global : (int*)__builtin_alloca (n);
|
||||
p += 7;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
may_return_global_or_array (int n, int i)
|
||||
{
|
||||
int a[32];
|
||||
void *p = i ? global : a;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
may_return_global_or_array_plus_cst (int n, int i)
|
||||
{
|
||||
int a[32];
|
||||
int *p = i ? global : a;
|
||||
p += 4;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
may_return_global_or_vla (int n, int i)
|
||||
{
|
||||
int a[n];
|
||||
void *p = i ? global : a;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
may_return_global_or_vla_plus_cst (int n, int i)
|
||||
{
|
||||
int a[n];
|
||||
int *p = i ? global : a;
|
||||
p += 4;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
248
gcc/testsuite/gcc.dg/Wreturn-local-addr-3.c
Normal file
248
gcc/testsuite/gcc.dg/Wreturn-local-addr-3.c
Normal file
@ -0,0 +1,248 @@
|
||||
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
#define ATTR(...) __attribute__ ((__VA_ARGS__))
|
||||
|
||||
typedef __INTPTR_TYPE__ intptr_t;
|
||||
|
||||
struct A { int a, b, c; };
|
||||
struct B { int a, b, c[]; };
|
||||
|
||||
extern int g1[5], g2[5], g3[5], g4[5], g5[5];
|
||||
|
||||
void sink (void*, ...);
|
||||
|
||||
/* Verify that a pointer difference expression is handled correctly
|
||||
even when converted to a pointer. */
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_local_diff_cst (void)
|
||||
{
|
||||
int a[5];
|
||||
void *p = (void*)(&a[4] - &a[1]);
|
||||
return p;
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_local_diff_var (int i, int j)
|
||||
{
|
||||
int a[5];
|
||||
void *p = (void*)(&a[j] - &a[i]);
|
||||
return p;
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_locals (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
void *p = i < 0 ? a : b;
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
/* Verify that returning the address of a local converted to intptr_t
|
||||
is not diagnosed (see bug 90737 for a case the front-end gets wrong). */
|
||||
|
||||
ATTR (noipa) intptr_t
|
||||
return_int_2_locals (int i)
|
||||
{
|
||||
int a[1];
|
||||
int b[2];
|
||||
void *p = i < 0 ? a : b;
|
||||
return (intptr_t)p;
|
||||
}
|
||||
|
||||
/* Verify that a conditional expression with a pointer first operand
|
||||
is handled correctly. */
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_locals_ptrcond (void *q)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
void *p = q ? a : b;
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
/* Verify that a preincrement expression with a pointer operand is
|
||||
handled correctly. */
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_locals_ptrinc (void *q)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int *p = q ? a : b;
|
||||
return ++p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_3_locals (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = i < 0 ? a : 0 < i ? c : b;
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
/* Verify that a conditional expression with a pointer first operand
|
||||
is handled correctly. */
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_3_locals_ptrcond (void *p, void *q)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
|
||||
void *r = q ? r ? a : b : c;
|
||||
return r; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_5_locals (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
int d[4]; /* { dg-message "declared here" } */
|
||||
int e[5]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = i < -1 ? a : i < 0 ? b : 1 < i ? e : 0 < i ? d : c;
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_1_global_4_locals (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
int d[4]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = i < -1 ? a : i < 0 ? b : 1 < i ? g1 : 0 < i ? d : c;
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_globals_3_locals (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = i < -1 ? a : i < 0 ? b : 1 < i ? g1 : 0 < i ? g2 : c;
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_3_globals_2_locals (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = i < -1 ? a : i < 0 ? b : 1 < i ? g1 : 0 < i ? g2 : g3;
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_4_globals_1_local (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = i < -1 ? a : i < 0 ? g1 : 1 < i ? g2 : 0 < i ? g4 : g3;
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_all_globals (int i)
|
||||
{
|
||||
void *p = i < -1 ? g1 : i < 0 ? g2 : 1 < i ? g3 : 0 < i ? g5 : g4;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_alloca_local_cstoff (int n, int i)
|
||||
{
|
||||
int *a = __builtin_alloca (n); /* { dg-message "declared here" } */
|
||||
int *b = __builtin_alloca (n); /* { dg-message "declared here" } */
|
||||
int *p = i < 0 ? a : b;
|
||||
p += 1;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_alloca_local_cstoff (int n, int i)
|
||||
{
|
||||
int *a = __builtin_alloca (n); /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int *p = i < 0 ? a : b;
|
||||
p += 1;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_local_alloca_cstoff (int n, int i)
|
||||
{
|
||||
int a[2]; /* { dg-message "declared here" } */
|
||||
int *b = __builtin_alloca (n); /* { dg-message "declared here" } */
|
||||
int *p = i < 0 ? a : b;
|
||||
p += 1;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_locals_cstoff (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int *p = i < 0 ? a : b;
|
||||
p += 1;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_globals_3_locals_cstoff (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
|
||||
int *p = i < -1 ? a : i < 0 ? b : 1 < i ? g1 : 0 < i ? g2 : c;
|
||||
p += 1;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_3_globals_alloca_local_varoff (int n, int i, int j)
|
||||
{
|
||||
int *a = __builtin_alloca (n); /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
|
||||
int *p = i < -1 ? a : i < 0 ? b : 1 < i ? g1 : 0 < i ? g2 : g3;
|
||||
p += j;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_3_globals_2_locals_varoff (int i, int j)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
|
||||
int *p = i < -1 ? a : i < 0 ? b : 1 < i ? g1 : 0 < i ? g2 : g3;
|
||||
p += j;
|
||||
sink (p);
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
370
gcc/testsuite/gcc.dg/Wreturn-local-addr-4.c
Normal file
370
gcc/testsuite/gcc.dg/Wreturn-local-addr-4.c
Normal file
@ -0,0 +1,370 @@
|
||||
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
#define ATTR(...) __attribute__ ((__VA_ARGS__))
|
||||
|
||||
struct A { int a, b, c; };
|
||||
struct B { int a, b, c[]; };
|
||||
|
||||
extern int g1[5], g2[5], g3[5], g4[5], g5[5];
|
||||
|
||||
void sink (void*, ...);
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_locals (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
void *p = b;
|
||||
if (i < 0)
|
||||
p = a;
|
||||
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_locals_after_2_globals (int i, int j)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
|
||||
int *p;
|
||||
if (i < 0)
|
||||
p = g1;
|
||||
else
|
||||
p = g2;
|
||||
|
||||
sink (p);
|
||||
|
||||
if (j < 0)
|
||||
p = a;
|
||||
else
|
||||
p = b;
|
||||
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_3_locals (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = b + 1;
|
||||
if (i < 0)
|
||||
p = a;
|
||||
else if (0 < i)
|
||||
p = c + 2;
|
||||
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_5_locals (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
int d[4]; /* { dg-message "declared here" } */
|
||||
int e[5]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = &c[2];
|
||||
if (i < -1)
|
||||
p = a;
|
||||
else if (i < 0)
|
||||
p = &b[1];
|
||||
else if (1 < i)
|
||||
p = &e[4];
|
||||
else if (0 < i)
|
||||
p = &d[3];
|
||||
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_5_locals_switch (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
int d[4]; /* { dg-message "declared here" } */
|
||||
int e[5]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = 0;
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0: p = &a[1]; break;
|
||||
case 1: p = &b[2]; break;
|
||||
case 2: p = &c[3]; break;
|
||||
case 3: p = &d[4]; break;
|
||||
default: p = &e[5]; break;
|
||||
}
|
||||
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_1_global_4_locals (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
int d[4]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = c;
|
||||
if (i < -1)
|
||||
sink (p = a);
|
||||
else if (i < 0)
|
||||
sink (p = b);
|
||||
else if (1 < i)
|
||||
sink (p = g1);
|
||||
else if (0 < i)
|
||||
sink (p = d);
|
||||
|
||||
sink (p, a, b, c, d);
|
||||
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_1_global_4_locals_switch (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
int d[4]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = 0;
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0: p = &a[0]; break;
|
||||
case 1: p = &b[1]; break;
|
||||
case 2: p = &c[2]; break;
|
||||
case 3: p = &d[3]; break;
|
||||
}
|
||||
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_globals_3_locals (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = c;
|
||||
if (i < -1)
|
||||
p = a;
|
||||
else if (i < 0)
|
||||
p = b;
|
||||
else if (1 < i)
|
||||
p = g1;
|
||||
else if (0 < i)
|
||||
p = g2;
|
||||
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_3_globals_2_locals (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = g3;
|
||||
if (i < -1)
|
||||
p = a;
|
||||
else if (i < 0)
|
||||
p = b;
|
||||
else if (1 < i)
|
||||
p = g1;
|
||||
else if (0 < i)
|
||||
p = g2;
|
||||
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_4_globals_1_local (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
|
||||
void *p = g3;
|
||||
if (i < -1)
|
||||
p = a;
|
||||
else if (i < 0)
|
||||
p = g1;
|
||||
else if (1 < i)
|
||||
p = g2;
|
||||
else if (0 < i)
|
||||
p = g4;
|
||||
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_all_globals (int i)
|
||||
{
|
||||
void *p = g4;
|
||||
if (i < -1)
|
||||
p = g1;
|
||||
else if (i < 0)
|
||||
p = g2;
|
||||
else if (1 < i)
|
||||
p = g3;
|
||||
else if (0 < i)
|
||||
p = g5;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_alloca_local_cstoff (int n, int i)
|
||||
{
|
||||
int *a = __builtin_alloca (n); /* { dg-message "declared here" } */
|
||||
int *b = __builtin_alloca (n); /* { dg-message "declared here" } */
|
||||
int *p = i < 0 ? a : b;
|
||||
|
||||
p += 1;
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_alloca_local_cstoff (int n, int i)
|
||||
{
|
||||
int *a = __builtin_alloca (n); /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
|
||||
int *p = b;
|
||||
if (i < 0)
|
||||
p = a;
|
||||
|
||||
p += 1;
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_local_alloca_cstoff (int n, int i)
|
||||
{
|
||||
int a[2]; /* { dg-message "declared here" } */
|
||||
int *b = __builtin_alloca (n); /* { dg-message "declared here" } */
|
||||
int *p = b;
|
||||
if (i < 0)
|
||||
p = a;
|
||||
|
||||
p += 1;
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_locals_cstoff (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
|
||||
int *p = b;
|
||||
if (i < 0)
|
||||
p = a;
|
||||
|
||||
p += 1;
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function returns address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_2_globals_3_locals_cstoff (int i)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
int c[3]; /* { dg-message "declared here" } */
|
||||
|
||||
int *p = c;
|
||||
if (i < -1)
|
||||
p = a;
|
||||
else if (i < 0)
|
||||
p = b;
|
||||
else if (1 < i)
|
||||
p = g1;
|
||||
else if (0 < i)
|
||||
p = g2;
|
||||
|
||||
p += 1;
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_3_globals_alloca_local_varoff (int n, int i, int j)
|
||||
{
|
||||
int *a = __builtin_alloca (n); /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
|
||||
int *p = g3;
|
||||
if (i < -1)
|
||||
p = a;
|
||||
else if (i < 0)
|
||||
p = b;
|
||||
else if (1 < i)
|
||||
p = g1;
|
||||
else if (0 < i)
|
||||
p = g2;
|
||||
|
||||
p += j;
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
||||
ATTR (noipa) void*
|
||||
return_3_globals_2_locals_varoff (int i, int j)
|
||||
{
|
||||
int a[1]; /* { dg-message "declared here" } */
|
||||
int b[2]; /* { dg-message "declared here" } */
|
||||
|
||||
int *p = g3;
|
||||
if (i < -1)
|
||||
p = a;
|
||||
else if (i < 0)
|
||||
p = b;
|
||||
else if (1 < i)
|
||||
p = g1;
|
||||
else if (0 < i)
|
||||
p = g2;
|
||||
|
||||
p += j;
|
||||
sink (p);
|
||||
|
||||
return p; /* { dg-warning "function may return address of local" } */
|
||||
}
|
||||
|
40
gcc/testsuite/gcc.dg/Wreturn-local-addr-5.c
Normal file
40
gcc/testsuite/gcc.dg/Wreturn-local-addr-5.c
Normal file
@ -0,0 +1,40 @@
|
||||
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
void sink (void*);
|
||||
|
||||
void* loop_idx (int x)
|
||||
{
|
||||
char a[32]; /* { dg-message "declared here" } */
|
||||
char *p = a;
|
||||
|
||||
sink (a);
|
||||
|
||||
int i;
|
||||
for (i = 0; i != 32; ++i)
|
||||
if (p[i] == x)
|
||||
break;
|
||||
|
||||
p = i < 32 ? &p[i] : 0;
|
||||
return p; /* { dg-warning "may return address of local variable" } */
|
||||
}
|
||||
|
||||
|
||||
void* loop_ptr (int i, int x)
|
||||
{
|
||||
char a[32]; /* { dg-message "declared here" } */
|
||||
char *p;
|
||||
|
||||
sink (a);
|
||||
|
||||
/* The warning for the statement below would ideally be a "returns"
|
||||
because it definitely returns the address of a, but when both
|
||||
returns get merged into one we end up with a "may return". */
|
||||
for (p = a; *p; ++p)
|
||||
if (*p == x)
|
||||
return p; /* { dg-warning "(returns|may return) address of local variable" "missing location" { xfail *-*-* } } */
|
||||
/* { dg-warning "(returns|may return) address of local variable" "pr90735" { target *-*-* } 0 } */
|
||||
|
||||
return 0;
|
||||
}
|
203
gcc/testsuite/gcc.dg/Wreturn-local-addr-6.c
Normal file
203
gcc/testsuite/gcc.dg/Wreturn-local-addr-6.c
Normal file
@ -0,0 +1,203 @@
|
||||
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
void* memcpy (void*, const void*, size_t);
|
||||
void* mempcpy (void*, const void*, size_t);
|
||||
void* memmove (void*, const void*, size_t);
|
||||
|
||||
char* stpcpy (char*, const char*);
|
||||
char* stpncpy (char*, const char*, size_t);
|
||||
|
||||
size_t strlen (const char*);
|
||||
size_t strnlen (const char*, size_t);
|
||||
|
||||
char* strcat (char*, const char*);
|
||||
char* strncat (char*, const char*, size_t);
|
||||
|
||||
char* strcpy (char*, const char*);
|
||||
char* strncpy (char*, const char*, size_t);
|
||||
|
||||
char* strdup (const char*);
|
||||
|
||||
char* strchr (const char*, int);
|
||||
char* strrchr (const char*, int);
|
||||
char* strstr (const char*, const char*);
|
||||
|
||||
void sink (void*, ...);
|
||||
|
||||
|
||||
void* return_memcpy (const void *s, unsigned n)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
void *p = memcpy (a, s, n);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
void* return_memcpy_cst (const void *s, unsigned n)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
void *p = memcpy (a + 1, s, n);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
void* return_memcpy_var (const void *s, unsigned n, int i)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
void *p = memcpy (a + i, s, n);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
void* return_mempcpy (const void *s, unsigned n)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
void *p = mempcpy (a, s, n);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
void* return_memmove_cst (unsigned n)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
sink (a);
|
||||
void *p = memmove (a + 1, a, n);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
void* return_memmove_var (unsigned n, int i)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
sink (a);
|
||||
void *p = memmove (a + i, a, n);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
char* return_stpcpy (unsigned n, const char *s)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
char *p = stpcpy (a, s);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
char* return_stpncpy (unsigned n, const char *s)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
char *p = stpncpy (a, s, n);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
char* return_strcat (unsigned n, const char *s)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
sink (a);
|
||||
char *p = strcat (a, s);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
char* return_strncat (unsigned n, const char *s)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
sink (a);
|
||||
char *p = strncat (a, s, n);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
|
||||
}
|
||||
char* return_strcpy (unsigned n, const char *s)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
char *p = strcpy (a, s);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
char* return_strcpy_plus_strlen (unsigned n, const char *s)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
char *p = strcpy (a, s);
|
||||
sink (p);
|
||||
p += strlen (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
char* return_strcpy_cst_plus_strlen (unsigned n, const char *s)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
sink (a);
|
||||
char *p = strcpy (a + 1, s);
|
||||
sink (p);
|
||||
p += strlen (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
char* return_strcpy_var_plus_strlen (unsigned n, const char *s, int i)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
sink (a);
|
||||
char *p = strcpy (a + i, s);
|
||||
sink (p);
|
||||
p += strlen (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
char* return_strncpy (unsigned n, const char *s)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
char *p = strncpy (a, s, n);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
char* return_strncpy_plus_strnlen (unsigned n, const char *s)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
char *p = strncpy (a, s, n);
|
||||
p += strnlen (p, n);
|
||||
sink (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
char* return_strdup (unsigned n)
|
||||
{
|
||||
char a[n];
|
||||
sink (a);
|
||||
char *p = strdup (a);
|
||||
return p;
|
||||
}
|
||||
|
||||
char* return_strchr (unsigned n, int c)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
sink (a);
|
||||
char *p = strchr (a, c);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
char* return_strstr (unsigned n, const char *s)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
sink (a);
|
||||
char *p = strstr (a, s);
|
||||
if (p)
|
||||
p += strlen (p);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
}
|
||||
|
||||
char* return_strrchr (unsigned n, int c)
|
||||
{
|
||||
char a[n]; /* { dg-message "declared here" } */
|
||||
sink (a);
|
||||
char *p = strrchr (a, c);
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
|
||||
|
||||
}
|
50
gcc/testsuite/gcc.dg/Wreturn-local-addr-7.c
Normal file
50
gcc/testsuite/gcc.dg/Wreturn-local-addr-7.c
Normal file
@ -0,0 +1,50 @@
|
||||
/* Test to verify that a PHI with a COND_EXPR argument in a return
|
||||
statement is handled correctly.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
extern struct S s;
|
||||
|
||||
void* f (int n)
|
||||
{
|
||||
void *p;
|
||||
int x = 0;
|
||||
|
||||
for (int i = n; i >= 0; i--)
|
||||
{
|
||||
p = &s;
|
||||
if (p == (void*)-1)
|
||||
x = 1;
|
||||
else if (p)
|
||||
return p;
|
||||
}
|
||||
|
||||
/* The return statement below ends up with the following IL:
|
||||
<bb 6> [local count: 59055800]:
|
||||
# x_10 = PHI <1(5), 0(2)>
|
||||
_5 = x_10 != 0 ? -1B : 0B;
|
||||
|
||||
<bb 7> [local count: 114863532]:
|
||||
# _3 = PHI <&s(4), _5(6), &s(3)>
|
||||
return _3; */
|
||||
return x ? (void*)-1 : 0;
|
||||
}
|
||||
|
||||
void* g (int n)
|
||||
{
|
||||
void *p;
|
||||
int x = 0; /* { dg-message "declared here" } */
|
||||
|
||||
for (int i = n; i >= 0; i--)
|
||||
{
|
||||
p = &s;
|
||||
if (p == (void*)-1)
|
||||
x = 1;
|
||||
else if (p)
|
||||
return p;
|
||||
}
|
||||
|
||||
/* The return statement below does not reference a COND_EXPR argument. */
|
||||
return x ? &x : 0; /* { dg-warning "may return address of local variable" "missing location" { xfail *-*-* } } */
|
||||
/* { dg-warning "may return address of local variable" "pr90735" { target *-*-* } 0 } */
|
||||
}
|
88
gcc/testsuite/gcc.dg/Wreturn-local-addr-8.c
Normal file
88
gcc/testsuite/gcc.dg/Wreturn-local-addr-8.c
Normal file
@ -0,0 +1,88 @@
|
||||
/* Test to verify that a MAX_EXPR and MIN_EXPR in a return statement
|
||||
is handled correctly and that all local variables whose address
|
||||
is or may be returned are identified.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
char* sink (char*, ...);
|
||||
|
||||
void* test_max_2 (void)
|
||||
{
|
||||
char c; /* { dg-message "declared here" } */
|
||||
|
||||
char *p = sink (&c);
|
||||
|
||||
void *q = p > &c ? p : &c; /* MAX_EXPR */
|
||||
return q; /* { dg-warning "\\\[-Wreturn-local-addr" } */
|
||||
}
|
||||
|
||||
void* test_max_3 (void)
|
||||
{
|
||||
char c; /* { dg-message "declared here" } */
|
||||
char d; /* { dg-message "declared here" } */
|
||||
|
||||
char *p = sink (&c, &d);
|
||||
|
||||
void *q = p < &c ? &c < &d ? &d : &c : p;
|
||||
return q; /* { dg-warning "\\\[-Wreturn-local-addr" } */
|
||||
}
|
||||
|
||||
void* test_min_2 (void)
|
||||
{
|
||||
char c; /* { dg-message "declared here" } */
|
||||
|
||||
char *p = sink (&c);
|
||||
|
||||
void *q = p < &c ? p : &c; /* MIN_EXPR" */
|
||||
return q; /* { dg-warning "\\\[-Wreturn-local-addr" } */
|
||||
}
|
||||
|
||||
void* test_min_3 (void)
|
||||
{
|
||||
char c; /* { dg-message "declared here" } */
|
||||
char d; /* { dg-message "declared here" } */
|
||||
|
||||
char *p = sink (&c, &d);
|
||||
|
||||
void *q = p > &c ? &c > &d ? &d : &c : p;
|
||||
return q; /* { dg-warning "\\\[-Wreturn-local-addr" } */
|
||||
}
|
||||
|
||||
void* test_min_2_phi (int i)
|
||||
{
|
||||
char a; /* { dg-message "declared here" } */
|
||||
|
||||
char *p = &a;
|
||||
char *q = sink (&a);
|
||||
p = p < q ? p : q;
|
||||
if (i == 1)
|
||||
return p;
|
||||
/* { dg-warning "may return address of local variable" "missing location" { xfail *-*-* } } */
|
||||
else
|
||||
return q;
|
||||
}
|
||||
|
||||
void* test_min_3_phi (int i)
|
||||
{
|
||||
char a; /* { dg-message "declared here" } */
|
||||
char b; /* { dg-message "declared here" } */
|
||||
|
||||
char *p0 = &a;
|
||||
char *p1 = &b;
|
||||
char *p2 = sink (&a, &b);
|
||||
char *p3 = sink (&a, &b);
|
||||
|
||||
char *p4 = p2 < p0 ? p2 : p0;
|
||||
char *p5 = p3 < p1 ? p3 : p1;
|
||||
|
||||
if (i == 1)
|
||||
/* { dg-warning "may return address of local variable" "missing location" { xfail *-*-* } } */
|
||||
return p4;
|
||||
else
|
||||
/* { dg-warning "may return address of local variable" "missing location" { xfail *-*-* } } */
|
||||
return p5;
|
||||
}
|
||||
|
||||
/* The directive below "swallows" warnings for both test_min_2_phi
|
||||
and test_min_3_phi.
|
||||
{ dg-warning "may return address of local variable" "pr90735" { target *-*-* } 0 } */
|
73
gcc/testsuite/gcc.dg/Wreturn-local-addr-9.c
Normal file
73
gcc/testsuite/gcc.dg/Wreturn-local-addr-9.c
Normal file
@ -0,0 +1,73 @@
|
||||
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
|
||||
Test derived from gcc.c-torture/execute/20071108-1.c. It shows
|
||||
a false positive at -Os caused by the jump threading/vrp1 pass.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-Os -fdump-tree-optimized" } */
|
||||
|
||||
struct S
|
||||
{
|
||||
int i;
|
||||
};
|
||||
|
||||
void* f (void);
|
||||
|
||||
__attribute__ ((noinline))
|
||||
struct S* g (int i)
|
||||
{
|
||||
struct S *p = f (), q;
|
||||
|
||||
if (p == 0)
|
||||
p = &q;
|
||||
|
||||
p->i = i;
|
||||
|
||||
if (p == &q)
|
||||
p = 0;
|
||||
|
||||
/* With -Os the warning pass sees:
|
||||
|
||||
...
|
||||
<bb 4>
|
||||
# p_1 = PHI <&q(2), p_5(3)>
|
||||
p_1->i = i_6(D);
|
||||
if (&q == p_1)
|
||||
goto <bb 6>; [14.90%]
|
||||
else
|
||||
goto <bb 5>; [85.10%]
|
||||
|
||||
<bb 5>
|
||||
|
||||
<bb 6>
|
||||
# p_2 = PHI <0B(4), p_1(5)>
|
||||
q ={v} {CLOBBER};
|
||||
return p_2;
|
||||
}
|
||||
|
||||
which leads to: */
|
||||
return p; /* { dg-bogus "may return address of local variable" "" { xfail *-*-* } } */
|
||||
|
||||
/* Whereas as -O2 the pass sees:
|
||||
|
||||
<bb 2>
|
||||
p_5 = f ();
|
||||
if (p_5 == 0B)
|
||||
goto <bb 4>; [30.00%]
|
||||
else
|
||||
goto <bb 3>; [70.00%]
|
||||
|
||||
<bb 3>
|
||||
# p_2 = PHI <0B(5), p_5(4)>
|
||||
q ={v} {CLOBBER};
|
||||
return p_2;
|
||||
|
||||
<bb 4>
|
||||
p_5->i = i_6(D);
|
||||
goto <bb 3>; [100.00%]
|
||||
|
||||
<bb 5>
|
||||
q.i = i_6(D);
|
||||
goto <bb 3>; [100.00%]
|
||||
}
|
||||
|
||||
and no warning. */
|
||||
}
|
@ -10,3 +10,5 @@ int main(void)
|
||||
int var, *p = &var;
|
||||
return (double)(uintptr_t)(p);
|
||||
}
|
||||
|
||||
/* { dg-prune-output "-Wreturn-local-addr" } */
|
||||
|
@ -16,3 +16,5 @@ foo (int a, int *b, int *c, int *d)
|
||||
r[i] = 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* { dg-prune-output "-Wreturn-local-addr" } */
|
||||
|
@ -12,7 +12,7 @@ int *foo (int bogus, int n)
|
||||
p = &a[2];
|
||||
else
|
||||
p = &i;
|
||||
return p;
|
||||
return p; /* { dg-warning "\\\[-Wreturn-local-addr" } */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump "Deleted dead store" "dse1" } } */
|
||||
|
@ -41,3 +41,5 @@ f5 (void)
|
||||
int c[64] = {}, d[64] = {};
|
||||
return (__UINTPTR_TYPE__) &c[64] != (__UINTPTR_TYPE__) &d[0];
|
||||
}
|
||||
|
||||
/* { dg-prune-output "-Wreturn-local-addr" } */
|
||||
|
@ -13,3 +13,5 @@ inline void *t (void)
|
||||
{
|
||||
return q (); /* { dg-message "called from here" } */
|
||||
}
|
||||
|
||||
/* { dg-prune-output "-Wreturn-local-addr" } */
|
||||
|
@ -23,6 +23,8 @@ a copy of the GCC Runtime Library Exception along with this program;
|
||||
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#pragma GCC optimize ("no-isolate-erroneous-paths-dereference")
|
||||
|
||||
/* powerpc 32-bit not supported. */
|
||||
#if !defined __powerpc__ || defined __powerpc64__
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user