[PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN

Matthew Malcomson matthew.malcomson@arm.com
Mon Aug 17 14:13:10 GMT 2020


Handling stack variables has three features.

1) Ensure HWASAN required alignment for stack variables

When tagging 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 each tagged variable is aligned to the tag
granule representation size and also ensure that the end of each
object is aligned to ensure the start of any other data stored on the
stack is in a different granule.

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

2) Put tags into each stack variable pointer

Make sure that every pointer to a stack variable includes a tag of some
sort on it.

The way tagging works is:
  1) For every new stack frame, a random tag is generated.
  2) A base register is formed from the stack pointer value and this
     random tag.
  3) References to stack variables are now formed with RTL describing an
     offset from this base in both tag and value.

The random tag generation is handled by a backend hook.  This hook
decides whether to introduce a random tag or use the stack background
based on the parameter hwasan-random-frame-tag.  Using the stack
background is necessary for testing and bootstrap.  It is necessary
during bootstrap to avoid breaking the `configure` test program for
determining stack direction.

Using the stack background means that every stack frame has the initial
tag of zero and variables are tagged with incrementing tags from 1,
which also makes debugging a bit easier.

The tag&value offsets are also handled by a backend hook.

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

3) For each stack variable, tag and untag the shadow stack on function
   prologue and epilogue.

On entry to each function we tag the relevant shadow stack region for
each stack variable the tag to match the tag added to each pointer for
that variable.

This is the first patch where we use the HWASAN shadow space, so we need
to add in the libhwasan initialisation code that creates this shadow
memory region into the binary we produce.  This instrumentation is done
in `compile_file`.

When exiting a function we need to ensure the shadow stack for this
function has no remaining tag.  Without clearing the shadow stack area
for this stack frame, later function calls could get false positives
when those later function calls check untagged areas (such as parameters
passed on the stack) against a shadow stack area with left-over tag.

Hence we ensure that the entire stack frame is cleared on function exit.

config/ChangeLog:

	* bootstrap-hwasan.mk: Disable random frame tags for
	stack-tagging during bootstrap.

gcc/ChangeLog:

	* asan.c (hwasan_record_base): New function.
	(hwasan_emit_untag_frame): New.
	(hwasan_increment_tag): New function.
	(hwasan_with_tag): New function.
	(hwasan_tag_init): New function.
	(initialize_sanitizer_builtins): Define new builtins.
	(ATTR_NOTHROW_LIST): New macro.
	(hwasan_current_tag): New.
	(hwasan_extract_tag): New.
	(hwasan_emit_prologue): New.
	(hwasan_create_untagged_base): New.
	(hwasan_finish_file): New.
	(hwasan_ctor_statements): New variable.
	(hwasan_sanitize_stack_p): New.
	(hwasan_sanitize_p): New.
	(hwasan_sanitize_allocas_p): New.
	* asan.h (hwasan_record_base): New declaration.
	(hwasan_emit_untag_frame): New.
	(hwasan_increment_tag): New declaration.
	(hwasan_with_tag): New declaration.
	(hwasan_sanitize_stack_p): New declaration.
	(hwasan_sanitize_allocas_p): New declaration.
	(hwasan_tag_init): New declaration.
	(hwasan_sanitize_p): New declaration.
	(HWASAN_TAG_SIZE): New macro.
	(HWASAN_TAG_GRANULE_SIZE): New macro.
	(HWASAN_TAG_SHIFT_SIZE): New macro.
	(HWASAN_SHIFT): New macro.
	(HWASAN_SHIFT_RTX): New macro.
	(HWASAN_STACK_BACKGROUND): New macro.
	(hwasan_finish_file): New declaration.
	(hwasan_current_tag): New declaration.
	(hwasan_create_untagged_base): New declaration.
	(hwasan_extract_tag): New declaration.
	(hwasan_emit_prologue): New declaration.
	* cfgexpand.c (struct stack_vars_data): Add information to
	record hwasan variable stack offsets.
	(expand_stack_vars): Ensure variables are offset from a tagged
	base. Record offsets for hwasan. Ensure alignment.
	(expand_used_vars): Call function to emit prologue, and get
	untagging instructions for function exit.
	(align_local_variable): Ensure alignment.
	(defer_stack_allocation): Ensure all variables are deferred so
	they can be handled by `expand_stack_vars`.
	(expand_one_stack_var_at): Account for tags in
	variables when using HWASAN.
	(expand_one_stack_var_1): Pass new argument to
	expand_one_stack_var_at.
	(init_vars_expansion): Initialise hwasan internal variables when
	starting variable expansion.
	* doc/tm.texi (TARGET_MEMTAG_GENTAG): Document.
	* doc/tm.texi.in (TARGET_MEMTAG_GENTAG): Document.
	* explow.c (get_dynamic_stack_base): Parametrise stack vars RTX
	base.
	* explow.h (get_dynamic_stack_base): New declaration.
	* expr.c (force_operand): Use new addtag_force_operand hook.
	* target.def (TARGET_MEMTAG_GENTAG, TARGET_MEMTAG_ADDTAG,
	TARGET_MEMTAG_ADDTAG_FORCE_OPERAND): Introduce new hooks.
	* targhooks.c (default_memtag_gentag, default_memtag_addtag):
	New default hooks.
	* targhooks.h (default_memtag_gentag, default_memtag_addtag):
	Declare new default hooks.
	* builtin-types.def (BT_FN_VOID_PTR_UINT8_SIZE): New.
	* builtins.def (DEF_SANITIZER_BUILTIN): Enable for HWASAN.
	* sanitizer.def (BUILT_IN_HWASAN_INIT): New.
	(BUILT_IN_HWASAN_TAG_MEM): New.
	* toplev.c (compile_file): Emit libhwasan initialisation.



###############     Attachment also inlined for ease of reply    ###############


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -1,7 +1,11 @@
 # This option enables -fsanitize=hwaddress for stage2 and stage3.
+# We need to disable random frame tags for bootstrap since the autoconf check
+# for which direction the stack is growing has UB that a random frame tag
+# breaks.  Running with a random frame tag gives approx. 50% chance of
+# bootstrap comparison diff in libiberty/alloca.c.
 
-STAGE2_CFLAGS += -fsanitize=hwaddress
-STAGE3_CFLAGS += -fsanitize=hwaddress
+STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
 POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
diff --git a/gcc/asan.h b/gcc/asan.h
index 9efd33f9b86babbc10c4553c31b86950a313a242..9f010993944058b10153ff258fd1d08f5dff67d9 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -23,6 +23,20 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void asan_function_start (void);
 extern void asan_finish_file (void);
