[4/X,libsanitizer,options] Add hwasan flags and argument parsing
diff mbox series

Message ID HE1PR0802MB2251FC8D950C22548C7DC04FE0780@HE1PR0802MB2251.eurprd08.prod.outlook.com
State New
Headers show
Series
  • [4/X,libsanitizer,options] Add hwasan flags and argument parsing
Related show

Commit Message

Matthew Malcomson Nov. 7, 2019, 6: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 that allow compiling KASAN with tags as
it is currently implemented.
These defaults are that we do not sanitize variables on the stack and
always recover from a detected bug.
Stack tagging in the kernel is a future aim, stack instrumentation has
not yet been enabled for the kernel for clang either
(https://lists.infradead.org/pipermail/linux-arm-kernel/2019-October/687121.html).

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:

2019-11-07  Matthew Malcomson  <matthew.malcomson@arm.com>

	* asan.c (memory_tagging_p): New.
	* asan.h (memory_tagging_p): New.
	* 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/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.
	(sanitizer_opts): Introduce new sanitizer flags.
	(common_handle_option): Add defaults for kernel sanitizer.
	* params.def (PARAM_HWASAN_RANDOM_FRAME_TAG): New.
	(PARAM_HWASAN_STACK): New.
	* params.h (HWASAN_STACK): New.
	(HWASAN_RANDOM_FRAME_TAG): New.
	* target.def (HOOK_PREFIX): Add new hook.
	* targhooks.c (default_memtag_can_tag_addresses): New.
	* toplev.c (process_options): Ensure hwasan only on TBI
	architectures.

gcc/c-family/ChangeLog:

2019-11-07  Matthew Malcomson  <matthew.malcomson@arm.com>

	* c-attribs.c (handle_no_sanitize_hwaddress_attribute): New
	attribute.



###############     Attachment also inlined for ease of reply    ###############
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 1c9f28587fbb2348cc30e302e889a5a22906901a..a5e68061ff956018957b6be137a7b2f2b7353647 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -54,6 +54,8 @@ static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_sanitize_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_sanitize_address_attribute (tree *, tree, tree,
 						  int, bool *);
+static tree handle_no_sanitize_hwaddress_attribute (tree *, tree, tree,
+						    int, bool *);
 static tree handle_no_sanitize_thread_attribute (tree *, tree, tree,
 						 int, bool *);
 static tree handle_no_address_safety_analysis_attribute (tree *, tree, tree,
@@ -412,6 +414,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_no_sanitize_attribute, NULL },
   { "no_sanitize_address",    0, 0, true, false, false, false,
 			      handle_no_sanitize_address_attribute, NULL },
+  { "no_sanitize_hwaddress",    0, 0, true, false, false, false,
+			      handle_no_sanitize_hwaddress_attribute, NULL },
   { "no_sanitize_thread",     0, 0, true, false, false, false,
 			      handle_no_sanitize_thread_attribute, NULL },
   { "no_sanitize_undefined",  0, 0, true, false, false, false,
@@ -941,6 +945,22 @@ handle_no_sanitize_address_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+/* Handle a "no_sanitize_hwaddress" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_no_sanitize_hwaddress_attribute (tree *node, tree name, tree, int,
+				      bool *no_add_attrs)
+{
+  *no_add_attrs = true;
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
+  else
+    add_no_sanitize_value (*node, SANITIZE_HWADDRESS);
+
+  return NULL_TREE;
+}
+
 /* Handle a "no_sanitize_thread" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/common.opt b/gcc/common.opt
index cc279f411d7967209a111d958437595ce5cacd3a..d44295530474f873a00bc97a7d46bdd26255db9c 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -215,7 +215,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
@@ -3313,6 +3313,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 599d07a729e7438080f8b5240ee95037a49fb983..e0c7e2c6276d91bd0efe5e9bf9b58b09087c3cd7 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -21371,6 +21371,15 @@ aarch64_stack_protect_guard (void)
   return NULL_TREE;
 }
 
+/* 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
@@ -21936,6 +21945,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 95a3c29f7cee86336f958bef1d7fe56b82e05e6c..90b1fa91742c6a7d76aa6c7e931f8014fc4fff0c 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 60e5bedc3665a25fa51c2eca00547f12a9953778..e8d0bedfc2eb22d1e72e7e4875155202c8389a38 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 d1eb317f43cd31156c80ef7dd910024c50f33deb..f710e29549f5d3d58ed74aca74f2c62b577cd155 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -12594,13 +12594,33 @@ 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}.
 
 @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 HardwareAddressSanitizer, a fast memory error detector.
+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://github.com/google/sanitizers/} 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}.
+
+@item -fsanitize=kernel-hwaddress
+@opindex fsanitize=kernel-hwaddress
+Enable HardwareAddressSanitizer for Linux kernel.
+Similar to @option{-fsanitize=kernel-address} but using the software tagging
+method of 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 cd9aed9874f4e6b2b0e2f8956ed6155975e643a8..e842b734c9c20253986880ba3622a8f692d3ca88 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2978,6 +2978,10 @@ 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 the top byte of pointers.  This feature means that -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 2739e9ceec5ad7253ff9135da8dbe3bf6010e8d7..6136ac1a5fe0c0980d5b5123b67b102a1b1e0bcc 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2377,6 +2377,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 a2103282d469db31ad157a87572068d943061c8c..57d8ff9a1a010409d966230140df1017bc3584a8 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -256,6 +256,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 c45a1df656b2b8122f457edeb55db5f44c57b5bf..037f9d3a688d8778b067aade698d40709ee97b3d 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -708,6 +708,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)}"
@@ -982,6 +1000,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
@@ -991,6 +1010,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 "}\
@@ -9439,8 +9460,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 10b9f108f8d0618501c1567bb4f8a7f9de2bdb98..b46c6d44164c04aa7e561fa3dc2513d66ce61e4d 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1119,6 +1119,13 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 		  "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>");
     }
 
+  /* Userspace and kernel HWasan conflict with each other.  */
+  if ((opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS))
+    error_at (loc,
+	      "%<-fsanitize=hwaddress%> is incompatible with "
+	      "%<-fsanitize=kernel-hwaddress%>");
+
   /* Userspace and kernel ASan conflict with each other.  */
   if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
       && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
@@ -1138,6 +1145,20 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
     error_at (loc,
 	      "%<-fsanitize=leak%> is incompatible with %<-fsanitize=thread%>");
 
+  /* HWASan and ASan conflict with each other.  */
+  if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_HWADDRESS))
+    error_at (loc,
+	      "%<-fsanitize=hwaddress%> is incompatible with both "
+	      "%<-fsanitize=address%> and %<-fsanitize=kernel-address%>");
+
+  /* HWASan conflicts with TSan.  */
+  if ((opts->x_flag_sanitize & SANITIZE_HWADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_THREAD))
+    error_at (loc,
+	      "%<-fsanitize=hwaddress%> is incompatible with "
+	      "%<-fsanitize=thread%>");
+
   /* Check error recovery for -fsanitize-recover option.  */
   for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
     if ((opts->x_flag_sanitize_recover & sanitizer_opts[i].flag)
@@ -1157,7 +1178,8 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 
   /* Enable -fsanitize-address-use-after-scope if address sanitizer is
      enabled.  */
-  if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+  if (((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+       || (opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS))
       && !opts_set->x_flag_sanitize_address_use_after_scope)
     opts->x_flag_sanitize_address_use_after_scope = true;
 
@@ -1786,8 +1808,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),
@@ -2322,6 +2349,14 @@ common_handle_option (struct gcc_options *opts,
 				 opts->x_param_values,
 				 opts_set->x_param_values);
 	}
