diff mbox series

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

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

Commit Message

Matthew Malcomson Aug. 17, 2020, 2:13 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, I don't know of any reason
it would not work, but this has not yet been tested.

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

gcc/ChangeLog:

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

gcc/c-family/ChangeLog:

	* 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 372148315389db6671dfd943fd1a68670fcb1cbc..f8bf165aa48b5709c26f4e8245e5ab929b44fca6 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,
@@ -946,6 +950,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 a3893a4725e55b9002613cd04d37e5e98582c16f..17b3079c5c40775d05e23a51e3465913124e8f11 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -218,7 +218,7 @@ unsigned int flag_sanitize
 
 ; What sanitizers should recover from errors
 Variable
-unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
+unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS | SANITIZE_KERNEL_HWADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
 
 ; What the coverage sanitizers should instrument
 Variable
@@ -3414,6 +3414,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 3fe1feaa80ccb0a287ee1c7ea1056e8f0a830532..54d5641f96c0fffaaf3ab83d1119d9d922966f6d 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -23065,6 +23065,15 @@ aarch64_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1,
   return NULL;
 }
 
+/* Implement TARGET_MEMTAG_CAN_TAG_ADDRESSES.  Here we tell the rest of the
+   compiler that we automatically ignore the top byte of our pointers, which
+   allows using -fsanitize=hwaddress.  */
+bool
+aarch64_can_tag_addresses ()
+{
+  return true;
+}
+
 /* Implement TARGET_ASM_FILE_END for AArch64.  This adds the AArch64 GNU NOTE
    section at the end if needed.  */
 #define GNU_PROPERTY_AARCH64_FEATURE_1_AND	0xc0000000
@@ -23878,6 +23887,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_FNTYPE_ABI
 #define TARGET_FNTYPE_ABI aarch64_fntype_abi
 
+#undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
+#define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h
index ff2e880b1fa81b76af866b6ba0e1fabf6d534f32..92950245e2b95c500da9df8b85940380e62030fa 100644
--- a/gcc/config/gnu-user.h
+++ b/gcc/config/gnu-user.h
@@ -129,14 +129,18 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Link -lasan early on the command line.  For -static-libasan, don't link
    it for -shared link, the executable should be compiled with -static-libasan
    in that case, and for executable link with --{,no-}whole-archive around
-   it to force everything into the executable.  And similarly for -ltsan
-   and -llsan.  */
+   it to force everything into the executable.  And similarly for -ltsan,
+   -lhwasan, and -llsan.  */
 #if defined(HAVE_LD_STATIC_DYNAMIC)
 #undef LIBASAN_EARLY_SPEC
 #define LIBASAN_EARLY_SPEC "%{!shared:libasan_preinit%O%s} " \
   "%{static-libasan:%{!shared:" \
   LD_STATIC_OPTION " --whole-archive -lasan --no-whole-archive " \
   LD_DYNAMIC_OPTION "}}%{!static-libasan:-lasan}"
+#undef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC "%{static-libhwasan:%{!shared:" \
+  LD_STATIC_OPTION " --whole-archive -lhwasan --no-whole-archive " \
+  LD_DYNAMIC_OPTION "}}%{!static-libhwasan:-lhwasan}"
 #undef LIBTSAN_EARLY_SPEC
 #define LIBTSAN_EARLY_SPEC "%{!shared:libtsan_preinit%O%s} " \
   "%{static-libtsan:%{!shared:" \
diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c
index 61efe9b2b75e961c15e9f399cafed559b5c8c9be..fc61c78a99373fcfbc2bb03a01134248214eb2d4 100644
--- a/gcc/cppbuiltin.c
+++ b/gcc/cppbuiltin.c
@@ -93,6 +93,9 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile)
   if (flag_sanitize & SANITIZE_ADDRESS)
     cpp_define (pfile, "__SANITIZE_ADDRESS__");
 
+  if (flag_sanitize & SANITIZE_HWADDRESS)
+    cpp_define (pfile, "__SANITIZE_HWADDRESS__");
+
   if (flag_sanitize & SANITIZE_THREAD)
     cpp_define (pfile, "__SANITIZE_THREAD__");
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ba18e05fb1abd0034afb73fd4a20feac27133149..97a5a532e31a9cea20955863bf6d2c8911a8e869 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13898,13 +13898,34 @@ 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://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html}
+for more details.  The run-time behavior can be influenced using the
+@env{HWASAN_OPTIONS} environment variable.  When set to @code{help=1},
+the available options are shown at startup of the instrumented program.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=address}.
+
+@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 437187694b67e8016aaaa310adf6d2aa32cbc0a8..0a1131962747fbd172a137c28606eb2b62372383 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2975,6 +2975,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 @option{-fsanitize=hwaddress} can work.
+@end deftypefn
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index d76c85d580096866d47836de75cd8437080ddfbb..80960c1fe041bd08ccc22a4c41ebf740eca80015 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2375,6 +2375,8 @@ in the reload pass.
 
 @hook TARGET_COMPUTE_PRESSURE_CLASSES
 
+@hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index b092c563f3dae3ca841bc71855cec033d87d1edd..18280cbf6d8d1cfc5cc598a6c58282182cb8cf69 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -272,6 +272,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 c0eb3c10cfd63f8d073c2927d66b827aac60fb1c..094c4f1d3c638a308a0003f6e84db11339128f3c 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -741,6 +741,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)}"
@@ -1020,6 +1038,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
@@ -1029,6 +1048,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 "}\
@@ -9996,8 +10017,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 499eb9006431f178d243fe99d9b776a4b00ade90..ff0d48949dcf94a86de00c16087ef1b6381a2328 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1070,6 +1070,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))
@@ -1089,6 +1096,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)
@@ -1106,9 +1127,10 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
   if (opts->x_flag_sanitize & ~(SANITIZE_LEAK | SANITIZE_UNREACHABLE))
     opts->x_flag_aggressive_loop_optimizations = 0;
 
-  /* Enable -fsanitize-address-use-after-scope if address sanitizer is
+  /* Enable -fsanitize-address-use-after-scope if either address sanitizer is
      enabled.  */
-  if (opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+  if (opts->x_flag_sanitize
+      & (SANITIZE_USER_ADDRESS | SANITIZE_USER_HWADDRESS))
     SET_OPTION_IF_UNSET (opts, opts_set, flag_sanitize_address_use_after_scope,
 			 true);
 
@@ -1712,8 +1734,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),
@@ -2245,6 +2272,14 @@ common_handle_option (struct gcc_options *opts,
 	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_protect_allocas, 0);
 	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_use_after_return, 0);
 	}
+      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+	{
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_instrument_stack, 0);
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_random_frame_tag, 0);
+	  SET_OPTION_IF_UNSET (opts, opts_set, param_hwasan_protect_allocas, 0);
+	}
       break;
 
     case OPT_fsanitize_recover_:
diff --git a/gcc/params.opt b/gcc/params.opt
index b36eee0e71f24a3201879b73c1cd0de3298cac32..c792670a4b88539ff52d64139cef5d44da469b32 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -62,6 +62,30 @@ Enable asan stack protection.
 Common Joined UInteger Var(param_asan_use_after_return) Init(1) IntegerRange(0, 1) Param Optimization
 Enable asan detection of use-after-return bugs.
 