+extern void hwasan_finish_file (void);
+extern void hwasan_record_base (rtx);
+extern uint8_t hwasan_current_tag ();
+extern void hwasan_increment_tag ();
+extern rtx hwasan_with_tag (rtx, poly_int64);
+extern void hwasan_tag_init ();
+extern rtx hwasan_create_untagged_base (rtx);
+extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern rtx_insn *hwasan_emit_prologue (rtx *, rtx *, rtx, poly_int64 *,
+				       uint8_t *, size_t);
+extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx);
+extern bool hwasan_sanitize_p (void);
+extern bool hwasan_sanitize_stack_p (void);
+extern bool hwasan_sanitize_allocas_p (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -75,6 +89,31 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The values below define an ABI and are hard-coded to these values in
+   libhwasan, hence they can't be changed independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).  */
+#define HWASAN_TAG_SIZE 8
+/* 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 tag in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   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 tag (the shared tag granule can only store one tag).  */
+#define HWASAN_TAG_SHIFT_SIZE 4
+#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SHIFT_SIZE)
+/* Define the tag for the stack background.
+   This defines what tag the stack pointer will be and hence what tag all
+   variables that are not given special tags are (e.g. spilled registers,
+   and parameters passed on the stack).  */
+#define HWASAN_STACK_BACKGROUND 0
+/* How many bits to shift in order to access the tag bits.
+   The tag is stored in the top 8 bits of a pointer hence shifting 56 bits will
+   leave just the tag.  */
+#define HWASAN_SHIFT 56
+#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/asan.c b/gcc/asan.c
index 9c9aa4cae35832c1534a2cffac1d3d13eed0e687..f755a3290f1091be14fbe4c51d9579389e5eb245 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -257,6 +257,15 @@ hash_set<tree> *asan_handled_variables = NULL;
 
 hash_set <tree> *asan_used_labels = NULL;
 
+/* Global variables for HWASAN stack tagging.  */
+/* tag_offset records the offset from the frame base tag that the next object
+   should have.  */
+static uint8_t tag_offset = 0;
+/* hwasan_base_ptr is a pointer with the same address as
+   `virtual_stack_vars_rtx` for the current frame, and with the frame base tag
+   stored in it.  */
+static rtx hwasan_base_ptr = NULL_RTX;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -1352,6 +1361,28 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+/* Returns whether we are tagging pointers and checking those tags on memory
+   access.  */
+bool
+hwasan_sanitize_p ()
+{
+    return sanitize_flags_p (SANITIZE_HWADDRESS);
+}
+
+/* Are we tagging the stack?  */
+bool
+hwasan_sanitize_stack_p ()
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_stack);
+}
+
+/* Are we protecting alloca objects?  */
+bool
+hwasan_sanitize_allocas_p (void)
+{
+  return (hwasan_sanitize_stack_p () && param_hwasan_protect_allocas);
+}
+
 /* 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
@@ -2901,6 +2932,11 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_VOID_PTR_UINT8_SIZE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				unsigned_char_type_node, size_type_node,
+				NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
@@ -2951,6 +2987,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NOTHROW_LIST
+#define ATTR_NOTHROW_LIST ECF_NOTHROW
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
@@ -3702,4 +3740,269 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/* For stack tagging:
+     Initialise tag of the base register.
+     This has to be done as soon as the stack is getting expanded to ensure
+     anything emitted with `get_dynamic_stack_base` will use the value set here
+     instead of using a register without a tag.
+     Especially note that RTL expansion of large aligned values does that.  */
+void
+hwasan_record_base (rtx base)
+{
+  targetm.memtag.gentag (base, virtual_stack_vars_rtx);
+  hwasan_base_ptr = base;
+}
+
+/* For stack tagging:
+     Return the offset from the frame base tag that the "next" expanded object
+     should have.  */
+uint8_t
+hwasan_current_tag ()
+{
+  return tag_offset;
+}
+
+/* For stack tagging:
+     Increment the tag offset modulo the size a tag can represent.  */
+void
+hwasan_increment_tag ()
+{
+  uint8_t tag_bits = HWASAN_TAG_SIZE;
+  STATIC_ASSERT (HWASAN_TAG_SIZE == sizeof (tag_offset) * CHAR_BIT);
+  tag_offset = (tag_offset + 1) % (1 << tag_bits);
+  /* The "background tag" of the stack is zero by definition.
+     This is the tag that objects like parameters passed on the stack and
+     spilled registers are given.  It is handy to avoid this for objects we
+     decide the tags ourselves, partly to ensure that buffer overruns can't
+     affect these important variables (e.g. saved link register, saved stack
+     pointer etc) and partly to make debugging easier (everything with a tag of
+     zero is space allocated automatically by the compiler).
+
+     This is not feasible when using random frame tags (the default
+     configuration for hwasan) since the tag for the given frame is randomly
+     chosen at runtime.  In order to avoid any tags matching the stack
+     background we would need to decide tag offsets at runtime instead of
+     compile time (and pay the resulting performance cost).
+
+     When not using random base tags for each frame (i.e. when compiled with
+     `--param hwasan-random-frame-tag=0`) the base tag for each frame is zero.
+     This means the tag that each object gets is equal to the tag_offset used
+     in determining it.
+     When this is the case we *can* ensure no object gets the tag of zero by
+     simply ensuring no object has the tag_offset of zero.  */
+  if (tag_offset == HWASAN_STACK_BACKGROUND
+      && ! param_hwasan_random_frame_tag)
+    tag_offset += 1;
+}
+
+/* For stack tagging:
+     Return an RTX representing `base + offset` address
+     and `tag_of(base) + tag_offset` tag.  */
+rtx
+hwasan_with_tag (rtx base, poly_int64 offset)
+{
+  gcc_assert (tag_offset < (1 << HWASAN_TAG_SIZE));
+  return targetm.memtag.addtag (base, offset, tag_offset);
+}
+
+/* Clear internal state for the next function.
+   This function is called before variables on the stack get expanded, in
+   `init_vars_expansion`.  */
+void
+hwasan_tag_init ()
+{
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
+  hwasan_base_ptr = NULL_RTX;
+  /* When not using a random frame tag we can avoid the background stack
+     colour which gives the user a little better debug output upon a crash.
+     Meanwhile, when using a random frame tag it will be nice to avoid adding
+     tags for the first object since that is unnecessary extra work.
+     Hence set the initial tag_offset to be 0 if using a random frame tag and 1
+     otherwise.  */
+  tag_offset = param_hwasan_random_frame_tag
+    ? 0
+    : HWASAN_STACK_BACKGROUND + 1;
+}
+
+/* Return an RTX for the tag of TAGGED_POINTER.  */
+rtx
+hwasan_extract_tag (rtx tagged_pointer)
+{
+  rtx tag = expand_simple_binop (Pmode,
+				 LSHIFTRT,
+				 tagged_pointer,
+				 HWASAN_SHIFT_RTX,
+				 NULL_RTX,
+				 /* unsignedp = */0,
+				 OPTAB_DIRECT);
+  return gen_lowpart (QImode, tag);
+}
+
+/* For stack tagging:
+      Does HWASAN equivalent of `asan_emit_stack_protection`.
+
+   Prologue sequence should be emitted directly, while the epilogue sequence is
+   returned.  The epilogue sequence is what should be used if we're not
+   protecting alloca objects.
+
+   BASES is an array containing the tagged base registers for each object.
+   We map each object to a given base since large aligned objects have a
+   different base to others and we need to know which objects use which base.
+
+   UNTAGGED_BASES contains the same information as above except without tags.
+   This is needed since libhwasan only accepts untagged pointers in
+   __hwasan_tag_memory.
+
+   OFFSETS is an array with the start and end offsets for each object stored on
+   the stack in this frame.  This array is hence twice the length of the other
+   array arguments (given it has two entries for each stack object).
+
+   TAGS is an array containing the tag *offset* each object should have from
+   the tag in its base pointer.
+
+   LENGTH contains the length of the OFFSETS array.  */
+rtx_insn *
+hwasan_emit_prologue (rtx *bases,
+		      rtx *untagged_bases,
+		      rtx frame_extent,
+		      poly_int64 *offsets,
+		      uint8_t *tags,
+		      size_t length)
+{
+  /* We need untagged base pointers since libhwasan only accepts untagged
+    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
+    the base tag for an offset.  */
+
+  if (length < 2)
+    return NULL;
+
+  poly_int64 bot = 0, top = 0;
+  size_t i = 0;
+  for (i = 0; (i * 2) + 1 < length; i++)
+    {
+      poly_int64 start = offsets[i * 2];
+      poly_int64 end = offsets[(i * 2) + 1];
+
+      if (known_ge (start, end))
+	{
+	  top = start;
+	  bot = end;
+	}
+      else
+	{
+	  /* Given how these values are calculated, one must be known greater
+	     than the other.  */
+	  gcc_assert (known_lt (start, end));
+	  top = end;
+	  bot = start;
+	}
+      poly_int64 size = (top - bot);
+
+      /* Can't check that all poly_int64's are aligned, but still nice
+	 to check those that are compile-time constants.  */
+      HOST_WIDE_INT tmp;
+      if (top.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (bot.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (size.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      rtx base_tag = hwasan_extract_tag (bases[i]);
+      /* In the case of tag overflow we would want modulo wrapping -- which
+	 should be given from the `plus_constant` in QImode.  */
+      rtx tag = plus_constant (QImode, base_tag, tags[i]);
+      rtx bottom = convert_memory_address (ptr_mode,
+					   plus_constant (Pmode,
+							  untagged_bases[i],
+							  bot));
+      emit_library_call (ret, LCT_NORMAL, VOIDmode,
+			 bottom, ptr_mode,
+			 tag, QImode,
+			 gen_int_mode (size, ptr_mode), ptr_mode);
+    }
+  return hwasan_emit_untag_frame (frame_extent, virtual_stack_vars_rtx);
+}
+
+/* For stack tagging:
+     Return RTL insns to clear the tags between DYNAMIC and VARS pointers
+     into the stack.  These instructions should be emitted at the end of
+     every function.  */
+rtx_insn *
+hwasan_emit_untag_frame (rtx dynamic, rtx vars)
+{
+  start_sequence ();
+
+  dynamic = convert_memory_address (ptr_mode, dynamic);
+  vars = convert_memory_address (ptr_mode, vars);
+
+  rtx top_rtx;
+  rtx bot_rtx;
+  if (FRAME_GROWS_DOWNWARD)
+    {
+      top_rtx = vars;
+      bot_rtx = dynamic;
+    }
+  else
+    {
+      top_rtx = dynamic;
+      bot_rtx = vars;
+    }
+
+  rtx size_rtx = expand_simple_binop (ptr_mode, MINUS, top_rtx, bot_rtx,
+				      NULL_RTX, /* unsignedp = */0,
+				      OPTAB_DIRECT);
+
+  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (ret, LCT_NORMAL, VOIDmode,
+      bot_rtx, ptr_mode,
+      const0_rtx, QImode,
+      size_rtx, ptr_mode);
+
+  do_pending_stack_adjust ();
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+/* For stack tagging:
+     Return an RTX representing ORIG_BASE without a tag.  */
+rtx
+hwasan_create_untagged_base (rtx orig_base)
+{
+  rtx untagged_base = gen_reg_rtx (Pmode);
+  rtx tag_mask = gen_int_mode ((1ULL << HWASAN_SHIFT) - 1, Pmode);
+  untagged_base = expand_binop (Pmode, and_optab,
+				orig_base, tag_mask,
+				untagged_base, true, OPTAB_DIRECT);
+  gcc_assert (untagged_base);
+  return untagged_base;
+}
+
+/* Needs to be GTY(()), because cgraph_build_static_cdtor may
+   invoke ggc_collect.  */
+static GTY(()) tree hwasan_ctor_statements;
+
+/* Insert module initialisation into this TU.  This initialisation calls the
+   initialisation code for libhwasan.  */
+void
+hwasan_finish_file (void)
+{
+  /* Do not emit constructor initialisation for the kernel.
+     (the kernel has its own initialisation already).  */
+  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+    return;
+
+  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
+  flag_sanitize &= ~SANITIZE_HWADDRESS;
+  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
+  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
+  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
+  flag_sanitize |= SANITIZE_HWADDRESS;
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index c46b1bc5cbd1fba03b033b8d44ba186570780c3f..b217f29b9f5ebd9924507490ecd3d1190fdb8a4b 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -637,6 +637,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
 DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
 DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_CONST_SIZE_BOOL,
 		     BT_PTR, BT_PTR, BT_CONST_SIZE, BT_BOOL)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_SIZE, BT_VOID, BT_PTR, BT_UINT8,
+		     BT_SIZE)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index ee67ac15d5cf98797144b9d08a75f4cf7ee5ad33..92121fb898bbf7a90aa0e43c65ff3fe7b20d7c99 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -250,6 +250,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
+				| SANITIZE_HWADDRESS \
 				| SANITIZE_UNDEFINED \
 				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index b270a4ddb9db469ba52e42f36a1bc2f02d8f03fc..13a7cb3877c27c4c45a445b7d2c068038d9c4568 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -377,7 +377,13 @@ align_local_variable (tree decl, bool really_expand)
       if (really_expand)
 	SET_DECL_ALIGN (decl, align);
     }
-  return align / BITS_PER_UNIT;
+
+  unsigned int ret_align = align / BITS_PER_UNIT;
+
+  if (hwasan_sanitize_stack_p ())
+    ret_align = MAX (ret_align, HWASAN_TAG_GRANULE_SIZE);
+
+  return ret_align;
 }
 
 /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
@@ -988,7 +994,7 @@ dump_stack_var_partition (void)
 
 static void
 expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
-			 poly_int64 offset)
+			 poly_int64 offset, rtx stack_base)
 {
   unsigned align;
   rtx x;
@@ -996,7 +1002,11 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   /* If this fails, we've overflowed the stack frame.  Error nicely?  */
   gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
 
-  x = plus_constant (Pmode, base, offset);
+  if (hwasan_sanitize_stack_p ())
+    x = hwasan_with_tag (base, offset);
+  else
+    x = plus_constant (Pmode, base, offset);
+
   x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
 		   ? TYPE_MODE (TREE_TYPE (decl))
 		   : DECL_MODE (SSAVAR (decl)), x);