+      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+	{
+	  maybe_set_param_value (PARAM_HWASAN_STACK, 0, opts->x_param_values,
+				 opts_set->x_param_values);
+	  maybe_set_param_value (PARAM_HWASAN_RANDOM_FRAME_TAG, 0,
+				 opts->x_param_values,
+				 opts_set->x_param_values);
+	}
       break;
 
     case OPT_fsanitize_recover_:
diff --git a/gcc/params.def b/gcc/params.def
index 942447d77e66a07736751ebf90bbce105b9dc267..33556f6fc18d8543a52a878cac4fbc1a2d85a73b 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1335,6 +1335,17 @@ DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
 	 "smaller or equal to this number.",
 	 256, 0, INT_MAX)
 
+/* HWAsan stands for HardwareAddressSanitizer: https://github.com/google/sanitizers.  */
+DEFPARAM (PARAM_HWASAN_RANDOM_FRAME_TAG,
+	  "hwasan-random-frame-tag",
+	  "Use random base tag for each frame, as opposed to base always zero.",
+	  1, 0, 1)
+
+DEFPARAM (PARAM_HWASAN_STACK,
+	  "hwasan-stack",
+	  "Enable hwasan stack protection.",
+	  1, 0, 1)
+
 DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
 	  "uninit-control-dep-attempts",
 	  "Maximum number of nested calls to search for control dependencies "
