diff mbox series

[4/X] libsanitizer: options: Add hwasan flags and argument parsing

Message ID AM6PR08MB315727107E58737DEDBB29CDE0E30@AM6PR08MB3157.eurprd08.prod.outlook.com
State New
Headers show
Series [4/X] libsanitizer: options: Add hwasan flags and argument parsing | expand

Commit Message

Matthew Malcomson Nov. 16, 2020, 3:37 p.m. UTC
These flags can't be used at the same time as any of the other
sanitizers.
We add an equivalent flag to -static-libasan in -static-libhwasan to
ensure static linking.

The -fsanitize=kernel-hwaddress option is for compiling targeting the
kernel.  This flag has defaults to match the LLVM implementation and
sets some other behaviors to work in the kernel (e.g. accounting for
the fact that the stack pointer will have 0xff in the top byte and to not
call the userspace library initialisation routines).
The defaults are that we do not sanitize variables on the stack and
always recover from a detected bug.

Since we are introducing a few more conflicts between sanitizer flags we
refactor the checking for such conflicts to use a helper function which
makes checking for such conflicts more easy and consistent.

We introduce a backend hook `targetm.memtag.can_tag_addresses` that
indicates to the mid-end whether a target has a feature like AArch64 TBI
where the top byte of an address is ignored.
Without this feature hwasan sanitization is not done.

gcc/ChangeLog:

	* common.opt (flag_sanitize_recover): Default for kernel
	hwaddress.
	(static-libhwasan): New cli option.
	* config/aarch64/aarch64.c (aarch64_can_tag_addresses): New.
	(TARGET_MEMTAG_CAN_TAG_ADDRESSES): New.
	* config/gnu-user.h (LIBHWASAN_EARLY_SPEC): hwasan equivalent of
	asan command line flags.
	* cppbuiltin.c (define_builtin_macros_for_compilation_flags):
	Add hwasan equivalent of __SANITIZE_ADDRESS__.
	* doc/invoke.texi: Document hwasan command line flags.
	* doc/tm.texi: Document new hook.
	* doc/tm.texi.in: Document new hook.
	* flag-types.h (enum sanitize_code): New sanitizer values.
	* gcc.c (STATIC_LIBHWASAN_LIBS): New macro.
	(LIBHWASAN_SPEC): New macro.
	(LIBHWASAN_EARLY_SPEC): New macro.
	(SANITIZER_EARLY_SPEC): Update to include hwasan.
	(SANITIZER_SPEC): Update to include hwasan.
	(sanitize_spec_function): Use hwasan options.
	* opts.c (finish_options): Describe conflicts between address
	sanitizers.
	(find_argument): New.
	(report_conflicting_sanitizer_options): New.
	(sanitizer_opts): Introduce new sanitizer flags.
	(common_handle_option): Add defaults for kernel sanitizer.
	* params.opt (hwasan--instrument-stack): New
	(hwasan-random-frame-tag): New
	(hwasan-instrument-allocas): New
	(hwasan-instrument-reads): New
	(hwasan-instrument-writes): New
	(hwasan-instrument-mem-intrinsics): New
	* target.def (HOOK_PREFIX): Add new hook.
	(can_tag_addresses): Add new hook under memtag prefix.
	* targhooks.c (default_memtag_can_tag_addresses): New.
	* targhooks.h (default_memtag_can_tag_addresses): New decl.
	* toplev.c (process_options): Ensure hwasan only on
	architectures that advertise the possibility.



###############     Attachment also inlined for ease of reply    ###############
diff --git a/gcc/common.opt b/gcc/common.opt
index d4cbb2f86a554fa2f87e52b2b6cbd1de27ddaa31..c7eec0fe683b817233ab4572024c16542a646cb4 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -218,7 +218,7 @@ unsigned int flag_sanitize
 
 ; What sanitizers should recover from errors
 Variable
-unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
+unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS | SANITIZE_KERNEL_HWADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
 
 ; What the coverage sanitizers should instrument
 Variable
@@ -3429,6 +3429,9 @@ Driver
 static-libasan
 Driver
 
+static-libhwasan
+Driver
+
 static-libtsan
 Driver
 
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 27f587be7e7483093f1fd381d8fa14a6c6581ccf..ea85aadb619eb2e9ee7328f035f81aa8578ee3ad 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -23175,6 +23175,15 @@ aarch64_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1,
   return NULL;
 }
 
+/* Implement TARGET_MEMTAG_CAN_TAG_ADDRESSES.  Here we tell the rest of the
+   compiler that we automatically ignore the top byte of our pointers, which
+   allows using -fsanitize=hwaddress.  */
+bool
+aarch64_can_tag_addresses ()
+{
+  return true;
+}
+
 /* Implement TARGET_ASM_FILE_END for AArch64.  This adds the AArch64 GNU NOTE
    section at the end if needed.  */
 #define GNU_PROPERTY_AARCH64_FEATURE_1_AND	0xc0000000
@@ -23994,6 +24003,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_FNTYPE_ABI
 #define TARGET_FNTYPE_ABI aarch64_fntype_abi
 
+#undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
+#define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h
index ff2e880b1fa81b76af866b6ba0e1fabf6d534f32..92950245e2b95c500da9df8b85940380e62030fa 100644
--- a/gcc/config/gnu-user.h
+++ b/gcc/config/gnu-user.h
@@ -129,14 +129,18 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Link -lasan early on the command line.  For -static-libasan, don't link
    it for -shared link, the executable should be compiled with -static-libasan
    in that case, and for executable link with --{,no-}whole-archive around
-   it to force everything into the executable.  And similarly for -ltsan
-   and -llsan.  */
+   it to force everything into the executable.  And similarly for -ltsan,
+   -lhwasan, and -llsan.  */
 #if defined(HAVE_LD_STATIC_DYNAMIC)
 #undef LIBASAN_EARLY_SPEC
 #define LIBASAN_EARLY_SPEC "%{!shared:libasan_preinit%O%s} " \
   "%{static-libasan:%{!shared:" \
   LD_STATIC_OPTION " --whole-archive -lasan --no-whole-archive " \
   LD_DYNAMIC_OPTION "}}%{!static-libasan:-lasan}"
+#undef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC "%{static-libhwasan:%{!shared:" \
+  LD_STATIC_OPTION " --whole-archive -lhwasan --no-whole-archive " \
+  LD_DYNAMIC_OPTION "}}%{!static-libhwasan:-lhwasan}"
 #undef LIBTSAN_EARLY_SPEC
 #define LIBTSAN_EARLY_SPEC "%{!shared:libtsan_preinit%O%s} " \
   "%{static-libtsan:%{!shared:" \
diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c
index 61efe9b2b75e961c15e9f399cafed559b5c8c9be..fc61c78a99373fcfbc2bb03a01134248214eb2d4 100644
--- a/gcc/cppbuiltin.c
+++ b/gcc/cppbuiltin.c
@@ -93,6 +93,9 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile)
   if (flag_sanitize & SANITIZE_ADDRESS)
     cpp_define (pfile, "__SANITIZE_ADDRESS__");
 
+  if (flag_sanitize & SANITIZE_HWADDRESS)
+    cpp_define (pfile, "__SANITIZE_HWADDRESS__");
+
   if (flag_sanitize & SANITIZE_THREAD)
     cpp_define (pfile, "__SANITIZE_THREAD__");
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 5320e6c1e1e3c8d1482c20590049f763e11f8ff0..84050058be8eaa306b07655737e49ea8b6eb21a9 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13709,6 +13709,53 @@ is greater or equal to this number, use callbacks instead of inline checks.
 E.g. to disable inline code use
 @option{--param asan-instrumentation-with-call-threshold=0}.
 
+@item hwasan-instrument-stack
+Enable hwasan instrumentation of statically sized stack-allocated variables.
+This kind of instrumentation is enabled by default when using
+@option{-fsanitize=hwaddress} and disabled by default when using
+@option{-fsanitize=kernel-hwaddress}.
+To disable stack instrumentation use
+@option{--param hwasan-instrument-stack=0}, and to enable it use
+@option{--param hwasan-instrument-stack=1}.
+
+@item hwasan-random-frame-tag
+When using stack instrumentation, decide tags for stack variables using a
+deterministic sequence beginning at a random tag for each frame.  Usually tags
+are chosen using the same sequence beginning from 1.
+This is enabled by default for @option{-fsanitize=hwaddress} and unavailable
+for @option{-fsanitize=kernel-hwaddress}.
+To disable it use @option{--param hwasan-random-frame-tag=0}.
+
+@item hwasan-instrument-allocas
+Enable hwasan instrumentation of dynamically sized stack-allocated variables.
+This kind of instrumentation is enabled by default when using
+@option{-fsanitize=hwaddress} and disabled by default when using
+@option{-fsanitize=kernel-hwaddress}.
+To disable instrumentation of such variables use
+@option{--param hwasan-instrument-allocas=0}, and to enable it use
+@option{--param hwasan-instrument-allocas=1}.
+
+@item hwasan-instrument-reads
+Enable hwasan checks on memory reads.  Instrumentation of reads is enabled by
+default for both @option{-fsanitize=hwaddress} and
+@option{-fsanitize=kernel-hwaddress}.
+To disable checking memory reads use
+@option{--param hwasan-instrument-reads=0}.
+
+@item hwasan-instrument-writes
+Enable hwasan checks on memory writes.  Instrumentation of writes is enabled by
+default for both @option{-fsanitize=hwaddress} and
+@option{-fsanitize=kernel-hwaddress}.
+To disable checking memory writes use
+@option{--param hwasan-instrument-writes=0}.
+
+@item hwasan-instrument-mem-intrinsics
+Enable hwasan instrumentation of builtin functions.  Instrumentation of these
+builtin functions is enabled by default for both @option{-fsanitize=hwaddress}
+and @option{-fsanitize=kernel-hwaddress}.
+To disable instrumentation of builtin functions use
+@option{--param hwasan-instrument-mem-intrinsics=0}.
+
 @item use-after-scope-direct-emission-threshold
 If the size of a local variable in bytes is smaller or equal to this
 number, directly poison (or unpoison) shadow memory instead of using
@@ -14243,13 +14290,46 @@ more details.  The run-time behavior can be influenced using the
 the available options are shown at startup of the instrumented program.  See
 @url{https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags}
 for a list of supported options.
-The option cannot be combined with @option{-fsanitize=thread}.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=hwaddress}.  Note that the only target this option is
+currently supported on is AArch64.
 
 @item -fsanitize=kernel-address
 @opindex fsanitize=kernel-address
 Enable AddressSanitizer for Linux kernel.
 See @uref{https://github.com/google/kasan/wiki} for more details.
 
+@item -fsanitize=hwaddress
+@opindex fsanitize=hwaddress
+Enable Hardware-assisted AddressSanitizer, which uses a hardware ability to
+ignore the top byte of a pointer to allow the detection of memory errors with
+a low memory overhead.
+Memory access instructions are instrumented to detect out-of-bounds and
+use-after-free bugs.
+The option enables @option{-fsanitize-address-use-after-scope}.
+See
+@uref{https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html}
+for more details.  The run-time behavior can be influenced using the
+@env{HWASAN_OPTIONS} environment variable.  When set to @code{help=1},
+the available options are shown at startup of the instrumented program.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=address}, and is currently only available on AArch64.
+
+@item -fsanitize=kernel-hwaddress
+@opindex fsanitize=kernel-hwaddress
+Enable Hardware-assisted AddressSanitizer for compilation of the Linux kernel.
+Similar to @option{-fsanitize=kernel-address} but using an alternate
+instrumentation method, and similar to @option{-fsanitize=hwaddress} but with
+instrumentation differences necessary for compiling the Linux kernel.
+These differences are to avoid hwasan library initialisation calls and to
+account for the stack pointer having a different value in its top byte.
+Note: This option has different defaults to the @option{-fsanitize=hwaddress}.
+Instrumenting the stack and alloca calls are not on by default but is still
+possible by specifying it on the command line with
+@option{--param hwasan-instrument-stack=1} and
+@option{--param hwasan-instrument-allocas=1}. Using a random frame tag is not
+implemented for kernel instrumentation.
+
 @item -fsanitize=pointer-compare
 @opindex fsanitize=pointer-compare
 Instrument comparison operation (<, <=, >, >=) with pointer operands.
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 833320ba7bfbea332d41cd38641c19ed9593366d..321b0fe35816a257e42a1464a4ac84e849d376f2 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2975,6 +2975,11 @@ This hook defines the machine mode to use for the boolean result of  conditional
 A target hook which lets a backend compute the set of pressure classes to  be used by those optimization passes which take register pressure into  account, as opposed to letting IRA compute them.  It returns the number of  register classes stored in the array @var{pressure_classes}.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_MEMTAG_CAN_TAG_ADDRESSES ()
+True if backend architecture naturally supports ignoring some region of
+pointers.  This feature means that @option{-fsanitize=hwaddress} can work.
+@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 58109be36932d89085240557da7be5664605946a..6b6313c8493636f3615cd9e5bc6cb091467a4a8e 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2375,6 +2375,8 @@ in the reload pass.
 
 @hook TARGET_COMPUTE_PRESSURE_CLASSES
 
+@hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index a887c75cfc792030661c3b004c9569e6816cde8f..8015fa44e06bb1f3e59288761a6097d67f4cbc20 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -274,6 +274,9 @@ enum sanitize_code {
   SANITIZE_BUILTIN = 1UL << 25,
   SANITIZE_POINTER_COMPARE = 1UL << 26,
   SANITIZE_POINTER_SUBTRACT = 1UL << 27,
+  SANITIZE_HWADDRESS = 1UL << 28,
+  SANITIZE_USER_HWADDRESS = 1UL << 29,
+  SANITIZE_KERNEL_HWADDRESS = 1UL << 30,
   SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
diff --git a/gcc/gcc.c b/gcc/gcc.c
index cdf4d4f2403e6236ff0f33c4def95bc445a51dee..69f6fdcb324342b0153b64af0b5d607e0a555d8a 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -743,6 +743,24 @@ proper position among the other output files.  */
 #define LIBASAN_EARLY_SPEC ""
 #endif
 
+#ifndef LIBHWASAN_SPEC
+#define STATIC_LIBHWASAN_LIBS \
+  " %{static-libhwasan|static:%:include(libsanitizer.spec)%(link_libhwasan)}"
+#ifdef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_SPEC STATIC_LIBHWASAN_LIBS
+#elif defined(HAVE_LD_STATIC_DYNAMIC)
+#define LIBHWASAN_SPEC "%{static-libhwasan:" LD_STATIC_OPTION \
+		     "} -lhwasan %{static-libhwasan:" LD_DYNAMIC_OPTION "}" \
+		     STATIC_LIBHWASAN_LIBS
+#else
+#define LIBHWASAN_SPEC "-lhwasan" STATIC_LIBHWASAN_LIBS
+#endif
+#endif
+
+#ifndef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC ""
+#endif
+
 #ifndef LIBTSAN_SPEC
 #define STATIC_LIBTSAN_LIBS \
   " %{static-libtsan|static:%:include(libsanitizer.spec)%(link_libtsan)}"
@@ -1065,6 +1083,7 @@ proper position among the other output files.  */
 #ifndef SANITIZER_EARLY_SPEC
 #define SANITIZER_EARLY_SPEC "\
 %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_EARLY_SPEC "} \
+    %{%:sanitize(hwaddress):" LIBHWASAN_EARLY_SPEC "} \
     %{%:sanitize(thread):" LIBTSAN_EARLY_SPEC "} \
     %{%:sanitize(leak):" LIBLSAN_EARLY_SPEC "}}}}"
 #endif
@@ -1074,6 +1093,8 @@ proper position among the other output files.  */
 #define SANITIZER_SPEC "\
 %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_SPEC "\
     %{static:%ecannot specify -static with -fsanitize=address}}\
+    %{%:sanitize(hwaddress):" LIBHWASAN_SPEC "\
+	%{static:%ecannot specify -static with -fsanitize=hwaddress}}\
     %{%:sanitize(thread):" LIBTSAN_SPEC "\
     %{static:%ecannot specify -static with -fsanitize=thread}}\
     %{%:sanitize(undefined):" LIBUBSAN_SPEC "}\
@@ -10125,8 +10146,12 @@ sanitize_spec_function (int argc, const char **argv)
 
   if (strcmp (argv[0], "address") == 0)
     return (flag_sanitize & SANITIZE_USER_ADDRESS) ? "" : NULL;
+  if (strcmp (argv[0], "hwaddress") == 0)
+    return (flag_sanitize & SANITIZE_USER_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "kernel-address") == 0)
     return (flag_sanitize & SANITIZE_KERNEL_ADDRESS) ? "" : NULL;
+  if (strcmp (argv[0], "kernel-hwaddress") == 0)
+    return (flag_sanitize & SANITIZE_KERNEL_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "thread") == 0)
     return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
   if (strcmp (argv[0], "undefined") == 0)
diff --git a/gcc/opts.c b/gcc/opts.c
index ac9972d9c386247af3482e07a94c76da3e1abb4d..f3662062c421e1c58c3243109891900eb2dc84bc 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -823,6 +823,51 @@ control_options_for_live_patching (struct gcc_options *opts,
 /* --help option argument if set.  */
 vec<const char *> help_option_arguments;
 
+/* Return the string name describing the argument provided on the command line
+    which has set this particular flag.  */
+const char *
+find_argument (struct gcc_options *opts, unsigned int flags)
+{
+  for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
+    {
+      /* Need to find the sanitizer_opts element which:
+	 a) Could have set the flags requested.
+	 b) Has been set on the command line.
+
+	 Can have (a) without (b) if the flag requested is e.g.
+	 SANITIZE_ADDRESS, since both -fsanitize=address and
+	 -fsanitize=kernel-address set this flag.
+
+	 Can have (b) without (a) by requesting more than one sanitizer on the
+	 command line.  */
+      if ((sanitizer_opts[i].flag & opts->x_flag_sanitize)
+	  != sanitizer_opts[i].flag)
+	continue;
+      if ((sanitizer_opts[i].flag & flags) != flags)
+	continue;
+      return sanitizer_opts[i].name;
+    }
+  return NULL;
+}
+
+
+/* Report any conflicting sanitizer options requested.  */
+static void
+report_conflicting_sanitizer_options (struct gcc_options *opts, location_t loc,
+				      unsigned int left, unsigned int right)
+{
+  unsigned int left_seen = (opts->x_flag_sanitize & left);
+  unsigned int right_seen = (opts->x_flag_sanitize & right);
+  if (left_seen && right_seen)
+    {
+      const char* left_arg = find_argument (opts, left_seen);
+      const char* right_arg = find_argument (opts, right_seen);
+      gcc_assert (left_arg && right_arg);
+      error_at (loc,
+		"%<-fsanitize=%s%> is incompatible with %<-fsanitize=%s%>",
+		left_arg, right_arg);
+    }
+}
 
 /* After all options at LOC have been read into OPTS and OPTS_SET,
    finalize settings of those options and diagnose incompatible
@@ -1074,22 +1119,21 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 		  "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>");
     }
 
-  /* Userspace and kernel ASan conflict with each other.  */
-  if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
-      && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
-    error_at (loc, "%qs is incompatible with %qs",
-	      "-fsanitize=address", "-fsanitize=kernel-address");
+  /* Address sanitizers conflict with the thread sanitizer.  */
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_THREAD,
+					SANITIZE_ADDRESS | SANITIZE_HWADDRESS);
+  /* The leak sanitizer conflicts with the thread sanitizer.  */
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_LEAK, SANITIZE_THREAD);
 
-  /* And with TSan.  */
-  if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
-      && (opts->x_flag_sanitize & SANITIZE_THREAD))
-    error_at (loc, "%qs is incompatible with %qs",
-	      "-fsanitize=thread", "-fsanitize=address|kernel-address");
+  /* No combination of HWASAN and ASAN work together.  */
+  report_conflicting_sanitizer_options (opts, loc,
+					SANITIZE_HWADDRESS, SANITIZE_ADDRESS);
 
-  if ((opts->x_flag_sanitize & SANITIZE_LEAK)
-      && (opts->x_flag_sanitize & SANITIZE_THREAD))
-    error_at (loc, "%qs is incompatible with %qs",
-	      "-fsanitize=leak", "-fsanitize=thread");
+  /* The userspace and kernel address sanitizers conflict with each other.  */
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_USER_HWADDRESS,
+					SANITIZE_KERNEL_HWADDRESS);
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_USER_ADDRESS,
+					SANITIZE_KERNEL_ADDRESS);
 
   /* Check error recovery for -fsanitize-recover option.  */
   for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
@@ -1108,9 +1152,10 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
   if (opts->x_flag_sanitize & ~(SANITIZE_LEAK | SANITIZE_UNREACHABLE))
     opts->x_flag_aggressive_loop_optimizations = 0;
 
-  /* Enable -fsanitize-address-use-after-scope if address sanitizer is
+  /* Enable -fsanitize-address-use-after-scope if either address sanitizer is
      enabled.  */
-  if (opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+  if (opts->x_flag_sanitize
+      & (SANITIZE_USER_ADDRESS | SANITIZE_USER_HWADDRESS))
     SET_OPTION_IF_UNSET (opts, opts_set, flag_sanitize_address_use_after_scope,
 			 true);
 
@@ -1724,8 +1769,13 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 #define SANITIZER_OPT(name, flags, recover) \
     { #name, flags, sizeof #name - 1, recover }
   SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true),
+  SANITIZER_OPT (hwaddress, (SANITIZE_HWADDRESS | SANITIZE_USER_HWADDRESS),
+		 true),
   SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
 		 true),
+  SANITIZER_OPT (kernel-hwaddress,
+		 (SANITIZE_HWADDRESS | SANITIZE_KERNEL_HWADDRESS),
+		 true),
   SANITIZER_OPT (pointer-compare, SANITIZE_POINTER_COMPARE, true),
   SANITIZER_OPT (pointer-subtract, SANITIZE_POINTER_SUBTRACT, true),
   SANITIZER_OPT (thread, SANITIZE_THREAD, false),
@@ -2304,6 +2354,14 @@ common_handle_option (struct gcc_options *opts,
 	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_protect_allocas, 0);
 	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_use_after_return, 0);
 	}
+      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+	{
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_instrument_stack, 0);
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_random_frame_tag, 0);
+	  SET_OPTION_IF_UNSET (opts, opts_set, param_hwasan_protect_allocas, 0);
+	}
       break;
 
     case OPT_fsanitize_recover_:
diff --git a/gcc/params.opt b/gcc/params.opt
index a33a371a395df179dc8b1ed2b7ef9e9120d384fa..75f89b168781080e0b9bf119c936cdb6c5b96b66 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -62,6 +62,30 @@ Enable asan stack protection.
 Common Joined UInteger Var(param_asan_use_after_return) Init(1) IntegerRange(0, 1) Param Optimization
 Enable asan detection of use-after-return bugs.
 
+-param=hwasan-instrument-stack=
+Common Joined UInteger Var(param_hwasan_instrument_stack) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of statically sized stack-allocated variables.
+
+-param=hwasan-random-frame-tag=
+Common Joined UInteger Var(param_hwasan_random_frame_tag) Init(1) IntegerRange(0, 1) Param Optimization
+Use random base tag for each frame, as opposed to base always zero.
+
+-param=hwasan-instrument-allocas=
+Common Joined UInteger Var(param_hwasan_protect_allocas) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of allocas/VLAs.
+
+-param=hwasan-instrument-reads=
+Common Joined UInteger Var(param_hwasan_instrument_reads) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of load operations.
+
+-param=hwasan-instrument-writes=
+Common Joined UInteger Var(param_hwasan_instrument_writes) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of store operations.
+
+-param=hwasan-instrument-mem-intrinsics=
+Common Joined UInteger Var(param_hwasan_instrument_mem_intrinsics) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of builtin functions.
+
 -param=avg-loop-niter=
 Common Joined UInteger Var(param_avg_loop_niter) Init(10) IntegerRange(1, 65536) Param Optimization
 Average number of iterations of a loop.
diff --git a/gcc/target.def b/gcc/target.def
index b916635be1816ee05327736e3ac4631a1214fba9..dc20d262fd1b7da074bb15ff2cc06f9164a4ae17 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6850,6 +6850,17 @@ DEFHOOK
 HOOK_VECTOR_END (mode_switching)
 
 #undef HOOK_PREFIX
+#define HOOK_PREFIX "TARGET_MEMTAG_"
+HOOK_VECTOR (TARGET_MEMTAG_, memtag)
+
+DEFHOOK
+(can_tag_addresses,
+ "True if backend architecture naturally supports ignoring some region of\n\
+pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
+ bool, (), default_memtag_can_tag_addresses)
+
+HOOK_VECTOR_END (memtag)
+#undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
 
 #define DEF_TARGET_INSN(NAME, PROTO) \
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index e0a925fa2bee2e73f82d244fe4364afc87eff7d2..0065c686978d7120978430013c73b1055aaf95c7 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -286,4 +286,5 @@ extern bool default_have_speculation_safe_value (bool);
 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 ();
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 5b68a2ad7d4a435d66fad68ab8827f59289e81f4..46cb536041d396c32fd08042581d6d5cd5ad0395 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2415,4 +2415,10 @@ default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
   return result;
 }
 
+bool
+default_memtag_can_tag_addresses ()
+{
+  return false;
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 20e231f4d2a47f142142832407541b9e70f13e81..a644a7307970b00c5d2433b3f7f3d228db00c1e6 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1853,6 +1853,15 @@ process_options (void)
       flag_sanitize &= ~SANITIZE_ADDRESS;
     }
 
+  /* HWAsan requires top byte ignore feature in the backend.  */
+  if (flag_sanitize & SANITIZE_HWADDRESS
+      && ! targetm.memtag.can_tag_addresses ())
+    {
+      warning_at (UNKNOWN_LOCATION, 0, "%qs is not supported for this target",
+		  "-fsanitize=hwaddress");
+      flag_sanitize &= ~SANITIZE_HWADDRESS;
+    }
+
  /* Do not use IPA optimizations for register allocation if profiler is active
     or patchable function entries are inserted for run-time instrumentation
     or port does not emit prologue and epilogue as RTL.  */

Comments

Richard Sandiford Nov. 18, 2020, 5:57 p.m. UTC | #1
Matthew Malcomson <matthew.malcomson@arm.com> writes:
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 5320e6c1e1e3c8d1482c20590049f763e11f8ff0..84050058be8eaa306b07655737e49ea8b6eb21a9 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -13709,6 +13709,53 @@ is greater or equal to this number, use callbacks instead of inline checks.
>  E.g. to disable inline code use
>  @option{--param asan-instrumentation-with-call-threshold=0}.
>  
> +@item hwasan-instrument-stack
> +Enable hwasan instrumentation of statically sized stack-allocated variables.
> +This kind of instrumentation is enabled by default when using
> +@option{-fsanitize=hwaddress} and disabled by default when using
> +@option{-fsanitize=kernel-hwaddress}.
> +To disable stack instrumentation use
> +@option{--param hwasan-instrument-stack=0}, and to enable it use
> +@option{--param hwasan-instrument-stack=1}.
> +
> +@item hwasan-random-frame-tag
> +When using stack instrumentation, decide tags for stack variables using a
> +deterministic sequence beginning at a random tag for each frame.  Usually tags
> +are chosen using the same sequence beginning from 1.
> +This is enabled by default for @option{-fsanitize=hwaddress} and unavailable
> +for @option{-fsanitize=kernel-hwaddress}.
> +To disable it use @option{--param hwasan-random-frame-tag=0}.

I think it would be worth clarifying this.  I wasn't sure whether
“Usually tags are chosen…” was describing the “determinstic random
sequence” or whether it was describing the behaviour of
hwasan-random-frame-tag=0.  If it's describing the behaviour of
hwasan-random-frame-tag=0, then I'm not sure “usually” applies,
given that hwasan-random-frame-tag=1 is the default.

> […]
> +@item -fsanitize=kernel-hwaddress
> +@opindex fsanitize=kernel-hwaddress
> +Enable Hardware-assisted AddressSanitizer for compilation of the Linux kernel.
> +Similar to @option{-fsanitize=kernel-address} but using an alternate
> +instrumentation method, and similar to @option{-fsanitize=hwaddress} but with
> +instrumentation differences necessary for compiling the Linux kernel.
> +These differences are to avoid hwasan library initialisation calls and to

initialization

> +account for the stack pointer having a different value in its top byte.
> +Note: This option has different defaults to the @option{-fsanitize=hwaddress}.

texinfo has:

  @quotation Note
  @end quotation

for this, but we don't seem to use it.  Still, I think it would be better
to break the paragraph before Note: and use:

  @emph{Note:}

which seems to be the preferred style in the GCC manual.

> +Instrumenting the stack and alloca calls are not on by default but is still

@code{alloca}
s/is still/are still/

> +possible by specifying it on the command line with

maybe s/it on the command line with/specifying the command-line options/?
Just a suggestion: would be happy with alternatives.

> +@option{--param hwasan-instrument-stack=1} and
> +@option{--param hwasan-instrument-allocas=1}. Using a random frame tag is not

and maybe add a “respectively” at the end of this sentence.

> +implemented for kernel instrumentation.
> +
>  @item -fsanitize=pointer-compare
>  @opindex fsanitize=pointer-compare
>  Instrument comparison operation (<, <=, >, >=) with pointer operands.
> […]
> diff --git a/gcc/opts.c b/gcc/opts.c
> index ac9972d9c386247af3482e07a94c76da3e1abb4d..f3662062c421e1c58c3243109891900eb2dc84bc 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -823,6 +823,51 @@ control_options_for_live_patching (struct gcc_options *opts,
>  /* --help option argument if set.  */
>  vec<const char *> help_option_arguments;
>  
> +/* Return the string name describing the argument provided on the command line
> +    which has set this particular flag.  */
> +const char *
> +find_argument (struct gcc_options *opts, unsigned int flags)
> +{

I think either (a) the name and comment need to mention the
sanitiser more explicitly or (b) this should be a lambda function
in report_conflicting_sanitizer_options:

  auto find_argument = [&](unsigned int flags)
    {
      …
    };

(in which case the implementation and comments above are fine as-is).

> +  for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
> +    {
> +      /* Need to find the sanitizer_opts element which:
> +	 a) Could have set the flags requested.
> +	 b) Has been set on the command line.
> +
> +	 Can have (a) without (b) if the flag requested is e.g.
> +	 SANITIZE_ADDRESS, since both -fsanitize=address and
> +	 -fsanitize=kernel-address set this flag.
> +
> +	 Can have (b) without (a) by requesting more than one sanitizer on the
> +	 command line.  */
> +      if ((sanitizer_opts[i].flag & opts->x_flag_sanitize)
> +	  != sanitizer_opts[i].flag)
> +	continue;
> +      if ((sanitizer_opts[i].flag & flags) != flags)
> +	continue;
> +      return sanitizer_opts[i].name;
> +    }
> +  return NULL;
> +}
> +
> +
> +/* Report any conflicting sanitizer options requested.  */
> +static void
> +report_conflicting_sanitizer_options (struct gcc_options *opts, location_t loc,
> +				      unsigned int left, unsigned int right)

The comment needs to describe at least the “LEFT” and “RIGHT” parameters

> +{
> +  unsigned int left_seen = (opts->x_flag_sanitize & left);
> +  unsigned int right_seen = (opts->x_flag_sanitize & right);
> +  if (left_seen && right_seen)
> +    {
> +      const char* left_arg = find_argument (opts, left_seen);
> +      const char* right_arg = find_argument (opts, right_seen);
> +      gcc_assert (left_arg && right_arg);
> +      error_at (loc,
> +		"%<-fsanitize=%s%> is incompatible with %<-fsanitize=%s%>",
> +		left_arg, right_arg);
> +    }
> +}
>  
>  /* After all options at LOC have been read into OPTS and OPTS_SET,
>     finalize settings of those options and diagnose incompatible
> @@ -1074,22 +1119,21 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
>  		  "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>");
>      }
>  
> -  /* Userspace and kernel ASan conflict with each other.  */
> -  if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
> -      && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
> -    error_at (loc, "%qs is incompatible with %qs",
> -	      "-fsanitize=address", "-fsanitize=kernel-address");
> +  /* Address sanitizers conflict with the thread sanitizer.  */
> +  report_conflicting_sanitizer_options (opts, loc, SANITIZE_THREAD,
> +					SANITIZE_ADDRESS | SANITIZE_HWADDRESS);
> +  /* The leak sanitizer conflicts with the thread sanitizer.  */
> +  report_conflicting_sanitizer_options (opts, loc, SANITIZE_LEAK, SANITIZE_THREAD);

Nit: long line.

> […]
> +-param=hwasan-instrument-allocas=
> +Common Joined UInteger Var(param_hwasan_protect_allocas) Init(1) IntegerRange(0, 1) Param Optimization
> +Enable hwasan instrumentation of allocas/VLAs.

It would be good to keep the variable name in sync with the parameter,
so s/protect/instrument/ there too.

> […]
> @@ -6850,6 +6850,17 @@ DEFHOOK
>  HOOK_VECTOR_END (mode_switching)
>  
>  #undef HOOK_PREFIX
> +#define HOOK_PREFIX "TARGET_MEMTAG_"
> +HOOK_VECTOR (TARGET_MEMTAG_, memtag)
> +
> +DEFHOOK
> +(can_tag_addresses,
> + "True if backend architecture naturally supports ignoring some region of\n\
> +pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",

“…if the backend architecture…”

LGTM otherwise, thanks.

Richard
Matthew Malcomson Nov. 20, 2020, 6:48 p.m. UTC | #2
Hi there,

I was just doing some double-checks and noticed I'd placed the
documentation in the wrong section of tm.texi.  The `MEMTAG` hooks were
documented in the `Register Classes` section, so I've now moved it to
the `Misc` section.

That's the only change, Ok for trunk?

Matthew


------------------------------------------------------------
These flags can't be used at the same time as any of the other
sanitizers.
We add an equivalent flag to -static-libasan in -static-libhwasan to
ensure static linking.

The -fsanitize=kernel-hwaddress option is for compiling targeting the
kernel.  This flag has defaults to match the LLVM implementation and
sets some other behaviors to work in the kernel (e.g. accounting for
the fact that the stack pointer will have 0xff in the top byte and to not
call the userspace library initialisation routines).
The defaults are that we do not sanitize variables on the stack and
always recover from a detected bug.

Since we are introducing a few more conflicts between sanitizer flags we
refactor the checking for such conflicts to use a helper function which
makes checking for such conflicts more easy and consistent.

We introduce a backend hook `targetm.memtag.can_tag_addresses` that
indicates to the mid-end whether a target has a feature like AArch64 TBI
where the top byte of an address is ignored.
Without this feature hwasan sanitization is not done.

gcc/ChangeLog:

	* common.opt (flag_sanitize_recover): Default for kernel
	hwaddress.
	(static-libhwasan): New cli option.
	* config/aarch64/aarch64.c (aarch64_can_tag_addresses): New.
	(TARGET_MEMTAG_CAN_TAG_ADDRESSES): New.
	* config/gnu-user.h (LIBHWASAN_EARLY_SPEC): hwasan equivalent of
	asan command line flags.
	* cppbuiltin.c (define_builtin_macros_for_compilation_flags):
	Add hwasan equivalent of __SANITIZE_ADDRESS__.
	* doc/invoke.texi: Document hwasan command line flags.
	* doc/tm.texi: Document new hook.
	* doc/tm.texi.in: Document new hook.
	* flag-types.h (enum sanitize_code): New sanitizer values.
	* gcc.c (STATIC_LIBHWASAN_LIBS): New macro.
	(LIBHWASAN_SPEC): New macro.
	(LIBHWASAN_EARLY_SPEC): New macro.
	(SANITIZER_EARLY_SPEC): Update to include hwasan.
	(SANITIZER_SPEC): Update to include hwasan.
	(sanitize_spec_function): Use hwasan options.
	* opts.c (finish_options): Describe conflicts between address
	sanitizers.
	(find_sanitizer_argument): New.
	(report_conflicting_sanitizer_options): New.
	(sanitizer_opts): Introduce new sanitizer flags.
	(common_handle_option): Add defaults for kernel sanitizer.
	* params.opt (hwasan--instrument-stack): New
	(hwasan-random-frame-tag): New
	(hwasan-instrument-allocas): New
	(hwasan-instrument-reads): New
	(hwasan-instrument-writes): New
	(hwasan-instrument-mem-intrinsics): New
	* target.def (HOOK_PREFIX): Add new hook.
	(can_tag_addresses): Add new hook under memtag prefix.
	* targhooks.c (default_memtag_can_tag_addresses): New.
	* targhooks.h (default_memtag_can_tag_addresses): New decl.
	* toplev.c (process_options): Ensure hwasan only on
	architectures that advertise the possibility.



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


diff --git a/gcc/common.opt b/gcc/common.opt
index fe39b3dee9f270dd39b3f69ff6a0e2e854058703..4e4ba790ce668e490e35c2d95a0b12472754fba4 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -218,7 +218,7 @@ unsigned int flag_sanitize
 
 ; What sanitizers should recover from errors
 Variable
-unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
+unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS | SANITIZE_KERNEL_HWADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
 
 ; What the coverage sanitizers should instrument
 Variable
@@ -3445,6 +3445,9 @@ Driver
 static-libasan
 Driver
 
+static-libhwasan
+Driver
+
 static-libtsan
 Driver
 
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 6de51b521bacb0530799c7cbddb5f6b170bf441c..4f90a49f1b79db406319ddbf89e42f58a695f430 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -23294,6 +23294,15 @@ aarch64_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1,
   return NULL;
 }
 
+/* Implement TARGET_MEMTAG_CAN_TAG_ADDRESSES.  Here we tell the rest of the
+   compiler that we automatically ignore the top byte of our pointers, which
+   allows using -fsanitize=hwaddress.  */
+bool
+aarch64_can_tag_addresses ()
+{
+  return !TARGET_ILP32;
+}
+
 /* Implement TARGET_ASM_FILE_END for AArch64.  This adds the AArch64 GNU NOTE
    section at the end if needed.  */
 #define GNU_PROPERTY_AARCH64_FEATURE_1_AND	0xc0000000
@@ -24113,6 +24122,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_FNTYPE_ABI
 #define TARGET_FNTYPE_ABI aarch64_fntype_abi
 
+#undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
+#define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h
index ff2e880b1fa81b76af866b6ba0e1fabf6d534f32..92950245e2b95c500da9df8b85940380e62030fa 100644
--- a/gcc/config/gnu-user.h
+++ b/gcc/config/gnu-user.h
@@ -129,14 +129,18 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Link -lasan early on the command line.  For -static-libasan, don't link
    it for -shared link, the executable should be compiled with -static-libasan
    in that case, and for executable link with --{,no-}whole-archive around
-   it to force everything into the executable.  And similarly for -ltsan
-   and -llsan.  */
+   it to force everything into the executable.  And similarly for -ltsan,
+   -lhwasan, and -llsan.  */
 #if defined(HAVE_LD_STATIC_DYNAMIC)
 #undef LIBASAN_EARLY_SPEC
 #define LIBASAN_EARLY_SPEC "%{!shared:libasan_preinit%O%s} " \
   "%{static-libasan:%{!shared:" \
   LD_STATIC_OPTION " --whole-archive -lasan --no-whole-archive " \
   LD_DYNAMIC_OPTION "}}%{!static-libasan:-lasan}"
+#undef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC "%{static-libhwasan:%{!shared:" \
+  LD_STATIC_OPTION " --whole-archive -lhwasan --no-whole-archive " \
+  LD_DYNAMIC_OPTION "}}%{!static-libhwasan:-lhwasan}"
 #undef LIBTSAN_EARLY_SPEC
 #define LIBTSAN_EARLY_SPEC "%{!shared:libtsan_preinit%O%s} " \
   "%{static-libtsan:%{!shared:" \
diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c
index 61efe9b2b75e961c15e9f399cafed559b5c8c9be..fc61c78a99373fcfbc2bb03a01134248214eb2d4 100644
--- a/gcc/cppbuiltin.c
+++ b/gcc/cppbuiltin.c
@@ -93,6 +93,9 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile)
   if (flag_sanitize & SANITIZE_ADDRESS)
     cpp_define (pfile, "__SANITIZE_ADDRESS__");
 