@@ -1006,7 +1016,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
       /* Set alignment we actually gave this decl if it isn't an SSA name.
          If it is we generate stack slots only accidentally so it isn't as
 	 important, we'll simply use the alignment that is already set.  */
-      if (base == virtual_stack_vars_rtx)
+      if (base == stack_base)
 	offset -= frame_phase;
       align = known_alignment (offset);
       align *= BITS_PER_UNIT;
@@ -1032,9 +1042,23 @@ public:
      The vector is in reversed, highest offset pairs come first.  */
   auto_vec<HOST_WIDE_INT> asan_vec;
 
+  /* HWASAN records the poly_int64 so it can handle any stack variable.  */
+  auto_vec<poly_int64> hwasan_vec;
+  auto_vec<rtx> hwasan_untagged_base_vec;
+  auto_vec<rtx> hwasan_base_vec;
+
   /* Vector of partition representative decls in between the paddings.  */
   auto_vec<tree> asan_decl_vec;
 
+  /* Vector of tag offsets representing the tag for each stack variable.
+     Each offset determines the difference between the randomly generated
+     tag for the current frame and the tag for this stack variable.  */
+  auto_vec<uint8_t> hwasan_tag_vec;
+  /* RTX representing the extent of the frame (i.e. the farthest from
+     virtual_stack_vars_rtx that the statically allocated objects on the stack
+     reach.  */
+  rtx hwasan_frame_extent;
+
   /* Base pseudo register for Address Sanitizer protected automatic vars.  */
   rtx asan_base;
 
@@ -1045,13 +1069,13 @@ public:
 /* 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) (size_t), class stack_vars_data *data)
 {
   size_t si, i, j, n = stack_vars_num;
   poly_uint64 large_size = 0, large_alloc = 0;
   rtx large_base = NULL;
+  rtx large_untagged_base = NULL;
   unsigned large_align = 0;
   bool large_allocation_done = false;
   tree decl;
@@ -1098,11 +1122,17 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	}
     }
 
+  if (hwasan_sanitize_stack_p () && data->asan_base == NULL)
+    {
+      data->asan_base = gen_reg_rtx (Pmode);
+      hwasan_record_base (data->asan_base);
+    }
+
   for (si = 0; si < n; ++si)
     {
       rtx base;
       unsigned base_align, alignb;
-      poly_int64 offset;
+      poly_int64 offset = 0;
 
       i = stack_vars_sorted[si];
 
@@ -1123,10 +1153,36 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       if (pred && !pred (i))
 	continue;
 
+      base = hwasan_sanitize_stack_p ()
+	? data->asan_base
+	: virtual_stack_vars_rtx;
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  base = virtual_stack_vars_rtx;
+	  if (hwasan_sanitize_stack_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 and these can't share a tag granule with a
+		 tagged variable.  */
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	      offset = alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (offset);
+	      data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
+	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
 	  HOST_WIDE_INT prev_offset;
@@ -1206,6 +1262,14 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
 	      base_align = crtl->max_used_stack_slot_alignment;
 	    }
+
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      data->hwasan_vec.safe_push (offset);
+	      data->hwasan_frame_extent = plus_constant (Pmode,
+							 virtual_stack_vars_rtx,
+							 offset);
+	    }
 	}
       else
 	{
@@ -1225,14 +1289,36 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      loffset = alloc_stack_frame_space
 		(rtx_to_poly_int64 (large_allocsize),
 		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
-	      large_base = get_dynamic_stack_base (loffset, large_align);
+	      large_base = get_dynamic_stack_base (loffset, large_align, base);
 	      large_allocation_done = true;
 	    }
-	  gcc_assert (large_base != NULL);
 
+	  gcc_assert (large_base != NULL);
 	  large_alloc = aligned_upper_bound (large_alloc, alignb);
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* An object with a large alignment requirement means that the
+		 alignment requirement is greater than the required alignment
+		 for tags.  */
+	      if (!large_untagged_base)
+		large_untagged_base = hwasan_create_untagged_base (large_base);
+	      data->hwasan_vec.safe_push (large_alloc);
+	      data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
+	    }
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Ensure the end of the variable is also aligned correctly.  */
+	      poly_int64 align_again
+		= aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (align_again);
+	      if (FRAME_GROWS_DOWNWARD)
+		data->hwasan_frame_extent = large_untagged_base;
+	      else
+		data->hwasan_frame_extent
+		  = plus_constant (Pmode, large_untagged_base, align_again);
+	    }
 
 	  base = large_base;
 	  base_align = large_align;
@@ -1244,7 +1330,21 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	{
 	  expand_one_stack_var_at (stack_vars[j].decl,
 				   base, base_align,
-				   offset);
+				   offset,
+				   hwasan_sanitize_stack_p ()
+				   ? data->asan_base
+				   : virtual_stack_vars_rtx);
+	}
+
+      if (hwasan_sanitize_stack_p ())
+	{
+	  /* Record the tag for this object in `data` so the prologue knows
+	     what tag to put in the shadow memory during cfgexpand.c.
+	     Then increment the tag so that the next object has a different
+	     tag to this object.  */
+	  data->hwasan_base_vec.safe_push (base);
+	  data->hwasan_tag_vec.safe_push (hwasan_current_tag ());
+	  hwasan_increment_tag ();
 	}
     }
 
@@ -1341,7 +1441,8 @@ expand_one_stack_var_1 (tree var)
   offset = alloc_stack_frame_space (size, byte_align);
 
   expand_one_stack_var_at (var, virtual_stack_vars_rtx,
-			   crtl->max_used_stack_slot_alignment, offset);
+			   crtl->max_used_stack_slot_alignment, offset,
+			   virtual_stack_vars_rtx);
 }
 
 /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