+-param=hwasan-instrument-stack=
+Common Joined UInteger Var(param_hwasan_instrument_stack) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan stack protection.
+
+-param=hwasan-random-frame-tag=
+Common Joined UInteger Var(param_hwasan_random_frame_tag) Init(1) IntegerRange(0, 1) Param Optimization
+Use random base tag for each frame, as opposed to base always zero.
+
+-param=hwasan-instrument-allocas=
+Common Joined UInteger Var(param_hwasan_protect_allocas) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan allocas/VLAs protection.
+
+-param=hwasan-instrument-reads=
+Common Joined UInteger Var(param_hwasan_instrument_reads) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan load operations protection.
+
+-param=hwasan-instrument-writes=
+Common Joined UInteger Var(param_hwasan_instrument_writes) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan store operations protection.
+
+-param=hwasan-instrument-mem-intrinsics=
+Common Joined UInteger Var(param_hwasan_instrument_mem_intrinsics) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan builtin functions protection.
+
 -param=avg-loop-niter=
 Common Joined UInteger Var(param_avg_loop_niter) Init(10) IntegerRange(1, 65536) Param Optimization
 Average number of iterations of a loop.
diff --git a/gcc/target.def b/gcc/target.def
index c11cab8891f9bf3c4c9b4357d7b4b0442560bb41..43b491b027615523f0202fe1ded430cac0b4bef4 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6830,6 +6830,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 @option{-fsanitize=hwaddress} can work.",
+ bool, (), default_memtag_can_tag_addresses)
+
+HOOK_VECTOR_END (memtag)
+#undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
 
 #define DEF_TARGET_INSN(NAME, PROTO) \
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index b572a36e8cf0147714fd14ec9096198c7d4e1a6d..c11b1879f65bb581af11963b40189028fa490623 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -285,4 +285,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 0113c7b0ce2359a59709402b6942e9eebb4adfb9..8c860f91cae7ccb5ca7fb0ff2364c1f67620d661 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2379,4 +2379,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 95eea63380f60ceae4996cac5f974d8a24b20061..cfbfc30ccfac9bd3bd3067bdddcdec71159a64a2 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1843,6 +1843,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

Richard Sandiford Oct. 14, 2020, 9:42 a.m. UTC | #1
Matthew Malcomson <matthew.malcomson@arm.com> writes:
> ###############     Attachment also inlined for ease of reply    ###############
>
>
> diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
> index 372148315389db6671dfd943fd1a68670fcb1cbc..f8bf165aa48b5709c26f4e8245e5ab929b44fca6 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,
> @@ -946,6 +950,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.  */
>  

Although the Clang design page mentions this attribute, it doesn't look
like it was ever committed upstream (unless I'm missing something).
Clang instead seems to require:

  __attribute__((no_sanitize("hwaddress")))
  __attribute__((no_sanitize("kernel-hwaddress")))

That seems more scalable than adding an extra attribute for each new
sanitiser, and of course is what this patch also supports.

So it might be better to drop this and just stick with what Clang provides.

> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index ba18e05fb1abd0034afb73fd4a20feac27133149..97a5a532e31a9cea20955863bf6d2c8911a8e869 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -13898,13 +13898,34 @@ 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.

Is HardwareAddressSanitizer an established shorthand?  All the references
I could see instead referred to it as “Hardware-assisted AddressSanitizer”,
which seems a bit more descriptive.

It would be good to expand on “a fast memory error detector“ a bit.
I was wondering about something like “, which uses dedicated features
of the target hardware to accelerate the detection of memory errors”.

> +Memory access instructions are instrumented to detect out-of-bounds and
> +use-after-free bugs.
> +The option enables @option{-fsanitize-address-use-after-scope}.
> +See
> +@uref{https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html}
> +for more details.  The run-time behavior can be influenced using the
> +@env{HWASAN_OPTIONS} environment variable.  When set to @code{help=1},
> +the available options are shown at startup of the instrumented program.
> +The option cannot be combined with @option{-fsanitize=thread} or
> +@option{-fsanitize=address}.

I think it would be worth emphasising that this option is only supported
on AArch64.

> +
> +@item -fsanitize=kernel-hwaddress
> +@opindex fsanitize=kernel-hwaddress
> +Enable HardwareAddressSanitizer for Linux kernel.

IMO “for Linux” or “for the Linux kernel” reads better.

> +Similar to @option{-fsanitize=kernel-address} but using the software tagging
> +method of instrumentation.

“the software tagging method of instrumentation” makes it sound like
users are already expected to know what that is.  I think this should
be expanded a bit.

> diff --git a/gcc/opts.c b/gcc/opts.c
> index 499eb9006431f178d243fe99d9b776a4b00ade90..ff0d48949dcf94a86de00c16087ef1b6381a2328 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -1070,6 +1070,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))