+  if (flag_sanitize & SANITIZE_HWADDRESS)
+    cpp_define (pfile, "__SANITIZE_HWADDRESS__");
+
   if (flag_sanitize & SANITIZE_THREAD)
     cpp_define (pfile, "__SANITIZE_THREAD__");
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 310c3f72a3fc625260a506c9280df2fd2e0b810b..9bcb0c718f2f81a1dc8a0379b75367cee0c17344 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13802,6 +13802,53 @@ is greater or equal to this number, use callbacks instead of inline checks.
 E.g. to disable inline code use
 @option{--param asan-instrumentation-with-call-threshold=0}.
 
+@item hwasan-instrument-stack
+Enable hwasan instrumentation of statically sized stack-allocated variables.
+This kind of instrumentation is enabled by default when using
+@option{-fsanitize=hwaddress} and disabled by default when using
+@option{-fsanitize=kernel-hwaddress}.
+To disable stack instrumentation use
+@option{--param hwasan-instrument-stack=0}, and to enable it use
+@option{--param hwasan-instrument-stack=1}.
+
+@item hwasan-random-frame-tag
+When using stack instrumentation, decide tags for stack variables using a
+deterministic sequence beginning at a random tag for each frame.  With this
+parameter unset tags are chosen using the same sequence but beginning from 1.
+This is enabled by default for @option{-fsanitize=hwaddress} and unavailable
+for @option{-fsanitize=kernel-hwaddress}.
+To disable it use @option{--param hwasan-random-frame-tag=0}.
+
+@item hwasan-instrument-allocas
+Enable hwasan instrumentation of dynamically sized stack-allocated variables.
+This kind of instrumentation is enabled by default when using
+@option{-fsanitize=hwaddress} and disabled by default when using
+@option{-fsanitize=kernel-hwaddress}.
+To disable instrumentation of such variables use
+@option{--param hwasan-instrument-allocas=0}, and to enable it use
+@option{--param hwasan-instrument-allocas=1}.
+
+@item hwasan-instrument-reads
+Enable hwasan checks on memory reads.  Instrumentation of reads is enabled by
+default for both @option{-fsanitize=hwaddress} and
+@option{-fsanitize=kernel-hwaddress}.
+To disable checking memory reads use
+@option{--param hwasan-instrument-reads=0}.
+
+@item hwasan-instrument-writes
+Enable hwasan checks on memory writes.  Instrumentation of writes is enabled by
+default for both @option{-fsanitize=hwaddress} and
+@option{-fsanitize=kernel-hwaddress}.
+To disable checking memory writes use
+@option{--param hwasan-instrument-writes=0}.
+
+@item hwasan-instrument-mem-intrinsics
+Enable hwasan instrumentation of builtin functions.  Instrumentation of these
+builtin functions is enabled by default for both @option{-fsanitize=hwaddress}
+and @option{-fsanitize=kernel-hwaddress}.
+To disable instrumentation of builtin functions use
+@option{--param hwasan-instrument-mem-intrinsics=0}.
+
 @item use-after-scope-direct-emission-threshold
 If the size of a local variable in bytes is smaller or equal to this
 number, directly poison (or unpoison) shadow memory instead of using
@@ -14364,13 +14411,47 @@ more details.  The run-time behavior can be influenced using the
 the available options are shown at startup of the instrumented program.  See
 @url{https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags}
 for a list of supported options.
-The option cannot be combined with @option{-fsanitize=thread}.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=hwaddress}.  Note that the only target this option is
+currently supported on is AArch64.
 
 @item -fsanitize=kernel-address
 @opindex fsanitize=kernel-address
 Enable AddressSanitizer for Linux kernel.
 See @uref{https://github.com/google/kasan/wiki} for more details.
 
+@item -fsanitize=hwaddress
+@opindex fsanitize=hwaddress
+Enable Hardware-assisted AddressSanitizer, which uses a hardware ability to
+ignore the top byte of a pointer to allow the detection of memory errors with
+a low memory overhead.
+Memory access instructions are instrumented to detect out-of-bounds and
+use-after-free bugs.
+The option enables @option{-fsanitize-address-use-after-scope}.
+See
+@uref{https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html}
+for more details.  The run-time behavior can be influenced using the
+@env{HWASAN_OPTIONS} environment variable.  When set to @code{help=1},
+the available options are shown at startup of the instrumented program.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=address}, and is currently only available on AArch64.
+
+@item -fsanitize=kernel-hwaddress
+@opindex fsanitize=kernel-hwaddress
+Enable Hardware-assisted AddressSanitizer for compilation of the Linux kernel.
+Similar to @option{-fsanitize=kernel-address} but using an alternate
+instrumentation method, and similar to @option{-fsanitize=hwaddress} but with
+instrumentation differences necessary for compiling the Linux kernel.
+These differences are to avoid hwasan library initialization calls and to
+account for the stack pointer having a different value in its top byte.
+
+@emph{Note:} This option has different defaults to the @option{-fsanitize=hwaddress}.
+Instrumenting the stack and alloca calls are not on by default but are still
+possible by specifying the command-line options
+@option{--param hwasan-instrument-stack=1} and
+@option{--param hwasan-instrument-allocas=1} respectively. Using a random frame
+tag is not implemented for kernel instrumentation.
+
 @item -fsanitize=pointer-compare
 @opindex fsanitize=pointer-compare
 Instrument comparison operation (<, <=, >, >=) with pointer operands.
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index f7f82911f051605330ac1abba38060eb76fe06d7..298fe4b295e2f81d679786f21f499183bc07078f 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -12221,3 +12221,12 @@ This target hook can be used to generate a target-specific code
 @deftypefn {Target Hook} void TARGET_RUN_TARGET_SELFTESTS (void)
 If selftests are enabled, run any selftests for this target.
 @end deftypefn
+
+@deftypefn {Target Hook} bool TARGET_MEMTAG_CAN_TAG_ADDRESSES ()
+True if the backend architecture naturally supports ignoring some region
+of pointers.  This feature means that @option{-fsanitize=hwaddress} can
+work.
+
+At preset, this feature does not support address spaces.  It also requires
+@code{Pmode} to be the same as @code{ptr_mode}.
+@end deftypefn
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 897f28962669031f21779847b7c63dab377ed7e2..8fbd36e2bf31e098f7827ce331fd7059c8a747bc 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -8184,3 +8184,5 @@ maintainer is familiar with.
 @hook TARGET_SPECULATION_SAFE_VALUE
 
 @hook TARGET_RUN_TARGET_SELFTESTS
+
+@hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 648ed096e30c89e5eca0caeaced7ba3ff57b5666..1c3e6a9951014c755e58b66cb7c6054f94baa78f 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -274,6 +274,9 @@ enum sanitize_code {
   SANITIZE_BUILTIN = 1UL << 25,
   SANITIZE_POINTER_COMPARE = 1UL << 26,
   SANITIZE_POINTER_SUBTRACT = 1UL << 27,
+  SANITIZE_HWADDRESS = 1UL << 28,
+  SANITIZE_USER_HWADDRESS = 1UL << 29,
+  SANITIZE_KERNEL_HWADDRESS = 1UL << 30,
   SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
diff --git a/gcc/gcc.c b/gcc/gcc.c
index cdf4d4f2403e6236ff0f33c4def95bc445a51dee..69f6fdcb324342b0153b64af0b5d607e0a555d8a 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -743,6 +743,24 @@ proper position among the other output files.  */
 #define LIBASAN_EARLY_SPEC ""
 #endif
 
+#ifndef LIBHWASAN_SPEC
+#define STATIC_LIBHWASAN_LIBS \
+  " %{static-libhwasan|static:%:include(libsanitizer.spec)%(link_libhwasan)}"
+#ifdef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_SPEC STATIC_LIBHWASAN_LIBS
+#elif defined(HAVE_LD_STATIC_DYNAMIC)
+#define LIBHWASAN_SPEC "%{static-libhwasan:" LD_STATIC_OPTION \
+		     "} -lhwasan %{static-libhwasan:" LD_DYNAMIC_OPTION "}" \
+		     STATIC_LIBHWASAN_LIBS
+#else
+#define LIBHWASAN_SPEC "-lhwasan" STATIC_LIBHWASAN_LIBS
+#endif
+#endif
+
+#ifndef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC ""
+#endif
+
 #ifndef LIBTSAN_SPEC
 #define STATIC_LIBTSAN_LIBS \
   " %{static-libtsan|static:%:include(libsanitizer.spec)%(link_libtsan)}"
@@ -1065,6 +1083,7 @@ proper position among the other output files.  */
 #ifndef SANITIZER_EARLY_SPEC
 #define SANITIZER_EARLY_SPEC "\
 %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_EARLY_SPEC "} \
+    %{%:sanitize(hwaddress):" LIBHWASAN_EARLY_SPEC "} \
     %{%:sanitize(thread):" LIBTSAN_EARLY_SPEC "} \
     %{%:sanitize(leak):" LIBLSAN_EARLY_SPEC "}}}}"
 #endif
@@ -1074,6 +1093,8 @@ proper position among the other output files.  */
 #define SANITIZER_SPEC "\
 %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_SPEC "\
     %{static:%ecannot specify -static with -fsanitize=address}}\
+    %{%:sanitize(hwaddress):" LIBHWASAN_SPEC "\
+	%{static:%ecannot specify -static with -fsanitize=hwaddress}}\
     %{%:sanitize(thread):" LIBTSAN_SPEC "\
     %{static:%ecannot specify -static with -fsanitize=thread}}\
     %{%:sanitize(undefined):" LIBUBSAN_SPEC "}\
@@ -10125,8 +10146,12 @@ sanitize_spec_function (int argc, const char **argv)
 
   if (strcmp (argv[0], "address") == 0)
     return (flag_sanitize & SANITIZE_USER_ADDRESS) ? "" : NULL;
+  if (strcmp (argv[0], "hwaddress") == 0)
+    return (flag_sanitize & SANITIZE_USER_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "kernel-address") == 0)
     return (flag_sanitize & SANITIZE_KERNEL_ADDRESS) ? "" : NULL;
+  if (strcmp (argv[0], "kernel-hwaddress") == 0)
+    return (flag_sanitize & SANITIZE_KERNEL_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "thread") == 0)
     return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
   if (strcmp (argv[0], "undefined") == 0)
diff --git a/gcc/opts.c b/gcc/opts.c
index 57774916a098425dd7a635e86610b1893b8a7907..cc1d0cc04f64133a22bac464732fd9b7fd8b5858 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -823,6 +823,57 @@ control_options_for_live_patching (struct gcc_options *opts,
 /* --help option argument if set.  */
 vec<const char *> help_option_arguments;
 
+/* Return the string name describing a sanitizer argument which has been
+   provided on the command line and has set this particular flag.  */
+const char *
+find_sanitizer_argument (struct gcc_options *opts, unsigned int flags)
+{
+  for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
+    {
+      /* Need to find the sanitizer_opts element which:
+	 a) Could have set the flags requested.
+	 b) Has been set on the command line.
+
+	 Can have (a) without (b) if the flag requested is e.g.
+	 SANITIZE_ADDRESS, since both -fsanitize=address and
+	 -fsanitize=kernel-address set this flag.
+
+	 Can have (b) without (a) by requesting more than one sanitizer on the
+	 command line.  */
+      if ((sanitizer_opts[i].flag & opts->x_flag_sanitize)
+	  != sanitizer_opts[i].flag)
+	continue;
+      if ((sanitizer_opts[i].flag & flags) != flags)
+	continue;
+      return sanitizer_opts[i].name;
+    }
+  return NULL;
+}
+
+
+/* Report an error to the user about sanitizer options they have requested
+   which have set conflicting flags.
+
+   LEFT and RIGHT indicate sanitizer flags which conflict with each other, this
+   function reports an error if both have been set in OPTS->x_flag_sanitize and
+   ensures the error identifies the requested command line options that have
+   set these flags.  */
+static void
+report_conflicting_sanitizer_options (struct gcc_options *opts, location_t loc,
+				      unsigned int left, unsigned int right)
+{
+  unsigned int left_seen = (opts->x_flag_sanitize & left);
+  unsigned int right_seen = (opts->x_flag_sanitize & right);
+  if (left_seen && right_seen)
+    {
+      const char* left_arg = find_sanitizer_argument (opts, left_seen);
+      const char* right_arg = find_sanitizer_argument (opts, right_seen);
+      gcc_assert (left_arg && right_arg);
+      error_at (loc,
+		"%<-fsanitize=%s%> is incompatible with %<-fsanitize=%s%>",
+		left_arg, right_arg);
+    }
+}
 
 /* After all options at LOC have been read into OPTS and OPTS_SET,
    finalize settings of those options and diagnose incompatible
@@ -1074,22 +1125,22 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 		  "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>");
     }
 
-  /* Userspace and kernel ASan conflict with each other.  */
-  if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
-      && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
-    error_at (loc, "%qs is incompatible with %qs",
-	      "-fsanitize=address", "-fsanitize=kernel-address");
+  /* Address sanitizers conflict with the thread sanitizer.  */
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_THREAD,
+					SANITIZE_ADDRESS | SANITIZE_HWADDRESS);
+  /* The leak sanitizer conflicts with the thread sanitizer.  */
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_LEAK,
+					SANITIZE_THREAD);
 