@@ -1554,8 +1655,13 @@ 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 tagging memory we defer all stack variables so we can handle them in
+     one place (handle here meaning ensure they are aligned and record
+     information on each variables position in the stack).  */
+  if (flag_stack_protect
+      || asan_sanitize_stack_p ()
+      || hwasan_sanitize_stack_p ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME
@@ -1947,6 +2053,8 @@ init_vars_expansion (void)
   /* Initialize local stack smashing state.  */
   has_protected_decls = false;
   has_short_buffer = false;
+  if (hwasan_sanitize_stack_p ())
+    hwasan_tag_init ();
 }
 
 /* Free up stack variable graph data.  */
@@ -2269,12 +2377,28 @@ expand_used_vars (void)
 	}
 
       expand_stack_vars (NULL, &data);
+
+      if (hwasan_sanitize_stack_p ())
+	var_end_seq
+	  = hwasan_emit_prologue (data.hwasan_base_vec.address (),
+				  data.hwasan_untagged_base_vec.address (),
+				  data.hwasan_frame_extent,
+				  data.hwasan_vec.address (),
+				  data.hwasan_tag_vec.address (),
+				  data.hwasan_vec.length ());
     }
 
   if (asan_sanitize_allocas_p () && cfun->calls_alloca)
     var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
 					      virtual_stack_vars_rtx,
 					      var_end_seq);
+  else if (hwasan_sanitize_allocas_p () && cfun->calls_alloca)
+    /* Here we replace `var_end_seq` while for asan we append to `var_end_seq`.
+       This is for the out-of-line case which uses only one function call in
+       either case (and hence we want to replace the previous function call if
+       we're changing things).  */
+    var_end_seq = hwasan_emit_untag_frame (virtual_stack_dynamic_rtx,
+					   virtual_stack_vars_rtx);
 
   fini_vars_expansion ();
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 0a1131962747fbd172a137c28606eb2b62372383..1ad1078b78a2c114f8bbfb8ca6b6b81adb363588 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2979,6 +2979,31 @@ A target hook which lets a backend compute the set of pressure classes to  be us
 True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that @option{-fsanitize=hwaddress} can work.
 @end deftypefn
 
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
+Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by TAG_OFFSET.
+The resulting RTX must either be a valid memory address or be able to get
+put into an operand with force_operand.  If overridden the more common case
+is that we force this into an operand using the backend hook
+"addtag_force_operand" that is called in force_operand.
+
+It is expected that that "addtag_force_operand" recognises the RTX
+generated by "addtag" and emits code to force that RTX into an operand.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG_FORCE_OPERAND (rtx @var{oper}, rtx @var{target})
+If the RTL expression OPER is of the form generated by
+targetm.memtag.addtag, then emit instructions to move the value into an
+operand (i.e. for force_operand).
+TARGET is an RTX suggestion of where to generate the value.
+This hook is most often implemented by emitting instructions to put the
+expression into a pseudo register, then returning that pseudo register.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_MEMTAG_GENTAG (rtx @var{base}, rtx @var{untagged})
+Set the BASE argument to UNTAGGED with some random tag.
+This function is used to generate a tagged base for the current stack frame.
+@end deftypefn
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 80960c1fe041bd08ccc22a4c41ebf740eca80015..140affd83529768e410957dc8f8058c6e39d698c 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2377,6 +2377,12 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_ADDTAG
+
+@hook TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
+
+@hook TARGET_MEMTAG_GENTAG
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/explow.h b/gcc/explow.h
index 0df8c62b82a8bf1d8d6baf0b6fb658e66361a407..581831cb19fdf9e8fd969bb30139e1358279a34d 100644
--- a/gcc/explow.h
+++ b/gcc/explow.h
@@ -106,7 +106,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
 extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
 
 /* Returns the address of the dynamic stack space without allocating it.  */
-extern rtx get_dynamic_stack_base (poly_int64, unsigned);
+extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
 
 /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
 extern rtx align_dynamic_address (rtx, unsigned);
diff --git a/gcc/explow.c b/gcc/explow.c
index 0fbc6d25b816457a3d13ed45d16b5dd0513cfacd..41c3f6ace49c0e55c080e10b917842b1b21d49eb 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1583,10 +1583,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
    OFFSET is the offset of the area into the virtual stack vars area.
 
    REQUIRED_ALIGN is the alignment (in bits) required for the region
-   of memory.  */
+   of memory.
+
+   BASE is the rtx of the base of this virtual stack vars area.
+   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+   on the stack.  */
 
 rtx
-get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
 {
   rtx target;
 
@@ -1594,7 +1598,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
     crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
 
   target = gen_reg_rtx (Pmode);
-  emit_move_insn (target, virtual_stack_vars_rtx);
+  emit_move_insn (target, base);
   target = expand_binop (Pmode, add_optab, target,
 			 gen_int_mode (offset, Pmode),
 			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
diff --git a/gcc/expr.c b/gcc/expr.c
index b4bbeffe3ce095674e8528286e8f013cc72de32e..6b89e1704792d4336e7b3287a54c197b442c42db 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -7645,6 +7645,13 @@ force_operand (rtx value, rtx target)
       return subtarget;
     }
 
+  if (targetm.memtag.addtag_force_operand)
+    {
+      rtx ret = targetm.memtag.addtag_force_operand (value, target);
+      if (ret)
+	return ret;
+    }
+
   if (ARITHMETIC_P (value))
     {
       op2 = XEXP (value, 1);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index a32715ddb92e69b7ca7be28a8f17a369b891bd76..39b9b05a75ff9d80663a5e0d5edbb73216be84a8 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
+/* Hardware Address Sanitizer.  */
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
+		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
+
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/target.def b/gcc/target.def
index 43b491b027615523f0202fe1ded430cac0b4bef4..145a631ceb5239a9f099ef531668603cc2f7aea6 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6839,6 +6839,37 @@ DEFHOOK
  pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
  bool, (), default_memtag_can_tag_addresses)
 
+DEFHOOK
+(addtag,
+ "Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by\
+ TAG_OFFSET.\n\
+The resulting RTX must either be a valid memory address or be able to get\n\
+put into an operand with force_operand.  If overridden the more common case\n\
+is that we force this into an operand using the backend hook\n\
+\"addtag_force_operand\" that is called in force_operand.\n\
+\n\
+It is expected that that \"addtag_force_operand\" recognises the RTX\n\
+generated by \"addtag\" and emits code to force that RTX into an operand.",
+rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
+default_memtag_addtag)
+
+DEFHOOK
+(addtag_force_operand,
+ "If the RTL expression OPER is of the form generated by\n\
+targetm.memtag.addtag, then emit instructions to move the value into an\n\
+operand (i.e. for force_operand).\n\
+TARGET is an RTX suggestion of where to generate the value.\n\
+This hook is most often implemented by emitting instructions to put the\n\
+expression into a pseudo register, then returning that pseudo register.",
+rtx, (rtx oper, rtx target), NULL)
+
+DEFHOOK
+(gentag,
+ "Set the BASE argument to UNTAGGED with some random tag.\n\
+This function is used to generate a tagged base for the current stack frame.",
+  void, (rtx base, rtx untagged),
+  default_memtag_gentag)
+
 HOOK_VECTOR_END (memtag)
 #undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index c11b1879f65bb581af11963b40189028fa490623..9e196c8798f9b6be369a34faba4823a41a16d724 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -286,4 +286,6 @@ extern bool speculation_safe_value_not_needed (bool);
 extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 
 extern bool default_memtag_can_tag_addresses ();
+extern void default_memtag_gentag (rtx, rtx);
+extern rtx default_memtag_addtag (rtx, poly_int64, uint8_t);
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 8c860f91cae7ccb5ca7fb0ff2364c1f67620d661..69ffccfd9714f662e0d42ac97908c955990b1c58 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "flags.h"
 #include "explow.h"
+#include "expmed.h"
 #include "calls.h"
 #include "expr.h"
 #include "output.h"
@@ -83,6 +84,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "sbitmap.h"
 #include "function-abi.h"
+#include "attribs.h"
+#include "asan.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -2385,4 +2388,89 @@ default_memtag_can_tag_addresses ()
   return false;
 }
 