diff --git a/gcc/params.h b/gcc/params.h
index 1aaef6d6a00da79533eb0bba540dd8c731b1eaa8..102d79d42409590040ad9fe0beed0be73c8cc50a 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -250,5 +250,9 @@ extern void init_param_values (int *params);
   PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
 #define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
   ((unsigned) PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD))
+#define HWASAN_STACK \
+  PARAM_VALUE (PARAM_HWASAN_STACK)
+#define HWASAN_RANDOM_FRAME_TAG \
+  PARAM_VALUE (PARAM_HWASAN_RANDOM_FRAME_TAG)
 
 #endif /* ! GCC_PARAMS_H */
diff --git a/gcc/target.def b/gcc/target.def
index 8e83c2c7a7136511c07a5bc9e18876c91a38b955..ad16a151b6af9b1ee13918c8f2980280d75b1d90 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6742,6 +6742,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 the top byte of\
+ pointers.  This feature means that -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 d4c3563e82587feea9523a15c2dcc490dad9919e..94e865259f35e46e26f6b4763c5e2f9dc9ed1b83 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -282,4 +282,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 fee4cc271cd86a2206e18da2c2db24c5e04b0f4f..6e9e877c32e2c8705056bdf0ce2b1b6f125d93c3 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2365,4 +2365,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 00a5e83212601bef62ea9a254ddbeae35cde8e6e..ab67384249a3437ac37f42f741ed516884677f9f 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1753,6 +1753,16 @@ 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,
+		  "%<-fsanitize=hwaddress%> can not be implemented on "
+		  "a backend that does not ignore the top byte of a pointer");
+      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

