Message ID | 52CBF834.3040004@redhat.com |
---|---|
State | New |
Headers | show |
On Tue, Jan 07, 2014 at 01:51:00PM +0100, Florian Weimer wrote: > +static bool > +stack_protect_return_slot_p () > +{ > + basic_block bb; > + > + FOR_ALL_BB_FN (bb, cfun) > + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); > + !gsi_end_p (gsi); gsi_next (&gsi)) > + { > + gimple stmt = gsi_stmt (gsi); > + if (is_gimple_call (stmt) && gimple_call_return_slot_opt_p (stmt)) I have to repeat, this is not the right test, it really is just an optimization hint, nothing else. Just look where it is set (unless it is C++ where some NRV is performed in the FE as mandated by the languages) - in pass_return_slot, which is an optimization pass, not run with -O0 or -Og at all. So, you'd stack protect something only if not -O0 or -Og, that is just wrong, and only if it was suitable for NRV. You really want to protect all functions that call something that is returned by hidden reference. Jakub
On 01/07/2014 02:07 PM, Jakub Jelinek wrote: > On Tue, Jan 07, 2014 at 01:51:00PM +0100, Florian Weimer wrote: >> +static bool >> +stack_protect_return_slot_p () >> +{ >> + basic_block bb; >> + >> + FOR_ALL_BB_FN (bb, cfun) >> + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); >> + !gsi_end_p (gsi); gsi_next (&gsi)) >> + { >> + gimple stmt = gsi_stmt (gsi); >> + if (is_gimple_call (stmt) && gimple_call_return_slot_opt_p (stmt)) > > I have to repeat, this is not the right test, it really is just an > optimization hint, nothing else. Just look where it is set (unless it is > C++ where some NRV is performed in the FE as mandated by the languages) > - in pass_return_slot, which is an optimization pass, not run with -O0 or > -Og at all. I don't understand. The corresponding tree flag is set by gimplify_modify_expr_rhs, in the CALL_EXPR case: if (use_target) { CALL_EXPR_RETURN_SLOT_OPT (*from_p) = 1; mark_addressable (*to_p); } Isn't this an integral part of the gimplifier which also runs at -O0? I certainly see the flag in the generated gimple at -O0, with the amended C test case: foo11 () { int D.2037; struct B D.2038; D.2038 = global3 (); [return slot optimization] D.2037 = D.2038.a1; return D.2037; } As I said, I think the function gimple_call_return_slot_opt_p is misnamed because it's not actually an optimization.
On Tue, Jan 07, 2014 at 02:27:04PM +0100, Florian Weimer wrote: > gimplify_modify_expr_rhs, in the CALL_EXPR case: > > if (use_target) > { > CALL_EXPR_RETURN_SLOT_OPT (*from_p) = 1; > mark_addressable (*to_p); > } Yeah, that sets it in some cases too, not in other testcases. Just look at how the flag is used when actually expanding it: if (target && MEM_P (target) && CALL_EXPR_RETURN_SLOT_OPT (exp)) structure_value_addr = XEXP (target, 0); else { /* For variable-sized objects, we must be called with a target specified. If we were to allocate space on the stack here, we would have no way of knowing when to free it. */ rtx d = assign_temp (rettype, 1, 1); structure_value_addr = XEXP (d, 0); target = 0; } so, if it is set, the address of the var on the LHS is passed to the function as hidden argument, if it is not set, we pass address of a stack temporary instead. Both the automatic var and the stack temporary can overflow, if the callee does something wrong. Jakub
gcc/ 2014-01-07 Florian Weimer <fweimer@redhat.com> * cfgexpand.c (stack_protect_decl_p): New function, extracted from expand_used_vars. (stack_protect_return_slot_p): New function. (expand_used_vars): Call stack_protect_decl_p and stack_protect_return_slot_p for -fstack-protector-strong. gcc/testsuite/ 2014-01-07 Florian Weimer <fweimer@redhat.com> * gcc.dg/fstack-protector-strong.c: Add coverage for return slots. * g++.dg/fstack-protector-strong.C: Likewise. Index: gcc/cfgexpand.c =================================================================== --- gcc/cfgexpand.c (revision 206311) +++ gcc/cfgexpand.c (working copy) @@ -1599,6 +1599,47 @@ return 0; } +/* Check if the current function has local referenced variables that + have their addresses taken, contain an array, or are arrays. */ + +static bool +stack_protect_decl_p () +{ + unsigned i; + tree var; + + FOR_EACH_LOCAL_DECL (cfun, i, var) + if (!is_global_var (var)) + { + tree var_type = TREE_TYPE (var); + if (TREE_CODE (var) == VAR_DECL + && (TREE_CODE (var_type) == ARRAY_TYPE + || TREE_ADDRESSABLE (var) + || (RECORD_OR_UNION_TYPE_P (var_type) + && record_or_union_type_has_array_p (var_type)))) + return true; + } + return false; +} + +/* Check if the current function has calls that use a return slot. */ + +static bool +stack_protect_return_slot_p () +{ + basic_block bb; + + FOR_ALL_BB_FN (bb, cfun) + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + if (is_gimple_call (stmt) && gimple_call_return_slot_opt_p (stmt)) + return true; + } + return false; +} + /* Expand all variables used in the function. */ static rtx @@ -1669,22 +1710,8 @@ pointer_map_destroy (ssa_name_decls); if (flag_stack_protect == SPCT_FLAG_STRONG) - FOR_EACH_LOCAL_DECL (cfun, i, var) - if (!is_global_var (var)) - { - tree var_type = TREE_TYPE (var); - /* Examine local referenced variables that have their addresses taken, - contain an array, or are arrays. */ - if (TREE_CODE (var) == VAR_DECL - && (TREE_CODE (var_type) == ARRAY_TYPE - || TREE_ADDRESSABLE (var) - || (RECORD_OR_UNION_TYPE_P (var_type) - && record_or_union_type_has_array_p (var_type)))) - { - gen_stack_protect_signal = true; - break; - } - } + gen_stack_protect_signal + = stack_protect_decl_p () || stack_protect_return_slot_p (); /* At this point all variables on the local_decls with TREE_USED set are not associated with any block scope. Lay them out. */ Index: gcc/testsuite/g++.dg/fstack-protector-strong.C =================================================================== --- gcc/testsuite/g++.dg/fstack-protector-strong.C (revision 206311) +++ gcc/testsuite/g++.dg/fstack-protector-strong.C (working copy) @@ -32,4 +32,39 @@ return global_func (a); } -/* { dg-final { scan-assembler-times "stack_chk_fail" 2 } } */ +/* Frame addressed exposed through return slot. */ + +struct B +{ + /* Discourage passing this struct in registers. */ + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10; +}; + +B global_func (); +void noop (); + +int foo3 () +{ + return global_func ().a1; +} + +int foo4 () +{ + try { + noop (); + return 0; + } catch (...) { + return global_func ().a1; + } +} + +int foo5 () +{ + try { + return global_func ().a1; + } catch (...) { + return 0; + } +} + +/* { dg-final { scan-assembler-times "stack_chk_fail" 5 } } */ Index: gcc/testsuite/gcc.dg/fstack-protector-strong.c =================================================================== --- gcc/testsuite/gcc.dg/fstack-protector-strong.c (revision 206311) +++ gcc/testsuite/gcc.dg/fstack-protector-strong.c (working copy) @@ -131,4 +131,17 @@ return bb.three; } -/* { dg-final { scan-assembler-times "stack_chk_fail" 10 } } */ +struct B +{ + /* Discourage passing this struct in registers. */ + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10; +}; + +struct B global3 (void); + +int foo11 () +{ + return global3 ().a1; +} + +/* { dg-final { scan-assembler-times "stack_chk_fail" 11 } } */