diff mbox series

[RFC,8/X,libsanitizer] Ensure HWASAN required alignment for stack variables

Message ID VI1PR08MB547152251A94D540887D4D84E0BA0@VI1PR08MB5471.eurprd08.prod.outlook.com
State New
Headers show
Series [RFC,8/X,libsanitizer] Ensure HWASAN required alignment for stack variables | expand

Commit Message

Matthew Malcomson Sept. 6, 2019, 2:46 p.m. UTC
When colouring shadow memory, we need to ensure that each tag granule
is only used by one variable at a time.

This is done by ensuring that ecah coloured variable is aligned to the
tag granule representation size and also ensure that the end of each
variable as an alignment boundary between the end and the start of any
other data stored on the stack.

This patch ensures this by adding alignment requirements in
`align_local_variable` and forcing all stack variable allocation to be
deferred to allow `expand_stack_vars` to ensure the stack pointer is
aligned before allocating any variable for the current frame.

This patch also adds some macros defining how the HWASAN shadow memory
is stored and how a tag is stored in a pointer.

gcc/ChangeLog:

2019-09-06  Matthew Malcomson  <matthew.malcomson@arm.com>

	* asan.h (HWASAN_TAG_SIZE): New macro.
	(HWASAN_TAG_GRANULE_SIZE):New macro.
	(HWASAN_SHIFT):New macro.
	(HWASAN_SHIFT_RTX):New macro.
	(HWASAN_STACK_BACKGROUND):New macro.
	* cfgexpand.c (align_local_variable): Ensure alignment.
	(expand_stack_vars): Ensure alignment.
	(defer_stack_allocation): Ensure all variables are deferred so
	they can be handled by `expand_stack_vars`.



###############     Attachment also inlined for ease of reply    ###############
diff --git a/gcc/asan.h b/gcc/asan.h
index 66c11139b5ccbe307850d0be57e86f96445dd18d..d78f6b821c7d1e859cc53f124e071eac27a5e9b8 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -75,6 +75,41 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The macros below are hard-coded to these values in libhwasan, and
+   hence can't be changed independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   For aarch64 MTE we have 4 bits per colour.
+   AArch64 has a Top-Byte-Ignore feature that allows the use of the top byte of
+   pointers for storing information, HWASAN can use that entire top byte if
+   required.
+   If changing this value, be careful of the predicates/constraints on the
+   addtag<mode>4 patterns in the backend.  */
+#define HWASAN_TAG_SIZE 4
+/* Tag Granule of HWASAN shadow stack.
+   This is the size in real memory that each byte in the shadow memory refers
+   to.  I.e. if a variable is X bytes long in memory then it's colour in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   On top of this, most variables will need to be aligned to this amount since
+   two variables that are neighbours in memory and share a tag granule would
+   need to share the same colour (as the shared tag granule can only store one
+   colour).  */
+#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SIZE)
+/* How many bits to shift in order to access the tag bits.
+   This approach assumes that the tag is stored in the top N bits of a pointer,
+   and hence that shifting a known amount will leave just the tag bits.  */
+#define HWASAN_SHIFT 56
+#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
+/* Define the tag for the stack background.
+   NOTE: Having a background colour of zero is hard-coded in the runtime
+   library, so we can't really change this.
+   This defines what colour the stack pointer will be and hence what colour all
+   uncoloured parts of the stack are (e.g. spilled registers).
+   It also provides a tag for stack allocation to avoid, since an object stored
+   on the stack will want to have a different colour to the background stack to
+   ensure things like the return address etc can't be affected by accesses
+   through pointer to a user-object.  */
+#define HWASAN_STACK_BACKGROUND 0
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 7722dcd4004e270551c8bcd4403e53a78ebfdff0..c5585d83c6e155856e7a4278e21c35ee5675bbdd 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -372,7 +372,14 @@ align_local_variable (tree decl)
       align = LOCAL_DECL_ALIGNMENT (decl);
       SET_DECL_ALIGN (decl, align);
     }
-  return align / BITS_PER_UNIT;
+
+  unsigned int ret_align = align / BITS_PER_UNIT;
+
+  if (memory_tagging_p ())
+    ret_align = ret_align > HWASAN_TAG_GRANULE_SIZE
+      ? ret_align
+      : HWASAN_TAG_GRANULE_SIZE;
+  return ret_align;
 }
 
 /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