-  /* And with TSan.  */
-  if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
-      && (opts->x_flag_sanitize & SANITIZE_THREAD))
-    error_at (loc, "%qs is incompatible with %qs",
-	      "-fsanitize=thread", "-fsanitize=address|kernel-address");
+  /* No combination of HWASAN and ASAN work together.  */
+  report_conflicting_sanitizer_options (opts, loc,
+					SANITIZE_HWADDRESS, SANITIZE_ADDRESS);
 
-  if ((opts->x_flag_sanitize & SANITIZE_LEAK)
-      && (opts->x_flag_sanitize & SANITIZE_THREAD))
-    error_at (loc, "%qs is incompatible with %qs",
-	      "-fsanitize=leak", "-fsanitize=thread");
+  /* The userspace and kernel address sanitizers conflict with each other.  */
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_USER_HWADDRESS,
+					SANITIZE_KERNEL_HWADDRESS);
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_USER_ADDRESS,
+					SANITIZE_KERNEL_ADDRESS);
 
   /* Check error recovery for -fsanitize-recover option.  */
   for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
@@ -1108,9 +1159,10 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
   if (opts->x_flag_sanitize & ~(SANITIZE_LEAK | SANITIZE_UNREACHABLE))
     opts->x_flag_aggressive_loop_optimizations = 0;
 
-  /* Enable -fsanitize-address-use-after-scope if address sanitizer is
+  /* Enable -fsanitize-address-use-after-scope if either address sanitizer is
      enabled.  */
-  if (opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+  if (opts->x_flag_sanitize
+      & (SANITIZE_USER_ADDRESS | SANITIZE_USER_HWADDRESS))
     SET_OPTION_IF_UNSET (opts, opts_set, flag_sanitize_address_use_after_scope,
 			 true);
 
@@ -1724,8 +1776,13 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 #define SANITIZER_OPT(name, flags, recover) \
     { #name, flags, sizeof #name - 1, recover }
   SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true),
+  SANITIZER_OPT (hwaddress, (SANITIZE_HWADDRESS | SANITIZE_USER_HWADDRESS),
+		 true),
   SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
 		 true),
+  SANITIZER_OPT (kernel-hwaddress,
+		 (SANITIZE_HWADDRESS | SANITIZE_KERNEL_HWADDRESS),
+		 true),
   SANITIZER_OPT (pointer-compare, SANITIZE_POINTER_COMPARE, true),
   SANITIZER_OPT (pointer-subtract, SANITIZE_POINTER_SUBTRACT, true),
   SANITIZER_OPT (thread, SANITIZE_THREAD, false),
@@ -2304,6 +2361,15 @@ common_handle_option (struct gcc_options *opts,
 	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_protect_allocas, 0);
 	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_use_after_return, 0);
 	}
+      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+	{
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_instrument_stack, 0);
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_random_frame_tag, 0);
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_instrument_allocas, 0);
+	}
       break;
 
     case OPT_fsanitize_recover_:
diff --git a/gcc/params.opt b/gcc/params.opt
index 3f1f6093c53a58e7644c481bf6e735fcf86be263..d9598187dd29af920f51f2ff9ac77cc3cd681ede 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -62,6 +62,30 @@ Enable asan stack protection.
 Common Joined UInteger Var(param_asan_use_after_return) Init(1) IntegerRange(0, 1) Param Optimization
 Enable asan detection of use-after-return bugs.
 
+-param=hwasan-instrument-stack=
+Common Joined UInteger Var(param_hwasan_instrument_stack) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of statically sized stack-allocated variables.
+
+-param=hwasan-random-frame-tag=
+Common Joined UInteger Var(param_hwasan_random_frame_tag) Init(1) IntegerRange(0, 1) Param Optimization
+Use random base tag for each frame, as opposed to base always zero.
+
+-param=hwasan-instrument-allocas=
+Common Joined UInteger Var(param_hwasan_instrument_allocas) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of allocas/VLAs.
+
+-param=hwasan-instrument-reads=
+Common Joined UInteger Var(param_hwasan_instrument_reads) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of load operations.
+
+-param=hwasan-instrument-writes=
+Common Joined UInteger Var(param_hwasan_instrument_writes) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of store operations.
+
+-param=hwasan-instrument-mem-intrinsics=
+Common Joined UInteger Var(param_hwasan_instrument_mem_intrinsics) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of builtin functions.
+
 -param=avg-loop-niter=
 Common Joined UInteger Var(param_avg_loop_niter) Init(10) IntegerRange(1, 65536) Param Optimization
 Average number of iterations of a loop.
diff --git a/gcc/target.def b/gcc/target.def
index ff7ad5983ac26a71dc7dd4f4b3cb85c4e8c3e362..25f0ae228210f926077020082f129fb2e599f062 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6861,6 +6861,21 @@ DEFHOOK
 HOOK_VECTOR_END (mode_switching)
 
 #undef HOOK_PREFIX
+#define HOOK_PREFIX "TARGET_MEMTAG_"
+HOOK_VECTOR (TARGET_MEMTAG_, memtag)
+
+DEFHOOK
+(can_tag_addresses,
+ "True if the backend architecture naturally supports ignoring some region\n\
+of pointers.  This feature means that @option{-fsanitize=hwaddress} can\n\
+work.\n\
+\n\
+At preset, this feature does not support address spaces.  It also requires\n\
+@code{Pmode} to be the same as @code{ptr_mode}.",
+ bool, (), default_memtag_can_tag_addresses)
+
+HOOK_VECTOR_END (memtag)
+#undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
 
 #define DEF_TARGET_INSN(NAME, PROTO) \
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index e0a925fa2bee2e73f82d244fe4364afc87eff7d2..0065c686978d7120978430013c73b1055aaf95c7 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -286,4 +286,5 @@ extern bool default_have_speculation_safe_value (bool);
 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 ();
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 5b68a2ad7d4a435d66fad68ab8827f59289e81f4..46cb536041d396c32fd08042581d6d5cd5ad0395 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2415,4 +2415,10 @@ default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
   return result;
 }
 
+bool
+default_memtag_can_tag_addresses ()
+{
+  return false;
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index e0e0e04e95ff6b295991ad288c916dfbea520883..2a3e7c064a5fbb6913481104975ca85615e49f8e 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1853,6 +1853,15 @@ process_options (void)
       flag_sanitize &= ~SANITIZE_ADDRESS;
     }
 
+  /* HWAsan requires top byte ignore feature in the backend.  */
+  if (flag_sanitize & SANITIZE_HWADDRESS
+      && ! targetm.memtag.can_tag_addresses ())
+    {
+      warning_at (UNKNOWN_LOCATION, 0, "%qs is not supported for this target",
+		  "-fsanitize=hwaddress");
+      flag_sanitize &= ~SANITIZE_HWADDRESS;
+    }
+
  /* Do not use IPA optimizations for register allocation if profiler is active
     or patchable function entries are inserted for run-time instrumentation
     or port does not emit prologue and epilogue as RTL.  */
diff --git a/gcc/common.opt b/gcc/common.opt
index fe39b3dee9f270dd39b3f69ff6a0e2e854058703..4e4ba790ce668e490e35c2d95a0b12472754fba4 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -218,7 +218,7 @@ unsigned int flag_sanitize
 
 ; What sanitizers should recover from errors
 Variable
-unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
+unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS | SANITIZE_KERNEL_HWADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
 
 ; What the coverage sanitizers should instrument
 Variable
@@ -3445,6 +3445,9 @@ Driver
 static-libasan
 Driver
 
+static-libhwasan
+Driver
+
 static-libtsan
 Driver
 
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 6de51b521bacb0530799c7cbddb5f6b170bf441c..4f90a49f1b79db406319ddbf89e42f58a695f430 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -23294,6 +23294,15 @@ aarch64_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1,
   return NULL;
 }
 
+/* Implement TARGET_MEMTAG_CAN_TAG_ADDRESSES.  Here we tell the rest of the
+   compiler that we automatically ignore the top byte of our pointers, which
+   allows using -fsanitize=hwaddress.  */
+bool
+aarch64_can_tag_addresses ()
+{
+  return !TARGET_ILP32;
+}
+
 /* Implement TARGET_ASM_FILE_END for AArch64.  This adds the AArch64 GNU NOTE
    section at the end if needed.  */
 #define GNU_PROPERTY_AARCH64_FEATURE_1_AND	0xc0000000
@@ -24113,6 +24122,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_FNTYPE_ABI
 #define TARGET_FNTYPE_ABI aarch64_fntype_abi
 
+#undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
+#define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h
index ff2e880b1fa81b76af866b6ba0e1fabf6d534f32..92950245e2b95c500da9df8b85940380e62030fa 100644
--- a/gcc/config/gnu-user.h
+++ b/gcc/config/gnu-user.h
@@ -129,14 +129,18 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Link -lasan early on the command line.  For -static-libasan, don't link
    it for -shared link, the executable should be compiled with -static-libasan
    in that case, and for executable link with --{,no-}whole-archive around
-   it to force everything into the executable.  And similarly for -ltsan
-   and -llsan.  */
+   it to force everything into the executable.  And similarly for -ltsan,
+   -lhwasan, and -llsan.  */
 #if defined(HAVE_LD_STATIC_DYNAMIC)
 #undef LIBASAN_EARLY_SPEC
 #define LIBASAN_EARLY_SPEC "%{!shared:libasan_preinit%O%s} " \
   "%{static-libasan:%{!shared:" \
   LD_STATIC_OPTION " --whole-archive -lasan --no-whole-archive " \
   LD_DYNAMIC_OPTION "}}%{!static-libasan:-lasan}"
+#undef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC "%{static-libhwasan:%{!shared:" \
+  LD_STATIC_OPTION " --whole-archive -lhwasan --no-whole-archive " \
+  LD_DYNAMIC_OPTION "}}%{!static-libhwasan:-lhwasan}"
 #undef LIBTSAN_EARLY_SPEC
 #define LIBTSAN_EARLY_SPEC "%{!shared:libtsan_preinit%O%s} " \
   "%{static-libtsan:%{!shared:" \
diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c
index 61efe9b2b75e961c15e9f399cafed559b5c8c9be..fc61c78a99373fcfbc2bb03a01134248214eb2d4 100644
--- a/gcc/cppbuiltin.c
+++ b/gcc/cppbuiltin.c
@@ -93,6 +93,9 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile)
   if (flag_sanitize & SANITIZE_ADDRESS)
     cpp_define (pfile, "__SANITIZE_ADDRESS__");
 
+  if (flag_sanitize & SANITIZE_HWADDRESS)
+    cpp_define (pfile, "__SANITIZE_HWADDRESS__");
+
   if (flag_sanitize & SANITIZE_THREAD)
     cpp_define (pfile, "__SANITIZE_THREAD__");
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 310c3f72a3fc625260a506c9280df2fd2e0b810b..9bcb0c718f2f81a1dc8a0379b75367cee0c17344 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13802,6 +13802,53 @@ is greater or equal to this number, use callbacks instead of inline checks.
 E.g. to disable inline code use
 @option{--param asan-instrumentation-with-call-threshold=0}.
 
+@item hwasan-instrument-stack
+Enable hwasan instrumentation of statically sized stack-allocated variables.
+This kind of instrumentation is enabled by default when using
+@option{-fsanitize=hwaddress} and disabled by default when using
+@option{-fsanitize=kernel-hwaddress}.
+To disable stack instrumentation use
+@option{--param hwasan-instrument-stack=0}, and to enable it use
+@option{--param hwasan-instrument-stack=1}.
+
+@item hwasan-random-frame-tag
+When using stack instrumentation, decide tags for stack variables using a
+deterministic sequence beginning at a random tag for each frame.  With this
+parameter unset tags are chosen using the same sequence but beginning from 1.
+This is enabled by default for @option{-fsanitize=hwaddress} and unavailable
+for @option{-fsanitize=kernel-hwaddress}.
+To disable it use @option{--param hwasan-random-frame-tag=0}.
+
+@item hwasan-instrument-allocas
+Enable hwasan instrumentation of dynamically sized stack-allocated variables.
+This kind of instrumentation is enabled by default when using
+@option{-fsanitize=hwaddress} and disabled by default when using
+@option{-fsanitize=kernel-hwaddress}.
+To disable instrumentation of such variables use
+@option{--param hwasan-instrument-allocas=0}, and to enable it use
+@option{--param hwasan-instrument-allocas=1}.
+
+@item hwasan-instrument-reads
+Enable hwasan checks on memory reads.  Instrumentation of reads is enabled by
+default for both @option{-fsanitize=hwaddress} and
+@option{-fsanitize=kernel-hwaddress}.
+To disable checking memory reads use
+@option{--param hwasan-instrument-reads=0}.
+
+@item hwasan-instrument-writes
+Enable hwasan checks on memory writes.  Instrumentation of writes is enabled by
+default for both @option{-fsanitize=hwaddress} and
+@option{-fsanitize=kernel-hwaddress}.
+To disable checking memory writes use
+@option{--param hwasan-instrument-writes=0}.
+
+@item hwasan-instrument-mem-intrinsics
+Enable hwasan instrumentation of builtin functions.  Instrumentation of these
+builtin functions is enabled by default for both @option{-fsanitize=hwaddress}
+and @option{-fsanitize=kernel-hwaddress}.
+To disable instrumentation of builtin functions use
+@option{--param hwasan-instrument-mem-intrinsics=0}.
+
 @item use-after-scope-direct-emission-threshold
 If the size of a local variable in bytes is smaller or equal to this
 number, directly poison (or unpoison) shadow memory instead of using
@@ -14364,13 +14411,47 @@ more details.  The run-time behavior can be influenced using the
 the available options are shown at startup of the instrumented program.  See
 @url{https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags}
 for a list of supported options.