I realise this is making more work for you, sorry, but I think we
should avoid having so many errors of the same form but with different
option names hard-coded into the strings.  Doing that just creates more
work for the translators.  IMO this and existing errors should use:

   error_at (loc, "%qs is incompatible with %qs", "-f…", "-f…");

> @@ -1089,6 +1096,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%>");

It looks like these tests are detecting errors for
-fsanitize=kernel-hwaddress as well as -fsanitize=hwaddress.

This is again an existing problem, but it seems like bad UI to list
multiple options that the user might have specified when we know
exactly which ones they did specify.  I.e. each error should only
need to specify two options.

Rather than trying to catch all the combinations individually, perhaps
it would be better to have a utility function along the lines of:

  report_conflicting_sanitizer_options (flags1, flags2)

where FLAGS1 and FLAGS2 are bitmasks of SANITIZER_* options.
The function could then use sanitizer_opts to report an appropriate
error if one option from FLAGS1 is set at the same time as a different
option from FLAGS2, or vice versa.

> +
> @@ -2245,6 +2272,14 @@ common_handle_option (struct gcc_options *opts,
>  	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_protect_allocas, 0);
>  	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_use_after_return, 0);
>  	}
> +      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
> +	{
> +	  SET_OPTION_IF_UNSET (opts, opts_set,
> +			       param_hwasan_instrument_stack, 0);
> +	  SET_OPTION_IF_UNSET (opts, opts_set,
> +			       param_hwasan_random_frame_tag, 0);
> +	  SET_OPTION_IF_UNSET (opts, opts_set, param_hwasan_protect_allocas, 0);
> +	}

I think the documentation should make it clearer that
-fsanitize=kernel-hwaddress offers a different level of protection
from -fsanitize=hwaddress.  It sounded from the documentation above
like it was a pure performance choice.

> diff --git a/gcc/params.opt b/gcc/params.opt
> index b36eee0e71f24a3201879b73c1cd0de3298cac32..c792670a4b88539ff52d64139cef5d44da469b32 100644
> --- a/gcc/params.opt
> +++ b/gcc/params.opt
> @@ -62,6 +62,30 @@ Enable asan stack protection.
>  Common Joined UInteger Var(param_asan_use_after_return) Init(1) IntegerRange(0, 1) Param Optimization
>  Enable asan detection of use-after-return bugs.
>  
> +-param=hwasan-instrument-stack=
> +Common Joined UInteger Var(param_hwasan_instrument_stack) Init(1) IntegerRange(0, 1) Param Optimization
> +Enable hwasan stack protection.

I think it would be worth expanding this a bit.  “stack protection”
can mean different things :-)

Also, “protection” seems like a dangerous word, especially given that we
continue after a problem has been detected.  The sanitisers are more about
detecting errors and finding bugs rather than trying to protect against
an attacker.  Same comment for the rest.