@@ -1117,6 +1124,29 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data)
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
+	  if (memory_tagging_p ())
+	    {
+	      /* Allocate zero bytes to take advantage of the
+		 alloc_stack_frame_space logic of ensuring the stack is aligned
+		 despite having poly_int64's to deal with.
+
+		 There must be no tag granule "shared" between different
+		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
+		 chunk can have more than one object in it.
+
+		 We ensure this by forcing the end of the last bit of data to
+		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
+		 the start of each variable to be aligned to
+		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
+
+		 We can't align just one of the start or end, since there are
+		 untagged things stored on the stack that we have no control on
+		 the alignment (e.g. function parameters which must conform to
+		 ABI requirements) and these can't share a tag granule with a
+		 tagged variable.  */
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	      alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
+	    }
 	  base = virtual_stack_vars_rtx;
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
@@ -1545,8 +1575,11 @@ 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.
-     Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || asan_sanitize_stack_p ())
+     Similarly for Address Sanitizer.
+     When memory tagging we defer all stack variables so we can handle them in
+     one place (handle here meaning ensure aligned and record information on
+     its position on the stack).  */
+  if (flag_stack_protect || asan_sanitize_stack_p () || memory_tagging_p ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME
diff mbox series

Patch

diff --git a/gcc/asan.h b/gcc/asan.h
index 66c11139b5ccbe307850d0be57e86f96445dd18d..d78f6b821c7d1e859cc53f124e071eac27a5e9b8 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -75,6 +75,41 @@  extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The macros below are hard-coded to these values in libhwasan, and
+   hence can't be changed independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   For aarch64 MTE we have 4 bits per colour.
+   AArch64 has a Top-Byte-Ignore feature that allows the use of the top byte of
+   pointers for storing information, HWASAN can use that entire top byte if
+   required.
+   If changing this value, be careful of the predicates/constraints on the
+   addtag<mode>4 patterns in the backend.  */
+#define HWASAN_TAG_SIZE 4
+/* Tag Granule of HWASAN shadow stack.
+   This is the size in real memory that each byte in the shadow memory refers
+   to.  I.e. if a variable is X bytes long in memory then it's colour in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   On top of this, most variables will need to be aligned to this amount since
+   two variables that are neighbours in memory and share a tag granule would
+   need to share the same colour (as the shared tag granule can only store one
+   colour).  */
+#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SIZE)
+/* How many bits to shift in order to access the tag bits.
+   This approach assumes that the tag is stored in the top N bits of a pointer,
+   and hence that shifting a known amount will leave just the tag bits.  */
+#define HWASAN_SHIFT 56
+#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
+/* Define the tag for the stack background.
+   NOTE: Having a background colour of zero is hard-coded in the runtime
+   library, so we can't really change this.
+   This defines what colour the stack pointer will be and hence what colour all
+   uncoloured parts of the stack are (e.g. spilled registers).
+   It also provides a tag for stack allocation to avoid, since an object stored
+   on the stack will want to have a different colour to the background stack to
+   ensure things like the return address etc can't be affected by accesses
+   through pointer to a user-object.  */
+#define HWASAN_STACK_BACKGROUND 0
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 7722dcd4004e270551c8bcd4403e53a78ebfdff0..c5585d83c6e155856e7a4278e21c35ee5675bbdd 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -372,7 +372,14 @@  align_local_variable (tree decl)
       align = LOCAL_DECL_ALIGNMENT (decl);
       SET_DECL_ALIGN (decl, align);
     }
-  return align / BITS_PER_UNIT;
+
+  unsigned int ret_align = align / BITS_PER_UNIT;
+
+  if (memory_tagging_p ())
+    ret_align = ret_align > HWASAN_TAG_GRANULE_SIZE
+      ? ret_align
+      : HWASAN_TAG_GRANULE_SIZE;
+  return ret_align;
 }
 
 /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
@@ -1117,6 +1124,29 @@  expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data)
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
+	  if (memory_tagging_p ())
+	    {
+	      /* Allocate zero bytes to take advantage of the
+		 alloc_stack_frame_space logic of ensuring the stack is aligned
+		 despite having poly_int64's to deal with.
+
+		 There must be no tag granule "shared" between different
+		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
+		 chunk can have more than one object in it.
+
+		 We ensure this by forcing the end of the last bit of data to
+		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
+		 the start of each variable to be aligned to
+		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
+
+		 We can't align just one of the start or end, since there are
+		 untagged things stored on the stack that we have no control on
+		 the alignment (e.g. function parameters which must conform to
+		 ABI requirements) and these can't share a tag granule with a
+		 tagged variable.  */
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	      alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
+	    }
 	  base = virtual_stack_vars_rtx;
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
@@ -1545,8 +1575,11 @@  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.
-     Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || asan_sanitize_stack_p ())
+     Similarly for Address Sanitizer.
+     When memory tagging we defer all stack variables so we can handle them in
+     one place (handle here meaning ensure aligned and record information on
+     its position on the stack).  */
+  if (flag_stack_protect || asan_sanitize_stack_p () || memory_tagging_p ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME