diff mbox series

[13/X,libsanitizer,options] Add hwasan flags and argument parsing

Message ID HE1PR0802MB2251783050BA897E608882ACE07E0@HE1PR0802MB2251.eurprd08.prod.outlook.com
State New
Headers show
Series [13/X,libsanitizer,options] Add hwasan flags and argument parsing | expand

Commit Message

Matthew Malcomson Nov. 5, 2019, 11:34 a.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.

NOTE:
------
I have defined a new macro of __SANITIZE_HWADDRESS__ that gets
automatically defined when compiling with hwasan.  This is analogous to
__SANITIZE_ADDRESS__ which is defined when compiling with asan.

Users in the kernel have expressed an interest in using
__SANITIZE_ADDRESS__ for both
(https://lists.infradead.org/pipermail/linux-arm-kernel/2019-October/690703.html).

One approach to do this could be to define __SANITIZE_ADDRESS__ with
different values depending on whether we are compiling with hwasan or
asan.

Using __SANITIZE_ADDRESS__ for both means that code like the kernel
which wants to treat the two sanitizers as alternate implementations of
the same thing gets that automatically.

My preference is to use __SANITIZE_HWADDRESS__ since that means any
existing code will not be predicated on this (and hence I guess less
surprises), but would appreciate feedback on this given the point above.
------

gcc/ChangeLog:

2019-11-05  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-05  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 6500b998321419a1d8d57062534206c5909adb7a..2de94815f91da5a0fd06c30d0044f866084121b8 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,
@@ -410,6 +412,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,
@@ -929,6 +933,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 1b9e0f3c8025a3b439f766edcd81db462973037b..d8ba9556801e5afc479c33ba359125d6354ca862 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
@@ -3289,6 +3289,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 232317d4a5a4a16529f573eef5a8d7a068068207..c556bcd1c37c3c4fdd9a829a28ee4ff56819b89e 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -20272,6 +20272,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
@@ -20839,6 +20848,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_GET_MULTILIB_ABI_NAME
 #define TARGET_GET_MULTILIB_ABI_NAME aarch64_get_multilib_abi_name
 
+#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/tm.texi b/gcc/doc/tm.texi
index 0250cf58e72b4df8fec19cfb4399ed0e2594342b..bf53df715391128d6fbe9be4e77906650309ab2e 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2972,6 +2972,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 0b77dd8eb46dc53fc585d7b3eac9805c6ed79951..005cef05999d7c334f16ffa368903c3b66806231 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2374,6 +2374,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 1216cdd505a18152dc1d3eee5f37755a396761f1..cf1bd9de660f32f060b9277f89a562873a48684a 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 "}\
@@ -9434,8 +9455,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 efd75aade6c879f330db1aa7b8ef6b9100862c04..88a94286e71f61f2dce907018e5185f63a830804 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1160,6 +1160,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))
@@ -1179,6 +1186,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)
@@ -1198,7 +1219,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;
 
@@ -1827,8 +1849,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),
@@ -2363,6 +2390,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 5fe33976b37bb0763986040f66a9c28681363535..a4b3f02b60898f54aeec40238ad417e423f56e01 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1299,6 +1299,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 26f1236aa65422f66939ef2a4c38958bdc984aee..ad40bd0b5d3b217e6d0dc531fce04faba97b5f60 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -252,5 +252,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 01609136848fc157a47a93a0267c03524fe9383e..0ade31accab25bf121f135cbf02c6adfcd6e1476 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6706,6 +6706,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 5aba67660f85406b9fd475e75a3cc65b0d1952f5..463c27c7d7b550bf63630f2102681b37ffd265cb 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -284,4 +284,5 @@ extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 extern void default_remove_extra_call_preserved_regs (rtx_insn *,
 						      HARD_REG_SET *);
 
+extern bool default_memtag_can_tag_addresses ();
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index ed77afb1da57e59bc0725dc0d6fac477391bae03..d7dd07db65c8248c2f170466db21449a56713d69 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2368,4 +2368,10 @@ default_remove_extra_call_preserved_regs (rtx_insn *, HARD_REG_SET *)
 {
 }
 