Martin Liška Nov. 20, 2019, 11:17 a.m. UTC | #1
On 11/7/19 7:37 PM, Matthew Malcomson wrote:
> 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 that allow compiling KASAN with tags as
> it is currently implemented.
> These defaults are that we do not sanitize variables on the stack and
> always recover from a detected bug.
> Stack tagging in the kernel is a future aim, stack instrumentation has
> not yet been enabled for the kernel for clang either
> (https://lists.infradead.org/pipermail/linux-arm-kernel/2019-October/687121.html).
> 
> 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:
> 
> 2019-11-07  Matthew Malcomson  <matthew.malcomson@arm.com>
> 
> 	* asan.c (memory_tagging_p): New.
> 	* asan.h (memory_tagging_p): New.
> 	* 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/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.
> 	(sanitizer_opts): Introduce new sanitizer flags.
> 	(common_handle_option): Add defaults for kernel sanitizer.
> 	* params.def (PARAM_HWASAN_RANDOM_FRAME_TAG): New.
> 	(PARAM_HWASAN_STACK): New.
> 	* params.h (HWASAN_STACK): New.
> 	(HWASAN_RANDOM_FRAME_TAG): New.
> 	* target.def (HOOK_PREFIX): Add new hook.
> 	* targhooks.c (default_memtag_can_tag_addresses): New.
> 	* toplev.c (process_options): Ensure hwasan only on TBI
> 	architectures.
> 
> gcc/c-family/ChangeLog:
> 
> 2019-11-07  Matthew Malcomson  <matthew.malcomson@arm.com>
> 
> 	* c-attribs.c (handle_no_sanitize_hwaddress_attribute): New
> 	attribute.
> 
> 
> 
> ###############     Attachment also inlined for ease of reply    ###############
> 
> 
> diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
> index 1c9f28587fbb2348cc30e302e889a5a22906901a..a5e68061ff956018957b6be137a7b2f2b7353647 100644
> --- a/gcc/c-family/c-attribs.c
> +++ b/gcc/c-family/c-attribs.c
> @@ -54,6 +54,8 @@ static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
>   static tree handle_no_sanitize_attribute (tree *, tree, tree, int, bool *);
>   static tree handle_no_sanitize_address_attribute (tree *, tree, tree,
>   						  int, bool *);
> +static tree handle_no_sanitize_hwaddress_attribute (tree *, tree, tree,
> +						    int, bool *);
>   static tree handle_no_sanitize_thread_attribute (tree *, tree, tree,
>   						 int, bool *);
>   static tree handle_no_address_safety_analysis_attribute (tree *, tree, tree,
> @@ -412,6 +414,8 @@ const struct attribute_spec c_common_attribute_table[] =
>   			      handle_no_sanitize_attribute, NULL },
>     { "no_sanitize_address",    0, 0, true, false, false, false,
>   			      handle_no_sanitize_address_attribute, NULL },
> +  { "no_sanitize_hwaddress",    0, 0, true, false, false, false,
> +			      handle_no_sanitize_hwaddress_attribute, NULL },
>     { "no_sanitize_thread",     0, 0, true, false, false, false,
>   			      handle_no_sanitize_thread_attribute, NULL },
>     { "no_sanitize_undefined",  0, 0, true, false, false, false,
> @@ -941,6 +945,22 @@ handle_no_sanitize_address_attribute (tree *node, tree name, tree, int,
>     return NULL_TREE;
>   }
>   
> +/* Handle a "no_sanitize_hwaddress" attribute; arguments as in
> +   struct attribute_spec.handler.  */
> +
> +static tree
> +handle_no_sanitize_hwaddress_attribute (tree *node, tree name, tree, int,
> +				      bool *no_add_attrs)
> +{
> +  *no_add_attrs = true;
> +  if (TREE_CODE (*node) != FUNCTION_DECL)
> +    warning (OPT_Wattributes, "%qE attribute ignored", name);
> +  else
> +    add_no_sanitize_value (*node, SANITIZE_HWADDRESS);
> +
> +  return NULL_TREE;
> +}
> +
>   /* Handle a "no_sanitize_thread" attribute; arguments as in
>      struct attribute_spec.handler.  */
>   
> diff --git a/gcc/common.opt b/gcc/common.opt
> index cc279f411d7967209a111d958437595ce5cacd3a..d44295530474f873a00bc97a7d46bdd26255db9c 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -215,7 +215,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
> @@ -3313,6 +3313,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 599d07a729e7438080f8b5240ee95037a49fb983..e0c7e2c6276d91bd0efe5e9bf9b58b09087c3cd7 100644
> --- a/gcc/config/aarch64/aarch64.c
> +++ b/gcc/config/aarch64/aarch64.c
> @@ -21371,6 +21371,15 @@ aarch64_stack_protect_guard (void)
>     return NULL_TREE;
>   }
>   
> +/* 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
> @@ -21936,6 +21945,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 95a3c29f7cee86336f958bef1d7fe56b82e05e6c..90b1fa91742c6a7d76aa6c7e931f8014fc4fff0c 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 60e5bedc3665a25fa51c2eca00547f12a9953778..e8d0bedfc2eb22d1e72e7e4875155202c8389a38 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 d1eb317f43cd31156c80ef7dd910024c50f33deb..f710e29549f5d3d58ed74aca74f2c62b577cd155 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -12594,13 +12594,33 @@ 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}.

We should also document that the option can't be combined with TSAN.

>   
>   @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 HardwareAddressSanitizer, a fast memory error detector.
> +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://github.com/google/sanitizers/} for

I would rather link to:
https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
?

> +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}.
> +
> +@item -fsanitize=kernel-hwaddress
> +@opindex fsanitize=kernel-hwaddress
> +Enable HardwareAddressSanitizer for Linux kernel.
> +Similar to @option{-fsanitize=kernel-address} but using the software tagging
> +method of 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 cd9aed9874f4e6b2b0e2f8956ed6155975e643a8..e842b734c9c20253986880ba3622a8f692d3ca88 100644
> --- a/gcc/doc/tm.texi
> +++ b/gcc/doc/tm.texi
> @@ -2978,6 +2978,10 @@ 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 the top byte of pointers.  This feature means that -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 2739e9ceec5ad7253ff9135da8dbe3bf6010e8d7..6136ac1a5fe0c0980d5b5123b67b102a1b1e0bcc 100644
> --- a/gcc/doc/tm.texi.in
> +++ b/gcc/doc/tm.texi.in
> @@ -2377,6 +2377,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 a2103282d469db31ad157a87572068d943061c8c..57d8ff9a1a010409d966230140df1017bc3584a8 100644
> --- a/gcc/flag-types.h
> +++ b/gcc/flag-types.h
> @@ -256,6 +256,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 c45a1df656b2b8122f457edeb55db5f44c57b5bf..037f9d3a688d8778b067aade698d40709ee97b3d 100644
> --- a/gcc/gcc.c
> +++ b/gcc/gcc.c
> @@ -708,6 +708,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)}"
> @@ -982,6 +1000,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
> @@ -991,6 +1010,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 "}\
> @@ -9439,8 +9460,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 10b9f108f8d0618501c1567bb4f8a7f9de2bdb98..b46c6d44164c04aa7e561fa3dc2513d66ce61e4d 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -1119,6 +1119,13 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
>   		  "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>");
>       }
>   
> +  /* Userspace and kernel HWasan conflict with each other.  */
> +  if ((opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS)
> +      && (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS))
> +    error_at (loc,
> +	      "%<-fsanitize=hwaddress%> is incompatible with "
> +	      "%<-fsanitize=kernel-hwaddress%>");
> +
>     /* Userspace and kernel ASan conflict with each other.  */
>     if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
>         && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
> @@ -1138,6 +1145,20 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
>       error_at (loc,
>   	      "%<-fsanitize=leak%> is incompatible with %<-fsanitize=thread%>");
>   
> +  /* HWASan and ASan conflict with each other.  */
> +  if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
> +      && (opts->x_flag_sanitize & SANITIZE_HWADDRESS))
> +    error_at (loc,
> +	      "%<-fsanitize=hwaddress%> is incompatible with both "
> +	      "%<-fsanitize=address%> and %<-fsanitize=kernel-address%>");
> +
> +  /* HWASan conflicts with TSan.  */
> +  if ((opts->x_flag_sanitize & SANITIZE_HWADDRESS)
> +      && (opts->x_flag_sanitize & SANITIZE_THREAD))
> +    error_at (loc,
> +	      "%<-fsanitize=hwaddress%> is incompatible with "
> +	      "%<-fsanitize=thread%>");
> +
>     /* Check error recovery for -fsanitize-recover option.  */
>     for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
>       if ((opts->x_flag_sanitize_recover & sanitizer_opts[i].flag)
> @@ -1157,7 +1178,8 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
>   
>     /* Enable -fsanitize-address-use-after-scope if address sanitizer is
>        enabled.  */
> -  if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
> +  if (((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
> +       || (opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS))
>         && !opts_set->x_flag_sanitize_address_use_after_scope)
>       opts->x_flag_sanitize_address_use_after_scope = true;
>   
> @@ -1786,8 +1808,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),
> @@ -2322,6 +2349,14 @@ common_handle_option (struct gcc_options *opts,
>   				 opts->x_param_values,
>   				 opts_set->x_param_values);
>   	}
> +      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
> +	{
> +	  maybe_set_param_value (PARAM_HWASAN_STACK, 0, opts->x_param_values,
> +				 opts_set->x_param_values);
> +	  maybe_set_param_value (PARAM_HWASAN_RANDOM_FRAME_TAG, 0,
> +				 opts->x_param_values,
> +				 opts_set->x_param_values);
> +	}

