[asan] Protection of stack vars (take 3)

Jakub Jelinek jakub@redhat.com
Wed Oct 17 09:57:00 GMT 2012


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



More information about the Gcc-patches mailing list