This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 06/13] Implement protection of stack variables


From: jakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>

This patch implements the protection of stack variables.

To understand how this works, lets look at this example on x86_64
where the stack grows downward:

 int
 foo ()
 {
   char a[23] = {0};
   int b[2] = {0};

   a[5] = 1;
   b[1] = 2;

   return a[5] + b[1];
 }

For this function, the stack protected by asan will be organized as
follows, from the top of the stack to the bottom:

Slot 1/ [red zone of 32 bytes called 'RIGHT RedZone']

Slot 2/ [24 bytes for variable 'a']

Slot 3/ [8 bytes of red zone, that adds up to the space of 'a' to make
         the next slot be 32 bytes aligned; this one is called Partial
         Redzone; this 32 bytes alignment is an asan constraint]

Slot 4/ [red zone of 32 bytes called 'Middle RedZone']

Slot 5/ [8 bytes for variable 'b']

Slot 6/ [24 bytes of Partial Red Zone (similar to slot 3]

Slot 7/ [32 bytes of Red Zone at the bottom of the stack, called 'LEFT
         RedZone']

[A cultural question I've kept asking myself is Why has address
 sanitizer authors called these red zones (LEFT, MIDDLE, RIGHT)
 instead of e.g, (BOTTOM, MIDDLE, TOP).  Maybe they can step up and
 educate me so that I get less confused in the future.  :-)]

The 32 bytes of LEFT red zone at the bottom of the stack can be
decomposed as such:

    1/ The first 8 bytes contain a magical asan number that is always
    0x41B58AB3.

    2/ The following 8 bytes contains a pointer to a string (to be
    parsed at runtime by the runtime asan library), which format is
    the following:

     "<function-name> <space> <num-of-variables-on-the-stack>
     (<32-bytes-aligned-offset-in-bytes-of-variable> <space>
     <length-of-var-in-bytes> ){n} "

	where '(...){n}' means the content inside the parenthesis occurs 'n'
	times, with 'n' being the number of variables on the stack.

     3/ The following 16 bytes of the red zone have no particular
     format.

The shadow memory for that stack layout is going to look like this:

    - content of shadow memory 8 bytes for slot 7: 0xFFFFFFFFF1F1F1F1.
      The F1 byte pattern is a magic number called
      ASAN_STACK_MAGIC_LEFT and is a way for the runtime to know that
      the memory for that shadow byte is part of a the LEFT red zone
      intended to seat at the bottom of the variables on the stack.

    - content of shadow memory 8 bytes for slots 6 and 5:
      0xFFFFFFFFF4F4F400.  The F4 byte pattern is a magic number
      called ASAN_STACK_MAGIC_PARTIAL.  It flags the fact that the
      memory region for this shadow byte is a PARTIAL red zone
      intended to pad a variable A, so that the slot following
      {A,padding} is 32 bytes aligned.

      Note that the fact that the least significant byte of this
      shadow memory content is 00 means that 8 bytes of its
      corresponding memory (which corresponds to the memory of
      variable 'b') is addressable.

    - content of shadow memory 8 bytes for slot 4: 0xFFFFFFFFF2F2F2F2.
      The F2 byte pattern is a magic number called
      ASAN_STACK_MAGIC_MIDDLE.  It flags the fact that the memory
      region for this shadow byte is a MIDDLE red zone intended to
      seat between two 32 aligned slots of {variable,padding}.

    - content of shadow memory 8 bytes for slot 3 and 2:
      0xFFFFFFFFF4000000.  This represents is the concatenation of
      variable 'a' and the partial red zone following it, like what we
      had for variable 'b'.  The least significant 3 bytes being 00
      means that the 3 bytes of variable 'a' are addressable.

    - content of shadow memory 8 bytes for slot 1: 0xFFFFFFFFF3F3F3F3.
      The F3 byte pattern is a magic number called
      ASAN_STACK_MAGIC_RIGHT.  It flags the fact that the memory
      region for this shadow byte is a RIGHT red zone intended to seat
      at the top of the variables of the stack.

So, the patch lays out stack variables as well as the different red
zones, emits some prologue code to populate the shadow memory as to
poison (mark as non-accessible) the regions of the red zones and mark
the regions of stack variables as accessible, and emit some epilogue
code to un-poison (mark as accessible) the regions of red zones right
before the function exits.

	* 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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/asan@192540 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/ChangeLog.asan |  37 ++++++++++
 gcc/Makefile.in    |   4 +-
 gcc/asan.c         | 193 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 gcc/asan.h         |  31 ++++++++-
 gcc/cfgexpand.c    | 159 +++++++++++++++++++++++++++++++++++++------
 gcc/toplev.c       |   4 +-
 6 files changed, 400 insertions(+), 28 deletions(-)

diff --git a/gcc/ChangeLog.asan b/gcc/ChangeLog.asan
index 505bce9..23454f3 100644
--- a/gcc/ChangeLog.asan
+++ b/gcc/ChangeLog.asan
@@ -1,3 +1,40 @@
+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.
+
 2012-10-12  Jakub Jelinek  <jakub@redhat.com>
 
 	* asan.c (build_check_stmt): Rename join_bb variable to else_bb.
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 2ab1ca9..2743e24 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -2213,7 +2213,7 @@ stor-layout.o : stor-layout.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_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) \
@@ -3083,7 +3083,7 @@ cfgexpand.o : cfgexpand.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_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) \
diff --git a/gcc/asan.c b/gcc/asan.c
index 66dc571..fe0e9a8 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -43,6 +43,8 @@ along with GCC; see the file COPYING3.  If not see
 #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.  If not see
  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]);
 }
 
diff --git a/gcc/asan.h b/gcc/asan.h
index 0d9ab8b..6f0edbf 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -21,10 +21,39 @@ along with GCC; see the file COPYING3.  If not see
 #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 */
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index e501b4b..67cf902 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -47,6 +47,7 @@ along with GCC; see the file COPYING3.  If not see
 #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.  */
@@ -736,6 +737,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
@@ -747,19 +749,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.  */
@@ -837,12 +848,26 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   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;
@@ -913,13 +938,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;
 	}
@@ -1057,8 +1114,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
@@ -1329,15 +1387,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
@@ -1448,11 +1522,12 @@ estimated_stack_frame_size (struct cgraph_node *node)
 
 /* 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;
@@ -1603,6 +1678,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
@@ -1611,14 +1691,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 ();
@@ -1645,6 +1752,8 @@ expand_used_vars (void)
 	frame_offset += align - 1;
       frame_offset &= -align;
     }
+
+  return var_end_seq;
 }
 
 
@@ -3662,7 +3771,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;
@@ -3950,6 +4059,11 @@ expand_gimple_basic_block (basic_block bb)
 	}
       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;
@@ -4309,7 +4423,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);
@@ -4369,7 +4483,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 ();
@@ -4495,7 +4609,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 ();
@@ -4523,6 +4637,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);
 
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 68849f5..0fa8ce3 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1542,7 +1542,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;
-- 
1.7.11.7


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]