The following is replaced on trunk :) I think you will need the code as the parameter
default values are what you're setting here.

>         break;
>   
>       case OPT_fsanitize_recover_:
> diff --git a/gcc/params.def b/gcc/params.def
> index 942447d77e66a07736751ebf90bbce105b9dc267..33556f6fc18d8543a52a878cac4fbc1a2d85a73b 100644
> --- a/gcc/params.def
> +++ b/gcc/params.def
> @@ -1335,6 +1335,17 @@ DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
>   	 "smaller or equal to this number.",
>   	 256, 0, INT_MAX)
>   
> +/* HWAsan stands for HardwareAddressSanitizer: https://github.com/google/sanitizers.  */
> +DEFPARAM (PARAM_HWASAN_RANDOM_FRAME_TAG,
> +	  "hwasan-random-frame-tag",
> +	  "Use random base tag for each frame, as opposed to base always zero.",
> +	  1, 0, 1)
> +
> +DEFPARAM (PARAM_HWASAN_STACK,
> +	  "hwasan-stack",
> +	  "Enable hwasan stack protection.",
> +	  1, 0, 1)

As mentioned, I has to go into params.def file. One can set a default value with Init(xyz)
and param range with IntegerRange(from,to).

Martin

> +
>   DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
>   	  "uninit-control-dep-attempts",
>   	  "Maximum number of nested calls to search for control dependencies "
> diff --git a/gcc/params.h b/gcc/params.h
> index 1aaef6d6a00da79533eb0bba540dd8c731b1eaa8..102d79d42409590040ad9fe0beed0be73c8cc50a 100644
> --- a/gcc/params.h
> +++ b/gcc/params.h
> @@ -250,5 +250,9 @@ extern void init_param_values (int *params);
>     PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
>   #define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
>     ((unsigned) PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD))
> +#define HWASAN_STACK \
> +  PARAM_VALUE (PARAM_HWASAN_STACK)
> +#define HWASAN_RANDOM_FRAME_TAG \
> +  PARAM_VALUE (PARAM_HWASAN_RANDOM_FRAME_TAG)
>   
>   #endif /* ! GCC_PARAMS_H */
> diff --git a/gcc/target.def b/gcc/target.def
> index 8e83c2c7a7136511c07a5bc9e18876c91a38b955..ad16a151b6af9b1ee13918c8f2980280d75b1d90 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -6742,6 +6742,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 the top byte of\
> + pointers.  This feature means that -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 d4c3563e82587feea9523a15c2dcc490dad9919e..94e865259f35e46e26f6b4763c5e2f9dc9ed1b83 100644
> --- a/gcc/targhooks.h
> +++ b/gcc/targhooks.h
> @@ -282,4 +282,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 fee4cc271cd86a2206e18da2c2db24c5e04b0f4f..6e9e877c32e2c8705056bdf0ce2b1b6f125d93c3 100644
> --- a/gcc/targhooks.c
> +++ b/gcc/targhooks.c
> @@ -2365,4 +2365,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 00a5e83212601bef62ea9a254ddbeae35cde8e6e..ab67384249a3437ac37f42f741ed516884677f9f 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -1753,6 +1753,16 @@ 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,
> +		  "%<-fsanitize=hwaddress%> can not be implemented on "
> +		  "a backend that does not ignore the top byte of a pointer");
> +      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.  */
>