+bool
+default_memtag_can_tag_addresses ()
+{
+  return false;
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index d741a66f3857a60bcdb6f5c1b60e781ff311aad4..3920ef5c40f27b27a449dc6bf1da795f0d40e77b 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1752,6 +1752,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

Li, Pan2 via Gcc-patches Nov. 5, 2019, 1:11 p.m. UTC | #1
On Tue, Nov 5, 2019 at 12:34 PM Matthew Malcomson
<Matthew.Malcomson@arm.com> 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.
>
> NOTE:
> ------
> I have defined a new macro of __SANITIZE_HWADDRESS__ that gets
> automatically defined when compiling with hwasan.  This is analogous to
> __SANITIZE_ADDRESS__ which is defined when compiling with asan.
>
> Users in the kernel have expressed an interest in using
> __SANITIZE_ADDRESS__ for both
> (https://lists.infradead.org/pipermail/linux-arm-kernel/2019-October/690703.html).
>
> One approach to do this could be to define __SANITIZE_ADDRESS__ with
> different values depending on whether we are compiling with hwasan or
> asan.
>
> Using __SANITIZE_ADDRESS__ for both means that code like the kernel
> which wants to treat the two sanitizers as alternate implementations of
> the same thing gets that automatically.
>
> My preference is to use __SANITIZE_HWADDRESS__ since that means any
> existing code will not be predicated on this (and hence I guess less
> surprises), but would appreciate feedback on this given the point above.

+Evgenii Stepanov

(A repost from my answer from the mentioned thread):

> Similarly, I'm thinking I'll add no_sanitize_hwaddress as the hwasan
> equivalent of no_sanitize_address, which will require an update in the
> kernel given it seems you want KASAN to be used the same whether using
> tags or not.

We have intentionally reused the same macros to simplify things. Is
there any reason to use separate macros for GCC? Are there places
where we need to use specifically no_sanitize_hwaddress and
__SANITIZE_HWADDRESS__, but not no_sanitize_address and
__SANITIZE_ADDRESS__?


> ------
>
> gcc/ChangeLog:
>
> 2019-11-05  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-05  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 6500b998321419a1d8d57062534206c5909adb7a..2de94815f91da5a0fd06c30d0044f866084121b8 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,
> @@ -410,6 +412,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,
> @@ -929,6 +933,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 1b9e0f3c8025a3b439f766edcd81db462973037b..d8ba9556801e5afc479c33ba359125d6354ca862 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
> @@ -3289,6 +3289,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 232317d4a5a4a16529f573eef5a8d7a068068207..c556bcd1c37c3c4fdd9a829a28ee4ff56819b89e 100644
> --- a/gcc/config/aarch64/aarch64.c
> +++ b/gcc/config/aarch64/aarch64.c
> @@ -20272,6 +20272,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
> @@ -20839,6 +20848,9 @@ aarch64_libgcc_floating_mode_supported_p
>  #undef TARGET_GET_MULTILIB_ABI_NAME
>  #define TARGET_GET_MULTILIB_ABI_NAME aarch64_get_multilib_abi_name
>
> +#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/tm.texi b/gcc/doc/tm.texi
> index 0250cf58e72b4df8fec19cfb4399ed0e2594342b..bf53df715391128d6fbe9be4e77906650309ab2e 100644
> --- a/gcc/doc/tm.texi
> +++ b/gcc/doc/tm.texi
> @@ -2972,6 +2972,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 0b77dd8eb46dc53fc585d7b3eac9805c6ed79951..005cef05999d7c334f16ffa368903c3b66806231 100644
> --- a/gcc/doc/tm.texi.in
> +++ b/gcc/doc/tm.texi.in
> @@ -2374,6 +2374,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 1216cdd505a18152dc1d3eee5f37755a396761f1..cf1bd9de660f32f060b9277f89a562873a48684a 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 "}\
> @@ -9434,8 +9455,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 efd75aade6c879f330db1aa7b8ef6b9100862c04..88a94286e71f61f2dce907018e5185f63a830804 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -1160,6 +1160,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))
> @@ -1179,6 +1186,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)
> @@ -1198,7 +1219,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;
>
> @@ -1827,8 +1849,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),
> @@ -2363,6 +2390,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 5fe33976b37bb0763986040f66a9c28681363535..a4b3f02b60898f54aeec40238ad417e423f56e01 100644
> --- a/gcc/params.def
> +++ b/gcc/params.def
> @@ -1299,6 +1299,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 26f1236aa65422f66939ef2a4c38958bdc984aee..ad40bd0b5d3b217e6d0dc531fce04faba97b5f60 100644
> --- a/gcc/params.h
> +++ b/gcc/params.h
> @@ -252,5 +252,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 01609136848fc157a47a93a0267c03524fe9383e..0ade31accab25bf121f135cbf02c6adfcd6e1476 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -6706,6 +6706,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 5aba67660f85406b9fd475e75a3cc65b0d1952f5..463c27c7d7b550bf63630f2102681b37ffd265cb 100644
> --- a/gcc/targhooks.h
> +++ b/gcc/targhooks.h
> @@ -284,4 +284,5 @@ extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
>  extern void default_remove_extra_call_preserved_regs (rtx_insn *,
>                                                       HARD_REG_SET *);
>
> +extern bool default_memtag_can_tag_addresses ();
>  #endif /* GCC_TARGHOOKS_H */
> diff --git a/gcc/targhooks.c b/gcc/targhooks.c
> index ed77afb1da57e59bc0725dc0d6fac477391bae03..d7dd07db65c8248c2f170466db21449a56713d69 100644
> --- a/gcc/targhooks.c
> +++ b/gcc/targhooks.c
> @@ -2368,4 +2368,10 @@ default_remove_extra_call_preserved_regs (rtx_insn *, HARD_REG_SET *)
>  {
>  }
>
> +bool
> +default_memtag_can_tag_addresses ()
> +{
> +  return false;
> +}
> +
>  #include "gt-targhooks.h"
> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index d741a66f3857a60bcdb6f5c1b60e781ff311aad4..3920ef5c40f27b27a449dc6bf1da795f0d40e77b 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -1752,6 +1752,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.  */
>
Joseph Myers Nov. 5, 2019, 6:31 p.m. UTC | #2
On Tue, 5 Nov 2019, Matthew Malcomson wrote:

> +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)

@option{-fsanitize=hwaddress} (and then regenerate tm.texi).
Matthew Malcomson Nov. 7, 2019, 12:47 p.m. UTC | #3
On 05/11/2019 13:11, Andrey Konovalov wrote:
> On Tue, Nov 5, 2019 at 12:34 PM Matthew Malcomson
> <Matthew.Malcomson@arm.com> wrote:
>>
>> NOTE:
>> ------
>> I have defined a new macro of __SANITIZE_HWADDRESS__ that gets
>> automatically defined when compiling with hwasan.  This is analogous to
>> __SANITIZE_ADDRESS__ which is defined when compiling with asan.
>>
>> Users in the kernel have expressed an interest in using
>> __SANITIZE_ADDRESS__ for both
>> (https://lists.infradead.org/pipermail/linux-arm-kernel/2019-October/690703.html).
>>
>> One approach to do this could be to define __SANITIZE_ADDRESS__ with
>> different values depending on whether we are compiling with hwasan or
>> asan.
>>
>> Using __SANITIZE_ADDRESS__ for both means that code like the kernel
>> which wants to treat the two sanitizers as alternate implementations of
>> the same thing gets that automatically.
>>
>> My preference is to use __SANITIZE_HWADDRESS__ since that means any
>> existing code will not be predicated on this (and hence I guess less
>> surprises), but would appreciate feedback on this given the point above.
> 
> +Evgenii Stepanov
> 
> (A repost from my answer from the mentioned thread):
> 
>> Similarly, I'm thinking I'll add no_sanitize_hwaddress as the hwasan
>> equivalent of no_sanitize_address, which will require an update in the
>> kernel given it seems you want KASAN to be used the same whether using
>> tags or not.
> 
> We have intentionally reused the same macros to simplify things. Is
> there any reason to use separate macros for GCC? Are there places
> where we need to use specifically no_sanitize_hwaddress and
> __SANITIZE_HWADDRESS__, but not no_sanitize_address and
> __SANITIZE_ADDRESS__?
> 
> 

I've just looked through some open source repositories (via github 
search) that used the existing __SANITIZE_ADDRESS__ macro.

There are a few repos that would want to use a feature macro for hwasan 
or asan in the exact same way as each other, but of the 31 truly 
different uses I found, 11 look like they would need to distinguish 
between hwasan and asan (where 4 uses I found I couldn't easily tell)

NOTE
- This is a count of unique uses, ignoring those repos which use a file 
from another repo.
- I'm just giving links to the first of the relevant kind that I found, 
not putting effort into finding the "canonical" source of each repository.


Places that need distinction (and their reasons):

There are quite a few that use the ASAN_POISON_MEMORY_REGION and 
ASAN_UNPOISON_MEMORY_REGION macros to poison/unpoison memory themselves. 
  This abstraction doesn't quite make sense in a hwasan environment, as 
there is not really a "poisoned/unpoisoned" concept.

https://github.com/laurynas-biveinis/unodb
https://github.com/darktable-org/rawspeed
https://github.com/MariaDB/server
https://github.com/ralfbrown/framepac-ng
https://github.com/peters/aom
https://github.com/pspacek/knot-resolver-docker-fix
https://github.com/harikrishnan94/sheap


Some use it to record their compilation "type" as `-fsanitize=address`
https://github.com/wallix/redemption

Or to decide to set the environment variable ASAN_OPTIONS
https://github.com/dephonatine/VBox5.2.18

Others worry about stack space due to asan's redzones (hwasan has a much 
smaller stack memory overhead).
https://github.com/fastbuild/fastbuild
https://github.com/scylladb/seastar
(n.b. seastar has a lot more conditioned code that would be the same 
between asan and hwasan).


Each of these needs to know the difference between compiling with asan 
and hwasan, so I'm confident that having some way to determine that in 
the source code is a good idea.


I also believe there could be code in the wild that would need to 
distinguish between hwasan and asan where the existence of tags could be 
problematic:

- code already using the top-byte-ignore feature may be able to be used 
with asan but not hwasan.
- Code that makes assumptions about pointer ordering (e.g. the autoconf 
program that looks for stack growth direction) could break on hwasan but 
not on asan.
- Code looking for the distance between two objects in memory would need 
to account for tags in pointers.


Hence I think this distinction is needed.

Matthew
Li, Pan2 via Gcc-patches Nov. 7, 2019, 3:51 p.m. UTC | #4
On Thu, Nov 7, 2019 at 1:48 PM Matthew Malcomson
<Matthew.Malcomson@arm.com> wrote:
>
> On 05/11/2019 13:11, Andrey Konovalov wrote:
> > On Tue, Nov 5, 2019 at 12:34 PM Matthew Malcomson
> > <Matthew.Malcomson@arm.com> wrote:
> >>
> >> NOTE:
> >> ------
> >> I have defined a new macro of __SANITIZE_HWADDRESS__ that gets
> >> automatically defined when compiling with hwasan.  This is analogous to
> >> __SANITIZE_ADDRESS__ which is defined when compiling with asan.
> >>
> >> Users in the kernel have expressed an interest in using
> >> __SANITIZE_ADDRESS__ for both
> >> (https://lists.infradead.org/pipermail/linux-arm-kernel/2019-October/690703.html).
> >>
> >> One approach to do this could be to define __SANITIZE_ADDRESS__ with
> >> different values depending on whether we are compiling with hwasan or
> >> asan.
> >>
> >> Using __SANITIZE_ADDRESS__ for both means that code like the kernel
> >> which wants to treat the two sanitizers as alternate implementations of
> >> the same thing gets that automatically.
> >>
> >> My preference is to use __SANITIZE_HWADDRESS__ since that means any
> >> existing code will not be predicated on this (and hence I guess less
> >> surprises), but would appreciate feedback on this given the point above.
> >
> > +Evgenii Stepanov
> >
> > (A repost from my answer from the mentioned thread):
> >
> >> Similarly, I'm thinking I'll add no_sanitize_hwaddress as the hwasan
> >> equivalent of no_sanitize_address, which will require an update in the
> >> kernel given it seems you want KASAN to be used the same whether using
> >> tags or not.
> >
> > We have intentionally reused the same macros to simplify things. Is
> > there any reason to use separate macros for GCC? Are there places
> > where we need to use specifically no_sanitize_hwaddress and
> > __SANITIZE_HWADDRESS__, but not no_sanitize_address and
> > __SANITIZE_ADDRESS__?
> >
> >
>
> I've just looked through some open source repositories (via github
> search) that used the existing __SANITIZE_ADDRESS__ macro.
>
> There are a few repos that would want to use a feature macro for hwasan
> or asan in the exact same way as each other, but of the 31 truly
> different uses I found, 11 look like they would need to distinguish
> between hwasan and asan (where 4 uses I found I couldn't easily tell)
>
> NOTE
> - This is a count of unique uses, ignoring those repos which use a file
> from another repo.
> - I'm just giving links to the first of the relevant kind that I found,
> not putting effort into finding the "canonical" source of each repository.
>
>
> Places that need distinction (and their reasons):
>
> There are quite a few that use the ASAN_POISON_MEMORY_REGION and
> ASAN_UNPOISON_MEMORY_REGION macros to poison/unpoison memory themselves.
>   This abstraction doesn't quite make sense in a hwasan environment, as
> there is not really a "poisoned/unpoisoned" concept.
>
> https://github.com/laurynas-biveinis/unodb
> https://github.com/darktable-org/rawspeed
> https://github.com/MariaDB/server
> https://github.com/ralfbrown/framepac-ng
> https://github.com/peters/aom
> https://github.com/pspacek/knot-resolver-docker-fix
> https://github.com/harikrishnan94/sheap
>
>
> Some use it to record their compilation "type" as `-fsanitize=address`
> https://github.com/wallix/redemption
>
> Or to decide to set the environment variable ASAN_OPTIONS
> https://github.com/dephonatine/VBox5.2.18
>
> Others worry about stack space due to asan's redzones (hwasan has a much
> smaller stack memory overhead).
> https://github.com/fastbuild/fastbuild
> https://github.com/scylladb/seastar
> (n.b. seastar has a lot more conditioned code that would be the same
> between asan and hwasan).
>
>
> Each of these needs to know the difference between compiling with asan
> and hwasan, so I'm confident that having some way to determine that in
> the source code is a good idea.
>
>
> I also believe there could be code in the wild that would need to
> distinguish between hwasan and asan where the existence of tags could be
> problematic:
>
> - code already using the top-byte-ignore feature may be able to be used
> with asan but not hwasan.
> - Code that makes assumptions about pointer ordering (e.g. the autoconf
> program that looks for stack growth direction) could break on hwasan but
> not on asan.
> - Code looking for the distance between two objects in memory would need
> to account for tags in pointers.
>
>
> Hence I think this distinction is needed.