> +-param=hwasan-random-frame-tag=
> +Common Joined UInteger Var(param_hwasan_random_frame_tag) Init(1) IntegerRange(0, 1) Param Optimization
> +Use random base tag for each frame, as opposed to base always zero.
> +
> +-param=hwasan-instrument-allocas=
> +Common Joined UInteger Var(param_hwasan_protect_allocas) Init(1) IntegerRange(0, 1) Param Optimization
> +Enable hwasan allocas/VLAs protection.
> +
> +-param=hwasan-instrument-reads=
> +Common Joined UInteger Var(param_hwasan_instrument_reads) Init(1) IntegerRange(0, 1) Param Optimization
> +Enable hwasan load operations protection.
> +
> +-param=hwasan-instrument-writes=
> +Common Joined UInteger Var(param_hwasan_instrument_writes) Init(1) IntegerRange(0, 1) Param Optimization
> +Enable hwasan store operations protection.
> +
> +-param=hwasan-instrument-mem-intrinsics=
> +Common Joined UInteger Var(param_hwasan_instrument_mem_intrinsics) Init(1) IntegerRange(0, 1) Param Optimization
> +Enable hwasan builtin functions protection.
> +
>  -param=avg-loop-niter=
>  Common Joined UInteger Var(param_avg_loop_niter) Init(10) IntegerRange(1, 65536) Param Optimization
>  Average number of iterations of a loop.

--params also need to be documented in invoke.texi, even though we don't
guarantee a stable interface for them.

> diff --git a/gcc/target.def b/gcc/target.def
> index c11cab8891f9bf3c4c9b4357d7b4b0442560bb41..43b491b027615523f0202fe1ded430cac0b4bef4 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -6830,6 +6830,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 @option{-fsanitize=hwaddress} can work.",
> + bool, (), default_memtag_can_tag_addresses)

I'm a bit wary of hard-coding the idea that the tag must be exactly
a byte in size.  How easy would it be to parameterise that?

If it's easy to do then it's probably worth it.  If it's not easy
to do then I think it's fair to leave it to whoever needs a different
size in future.

> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index 95eea63380f60ceae4996cac5f974d8a24b20061..cfbfc30ccfac9bd3bd3067bdddcdec71159a64a2 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -1843,6 +1843,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;
> +    }

I think this should just be "%qs is not supported for this target".
If other architectures add support for a similar feature in future then
existing GCCs will be unaware of that feature.  The limitation will then
be in GCC rather than the target.

Thanks,
Richard
diff mbox series

Patch

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 372148315389db6671dfd943fd1a68670fcb1cbc..f8bf165aa48b5709c26f4e8245e5ab929b44fca6 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,
@@ -946,6 +950,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 a3893a4725e55b9002613cd04d37e5e98582c16f..17b3079c5c40775d05e23a51e3465913124e8f11 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -218,7 +218,7 @@  unsigned int flag_sanitize
 
 ; What sanitizers should recover from errors
 Variable
-unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
+unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS | SANITIZE_KERNEL_HWADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
 
 ; What the coverage sanitizers should instrument
 Variable
@@ -3414,6 +3414,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 3fe1feaa80ccb0a287ee1c7ea1056e8f0a830532..54d5641f96c0fffaaf3ab83d1119d9d922966f6d 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -23065,6 +23065,15 @@  aarch64_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1,
   return NULL;
 }
 
+/* Implement TARGET_MEMTAG_CAN_TAG_ADDRESSES.  Here we tell the rest of the
+   compiler that we automatically ignore the top byte of our pointers, which
+   allows using -fsanitize=hwaddress.  */
+bool
+aarch64_can_tag_addresses ()
+{
+  return true;
+}
+
 /* Implement TARGET_ASM_FILE_END for AArch64.  This adds the AArch64 GNU NOTE
    section at the end if needed.  */
 #define GNU_PROPERTY_AARCH64_FEATURE_1_AND	0xc0000000
@@ -23878,6 +23887,9 @@  aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_FNTYPE_ABI
 #define TARGET_FNTYPE_ABI aarch64_fntype_abi
 
+#undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
+#define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h
index ff2e880b1fa81b76af866b6ba0e1fabf6d534f32..92950245e2b95c500da9df8b85940380e62030fa 100644
--- a/gcc/config/gnu-user.h
+++ b/gcc/config/gnu-user.h
@@ -129,14 +129,18 @@  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Link -lasan early on the command line.  For -static-libasan, don't link
    it for -shared link, the executable should be compiled with -static-libasan
    in that case, and for executable link with --{,no-}whole-archive around