Patch
diff mbox series

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 1c9f28587fbb2348cc30e302e889a5a22906901a..a5e68061ff956018957b6be137a7b2f2b7353647 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -54,6 +54,8 @@  static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_sanitize_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_sanitize_address_attribute (tree *, tree, tree,
 						  int, bool *);
+static tree handle_no_sanitize_hwaddress_attribute (tree *, tree, tree,
+						    int, bool *);
 static tree handle_no_sanitize_thread_attribute (tree *, tree, tree,
 						 int, bool *);
 static tree handle_no_address_safety_analysis_attribute (tree *, tree, tree,
@@ -412,6 +414,8 @@  const struct attribute_spec c_common_attribute_table[] =
 			      handle_no_sanitize_attribute, NULL },
   { "no_sanitize_address",    0, 0, true, false, false, false,
 			      handle_no_sanitize_address_attribute, NULL },
+  { "no_sanitize_hwaddress",    0, 0, true, false, false, false,
+			      handle_no_sanitize_hwaddress_attribute, NULL },
   { "no_sanitize_thread",     0, 0, true, false, false, false,
 			      handle_no_sanitize_thread_attribute, NULL },
   { "no_sanitize_undefined",  0, 0, true, false, false, false,
@@ -941,6 +945,22 @@  handle_no_sanitize_address_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+/* Handle a "no_sanitize_hwaddress" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_no_sanitize_hwaddress_attribute (tree *node, tree name, tree, int,
+				      bool *no_add_attrs)
+{
+  *no_add_attrs = true;
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
+  else
+    add_no_sanitize_value (*node, SANITIZE_HWADDRESS);
+
+  return NULL_TREE;
+}
+
 /* Handle a "no_sanitize_thread" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/common.opt b/gcc/common.opt
index cc279f411d7967209a111d958437595ce5cacd3a..d44295530474f873a00bc97a7d46bdd26255db9c 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -215,7 +215,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
@@ -3313,6 +3313,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 599d07a729e7438080f8b5240ee95037a49fb983..e0c7e2c6276d91bd0efe5e9bf9b58b09087c3cd7 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -21371,6 +21371,15 @@  aarch64_stack_protect_guard (void)
   return NULL_TREE;
 }
 
+/* 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
@@ -21936,6 +21945,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 95a3c29f7cee86336f958bef1d7fe56b82e05e6c..90b1fa91742c6a7d76aa6c7e931f8014fc4fff0c 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 60e5bedc3665a25fa51c2eca00547f12a9953778..e8d0bedfc2eb22d1e72e7e4875155202c8389a38 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 d1eb317f43cd31156c80ef7dd910024c50f33deb..f710e29549f5d3d58ed74aca74f2c62b577cd155 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -12594,13 +12594,33 @@  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}.
 
 @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 HardwareAddressSanitizer, a fast memory error detector.
+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://github.com/google/sanitizers/} 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}.
+
+@item -fsanitize=kernel-hwaddress
+@opindex fsanitize=kernel-hwaddress
+Enable HardwareAddressSanitizer for Linux kernel.
+Similar to @option{-fsanitize=kernel-address} but using the software tagging
+method of 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 cd9aed9874f4e6b2b0e2f8956ed6155975e643a8..e842b734c9c20253986880ba3622a8f692d3ca88 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2978,6 +2978,10 @@  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 the top byte of pointers.  This feature means that -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 2739e9ceec5ad7253ff9135da8dbe3bf6010e8d7..6136ac1a5fe0c0980d5b5123b67b102a1b1e0bcc 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2377,6 +2377,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 a2103282d469db31ad157a87572068d943061c8c..57d8ff9a1a010409d966230140df1017bc3584a8 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -256,6 +256,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 c45a1df656b2b8122f457edeb55db5f44c57b5bf..037f9d3a688d8778b067aade698d40709ee97b3d 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -708,6 +708,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)}"
@@ -982,6 +1000,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
@@ -991,6 +1010,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 "}\
@@ -9439,8 +9460,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 10b9f108f8d0618501c1567bb4f8a7f9de2bdb98..b46c6d44164c04aa7e561fa3dc2513d66ce61e4d 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1119,6 +1119,13 @@  finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 		  "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>");
     }
 
+  /* Userspace and kernel HWasan conflict with each other.  */
+  if ((opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS))
+    error_at (loc,
+	      "%<-fsanitize=hwaddress%> is incompatible with "
+	      "%<-fsanitize=kernel-hwaddress%>");
+
   /* Userspace and kernel ASan conflict with each other.  */
   if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
       && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
@@ -1138,6 +1145,20 @@  finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
     error_at (loc,
 	      "%<-fsanitize=leak%> is incompatible with %<-fsanitize=thread%>");
 
+  /* HWASan and ASan conflict with each other.  */
+  if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_HWADDRESS))
+    error_at (loc,
+	      "%<-fsanitize=hwaddress%> is incompatible with both "
+	      "%<-fsanitize=address%> and %<-fsanitize=kernel-address%>");
+
+  /* HWASan conflicts with TSan.  */
+  if ((opts->x_flag_sanitize & SANITIZE_HWADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_THREAD))
+    error_at (loc,
+	      "%<-fsanitize=hwaddress%> is incompatible with "
+	      "%<-fsanitize=thread%>");
+
   /* Check error recovery for -fsanitize-recover option.  */
   for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
     if ((opts->x_flag_sanitize_recover & sanitizer_opts[i].flag)
@@ -1157,7 +1178,8 @@  finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 
   /* Enable -fsanitize-address-use-after-scope if address sanitizer is
      enabled.  */
-  if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+  if (((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+       || (opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS))
       && !opts_set->x_flag_sanitize_address_use_after_scope)
     opts->x_flag_sanitize_address_use_after_scope = true;
 
@@ -1786,8 +1808,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),
@@ -2322,6 +2349,14 @@  common_handle_option (struct gcc_options *opts,
 				 opts->x_param_values,
 				 opts_set->x_param_values);
 	}