Evgenii, how does clang-compiled code dististinguishes whether it's
being compiled with ASAN or HWASAN?
Li, Pan2 via Gcc-patches Nov. 7, 2019, 6:01 p.m. UTC | #5
Clang has a function level attribute,
  __attribute__((no_sanitize("hwaddress")))
a feature macro
  #if __has_feature(hwaddress_sanitizer)
and a blacklist section
  [hwaddress]
  https://clang.llvm.org/docs/SanitizerSpecialCaseList.html

I think it makes sense for the compiler to err on the side of not losing
information and provide distinct macros for these two sanitizers. If the
kernel does not care about the difference, they can add a simple #ifdef.
They would need to, anyway, because gcc does not have feature macros and
clang does not define __SANITIZE_ADDRESS__.


On Thu, Nov 7, 2019 at 7:51 AM Andrey Konovalov <andreyknvl@google.com>
wrote:

> On Thu, Nov 7, 2019 at 1:48 PM Matthew Malcomson
> <Matthew.Malcomson@arm.com> wrote:
> >
> > On 05/11/2019 13:11, Andrey Konovalov wrote:
> > > On Tue, Nov 5, 2019 at 12:34 PM Matthew Malcomson
> > > <Matthew.Malcomson@arm.com> wrote:
> > >>
> > >> NOTE:
> > >> ------
> > >> I have defined a new macro of __SANITIZE_HWADDRESS__ that gets
> > >> automatically defined when compiling with hwasan.  This is analogous
> to
> > >> __SANITIZE_ADDRESS__ which is defined when compiling with asan.
> > >>
> > >> Users in the kernel have expressed an interest in using
> > >> __SANITIZE_ADDRESS__ for both
> > >> (
> https://lists.infradead.org/pipermail/linux-arm-kernel/2019-October/690703.html
> ).
> > >>
> > >> One approach to do this could be to define __SANITIZE_ADDRESS__ with
> > >> different values depending on whether we are compiling with hwasan or
> > >> asan.
> > >>
> > >> Using __SANITIZE_ADDRESS__ for both means that code like the kernel
> > >> which wants to treat the two sanitizers as alternate implementations
> of
> > >> the same thing gets that automatically.
> > >>
> > >> My preference is to use __SANITIZE_HWADDRESS__ since that means any
> > >> existing code will not be predicated on this (and hence I guess less
> > >> surprises), but would appreciate feedback on this given the point
> above.
> > >
> > > +Evgenii Stepanov
> > >
> > > (A repost from my answer from the mentioned thread):
> > >
> > >> Similarly, I'm thinking I'll add no_sanitize_hwaddress as the hwasan
> > >> equivalent of no_sanitize_address, which will require an update in the
> > >> kernel given it seems you want KASAN to be used the same whether using
> > >> tags or not.
> > >
> > > We have intentionally reused the same macros to simplify things. Is
> > > there any reason to use separate macros for GCC? Are there places
> > > where we need to use specifically no_sanitize_hwaddress and
> > > __SANITIZE_HWADDRESS__, but not no_sanitize_address and
> > > __SANITIZE_ADDRESS__?
> > >
> > >
> >
> > I've just looked through some open source repositories (via github
> > search) that used the existing __SANITIZE_ADDRESS__ macro.
> >
> > There are a few repos that would want to use a feature macro for hwasan
> > or asan in the exact same way as each other, but of the 31 truly
> > different uses I found, 11 look like they would need to distinguish
> > between hwasan and asan (where 4 uses I found I couldn't easily tell)
> >
> > NOTE
> > - This is a count of unique uses, ignoring those repos which use a file
> > from another repo.
> > - I'm just giving links to the first of the relevant kind that I found,
> > not putting effort into finding the "canonical" source of each
> repository.
> >
> >
> > Places that need distinction (and their reasons):
> >
> > There are quite a few that use the ASAN_POISON_MEMORY_REGION and
> > ASAN_UNPOISON_MEMORY_REGION macros to poison/unpoison memory themselves.
> >   This abstraction doesn't quite make sense in a hwasan environment, as
> > there is not really a "poisoned/unpoisoned" concept.
> >
> > https://github.com/laurynas-biveinis/unodb
> > https://github.com/darktable-org/rawspeed
> > https://github.com/MariaDB/server
> > https://github.com/ralfbrown/framepac-ng
> > https://github.com/peters/aom
> > https://github.com/pspacek/knot-resolver-docker-fix
> > https://github.com/harikrishnan94/sheap
> >
> >
> > Some use it to record their compilation "type" as `-fsanitize=address`
> > https://github.com/wallix/redemption
> >
> > Or to decide to set the environment variable ASAN_OPTIONS
> > https://github.com/dephonatine/VBox5.2.18
> >
> > Others worry about stack space due to asan's redzones (hwasan has a much
> > smaller stack memory overhead).
> > https://github.com/fastbuild/fastbuild
> > https://github.com/scylladb/seastar
> > (n.b. seastar has a lot more conditioned code that would be the same
> > between asan and hwasan).
> >
> >
> > Each of these needs to know the difference between compiling with asan
> > and hwasan, so I'm confident that having some way to determine that in
> > the source code is a good idea.
> >
> >
> > I also believe there could be code in the wild that would need to
> > distinguish between hwasan and asan where the existence of tags could be
> > problematic:
> >
> > - code already using the top-byte-ignore feature may be able to be used
> > with asan but not hwasan.
> > - Code that makes assumptions about pointer ordering (e.g. the autoconf
> > program that looks for stack growth direction) could break on hwasan but
> > not on asan.
> > - Code looking for the distance between two objects in memory would need
> > to account for tags in pointers.
> >
> >
> > Hence I think this distinction is needed.
>
> Evgenii, how does clang-compiled code dististinguishes whether it's
> being compiled with ASAN or HWASAN?
>
Li, Pan2 via Gcc-patches Nov. 8, 2019, 1:55 p.m. UTC | #6
OK, let's keep the macros district then. In the kernel it doesn't give
you a lot, since you actually know which ASAN you're using based on
the kernel CONFIG_ values, but looks like it's important for
userspace. Thanks!