-   it to force everything into the executable.  And similarly for -ltsan
-   and -llsan.  */
+   it to force everything into the executable.  And similarly for -ltsan,
+   -lhwasan, and -llsan.  */
 #if defined(HAVE_LD_STATIC_DYNAMIC)
 #undef LIBASAN_EARLY_SPEC
 #define LIBASAN_EARLY_SPEC "%{!shared:libasan_preinit%O%s} " \
   "%{static-libasan:%{!shared:" \
   LD_STATIC_OPTION " --whole-archive -lasan --no-whole-archive " \
   LD_DYNAMIC_OPTION "}}%{!static-libasan:-lasan}"
+#undef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC "%{static-libhwasan:%{!shared:" \
+  LD_STATIC_OPTION " --whole-archive -lhwasan --no-whole-archive " \
+  LD_DYNAMIC_OPTION "}}%{!static-libhwasan:-lhwasan}"
 #undef LIBTSAN_EARLY_SPEC
 #define LIBTSAN_EARLY_SPEC "%{!shared:libtsan_preinit%O%s} " \
   "%{static-libtsan:%{!shared:" \
diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c
index 61efe9b2b75e961c15e9f399cafed559b5c8c9be..fc61c78a99373fcfbc2bb03a01134248214eb2d4 100644
--- a/gcc/cppbuiltin.c
+++ b/gcc/cppbuiltin.c
@@ -93,6 +93,9 @@  define_builtin_macros_for_compilation_flags (cpp_reader *pfile)
   if (flag_sanitize & SANITIZE_ADDRESS)
     cpp_define (pfile, "__SANITIZE_ADDRESS__");
 
+  if (flag_sanitize & SANITIZE_HWADDRESS)
+    cpp_define (pfile, "__SANITIZE_HWADDRESS__");
+
   if (flag_sanitize & SANITIZE_THREAD)
     cpp_define (pfile, "__SANITIZE_THREAD__");
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ba18e05fb1abd0034afb73fd4a20feac27133149..97a5a532e31a9cea20955863bf6d2c8911a8e869 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13898,13 +13898,34 @@  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://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html}
+for more details.  The run-time behavior can be influenced using the
+@env{HWASAN_OPTIONS} environment variable.  When set to @code{help=1},
+the available options are shown at startup of the instrumented program.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=address}.
+
+@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 437187694b67e8016aaaa310adf6d2aa32cbc0a8..0a1131962747fbd172a137c28606eb2b62372383 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2975,6 +2975,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 @option{-fsanitize=hwaddress} can work.
+@end deftypefn
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index d76c85d580096866d47836de75cd8437080ddfbb..80960c1fe041bd08ccc22a4c41ebf740eca80015 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2375,6 +2375,8 @@  in the reload pass.
 
 @hook TARGET_COMPUTE_PRESSURE_CLASSES
 
+@hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index b092c563f3dae3ca841bc71855cec033d87d1edd..18280cbf6d8d1cfc5cc598a6c58282182cb8cf69 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -272,6 +272,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 c0eb3c10cfd63f8d073c2927d66b827aac60fb1c..094c4f1d3c638a308a0003f6e84db11339128f3c 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -741,6 +741,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)}"
@@ -1020,6 +1038,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
@@ -1029,6 +1048,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 "}\
@@ -9996,8 +10017,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 499eb9006431f178d243fe99d9b776a4b00ade90..ff0d48949dcf94a86de00c16087ef1b6381a2328 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1070,6 +1070,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))
@@ -1089,6 +1096,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)
@@ -1106,9 +1127,10 @@  finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
   if (opts->x_flag_sanitize & ~(SANITIZE_LEAK | SANITIZE_UNREACHABLE))
     opts->x_flag_aggressive_loop_optimizations = 0;
 