-The option cannot be combined with @option{-fsanitize=thread}.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=hwaddress}.  Note that the only target this option is
+currently supported on is AArch64.
 
 @item -fsanitize=kernel-address
 @opindex fsanitize=kernel-address
 Enable AddressSanitizer for Linux kernel.
 See @uref{https://github.com/google/kasan/wiki} for more details.
 
+@item -fsanitize=hwaddress
+@opindex fsanitize=hwaddress
+Enable Hardware-assisted AddressSanitizer, which uses a hardware ability to
+ignore the top byte of a pointer to allow the detection of memory errors with
+a low memory overhead.
+Memory access instructions are instrumented to detect out-of-bounds and
+use-after-free bugs.
+The option enables @option{-fsanitize-address-use-after-scope}.
+See
+@uref{https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html}
+for more details.  The run-time behavior can be influenced using the
+@env{HWASAN_OPTIONS} environment variable.  When set to @code{help=1},
+the available options are shown at startup of the instrumented program.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=address}, and is currently only available on AArch64.
+
+@item -fsanitize=kernel-hwaddress
+@opindex fsanitize=kernel-hwaddress
+Enable Hardware-assisted AddressSanitizer for compilation of the Linux kernel.
+Similar to @option{-fsanitize=kernel-address} but using an alternate
+instrumentation method, and similar to @option{-fsanitize=hwaddress} but with
+instrumentation differences necessary for compiling the Linux kernel.
+These differences are to avoid hwasan library initialization calls and to
+account for the stack pointer having a different value in its top byte.
+
+@emph{Note:} This option has different defaults to the @option{-fsanitize=hwaddress}.
+Instrumenting the stack and alloca calls are not on by default but are still
+possible by specifying the command-line options
+@option{--param hwasan-instrument-stack=1} and
+@option{--param hwasan-instrument-allocas=1} respectively. Using a random frame
+tag is not implemented for kernel instrumentation.
+
 @item -fsanitize=pointer-compare
 @opindex fsanitize=pointer-compare
 Instrument comparison operation (<, <=, >, >=) with pointer operands.
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index f7f82911f051605330ac1abba38060eb76fe06d7..298fe4b295e2f81d679786f21f499183bc07078f 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -12221,3 +12221,12 @@ This target hook can be used to generate a target-specific code
 @deftypefn {Target Hook} void TARGET_RUN_TARGET_SELFTESTS (void)
 If selftests are enabled, run any selftests for this target.
 @end deftypefn
+
+@deftypefn {Target Hook} bool TARGET_MEMTAG_CAN_TAG_ADDRESSES ()
+True if the backend architecture naturally supports ignoring some region
+of pointers.  This feature means that @option{-fsanitize=hwaddress} can
+work.
+
+At preset, this feature does not support address spaces.  It also requires
+@code{Pmode} to be the same as @code{ptr_mode}.
+@end deftypefn
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 897f28962669031f21779847b7c63dab377ed7e2..8fbd36e2bf31e098f7827ce331fd7059c8a747bc 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -8184,3 +8184,5 @@ maintainer is familiar with.
 @hook TARGET_SPECULATION_SAFE_VALUE
 
 @hook TARGET_RUN_TARGET_SELFTESTS
+
+@hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 648ed096e30c89e5eca0caeaced7ba3ff57b5666..1c3e6a9951014c755e58b66cb7c6054f94baa78f 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -274,6 +274,9 @@ enum sanitize_code {
   SANITIZE_BUILTIN = 1UL << 25,
   SANITIZE_POINTER_COMPARE = 1UL << 26,
   SANITIZE_POINTER_SUBTRACT = 1UL << 27,
+  SANITIZE_HWADDRESS = 1UL << 28,
+  SANITIZE_USER_HWADDRESS = 1UL << 29,
+  SANITIZE_KERNEL_HWADDRESS = 1UL << 30,
   SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
diff --git a/gcc/gcc.c b/gcc/gcc.c
index cdf4d4f2403e6236ff0f33c4def95bc445a51dee..69f6fdcb324342b0153b64af0b5d607e0a555d8a 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -743,6 +743,24 @@ proper position among the other output files.  */
 #define LIBASAN_EARLY_SPEC ""
 #endif
 
+#ifndef LIBHWASAN_SPEC
+#define STATIC_LIBHWASAN_LIBS \
+  " %{static-libhwasan|static:%:include(libsanitizer.spec)%(link_libhwasan)}"
+#ifdef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_SPEC STATIC_LIBHWASAN_LIBS
+#elif defined(HAVE_LD_STATIC_DYNAMIC)
+#define LIBHWASAN_SPEC "%{static-libhwasan:" LD_STATIC_OPTION \
+		     "} -lhwasan %{static-libhwasan:" LD_DYNAMIC_OPTION "}" \
+		     STATIC_LIBHWASAN_LIBS
+#else
+#define LIBHWASAN_SPEC "-lhwasan" STATIC_LIBHWASAN_LIBS
+#endif
+#endif
+
+#ifndef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC ""
+#endif
+
 #ifndef LIBTSAN_SPEC
 #define STATIC_LIBTSAN_LIBS \
   " %{static-libtsan|static:%:include(libsanitizer.spec)%(link_libtsan)}"
@@ -1065,6 +1083,7 @@ proper position among the other output files.  */
 #ifndef SANITIZER_EARLY_SPEC
 #define SANITIZER_EARLY_SPEC "\
 %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_EARLY_SPEC "} \
+    %{%:sanitize(hwaddress):" LIBHWASAN_EARLY_SPEC "} \
     %{%:sanitize(thread):" LIBTSAN_EARLY_SPEC "} \
     %{%:sanitize(leak):" LIBLSAN_EARLY_SPEC "}}}}"
 #endif
@@ -1074,6 +1093,8 @@ proper position among the other output files.  */
 #define SANITIZER_SPEC "\
 %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_SPEC "\
     %{static:%ecannot specify -static with -fsanitize=address}}\
+    %{%:sanitize(hwaddress):" LIBHWASAN_SPEC "\
+	%{static:%ecannot specify -static with -fsanitize=hwaddress}}\
     %{%:sanitize(thread):" LIBTSAN_SPEC "\
     %{static:%ecannot specify -static with -fsanitize=thread}}\
     %{%:sanitize(undefined):" LIBUBSAN_SPEC "}\
@@ -10125,8 +10146,12 @@ sanitize_spec_function (int argc, const char **argv)
 
   if (strcmp (argv[0], "address") == 0)
     return (flag_sanitize & SANITIZE_USER_ADDRESS) ? "" : NULL;
+  if (strcmp (argv[0], "hwaddress") == 0)
+    return (flag_sanitize & SANITIZE_USER_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "kernel-address") == 0)
     return (flag_sanitize & SANITIZE_KERNEL_ADDRESS) ? "" : NULL;
+  if (strcmp (argv[0], "kernel-hwaddress") == 0)
+    return (flag_sanitize & SANITIZE_KERNEL_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "thread") == 0)
     return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
   if (strcmp (argv[0], "undefined") == 0)
diff --git a/gcc/opts.c b/gcc/opts.c
index 57774916a098425dd7a635e86610b1893b8a7907..cc1d0cc04f64133a22bac464732fd9b7fd8b5858 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -823,6 +823,57 @@ control_options_for_live_patching (struct gcc_options *opts,
 /* --help option argument if set.  */
 vec<const char *> help_option_arguments;
 
+/* Return the string name describing a sanitizer argument which has been
+   provided on the command line and has set this particular flag.  */
+const char *
+find_sanitizer_argument (struct gcc_options *opts, unsigned int flags)
+{
+  for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
+    {
+      /* Need to find the sanitizer_opts element which:
+	 a) Could have set the flags requested.
+	 b) Has been set on the command line.
+
+	 Can have (a) without (b) if the flag requested is e.g.
+	 SANITIZE_ADDRESS, since both -fsanitize=address and
+	 -fsanitize=kernel-address set this flag.
+
+	 Can have (b) without (a) by requesting more than one sanitizer on the
+	 command line.  */
+      if ((sanitizer_opts[i].flag & opts->x_flag_sanitize)
+	  != sanitizer_opts[i].flag)
+	continue;
+      if ((sanitizer_opts[i].flag & flags) != flags)
+	continue;
+      return sanitizer_opts[i].name;
+    }
+  return NULL;
+}
+
+
+/* Report an error to the user about sanitizer options they have requested
+   which have set conflicting flags.
+
+   LEFT and RIGHT indicate sanitizer flags which conflict with each other, this
+   function reports an error if both have been set in OPTS->x_flag_sanitize and
+   ensures the error identifies the requested command line options that have
+   set these flags.  */
+static void
+report_conflicting_sanitizer_options (struct gcc_options *opts, location_t loc,
+				      unsigned int left, unsigned int right)
+{
+  unsigned int left_seen = (opts->x_flag_sanitize & left);
+  unsigned int right_seen = (opts->x_flag_sanitize & right);
+  if (left_seen && right_seen)
+    {
+      const char* left_arg = find_sanitizer_argument (opts, left_seen);
+      const char* right_arg = find_sanitizer_argument (opts, right_seen);
+      gcc_assert (left_arg && right_arg);
+      error_at (loc,
+		"%<-fsanitize=%s%> is incompatible with %<-fsanitize=%s%>",
+		left_arg, right_arg);
+    }
+}
 
 /* After all options at LOC have been read into OPTS and OPTS_SET,
    finalize settings of those options and diagnose incompatible
@@ -1074,22 +1125,22 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 		  "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>");
     }
 
-  /* Userspace and kernel ASan conflict with each other.  */
-  if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
-      && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
-    error_at (loc, "%qs is incompatible with %qs",
-	      "-fsanitize=address", "-fsanitize=kernel-address");
+  /* Address sanitizers conflict with the thread sanitizer.  */
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_THREAD,
+					SANITIZE_ADDRESS | SANITIZE_HWADDRESS);
+  /* The leak sanitizer conflicts with the thread sanitizer.  */
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_LEAK,
+					SANITIZE_THREAD);
 
-  /* And with TSan.  */
-  if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
-      && (opts->x_flag_sanitize & SANITIZE_THREAD))
-    error_at (loc, "%qs is incompatible with %qs",
-	      "-fsanitize=thread", "-fsanitize=address|kernel-address");
+  /* No combination of HWASAN and ASAN work together.  */
+  report_conflicting_sanitizer_options (opts, loc,
+					SANITIZE_HWADDRESS, SANITIZE_ADDRESS);
 
-  if ((opts->x_flag_sanitize & SANITIZE_LEAK)
-      && (opts->x_flag_sanitize & SANITIZE_THREAD))
-    error_at (loc, "%qs is incompatible with %qs",
-	      "-fsanitize=leak", "-fsanitize=thread");
+  /* The userspace and kernel address sanitizers conflict with each other.  */
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_USER_HWADDRESS,
+					SANITIZE_KERNEL_HWADDRESS);
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_USER_ADDRESS,
+					SANITIZE_KERNEL_ADDRESS);
 
   /* Check error recovery for -fsanitize-recover option.  */
   for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
@@ -1108,9 +1159,10 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
   if (opts->x_flag_sanitize & ~(SANITIZE_LEAK | SANITIZE_UNREACHABLE))
     opts->x_flag_aggressive_loop_optimizations = 0;
 
-  /* Enable -fsanitize-address-use-after-scope if address sanitizer is
+  /* Enable -fsanitize-address-use-after-scope if either address sanitizer is
      enabled.  */
-  if (opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+  if (opts->x_flag_sanitize
+      & (SANITIZE_USER_ADDRESS | SANITIZE_USER_HWADDRESS))
     SET_OPTION_IF_UNSET (opts, opts_set, flag_sanitize_address_use_after_scope,
 			 true);
 
@@ -1724,8 +1776,13 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 #define SANITIZER_OPT(name, flags, recover) \
     { #name, flags, sizeof #name - 1, recover }
   SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true),
+  SANITIZER_OPT (hwaddress, (SANITIZE_HWADDRESS | SANITIZE_USER_HWADDRESS),
+		 true),
   SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
 		 true),
+  SANITIZER_OPT (kernel-hwaddress,
+		 (SANITIZE_HWADDRESS | SANITIZE_KERNEL_HWADDRESS),
+		 true),
   SANITIZER_OPT (pointer-compare, SANITIZE_POINTER_COMPARE, true),
   SANITIZER_OPT (pointer-subtract, SANITIZE_POINTER_SUBTRACT, true),
   SANITIZER_OPT (thread, SANITIZE_THREAD, false),
@@ -2304,6 +2361,15 @@ common_handle_option (struct gcc_options *opts,
 	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_protect_allocas, 0);
 	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_use_after_return, 0);
 	}
+      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+	{
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_instrument_stack, 0);
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_random_frame_tag, 0);
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_instrument_allocas, 0);
+	}
       break;
 
     case OPT_fsanitize_recover_:
diff --git a/gcc/params.opt b/gcc/params.opt
index 3f1f6093c53a58e7644c481bf6e735fcf86be263..d9598187dd29af920f51f2ff9ac77cc3cd681ede 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -62,6 +62,30 @@ Enable asan stack protection.
 Common Joined UInteger Var(param_asan_use_after_return) Init(1) IntegerRange(0, 1) Param Optimization
 Enable asan detection of use-after-return bugs.
 