+      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+	{
+	  maybe_set_param_value (PARAM_HWASAN_STACK, 0, opts->x_param_values,
+				 opts_set->x_param_values);
+	  maybe_set_param_value (PARAM_HWASAN_RANDOM_FRAME_TAG, 0,
+				 opts->x_param_values,
+				 opts_set->x_param_values);
+	}
       break;
 
     case OPT_fsanitize_recover_:
diff --git a/gcc/params.def b/gcc/params.def
index 942447d77e66a07736751ebf90bbce105b9dc267..33556f6fc18d8543a52a878cac4fbc1a2d85a73b 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1335,6 +1335,17 @@  DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
 	 "smaller or equal to this number.",
 	 256, 0, INT_MAX)
 
+/* HWAsan stands for HardwareAddressSanitizer: https://github.com/google/sanitizers.  */
+DEFPARAM (PARAM_HWASAN_RANDOM_FRAME_TAG,
+	  "hwasan-random-frame-tag",
+	  "Use random base tag for each frame, as opposed to base always zero.",
+	  1, 0, 1)
+
+DEFPARAM (PARAM_HWASAN_STACK,
+	  "hwasan-stack",
+	  "Enable hwasan stack protection.",
+	  1, 0, 1)
+
 DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
 	  "uninit-control-dep-attempts",
 	  "Maximum number of nested calls to search for control dependencies "