On Thu, Nov 7, 2019 at 7:01 PM Evgenii Stepanov <eugenis@google.com> wrote:
>
> Clang has a function level attribute,
>   __attribute__((no_sanitize("hwaddress")))
> a feature macro
>   #if __has_feature(hwaddress_sanitizer)
> and a blacklist section
>   [hwaddress]
>   https://clang.llvm.org/docs/SanitizerSpecialCaseList.html
>
> I think it makes sense for the compiler to err on the side of not losing information and provide distinct macros for these two sanitizers. If the kernel does not care about the difference, they can add a simple #ifdef. They would need to, anyway, because gcc does not have feature macros and clang does not define __SANITIZE_ADDRESS__.
>
>
> On Thu, Nov 7, 2019 at 7:51 AM Andrey Konovalov <andreyknvl@google.com> wrote:
>>
>> On Thu, Nov 7, 2019 at 1:48 PM Matthew Malcomson
>> <Matthew.Malcomson@arm.com> wrote:
>> >
>> > On 05/11/2019 13:11, Andrey Konovalov wrote:
>> > > On Tue, Nov 5, 2019 at 12:34 PM Matthew Malcomson
>> > > <Matthew.Malcomson@arm.com> wrote:
>> > >>
>> > >> NOTE:
>> > >> ------
>> > >> I have defined a new macro of __SANITIZE_HWADDRESS__ that gets
>> > >> automatically defined when compiling with hwasan.  This is analogous to
>> > >> __SANITIZE_ADDRESS__ which is defined when compiling with asan.
>> > >>
>> > >> Users in the kernel have expressed an interest in using
>> > >> __SANITIZE_ADDRESS__ for both
>> > >> (https://lists.infradead.org/pipermail/linux-arm-kernel/2019-October/690703.html).
>> > >>
>> > >> One approach to do this could be to define __SANITIZE_ADDRESS__ with
>> > >> different values depending on whether we are compiling with hwasan or
>> > >> asan.
>> > >>
>> > >> Using __SANITIZE_ADDRESS__ for both means that code like the kernel
>> > >> which wants to treat the two sanitizers as alternate implementations of
>> > >> the same thing gets that automatically.
>> > >>
>> > >> My preference is to use __SANITIZE_HWADDRESS__ since that means any
>> > >> existing code will not be predicated on this (and hence I guess less
>> > >> surprises), but would appreciate feedback on this given the point above.
>> > >
>> > > +Evgenii Stepanov
>> > >
>> > > (A repost from my answer from the mentioned thread):
>> > >
>> > >> Similarly, I'm thinking I'll add no_sanitize_hwaddress as the hwasan
>> > >> equivalent of no_sanitize_address, which will require an update in the
>> > >> kernel given it seems you want KASAN to be used the same whether using
>> > >> tags or not.
>> > >
>> > > We have intentionally reused the same macros to simplify things. Is
>> > > there any reason to use separate macros for GCC? Are there places
>> > > where we need to use specifically no_sanitize_hwaddress and
>> > > __SANITIZE_HWADDRESS__, but not no_sanitize_address and
>> > > __SANITIZE_ADDRESS__?
>> > >
>> > >
>> >
>> > I've just looked through some open source repositories (via github
>> > search) that used the existing __SANITIZE_ADDRESS__ macro.
>> >
>> > There are a few repos that would want to use a feature macro for hwasan
>> > or asan in the exact same way as each other, but of the 31 truly
>> > different uses I found, 11 look like they would need to distinguish
>> > between hwasan and asan (where 4 uses I found I couldn't easily tell)
>> >
>> > NOTE
>> > - This is a count of unique uses, ignoring those repos which use a file
>> > from another repo.
>> > - I'm just giving links to the first of the relevant kind that I found,
>> > not putting effort into finding the "canonical" source of each repository.
>> >
>> >
>> > Places that need distinction (and their reasons):
>> >
>> > There are quite a few that use the ASAN_POISON_MEMORY_REGION and
>> > ASAN_UNPOISON_MEMORY_REGION macros to poison/unpoison memory themselves.
>> >   This abstraction doesn't quite make sense in a hwasan environment, as
>> > there is not really a "poisoned/unpoisoned" concept.
>> >
>> > https://github.com/laurynas-biveinis/unodb
>> > https://github.com/darktable-org/rawspeed
>> > https://github.com/MariaDB/server
>> > https://github.com/ralfbrown/framepac-ng
>> > https://github.com/peters/aom
>> > https://github.com/pspacek/knot-resolver-docker-fix
>> > https://github.com/harikrishnan94/sheap
>> >
>> >
>> > Some use it to record their compilation "type" as `-fsanitize=address`
>> > https://github.com/wallix/redemption
>> >
>> > Or to decide to set the environment variable ASAN_OPTIONS
>> > https://github.com/dephonatine/VBox5.2.18
>> >
>> > Others worry about stack space due to asan's redzones (hwasan has a much
>> > smaller stack memory overhead).
>> > https://github.com/fastbuild/fastbuild
>> > https://github.com/scylladb/seastar
>> > (n.b. seastar has a lot more conditioned code that would be the same
>> > between asan and hwasan).
>> >
>> >
>> > Each of these needs to know the difference between compiling with asan
>> > and hwasan, so I'm confident that having some way to determine that in
>> > the source code is a good idea.
>> >
>> >
>> > I also believe there could be code in the wild that would need to
>> > distinguish between hwasan and asan where the existence of tags could be
>> > problematic:
>> >
>> > - code already using the top-byte-ignore feature may be able to be used
>> > with asan but not hwasan.
>> > - Code that makes assumptions about pointer ordering (e.g. the autoconf
>> > program that looks for stack growth direction) could break on hwasan but
>> > not on asan.
>> > - Code looking for the distance between two objects in memory would need
>> > to account for tags in pointers.
>> >
>> >
>> > Hence I think this distinction is needed.
>>
>> Evgenii, how does clang-compiled code dististinguishes whether it's
>> being compiled with ASAN or HWASAN?
diff mbox series