+-param=hwasan-instrument-stack=
+Common Joined UInteger Var(param_hwasan_instrument_stack) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of statically sized stack-allocated variables.
+
+-param=hwasan-random-frame-tag=
+Common Joined UInteger Var(param_hwasan_random_frame_tag) Init(1) IntegerRange(0, 1) Param Optimization
+Use random base tag for each frame, as opposed to base always zero.
+
+-param=hwasan-instrument-allocas=
+Common Joined UInteger Var(param_hwasan_instrument_allocas) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of allocas/VLAs.
+
+-param=hwasan-instrument-reads=
+Common Joined UInteger Var(param_hwasan_instrument_reads) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of load operations.
+
+-param=hwasan-instrument-writes=
+Common Joined UInteger Var(param_hwasan_instrument_writes) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of store operations.
+
+-param=hwasan-instrument-mem-intrinsics=
+Common Joined UInteger Var(param_hwasan_instrument_mem_intrinsics) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of builtin functions.
+
 -param=avg-loop-niter=
 Common Joined UInteger Var(param_avg_loop_niter) Init(10) IntegerRange(1, 65536) Param Optimization
 Average number of iterations of a loop.
diff --git a/gcc/target.def b/gcc/target.def
index ff7ad5983ac26a71dc7dd4f4b3cb85c4e8c3e362..25f0ae228210f926077020082f129fb2e599f062 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6861,6 +6861,21 @@ DEFHOOK
 HOOK_VECTOR_END (mode_switching)
 
 #undef HOOK_PREFIX
+#define HOOK_PREFIX "TARGET_MEMTAG_"
+HOOK_VECTOR (TARGET_MEMTAG_, memtag)
+
+DEFHOOK
+(can_tag_addresses,
+ "True if the backend architecture naturally supports ignoring some region\n\
+of pointers.  This feature means that @option{-fsanitize=hwaddress} can\n\
+work.\n\
+\n\
+At preset, this feature does not support address spaces.  It also requires\n\
+@code{Pmode} to be the same as @code{ptr_mode}.",
+ bool, (), default_memtag_can_tag_addresses)
+
+HOOK_VECTOR_END (memtag)
+#undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
 
 #define DEF_TARGET_INSN(NAME, PROTO) \
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index e0a925fa2bee2e73f82d244fe4364afc87eff7d2..0065c686978d7120978430013c73b1055aaf95c7 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -286,4 +286,5 @@ extern bool default_have_speculation_safe_value (bool);
 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 ();
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 5b68a2ad7d4a435d66fad68ab8827f59289e81f4..46cb536041d396c32fd08042581d6d5cd5ad0395 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2415,4 +2415,10 @@ default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
   return result;
 }
 
+bool
+default_memtag_can_tag_addresses ()
+{
+  return false;
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index e0e0e04e95ff6b295991ad288c916dfbea520883..2a3e7c064a5fbb6913481104975ca85615e49f8e 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1853,6 +1853,15 @@ process_options (void)
       flag_sanitize &= ~SANITIZE_ADDRESS;
     }
 
+  /* HWAsan requires top byte ignore feature in the backend.  */
+  if (flag_sanitize & SANITIZE_HWADDRESS
+      && ! targetm.memtag.can_tag_addresses ())
+    {
+      warning_at (UNKNOWN_LOCATION, 0, "%qs is not supported for this target",
+		  "-fsanitize=hwaddress");
+      flag_sanitize &= ~SANITIZE_HWADDRESS;
+    }
+
  /* Do not use IPA optimizations for register allocation if profiler is active
     or patchable function entries are inserted for run-time instrumentation
     or port does not emit prologue and epilogue as RTL.  */
Richard Sandiford Nov. 23, 2020, 8:12 p.m. UTC | #3
Matthew Malcomson <matthew.malcomson@arm.com> writes:
> Hi there,
>
> I was just doing some double-checks and noticed I'd placed the
> documentation in the wrong section of tm.texi.  The `MEMTAG` hooks were
> documented in the `Register Classes` section, so I've now moved it to
> the `Misc` section.
>
> That's the only change, Ok for trunk?

OK, thanks.

Richard
diff mbox series

Patch

diff --git a/gcc/common.opt b/gcc/common.opt
index d4cbb2f86a554fa2f87e52b2b6cbd1de27ddaa31..c7eec0fe683b817233ab4572024c16542a646cb4 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -218,7 +218,7 @@  unsigned int flag_sanitize
 
 ; What sanitizers should recover from errors
 Variable
-unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
+unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS | SANITIZE_KERNEL_HWADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
 
 ; What the coverage sanitizers should instrument
 Variable
@@ -3429,6 +3429,9 @@  Driver
 static-libasan
 Driver
 
+static-libhwasan
+Driver
+
 static-libtsan
 Driver
 
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 27f587be7e7483093f1fd381d8fa14a6c6581ccf..ea85aadb619eb2e9ee7328f035f81aa8578ee3ad 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -23175,6 +23175,15 @@  aarch64_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1,
   return NULL;
 }
 
+/* Implement TARGET_MEMTAG_CAN_TAG_ADDRESSES.  Here we tell the rest of the
+   compiler that we automatically ignore the top byte of our pointers, which
+   allows using -fsanitize=hwaddress.  */
+bool
+aarch64_can_tag_addresses ()
+{
+  return true;
+}
+
 /* Implement TARGET_ASM_FILE_END for AArch64.  This adds the AArch64 GNU NOTE
    section at the end if needed.  */
 #define GNU_PROPERTY_AARCH64_FEATURE_1_AND	0xc0000000
@@ -23994,6 +24003,9 @@  aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_FNTYPE_ABI
 #define TARGET_FNTYPE_ABI aarch64_fntype_abi
 
+#undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
+#define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h
index ff2e880b1fa81b76af866b6ba0e1fabf6d534f32..92950245e2b95c500da9df8b85940380e62030fa 100644
--- a/gcc/config/gnu-user.h
+++ b/gcc/config/gnu-user.h
@@ -129,14 +129,18 @@  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Link -lasan early on the command line.  For -static-libasan, don't link
    it for -shared link, the executable should be compiled with -static-libasan
    in that case, and for executable link with --{,no-}whole-archive around
-   it to force everything into the executable.  And similarly for -ltsan
-   and -llsan.  */
+   it to force everything into the executable.  And similarly for -ltsan,
+   -lhwasan, and -llsan.  */
 #if defined(HAVE_LD_STATIC_DYNAMIC)
 #undef LIBASAN_EARLY_SPEC
 #define LIBASAN_EARLY_SPEC "%{!shared:libasan_preinit%O%s} " \
   "%{static-libasan:%{!shared:" \
   LD_STATIC_OPTION " --whole-archive -lasan --no-whole-archive " \
   LD_DYNAMIC_OPTION "}}%{!static-libasan:-lasan}"
+#undef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC "%{static-libhwasan:%{!shared:" \
+  LD_STATIC_OPTION " --whole-archive -lhwasan --no-whole-archive " \
+  LD_DYNAMIC_OPTION "}}%{!static-libhwasan:-lhwasan}"
 #undef LIBTSAN_EARLY_SPEC
 #define LIBTSAN_EARLY_SPEC "%{!shared:libtsan_preinit%O%s} " \
   "%{static-libtsan:%{!shared:" \
diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c
index 61efe9b2b75e961c15e9f399cafed559b5c8c9be..fc61c78a99373fcfbc2bb03a01134248214eb2d4 100644
--- a/gcc/cppbuiltin.c
+++ b/gcc/cppbuiltin.c
@@ -93,6 +93,9 @@  define_builtin_macros_for_compilation_flags (cpp_reader *pfile)
   if (flag_sanitize & SANITIZE_ADDRESS)
     cpp_define (pfile, "__SANITIZE_ADDRESS__");
 
+  if (flag_sanitize & SANITIZE_HWADDRESS)
+    cpp_define (pfile, "__SANITIZE_HWADDRESS__");
+
   if (flag_sanitize & SANITIZE_THREAD)
     cpp_define (pfile, "__SANITIZE_THREAD__");
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 5320e6c1e1e3c8d1482c20590049f763e11f8ff0..84050058be8eaa306b07655737e49ea8b6eb21a9 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13709,6 +13709,53 @@  is greater or equal to this number, use callbacks instead of inline checks.
 E.g. to disable inline code use
 @option{--param asan-instrumentation-with-call-threshold=0}.
 
+@item hwasan-instrument-stack
+Enable hwasan instrumentation of statically sized stack-allocated variables.
+This kind of instrumentation is enabled by default when using
+@option{-fsanitize=hwaddress} and disabled by default when using
+@option{-fsanitize=kernel-hwaddress}.
+To disable stack instrumentation use
+@option{--param hwasan-instrument-stack=0}, and to enable it use
+@option{--param hwasan-instrument-stack=1}.
+
+@item hwasan-random-frame-tag
+When using stack instrumentation, decide tags for stack variables using a
+deterministic sequence beginning at a random tag for each frame.  Usually tags
+are chosen using the same sequence beginning from 1.
+This is enabled by default for @option{-fsanitize=hwaddress} and unavailable
+for @option{-fsanitize=kernel-hwaddress}.
+To disable it use @option{--param hwasan-random-frame-tag=0}.
+
+@item hwasan-instrument-allocas
+Enable hwasan instrumentation of dynamically sized stack-allocated variables.
+This kind of instrumentation is enabled by default when using
+@option{-fsanitize=hwaddress} and disabled by default when using
+@option{-fsanitize=kernel-hwaddress}.
+To disable instrumentation of such variables use
+@option{--param hwasan-instrument-allocas=0}, and to enable it use
+@option{--param hwasan-instrument-allocas=1}.
+
+@item hwasan-instrument-reads
+Enable hwasan checks on memory reads.  Instrumentation of reads is enabled by
+default for both @option{-fsanitize=hwaddress} and
+@option{-fsanitize=kernel-hwaddress}.
+To disable checking memory reads use
+@option{--param hwasan-instrument-reads=0}.
+
+@item hwasan-instrument-writes
+Enable hwasan checks on memory writes.  Instrumentation of writes is enabled by
+default for both @option{-fsanitize=hwaddress} and
+@option{-fsanitize=kernel-hwaddress}.
+To disable checking memory writes use
+@option{--param hwasan-instrument-writes=0}.
+
+@item hwasan-instrument-mem-intrinsics
+Enable hwasan instrumentation of builtin functions.  Instrumentation of these
+builtin functions is enabled by default for both @option{-fsanitize=hwaddress}
+and @option{-fsanitize=kernel-hwaddress}.
+To disable instrumentation of builtin functions use
+@option{--param hwasan-instrument-mem-intrinsics=0}.
+
 @item use-after-scope-direct-emission-threshold
 If the size of a local variable in bytes is smaller or equal to this
 number, directly poison (or unpoison) shadow memory instead of using
@@ -14243,13 +14290,46 @@  more details.  The run-time behavior can be influenced using the
 the available options are shown at startup of the instrumented program.  See
 @url{https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags}
 for a list of supported options.
-The option cannot be combined with @option{-fsanitize=thread}.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=hwaddress}.  Note that the only target this option is
+currently supported on is AArch64.
 
 @item -fsanitize=kernel-address
 @opindex fsanitize=kernel-address
 Enable AddressSanitizer for Linux kernel.
 See @uref{https://github.com/google/kasan/wiki} for more details.
 
+@item -fsanitize=hwaddress
+@opindex fsanitize=hwaddress
+Enable Hardware-assisted AddressSanitizer, which uses a hardware ability to
+ignore the top byte of a pointer to allow the detection of memory errors with
+a low memory overhead.
+Memory access instructions are instrumented to detect out-of-bounds and
+use-after-free bugs.
+The option enables @option{-fsanitize-address-use-after-scope}.
+See
+@uref{https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html}
+for more details.  The run-time behavior can be influenced using the
+@env{HWASAN_OPTIONS} environment variable.  When set to @code{help=1},
+the available options are shown at startup of the instrumented program.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=address}, and is currently only available on AArch64.
+
+@item -fsanitize=kernel-hwaddress
+@opindex fsanitize=kernel-hwaddress
+Enable Hardware-assisted AddressSanitizer for compilation of the Linux kernel.
+Similar to @option{-fsanitize=kernel-address} but using an alternate
+instrumentation method, and similar to @option{-fsanitize=hwaddress} but with
+instrumentation differences necessary for compiling the Linux kernel.
+These differences are to avoid hwasan library initialisation calls and to
+account for the stack pointer having a different value in its top byte.
+Note: This option has different defaults to the @option{-fsanitize=hwaddress}.
+Instrumenting the stack and alloca calls are not on by default but is still
+possible by specifying it on the command line with
+@option{--param hwasan-instrument-stack=1} and
+@option{--param hwasan-instrument-allocas=1}. Using a random frame tag is not
+implemented for kernel instrumentation.
+
 @item -fsanitize=pointer-compare
 @opindex fsanitize=pointer-compare
 Instrument comparison operation (<, <=, >, >=) with pointer operands.
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 833320ba7bfbea332d41cd38641c19ed9593366d..321b0fe35816a257e42a1464a4ac84e849d376f2 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2975,6 +2975,11 @@  This hook defines the machine mode to use for the boolean result of  conditional
 A target hook which lets a backend compute the set of pressure classes to  be used by those optimization passes which take register pressure into  account, as opposed to letting IRA compute them.  It returns the number of  register classes stored in the array @var{pressure_classes}.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_MEMTAG_CAN_TAG_ADDRESSES ()
+True if backend architecture naturally supports ignoring some region of
+pointers.  This feature means that @option{-fsanitize=hwaddress} can work.
+@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 58109be36932d89085240557da7be5664605946a..6b6313c8493636f3615cd9e5bc6cb091467a4a8e 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2375,6 +2375,8 @@  in the reload pass.
 
 @hook TARGET_COMPUTE_PRESSURE_CLASSES
 
