Message ID | 20121017093555.GT584@tucnak.redhat.com |
---|---|
State | New |
Headers | show |
Looks ok to me for the branch. thanks, David On Wed, Oct 17, 2012 at 2:35 AM, Jakub Jelinek <jakub@redhat.com> wrote: > On Tue, Oct 16, 2012 at 03:56:31PM -0700, Xinliang David Li wrote: >> >> 1) I am not sure if the stack slot sharing is handled correctly. If I >> >> read the code correctly, the redzone var will be only created for the >> >> representative variable in a partition -- will this lead to false >> >> negatives? As I asked before, should stack slot sharing even turned >> >> on? >> > >> > I thought we don't merge stack slots if the vars have different size, >> > apparently I've been wrong about that. So I'll make sure those aren't >> > shared with flag_asan. When the size is the same, there should be no false >> > negatives. >> > >> >> 2) Performance tuning -- it is probably better to skip those variables >> >> that are compiler generated -- is there any point guarding them? >> > >> > In my (admittedly very limited) testing only at -O0 some compiler generated >> > vars (SSA_NAMEs) got guards. The trouble here is -fasan -fstack-protector >> > combination. I wonder if there can be any DECL_ARTIFICIAL (or non-VAR_DECL/ >> > RESULT_DECL) decls for which stack_protect_decl_phase returns 1 or 2. >> > If it is unlikely, the best might be to protect all phase 1 and 2 vars and >> > if flag_asan call expand_stack_vars once more for partitions where any of >> > the protected vars are user vars, and finally the current expand_stack_vars >> > (NULL) which would not do any asan protection. >> > >> >> Do we expect people to use -fasan together with -fstack-protector? Is >> it enough to just skip those DECL_ARTIFICIAL ones? most of the >> ssa_name var decls are already skipped already. > > Why can't they? -fstack-protector is for some distros even on by default, > or others turn it on e.g. in the standard CFLAGS/CXXFLAGS for building > packages. And -fstack-protector has a value even with -fasan, e.g. if you > call some function not instrumented with -fasan (some other library, etc.) > and pass an address of an automatic array etc. to it. > > Anyway, here is (again, very lightly tested just by skimming assembly on a > few testcases) an updated patch that won't do stack slot sharing for > variables with different sizes, and will not put artificial vars/SSA_NAMEs > into protected stack block, but below it, unless -fstack-protector wants to > push those high in the frame. Also, it attempts to find best var name > for each partition, say if some partition contains both some artificial > variable, some variable without name and some with name, it will pick the > non-artificial one with name. > > Ok for asan? > > 2012-10-17 Jakub Jelinek <jakub@redhat.com> > > * Makefile.in (asan.o): Depend on $(EXPR_H) $(OPTABS_H). > (cfgexpand.o): Depend on asan.h. > * asan.c: Include expr.h and optabs.h. > (asan_shadow_set): New variable. > (asan_shadow_cst, asan_emit_stack_protection): New functions. > (asan_init_shadow_ptr_types): Initialize also asan_shadow_set. > * cfgexpand.c: Include asan.h. Define HOST_WIDE_INT heap vector. > (partition_stack_vars): If i is large alignment and j small > alignment or vice versa, break out of the loop instead of continue, > and put the test earlier. If flag_asan, break out of the loop > if for small alignment size is different. > (struct stack_vars_data): New type. > (expand_stack_vars): Add DATA argument. Change PRED type to > function taking size_t argument instead of tree. Adjust pred calls. > Fill DATA in and add needed padding in between variables if -fasan. > (defer_stack_allocation): Defer everything for flag_asan. > (stack_protect_decl_phase_1, stack_protect_decl_phase_2): Take > size_t index into stack_vars array instead of the decl directly. > (asan_decl_phase_3): New function. > (expand_used_vars): Return var destruction sequence. Adjust > expand_stack_vars calls, add another one for flag_asan. Call > asan_emit_stack_protection if expand_stack_vars added anything > to the vectors. > (expand_gimple_basic_block): Add disable_tail_calls argument. > (gimple_expand_cfg): Pass true to it if expand_used_vars returned > non-NULL. Emit the sequence returned by expand_used_vars after > return_label. > * asan.h (asan_emit_stack_protection): New prototype. > (asan_shadow_set): New decl. > (ASAN_RED_ZONE_SIZE, ASAN_STACK_MAGIC_LEFT, ASAN_STACK_MAGIC_MIDDLE, > ASAN_STACK_MAGIC_RIGHT, ASAN_STACK_FRAME_MAGIC): Define. > (asan_protect_stack_decl): New inline. > * toplev.c (process_options): Also disable -fasan on > !FRAME_GROWS_DOWNWARDS targets. > > --- gcc/Makefile.in.jj 2012-10-12 23:30:31.635325772 +0200 > +++ gcc/Makefile.in 2012-10-15 09:40:40.287706610 +0200 > @@ -2204,7 +2204,7 @@ stor-layout.o : stor-layout.c $(CONFIG_H > asan.o : asan.c asan.h $(CONFIG_H) pointer-set.h \ > $(SYSTEM_H) $(TREE_H) $(GIMPLE_H) \ > output.h $(DIAGNOSTIC_H) coretypes.h $(TREE_DUMP_H) $(FLAGS_H) \ > - tree-pretty-print.h $(TARGET_H) > + tree-pretty-print.h $(TARGET_H) $(EXPR_H) $(OPTABS_H) > tree-ssa-tail-merge.o: tree-ssa-tail-merge.c \ > $(SYSTEM_H) $(CONFIG_H) coretypes.h $(TM_H) $(BITMAP_H) \ > $(FLAGS_H) $(TM_P_H) $(BASIC_BLOCK_H) \ > @@ -3074,7 +3074,7 @@ cfgexpand.o : cfgexpand.c $(TREE_FLOW_H) > $(DIAGNOSTIC_H) toplev.h $(DIAGNOSTIC_CORE_H) $(BASIC_BLOCK_H) $(FLAGS_H) debug.h $(PARAMS_H) \ > value-prof.h $(TREE_INLINE_H) $(TARGET_H) $(SSAEXPAND_H) $(REGS_H) \ > $(GIMPLE_PRETTY_PRINT_H) $(BITMAP_H) sbitmap.h \ > - $(INSN_ATTR_H) $(CFGLOOP_H) > + $(INSN_ATTR_H) $(CFGLOOP_H) asan.h > cfgrtl.o : cfgrtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_ERROR_H) \ > $(FLAGS_H) insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h \ > $(FUNCTION_H) $(EXCEPT_H) $(TM_P_H) $(INSN_ATTR_H) \ > --- gcc/toplev.c.jj 2012-10-12 23:30:31.717325313 +0200 > +++ gcc/toplev.c 2012-10-15 09:40:03.540923935 +0200 > @@ -1544,7 +1544,9 @@ process_options (void) > } > > /* Address Sanitizer needs porting to each target architecture. */ > - if (flag_asan && targetm.asan_shadow_offset == NULL) > + if (flag_asan > + && (targetm.asan_shadow_offset == NULL > + || !FRAME_GROWS_DOWNWARD)) > { > warning (0, "-fasan not supported for this target"); > flag_asan = 0; > --- gcc/asan.c.jj 2012-10-12 23:31:55.423856689 +0200 > +++ gcc/asan.c 2012-10-16 12:18:41.414029844 +0200 > @@ -43,6 +43,8 @@ along with GCC; see the file COPYING3. > #include "asan.h" > #include "gimple-pretty-print.h" > #include "target.h" > +#include "expr.h" > +#include "optabs.h" > > /* > AddressSanitizer finds out-of-bounds and use-after-free bugs > @@ -79,10 +81,195 @@ along with GCC; see the file COPYING3. > to create redzones for stack and global object and poison them. > */ > > +alias_set_type asan_shadow_set = -1; > + > /* Pointer types to 1 resp. 2 byte integers in shadow memory. A separate > alias set is used for all shadow memory accesses. */ > static GTY(()) tree shadow_ptr_types[2]; > > +/* Return a CONST_INT representing 4 subsequent shadow memory bytes. */ > + > +static rtx > +asan_shadow_cst (unsigned char shadow_bytes[4]) > +{ > + int i; > + unsigned HOST_WIDE_INT val = 0; > + gcc_assert (WORDS_BIG_ENDIAN == BYTES_BIG_ENDIAN); > + for (i = 0; i < 4; i++) > + val |= (unsigned HOST_WIDE_INT) shadow_bytes[BYTES_BIG_ENDIAN ? 3 - i : i] > + << (BITS_PER_UNIT * i); > + return GEN_INT (trunc_int_for_mode (val, SImode)); > +} > + > +/* Insert code to protect stack vars. The prologue sequence should be emitted > + directly, epilogue sequence returned. BASE is the register holding the > + stack base, against which OFFSETS array offsets are relative to, OFFSETS > + array contains pairs of offsets in reverse order, always the end offset > + of some gap that needs protection followed by starting offset, > + and DECLS is an array of representative decls for each var partition. > + LENGTH is the length of the OFFSETS array, DECLS array is LENGTH / 2 - 1 > + elements long (OFFSETS include gap before the first variable as well > + as gaps after each stack variable). */ > + > +rtx > +asan_emit_stack_protection (rtx base, HOST_WIDE_INT *offsets, tree *decls, > + int length) > +{ > + rtx shadow_base, shadow_mem, ret, mem; > + unsigned char shadow_bytes[4]; > + HOST_WIDE_INT base_offset = offsets[length - 1], offset, prev_offset; > + HOST_WIDE_INT last_offset, last_size; > + int l; > + unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT; > + static pretty_printer pp; > + static bool pp_initialized; > + const char *buf; > + size_t len; > + tree str_cst; > + > + /* First of all, prepare the description string. */ > + if (!pp_initialized) > + { > + pp_construct (&pp, /* prefix */NULL, /* line-width */0); > + pp_initialized = true; > + } > + pp_clear_output_area (&pp); > + if (DECL_NAME (current_function_decl)) > + pp_base_tree_identifier (&pp, DECL_NAME (current_function_decl)); > + else > + pp_string (&pp, "<unknown>"); > + pp_space (&pp); > + pp_decimal_int (&pp, length / 2 - 1); > + pp_space (&pp); > + for (l = length - 2; l; l -= 2) > + { > + tree decl = decls[l / 2 - 1]; > + pp_wide_integer (&pp, offsets[l] - base_offset); > + pp_space (&pp); > + pp_wide_integer (&pp, offsets[l - 1] - offsets[l]); > + pp_space (&pp); > + if (DECL_P (decl) && DECL_NAME (decl)) > + { > + pp_decimal_int (&pp, IDENTIFIER_LENGTH (DECL_NAME (decl))); > + pp_space (&pp); > + pp_base_tree_identifier (&pp, DECL_NAME (decl)); > + } > + else > + pp_string (&pp, "9 <unknown>"); > + pp_space (&pp); > + } > + buf = pp_base_formatted_text (&pp); > + len = strlen (buf); > + str_cst = build_string (len + 1, buf); > + TREE_TYPE (str_cst) > + = build_array_type (char_type_node, build_index_type (size_int (len))); > + TREE_READONLY (str_cst) = 1; > + TREE_STATIC (str_cst) = 1; > + str_cst = build1 (ADDR_EXPR, build_pointer_type (char_type_node), str_cst); > + > + /* Emit the prologue sequence. */ > + base = expand_binop (Pmode, add_optab, base, GEN_INT (base_offset), > + NULL_RTX, 1, OPTAB_DIRECT); > + mem = gen_rtx_MEM (ptr_mode, base); > + emit_move_insn (mem, GEN_INT (ASAN_STACK_FRAME_MAGIC)); > + mem = adjust_address (mem, VOIDmode, GET_MODE_SIZE (ptr_mode)); > + emit_move_insn (mem, expand_normal (str_cst)); > + shadow_base = expand_binop (Pmode, lshr_optab, base, > + GEN_INT (ASAN_SHADOW_SHIFT), > + NULL_RTX, 1, OPTAB_DIRECT); > + shadow_base = expand_binop (Pmode, add_optab, shadow_base, > + GEN_INT (targetm.asan_shadow_offset ()), > + NULL_RTX, 1, OPTAB_DIRECT); > + gcc_assert (asan_shadow_set != -1 > + && (ASAN_RED_ZONE_SIZE >> ASAN_SHADOW_SHIFT) == 4); > + shadow_mem = gen_rtx_MEM (SImode, shadow_base); > + set_mem_alias_set (shadow_mem, asan_shadow_set); > + prev_offset = base_offset; > + for (l = length; l; l -= 2) > + { > + if (l == 2) > + cur_shadow_byte = ASAN_STACK_MAGIC_RIGHT; > + offset = offsets[l - 1]; > + if ((offset - base_offset) & (ASAN_RED_ZONE_SIZE - 1)) > + { > + int i; > + HOST_WIDE_INT aoff > + = base_offset + ((offset - base_offset) > + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); > + shadow_mem = adjust_address (shadow_mem, VOIDmode, > + (aoff - prev_offset) > + >> ASAN_SHADOW_SHIFT); > + prev_offset = aoff; > + for (i = 0; i < 4; i++, aoff += (1 << ASAN_SHADOW_SHIFT)) > + if (aoff < offset) > + { > + if (aoff < offset - (1 << ASAN_SHADOW_SHIFT) + 1) > + shadow_bytes[i] = 0; > + else > + shadow_bytes[i] = offset - aoff; > + } > + else > + shadow_bytes[i] = ASAN_STACK_MAGIC_PARTIAL; > + emit_move_insn (shadow_mem, asan_shadow_cst (shadow_bytes)); > + offset = aoff; > + } > + while (offset <= offsets[l - 2] - ASAN_RED_ZONE_SIZE) > + { > + shadow_mem = adjust_address (shadow_mem, VOIDmode, > + (offset - prev_offset) > + >> ASAN_SHADOW_SHIFT); > + prev_offset = offset; > + memset (shadow_bytes, cur_shadow_byte, 4); > + emit_move_insn (shadow_mem, asan_shadow_cst (shadow_bytes)); > + offset += ASAN_RED_ZONE_SIZE; > + } > + cur_shadow_byte = ASAN_STACK_MAGIC_MIDDLE; > + } > + do_pending_stack_adjust (); > + > + /* Construct epilogue sequence. */ > + start_sequence (); > + > + shadow_mem = gen_rtx_MEM (BLKmode, shadow_base); > + set_mem_alias_set (shadow_mem, asan_shadow_set); > + prev_offset = base_offset; > + last_offset = base_offset; > + last_size = 0; > + for (l = length; l; l -= 2) > + { > + offset = base_offset + ((offsets[l - 1] - base_offset) > + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); > + if (last_offset + last_size != offset) > + { > + shadow_mem = adjust_address (shadow_mem, VOIDmode, > + (last_offset - prev_offset) > + >> ASAN_SHADOW_SHIFT); > + prev_offset = last_offset; > + clear_storage (shadow_mem, GEN_INT (last_size >> ASAN_SHADOW_SHIFT), > + BLOCK_OP_NORMAL); > + last_offset = offset; > + last_size = 0; > + } > + last_size += base_offset + ((offsets[l - 2] - base_offset) > + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)) > + - offset; > + } > + if (last_size) > + { > + shadow_mem = adjust_address (shadow_mem, VOIDmode, > + (last_offset - prev_offset) > + >> ASAN_SHADOW_SHIFT); > + clear_storage (shadow_mem, GEN_INT (last_size >> ASAN_SHADOW_SHIFT), > + BLOCK_OP_NORMAL); > + } > + > + do_pending_stack_adjust (); > + > + ret = get_insns (); > + end_sequence (); > + return ret; > +} > + > /* Construct a function tree for __asan_report_{load,store}{1,2,4,8,16}. > IS_STORE is either 1 (for a store) or 0 (for a load). > SIZE_IN_BYTES is one of 1, 2, 4, 8, 16. */ > @@ -401,12 +588,12 @@ asan_finish_file (void) > static void > asan_init_shadow_ptr_types (void) > { > - alias_set_type set = new_alias_set (); > + asan_shadow_set = new_alias_set (); > shadow_ptr_types[0] = build_distinct_type_copy (unsigned_char_type_node); > - TYPE_ALIAS_SET (shadow_ptr_types[0]) = set; > + TYPE_ALIAS_SET (shadow_ptr_types[0]) = asan_shadow_set; > shadow_ptr_types[0] = build_pointer_type (shadow_ptr_types[0]); > shadow_ptr_types[1] = build_distinct_type_copy (short_unsigned_type_node); > - TYPE_ALIAS_SET (shadow_ptr_types[1]) = set; > + TYPE_ALIAS_SET (shadow_ptr_types[1]) = asan_shadow_set; > shadow_ptr_types[1] = build_pointer_type (shadow_ptr_types[1]); > } > > --- gcc/asan.h.jj 2012-10-12 23:30:31.689325469 +0200 > +++ gcc/asan.h 2012-10-17 11:01:17.720950926 +0200 > @@ -21,10 +21,39 @@ along with GCC; see the file COPYING3. > #ifndef TREE_ASAN > #define TREE_ASAN > > -extern void asan_finish_file(void); > +extern void asan_finish_file (void); > +extern rtx asan_emit_stack_protection (rtx, HOST_WIDE_INT *, tree *, int); > + > +/* Alias set for accessing the shadow memory. */ > +extern alias_set_type asan_shadow_set; > > /* Shadow memory is found at > (address >> ASAN_SHADOW_SHIFT) + targetm.asan_shadow_offset (). */ > #define ASAN_SHADOW_SHIFT 3 > > +/* Red zone size, stack and global variables are padded by ASAN_RED_ZONE_SIZE > + up to 2 * ASAN_RED_ZONE_SIZE - 1 bytes. */ > +#define ASAN_RED_ZONE_SIZE 32 > + > +/* Shadow memory values for stack protection. Left is below protected vars, > + the first pointer in stack corresponding to that offset contains > + ASAN_STACK_FRAME_MAGIC word, the second pointer to a string describing > + the frame. Middle is for padding in between variables, right is > + above the last protected variable and partial immediately after variables > + up to ASAN_RED_ZONE_SIZE alignment. */ > +#define ASAN_STACK_MAGIC_LEFT 0xf1 > +#define ASAN_STACK_MAGIC_MIDDLE 0xf2 > +#define ASAN_STACK_MAGIC_RIGHT 0xf3 > +#define ASAN_STACK_MAGIC_PARTIAL 0xf4 > + > +#define ASAN_STACK_FRAME_MAGIC 0x41b58ab3 > + > +/* Return true if DECL should be guarded on the stack. */ > + > +static inline bool > +asan_protect_stack_decl (tree decl) > +{ > + return DECL_P (decl) && !DECL_ARTIFICIAL (decl); > +} > + > #endif /* TREE_ASAN */ > --- gcc/cfgexpand.c.jj 2012-10-12 23:30:31.772325005 +0200 > +++ gcc/cfgexpand.c 2012-10-17 11:19:59.542778069 +0200 > @@ -47,6 +47,7 @@ along with GCC; see the file COPYING3. > #include "cfgloop.h" > #include "regs.h" /* For reg_renumber. */ > #include "insn-attr.h" /* For INSN_SCHEDULING. */ > +#include "asan.h" > > /* This variable holds information helping the rewriting of SSA trees > into RTL. */ > @@ -734,6 +735,7 @@ partition_stack_vars (void) > { > size_t i = stack_vars_sorted[si]; > unsigned int ialign = stack_vars[i].alignb; > + HOST_WIDE_INT isize = stack_vars[i].size; > > /* Ignore objects that aren't partition representatives. If we > see a var that is not a partition representative, it must > @@ -745,19 +747,28 @@ partition_stack_vars (void) > { > size_t j = stack_vars_sorted[sj]; > unsigned int jalign = stack_vars[j].alignb; > + HOST_WIDE_INT jsize = stack_vars[j].size; > > /* Ignore objects that aren't partition representatives. */ > if (stack_vars[j].representative != j) > continue; > > - /* Ignore conflicting objects. */ > - if (stack_var_conflict_p (i, j)) > - continue; > - > /* Do not mix objects of "small" (supported) alignment > and "large" (unsupported) alignment. */ > if ((ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) > != (jalign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)) > + break; > + > + /* For Address Sanitizer do not mix objects with different > + sizes, as the shorter vars wouldn't be adequately protected. > + Don't do that for "large" (unsupported) alignment objects, > + those aren't protected anyway. */ > + if (flag_asan && isize != jsize > + && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) > + break; > + > + /* Ignore conflicting objects. */ > + if (stack_var_conflict_p (i, j)) > continue; > > /* UNION the objects, placing J at OFFSET. */ > @@ -835,12 +846,26 @@ expand_one_stack_var_at (tree decl, rtx > set_rtl (decl, x); > } > > +DEF_VEC_I(HOST_WIDE_INT); > +DEF_VEC_ALLOC_I(HOST_WIDE_INT,heap); > + > +struct stack_vars_data > +{ > + /* Vector of offset pairs, always end of some padding followed > + by start of the padding that needs Address Sanitizer protection. > + The vector is in reversed, highest offset pairs come first. */ > + VEC(HOST_WIDE_INT, heap) *asan_vec; > + > + /* Vector of partition representative decls in between the paddings. */ > + VEC(tree, heap) *asan_decl_vec; > +}; > + > /* A subroutine of expand_used_vars. Give each partition representative > a unique location within the stack frame. Update each partition member > with that location. */ > > static void > -expand_stack_vars (bool (*pred) (tree)) > +expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data) > { > size_t si, i, j, n = stack_vars_num; > HOST_WIDE_INT large_size = 0, large_alloc = 0; > @@ -911,13 +936,45 @@ expand_stack_vars (bool (*pred) (tree)) > > /* Check the predicate to see whether this variable should be > allocated in this pass. */ > - if (pred && !pred (decl)) > + if (pred && !pred (i)) > continue; > > alignb = stack_vars[i].alignb; > if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) > { > - offset = alloc_stack_frame_space (stack_vars[i].size, alignb); > + if (flag_asan && pred) > + { > + HOST_WIDE_INT prev_offset = frame_offset; > + tree repr_decl = NULL_TREE; > + > + offset > + = alloc_stack_frame_space (stack_vars[i].size > + + ASAN_RED_ZONE_SIZE, > + MAX (alignb, ASAN_RED_ZONE_SIZE)); > + VEC_safe_push (HOST_WIDE_INT, heap, data->asan_vec, > + prev_offset); > + VEC_safe_push (HOST_WIDE_INT, heap, data->asan_vec, > + offset + stack_vars[i].size); > + /* Find best representative of the partition. > + Prefer those with DECL_NAME, even better > + satisfying asan_protect_stack_decl predicate. */ > + for (j = i; j != EOC; j = stack_vars[j].next) > + if (asan_protect_stack_decl (stack_vars[j].decl) > + && DECL_NAME (stack_vars[j].decl)) > + { > + repr_decl = stack_vars[j].decl; > + break; > + } > + else if (repr_decl == NULL_TREE > + && DECL_P (stack_vars[j].decl) > + && DECL_NAME (stack_vars[j].decl)) > + repr_decl = stack_vars[j].decl; > + if (repr_decl == NULL_TREE) > + repr_decl = stack_vars[i].decl; > + VEC_safe_push (tree, heap, data->asan_decl_vec, repr_decl); > + } > + else > + offset = alloc_stack_frame_space (stack_vars[i].size, alignb); > base = virtual_stack_vars_rtx; > base_align = crtl->max_used_stack_slot_alignment; > } > @@ -1055,8 +1112,9 @@ static bool > defer_stack_allocation (tree var, bool toplevel) > { > /* If stack protection is enabled, *all* stack variables must be deferred, > - so that we can re-order the strings to the top of the frame. */ > - if (flag_stack_protect) > + so that we can re-order the strings to the top of the frame. > + Similarly for Address Sanitizer. */ > + if (flag_stack_protect || flag_asan) > return true; > > /* We handle "large" alignment via dynamic allocation. We want to handle > @@ -1327,15 +1385,31 @@ stack_protect_decl_phase (tree decl) > as callbacks for expand_stack_vars. */ > > static bool > -stack_protect_decl_phase_1 (tree decl) > +stack_protect_decl_phase_1 (size_t i) > { > - return stack_protect_decl_phase (decl) == 1; > + return stack_protect_decl_phase (stack_vars[i].decl) == 1; > } > > static bool > -stack_protect_decl_phase_2 (tree decl) > +stack_protect_decl_phase_2 (size_t i) > { > - return stack_protect_decl_phase (decl) == 2; > + return stack_protect_decl_phase (stack_vars[i].decl) == 2; > +} > + > +/* And helper function that checks for asan phase (with stack protector > + it is phase 3). This is used as callback for expand_stack_vars. > + Returns true if any of the vars in the partition need to be protected. */ > + > +static bool > +asan_decl_phase_3 (size_t i) > +{ > + while (i != EOC) > + { > + if (asan_protect_stack_decl (stack_vars[i].decl)) > + return true; > + i = stack_vars[i].next; > + } > + return false; > } > > /* Ensure that variables in different stack protection phases conflict > @@ -1446,11 +1520,12 @@ estimated_stack_frame_size (struct cgrap > > /* Expand all variables used in the function. */ > > -static void > +static rtx > expand_used_vars (void) > { > tree var, outer_block = DECL_INITIAL (current_function_decl); > VEC(tree,heap) *maybe_local_decls = NULL; > + rtx var_end_seq = NULL_RTX; > struct pointer_map_t *ssa_name_decls; > unsigned i; > unsigned len; > @@ -1601,6 +1676,11 @@ expand_used_vars (void) > /* Assign rtl to each variable based on these partitions. */ > if (stack_vars_num > 0) > { > + struct stack_vars_data data; > + > + data.asan_vec = NULL; > + data.asan_decl_vec = NULL; > + > /* Reorder decls to be protected by iterating over the variables > array multiple times, and allocating out of each phase in turn. */ > /* ??? We could probably integrate this into the qsort we did > @@ -1609,14 +1689,41 @@ expand_used_vars (void) > if (has_protected_decls) > { > /* Phase 1 contains only character arrays. */ > - expand_stack_vars (stack_protect_decl_phase_1); > + expand_stack_vars (stack_protect_decl_phase_1, &data); > > /* Phase 2 contains other kinds of arrays. */ > if (flag_stack_protect == 2) > - expand_stack_vars (stack_protect_decl_phase_2); > + expand_stack_vars (stack_protect_decl_phase_2, &data); > + } > + > + if (flag_asan) > + /* Phase 3, any partitions that need asan protection > + in addition to phase 1 and 2. */ > + expand_stack_vars (asan_decl_phase_3, &data); > + > + if (!VEC_empty (HOST_WIDE_INT, data.asan_vec)) > + { > + HOST_WIDE_INT prev_offset = frame_offset; > + HOST_WIDE_INT offset > + = alloc_stack_frame_space (ASAN_RED_ZONE_SIZE, > + ASAN_RED_ZONE_SIZE); > + VEC_safe_push (HOST_WIDE_INT, heap, data.asan_vec, prev_offset); > + VEC_safe_push (HOST_WIDE_INT, heap, data.asan_vec, offset); > + > + var_end_seq > + = asan_emit_stack_protection (virtual_stack_vars_rtx, > + VEC_address (HOST_WIDE_INT, > + data.asan_vec), > + VEC_address (tree, > + data.asan_decl_vec), > + VEC_length (HOST_WIDE_INT, > + data.asan_vec)); > } > > - expand_stack_vars (NULL); > + expand_stack_vars (NULL, &data); > + > + VEC_free (HOST_WIDE_INT, heap, data.asan_vec); > + VEC_free (tree, heap, data.asan_decl_vec); > } > > fini_vars_expansion (); > @@ -1643,6 +1750,8 @@ expand_used_vars (void) > frame_offset += align - 1; > frame_offset &= -align; > } > + > + return var_end_seq; > } > > > @@ -3639,7 +3748,7 @@ expand_debug_locations (void) > /* Expand basic block BB from GIMPLE trees to RTL. */ > > static basic_block > -expand_gimple_basic_block (basic_block bb) > +expand_gimple_basic_block (basic_block bb, bool disable_tail_calls) > { > gimple_stmt_iterator gsi; > gimple_seq stmts; > @@ -3927,6 +4036,11 @@ expand_gimple_basic_block (basic_block b > } > else > { > + if (is_gimple_call (stmt) > + && gimple_call_tail_p (stmt) > + && disable_tail_calls) > + gimple_call_set_tail (stmt, false); > + > if (is_gimple_call (stmt) && gimple_call_tail_p (stmt)) > { > bool can_fallthru; > @@ -4286,7 +4400,7 @@ gimple_expand_cfg (void) > sbitmap blocks; > edge_iterator ei; > edge e; > - rtx var_seq; > + rtx var_seq, var_ret_seq; > unsigned i; > > timevar_push (TV_OUT_OF_SSA); > @@ -4346,7 +4460,7 @@ gimple_expand_cfg (void) > timevar_push (TV_VAR_EXPAND); > start_sequence (); > > - expand_used_vars (); > + var_ret_seq = expand_used_vars (); > > var_seq = get_insns (); > end_sequence (); > @@ -4472,7 +4586,7 @@ gimple_expand_cfg (void) > > lab_rtx_for_bb = pointer_map_create (); > FOR_BB_BETWEEN (bb, init_block->next_bb, EXIT_BLOCK_PTR, next_bb) > - bb = expand_gimple_basic_block (bb); > + bb = expand_gimple_basic_block (bb, var_ret_seq != NULL_RTX); > > if (MAY_HAVE_DEBUG_INSNS) > expand_debug_locations (); > @@ -4500,6 +4614,9 @@ gimple_expand_cfg (void) > construct_exit_block (); > insn_locations_finalize (); > > + if (var_ret_seq) > + emit_insn_after (var_ret_seq, return_label); > + > /* Zap the tree EH table. */ > set_eh_throw_stmt_table (cfun, NULL); > > > > Jakub
On 2012-10-17 05:35 , Jakub Jelinek wrote: > 2012-10-17 Jakub Jelinek <jakub@redhat.com> > > * Makefile.in (asan.o): Depend on $(EXPR_H) $(OPTABS_H). > (cfgexpand.o): Depend on asan.h. > * asan.c: Include expr.h and optabs.h. > (asan_shadow_set): New variable. > (asan_shadow_cst, asan_emit_stack_protection): New functions. > (asan_init_shadow_ptr_types): Initialize also asan_shadow_set. > * cfgexpand.c: Include asan.h. Define HOST_WIDE_INT heap vector. > (partition_stack_vars): If i is large alignment and j small > alignment or vice versa, break out of the loop instead of continue, > and put the test earlier. If flag_asan, break out of the loop > if for small alignment size is different. > (struct stack_vars_data): New type. > (expand_stack_vars): Add DATA argument. Change PRED type to > function taking size_t argument instead of tree. Adjust pred calls. > Fill DATA in and add needed padding in between variables if -fasan. > (defer_stack_allocation): Defer everything for flag_asan. > (stack_protect_decl_phase_1, stack_protect_decl_phase_2): Take > size_t index into stack_vars array instead of the decl directly. > (asan_decl_phase_3): New function. > (expand_used_vars): Return var destruction sequence. Adjust > expand_stack_vars calls, add another one for flag_asan. Call > asan_emit_stack_protection if expand_stack_vars added anything > to the vectors. > (expand_gimple_basic_block): Add disable_tail_calls argument. > (gimple_expand_cfg): Pass true to it if expand_used_vars returned > non-NULL. Emit the sequence returned by expand_used_vars after > return_label. > * asan.h (asan_emit_stack_protection): New prototype. > (asan_shadow_set): New decl. > (ASAN_RED_ZONE_SIZE, ASAN_STACK_MAGIC_LEFT, ASAN_STACK_MAGIC_MIDDLE, > ASAN_STACK_MAGIC_RIGHT, ASAN_STACK_FRAME_MAGIC): Define. > (asan_protect_stack_decl): New inline. > * toplev.c (process_options): Also disable -fasan on > !FRAME_GROWS_DOWNWARDS targets. This is OK. Thanks. Diego.
--- gcc/Makefile.in.jj 2012-10-12 23:30:31.635325772 +0200 +++ gcc/Makefile.in 2012-10-15 09:40:40.287706610 +0200 @@ -2204,7 +2204,7 @@ stor-layout.o : stor-layout.c $(CONFIG_H asan.o : asan.c asan.h $(CONFIG_H) pointer-set.h \ $(SYSTEM_H) $(TREE_H) $(GIMPLE_H) \ output.h $(DIAGNOSTIC_H) coretypes.h $(TREE_DUMP_H) $(FLAGS_H) \ - tree-pretty-print.h $(TARGET_H) + tree-pretty-print.h $(TARGET_H) $(EXPR_H) $(OPTABS_H) tree-ssa-tail-merge.o: tree-ssa-tail-merge.c \ $(SYSTEM_H) $(CONFIG_H) coretypes.h $(TM_H) $(BITMAP_H) \ $(FLAGS_H) $(TM_P_H) $(BASIC_BLOCK_H) \ @@ -3074,7 +3074,7 @@ cfgexpand.o : cfgexpand.c $(TREE_FLOW_H) $(DIAGNOSTIC_H) toplev.h $(DIAGNOSTIC_CORE_H) $(BASIC_BLOCK_H) $(FLAGS_H) debug.h $(PARAMS_H) \ value-prof.h $(TREE_INLINE_H) $(TARGET_H) $(SSAEXPAND_H) $(REGS_H) \ $(GIMPLE_PRETTY_PRINT_H) $(BITMAP_H) sbitmap.h \ - $(INSN_ATTR_H) $(CFGLOOP_H) + $(INSN_ATTR_H) $(CFGLOOP_H) asan.h cfgrtl.o : cfgrtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_ERROR_H) \ $(FLAGS_H) insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h \ $(FUNCTION_H) $(EXCEPT_H) $(TM_P_H) $(INSN_ATTR_H) \ --- gcc/toplev.c.jj 2012-10-12 23:30:31.717325313 +0200 +++ gcc/toplev.c 2012-10-15 09:40:03.540923935 +0200 @@ -1544,7 +1544,9 @@ process_options (void) } /* Address Sanitizer needs porting to each target architecture. */ - if (flag_asan && targetm.asan_shadow_offset == NULL) + if (flag_asan + && (targetm.asan_shadow_offset == NULL + || !FRAME_GROWS_DOWNWARD)) { warning (0, "-fasan not supported for this target"); flag_asan = 0; --- gcc/asan.c.jj 2012-10-12 23:31:55.423856689 +0200 +++ gcc/asan.c 2012-10-16 12:18:41.414029844 +0200 @@ -43,6 +43,8 @@ along with GCC; see the file COPYING3. #include "asan.h" #include "gimple-pretty-print.h" #include "target.h" +#include "expr.h" +#include "optabs.h" /* AddressSanitizer finds out-of-bounds and use-after-free bugs @@ -79,10 +81,195 @@ along with GCC; see the file COPYING3. to create redzones for stack and global object and poison them. */ +alias_set_type asan_shadow_set = -1; + /* Pointer types to 1 resp. 2 byte integers in shadow memory. A separate alias set is used for all shadow memory accesses. */ static GTY(()) tree shadow_ptr_types[2]; +/* Return a CONST_INT representing 4 subsequent shadow memory bytes. */ + +static rtx +asan_shadow_cst (unsigned char shadow_bytes[4]) +{ + int i; + unsigned HOST_WIDE_INT val = 0; + gcc_assert (WORDS_BIG_ENDIAN == BYTES_BIG_ENDIAN); + for (i = 0; i < 4; i++) + val |= (unsigned HOST_WIDE_INT) shadow_bytes[BYTES_BIG_ENDIAN ? 3 - i : i] + << (BITS_PER_UNIT * i); + return GEN_INT (trunc_int_for_mode (val, SImode)); +} + +/* Insert code to protect stack vars. The prologue sequence should be emitted + directly, epilogue sequence returned. BASE is the register holding the + stack base, against which OFFSETS array offsets are relative to, OFFSETS + array contains pairs of offsets in reverse order, always the end offset + of some gap that needs protection followed by starting offset, + and DECLS is an array of representative decls for each var partition. + LENGTH is the length of the OFFSETS array, DECLS array is LENGTH / 2 - 1 + elements long (OFFSETS include gap before the first variable as well + as gaps after each stack variable). */ + +rtx +asan_emit_stack_protection (rtx base, HOST_WIDE_INT *offsets, tree *decls, + int length) +{ + rtx shadow_base, shadow_mem, ret, mem; + unsigned char shadow_bytes[4]; + HOST_WIDE_INT base_offset = offsets[length - 1], offset, prev_offset; + HOST_WIDE_INT last_offset, last_size; + int l; + unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT; + static pretty_printer pp; + static bool pp_initialized; + const char *buf; + size_t len; + tree str_cst; + + /* First of all, prepare the description string. */ + if (!pp_initialized) + { + pp_construct (&pp, /* prefix */NULL, /* line-width */0); + pp_initialized = true; + } + pp_clear_output_area (&pp); + if (DECL_NAME (current_function_decl)) + pp_base_tree_identifier (&pp, DECL_NAME (current_function_decl)); + else + pp_string (&pp, "<unknown>"); + pp_space (&pp); + pp_decimal_int (&pp, length / 2 - 1); + pp_space (&pp); + for (l = length - 2; l; l -= 2) + { + tree decl = decls[l / 2 - 1]; + pp_wide_integer (&pp, offsets[l] - base_offset); + pp_space (&pp); + pp_wide_integer (&pp, offsets[l - 1] - offsets[l]); + pp_space (&pp); + if (DECL_P (decl) && DECL_NAME (decl)) + { + pp_decimal_int (&pp, IDENTIFIER_LENGTH (DECL_NAME (decl))); + pp_space (&pp); + pp_base_tree_identifier (&pp, DECL_NAME (decl)); + } + else + pp_string (&pp, "9 <unknown>"); + pp_space (&pp); + } + buf = pp_base_formatted_text (&pp); + len = strlen (buf); + str_cst = build_string (len + 1, buf); + TREE_TYPE (str_cst) + = build_array_type (char_type_node, build_index_type (size_int (len))); + TREE_READONLY (str_cst) = 1; + TREE_STATIC (str_cst) = 1; + str_cst = build1 (ADDR_EXPR, build_pointer_type (char_type_node), str_cst); + + /* Emit the prologue sequence. */ + base = expand_binop (Pmode, add_optab, base, GEN_INT (base_offset), + NULL_RTX, 1, OPTAB_DIRECT); + mem = gen_rtx_MEM (ptr_mode, base); + emit_move_insn (mem, GEN_INT (ASAN_STACK_FRAME_MAGIC)); + mem = adjust_address (mem, VOIDmode, GET_MODE_SIZE (ptr_mode)); + emit_move_insn (mem, expand_normal (str_cst)); + shadow_base = expand_binop (Pmode, lshr_optab, base, + GEN_INT (ASAN_SHADOW_SHIFT), + NULL_RTX, 1, OPTAB_DIRECT); + shadow_base = expand_binop (Pmode, add_optab, shadow_base, + GEN_INT (targetm.asan_shadow_offset ()), + NULL_RTX, 1, OPTAB_DIRECT); + gcc_assert (asan_shadow_set != -1 + && (ASAN_RED_ZONE_SIZE >> ASAN_SHADOW_SHIFT) == 4); + shadow_mem = gen_rtx_MEM (SImode, shadow_base); + set_mem_alias_set (shadow_mem, asan_shadow_set); + prev_offset = base_offset; + for (l = length; l; l -= 2) + { + if (l == 2) + cur_shadow_byte = ASAN_STACK_MAGIC_RIGHT; + offset = offsets[l - 1]; + if ((offset - base_offset) & (ASAN_RED_ZONE_SIZE - 1)) + { + int i; + HOST_WIDE_INT aoff + = base_offset + ((offset - base_offset) + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); + shadow_mem = adjust_address (shadow_mem, VOIDmode, + (aoff - prev_offset) + >> ASAN_SHADOW_SHIFT); + prev_offset = aoff; + for (i = 0; i < 4; i++, aoff += (1 << ASAN_SHADOW_SHIFT)) + if (aoff < offset) + { + if (aoff < offset - (1 << ASAN_SHADOW_SHIFT) + 1) + shadow_bytes[i] = 0; + else + shadow_bytes[i] = offset - aoff; + } + else + shadow_bytes[i] = ASAN_STACK_MAGIC_PARTIAL; + emit_move_insn (shadow_mem, asan_shadow_cst (shadow_bytes)); + offset = aoff; + } + while (offset <= offsets[l - 2] - ASAN_RED_ZONE_SIZE) + { + shadow_mem = adjust_address (shadow_mem, VOIDmode, + (offset - prev_offset) + >> ASAN_SHADOW_SHIFT); + prev_offset = offset; + memset (shadow_bytes, cur_shadow_byte, 4); + emit_move_insn (shadow_mem, asan_shadow_cst (shadow_bytes)); + offset += ASAN_RED_ZONE_SIZE; + } + cur_shadow_byte = ASAN_STACK_MAGIC_MIDDLE; + } + do_pending_stack_adjust (); + + /* Construct epilogue sequence. */ + start_sequence (); + + shadow_mem = gen_rtx_MEM (BLKmode, shadow_base); + set_mem_alias_set (shadow_mem, asan_shadow_set); + prev_offset = base_offset; + last_offset = base_offset; + last_size = 0; + for (l = length; l; l -= 2) + { + offset = base_offset + ((offsets[l - 1] - base_offset) + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); + if (last_offset + last_size != offset) + { + shadow_mem = adjust_address (shadow_mem, VOIDmode, + (last_offset - prev_offset) + >> ASAN_SHADOW_SHIFT); + prev_offset = last_offset; + clear_storage (shadow_mem, GEN_INT (last_size >> ASAN_SHADOW_SHIFT), + BLOCK_OP_NORMAL); + last_offset = offset; + last_size = 0; + } + last_size += base_offset + ((offsets[l - 2] - base_offset) + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)) + - offset; + } + if (last_size) + { + shadow_mem = adjust_address (shadow_mem, VOIDmode, + (last_offset - prev_offset) + >> ASAN_SHADOW_SHIFT); + clear_storage (shadow_mem, GEN_INT (last_size >> ASAN_SHADOW_SHIFT), + BLOCK_OP_NORMAL); + } + + do_pending_stack_adjust (); + + ret = get_insns (); + end_sequence (); + return ret; +} + /* Construct a function tree for __asan_report_{load,store}{1,2,4,8,16}. IS_STORE is either 1 (for a store) or 0 (for a load). SIZE_IN_BYTES is one of 1, 2, 4, 8, 16. */ @@ -401,12 +588,12 @@ asan_finish_file (void) static void asan_init_shadow_ptr_types (void) { - alias_set_type set = new_alias_set (); + asan_shadow_set = new_alias_set (); shadow_ptr_types[0] = build_distinct_type_copy (unsigned_char_type_node); - TYPE_ALIAS_SET (shadow_ptr_types[0]) = set; + TYPE_ALIAS_SET (shadow_ptr_types[0]) = asan_shadow_set; shadow_ptr_types[0] = build_pointer_type (shadow_ptr_types[0]); shadow_ptr_types[1] = build_distinct_type_copy (short_unsigned_type_node); - TYPE_ALIAS_SET (shadow_ptr_types[1]) = set; + TYPE_ALIAS_SET (shadow_ptr_types[1]) = asan_shadow_set; shadow_ptr_types[1] = build_pointer_type (shadow_ptr_types[1]); } --- gcc/asan.h.jj 2012-10-12 23:30:31.689325469 +0200 +++ gcc/asan.h 2012-10-17 11:01:17.720950926 +0200 @@ -21,10 +21,39 @@ along with GCC; see the file COPYING3. #ifndef TREE_ASAN #define TREE_ASAN -extern void asan_finish_file(void); +extern void asan_finish_file (void); +extern rtx asan_emit_stack_protection (rtx, HOST_WIDE_INT *, tree *, int); + +/* Alias set for accessing the shadow memory. */ +extern alias_set_type asan_shadow_set; /* Shadow memory is found at (address >> ASAN_SHADOW_SHIFT) + targetm.asan_shadow_offset (). */ #define ASAN_SHADOW_SHIFT 3 +/* Red zone size, stack and global variables are padded by ASAN_RED_ZONE_SIZE + up to 2 * ASAN_RED_ZONE_SIZE - 1 bytes. */ +#define ASAN_RED_ZONE_SIZE 32 + +/* Shadow memory values for stack protection. Left is below protected vars, + the first pointer in stack corresponding to that offset contains + ASAN_STACK_FRAME_MAGIC word, the second pointer to a string describing + the frame. Middle is for padding in between variables, right is + above the last protected variable and partial immediately after variables + up to ASAN_RED_ZONE_SIZE alignment. */ +#define ASAN_STACK_MAGIC_LEFT 0xf1 +#define ASAN_STACK_MAGIC_MIDDLE 0xf2 +#define ASAN_STACK_MAGIC_RIGHT 0xf3 +#define ASAN_STACK_MAGIC_PARTIAL 0xf4 + +#define ASAN_STACK_FRAME_MAGIC 0x41b58ab3 + +/* Return true if DECL should be guarded on the stack. */ + +static inline bool +asan_protect_stack_decl (tree decl) +{ + return DECL_P (decl) && !DECL_ARTIFICIAL (decl); +} + #endif /* TREE_ASAN */ --- gcc/cfgexpand.c.jj 2012-10-12 23:30:31.772325005 +0200 +++ gcc/cfgexpand.c 2012-10-17 11:19:59.542778069 +0200 @@ -47,6 +47,7 @@ along with GCC; see the file COPYING3. #include "cfgloop.h" #include "regs.h" /* For reg_renumber. */ #include "insn-attr.h" /* For INSN_SCHEDULING. */ +#include "asan.h" /* This variable holds information helping the rewriting of SSA trees into RTL. */ @@ -734,6 +735,7 @@ partition_stack_vars (void) { size_t i = stack_vars_sorted[si]; unsigned int ialign = stack_vars[i].alignb; + HOST_WIDE_INT isize = stack_vars[i].size; /* Ignore objects that aren't partition representatives. If we see a var that is not a partition representative, it must @@ -745,19 +747,28 @@ partition_stack_vars (void) { size_t j = stack_vars_sorted[sj]; unsigned int jalign = stack_vars[j].alignb; + HOST_WIDE_INT jsize = stack_vars[j].size; /* Ignore objects that aren't partition representatives. */ if (stack_vars[j].representative != j) continue; - /* Ignore conflicting objects. */ - if (stack_var_conflict_p (i, j)) - continue; - /* Do not mix objects of "small" (supported) alignment and "large" (unsupported) alignment. */ if ((ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) != (jalign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)) + break; + + /* For Address Sanitizer do not mix objects with different + sizes, as the shorter vars wouldn't be adequately protected. + Don't do that for "large" (unsupported) alignment objects, + those aren't protected anyway. */ + if (flag_asan && isize != jsize + && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) + break; + + /* Ignore conflicting objects. */ + if (stack_var_conflict_p (i, j)) continue; /* UNION the objects, placing J at OFFSET. */ @@ -835,12 +846,26 @@ expand_one_stack_var_at (tree decl, rtx set_rtl (decl, x); } +DEF_VEC_I(HOST_WIDE_INT); +DEF_VEC_ALLOC_I(HOST_WIDE_INT,heap); + +struct stack_vars_data +{ + /* Vector of offset pairs, always end of some padding followed + by start of the padding that needs Address Sanitizer protection. + The vector is in reversed, highest offset pairs come first. */ + VEC(HOST_WIDE_INT, heap) *asan_vec; + + /* Vector of partition representative decls in between the paddings. */ + VEC(tree, heap) *asan_decl_vec; +}; + /* A subroutine of expand_used_vars. Give each partition representative a unique location within the stack frame. Update each partition member with that location. */ static void -expand_stack_vars (bool (*pred) (tree)) +expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data) { size_t si, i, j, n = stack_vars_num; HOST_WIDE_INT large_size = 0, large_alloc = 0; @@ -911,13 +936,45 @@ expand_stack_vars (bool (*pred) (tree)) /* Check the predicate to see whether this variable should be allocated in this pass. */ - if (pred && !pred (decl)) + if (pred && !pred (i)) continue; alignb = stack_vars[i].alignb; if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) { - offset = alloc_stack_frame_space (stack_vars[i].size, alignb); + if (flag_asan && pred) + { + HOST_WIDE_INT prev_offset = frame_offset; + tree repr_decl = NULL_TREE; + + offset + = alloc_stack_frame_space (stack_vars[i].size + + ASAN_RED_ZONE_SIZE, + MAX (alignb, ASAN_RED_ZONE_SIZE)); + VEC_safe_push (HOST_WIDE_INT, heap, data->asan_vec, + prev_offset); + VEC_safe_push (HOST_WIDE_INT, heap, data->asan_vec, + offset + stack_vars[i].size); + /* Find best representative of the partition. + Prefer those with DECL_NAME, even better + satisfying asan_protect_stack_decl predicate. */ + for (j = i; j != EOC; j = stack_vars[j].next) + if (asan_protect_stack_decl (stack_vars[j].decl) + && DECL_NAME (stack_vars[j].decl)) + { + repr_decl = stack_vars[j].decl; + break; + } + else if (repr_decl == NULL_TREE + && DECL_P (stack_vars[j].decl) + && DECL_NAME (stack_vars[j].decl)) + repr_decl = stack_vars[j].decl; + if (repr_decl == NULL_TREE) + repr_decl = stack_vars[i].decl; + VEC_safe_push (tree, heap, data->asan_decl_vec, repr_decl); + } + else + offset = alloc_stack_frame_space (stack_vars[i].size, alignb); base = virtual_stack_vars_rtx; base_align = crtl->max_used_stack_slot_alignment; } @@ -1055,8 +1112,9 @@ static bool defer_stack_allocation (tree var, bool toplevel) { /* If stack protection is enabled, *all* stack variables must be deferred, - so that we can re-order the strings to the top of the frame. */ - if (flag_stack_protect) + so that we can re-order the strings to the top of the frame. + Similarly for Address Sanitizer. */ + if (flag_stack_protect || flag_asan) return true; /* We handle "large" alignment via dynamic allocation. We want to handle @@ -1327,15 +1385,31 @@ stack_protect_decl_phase (tree decl) as callbacks for expand_stack_vars. */ static bool -stack_protect_decl_phase_1 (tree decl) +stack_protect_decl_phase_1 (size_t i) { - return stack_protect_decl_phase (decl) == 1; + return stack_protect_decl_phase (stack_vars[i].decl) == 1; } static bool -stack_protect_decl_phase_2 (tree decl) +stack_protect_decl_phase_2 (size_t i) { - return stack_protect_decl_phase (decl) == 2; + return stack_protect_decl_phase (stack_vars[i].decl) == 2; +} + +/* And helper function that checks for asan phase (with stack protector + it is phase 3). This is used as callback for expand_stack_vars. + Returns true if any of the vars in the partition need to be protected. */ + +static bool +asan_decl_phase_3 (size_t i) +{ + while (i != EOC) + { + if (asan_protect_stack_decl (stack_vars[i].decl)) + return true; + i = stack_vars[i].next; + } + return false; } /* Ensure that variables in different stack protection phases conflict @@ -1446,11 +1520,12 @@ estimated_stack_frame_size (struct cgrap /* Expand all variables used in the function. */ -static void +static rtx expand_used_vars (void) { tree var, outer_block = DECL_INITIAL (current_function_decl); VEC(tree,heap) *maybe_local_decls = NULL; + rtx var_end_seq = NULL_RTX; struct pointer_map_t *ssa_name_decls; unsigned i; unsigned len; @@ -1601,6 +1676,11 @@ expand_used_vars (void) /* Assign rtl to each variable based on these partitions. */ if (stack_vars_num > 0) { + struct stack_vars_data data; + + data.asan_vec = NULL; + data.asan_decl_vec = NULL; + /* Reorder decls to be protected by iterating over the variables array multiple times, and allocating out of each phase in turn. */ /* ??? We could probably integrate this into the qsort we did @@ -1609,14 +1689,41 @@ expand_used_vars (void) if (has_protected_decls) { /* Phase 1 contains only character arrays. */ - expand_stack_vars (stack_protect_decl_phase_1); + expand_stack_vars (stack_protect_decl_phase_1, &data); /* Phase 2 contains other kinds of arrays. */ if (flag_stack_protect == 2) - expand_stack_vars (stack_protect_decl_phase_2); + expand_stack_vars (stack_protect_decl_phase_2, &data); + } + + if (flag_asan) + /* Phase 3, any partitions that need asan protection + in addition to phase 1 and 2. */ + expand_stack_vars (asan_decl_phase_3, &data); + + if (!VEC_empty (HOST_WIDE_INT, data.asan_vec)) + { + HOST_WIDE_INT prev_offset = frame_offset; + HOST_WIDE_INT offset + = alloc_stack_frame_space (ASAN_RED_ZONE_SIZE, + ASAN_RED_ZONE_SIZE); + VEC_safe_push (HOST_WIDE_INT, heap, data.asan_vec, prev_offset); + VEC_safe_push (HOST_WIDE_INT, heap, data.asan_vec, offset); + + var_end_seq + = asan_emit_stack_protection (virtual_stack_vars_rtx, + VEC_address (HOST_WIDE_INT, + data.asan_vec), + VEC_address (tree, + data.asan_decl_vec), + VEC_length (HOST_WIDE_INT, + data.asan_vec)); } - expand_stack_vars (NULL); + expand_stack_vars (NULL, &data); + + VEC_free (HOST_WIDE_INT, heap, data.asan_vec); + VEC_free (tree, heap, data.asan_decl_vec); } fini_vars_expansion (); @@ -1643,6 +1750,8 @@ expand_used_vars (void) frame_offset += align - 1; frame_offset &= -align; } + + return var_end_seq; } @@ -3639,7 +3748,7 @@ expand_debug_locations (void) /* Expand basic block BB from GIMPLE trees to RTL. */ static basic_block -expand_gimple_basic_block (basic_block bb) +expand_gimple_basic_block (basic_block bb, bool disable_tail_calls) { gimple_stmt_iterator gsi; gimple_seq stmts; @@ -3927,6 +4036,11 @@ expand_gimple_basic_block (basic_block b } else { + if (is_gimple_call (stmt) + && gimple_call_tail_p (stmt) + && disable_tail_calls) + gimple_call_set_tail (stmt, false); + if (is_gimple_call (stmt) && gimple_call_tail_p (stmt)) { bool can_fallthru; @@ -4286,7 +4400,7 @@ gimple_expand_cfg (void) sbitmap blocks; edge_iterator ei; edge e; - rtx var_seq; + rtx var_seq, var_ret_seq; unsigned i; timevar_push (TV_OUT_OF_SSA); @@ -4346,7 +4460,7 @@ gimple_expand_cfg (void) timevar_push (TV_VAR_EXPAND); start_sequence (); - expand_used_vars (); + var_ret_seq = expand_used_vars (); var_seq = get_insns (); end_sequence (); @@ -4472,7 +4586,7 @@ gimple_expand_cfg (void) lab_rtx_for_bb = pointer_map_create (); FOR_BB_BETWEEN (bb, init_block->next_bb, EXIT_BLOCK_PTR, next_bb) - bb = expand_gimple_basic_block (bb); + bb = expand_gimple_basic_block (bb, var_ret_seq != NULL_RTX); if (MAY_HAVE_DEBUG_INSNS) expand_debug_locations (); @@ -4500,6 +4614,9 @@ gimple_expand_cfg (void) construct_exit_block (); insn_locations_finalize (); + if (var_ret_seq) + emit_insn_after (var_ret_seq, return_label); + /* Zap the tree EH table. */ set_eh_throw_stmt_table (cfun, NULL);