-  /* Enable -fsanitize-address-use-after-scope if address sanitizer is
+  /* Enable -fsanitize-address-use-after-scope if either address sanitizer is
      enabled.  */
-  if (opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+  if (opts->x_flag_sanitize
+      & (SANITIZE_USER_ADDRESS | SANITIZE_USER_HWADDRESS))
     SET_OPTION_IF_UNSET (opts, opts_set, flag_sanitize_address_use_after_scope,
 			 true);
 
@@ -1712,8 +1734,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),
@@ -2245,6 +2272,14 @@  common_handle_option (struct gcc_options *opts,
 	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_protect_allocas, 0);
 	  SET_OPTION_IF_UNSET (opts, opts_set, param_asan_use_after_return, 0);
 	}
+      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+	{
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_instrument_stack, 0);
+	  SET_OPTION_IF_UNSET (opts, opts_set,
+			       param_hwasan_random_frame_tag, 0);
+	  SET_OPTION_IF_UNSET (opts, opts_set, param_hwasan_protect_allocas, 0);
+	}
       break;
 
     case OPT_fsanitize_recover_:
diff --git a/gcc/params.opt b/gcc/params.opt
index b36eee0e71f24a3201879b73c1cd0de3298cac32..c792670a4b88539ff52d64139cef5d44da469b32 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -62,6 +62,30 @@  Enable asan stack protection.
 Common Joined UInteger Var(param_asan_use_after_return) Init(1) IntegerRange(0, 1) Param Optimization
 Enable asan detection of use-after-return bugs.
 
+-param=hwasan-instrument-stack=
+Common Joined UInteger Var(param_hwasan_instrument_stack) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan stack protection.
+
+-param=hwasan-random-frame-tag=
+Common Joined UInteger Var(param_hwasan_random_frame_tag) Init(1) IntegerRange(0, 1) Param Optimization
+Use random base tag for each frame, as opposed to base always zero.
+
+-param=hwasan-instrument-allocas=
+Common Joined UInteger Var(param_hwasan_protect_allocas) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan allocas/VLAs protection.
+
+-param=hwasan-instrument-reads=
+Common Joined UInteger Var(param_hwasan_instrument_reads) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan load operations protection.
+
+-param=hwasan-instrument-writes=
+Common Joined UInteger Var(param_hwasan_instrument_writes) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan store operations protection.
+
+-param=hwasan-instrument-mem-intrinsics=
+Common Joined UInteger Var(param_hwasan_instrument_mem_intrinsics) Init(1) IntegerRange(0, 1) Param Optimization
+Enable hwasan builtin functions protection.
+
 -param=avg-loop-niter=
 Common Joined UInteger Var(param_avg_loop_niter) Init(10) IntegerRange(1, 65536) Param Optimization
 Average number of iterations of a loop.
diff --git a/gcc/target.def b/gcc/target.def
index c11cab8891f9bf3c4c9b4357d7b4b0442560bb41..43b491b027615523f0202fe1ded430cac0b4bef4 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6830,6 +6830,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 @option{-fsanitize=hwaddress} can work.",
+ bool, (), default_memtag_can_tag_addresses)
+
+HOOK_VECTOR_END (memtag)
+#undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
 
 #define DEF_TARGET_INSN(NAME, PROTO) \
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index b572a36e8cf0147714fd14ec9096198c7d4e1a6d..c11b1879f65bb581af11963b40189028fa490623 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -285,4 +285,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 0113c7b0ce2359a59709402b6942e9eebb4adfb9..8c860f91cae7ccb5ca7fb0ff2364c1f67620d661 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2379,4 +2379,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 95eea63380f60ceae4996cac5f974d8a24b20061..cfbfc30ccfac9bd3bd3067bdddcdec71159a64a2 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1843,6 +1843,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.  */