+@hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index a887c75cfc792030661c3b004c9569e6816cde8f..8015fa44e06bb1f3e59288761a6097d67f4cbc20 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -274,6 +274,9 @@  enum sanitize_code {
   SANITIZE_BUILTIN = 1UL << 25,
   SANITIZE_POINTER_COMPARE = 1UL << 26,
   SANITIZE_POINTER_SUBTRACT = 1UL << 27,
+  SANITIZE_HWADDRESS = 1UL << 28,
+  SANITIZE_USER_HWADDRESS = 1UL << 29,
+  SANITIZE_KERNEL_HWADDRESS = 1UL << 30,
   SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
diff --git a/gcc/gcc.c b/gcc/gcc.c
index cdf4d4f2403e6236ff0f33c4def95bc445a51dee..69f6fdcb324342b0153b64af0b5d607e0a555d8a 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -743,6 +743,24 @@  proper position among the other output files.  */
 #define LIBASAN_EARLY_SPEC ""
 #endif
 
+#ifndef LIBHWASAN_SPEC
+#define STATIC_LIBHWASAN_LIBS \
+  " %{static-libhwasan|static:%:include(libsanitizer.spec)%(link_libhwasan)}"
+#ifdef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_SPEC STATIC_LIBHWASAN_LIBS
+#elif defined(HAVE_LD_STATIC_DYNAMIC)
+#define LIBHWASAN_SPEC "%{static-libhwasan:" LD_STATIC_OPTION \
+		     "} -lhwasan %{static-libhwasan:" LD_DYNAMIC_OPTION "}" \
+		     STATIC_LIBHWASAN_LIBS
+#else
+#define LIBHWASAN_SPEC "-lhwasan" STATIC_LIBHWASAN_LIBS
+#endif
+#endif
+
+#ifndef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC ""
+#endif
+
 #ifndef LIBTSAN_SPEC
 #define STATIC_LIBTSAN_LIBS \
   " %{static-libtsan|static:%:include(libsanitizer.spec)%(link_libtsan)}"
@@ -1065,6 +1083,7 @@  proper position among the other output files.  */
 #ifndef SANITIZER_EARLY_SPEC
 #define SANITIZER_EARLY_SPEC "\
 %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_EARLY_SPEC "} \
+    %{%:sanitize(hwaddress):" LIBHWASAN_EARLY_SPEC "} \
     %{%:sanitize(thread):" LIBTSAN_EARLY_SPEC "} \
     %{%:sanitize(leak):" LIBLSAN_EARLY_SPEC "}}}}"
 #endif
@@ -1074,6 +1093,8 @@  proper position among the other output files.  */
 #define SANITIZER_SPEC "\
 %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_SPEC "\
     %{static:%ecannot specify -static with -fsanitize=address}}\
+    %{%:sanitize(hwaddress):" LIBHWASAN_SPEC "\
+	%{static:%ecannot specify -static with -fsanitize=hwaddress}}\
     %{%:sanitize(thread):" LIBTSAN_SPEC "\
     %{static:%ecannot specify -static with -fsanitize=thread}}\
     %{%:sanitize(undefined):" LIBUBSAN_SPEC "}\
@@ -10125,8 +10146,12 @@  sanitize_spec_function (int argc, const char **argv)
 
   if (strcmp (argv[0], "address") == 0)
     return (flag_sanitize & SANITIZE_USER_ADDRESS) ? "" : NULL;
+  if (strcmp (argv[0], "hwaddress") == 0)
+    return (flag_sanitize & SANITIZE_USER_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "kernel-address") == 0)
     return (flag_sanitize & SANITIZE_KERNEL_ADDRESS) ? "" : NULL;
+  if (strcmp (argv[0], "kernel-hwaddress") == 0)
+    return (flag_sanitize & SANITIZE_KERNEL_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "thread") == 0)
     return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
   if (strcmp (argv[0], "undefined") == 0)
diff --git a/gcc/opts.c b/gcc/opts.c
index ac9972d9c386247af3482e07a94c76da3e1abb4d..f3662062c421e1c58c3243109891900eb2dc84bc 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -823,6 +823,51 @@  control_options_for_live_patching (struct gcc_options *opts,
 /* --help option argument if set.  */
 vec<const char *> help_option_arguments;
 
+/* Return the string name describing the argument provided on the command line
+    which has set this particular flag.  */
+const char *
+find_argument (struct gcc_options *opts, unsigned int flags)
+{
+  for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
+    {
+      /* Need to find the sanitizer_opts element which:
+	 a) Could have set the flags requested.
+	 b) Has been set on the command line.
+
+	 Can have (a) without (b) if the flag requested is e.g.
+	 SANITIZE_ADDRESS, since both -fsanitize=address and
+	 -fsanitize=kernel-address set this flag.
+
+	 Can have (b) without (a) by requesting more than one sanitizer on the
+	 command line.  */
+      if ((sanitizer_opts[i].flag & opts->x_flag_sanitize)
+	  != sanitizer_opts[i].flag)
+	continue;
+      if ((sanitizer_opts[i].flag & flags) != flags)
+	continue;
+      return sanitizer_opts[i].name;
+    }
+  return NULL;
+}
+
+
+/* Report any conflicting sanitizer options requested.  */
+static void
+report_conflicting_sanitizer_options (struct gcc_options *opts, location_t loc,
+				      unsigned int left, unsigned int right)
+{
+  unsigned int left_seen = (opts->x_flag_sanitize & left);
+  unsigned int right_seen = (opts->x_flag_sanitize & right);
+  if (left_seen && right_seen)
+    {
+      const char* left_arg = find_argument (opts, left_seen);
+      const char* right_arg = find_argument (opts, right_seen);
+      gcc_assert (left_arg && right_arg);
+      error_at (loc,
+		"%<-fsanitize=%s%> is incompatible with %<-fsanitize=%s%>",
+		left_arg, right_arg);
+    }
+}
 
 /* After all options at LOC have been read into OPTS and OPTS_SET,
    finalize settings of those options and diagnose incompatible
@@ -1074,22 +1119,21 @@  finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 		  "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>");
     }
 
-  /* Userspace and kernel ASan conflict with each other.  */
-  if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
-      && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
-    error_at (loc, "%qs is incompatible with %qs",
-	      "-fsanitize=address", "-fsanitize=kernel-address");
+  /* Address sanitizers conflict with the thread sanitizer.  */
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_THREAD,
+					SANITIZE_ADDRESS | SANITIZE_HWADDRESS);
+  /* The leak sanitizer conflicts with the thread sanitizer.  */
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_LEAK, SANITIZE_THREAD);
 
-  /* And with TSan.  */
-  if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
-      && (opts->x_flag_sanitize & SANITIZE_THREAD))
-    error_at (loc, "%qs is incompatible with %qs",
-	      "-fsanitize=thread", "-fsanitize=address|kernel-address");
+  /* No combination of HWASAN and ASAN work together.  */
+  report_conflicting_sanitizer_options (opts, loc,
+					SANITIZE_HWADDRESS, SANITIZE_ADDRESS);
 
-  if ((opts->x_flag_sanitize & SANITIZE_LEAK)
-      && (opts->x_flag_sanitize & SANITIZE_THREAD))
-    error_at (loc, "%qs is incompatible with %qs",
-	      "-fsanitize=leak", "-fsanitize=thread");
+  /* The userspace and kernel address sanitizers conflict with each other.  */
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_USER_HWADDRESS,
+					SANITIZE_KERNEL_HWADDRESS);
+  report_conflicting_sanitizer_options (opts, loc, SANITIZE_USER_ADDRESS,
+					SANITIZE_KERNEL_ADDRESS);
 
   /* Check error recovery for -fsanitize-recover option.  */
   for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
@@ -1108,9 +1152,10 @@  finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
   if (opts->x_flag_sanitize & ~(SANITIZE_LEAK | SANITIZE_UNREACHABLE))
     opts->x_flag_aggressive_loop_optimizations = 0;
 
-  /* Enable -fsanitize-address-use-after-scope if address sanitizer is
+  /* Enable -fsanitize-address-use-after-scope if either address sanitizer is
      enabled.  */
-  if (opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+  if (opts->x_flag_sanitize
+      & (SANITIZE_USER_ADDRESS | SANITIZE_USER_HWADDRESS))
     SET_OPTION_IF_UNSET (opts, opts_set, flag_sanitize_address_use_after_scope,
 			 true);
 
@@ -1724,8 +1769,13 @@  const struct sanitizer_opts_s sanitizer_opts[] =
 #define SANITIZER_OPT(name, flags, recover) \
     { #name, flags, sizeof #name - 1, recover }
   SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true),
+  SANITIZER_OPT (hwaddress, (SANITIZE_HWADDRESS | SANITIZE_USER_HWADDRESS),
+		 true),
   SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
 		 true),
+  SANITIZER_OPT (kernel-hwaddress,
+		 (SANITIZE_HWADDRESS | SANITIZE_KERNEL_HWADDRESS),
+		 true),
   SANITIZER_OPT (pointer-compare, SANITIZE_POINTER_COMPARE, true),
   SANITIZER_OPT (pointer-subtract, SANITIZE_POINTER_SUBTRACT, true),
   SANITIZER_OPT (thread, SANITIZE_THREAD, false),
@@ -2304,6 +2354,14 @@  common_handle_option (struct gcc_options *opts,
 	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_protect_allocas, 0);
 	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_use_after_return, 0);
 	}
+      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+	{
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_instrument_stack, 0);
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_random_frame_tag, 0);
+	  SET_OPTION_IF_UNSET (opts, opts_set, param_hwasan_protect_allocas, 0);
+	}
       break;
 
     case OPT_fsanitize_recover_:
diff --git a/gcc/params.opt b/gcc/params.opt
index a33a371a395df179dc8b1ed2b7ef9e9120d384fa..75f89b168781080e0b9bf119c936cdb6c5b96b66 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -62,6 +62,30 @@  Enable asan stack protection.
 Common Joined UInteger Var(param_asan_use_after_return) Init(1) IntegerRange(0, 1) Param Optimization
 Enable asan detection of use-after-return bugs.
 
+-param=hwasan-instrument-stack=
+Common Joined UInteger Var(param_hwasan_instrument_stack) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of statically sized stack-allocated variables.
+
+-param=hwasan-random-frame-tag=
+Common Joined UInteger Var(param_hwasan_random_frame_tag) Init(1) IntegerRange(0, 1) Param Optimization
+Use random base tag for each frame, as opposed to base always zero.
+
+-param=hwasan-instrument-allocas=
+Common Joined UInteger Var(param_hwasan_protect_allocas) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of allocas/VLAs.
+
+-param=hwasan-instrument-reads=
+Common Joined UInteger Var(param_hwasan_instrument_reads) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of load operations.
+
+-param=hwasan-instrument-writes=
+Common Joined UInteger Var(param_hwasan_instrument_writes) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of store operations.
+
+-param=hwasan-instrument-mem-intrinsics=
+Common Joined UInteger Var(param_hwasan_instrument_mem_intrinsics) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan instrumentation of builtin functions.
+
 -param=avg-loop-niter=
 Common Joined UInteger Var(param_avg_loop_niter) Init(10) IntegerRange(1, 65536) Param Optimization
 Average number of iterations of a loop.
diff --git a/gcc/target.def b/gcc/target.def
index b916635be1816ee05327736e3ac4631a1214fba9..dc20d262fd1b7da074bb15ff2cc06f9164a4ae17 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6850,6 +6850,17 @@  DEFHOOK
 HOOK_VECTOR_END (mode_switching)
 
 #undef HOOK_PREFIX
+#define HOOK_PREFIX "TARGET_MEMTAG_"
+HOOK_VECTOR (TARGET_MEMTAG_, memtag)
+
+DEFHOOK
+(can_tag_addresses,
+ "True if backend architecture naturally supports ignoring some region of\n\
+pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
+ bool, (), default_memtag_can_tag_addresses)
+
+HOOK_VECTOR_END (memtag)
+#undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
 
 #define DEF_TARGET_INSN(NAME, PROTO) \
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index e0a925fa2bee2e73f82d244fe4364afc87eff7d2..0065c686978d7120978430013c73b1055aaf95c7 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -286,4 +286,5 @@  extern bool default_have_speculation_safe_value (bool);
 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 ();
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 5b68a2ad7d4a435d66fad68ab8827f59289e81f4..46cb536041d396c32fd08042581d6d5cd5ad0395 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2415,4 +2415,10 @@  default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
   return result;
 }
 
+bool
+default_memtag_can_tag_addresses ()
+{
+  return false;
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 20e231f4d2a47f142142832407541b9e70f13e81..a644a7307970b00c5d2433b3f7f3d228db00c1e6 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1853,6 +1853,15 @@  process_options (void)
       flag_sanitize &= ~SANITIZE_ADDRESS;
     }
 
+  /* HWAsan requires top byte ignore feature in the backend.  */
+  if (flag_sanitize & SANITIZE_HWADDRESS
+      && ! targetm.memtag.can_tag_addresses ())
+    {
+      warning_at (UNKNOWN_LOCATION, 0, "%qs is not supported for this target",
+		  "-fsanitize=hwaddress");
+      flag_sanitize &= ~SANITIZE_HWADDRESS;
+    }
+
  /* Do not use IPA optimizations for register allocation if profiler is active
     or patchable function entries are inserted for run-time instrumentation
     or port does not emit prologue and epilogue as RTL.  */