+/* Takes a REG rtx as BASE and some value as UNTAGGED.
+   Ensures the register in BASE is given the value of UNTAGGED with the
+   (possibly random) base frame tag on it.  */
+void
+default_memtag_gentag (rtx base, rtx untagged)
+{
+  gcc_assert (param_hwasan_instrument_stack);
+  if (param_hwasan_random_frame_tag)
+    {
+    rtx temp = gen_reg_rtx (QImode);
+    rtx ret = init_one_libfunc ("__hwasan_generate_tag");
+    rtx new_tag = emit_library_call_value (ret, temp, LCT_NORMAL, QImode);
+    emit_move_insn (base, untagged);
+    /* We know that `base` is not the stack pointer, since we never want to put
+      a randomly generated tag into the stack pointer.  Hence we can use
+      `store_bit_field` which on aarch64 generates a `bfi` which can not act on
+      the stack pointer.  */
+    store_bit_field (base, 8, 56, 0, 0, QImode, new_tag, false);
+    }
+  else
+    {
+      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
+	 In the future we may add the option emit random tags with inline
+	 instrumentation instead of function calls.  This would be the same
+	 between the kernel and userland.  */
+      emit_move_insn (base, untagged);
+    }
+}
+
+rtx
+default_memtag_addtag (rtx base, poly_int64 offset, uint8_t tag_offset)
+{
+  /* Need to look into what the most efficient code sequence is.
+     This is a code sequence that would be emitted *many* times, so we
+     want it as small as possible.
+
+     If the tag offset is greater that (1 << 7) then the most efficient
+     sequence here would give UB from signed integer overflow in the
+     poly_int64.  Hence in that case we emit the slightly less efficient
+     sequence.
+
+     There are two places where tag overflow is a question:
+       - Tagging the shadow stack.
+	  (both tagging and untagging).
+       - Tagging addressable pointers.
+
+     We need to ensure both behaviours are the same (i.e. that the tag that
+     ends up in a pointer after "overflowing" the tag bits with a tag addition
+     is the same that ends up in the shadow space).
+
+     The aim is that the behaviour of tag addition should follow modulo
+     wrapping in both instances.
+
+     The libhwasan code doesn't have any path that increments a pointers tag,
+     which means it has no opinion on what happens when a tag increment
+     overflows (and hence we can choose our own behaviour).
+
+     NOTE:
+	Here we return an expression which represents the base with the
+	provided offsets.
+	This does not have to be a valid operand to anything, since the
+	`force_operand` machinery in the compiler already handles this.  */
+
+  if (tag_offset < (1 << 7))
+    {
+      offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
+      return plus_constant (Pmode, base, offset);
+    }
+  else
+    {
+      /* This is the fallback, it would be nice if it had less instructions,
+	 but we can look for cleverer ways later.  */
+      uint64_t tag_mask = ~(0xFFUL << HWASAN_SHIFT);
+      rtx untagged_base = gen_rtx_AND (Pmode, GEN_INT (tag_mask), base);
+      rtx new_addr = plus_constant (Pmode, untagged_base, offset);
+
+      rtx original_tag_value = gen_rtx_LSHIFTRT (Pmode, base,
+						 GEN_INT (HWASAN_SHIFT));
+      rtx new_tag_value = plus_constant (Pmode, original_tag_value, tag_offset);
+      rtx new_tag = gen_rtx_ASHIFT (Pmode, new_tag_value,
+				    GEN_INT (HWASAN_SHIFT));
+      return gen_rtx_IOR (Pmode, new_addr, new_tag);
+    }
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index cfbfc30ccfac9bd3bd3067bdddcdec71159a64a2..42aa4bdad9779179b9b6270b5342c99b53e1ca39 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -510,6 +510,9 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      if (flag_sanitize & SANITIZE_HWADDRESS)
+	hwasan_finish_file ();
+
       omp_finish_file ();
 
       hsa_output_brig ();

-------------- next part --------------
diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -1,7 +1,11 @@
 # This option enables -fsanitize=hwaddress for stage2 and stage3.
+# We need to disable random frame tags for bootstrap since the autoconf check
+# for which direction the stack is growing has UB that a random frame tag
+# breaks.  Running with a random frame tag gives approx. 50% chance of
+# bootstrap comparison diff in libiberty/alloca.c.
 
-STAGE2_CFLAGS += -fsanitize=hwaddress
-STAGE3_CFLAGS += -fsanitize=hwaddress
+STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
 POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
diff --git a/gcc/asan.h b/gcc/asan.h
index 9efd33f9b86babbc10c4553c31b86950a313a242..9f010993944058b10153ff258fd1d08f5dff67d9 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -23,6 +23,20 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void asan_function_start (void);
 extern void asan_finish_file (void);
+extern void hwasan_finish_file (void);
+extern void hwasan_record_base (rtx);
+extern uint8_t hwasan_current_tag ();
+extern void hwasan_increment_tag ();
+extern rtx hwasan_with_tag (rtx, poly_int64);
+extern void hwasan_tag_init ();
+extern rtx hwasan_create_untagged_base (rtx);
+extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern rtx_insn *hwasan_emit_prologue (rtx *, rtx *, rtx, poly_int64 *,
+				       uint8_t *, size_t);
+extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx);
+extern bool hwasan_sanitize_p (void);
+extern bool hwasan_sanitize_stack_p (void);
+extern bool hwasan_sanitize_allocas_p (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -75,6 +89,31 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The values below define an ABI and are hard-coded to these values in
+   libhwasan, hence they can't be changed independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).  */
+#define HWASAN_TAG_SIZE 8
+/* 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 tag in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   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 tag (the shared tag granule can only store one tag).  */
+#define HWASAN_TAG_SHIFT_SIZE 4
+#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SHIFT_SIZE)
+/* Define the tag for the stack background.
+   This defines what tag the stack pointer will be and hence what tag all
+   variables that are not given special tags are (e.g. spilled registers,
+   and parameters passed on the stack).  */
+#define HWASAN_STACK_BACKGROUND 0
+/* How many bits to shift in order to access the tag bits.
+   The tag is stored in the top 8 bits of a pointer hence shifting 56 bits will
+   leave just the tag.  */
+#define HWASAN_SHIFT 56
+#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/asan.c b/gcc/asan.c
index 9c9aa4cae35832c1534a2cffac1d3d13eed0e687..f755a3290f1091be14fbe4c51d9579389e5eb245 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -257,6 +257,15 @@ hash_set<tree> *asan_handled_variables = NULL;
 
 hash_set <tree> *asan_used_labels = NULL;
 
+/* Global variables for HWASAN stack tagging.  */
+/* tag_offset records the offset from the frame base tag that the next object
+   should have.  */
+static uint8_t tag_offset = 0;
+/* hwasan_base_ptr is a pointer with the same address as
+   `virtual_stack_vars_rtx` for the current frame, and with the frame base tag
+   stored in it.  */
+static rtx hwasan_base_ptr = NULL_RTX;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -1352,6 +1361,28 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+/* Returns whether we are tagging pointers and checking those tags on memory
+   access.  */
+bool
+hwasan_sanitize_p ()
+{
+    return sanitize_flags_p (SANITIZE_HWADDRESS);
+}
+
+/* Are we tagging the stack?  */
+bool
+hwasan_sanitize_stack_p ()
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_stack);
+}
+
+/* Are we protecting alloca objects?  */
+bool
+hwasan_sanitize_allocas_p (void)
+{
+  return (hwasan_sanitize_stack_p () && param_hwasan_protect_allocas);
+}
+
 /* 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
@@ -2901,6 +2932,11 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_VOID_PTR_UINT8_SIZE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				unsigned_char_type_node, size_type_node,
+				NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
@@ -2951,6 +2987,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NOTHROW_LIST
+#define ATTR_NOTHROW_LIST ECF_NOTHROW
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
@@ -3702,4 +3740,269 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/* For stack tagging:
+     Initialise tag of the base register.
+     This has to be done as soon as the stack is getting expanded to ensure
+     anything emitted with `get_dynamic_stack_base` will use the value set here
+     instead of using a register without a tag.
+     Especially note that RTL expansion of large aligned values does that.  */
+void
+hwasan_record_base (rtx base)
+{
+  targetm.memtag.gentag (base, virtual_stack_vars_rtx);
+  hwasan_base_ptr = base;
+}
+
+/* For stack tagging:
+     Return the offset from the frame base tag that the "next" expanded object
+     should have.  */
+uint8_t
+hwasan_current_tag ()
+{
+  return tag_offset;
+}
+
+/* For stack tagging:
+     Increment the tag offset modulo the size a tag can represent.  */
+void
+hwasan_increment_tag ()
+{
+  uint8_t tag_bits = HWASAN_TAG_SIZE;
+  STATIC_ASSERT (HWASAN_TAG_SIZE == sizeof (tag_offset) * CHAR_BIT);
+  tag_offset = (tag_offset + 1) % (1 << tag_bits);
+  /* The "background tag" of the stack is zero by definition.
+     This is the tag that objects like parameters passed on the stack and
+     spilled registers are given.  It is handy to avoid this for objects we
+     decide the tags ourselves, partly to ensure that buffer overruns can't
+     affect these important variables (e.g. saved link register, saved stack
+     pointer etc) and partly to make debugging easier (everything with a tag of
+     zero is space allocated automatically by the compiler).
+
+     This is not feasible when using random frame tags (the default
+     configuration for hwasan) since the tag for the given frame is randomly
+     chosen at runtime.  In order to avoid any tags matching the stack
+     background we would need to decide tag offsets at runtime instead of
+     compile time (and pay the resulting performance cost).
+
+     When not using random base tags for each frame (i.e. when compiled with
+     `--param hwasan-random-frame-tag=0`) the base tag for each frame is zero.
+     This means the tag that each object gets is equal to the tag_offset used
+     in determining it.
+     When this is the case we *can* ensure no object gets the tag of zero by
+     simply ensuring no object has the tag_offset of zero.  */
+  if (tag_offset == HWASAN_STACK_BACKGROUND
+      && ! param_hwasan_random_frame_tag)
+    tag_offset += 1;
+}
+
+/* For stack tagging:
+     Return an RTX representing `base + offset` address
+     and `tag_of(base) + tag_offset` tag.  */
+rtx
+hwasan_with_tag (rtx base, poly_int64 offset)
+{
+  gcc_assert (tag_offset < (1 << HWASAN_TAG_SIZE));
+  return targetm.memtag.addtag (base, offset, tag_offset);
+}
+
+/* Clear internal state for the next function.
+   This function is called before variables on the stack get expanded, in
+   `init_vars_expansion`.  */
+void
+hwasan_tag_init ()
+{
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
+  hwasan_base_ptr = NULL_RTX;
+  /* When not using a random frame tag we can avoid the background stack
+     colour which gives the user a little better debug output upon a crash.
+     Meanwhile, when using a random frame tag it will be nice to avoid adding
+     tags for the first object since that is unnecessary extra work.
+     Hence set the initial tag_offset to be 0 if using a random frame tag and 1
+     otherwise.  */
+  tag_offset = param_hwasan_random_frame_tag
+    ? 0
+    : HWASAN_STACK_BACKGROUND + 1;
+}
+
+/* Return an RTX for the tag of TAGGED_POINTER.  */
+rtx
+hwasan_extract_tag (rtx tagged_pointer)
+{
+  rtx tag = expand_simple_binop (Pmode,
+				 LSHIFTRT,
+				 tagged_pointer,
+				 HWASAN_SHIFT_RTX,
+				 NULL_RTX,
+				 /* unsignedp = */0,
+				 OPTAB_DIRECT);
+  return gen_lowpart (QImode, tag);
+}
+
+/* For stack tagging:
+      Does HWASAN equivalent of `asan_emit_stack_protection`.
+
+   Prologue sequence should be emitted directly, while the epilogue sequence is
+   returned.  The epilogue sequence is what should be used if we're not
+   protecting alloca objects.
+
+   BASES is an array containing the tagged base registers for each object.
+   We map each object to a given base since large aligned objects have a
+   different base to others and we need to know which objects use which base.
+
+   UNTAGGED_BASES contains the same information as above except without tags.
+   This is needed since libhwasan only accepts untagged pointers in
+   __hwasan_tag_memory.
+
+   OFFSETS is an array with the start and end offsets for each object stored on
+   the stack in this frame.  This array is hence twice the length of the other
+   array arguments (given it has two entries for each stack object).
+
+   TAGS is an array containing the tag *offset* each object should have from
+   the tag in its base pointer.
+
+   LENGTH contains the length of the OFFSETS array.  */
+rtx_insn *
+hwasan_emit_prologue (rtx *bases,
+		      rtx *untagged_bases,
+		      rtx frame_extent,
+		      poly_int64 *offsets,
+		      uint8_t *tags,
+		      size_t length)
+{
+  /* We need untagged base pointers since libhwasan only accepts untagged
+    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
+    the base tag for an offset.  */
+
+  if (length < 2)
+    return NULL;
+
+  poly_int64 bot = 0, top = 0;
+  size_t i = 0;
+  for (i = 0; (i * 2) + 1 < length; i++)
+    {
+      poly_int64 start = offsets[i * 2];
+      poly_int64 end = offsets[(i * 2) + 1];
+
+      if (known_ge (start, end))
+	{
+	  top = start;
+	  bot = end;
+	}
+      else
+	{
+	  /* Given how these values are calculated, one must be known greater
+	     than the other.  */
+	  gcc_assert (known_lt (start, end));
+	  top = end;
+	  bot = start;
+	}
+      poly_int64 size = (top - bot);
+
+      /* Can't check that all poly_int64's are aligned, but still nice
+	 to check those that are compile-time constants.  */
+      HOST_WIDE_INT tmp;
+      if (top.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (bot.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (size.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      rtx base_tag = hwasan_extract_tag (bases[i]);
+      /* In the case of tag overflow we would want modulo wrapping -- which
+	 should be given from the `plus_constant` in QImode.  */
+      rtx tag = plus_constant (QImode, base_tag, tags[i]);
+      rtx bottom = convert_memory_address (ptr_mode,
+					   plus_constant (Pmode,
+							  untagged_bases[i],
+							  bot));
+      emit_library_call (ret, LCT_NORMAL, VOIDmode,
+			 bottom, ptr_mode,
+			 tag, QImode,
+			 gen_int_mode (size, ptr_mode), ptr_mode);
+    }
+  return hwasan_emit_untag_frame (frame_extent, virtual_stack_vars_rtx);
+}
+
+/* For stack tagging:
+     Return RTL insns to clear the tags between DYNAMIC and VARS pointers
+     into the stack.  These instructions should be emitted at the end of
+     every function.  */
+rtx_insn *
+hwasan_emit_untag_frame (rtx dynamic, rtx vars)
+{
+  start_sequence ();
+
+  dynamic = convert_memory_address (ptr_mode, dynamic);
+  vars = convert_memory_address (ptr_mode, vars);
+
+  rtx top_rtx;
+  rtx bot_rtx;
+  if (FRAME_GROWS_DOWNWARD)
+    {
+      top_rtx = vars;
+      bot_rtx = dynamic;
+    }
+  else
+    {
+      top_rtx = dynamic;
+      bot_rtx = vars;
+    }
+
+  rtx size_rtx = expand_simple_binop (ptr_mode, MINUS, top_rtx, bot_rtx,
+				      NULL_RTX, /* unsignedp = */0,
+				      OPTAB_DIRECT);
+
+  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (ret, LCT_NORMAL, VOIDmode,
+      bot_rtx, ptr_mode,
+      const0_rtx, QImode,
+      size_rtx, ptr_mode);
+
+  do_pending_stack_adjust ();
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+/* For stack tagging:
+     Return an RTX representing ORIG_BASE without a tag.  */
+rtx
+hwasan_create_untagged_base (rtx orig_base)
+{
+  rtx untagged_base = gen_reg_rtx (Pmode);
+  rtx tag_mask = gen_int_mode ((1ULL << HWASAN_SHIFT) - 1, Pmode);
+  untagged_base = expand_binop (Pmode, and_optab,
+				orig_base, tag_mask,
+				untagged_base, true, OPTAB_DIRECT);
+  gcc_assert (untagged_base);
+  return untagged_base;
+}
+
+/* Needs to be GTY(()), because cgraph_build_static_cdtor may
+   invoke ggc_collect.  */
+static GTY(()) tree hwasan_ctor_statements;
+
+/* Insert module initialisation into this TU.  This initialisation calls the
+   initialisation code for libhwasan.  */
+void
+hwasan_finish_file (void)
+{
+  /* Do not emit constructor initialisation for the kernel.
+     (the kernel has its own initialisation already).  */
+  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+    return;
+
+  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
+  flag_sanitize &= ~SANITIZE_HWADDRESS;
+  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
+  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
+  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
+  flag_sanitize |= SANITIZE_HWADDRESS;
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index c46b1bc5cbd1fba03b033b8d44ba186570780c3f..b217f29b9f5ebd9924507490ecd3d1190fdb8a4b 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -637,6 +637,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
 DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
 DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_CONST_SIZE_BOOL,
 		     BT_PTR, BT_PTR, BT_CONST_SIZE, BT_BOOL)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_SIZE, BT_VOID, BT_PTR, BT_UINT8,
+		     BT_SIZE)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index ee67ac15d5cf98797144b9d08a75f4cf7ee5ad33..92121fb898bbf7a90aa0e43c65ff3fe7b20d7c99 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -250,6 +250,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
+				| SANITIZE_HWADDRESS \
 				| SANITIZE_UNDEFINED \
 				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index b270a4ddb9db469ba52e42f36a1bc2f02d8f03fc..13a7cb3877c27c4c45a445b7d2c068038d9c4568 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -377,7 +377,13 @@ align_local_variable (tree decl, bool really_expand)
       if (really_expand)
 	SET_DECL_ALIGN (decl, align);
     }
-  return align / BITS_PER_UNIT;
+
+  unsigned int ret_align = align / BITS_PER_UNIT;
+
+  if (hwasan_sanitize_stack_p ())
+    ret_align = MAX (ret_align, HWASAN_TAG_GRANULE_SIZE);
+
+  return ret_align;
 }
 
 /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
@@ -988,7 +994,7 @@ dump_stack_var_partition (void)
 
 static void
 expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
-			 poly_int64 offset)
+			 poly_int64 offset, rtx stack_base)
 {
   unsigned align;
   rtx x;
@@ -996,7 +1002,11 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   /* If this fails, we've overflowed the stack frame.  Error nicely?  */
   gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
 
-  x = plus_constant (Pmode, base, offset);
+  if (hwasan_sanitize_stack_p ())
+    x = hwasan_with_tag (base, offset);
+  else
+    x = plus_constant (Pmode, base, offset);
+
   x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
 		   ? TYPE_MODE (TREE_TYPE (decl))
 		   : DECL_MODE (SSAVAR (decl)), x);