Patch

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 6500b998321419a1d8d57062534206c5909adb7a..2de94815f91da5a0fd06c30d0044f866084121b8 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,
@@ -410,6 +412,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,
@@ -929,6 +933,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 1b9e0f3c8025a3b439f766edcd81db462973037b..d8ba9556801e5afc479c33ba359125d6354ca862 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
@@ -3289,6 +3289,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 232317d4a5a4a16529f573eef5a8d7a068068207..c556bcd1c37c3c4fdd9a829a28ee4ff56819b89e 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -20272,6 +20272,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
@@ -20839,6 +20848,9 @@  aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_GET_MULTILIB_ABI_NAME
 #define TARGET_GET_MULTILIB_ABI_NAME aarch64_get_multilib_abi_name
 
+#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/tm.texi b/gcc/doc/tm.texi
index 0250cf58e72b4df8fec19cfb4399ed0e2594342b..bf53df715391128d6fbe9be4e77906650309ab2e 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2972,6 +2972,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 0b77dd8eb46dc53fc585d7b3eac9805c6ed79951..005cef05999d7c334f16ffa368903c3b66806231 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2374,6 +2374,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 1216cdd505a18152dc1d3eee5f37755a396761f1..cf1bd9de660f32f060b9277f89a562873a48684a 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 "}\
@@ -9434,8 +9455,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 efd75aade6c879f330db1aa7b8ef6b9100862c04..88a94286e71f61f2dce907018e5185f63a830804 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1160,6 +1160,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))
@@ -1179,6 +1186,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)
@@ -1198,7 +1219,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;
 