diff --git a/gcc/params.h b/gcc/params.h
index 1aaef6d6a00da79533eb0bba540dd8c731b1eaa8..102d79d42409590040ad9fe0beed0be73c8cc50a 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -250,5 +250,9 @@  extern void init_param_values (int *params);
   PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
 #define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
   ((unsigned) PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD))
+#define HWASAN_STACK \
+  PARAM_VALUE (PARAM_HWASAN_STACK)
+#define HWASAN_RANDOM_FRAME_TAG \
+  PARAM_VALUE (PARAM_HWASAN_RANDOM_FRAME_TAG)
 
 #endif /* ! GCC_PARAMS_H */
diff --git a/gcc/target.def b/gcc/target.def
index 8e83c2c7a7136511c07a5bc9e18876c91a38b955..ad16a151b6af9b1ee13918c8f2980280d75b1d90 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6742,6 +6742,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 the top byte of\
+ pointers.  This feature means that -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 d4c3563e82587feea9523a15c2dcc490dad9919e..94e865259f35e46e26f6b4763c5e2f9dc9ed1b83 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -282,4 +282,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 fee4cc271cd86a2206e18da2c2db24c5e04b0f4f..6e9e877c32e2c8705056bdf0ce2b1b6f125d93c3 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2365,4 +2365,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 00a5e83212601bef62ea9a254ddbeae35cde8e6e..ab67384249a3437ac37f42f741ed516884677f9f 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1753,6 +1753,16 @@  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,
+		  "%<-fsanitize=hwaddress%> can not be implemented on "
+		  "a backend that does not ignore the top byte of a pointer");
+      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.  */