@@ -1006,7 +1016,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
       /* Set alignment we actually gave this decl if it isn't an SSA name.
          If it is we generate stack slots only accidentally so it isn't as
 	 important, we'll simply use the alignment that is already set.  */
-      if (base == virtual_stack_vars_rtx)
+      if (base == stack_base)
 	offset -= frame_phase;
       align = known_alignment (offset);
       align *= BITS_PER_UNIT;
@@ -1032,9 +1042,23 @@ public:
      The vector is in reversed, highest offset pairs come first.  */
   auto_vec<HOST_WIDE_INT> asan_vec;
 
+  /* HWASAN records the poly_int64 so it can handle any stack variable.  */
+  auto_vec<poly_int64> hwasan_vec;
+  auto_vec<rtx> hwasan_untagged_base_vec;
+  auto_vec<rtx> hwasan_base_vec;
+
   /* Vector of partition representative decls in between the paddings.  */
   auto_vec<tree> asan_decl_vec;
 
+  /* Vector of tag offsets representing the tag for each stack variable.
+     Each offset determines the difference between the randomly generated
+     tag for the current frame and the tag for this stack variable.  */
+  auto_vec<uint8_t> hwasan_tag_vec;
+  /* RTX representing the extent of the frame (i.e. the farthest from
+     virtual_stack_vars_rtx that the statically allocated objects on the stack
+     reach.  */
+  rtx hwasan_frame_extent;
+
   /* Base pseudo register for Address Sanitizer protected automatic vars.  */
   rtx asan_base;
 