@@ -1827,8 +1849,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),
@@ -2363,6 +2390,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 5fe33976b37bb0763986040f66a9c28681363535..a4b3f02b60898f54aeec40238ad417e423f56e01 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1299,6 +1299,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 26f1236aa65422f66939ef2a4c38958bdc984aee..ad40bd0b5d3b217e6d0dc531fce04faba97b5f60 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -252,5 +252,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 01609136848fc157a47a93a0267c03524fe9383e..0ade31accab25bf121f135cbf02c6adfcd6e1476 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6706,6 +6706,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 5aba67660f85406b9fd475e75a3cc65b0d1952f5..463c27c7d7b550bf63630f2102681b37ffd265cb 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -284,4 +284,5 @@  extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 extern void default_remove_extra_call_preserved_regs (rtx_insn *,
 						      HARD_REG_SET *);
 
+extern bool default_memtag_can_tag_addresses ();
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index ed77afb1da57e59bc0725dc0d6fac477391bae03..d7dd07db65c8248c2f170466db21449a56713d69 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2368,4 +2368,10 @@  default_remove_extra_call_preserved_regs (rtx_insn *, HARD_REG_SET *)
 {
 }
 
+bool
+default_memtag_can_tag_addresses ()
+{
+  return false;
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index d741a66f3857a60bcdb6f5c1b60e781ff311aad4..3920ef5c40f27b27a449dc6bf1da795f0d40e77b 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1752,6 +1752,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.  */