@@ -1045,13 +1069,13 @@ public:
 /* 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) (size_t), class stack_vars_data *data)
 {
   size_t si, i, j, n = stack_vars_num;
   poly_uint64 large_size = 0, large_alloc = 0;
   rtx large_base = NULL;
+  rtx large_untagged_base = NULL;
   unsigned large_align = 0;
   bool large_allocation_done = false;
   tree decl;
@@ -1098,11 +1122,17 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	}
     }
 
+  if (hwasan_sanitize_stack_p () && data->asan_base == NULL)
+    {
+      data->asan_base = gen_reg_rtx (Pmode);
+      hwasan_record_base (data->asan_base);
+    }
+
   for (si = 0; si < n; ++si)
     {
       rtx base;
       unsigned base_align, alignb;
-      poly_int64 offset;
+      poly_int64 offset = 0;
 
       i = stack_vars_sorted[si];
 
@@ -1123,10 +1153,36 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       if (pred && !pred (i))
 	continue;
 
+      base = hwasan_sanitize_stack_p ()
+	? data->asan_base
+	: virtual_stack_vars_rtx;
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  base = virtual_stack_vars_rtx;
+	  if (hwasan_sanitize_stack_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 and these can't share a tag granule with a
+		 tagged variable.  */
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	      offset = alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (offset);
+	      data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
+	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
 	  HOST_WIDE_INT prev_offset;
@@ -1206,6 +1262,14 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
 	      base_align = crtl->max_used_stack_slot_alignment;
 	    }
+
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      data->hwasan_vec.safe_push (offset);
+	      data->hwasan_frame_extent = plus_constant (Pmode,
+							 virtual_stack_vars_rtx,
+							 offset);
+	    }
 	}
       else
 	{
@@ -1225,14 +1289,36 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      loffset = alloc_stack_frame_space
 		(rtx_to_poly_int64 (large_allocsize),
 		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
-	      large_base = get_dynamic_stack_base (loffset, large_align);
+	      large_base = get_dynamic_stack_base (loffset, large_align, base);
 	      large_allocation_done = true;
 	    }
-	  gcc_assert (large_base != NULL);
 
+	  gcc_assert (large_base != NULL);
 	  large_alloc = aligned_upper_bound (large_alloc, alignb);
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* An object with a large alignment requirement means that the
+		 alignment requirement is greater than the required alignment
+		 for tags.  */
+	      if (!large_untagged_base)
+		large_untagged_base = hwasan_create_untagged_base (large_base);
+	      data->hwasan_vec.safe_push (large_alloc);
+	      data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
+	    }
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Ensure the end of the variable is also aligned correctly.  */
+	      poly_int64 align_again
+		= aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (align_again);
+	      if (FRAME_GROWS_DOWNWARD)
+		data->hwasan_frame_extent = large_untagged_base;
+	      else
+		data->hwasan_frame_extent
+		  = plus_constant (Pmode, large_untagged_base, align_again);
+	    }
 
 	  base = large_base;
 	  base_align = large_align;
@@ -1244,7 +1330,21 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	{
 	  expand_one_stack_var_at (stack_vars[j].decl,
 				   base, base_align,
-				   offset);
+				   offset,
+				   hwasan_sanitize_stack_p ()
+				   ? data->asan_base
+				   : virtual_stack_vars_rtx);
+	}
+
+      if (hwasan_sanitize_stack_p ())
+	{
+	  /* Record the tag for this object in `data` so the prologue knows
+	     what tag to put in the shadow memory during cfgexpand.c.
+	     Then increment the tag so that the next object has a different
+	     tag to this object.  */
+	  data->hwasan_base_vec.safe_push (base);
+	  data->hwasan_tag_vec.safe_push (hwasan_current_tag ());
+	  hwasan_increment_tag ();
 	}
     }
 
@@ -1341,7 +1441,8 @@ expand_one_stack_var_1 (tree var)
   offset = alloc_stack_frame_space (size, byte_align);
 
   expand_one_stack_var_at (var, virtual_stack_vars_rtx,
-			   crtl->max_used_stack_slot_alignment, offset);
+			   crtl->max_used_stack_slot_alignment, offset,
+			   virtual_stack_vars_rtx);
 }
 
 /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
@@ -1554,8 +1655,13 @@ 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 tagging memory we defer all stack variables so we can handle them in
+     one place (handle here meaning ensure they are aligned and record
+     information on each variables position in the stack).  */
+  if (flag_stack_protect
+      || asan_sanitize_stack_p ()
+      || hwasan_sanitize_stack_p ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME
@@ -1947,6 +2053,8 @@ init_vars_expansion (void)
   /* Initialize local stack smashing state.  */
   has_protected_decls = false;
   has_short_buffer = false;
+  if (hwasan_sanitize_stack_p ())
+    hwasan_tag_init ();
 }
 
 /* Free up stack variable graph data.  */
@@ -2269,12 +2377,28 @@ expand_used_vars (void)
 	}
 
       expand_stack_vars (NULL, &data);
+
+      if (hwasan_sanitize_stack_p ())
+	var_end_seq
+	  = hwasan_emit_prologue (data.hwasan_base_vec.address (),
+				  data.hwasan_untagged_base_vec.address (),
+				  data.hwasan_frame_extent,
+				  data.hwasan_vec.address (),
+				  data.hwasan_tag_vec.address (),
+				  data.hwasan_vec.length ());
     }
 
   if (asan_sanitize_allocas_p () && cfun->calls_alloca)
     var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
 					      virtual_stack_vars_rtx,
 					      var_end_seq);
+  else if (hwasan_sanitize_allocas_p () && cfun->calls_alloca)
+    /* Here we replace `var_end_seq` while for asan we append to `var_end_seq`.
+       This is for the out-of-line case which uses only one function call in
+       either case (and hence we want to replace the previous function call if
+       we're changing things).  */
+    var_end_seq = hwasan_emit_untag_frame (virtual_stack_dynamic_rtx,
+					   virtual_stack_vars_rtx);
 
   fini_vars_expansion ();
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 0a1131962747fbd172a137c28606eb2b62372383..1ad1078b78a2c114f8bbfb8ca6b6b81adb363588 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2979,6 +2979,31 @@ A target hook which lets a backend compute the set of pressure classes to  be us
 True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that @option{-fsanitize=hwaddress} can work.
 @end deftypefn
 
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
+Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by TAG_OFFSET.
+The resulting RTX must either be a valid memory address or be able to get
+put into an operand with force_operand.  If overridden the more common case
+is that we force this into an operand using the backend hook
+"addtag_force_operand" that is called in force_operand.
+
+It is expected that that "addtag_force_operand" recognises the RTX
+generated by "addtag" and emits code to force that RTX into an operand.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG_FORCE_OPERAND (rtx @var{oper}, rtx @var{target})
+If the RTL expression OPER is of the form generated by
+targetm.memtag.addtag, then emit instructions to move the value into an
+operand (i.e. for force_operand).
+TARGET is an RTX suggestion of where to generate the value.
+This hook is most often implemented by emitting instructions to put the
+expression into a pseudo register, then returning that pseudo register.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_MEMTAG_GENTAG (rtx @var{base}, rtx @var{untagged})
+Set the BASE argument to UNTAGGED with some random tag.
+This function is used to generate a tagged base for the current stack frame.
+@end deftypefn
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 80960c1fe041bd08ccc22a4c41ebf740eca80015..140affd83529768e410957dc8f8058c6e39d698c 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2377,6 +2377,12 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_ADDTAG
+
+@hook TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
+
+@hook TARGET_MEMTAG_GENTAG
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/explow.h b/gcc/explow.h
index 0df8c62b82a8bf1d8d6baf0b6fb658e66361a407..581831cb19fdf9e8fd969bb30139e1358279a34d 100644
--- a/gcc/explow.h
+++ b/gcc/explow.h
@@ -106,7 +106,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
 extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
 
 /* Returns the address of the dynamic stack space without allocating it.  */
-extern rtx get_dynamic_stack_base (poly_int64, unsigned);
+extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
 
 /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
 extern rtx align_dynamic_address (rtx, unsigned);
diff --git a/gcc/explow.c b/gcc/explow.c
index 0fbc6d25b816457a3d13ed45d16b5dd0513cfacd..41c3f6ace49c0e55c080e10b917842b1b21d49eb 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1583,10 +1583,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
    OFFSET is the offset of the area into the virtual stack vars area.
 
    REQUIRED_ALIGN is the alignment (in bits) required for the region
-   of memory.  */
+   of memory.
+
+   BASE is the rtx of the base of this virtual stack vars area.
+   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+   on the stack.  */
 
 rtx
-get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
 {
   rtx target;
 
@@ -1594,7 +1598,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
     crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
 
   target = gen_reg_rtx (Pmode);
-  emit_move_insn (target, virtual_stack_vars_rtx);
+  emit_move_insn (target, base);
   target = expand_binop (Pmode, add_optab, target,
 			 gen_int_mode (offset, Pmode),
 			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
diff --git a/gcc/expr.c b/gcc/expr.c
index b4bbeffe3ce095674e8528286e8f013cc72de32e..6b89e1704792d4336e7b3287a54c197b442c42db 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -7645,6 +7645,13 @@ force_operand (rtx value, rtx target)
       return subtarget;
     }
 
+  if (targetm.memtag.addtag_force_operand)
+    {
+      rtx ret = targetm.memtag.addtag_force_operand (value, target);
+      if (ret)
+	return ret;
+    }
+
   if (ARITHMETIC_P (value))
     {
       op2 = XEXP (value, 1);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index a32715ddb92e69b7ca7be28a8f17a369b891bd76..39b9b05a75ff9d80663a5e0d5edbb73216be84a8 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
+/* Hardware Address Sanitizer.  */
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
+		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
+
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/target.def b/gcc/target.def
index 43b491b027615523f0202fe1ded430cac0b4bef4..145a631ceb5239a9f099ef531668603cc2f7aea6 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6839,6 +6839,37 @@ DEFHOOK
  pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
  bool, (), default_memtag_can_tag_addresses)
 
+DEFHOOK
+(addtag,
+ "Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by\
+ TAG_OFFSET.\n\
+The resulting RTX must either be a valid memory address or be able to get\n\
+put into an operand with force_operand.  If overridden the more common case\n\
+is that we force this into an operand using the backend hook\n\
+\"addtag_force_operand\" that is called in force_operand.\n\
+\n\
+It is expected that that \"addtag_force_operand\" recognises the RTX\n\
+generated by \"addtag\" and emits code to force that RTX into an operand.",
+rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
+default_memtag_addtag)
+
+DEFHOOK
+(addtag_force_operand,
+ "If the RTL expression OPER is of the form generated by\n\
+targetm.memtag.addtag, then emit instructions to move the value into an\n\
+operand (i.e. for force_operand).\n\
+TARGET is an RTX suggestion of where to generate the value.\n\
+This hook is most often implemented by emitting instructions to put the\n\
+expression into a pseudo register, then returning that pseudo register.",
+rtx, (rtx oper, rtx target), NULL)
+
+DEFHOOK
+(gentag,
+ "Set the BASE argument to UNTAGGED with some random tag.\n\
+This function is used to generate a tagged base for the current stack frame.",
+  void, (rtx base, rtx untagged),
+  default_memtag_gentag)
+
 HOOK_VECTOR_END (memtag)
 #undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index c11b1879f65bb581af11963b40189028fa490623..9e196c8798f9b6be369a34faba4823a41a16d724 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -286,4 +286,6 @@ extern bool speculation_safe_value_not_needed (bool);
 extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 
 extern bool default_memtag_can_tag_addresses ();
+extern void default_memtag_gentag (rtx, rtx);
+extern rtx default_memtag_addtag (rtx, poly_int64, uint8_t);
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 8c860f91cae7ccb5ca7fb0ff2364c1f67620d661..69ffccfd9714f662e0d42ac97908c955990b1c58 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "flags.h"
 #include "explow.h"
+#include "expmed.h"
 #include "calls.h"
 #include "expr.h"
 #include "output.h"
@@ -83,6 +84,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "sbitmap.h"
 #include "function-abi.h"
+#include "attribs.h"
+#include "asan.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -2385,4 +2388,89 @@ default_memtag_can_tag_addresses ()
   return false;
 }
 
+/* Takes a REG rtx as BASE and some value as UNTAGGED.
+   Ensures the register in BASE is given the value of UNTAGGED with the
+   (possibly random) base frame tag on it.  */
+void
+default_memtag_gentag (rtx base, rtx untagged)
+{
+  gcc_assert (param_hwasan_instrument_stack);
+  if (param_hwasan_random_frame_tag)
+    {
+    rtx temp = gen_reg_rtx (QImode);
+    rtx ret = init_one_libfunc ("__hwasan_generate_tag");
+    rtx new_tag = emit_library_call_value (ret, temp, LCT_NORMAL, QImode);
+    emit_move_insn (base, untagged);
+    /* We know that `base` is not the stack pointer, since we never want to put
+      a randomly generated tag into the stack pointer.  Hence we can use
+      `store_bit_field` which on aarch64 generates a `bfi` which can not act on
+      the stack pointer.  */
+    store_bit_field (base, 8, 56, 0, 0, QImode, new_tag, false);
+    }
+  else
+    {
+      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
+	 In the future we may add the option emit random tags with inline
+	 instrumentation instead of function calls.  This would be the same
+	 between the kernel and userland.  */
+      emit_move_insn (base, untagged);
+    }
+}
+
+rtx
+default_memtag_addtag (rtx base, poly_int64 offset, uint8_t tag_offset)
+{
+  /* Need to look into what the most efficient code sequence is.
+     This is a code sequence that would be emitted *many* times, so we
+     want it as small as possible.
+
+     If the tag offset is greater that (1 << 7) then the most efficient
+     sequence here would give UB from signed integer overflow in the
+     poly_int64.  Hence in that case we emit the slightly less efficient
+     sequence.
+
+     There are two places where tag overflow is a question:
+       - Tagging the shadow stack.
+	  (both tagging and untagging).
+       - Tagging addressable pointers.
+
+     We need to ensure both behaviours are the same (i.e. that the tag that
+     ends up in a pointer after "overflowing" the tag bits with a tag addition
+     is the same that ends up in the shadow space).
+
+     The aim is that the behaviour of tag addition should follow modulo
+     wrapping in both instances.
+
+     The libhwasan code doesn't have any path that increments a pointers tag,
+     which means it has no opinion on what happens when a tag increment
+     overflows (and hence we can choose our own behaviour).
+
+     NOTE:
+	Here we return an expression which represents the base with the
+	provided offsets.
+	This does not have to be a valid operand to anything, since the
+	`force_operand` machinery in the compiler already handles this.  */
+
+  if (tag_offset < (1 << 7))
+    {
+      offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
+      return plus_constant (Pmode, base, offset);
+    }
+  else
+    {
+      /* This is the fallback, it would be nice if it had less instructions,
+	 but we can look for cleverer ways later.  */
+      uint64_t tag_mask = ~(0xFFUL << HWASAN_SHIFT);
+      rtx untagged_base = gen_rtx_AND (Pmode, GEN_INT (tag_mask), base);
+      rtx new_addr = plus_constant (Pmode, untagged_base, offset);
+
+      rtx original_tag_value = gen_rtx_LSHIFTRT (Pmode, base,
+						 GEN_INT (HWASAN_SHIFT));
+      rtx new_tag_value = plus_constant (Pmode, original_tag_value, tag_offset);
+      rtx new_tag = gen_rtx_ASHIFT (Pmode, new_tag_value,
+				    GEN_INT (HWASAN_SHIFT));
+      return gen_rtx_IOR (Pmode, new_addr, new_tag);
+    }
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index cfbfc30ccfac9bd3bd3067bdddcdec71159a64a2..42aa4bdad9779179b9b6270b5342c99b53e1ca39 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -510,6 +510,9 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      if (flag_sanitize & SANITIZE_HWADDRESS)
+	hwasan_finish_file ();
+
       omp_finish_file ();
 
       hsa_output_brig ();



More information about the Gcc-patches mailing list