public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 0/X] Introduce HWASAN sanitizer to GCC
@ 2019-11-05 11:32 ` Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 3/X] [libsanitizer] libhwasan initialisation include kernel syscall ABI relaxation Matthew Malcomson
                     ` (29 more replies)
  0 siblings, 30 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:32 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov, Martin Liska,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

[-- Attachment #1: Type: text/plain, Size: 4922 bytes --]

Hello,

This patch series adds the LLVM hardware address sanitizer (HWASAN) to
GCC.  The document describing HWASAN can be found here
http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html.

This address sanitizer only works for AArch64 at the moment.  It
requires the "top byte ignore" feature where the top byte of a pointer
does not affect dereferences.  This is checked for by a backend hook so
that if other architectures have this feature HWASAN can be used for
them.

We require a linux kernel with the relaxed ABI to allow tagged pointers
in system calls.  This is in the linux mainline, I have been testing
this feature on 5.4.0-rc2.

HWASAN works by storing a tag in the top bits of every pointer and a tag in
a shadow memory region corresponding to each area of memory pointed at.
On every memory access through a pointer the tag in the pointer is
checked against the tag in shadow memory corresponding to the memory the
pointer is accessing.  If the pointer tag and memory tag do not match
then a fault is signalled.

The instrumentation required for this sanitizer has a large overlap with
the instrumentation required for implementing MTE (which has similar
functionality but checks are automatically done in the hardware and
instructions for tagging shadow memory and for managing tags are
provided by the architecture).
https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/arm-a-profile-architecture-2018-developments-armv85a

We hope to use the HWASAN framework to implement MTE for the stack, and
hence I have a "dummy" patch demonstrating the approach envisaged for
this.


Some points that I know need some discussion are:

1) Should we set the existing inbuilt __SANITIZER_ADDRESS__ macro, or a
   new __SANITIZER_HWADDRESS__ macro?
   (extra information in patch 13).
2) How should we account for exceptions?
   (see demo patch number 17).


Outline of patch series is:

  1 - Change type used in `build_personality_function`.
      Bug I noticed -- fix not actually needed for patch series.
  2 - Fix off-by-one allocation mistake.
      Bug that hwasan found, not needed for the patch series.
  2 - Introduce libhwasan to GCC tree
      Take from LLVM at same revision as rest of libsanitizer.
  3 - libhwasan initialisation include kernel syscall ABI relaxation
      Backport from LLVM
  4 - libhwasan add longjmp & setjmp interceptors
      Backport from LLVM
  5 - Remove system allocator fallback
      Backport from LLVM
  6 - Add hwasan_exceptions.cpp file
      Backport from LLVM
  7 - Add missing SANITIZER_INTERFACE_ATTRIBUTE on __hwasan_personality_wrapper
      Backport from LLVM
  8 - Expose __hwasan_tag_mismatch_stub
      Backport from LLVM
  9 - Remove lazy thread initialisation
      Backport from LLVM
  10 - Tie the hwasan library into our build system
  11 - Only build libhwasan when targeting AArch64
  12 - Add option to bootstrap using HWASAN
       Write a bootstrap-*.mk file similar to bootstrap-asan.mk
  13 - Add hwasan flags and argument parsing
  14 - Introduce stack variable handling for HWASAN
       Ensuring the "shadow stack" is tagged and each pointer to a stack
       variable has a tag in it.
  15 - Add hwasan pass and associated gimple changes
       Introduce checks that tags match on access, also tag variables
       around the blocks they're defined for.
  16 - Add tests
  17 - Add hwasan Exception handling
       This is a temporary patch -- it does not work due to a mismatch
       with libhwasan.  I am hoping for a discussion on what to do here.
       Since our main aim is to implement this sanitizer for the kernel,
       not handling C++ exceptions is not a blocker.
  18 - Add in MTE stubs
       Demonstration patch to show how MTE might be added on top of
       this.  Is not to go into trunk.


Testing done:
Full bootstrap and regtest on x86_64 (no difference -- hwasan not used).

Full bootstrap and regtest on AArch64 sanitizing with hwasan and running
on recent kernel.
Regressions all accounted for:
  1) tests under plugin/
     testism where hwasan library is not linked in.
     (same appears to happen for asan)
  2) branch-protection-attr.c
     New bug found by hwasan, fix in this patch series.
  3) pr88597.c 
     timeout, can run manually and everything works (but is very slow)
  4) aarch64/long_branch_1.c
     timeout, as above.
  5) gfortran/class_61.f90
     bug already caught by ASAN and reported upstream
     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89661
  6) gfortran/dec_type_print_2.f03
     bug already caught by ASAN and reported upstream
     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86657
  7) gfortran/minlocval_3.f90
     timeout, can run manually and passes (but is very slow)


Entire patch series attached to cover letter.

[-- Attachment #2: all-patches.tar.gz --]
[-- Type: application/gzip, Size: 110409 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 4/X] [libsanitizer] libhwasan add longjmp & setjmp interceptors
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (7 preceding siblings ...)
  2019-11-05 11:34   ` [PATCH 11/X] [libsanitizer] Only build libhwasan when targeting AArch64 Matthew Malcomson
@ 2019-11-05 11:34   ` Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 8/X] [libsanitizer] Expose __hwasan_tag_mismatch_stub Matthew Malcomson
                     ` (20 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:34 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 10199 bytes --]

Backported from LLVM git id 67474c60d

libsanitizer/ChangeLog:

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

	* hwasan/hwasan.h (__hw_jmp_buf_struct, __hw_jmp_buf,
	__hw_sigjmp_buf): Define new types for internal longjmp
	implementation.
	* hwasan/hwasan_interceptors.cpp (__sigjmp_save,
	InternalLongjmp, siglongjmp, longjmp, __libc_longjmp): New.
	* hwasan/hwasan_setjmp.S: New file.
	* hwasan/hwasan_type_test.cpp: New file.



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


diff --git a/libsanitizer/hwasan/hwasan.h b/libsanitizer/hwasan/hwasan.h
index 9e0ced93b55d361cd5aae787db7562741683944c..64cdcf30f5c7059dcc1916f72e14b6649ca701f5 100644
--- a/libsanitizer/hwasan/hwasan.h
+++ b/libsanitizer/hwasan/hwasan.h
@@ -172,4 +172,24 @@ void AndroidTestTlsSlot();
     RunFreeHooks(ptr);            \
   } while (false)
 
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+// For both bionic and glibc __sigset_t is an unsigned long.
+typedef unsigned long __hw_sigset_t;
+// Setjmp and longjmp implementations are platform specific, and hence the
+// interception code is platform specific too.  As yet we've only implemented
+// the interception for AArch64.
+typedef unsigned long long __hw_register_buf[22];
+struct __hw_jmp_buf_struct {
+  // NOTE: The machine-dependent definition of `__sigsetjmp'
+  // assume that a `__hw_jmp_buf' begins with a `__hw_register_buf' and that
+  // `__mask_was_saved' follows it.  Do not move these members or add others
+  // before it.
+  __hw_register_buf __jmpbuf; // Calling environment.
+  int __mask_was_saved;       // Saved the signal mask?
+  __hw_sigset_t __saved_mask; // Saved signal mask.
+};
+typedef struct __hw_jmp_buf_struct __hw_jmp_buf[1];
+typedef struct __hw_jmp_buf_struct __hw_sigjmp_buf[1];
+#endif // HWASAN_WITH_INTERCEPTORS && __aarch64__
+
 #endif  // HWASAN_H
diff --git a/libsanitizer/hwasan/hwasan_interceptors.cpp b/libsanitizer/hwasan/hwasan_interceptors.cpp
index 47fed0fc9abb821996efcd8d12f7e5442916326d..f6758efa65c051376468d3cad2c1530fa7329627 100644
--- a/libsanitizer/hwasan/hwasan_interceptors.cpp
+++ b/libsanitizer/hwasan/hwasan_interceptors.cpp
@@ -220,6 +220,80 @@ DEFINE_REAL(int, vfork)
 DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
 #endif
 
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+// Get and/or change the set of blocked signals.
+extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set,
+                           __hw_sigset_t *__restrict __oset);
+#define SIG_BLOCK 0
+#define SIG_SETMASK 2
+extern "C" int __sigjmp_save(__hw_sigjmp_buf env, int savemask) {
+  env[0].__mask_was_saved =
+      (savemask && sigprocmask(SIG_BLOCK, (__hw_sigset_t *)0,
+                               &env[0].__saved_mask) == 0);
+  return 0;
+}
+
+static void __attribute__((always_inline))
+InternalLongjmp(__hw_register_buf env, int retval) {
+  // Clear all memory tags on the stack between here and where we're going.
+  unsigned long long stack_pointer = env[13];
+  // The stack pointer should never be tagged, so we don't need to clear the
+  // tag for this function call.
+  __hwasan_handle_longjmp((void *)stack_pointer);
+
+  // Run code for handling a longjmp.
+  // Need to use a register that isn't going to be loaded from the environment
+  // buffer -- hence why we need to specify the register to use.
+  // Must implement this ourselves, since we don't know the order of registers
+  // in different libc implementations and many implementations mangle the
+  // stack pointer so we can't use it without knowing the demangling scheme.
+  register long int retval_tmp asm("x1") = retval;
+  register void *env_address asm("x0") = &env[0];
+  asm volatile("ldp	x19, x20, [%0, #0<<3];"
+               "ldp	x21, x22, [%0, #2<<3];"
+               "ldp	x23, x24, [%0, #4<<3];"
+               "ldp	x25, x26, [%0, #6<<3];"
+               "ldp	x27, x28, [%0, #8<<3];"
+               "ldp	x29, x30, [%0, #10<<3];"
+               "ldp	 d8,  d9, [%0, #14<<3];"
+               "ldp	d10, d11, [%0, #16<<3];"
+               "ldp	d12, d13, [%0, #18<<3];"
+               "ldp	d14, d15, [%0, #20<<3];"
+               "ldr	x5, [%0, #13<<3];"
+               "mov	sp, x5;"
+               // Return the value requested to return through arguments.
+               // This should be in x1 given what we requested above.
+               "cmp	%1, #0;"
+               "mov	x0, #1;"
+               "csel	x0, %1, x0, ne;"
+               "br	x30;"
+               : "+r"(env_address)
+               : "r"(retval_tmp));
+}
+
+INTERCEPTOR(void, siglongjmp, __hw_sigjmp_buf env, int val) {
+  if (env[0].__mask_was_saved)
+    // Restore the saved signal mask.
+    (void)sigprocmask(SIG_SETMASK, &env[0].__saved_mask,
+                      (__hw_sigset_t *)0);
+  InternalLongjmp(env[0].__jmpbuf, val);
+}
+
+// Required since glibc libpthread calls __libc_longjmp on pthread_exit, and
+// _setjmp on start_thread.  Hence we have to intercept the longjmp on
+// pthread_exit so the __hw_jmp_buf order matches.
+INTERCEPTOR(void, __libc_longjmp, __hw_jmp_buf env, int val) {
+  InternalLongjmp(env[0].__jmpbuf, val);
+}
+
+INTERCEPTOR(void, longjmp, __hw_jmp_buf env, int val) {
+  InternalLongjmp(env[0].__jmpbuf, val);
+}
+#undef SIG_BLOCK
+#undef SIG_SETMASK
+
+#endif // HWASAN_WITH_INTERCEPTORS && __aarch64__
+
 static void BeforeFork() {
   StackDepotLockAll();
 }
diff --git a/libsanitizer/hwasan/hwasan_setjmp.S b/libsanitizer/hwasan/hwasan_setjmp.S
new file mode 100644
index 0000000000000000000000000000000000000000..0c1354331940e23acad1ca4becba87199a211653
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_setjmp.S
@@ -0,0 +1,100 @@
+//===-- hwasan_setjmp.S --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer runtime.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_asm.h"
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+#include "sanitizer_common/sanitizer_platform.h"
+
+// We want to save the context of the calling function.
+// That requires
+// 1) No modification of the link register by this function.
+// 2) No modification of the stack pointer by this function.
+// 3) (no modification of any other saved register, but that's not really going
+// to occur, and hence isn't as much of a worry).
+//
+// There's essentially no way to ensure that the compiler will not modify the
+// stack pointer when compiling a C function.
+// Hence we have to write this function in assembly.
+
+.section .text
+.file "hwasan_setjmp.S"
+
+.global __interceptor_setjmp
+ASM_TYPE_FUNCTION(__interceptor_setjmp)
+__interceptor_setjmp:
+  CFI_STARTPROC
+  mov	x1, #0
+  b	__interceptor_sigsetjmp
+  CFI_ENDPROC
+ASM_SIZE(__interceptor_setjmp)
+
+#if SANITIZER_ANDROID
+// Bionic also defines a function `setjmp` that calls `sigsetjmp` saving the
+// current signal.
+.global __interceptor_setjmp_bionic
+ASM_TYPE_FUNCTION(__interceptor_setjmp_bionic)
+__interceptor_setjmp_bionic:
+  CFI_STARTPROC
+  mov	x1, #1
+  b	__interceptor_sigsetjmp
+  CFI_ENDPROC
+ASM_SIZE(__interceptor_setjmp_bionic)
+#endif
+
+.global __interceptor_sigsetjmp
+ASM_TYPE_FUNCTION(__interceptor_sigsetjmp)
+__interceptor_sigsetjmp:
+  CFI_STARTPROC
+  stp	x19, x20, [x0, #0<<3]
+  stp	x21, x22, [x0, #2<<3]
+  stp	x23, x24, [x0, #4<<3]
+  stp	x25, x26, [x0, #6<<3]
+  stp	x27, x28, [x0, #8<<3]
+  stp	x29, x30, [x0, #10<<3]
+  stp	 d8,  d9, [x0, #14<<3]
+  stp	d10, d11, [x0, #16<<3]
+  stp	d12, d13, [x0, #18<<3]
+  stp	d14, d15, [x0, #20<<3]
+  mov	x2,  sp
+  str	x2,  [x0, #13<<3]
+  // We always have the second argument to __sigjmp_save (savemask) set, since
+  // the _setjmp function above has set it for us as `false`.
+  // This function is defined in hwasan_interceptors.cc
+  b	__sigjmp_save
+  CFI_ENDPROC
+ASM_SIZE(__interceptor_sigsetjmp)
+
+
+.macro ALIAS first second
+  .globl \second
+  .equ \second\(), \first
+.endm
+
+#if SANITIZER_ANDROID
+ALIAS __interceptor_sigsetjmp, sigsetjmp
+.weak sigsetjmp
+
+ALIAS __interceptor_setjmp_bionic, setjmp
+.weak setjmp
+#else
+ALIAS __interceptor_sigsetjmp, __sigsetjmp
+.weak __sigsetjmp
+#endif
+
+ALIAS __interceptor_setjmp, _setjmp
+.weak _setjmp
+#endif
+
+// We do not need executable stack.
+NO_EXEC_STACK_DIRECTIVE
diff --git a/libsanitizer/hwasan/hwasan_type_test.cpp b/libsanitizer/hwasan/hwasan_type_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8cff495bae153eb728a9dc7d12e80be3bc976a85
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_type_test.cpp
@@ -0,0 +1,25 @@
+//===-- hwasan_type_test.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Compile-time tests of the internal type definitions.
+//===----------------------------------------------------------------------===//
+
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include "hwasan.h"
+#include <setjmp.h>
+
+#define CHECK_TYPE_SIZE_FITS(TYPE) \
+  COMPILER_CHECK(sizeof(__hw_##TYPE) <= sizeof(TYPE))
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+CHECK_TYPE_SIZE_FITS(jmp_buf);
+CHECK_TYPE_SIZE_FITS(sigjmp_buf);
+#endif


[-- Attachment #2: hwasan-patch04.patch --]
[-- Type: text/plain, Size: 9399 bytes --]

diff --git a/libsanitizer/hwasan/hwasan.h b/libsanitizer/hwasan/hwasan.h
index 9e0ced93b55d361cd5aae787db7562741683944c..64cdcf30f5c7059dcc1916f72e14b6649ca701f5 100644
--- a/libsanitizer/hwasan/hwasan.h
+++ b/libsanitizer/hwasan/hwasan.h
@@ -172,4 +172,24 @@ void AndroidTestTlsSlot();
     RunFreeHooks(ptr);            \
   } while (false)
 
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+// For both bionic and glibc __sigset_t is an unsigned long.
+typedef unsigned long __hw_sigset_t;
+// Setjmp and longjmp implementations are platform specific, and hence the
+// interception code is platform specific too.  As yet we've only implemented
+// the interception for AArch64.
+typedef unsigned long long __hw_register_buf[22];
+struct __hw_jmp_buf_struct {
+  // NOTE: The machine-dependent definition of `__sigsetjmp'
+  // assume that a `__hw_jmp_buf' begins with a `__hw_register_buf' and that
+  // `__mask_was_saved' follows it.  Do not move these members or add others
+  // before it.
+  __hw_register_buf __jmpbuf; // Calling environment.
+  int __mask_was_saved;       // Saved the signal mask?
+  __hw_sigset_t __saved_mask; // Saved signal mask.
+};
+typedef struct __hw_jmp_buf_struct __hw_jmp_buf[1];
+typedef struct __hw_jmp_buf_struct __hw_sigjmp_buf[1];
+#endif // HWASAN_WITH_INTERCEPTORS && __aarch64__
+
 #endif  // HWASAN_H
diff --git a/libsanitizer/hwasan/hwasan_interceptors.cpp b/libsanitizer/hwasan/hwasan_interceptors.cpp
index 47fed0fc9abb821996efcd8d12f7e5442916326d..f6758efa65c051376468d3cad2c1530fa7329627 100644
--- a/libsanitizer/hwasan/hwasan_interceptors.cpp
+++ b/libsanitizer/hwasan/hwasan_interceptors.cpp
@@ -220,6 +220,80 @@ DEFINE_REAL(int, vfork)
 DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
 #endif
 
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+// Get and/or change the set of blocked signals.
+extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set,
+                           __hw_sigset_t *__restrict __oset);
+#define SIG_BLOCK 0
+#define SIG_SETMASK 2
+extern "C" int __sigjmp_save(__hw_sigjmp_buf env, int savemask) {
+  env[0].__mask_was_saved =
+      (savemask && sigprocmask(SIG_BLOCK, (__hw_sigset_t *)0,
+                               &env[0].__saved_mask) == 0);
+  return 0;
+}
+
+static void __attribute__((always_inline))
+InternalLongjmp(__hw_register_buf env, int retval) {
+  // Clear all memory tags on the stack between here and where we're going.
+  unsigned long long stack_pointer = env[13];
+  // The stack pointer should never be tagged, so we don't need to clear the
+  // tag for this function call.
+  __hwasan_handle_longjmp((void *)stack_pointer);
+
+  // Run code for handling a longjmp.
+  // Need to use a register that isn't going to be loaded from the environment
+  // buffer -- hence why we need to specify the register to use.
+  // Must implement this ourselves, since we don't know the order of registers
+  // in different libc implementations and many implementations mangle the
+  // stack pointer so we can't use it without knowing the demangling scheme.
+  register long int retval_tmp asm("x1") = retval;
+  register void *env_address asm("x0") = &env[0];
+  asm volatile("ldp	x19, x20, [%0, #0<<3];"
+               "ldp	x21, x22, [%0, #2<<3];"
+               "ldp	x23, x24, [%0, #4<<3];"
+               "ldp	x25, x26, [%0, #6<<3];"
+               "ldp	x27, x28, [%0, #8<<3];"
+               "ldp	x29, x30, [%0, #10<<3];"
+               "ldp	 d8,  d9, [%0, #14<<3];"
+               "ldp	d10, d11, [%0, #16<<3];"
+               "ldp	d12, d13, [%0, #18<<3];"
+               "ldp	d14, d15, [%0, #20<<3];"
+               "ldr	x5, [%0, #13<<3];"
+               "mov	sp, x5;"
+               // Return the value requested to return through arguments.
+               // This should be in x1 given what we requested above.
+               "cmp	%1, #0;"
+               "mov	x0, #1;"
+               "csel	x0, %1, x0, ne;"
+               "br	x30;"
+               : "+r"(env_address)
+               : "r"(retval_tmp));
+}
+
+INTERCEPTOR(void, siglongjmp, __hw_sigjmp_buf env, int val) {
+  if (env[0].__mask_was_saved)
+    // Restore the saved signal mask.
+    (void)sigprocmask(SIG_SETMASK, &env[0].__saved_mask,
+                      (__hw_sigset_t *)0);
+  InternalLongjmp(env[0].__jmpbuf, val);
+}
+
+// Required since glibc libpthread calls __libc_longjmp on pthread_exit, and
+// _setjmp on start_thread.  Hence we have to intercept the longjmp on
+// pthread_exit so the __hw_jmp_buf order matches.
+INTERCEPTOR(void, __libc_longjmp, __hw_jmp_buf env, int val) {
+  InternalLongjmp(env[0].__jmpbuf, val);
+}
+
+INTERCEPTOR(void, longjmp, __hw_jmp_buf env, int val) {
+  InternalLongjmp(env[0].__jmpbuf, val);
+}
+#undef SIG_BLOCK
+#undef SIG_SETMASK
+
+#endif // HWASAN_WITH_INTERCEPTORS && __aarch64__
+
 static void BeforeFork() {
   StackDepotLockAll();
 }
diff --git a/libsanitizer/hwasan/hwasan_setjmp.S b/libsanitizer/hwasan/hwasan_setjmp.S
new file mode 100644
index 0000000000000000000000000000000000000000..0c1354331940e23acad1ca4becba87199a211653
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_setjmp.S
@@ -0,0 +1,100 @@
+//===-- hwasan_setjmp.S --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer runtime.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_asm.h"
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+#include "sanitizer_common/sanitizer_platform.h"
+
+// We want to save the context of the calling function.
+// That requires
+// 1) No modification of the link register by this function.
+// 2) No modification of the stack pointer by this function.
+// 3) (no modification of any other saved register, but that's not really going
+// to occur, and hence isn't as much of a worry).
+//
+// There's essentially no way to ensure that the compiler will not modify the
+// stack pointer when compiling a C function.
+// Hence we have to write this function in assembly.
+
+.section .text
+.file "hwasan_setjmp.S"
+
+.global __interceptor_setjmp
+ASM_TYPE_FUNCTION(__interceptor_setjmp)
+__interceptor_setjmp:
+  CFI_STARTPROC
+  mov	x1, #0
+  b	__interceptor_sigsetjmp
+  CFI_ENDPROC
+ASM_SIZE(__interceptor_setjmp)
+
+#if SANITIZER_ANDROID
+// Bionic also defines a function `setjmp` that calls `sigsetjmp` saving the
+// current signal.
+.global __interceptor_setjmp_bionic
+ASM_TYPE_FUNCTION(__interceptor_setjmp_bionic)
+__interceptor_setjmp_bionic:
+  CFI_STARTPROC
+  mov	x1, #1
+  b	__interceptor_sigsetjmp
+  CFI_ENDPROC
+ASM_SIZE(__interceptor_setjmp_bionic)
+#endif
+
+.global __interceptor_sigsetjmp
+ASM_TYPE_FUNCTION(__interceptor_sigsetjmp)
+__interceptor_sigsetjmp:
+  CFI_STARTPROC
+  stp	x19, x20, [x0, #0<<3]
+  stp	x21, x22, [x0, #2<<3]
+  stp	x23, x24, [x0, #4<<3]
+  stp	x25, x26, [x0, #6<<3]
+  stp	x27, x28, [x0, #8<<3]
+  stp	x29, x30, [x0, #10<<3]
+  stp	 d8,  d9, [x0, #14<<3]
+  stp	d10, d11, [x0, #16<<3]
+  stp	d12, d13, [x0, #18<<3]
+  stp	d14, d15, [x0, #20<<3]
+  mov	x2,  sp
+  str	x2,  [x0, #13<<3]
+  // We always have the second argument to __sigjmp_save (savemask) set, since
+  // the _setjmp function above has set it for us as `false`.
+  // This function is defined in hwasan_interceptors.cc
+  b	__sigjmp_save
+  CFI_ENDPROC
+ASM_SIZE(__interceptor_sigsetjmp)
+
+
+.macro ALIAS first second
+  .globl \second
+  .equ \second\(), \first
+.endm
+
+#if SANITIZER_ANDROID
+ALIAS __interceptor_sigsetjmp, sigsetjmp
+.weak sigsetjmp
+
+ALIAS __interceptor_setjmp_bionic, setjmp
+.weak setjmp
+#else
+ALIAS __interceptor_sigsetjmp, __sigsetjmp
+.weak __sigsetjmp
+#endif
+
+ALIAS __interceptor_setjmp, _setjmp
+.weak _setjmp
+#endif
+
+// We do not need executable stack.
+NO_EXEC_STACK_DIRECTIVE
diff --git a/libsanitizer/hwasan/hwasan_type_test.cpp b/libsanitizer/hwasan/hwasan_type_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8cff495bae153eb728a9dc7d12e80be3bc976a85
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_type_test.cpp
@@ -0,0 +1,25 @@
+//===-- hwasan_type_test.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Compile-time tests of the internal type definitions.
+//===----------------------------------------------------------------------===//
+
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include "hwasan.h"
+#include <setjmp.h>
+
+#define CHECK_TYPE_SIZE_FITS(TYPE) \
+  COMPILER_CHECK(sizeof(__hw_##TYPE) <= sizeof(TYPE))
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+CHECK_TYPE_SIZE_FITS(jmp_buf);
+CHECK_TYPE_SIZE_FITS(sigjmp_buf);
+#endif


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 7/X] [libsanitizer] Add missing SANITIZER_INTERFACE_ATTRIBUTE on __hwasan_personality_wrapper
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (4 preceding siblings ...)
  2019-11-05 11:34   ` [PATCH 10/X] [libsanitizer] Tie the hwasan library into our build system Matthew Malcomson
@ 2019-11-05 11:34   ` Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 1/X][mid-end] Fix declared type of personality functions Matthew Malcomson
                     ` (23 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:34 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 1717 bytes --]

Backport from llvm upstream llvm-svn: 375298.

libsanitizer/ChangeLog:

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

	* hwasan/hwasan_exceptions.cpp (__hwasan_personality_wrapper):
	Add missing interface attribute.



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


diff --git a/libsanitizer/hwasan/hwasan_exceptions.cpp b/libsanitizer/hwasan/hwasan_exceptions.cpp
index 57a1438064cd28bb609359a4c841acf78337ebc3..169e7876cb58a9dafb70973ed9fb1dfd815a7ceb 100644
--- a/libsanitizer/hwasan/hwasan_exceptions.cpp
+++ b/libsanitizer/hwasan/hwasan_exceptions.cpp
@@ -32,10 +32,13 @@ typedef _Unwind_Reason_Code PersonalityFn(int version, _Unwind_Action actions,
 typedef _Unwind_Word GetGRFn(_Unwind_Context* context, int index);
 typedef _Unwind_Word GetCFAFn(_Unwind_Context* context);
 
-extern "C" _Unwind_Reason_Code __hwasan_personality_wrapper(
-    int version, _Unwind_Action actions, uint64_t exception_class,
-    _Unwind_Exception* unwind_exception, _Unwind_Context* context,
-    PersonalityFn* real_personality, GetGRFn* get_gr, GetCFAFn* get_cfa) {
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE _Unwind_Reason_Code
+__hwasan_personality_wrapper(int version, _Unwind_Action actions,
+                             uint64_t exception_class,
+                             _Unwind_Exception* unwind_exception,
+                             _Unwind_Context* context,
+                             PersonalityFn* real_personality, GetGRFn* get_gr,
+                             GetCFAFn* get_cfa) {
   _Unwind_Reason_Code rc;
   if (real_personality)
     rc = real_personality(version, actions, exception_class, unwind_exception,


[-- Attachment #2: hwasan-patch07.patch --]
[-- Type: text/plain, Size: 1364 bytes --]

diff --git a/libsanitizer/hwasan/hwasan_exceptions.cpp b/libsanitizer/hwasan/hwasan_exceptions.cpp
index 57a1438064cd28bb609359a4c841acf78337ebc3..169e7876cb58a9dafb70973ed9fb1dfd815a7ceb 100644
--- a/libsanitizer/hwasan/hwasan_exceptions.cpp
+++ b/libsanitizer/hwasan/hwasan_exceptions.cpp
@@ -32,10 +32,13 @@ typedef _Unwind_Reason_Code PersonalityFn(int version, _Unwind_Action actions,
 typedef _Unwind_Word GetGRFn(_Unwind_Context* context, int index);
 typedef _Unwind_Word GetCFAFn(_Unwind_Context* context);
 
-extern "C" _Unwind_Reason_Code __hwasan_personality_wrapper(
-    int version, _Unwind_Action actions, uint64_t exception_class,
-    _Unwind_Exception* unwind_exception, _Unwind_Context* context,
-    PersonalityFn* real_personality, GetGRFn* get_gr, GetCFAFn* get_cfa) {
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE _Unwind_Reason_Code
+__hwasan_personality_wrapper(int version, _Unwind_Action actions,
+                             uint64_t exception_class,
+                             _Unwind_Exception* unwind_exception,
+                             _Unwind_Context* context,
+                             PersonalityFn* real_personality, GetGRFn* get_gr,
+                             GetCFAFn* get_cfa) {
   _Unwind_Reason_Code rc;
   if (real_personality)
     rc = real_personality(version, actions, exception_class, unwind_exception,


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 8/X] [libsanitizer] Expose __hwasan_tag_mismatch_stub
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (8 preceding siblings ...)
  2019-11-05 11:34   ` [PATCH 4/X] [libsanitizer] libhwasan add longjmp & setjmp interceptors Matthew Malcomson
@ 2019-11-05 11:34   ` Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 12/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
                     ` (19 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:34 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 4363 bytes --]

Backport from llvm upstream (monorepo revision 612eadb).
This allows us to report tag mismatches without threading it through the
backend to generate assembly.

libsanitizer/ChangeLog:

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

	* hwasan/hwasan_interface_internal.h (__hwasan_tag_mismatch4):
	New exported symbol.
	* hwasan/hwasan_linux.cpp (__hwasan_tag_mismatch_stub): Rename
	to ...
	(__hwasan_tag_mismatch4): ... this.  Also add "size" argument.
	* hwasan/hwasan_tag_mismatch_aarch64.S: Update function call to
	use new name.



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


diff --git a/libsanitizer/hwasan/hwasan_interface_internal.h b/libsanitizer/hwasan/hwasan_interface_internal.h
index ca57f0fe437bfdbc3d490c1978985fc3ab64d4c5..aedda317497b61349050511a3d244f480fae5ba2 100644
--- a/libsanitizer/hwasan/hwasan_interface_internal.h
+++ b/libsanitizer/hwasan/hwasan_interface_internal.h
@@ -112,6 +112,10 @@ SANITIZER_INTERFACE_ATTRIBUTE
 void __hwasan_tag_mismatch(uptr addr, u8 ts);
 
 SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_tag_mismatch4(uptr addr, uptr access_info, uptr *registers_frame,
+                            size_t outsize);
+
+SANITIZER_INTERFACE_ATTRIBUTE
 u8 __hwasan_generate_tag();
 
 // Returns the offset of the first tag mismatch or -1 if the whole range is
diff --git a/libsanitizer/hwasan/hwasan_linux.cpp b/libsanitizer/hwasan/hwasan_linux.cpp
index 948e40154fec9295a451a3bc4e6a6914f619d6e3..dfef11883a284dae0c96cfcc6a8fd1cc06c24d71 100644
--- a/libsanitizer/hwasan/hwasan_linux.cpp
+++ b/libsanitizer/hwasan/hwasan_linux.cpp
@@ -460,21 +460,6 @@ static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) {
   return true;
 }
 
-// Entry point stub for interoperability between __hwasan_tag_mismatch (ASM) and
-// the rest of the mismatch handling code (C++).
-extern "C" void __hwasan_tag_mismatch_stub(uptr addr, uptr access_info,
-                                           uptr *registers_frame) {
-  AccessInfo ai;
-  ai.is_store = access_info & 0x10;
-  ai.recover = false;
-  ai.addr = addr;
-  ai.size = 1 << (access_info & 0xf);
-
-  HandleTagMismatch(ai, (uptr)__builtin_return_address(0),
-                    (uptr)__builtin_frame_address(0), nullptr, registers_frame);
-  __builtin_unreachable();
-}
-
 static void OnStackUnwind(const SignalContext &sig, const void *,
                           BufferedStackTrace *stack) {
   stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context,
@@ -493,4 +478,24 @@ void HwasanOnDeadlySignal(int signo, void *info, void *context) {
 
 } // namespace __hwasan
 
+// Entry point for interoperability between __hwasan_tag_mismatch (ASM) and the
+// rest of the mismatch handling code (C++).
+void __hwasan_tag_mismatch4(uptr addr, uptr access_info, uptr *registers_frame,
+                            size_t outsize) {
+  __hwasan::AccessInfo ai;
+  ai.is_store = access_info & 0x10;
+  ai.is_load = !ai.is_store;
+  ai.recover = access_info & 0x20;
+  ai.addr = addr;
+  if ((access_info & 0xf) == 0xf)
+    ai.size = outsize;
+  else
+    ai.size = 1 << (access_info & 0xf);
+
+  __hwasan::HandleTagMismatch(ai, (uptr)__builtin_return_address(0),
+                              (uptr)__builtin_frame_address(0), nullptr,
+                              registers_frame);
+  __builtin_unreachable();
+}
+
 #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
diff --git a/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S b/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S
index 92f62748048682f2e762a91050232fd3c65d538f..d1e0654cf715149d9ce6e3d8863ccd4f33745c95 100644
--- a/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S
+++ b/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S
@@ -89,12 +89,12 @@ __hwasan_tag_mismatch:
   stp     x4,  x5,  [sp, #32]
   stp     x2,  x3,  [sp, #16]
 
-  // Pass the address of the frame to __hwasan_tag_mismatch_stub, so that it can
+  // Pass the address of the frame to __hwasan_tag_mismatch4, so that it can
   // extract the saved registers from this frame without having to worry about
   // finding this frame.
   mov x2, sp
 
-  bl __hwasan_tag_mismatch_stub
+  bl __hwasan_tag_mismatch4
   CFI_ENDPROC
 
 .Lfunc_end0:


[-- Attachment #2: hwasan-patch08.patch --]
[-- Type: text/plain, Size: 3622 bytes --]

diff --git a/libsanitizer/hwasan/hwasan_interface_internal.h b/libsanitizer/hwasan/hwasan_interface_internal.h
index ca57f0fe437bfdbc3d490c1978985fc3ab64d4c5..aedda317497b61349050511a3d244f480fae5ba2 100644
--- a/libsanitizer/hwasan/hwasan_interface_internal.h
+++ b/libsanitizer/hwasan/hwasan_interface_internal.h
@@ -112,6 +112,10 @@ SANITIZER_INTERFACE_ATTRIBUTE
 void __hwasan_tag_mismatch(uptr addr, u8 ts);
 
 SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_tag_mismatch4(uptr addr, uptr access_info, uptr *registers_frame,
+                            size_t outsize);
+
+SANITIZER_INTERFACE_ATTRIBUTE
 u8 __hwasan_generate_tag();
 
 // Returns the offset of the first tag mismatch or -1 if the whole range is
diff --git a/libsanitizer/hwasan/hwasan_linux.cpp b/libsanitizer/hwasan/hwasan_linux.cpp
index 948e40154fec9295a451a3bc4e6a6914f619d6e3..dfef11883a284dae0c96cfcc6a8fd1cc06c24d71 100644
--- a/libsanitizer/hwasan/hwasan_linux.cpp
+++ b/libsanitizer/hwasan/hwasan_linux.cpp
@@ -460,21 +460,6 @@ static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) {
   return true;
 }
 
-// Entry point stub for interoperability between __hwasan_tag_mismatch (ASM) and
-// the rest of the mismatch handling code (C++).
-extern "C" void __hwasan_tag_mismatch_stub(uptr addr, uptr access_info,
-                                           uptr *registers_frame) {
-  AccessInfo ai;
-  ai.is_store = access_info & 0x10;
-  ai.recover = false;
-  ai.addr = addr;
-  ai.size = 1 << (access_info & 0xf);
-
-  HandleTagMismatch(ai, (uptr)__builtin_return_address(0),
-                    (uptr)__builtin_frame_address(0), nullptr, registers_frame);
-  __builtin_unreachable();
-}
-
 static void OnStackUnwind(const SignalContext &sig, const void *,
                           BufferedStackTrace *stack) {
   stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context,
@@ -493,4 +478,24 @@ void HwasanOnDeadlySignal(int signo, void *info, void *context) {
 
 } // namespace __hwasan
 
+// Entry point for interoperability between __hwasan_tag_mismatch (ASM) and the
+// rest of the mismatch handling code (C++).
+void __hwasan_tag_mismatch4(uptr addr, uptr access_info, uptr *registers_frame,
+                            size_t outsize) {
+  __hwasan::AccessInfo ai;
+  ai.is_store = access_info & 0x10;
+  ai.is_load = !ai.is_store;
+  ai.recover = access_info & 0x20;
+  ai.addr = addr;
+  if ((access_info & 0xf) == 0xf)
+    ai.size = outsize;
+  else
+    ai.size = 1 << (access_info & 0xf);
+
+  __hwasan::HandleTagMismatch(ai, (uptr)__builtin_return_address(0),
+                              (uptr)__builtin_frame_address(0), nullptr,
+                              registers_frame);
+  __builtin_unreachable();
+}
+
 #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
diff --git a/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S b/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S
index 92f62748048682f2e762a91050232fd3c65d538f..d1e0654cf715149d9ce6e3d8863ccd4f33745c95 100644
--- a/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S
+++ b/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S
@@ -89,12 +89,12 @@ __hwasan_tag_mismatch:
   stp     x4,  x5,  [sp, #32]
   stp     x2,  x3,  [sp, #16]
 
-  // Pass the address of the frame to __hwasan_tag_mismatch_stub, so that it can
+  // Pass the address of the frame to __hwasan_tag_mismatch4, so that it can
   // extract the saved registers from this frame without having to worry about
   // finding this frame.
   mov x2, sp
 
-  bl __hwasan_tag_mismatch_stub
+  bl __hwasan_tag_mismatch4
   CFI_ENDPROC
 
 .Lfunc_end0:


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 11/X] [libsanitizer] Only build libhwasan when targeting AArch64
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (6 preceding siblings ...)
  2019-11-05 11:34   ` [PATCH 1/X][mid-end] Fix declared type of personality functions Matthew Malcomson
@ 2019-11-05 11:34   ` Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 4/X] [libsanitizer] libhwasan add longjmp & setjmp interceptors Matthew Malcomson
                     ` (21 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:34 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 9744 bytes --]

Though the library has limited support for x86, we don't have any
support for generating code targeting x86 so there is no point building
for that target.

libsanitizer/ChangeLog:

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

	* Makefile.am: Condition building hwasan directory.
	* Makefile.in: Regenerate.
	* configure: Regenerate.
	* configure.ac: Set HWASAN_SUPPORTED based on target
	architecture.
	* configure.tgt: Likewise.



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


diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 2a7e8e1debe838719db0f0fad218b2543cc3111b..065a65e78d49f7689a01ecb64db1f07ca83aa987 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,7 +14,7 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan hwasan
+SUBDIRS += lsan asan ubsan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
@@ -23,6 +23,9 @@ nodist_saninclude_HEADERS += \
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
+if HWASAN_SUPPORTED
+SUBDIRS += hwasan
+endif
 endif
 
 ## May be used by toolexeclibdir.
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 404ddcedde5428e0bc6d8ebc5f6568f99741ce2a..3883c7d934884146763d2d751a7e88bdf31341fe 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -97,6 +97,7 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
+@HWASAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_5 = hwasan
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
@@ -207,7 +208,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan hwasan tsan
+	ubsan tsan hwasan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -364,7 +365,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	$(am__append_4) $(am__append_5)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/configure b/libsanitizer/configure
index 967efdb1f6f8ed30089ff73efc4e349a8bf9c1fc..f44fdb9485e7e00c2f3f98fac3f0e93a3ca6abb4 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -659,6 +659,8 @@ link_libubsan
 link_libtsan
 link_libhwasan
 link_libasan
+HWASAN_SUPPORTED_FALSE
+HWASAN_SUPPORTED_TRUE
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
 TSAN_SUPPORTED_FALSE
@@ -12347,7 +12349,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12350 "configure"
+#line 12352 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12453,7 +12455,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12456 "configure"
+#line 12458 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15804,6 +15806,7 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
  if test "x$TSAN_SUPPORTED" = "xyes"; then
   TSAN_SUPPORTED_TRUE=
@@ -15821,6 +15824,14 @@ else
   LSAN_SUPPORTED_FALSE=
 fi
 
+ if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  HWASAN_SUPPORTED_TRUE=
+  HWASAN_SUPPORTED_FALSE='#'
+else
+  HWASAN_SUPPORTED_TRUE='#'
+  HWASAN_SUPPORTED_FALSE=
+fi
+
 
 # Check for functions needed.
 for ac_func in clock_getres clock_gettime clock_settime lstat readlink
@@ -16803,7 +16814,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -16811,6 +16822,11 @@ if test "x$TSAN_SUPPORTED" = "xyes"; then
 
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  ac_config_files="$ac_config_files hwasan/Makefile"
+
+fi
+
 
 
 
@@ -17071,6 +17087,10 @@ if test -z "${LSAN_SUPPORTED_TRUE}" && test -z "${LSAN_SUPPORTED_FALSE}"; then
   as_fn_error $? "conditional \"LSAN_SUPPORTED\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HWASAN_SUPPORTED_TRUE}" && test -z "${HWASAN_SUPPORTED_FALSE}"; then
+  as_fn_error $? "conditional \"HWASAN_SUPPORTED\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${USING_MAC_INTERPOSE_TRUE}" && test -z "${USING_MAC_INTERPOSE_FALSE}"; then
   as_fn_error $? "conditional \"USING_MAC_INTERPOSE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -18080,9 +18100,9 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
-    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
@@ -20016,7 +20036,7 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
    sed -f vpsed$$ $ac_file > tmp$$
@@ -20027,18 +20047,18 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
-   sed -f vpsed$$ $ac_file > tmp$$
-   mv tmp$$ $ac_file
-   rm vpsed$$
-   echo 'MULTISUBDIR =' >> $ac_file
-   ml_norecursion=yes
-   . ${multi_basedir}/config-ml.in
-   { ml_norecursion=; unset ml_norecursion;}
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
     sed -f vpsed$$ $ac_file > tmp$$
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index 02c0c235171e4c8249821c5db91414f8e4bfb3ee..ba56e911ad59fc4f3aa88fa01ea4201b9549cd44 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -87,9 +87,11 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
 AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
 AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"])
+AM_CONDITIONAL(HWASAN_SUPPORTED, [test "x$HWASAN_SUPPORTED" = "xyes"])
 
 # Check for functions needed.
 AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime lstat readlink)
@@ -343,7 +345,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
@@ -371,6 +373,21 @@ _EOF
 ])
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  AC_CONFIG_FILES(AC_FOREACH([DIR], [hwasan], [DIR/Makefile ]), 
+    [cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    AS_UNSET([ml_norecursion])
+])
+fi
+
 AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS])
 AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS])
 
diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt
index 714f2923605011329b54be7e9fcf7af44a0593fd..92f4e9ff507b2972d1993ee288ea9ccef0103ec3 100644
--- a/libsanitizer/configure.tgt
+++ b/libsanitizer/configure.tgt
@@ -59,6 +59,7 @@ case "${target}" in
 		LSAN_SUPPORTED=yes
 		TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
 	fi
+	HWASAN_SUPPORTED=yes
 	;;
   x86_64-*-darwin[1]* | i?86-*-darwin[1]*)
 	TSAN_SUPPORTED=no


[-- Attachment #2: hwasan-patch11.patch --]
[-- Type: text/plain, Size: 8946 bytes --]

diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 2a7e8e1debe838719db0f0fad218b2543cc3111b..065a65e78d49f7689a01ecb64db1f07ca83aa987 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,7 +14,7 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan hwasan
+SUBDIRS += lsan asan ubsan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
@@ -23,6 +23,9 @@ nodist_saninclude_HEADERS += \
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
+if HWASAN_SUPPORTED
+SUBDIRS += hwasan
+endif
 endif
 
 ## May be used by toolexeclibdir.
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 404ddcedde5428e0bc6d8ebc5f6568f99741ce2a..3883c7d934884146763d2d751a7e88bdf31341fe 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -97,6 +97,7 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
+@HWASAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_5 = hwasan
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
@@ -207,7 +208,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan hwasan tsan
+	ubsan tsan hwasan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -364,7 +365,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	$(am__append_4) $(am__append_5)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/configure b/libsanitizer/configure
index 967efdb1f6f8ed30089ff73efc4e349a8bf9c1fc..f44fdb9485e7e00c2f3f98fac3f0e93a3ca6abb4 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -659,6 +659,8 @@ link_libubsan
 link_libtsan
 link_libhwasan
 link_libasan
+HWASAN_SUPPORTED_FALSE
+HWASAN_SUPPORTED_TRUE
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
 TSAN_SUPPORTED_FALSE
@@ -12347,7 +12349,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12350 "configure"
+#line 12352 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12453,7 +12455,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12456 "configure"
+#line 12458 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15804,6 +15806,7 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
  if test "x$TSAN_SUPPORTED" = "xyes"; then
   TSAN_SUPPORTED_TRUE=
@@ -15821,6 +15824,14 @@ else
   LSAN_SUPPORTED_FALSE=
 fi
 
+ if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  HWASAN_SUPPORTED_TRUE=
+  HWASAN_SUPPORTED_FALSE='#'
+else
+  HWASAN_SUPPORTED_TRUE='#'
+  HWASAN_SUPPORTED_FALSE=
+fi
+
 
 # Check for functions needed.
 for ac_func in clock_getres clock_gettime clock_settime lstat readlink
@@ -16803,7 +16814,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -16811,6 +16822,11 @@ if test "x$TSAN_SUPPORTED" = "xyes"; then
 
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  ac_config_files="$ac_config_files hwasan/Makefile"
+
+fi
+
 
 
 
@@ -17071,6 +17087,10 @@ if test -z "${LSAN_SUPPORTED_TRUE}" && test -z "${LSAN_SUPPORTED_FALSE}"; then
   as_fn_error $? "conditional \"LSAN_SUPPORTED\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HWASAN_SUPPORTED_TRUE}" && test -z "${HWASAN_SUPPORTED_FALSE}"; then
+  as_fn_error $? "conditional \"HWASAN_SUPPORTED\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${USING_MAC_INTERPOSE_TRUE}" && test -z "${USING_MAC_INTERPOSE_FALSE}"; then
   as_fn_error $? "conditional \"USING_MAC_INTERPOSE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -18080,9 +18100,9 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
-    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
@@ -20016,7 +20036,7 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
    sed -f vpsed$$ $ac_file > tmp$$
@@ -20027,18 +20047,18 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
-   sed -f vpsed$$ $ac_file > tmp$$
-   mv tmp$$ $ac_file
-   rm vpsed$$
-   echo 'MULTISUBDIR =' >> $ac_file
-   ml_norecursion=yes
-   . ${multi_basedir}/config-ml.in
-   { ml_norecursion=; unset ml_norecursion;}
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
     sed -f vpsed$$ $ac_file > tmp$$
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index 02c0c235171e4c8249821c5db91414f8e4bfb3ee..ba56e911ad59fc4f3aa88fa01ea4201b9549cd44 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -87,9 +87,11 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
 AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
 AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"])
+AM_CONDITIONAL(HWASAN_SUPPORTED, [test "x$HWASAN_SUPPORTED" = "xyes"])
 
 # Check for functions needed.
 AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime lstat readlink)
@@ -343,7 +345,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
@@ -371,6 +373,21 @@ _EOF
 ])
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  AC_CONFIG_FILES(AC_FOREACH([DIR], [hwasan], [DIR/Makefile ]), 
+    [cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    AS_UNSET([ml_norecursion])
+])
+fi
+
 AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS])
 AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS])
 
diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt
index 714f2923605011329b54be7e9fcf7af44a0593fd..92f4e9ff507b2972d1993ee288ea9ccef0103ec3 100644
--- a/libsanitizer/configure.tgt
+++ b/libsanitizer/configure.tgt
@@ -59,6 +59,7 @@ case "${target}" in
 		LSAN_SUPPORTED=yes
 		TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
 	fi
+	HWASAN_SUPPORTED=yes
 	;;
   x86_64-*-darwin[1]* | i?86-*-darwin[1]*)
 	TSAN_SUPPORTED=no


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 1/X][mid-end] Fix declared type of personality functions
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (5 preceding siblings ...)
  2019-11-05 11:34   ` [PATCH 7/X] [libsanitizer] Add missing SANITIZER_INTERFACE_ATTRIBUTE on __hwasan_personality_wrapper Matthew Malcomson
@ 2019-11-05 11:34   ` Matthew Malcomson
  2019-11-05 11:41     ` Richard Biener
  2019-11-05 11:34   ` [PATCH 11/X] [libsanitizer] Only build libhwasan when targeting AArch64 Matthew Malcomson
                     ` (22 subsequent siblings)
  29 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:34 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, rguenther, law, Martin Liska, Richard Earnshaw, Kyrylo Tkachov, ian

[-- Attachment #1: Type: text/plain, Size: 1502 bytes --]

`build_personality_function` generates a declaration for a personality
function.  The type it declares for these functions doesn't match the
type of the actual personality functions that are defined by the C++
unwinding ABI.

This doesn't cause any crashes since the compiler never generates a call
to these decl's, and hence the type of the function is never used.
Nonetheless, for the sake of consistency and readability we update the
type of this declaration.

(See libstdc++-v3/libsupc++/unwind-cxx.h for declaration of _gxx_personality_v0
to compare types).

gcc/ChangeLog:

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

	* expr.c (build_personality_function): Fix generated type to
	match actual personality functions.



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


diff --git a/gcc/expr.c b/gcc/expr.c
index 2f2b53f8b6905013b4214eea137d67c666b0c795..7dc37a288ebffb99c990442cf339b848c5fa9d2e 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -12525,7 +12525,8 @@ build_personality_function (const char *lang)
 
   name = ACONCAT (("__", lang, "_personality", unwind_and_version, NULL));
 
-  type = build_function_type_list (integer_type_node, integer_type_node,
+  type = build_function_type_list (unsigned_type_node,
+				   integer_type_node, integer_type_node,
 				   long_long_unsigned_type_node,
 				   ptr_type_node, ptr_type_node, NULL_TREE);
   decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL,


[-- Attachment #2: hwasan-patch00.patch --]
[-- Type: text/plain, Size: 637 bytes --]

diff --git a/gcc/expr.c b/gcc/expr.c
index 2f2b53f8b6905013b4214eea137d67c666b0c795..7dc37a288ebffb99c990442cf339b848c5fa9d2e 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -12525,7 +12525,8 @@ build_personality_function (const char *lang)
 
   name = ACONCAT (("__", lang, "_personality", unwind_and_version, NULL));
 
-  type = build_function_type_list (integer_type_node, integer_type_node,
+  type = build_function_type_list (unsigned_type_node,
+				   integer_type_node, integer_type_node,
 				   long_long_unsigned_type_node,
 				   ptr_type_node, ptr_type_node, NULL_TREE);
   decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL,


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 10/X] [libsanitizer] Tie the hwasan library into our build system
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (3 preceding siblings ...)
  2019-11-05 11:34   ` [PATCH 5/X] [libsanitizer] Remove system allocator fallback Matthew Malcomson
@ 2019-11-05 11:34   ` Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 7/X] [libsanitizer] Add missing SANITIZER_INTERFACE_ATTRIBUTE on __hwasan_personality_wrapper Matthew Malcomson
                     ` (24 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:34 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 51437 bytes --]

This patch does tries to tie libhwasan into the GCC build system in the
same way that the other sanitizer runtime libraries are handled.

libsanitizer/ChangeLog:

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

	* Makefile.am:  Build libhwasan.
	* Makefile.in:  Build libhwasan.
	* asan/Makefile.in:  Build libhwasan.
	* configure:  Build libhwasan.
	* configure.ac:  Build libhwasan.
	* hwasan/Makefile.am: New file.
	* hwasan/Makefile.in: New file.
	* hwasan/libtool-version: New file.
	* interception/Makefile.in: Build libhwasan.
	* libbacktrace/Makefile.in: Build libhwasan.
	* libsanitizer.spec.in: Build libhwasan.
	* lsan/Makefile.in: Build libhwasan.
	* merge.sh: Build libhwasan.
	* sanitizer_common/Makefile.in: Build libhwasan.
	* tsan/Makefile.in: Build libhwasan.
	* ubsan/Makefile.in: Build libhwasan.



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


diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 65ed1e712378ef453f820f86c4d3221f9dee5f2c..2a7e8e1debe838719db0f0fad218b2543cc3111b 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,11 +14,12 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan
+SUBDIRS += lsan asan ubsan hwasan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
-  include/sanitizer/tsan_interface.h
+  include/sanitizer/tsan_interface.h \
+  include/sanitizer/hwasan_interface.h
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 0d789b3a59d21ea2e5a23057ca3afe15425feec4..404ddcedde5428e0bc6d8ebc5f6568f99741ce2a 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -92,7 +92,8 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@am__append_1 = include/sanitizer/common_interface_defs.h \
 @SANITIZER_SUPPORTED_TRUE@	include/sanitizer/lsan_interface.h \
 @SANITIZER_SUPPORTED_TRUE@	include/sanitizer/asan_interface.h \
-@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/tsan_interface.h
+@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/tsan_interface.h \
+@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/hwasan_interface.h
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
@@ -206,7 +207,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan tsan
+	ubsan hwasan tsan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -328,6 +329,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
@@ -341,6 +343,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -361,7 +364,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	$(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in
index 00b6082da5372efd679ddc230f588bbc58161ef6..5ce6944a526418e484ee5925d2573248e168a736 100644
--- a/libsanitizer/asan/Makefile.in
+++ b/libsanitizer/asan/Makefile.in
@@ -382,6 +382,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
@@ -395,6 +396,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
diff --git a/libsanitizer/configure b/libsanitizer/configure
index 2d25147adbaaa3578388cf75763e6178302aa3dc..967efdb1f6f8ed30089ff73efc4e349a8bf9c1fc 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -657,6 +657,7 @@ USING_MAC_INTERPOSE_TRUE
 link_liblsan
 link_libubsan
 link_libtsan
+link_libhwasan
 link_libasan
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
@@ -779,6 +780,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -868,6 +870,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1120,6 +1123,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1257,7 +1269,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1410,6 +1422,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -7298,7 +7311,7 @@ irix5* | irix6* | nonstopux*)
   ;;
 
 # This must be Linux ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu)
+linux* | k*bsd*-gnu | kopensolaris*-gnu | uclinuxfdpiceabi)
   lt_cv_deplibs_check_method=pass_all
   ;;
 
@@ -10109,7 +10122,7 @@ _LT_EOF
       archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
       ;;
 
-    gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+    gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu | uclinuxfdpiceabi)
       tmp_diet=no
       if test "$host_os" = linux-dietlibc; then
 	case $cc_basename in
@@ -11640,7 +11653,12 @@ linux*oldld* | linux*aout* | linux*coff*)
   ;;
 
 # This must be Linux ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu)
+
+# uclinux* changes (here and below) have been submitted to the libtool
+# project, but have not yet been accepted: they are GCC-local changes
+# for the time being.  (See
+# https://lists.gnu.org/archive/html/libtool-patches/2018-05/msg00000.html)
+linux* | k*bsd*-gnu | kopensolaris*-gnu | uclinuxfdpiceabi)
   version_type=linux
   need_lib_prefix=no
   need_version=no
@@ -12329,7 +12347,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12332 "configure"
+#line 12350 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12435,7 +12453,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12438 "configure"
+#line 12456 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15300,7 +15318,12 @@ linux*oldld* | linux*aout* | linux*coff*)
   ;;
 
 # This must be Linux ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu)
+
+# uclinux* changes (here and below) have been submitted to the libtool
+# project, but have not yet been accepted: they are GCC-local changes
+# for the time being.  (See
+# https://lists.gnu.org/archive/html/libtool-patches/2018-05/msg00000.html)
+linux* | k*bsd*-gnu | kopensolaris*-gnu | uclinuxfdpiceabi)
   version_type=linux
   need_lib_prefix=no
   need_version=no
@@ -15906,6 +15929,10 @@ fi
 link_libasan=$link_sanitizer_common
 
 
+# Set up the set of additional libraries that we need to link against for libhwasan.
+link_libhwasan=$link_sanitizer_common
+
+
 # Set up the set of additional libraries that we need to link against for libtsan.
 link_libtsan=$link_sanitizer_common
 
@@ -16776,7 +16803,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -18053,6 +18080,7 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
 
@@ -19988,6 +20016,17 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+   sed -f vpsed$$ $ac_file > tmp$$
+   mv tmp$$ $ac_file
+   rm vpsed$$
+   echo 'MULTISUBDIR =' >> $ac_file
+   ml_norecursion=yes
+   . ${multi_basedir}/config-ml.in
+   { ml_norecursion=; unset ml_norecursion;}
+ ;;
     "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index 8aac33cb5c980db77e31e8d799ab5a12f3e3a404..02c0c235171e4c8249821c5db91414f8e4bfb3ee 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -111,6 +111,10 @@ AC_CHECK_LIB(dl, dlsym,
 link_libasan=$link_sanitizer_common
 AC_SUBST(link_libasan)
 
+# Set up the set of additional libraries that we need to link against for libhwasan.
+link_libhwasan=$link_sanitizer_common
+AC_SUBST(link_libhwasan)
+
 # Set up the set of additional libraries that we need to link against for libtsan.
 link_libtsan=$link_sanitizer_common
 AC_SUBST(link_libtsan)
@@ -339,7 +343,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
diff --git a/libsanitizer/hwasan/Makefile.am b/libsanitizer/hwasan/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..a20705ae1277b49d2edc464e23b106c5e6086d35
--- /dev/null
+++ b/libsanitizer/hwasan/Makefile.am
@@ -0,0 +1,88 @@
+AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
+ 
+# May be used by toolexeclibdir.
+gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
+
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -fno-ipa-icf
+AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+AM_CXXFLAGS += -std=gnu++11
+AM_CXXFLAGS += $(EXTRA_CXXFLAGS)
+ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
+
+toolexeclib_LTLIBRARIES = libhwasan.la
+
+hwasan_files = \
+	       hwasan_allocator.cpp \
+	       hwasan.cpp \
+	       hwasan_dynamic_shadow.cpp \
+	       hwasan_exceptions.cpp \
+	       hwasan_flags.inc \
+	       hwasan_interceptors.cpp \
+	       hwasan_interceptors_vfork.S \
+	       hwasan_linux.cpp \
+	       hwasan_memintrinsics.cpp \
+	       hwasan_new_delete.cpp \
+	       hwasan_poisoning.cpp \
+	       hwasan_report.cpp \
+	       hwasan_setjmp.S \
+	       hwasan_tag_mismatch_aarch64.S \
+	       hwasan_thread.cpp \
+	       hwasan_thread_list.cpp \
+	       hwasan_type_test.cpp
+
+libhwasan_la_SOURCES = $(hwasan_files)
+libhwasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la
+if !USING_MAC_INTERPOSE
+libhwasan_la_LIBADD += $(top_builddir)/interception/libinterception.la
+endif
+if LIBBACKTRACE_SUPPORTED
+libhwasan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
+endif
+libhwasan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+
+libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan)
+
+# Work around what appears to be a GNU make bug handling MAKEFLAGS
+# values defined in terms of make variables, as is the case for CC and
+# friends when we are called from the top level Makefile.
+AM_MAKEFLAGS = \
+	"AR_FLAGS=$(AR_FLAGS)" \
+	"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
+	"CFLAGS=$(CFLAGS)" \
+	"CXXFLAGS=$(CXXFLAGS)" \
+	"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
+	"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
+	"INSTALL=$(INSTALL)" \
+	"INSTALL_DATA=$(INSTALL_DATA)" \
+	"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
+	"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
+	"JC1FLAGS=$(JC1FLAGS)" \
+	"LDFLAGS=$(LDFLAGS)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
+	"MAKE=$(MAKE)" \
+	"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
+	"PICFLAG=$(PICFLAG)" \
+	"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
+	"SHELL=$(SHELL)" \
+	"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
+	"exec_prefix=$(exec_prefix)" \
+	"infodir=$(infodir)" \
+	"libdir=$(libdir)" \
+	"prefix=$(prefix)" \
+	"includedir=$(includedir)" \
+	"AR=$(AR)" \
+	"AS=$(AS)" \
+	"LD=$(LD)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"NM=$(NM)" \
+	"PICFLAG=$(PICFLAG)" \
+	"RANLIB=$(RANLIB)" \
+	"DESTDIR=$(DESTDIR)"
+
+MAKEOVERRIDES=
+
+## ################################################################
+
+
diff --git a/libsanitizer/hwasan/Makefile.in b/libsanitizer/hwasan/Makefile.in
new file mode 100644
index 0000000000000000000000000000000000000000..e31377e2b9322b683ecd51c5b44abe93bfc55b9d
--- /dev/null
+++ b/libsanitizer/hwasan/Makefile.in
@@ -0,0 +1,801 @@
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+@USING_MAC_INTERPOSE_FALSE@am__append_1 = $(top_builddir)/interception/libinterception.la
+@LIBBACKTRACE_SUPPORTED_TRUE@am__append_2 = $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
+subdir = hwasan
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
+	$(top_srcdir)/../config/depstand.m4 \
+	$(top_srcdir)/../config/lead-dot.m4 \
+	$(top_srcdir)/../config/libstdc++-raw-cxx.m4 \
+	$(top_srcdir)/../config/multi.m4 \
+	$(top_srcdir)/../config/override.m4 \
+	$(top_srcdir)/../config/stdint.m4 \
+	$(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \
+	$(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \
+	$(top_srcdir)/acinclude.m4 $(top_srcdir)/../libtool.m4 \
+	$(top_srcdir)/../config/enable.m4 \
+	$(top_srcdir)/../config/cet.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am
+mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(toolexeclibdir)"
+LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libhwasan_la_DEPENDENCIES =  \
+	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
+	$(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1)
+am__objects_1 = hwasan_allocator.lo hwasan.lo hwasan_dynamic_shadow.lo \
+	hwasan_exceptions.lo hwasan_interceptors.lo \
+	hwasan_interceptors_vfork.lo hwasan_linux.lo \
+	hwasan_memintrinsics.lo hwasan_new_delete.lo \
+	hwasan_poisoning.lo hwasan_report.lo hwasan_setjmp.lo \
+	hwasan_tag_mismatch_aarch64.lo hwasan_thread.lo \
+	hwasan_thread_list.lo hwasan_type_test.lo
+am_libhwasan_la_OBJECTS = $(am__objects_1)
+libhwasan_la_OBJECTS = $(am_libhwasan_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libhwasan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+	$(CXXFLAGS) $(libhwasan_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/../depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS)
+LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CCAS) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CCASFLAGS) $(CCASFLAGS)
+AM_V_CPPAS = $(am__v_CPPAS_@AM_V@)
+am__v_CPPAS_ = $(am__v_CPPAS_@AM_DEFAULT_V@)
+am__v_CPPAS_0 = @echo "  CPPAS   " $@;
+am__v_CPPAS_1 = 
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo "  CXX     " $@;
+am__v_CXX_1 = 
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+	$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo "  CXXLD   " $@;
+am__v_CXXLD_1 = 
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libhwasan_la_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+ACLOCAL = @ACLOCAL@
+ALLOC_FILE = @ALLOC_FILE@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BACKTRACE_SUPPORTED = @BACKTRACE_SUPPORTED@
+BACKTRACE_SUPPORTS_THREADS = @BACKTRACE_SUPPORTS_THREADS@
+BACKTRACE_USES_MALLOC = @BACKTRACE_USES_MALLOC@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXTRA_ASFLAGS = @EXTRA_ASFLAGS@
+EXTRA_CFLAGS = @EXTRA_CFLAGS@
+EXTRA_CXXFLAGS = @EXTRA_CXXFLAGS@
+FGREP = @FGREP@
+FORMAT_FILE = @FORMAT_FILE@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSTDCXX_RAW_CXX_CXXFLAGS = @LIBSTDCXX_RAW_CXX_CXXFLAGS@
+LIBSTDCXX_RAW_CXX_LDFLAGS = @LIBSTDCXX_RAW_CXX_LDFLAGS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
+SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS = @SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
+VERSION = @VERSION@
+VIEW_FILE = @VIEW_FILE@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+enable_shared = @enable_shared@
+enable_static = @enable_static@
+exec_prefix = @exec_prefix@
+get_gcc_base_ver = @get_gcc_base_ver@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
+link_liblsan = @link_liblsan@
+link_libtsan = @link_libtsan@
+link_libubsan = @link_libubsan@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+multi_basedir = @multi_basedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_noncanonical = @target_noncanonical@
+target_os = @target_os@
+target_vendor = @target_vendor@
+toolexecdir = @toolexecdir@
+toolexeclibdir = @toolexeclibdir@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
+
+# May be used by toolexeclibdir.
+gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
+	-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
+	-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
+	-Wno-variadic-macros -fno-ipa-icf \
+	$(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=gnu++11 $(EXTRA_CXXFLAGS)
+ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
+toolexeclib_LTLIBRARIES = libhwasan.la
+hwasan_files = \
+	       hwasan_allocator.cpp \
+	       hwasan.cpp \
+	       hwasan_dynamic_shadow.cpp \
+	       hwasan_exceptions.cpp \
+	       hwasan_flags.inc \
+	       hwasan_interceptors.cpp \
+	       hwasan_interceptors_vfork.S \
+	       hwasan_linux.cpp \
+	       hwasan_memintrinsics.cpp \
+	       hwasan_new_delete.cpp \
+	       hwasan_poisoning.cpp \
+	       hwasan_report.cpp \
+	       hwasan_setjmp.S \
+	       hwasan_tag_mismatch_aarch64.S \
+	       hwasan_thread.cpp \
+	       hwasan_thread_list.cpp \
+	       hwasan_type_test.cpp
+
+libhwasan_la_SOURCES = $(hwasan_files)
+libhwasan_la_LIBADD =  \
+	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
+	$(am__append_1) $(am__append_2) $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan)
+
+# Work around what appears to be a GNU make bug handling MAKEFLAGS
+# values defined in terms of make variables, as is the case for CC and
+# friends when we are called from the top level Makefile.
+AM_MAKEFLAGS = \
+	"AR_FLAGS=$(AR_FLAGS)" \
+	"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
+	"CFLAGS=$(CFLAGS)" \
+	"CXXFLAGS=$(CXXFLAGS)" \
+	"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
+	"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
+	"INSTALL=$(INSTALL)" \
+	"INSTALL_DATA=$(INSTALL_DATA)" \
+	"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
+	"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
+	"JC1FLAGS=$(JC1FLAGS)" \
+	"LDFLAGS=$(LDFLAGS)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
+	"MAKE=$(MAKE)" \
+	"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
+	"PICFLAG=$(PICFLAG)" \
+	"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
+	"SHELL=$(SHELL)" \
+	"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
+	"exec_prefix=$(exec_prefix)" \
+	"infodir=$(infodir)" \
+	"libdir=$(libdir)" \
+	"prefix=$(prefix)" \
+	"includedir=$(includedir)" \
+	"AR=$(AR)" \
+	"AS=$(AS)" \
+	"LD=$(LD)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"NM=$(NM)" \
+	"PICFLAG=$(PICFLAG)" \
+	"RANLIB=$(RANLIB)" \
+	"DESTDIR=$(DESTDIR)"
+
+MAKEOVERRIDES = 
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .S .cpp .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign hwasan/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign hwasan/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-toolexeclibLTLIBRARIES: $(toolexeclib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(toolexeclibdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(toolexeclibdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(toolexeclibdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(toolexeclibdir)"; \
+	}
+
+uninstall-toolexeclibLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(toolexeclibdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(toolexeclibdir)/$$f"; \
+	done
+
+clean-toolexeclibLTLIBRARIES:
+	-test -z "$(toolexeclib_LTLIBRARIES)" || rm -f $(toolexeclib_LTLIBRARIES)
+	@list='$(toolexeclib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libhwasan.la: $(libhwasan_la_OBJECTS) $(libhwasan_la_DEPENDENCIES) $(EXTRA_libhwasan_la_DEPENDENCIES) 
+	$(AM_V_CXXLD)$(libhwasan_la_LINK) -rpath $(toolexeclibdir) $(libhwasan_la_OBJECTS) $(libhwasan_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_allocator.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_dynamic_shadow.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_exceptions.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors_vfork.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_linux.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_memintrinsics.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_new_delete.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_poisoning.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_report.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_setjmp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_tag_mismatch_aarch64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread_list.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_type_test.Plo@am__quote@
+
+.S.o:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ $<
+
+.S.obj:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.S.lo:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(LTCPPASCOMPILE) -c -o $@ $<
+
+.cpp.o:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+	for dir in "$(DESTDIR)$(toolexeclibdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-toolexeclibLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-toolexeclibLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-toolexeclibLTLIBRARIES cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags dvi dvi-am html html-am info \
+	info-am install install-am install-data install-data-am \
+	install-dvi install-dvi-am install-exec install-exec-am \
+	install-html install-html-am install-info install-info-am \
+	install-man install-pdf install-pdf-am install-ps \
+	install-ps-am install-strip install-toolexeclibLTLIBRARIES \
+	installcheck installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am \
+	uninstall-toolexeclibLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libsanitizer/hwasan/libtool-version b/libsanitizer/hwasan/libtool-version
new file mode 100644
index 0000000000000000000000000000000000000000..204fdd2d8e5fe21a5a9f9e23d6a6cf17d353362a
--- /dev/null
+++ b/libsanitizer/hwasan/libtool-version
@@ -0,0 +1,6 @@
+# This file is used to maintain libtool version info for libmudflap.  See
+# the libtool manual to understand the meaning of the fields.  This is
+# a separate file so that version updates don't involve re-running
+# automake.
+# CURRENT:REVISION:AGE
+0:0:0
diff --git a/libsanitizer/interception/Makefile.in b/libsanitizer/interception/Makefile.in
index 358afe9d8844e411897eb24cd8f0960d0758e375..fa1e89177172f7109555d3b3f068b02dd7ebdf08 100644
--- a/libsanitizer/interception/Makefile.in
+++ b/libsanitizer/interception/Makefile.in
@@ -300,6 +300,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
@@ -313,6 +314,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
diff --git a/libsanitizer/libbacktrace/Makefile.in b/libsanitizer/libbacktrace/Makefile.in
index f0e190a81d82ddc86a3e39a5dc41e864190582c0..ba1bcc4b1a6d1ac27e76762219030b39c6242851 100644
--- a/libsanitizer/libbacktrace/Makefile.in
+++ b/libsanitizer/libbacktrace/Makefile.in
@@ -350,6 +350,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
@@ -363,6 +364,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
diff --git a/libsanitizer/libsanitizer.spec.in b/libsanitizer/libsanitizer.spec.in
index a4fa87d8dfc51549b0f932b31a610db1a2e86c29..70a33574d7b1d494c4875cfc41f29606bde2172e 100644
--- a/libsanitizer/libsanitizer.spec.in
+++ b/libsanitizer/libsanitizer.spec.in
@@ -3,6 +3,8 @@
 
 *link_libasan: @link_libasan@
 
+*link_libhwasan: @link_libhwasan@
+
 *link_libtsan: @link_libtsan@
 
 *link_libubsan: @link_libubsan@
diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in
index 3ebf4ba08f6ac0c323dcdd5e3c1b518fcbd7a67f..1bf23a31e106a294f56641adfd0f5ed619c810ec 100644
--- a/libsanitizer/lsan/Makefile.in
+++ b/libsanitizer/lsan/Makefile.in
@@ -345,6 +345,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
@@ -358,6 +359,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
diff --git a/libsanitizer/merge.sh b/libsanitizer/merge.sh
index 168fbbc9729d9873c4ca858f41db01e4f22cfedb..78a777f123d84b853dbfaa41a7da45c634ad7eca 100755
--- a/libsanitizer/merge.sh
+++ b/libsanitizer/merge.sh
@@ -69,6 +69,7 @@ CUR_REV=$(get_current_rev)
 echo Current upstream revision: $CUR_REV
 merge include/sanitizer include/sanitizer
 merge lib/asan asan
+merge lib/hwasan hwasan
 merge lib/lsan lsan
 merge lib/tsan/rtl tsan
 merge lib/sanitizer_common sanitizer_common
diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in
index 0fcebc89bc97da31a62bd58e7802d7a8d20007ec..f48fe312116b03689899b0574f883e032554aa89 100644
--- a/libsanitizer/sanitizer_common/Makefile.in
+++ b/libsanitizer/sanitizer_common/Makefile.in
@@ -336,6 +336,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
@@ -349,6 +350,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in
index cae00ab45ad6d01330d17cc5e55dddfffeae4a4b..b5bc5eaaccf714c526314e08488dc48cdc020a0e 100644
--- a/libsanitizer/tsan/Makefile.in
+++ b/libsanitizer/tsan/Makefile.in
@@ -374,6 +374,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
@@ -387,6 +388,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
diff --git a/libsanitizer/ubsan/Makefile.in b/libsanitizer/ubsan/Makefile.in
index 71d2626be63d11d2700d0fa729b1fae7956ee7da..348bd9e7ddd8081400732db4f0eb28f19a16222e 100644
--- a/libsanitizer/ubsan/Makefile.in
+++ b/libsanitizer/ubsan/Makefile.in
@@ -339,6 +339,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
@@ -352,6 +353,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@


[-- Attachment #2: hwasan-patch10.patch.gz --]
[-- Type: application/gzip, Size: 12203 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 3/X] [libsanitizer] libhwasan initialisation include kernel syscall ABI relaxation
  2019-11-05 11:32 ` Matthew Malcomson
@ 2019-11-05 11:34   ` Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 2/X] [libsanitizer] Introduce libhwasan to GCC tree Matthew Malcomson
                     ` (28 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:34 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 3416 bytes --]

Backported from LLVM-svn 375166.

libsanitizer/ChangeLog:

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

	* hwasan/hwasan.cc (InitInstrumentation): Call InitPrctl.
	* hwasan/hwasan.h (InitPrctl): New decl.
	* hwasan/hwasan_linux.cc (InitPrctl): New function.



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


diff --git a/libsanitizer/hwasan/hwasan.h b/libsanitizer/hwasan/hwasan.h
index 817cee65016ee60f5cf6b5dc716a30e192e51e73..9e0ced93b55d361cd5aae787db7562741683944c 100644
--- a/libsanitizer/hwasan/hwasan.h
+++ b/libsanitizer/hwasan/hwasan.h
@@ -74,6 +74,7 @@ extern int hwasan_report_count;
 
 bool ProtectRange(uptr beg, uptr end);
 bool InitShadow();
+void InitPrctl();
 void InitThreads();
 void MadviseShadow();
 char *GetProcSelfMaps();
diff --git a/libsanitizer/hwasan/hwasan.cpp b/libsanitizer/hwasan/hwasan.cpp
index 999b51183f6184bb6564f3ec2f51f437e2598314..36d931caf7d4091480e1fc183a09d68735008b97 100644
--- a/libsanitizer/hwasan/hwasan.cpp
+++ b/libsanitizer/hwasan/hwasan.cpp
@@ -312,6 +312,8 @@ static void InitLoadedGlobals() {
 static void InitInstrumentation() {
   if (hwasan_instrumentation_inited) return;
 
+  InitPrctl();
+
   if (!InitShadow()) {
     Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
     DumpProcessMap();
diff --git a/libsanitizer/hwasan/hwasan_linux.cpp b/libsanitizer/hwasan/hwasan_linux.cpp
index 051ec2fb9cc3aa333f4079dde6e5052173f84723..948e40154fec9295a451a3bc4e6a6914f619d6e3 100644
--- a/libsanitizer/hwasan/hwasan_linux.cpp
+++ b/libsanitizer/hwasan/hwasan_linux.cpp
@@ -34,6 +34,8 @@
 #include <sys/time.h>
 #include <unistd.h>
 #include <unwind.h>
+#include <sys/prctl.h>
+#include <errno.h>
 
 #include "sanitizer_common/sanitizer_common.h"
 #include "sanitizer_common/sanitizer_procmaps.h"
@@ -144,6 +146,43 @@ static void InitializeShadowBaseAddress(uptr shadow_size_bytes) {
       FindDynamicShadowStart(shadow_size_bytes);
 }
 
+void InitPrctl() {
+#define PR_SET_TAGGED_ADDR_CTRL 55
+#define PR_GET_TAGGED_ADDR_CTRL 56
+#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
+  // Check we're running on a kernel that can use the tagged address ABI.
+  if (internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0) == (uptr)-1 &&
+      errno == EINVAL) {
+#if SANITIZER_ANDROID
+    // Some older Android kernels have the tagged pointer ABI on
+    // unconditionally, and hence don't have the tagged-addr prctl while still
+    // allow the ABI.
+    // If targeting Android and the prctl is not around we assume this is the
+    // case.
+    return;
+#else
+    Printf(
+        "FATAL: "
+        "HWAddressSanitizer requires a kernel with tagged address ABI.\n");
+    Die();
+#endif
+  }
+
+  // Turn on the tagged address ABI.
+  if (internal_prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) ==
+          (uptr)-1 ||
+      !internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0)) {
+    Printf(
+        "FATAL: HWAddressSanitizer failed to enable tagged address syscall "
+        "ABI.\nSuggest check `sysctl abi.tagged_addr_disabled` "
+        "configuration.\n");
+    Die();
+  }
+#undef PR_SET_TAGGED_ADDR_CTRL
+#undef PR_GET_TAGGED_ADDR_CTRL
+#undef PR_TAGGED_ADDR_ENABLE
+}
+
 bool InitShadow() {
   // Define the entire memory range.
   kHighMemEnd = GetHighMemEnd();


[-- Attachment #2: hwasan-patch03.patch --]
[-- Type: text/plain, Size: 2959 bytes --]

diff --git a/libsanitizer/hwasan/hwasan.h b/libsanitizer/hwasan/hwasan.h
index 817cee65016ee60f5cf6b5dc716a30e192e51e73..9e0ced93b55d361cd5aae787db7562741683944c 100644
--- a/libsanitizer/hwasan/hwasan.h
+++ b/libsanitizer/hwasan/hwasan.h
@@ -74,6 +74,7 @@ extern int hwasan_report_count;
 
 bool ProtectRange(uptr beg, uptr end);
 bool InitShadow();
+void InitPrctl();
 void InitThreads();
 void MadviseShadow();
 char *GetProcSelfMaps();
diff --git a/libsanitizer/hwasan/hwasan.cpp b/libsanitizer/hwasan/hwasan.cpp
index 999b51183f6184bb6564f3ec2f51f437e2598314..36d931caf7d4091480e1fc183a09d68735008b97 100644
--- a/libsanitizer/hwasan/hwasan.cpp
+++ b/libsanitizer/hwasan/hwasan.cpp
@@ -312,6 +312,8 @@ static void InitLoadedGlobals() {
 static void InitInstrumentation() {
   if (hwasan_instrumentation_inited) return;
 
+  InitPrctl();
+
   if (!InitShadow()) {
     Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
     DumpProcessMap();
diff --git a/libsanitizer/hwasan/hwasan_linux.cpp b/libsanitizer/hwasan/hwasan_linux.cpp
index 051ec2fb9cc3aa333f4079dde6e5052173f84723..948e40154fec9295a451a3bc4e6a6914f619d6e3 100644
--- a/libsanitizer/hwasan/hwasan_linux.cpp
+++ b/libsanitizer/hwasan/hwasan_linux.cpp
@@ -34,6 +34,8 @@
 #include <sys/time.h>
 #include <unistd.h>
 #include <unwind.h>
+#include <sys/prctl.h>
+#include <errno.h>
 
 #include "sanitizer_common/sanitizer_common.h"
 #include "sanitizer_common/sanitizer_procmaps.h"
@@ -144,6 +146,43 @@ static void InitializeShadowBaseAddress(uptr shadow_size_bytes) {
       FindDynamicShadowStart(shadow_size_bytes);
 }
 
+void InitPrctl() {
+#define PR_SET_TAGGED_ADDR_CTRL 55
+#define PR_GET_TAGGED_ADDR_CTRL 56
+#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
+  // Check we're running on a kernel that can use the tagged address ABI.
+  if (internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0) == (uptr)-1 &&
+      errno == EINVAL) {
+#if SANITIZER_ANDROID
+    // Some older Android kernels have the tagged pointer ABI on
+    // unconditionally, and hence don't have the tagged-addr prctl while still
+    // allow the ABI.
+    // If targeting Android and the prctl is not around we assume this is the
+    // case.
+    return;
+#else
+    Printf(
+        "FATAL: "
+        "HWAddressSanitizer requires a kernel with tagged address ABI.\n");
+    Die();
+#endif
+  }
+
+  // Turn on the tagged address ABI.
+  if (internal_prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) ==
+          (uptr)-1 ||
+      !internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0)) {
+    Printf(
+        "FATAL: HWAddressSanitizer failed to enable tagged address syscall "
+        "ABI.\nSuggest check `sysctl abi.tagged_addr_disabled` "
+        "configuration.\n");
+    Die();
+  }
+#undef PR_SET_TAGGED_ADDR_CTRL
+#undef PR_GET_TAGGED_ADDR_CTRL
+#undef PR_TAGGED_ADDR_ENABLE
+}
+
 bool InitShadow() {
   // Define the entire memory range.
   kHighMemEnd = GetHighMemEnd();


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 12/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (9 preceding siblings ...)
  2019-11-05 11:34   ` [PATCH 8/X] [libsanitizer] Expose __hwasan_tag_mismatch_stub Matthew Malcomson
@ 2019-11-05 11:34   ` Matthew Malcomson
  2019-11-05 11:34   ` [aarch64] Allocate enough space for err_str in aarch64_handle_attr_branch_protection Matthew Malcomson
                     ` (18 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:34 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 7107 bytes --]

This is an analogous option to --bootstrap-asan to configure.  It allows
bootstrapping GCC using HWASAN.

For the same reasons as for ASAN we have to avoid using the HWASAN
sanitizer when compiling libiberty and the lto-plugin.

Also add a function to query whether -fsanitize=hwaddress has been
passed.

ChangeLog:

2019-08-29  Matthew Malcomson  <matthew.malcomson@arm.com>

	* configure: Regenerate.
	* configure.ac: Add --bootstrap-hwasan option.

config/ChangeLog:

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

	* bootstrap-hwasan.mk: New file.

libiberty/ChangeLog:

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

	* configure: Regenerate.
	* configure.ac: Avoid using sanitizer.

lto-plugin/ChangeLog:

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

	* Makefile.am: Avoid using sanitizer.
	* Makefile.in: Regenerate.



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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index aec9186b2b0123d3088b69eb1ee541567654953e..c1a2b7ed25b6b4f8edfd44ed9049270b7eec2317 100755
--- a/configure
+++ b/configure
@@ -754,6 +754,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -920,6 +921,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE}'
@@ -1172,6 +1174,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1309,7 +1320,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1469,6 +1480,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -7270,7 +7282,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index b8ce2ad20b9d03e42731252a9ec2a8417c13e566..16bfdf164555dad94c789f17b6a63ba1a2e3e9f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2775,7 +2775,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/libiberty/configure b/libiberty/configure
index 7a34dabec32b0b383bd33f07811757335f4dd39c..cb2dd4ff5295598343cc18b3a79a86a778f2261d 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5261,6 +5261,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index f1ce76010c9acde79c5dc46686a78b2e2f19244e..043237628b79cbf37d07359b59c5ffe17a7a22ef 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index 28dc21014b2e86988fa88adabd63ce6092e18e02..34aa397d785e3cc9b6975de460d065900364c3ff 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 8dd6e40ac9dddab39fe1752f9a70e6834ab3c926..7acfc047eff6f86f8d38287e6ffb6533c4c13500 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -672,8 +672,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


[-- Attachment #2: hwasan-patch12.patch --]
[-- Type: text/plain, Size: 5971 bytes --]

diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index aec9186b2b0123d3088b69eb1ee541567654953e..c1a2b7ed25b6b4f8edfd44ed9049270b7eec2317 100755
--- a/configure
+++ b/configure
@@ -754,6 +754,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -920,6 +921,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE}'
@@ -1172,6 +1174,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1309,7 +1320,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1469,6 +1480,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -7270,7 +7282,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index b8ce2ad20b9d03e42731252a9ec2a8417c13e566..16bfdf164555dad94c789f17b6a63ba1a2e3e9f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2775,7 +2775,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/libiberty/configure b/libiberty/configure
index 7a34dabec32b0b383bd33f07811757335f4dd39c..cb2dd4ff5295598343cc18b3a79a86a778f2261d 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5261,6 +5261,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index f1ce76010c9acde79c5dc46686a78b2e2f19244e..043237628b79cbf37d07359b59c5ffe17a7a22ef 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index 28dc21014b2e86988fa88adabd63ce6092e18e02..34aa397d785e3cc9b6975de460d065900364c3ff 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 8dd6e40ac9dddab39fe1752f9a70e6834ab3c926..7acfc047eff6f86f8d38287e6ffb6533c4c13500 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -672,8 +672,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 5/X] [libsanitizer] Remove system allocator fallback
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (2 preceding siblings ...)
  2019-11-05 11:34   ` [PATCH 6/X] [libsanitizer] Add hwasan_exceptions.cpp file Matthew Malcomson
@ 2019-11-05 11:34   ` Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 10/X] [libsanitizer] Tie the hwasan library into our build system Matthew Malcomson
                     ` (25 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:34 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 4323 bytes --]

Backport from llvm-svn: 375296.
This was an experiment made possible by a non-standard feature of the
Android dynamic loader.

Going without that experiment makes implementation for glibc easier.

libsanitizer/ChangeLog:

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

	* hwasan/hwasan_allocator.cpp (hwasan_realloc, hwasan_free,
	__hwasan_disable_allocator_tagging): Remove allocator fallback.
	* hwasan/hwasan_allocator.h (free, realloc): Remove REAL
	declarations.
	* hwasan/hwasan_interceptors.cpp (free, realloc): Remove
	interceptors.



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


diff --git a/libsanitizer/hwasan/hwasan_allocator.h b/libsanitizer/hwasan/hwasan_allocator.h
index 3a50a11f352600d82aaaf73ddd7a5595e9f8a4d5..f62be269602143679f9f7abc9ec4a8a08dad207f 100644
--- a/libsanitizer/hwasan/hwasan_allocator.h
+++ b/libsanitizer/hwasan/hwasan_allocator.h
@@ -13,7 +13,6 @@
 #ifndef HWASAN_ALLOCATOR_H
 #define HWASAN_ALLOCATOR_H
 
-#include "interception/interception.h"
 #include "sanitizer_common/sanitizer_allocator.h"
 #include "sanitizer_common/sanitizer_allocator_checks.h"
 #include "sanitizer_common/sanitizer_allocator_interface.h"
@@ -26,11 +25,6 @@
 #error Unsupported platform
 #endif
 
-#if HWASAN_WITH_INTERCEPTORS
-DECLARE_REAL(void *, realloc, void *ptr, uptr size)
-DECLARE_REAL(void, free, void *ptr)
-#endif
-
 namespace __hwasan {
 
 struct Metadata {
diff --git a/libsanitizer/hwasan/hwasan_allocator.cpp b/libsanitizer/hwasan/hwasan_allocator.cpp
index b4fae5820d0a1749663f251c7a3f1dc841741aed..81a57d3afd4d364dca96f20df3e94014483b6df1 100644
--- a/libsanitizer/hwasan/hwasan_allocator.cpp
+++ b/libsanitizer/hwasan/hwasan_allocator.cpp
@@ -22,11 +22,6 @@
 #include "hwasan_thread.h"
 #include "hwasan_report.h"
 
-#if HWASAN_WITH_INTERCEPTORS
-DEFINE_REAL(void *, realloc, void *ptr, uptr size)
-DEFINE_REAL(void, free, void *ptr)
-#endif
-
 namespace __hwasan {
 
 static Allocator allocator;
@@ -301,14 +296,6 @@ void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
 void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack) {
   if (!ptr)
     return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false));
-
-#if HWASAN_WITH_INTERCEPTORS
-  // A tag of 0 means that this is a system allocator allocation, so we must use
-  // the system allocator to realloc it.
-  if (!flags()->disable_allocator_tagging && GetTagFromPointer((uptr)ptr) == 0)
-    return REAL(realloc)(ptr, size);
-#endif
-
   if (size == 0) {
     HwasanDeallocate(stack, ptr);
     return nullptr;
@@ -381,13 +368,6 @@ int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
 }
 
 void hwasan_free(void *ptr, StackTrace *stack) {
-#if HWASAN_WITH_INTERCEPTORS
-  // A tag of 0 means that this is a system allocator allocation, so we must use
-  // the system allocator to free it.
-  if (!flags()->disable_allocator_tagging && GetTagFromPointer((uptr)ptr) == 0)
-    return REAL(free)(ptr);
-#endif
-
   return HwasanDeallocate(stack, ptr);
 }
 
@@ -400,15 +380,6 @@ void __hwasan_enable_allocator_tagging() {
 }
 
 void __hwasan_disable_allocator_tagging() {
-#if HWASAN_WITH_INTERCEPTORS
-  // Allocator tagging must be enabled for the system allocator fallback to work
-  // correctly. This means that we can't disable it at runtime if it was enabled
-  // at startup since that might result in our deallocations going to the system
-  // allocator. If tagging was disabled at startup we avoid this problem by
-  // disabling the fallback altogether.
-  CHECK(flags()->disable_allocator_tagging);
-#endif
-
   atomic_store_relaxed(&hwasan_allocator_tagging_enabled, 0);
 }
 
diff --git a/libsanitizer/hwasan/hwasan_interceptors.cpp b/libsanitizer/hwasan/hwasan_interceptors.cpp
index f6758efa65c051376468d3cad2c1530fa7329627..4f9bd3469eb10ca2cf3108326308e45e7a9d38b6 100644
--- a/libsanitizer/hwasan/hwasan_interceptors.cpp
+++ b/libsanitizer/hwasan/hwasan_interceptors.cpp
@@ -334,8 +334,6 @@ void InitializeInterceptors() {
 #if !defined(__aarch64__)
   INTERCEPT_FUNCTION(pthread_create);
 #endif  // __aarch64__
-  INTERCEPT_FUNCTION(realloc);
-  INTERCEPT_FUNCTION(free);
 #endif
 
   inited = 1;


[-- Attachment #2: hwasan-patch05.patch --]
[-- Type: text/plain, Size: 3562 bytes --]

diff --git a/libsanitizer/hwasan/hwasan_allocator.h b/libsanitizer/hwasan/hwasan_allocator.h
index 3a50a11f352600d82aaaf73ddd7a5595e9f8a4d5..f62be269602143679f9f7abc9ec4a8a08dad207f 100644
--- a/libsanitizer/hwasan/hwasan_allocator.h
+++ b/libsanitizer/hwasan/hwasan_allocator.h
@@ -13,7 +13,6 @@
 #ifndef HWASAN_ALLOCATOR_H
 #define HWASAN_ALLOCATOR_H
 
-#include "interception/interception.h"
 #include "sanitizer_common/sanitizer_allocator.h"
 #include "sanitizer_common/sanitizer_allocator_checks.h"
 #include "sanitizer_common/sanitizer_allocator_interface.h"
@@ -26,11 +25,6 @@
 #error Unsupported platform
 #endif
 
-#if HWASAN_WITH_INTERCEPTORS
-DECLARE_REAL(void *, realloc, void *ptr, uptr size)
-DECLARE_REAL(void, free, void *ptr)
-#endif
-
 namespace __hwasan {
 
 struct Metadata {
diff --git a/libsanitizer/hwasan/hwasan_allocator.cpp b/libsanitizer/hwasan/hwasan_allocator.cpp
index b4fae5820d0a1749663f251c7a3f1dc841741aed..81a57d3afd4d364dca96f20df3e94014483b6df1 100644
--- a/libsanitizer/hwasan/hwasan_allocator.cpp
+++ b/libsanitizer/hwasan/hwasan_allocator.cpp
@@ -22,11 +22,6 @@
 #include "hwasan_thread.h"
 #include "hwasan_report.h"
 
-#if HWASAN_WITH_INTERCEPTORS
-DEFINE_REAL(void *, realloc, void *ptr, uptr size)
-DEFINE_REAL(void, free, void *ptr)
-#endif
-
 namespace __hwasan {
 
 static Allocator allocator;
@@ -301,14 +296,6 @@ void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
 void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack) {
   if (!ptr)
     return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false));
-
-#if HWASAN_WITH_INTERCEPTORS
-  // A tag of 0 means that this is a system allocator allocation, so we must use
-  // the system allocator to realloc it.
-  if (!flags()->disable_allocator_tagging && GetTagFromPointer((uptr)ptr) == 0)
-    return REAL(realloc)(ptr, size);
-#endif
-
   if (size == 0) {
     HwasanDeallocate(stack, ptr);
     return nullptr;
@@ -381,13 +368,6 @@ int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
 }
 
 void hwasan_free(void *ptr, StackTrace *stack) {
-#if HWASAN_WITH_INTERCEPTORS
-  // A tag of 0 means that this is a system allocator allocation, so we must use
-  // the system allocator to free it.
-  if (!flags()->disable_allocator_tagging && GetTagFromPointer((uptr)ptr) == 0)
-    return REAL(free)(ptr);
-#endif
-
   return HwasanDeallocate(stack, ptr);
 }
 
@@ -400,15 +380,6 @@ void __hwasan_enable_allocator_tagging() {
 }
 
 void __hwasan_disable_allocator_tagging() {
-#if HWASAN_WITH_INTERCEPTORS
-  // Allocator tagging must be enabled for the system allocator fallback to work
-  // correctly. This means that we can't disable it at runtime if it was enabled
-  // at startup since that might result in our deallocations going to the system
-  // allocator. If tagging was disabled at startup we avoid this problem by
-  // disabling the fallback altogether.
-  CHECK(flags()->disable_allocator_tagging);
-#endif
-
   atomic_store_relaxed(&hwasan_allocator_tagging_enabled, 0);
 }
 
diff --git a/libsanitizer/hwasan/hwasan_interceptors.cpp b/libsanitizer/hwasan/hwasan_interceptors.cpp
index f6758efa65c051376468d3cad2c1530fa7329627..4f9bd3469eb10ca2cf3108326308e45e7a9d38b6 100644
--- a/libsanitizer/hwasan/hwasan_interceptors.cpp
+++ b/libsanitizer/hwasan/hwasan_interceptors.cpp
@@ -334,8 +334,6 @@ void InitializeInterceptors() {
 #if !defined(__aarch64__)
   INTERCEPT_FUNCTION(pthread_create);
 #endif  // __aarch64__
-  INTERCEPT_FUNCTION(realloc);
-  INTERCEPT_FUNCTION(free);
 #endif
 
   inited = 1;


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [aarch64] Allocate enough space for err_str in aarch64_handle_attr_branch_protection
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (10 preceding siblings ...)
  2019-11-05 11:34   ` [PATCH 12/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
@ 2019-11-05 11:34   ` Matthew Malcomson
  2019-11-05 11:38     ` Kyrylo Tkachov
  2019-11-05 11:35   ` [PATCH 16/X] [libsanitizer] Add tests Matthew Malcomson
                     ` (17 subsequent siblings)
  29 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:34 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, rguenther, James Greenhalgh, Martin Liska, Richard Earnshaw,
	Kyrylo Tkachov, Marcus Shawcroft

[-- Attachment #1: Type: text/plain, Size: 1281 bytes --]

-fsanitize=hwaddress found a one-byte overwrite when running the
testsuite here.  aarch64_handle_attr_branch_protection allocates
`strlen(str)` bytes for an error string, which is populated by
`strcpy(..., str)` in the case where the branch protection string is
completely invalid.

Tested on aarch64 with hwasan (though not a full bootstrap since it's
obvious).

gcc/ChangeLog:

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

	* config/aarch64/aarch64.c (aarch64_handle_attr_cpu): Allocate
	enough bytes for the NULL character.



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


diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 232317d4a5a4a16529f573eef5a8d7a068068207..fc03faa8f8d459a84024d4394fff375b72d31264 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -13298,7 +13298,7 @@ aarch64_handle_attr_cpu (const char *str)
  static bool
  aarch64_handle_attr_branch_protection (const char* str)
  {
-  char *err_str = (char *) xmalloc (strlen (str));
+  char *err_str = (char *) xmalloc (strlen (str) + 1);
   enum aarch64_parse_opt_result res = aarch64_parse_branch_protection (str,
 								      &err_str);
   bool success = false;


[-- Attachment #2: hwasan-patch01.patch --]
[-- Type: text/plain, Size: 618 bytes --]

diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 232317d4a5a4a16529f573eef5a8d7a068068207..fc03faa8f8d459a84024d4394fff375b72d31264 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -13298,7 +13298,7 @@ aarch64_handle_attr_cpu (const char *str)
  static bool
  aarch64_handle_attr_branch_protection (const char* str)
  {
-  char *err_str = (char *) xmalloc (strlen (str));
+  char *err_str = (char *) xmalloc (strlen (str) + 1);
   enum aarch64_parse_opt_result res = aarch64_parse_branch_protection (str,
 								      &err_str);
   bool success = false;


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 6/X] [libsanitizer] Add hwasan_exceptions.cpp file
  2019-11-05 11:32 ` Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 3/X] [libsanitizer] libhwasan initialisation include kernel syscall ABI relaxation Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 2/X] [libsanitizer] Introduce libhwasan to GCC tree Matthew Malcomson
@ 2019-11-05 11:34   ` Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 5/X] [libsanitizer] Remove system allocator fallback Matthew Malcomson
                     ` (26 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:34 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 3334 bytes --]

This is needed for the hwasan_personality instrumentation I've added.
Backported from llvm-svn: 369721

libsanitizer/ChangeLog:

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

	* hwasan/hwasan_exceptions.cpp: New file.



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


diff --git a/libsanitizer/hwasan/hwasan_exceptions.cpp b/libsanitizer/hwasan/hwasan_exceptions.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..57a1438064cd28bb609359a4c841acf78337ebc3
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_exceptions.cpp
@@ -0,0 +1,64 @@
+//===-- hwasan_exceptions.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer runtime.
+//===----------------------------------------------------------------------===//
+
+#include "hwasan_poisoning.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+#include <unwind.h>
+
+using namespace __hwasan;
+using namespace __sanitizer;
+
+typedef _Unwind_Reason_Code PersonalityFn(int version, _Unwind_Action actions,
+                                          uint64_t exception_class,
+                                          _Unwind_Exception* unwind_exception,
+                                          _Unwind_Context* context);
+
+// Pointers to the _Unwind_GetGR and _Unwind_GetCFA functions are passed in
+// instead of being called directly. This is to handle cases where the unwinder
+// is statically linked and the sanitizer runtime and the program are linked
+// against different unwinders. The _Unwind_Context data structure is opaque so
+// it may be incompatible between unwinders.
+typedef _Unwind_Word GetGRFn(_Unwind_Context* context, int index);
+typedef _Unwind_Word GetCFAFn(_Unwind_Context* context);
+
+extern "C" _Unwind_Reason_Code __hwasan_personality_wrapper(
+    int version, _Unwind_Action actions, uint64_t exception_class,
+    _Unwind_Exception* unwind_exception, _Unwind_Context* context,
+    PersonalityFn* real_personality, GetGRFn* get_gr, GetCFAFn* get_cfa) {
+  _Unwind_Reason_Code rc;
+  if (real_personality)
+    rc = real_personality(version, actions, exception_class, unwind_exception,
+                          context);
+  else
+    rc = _URC_CONTINUE_UNWIND;
+
+  // We only untag frames without a landing pad because landing pads are
+  // responsible for untagging the stack themselves if they resume.
+  //
+  // Here we assume that the frame record appears after any locals. This is not
+  // required by AAPCS but is a requirement for HWASAN instrumented functions.
+  if ((actions & _UA_CLEANUP_PHASE) && rc == _URC_CONTINUE_UNWIND) {
+#if defined(__x86_64__)
+    uptr fp = get_gr(context, 6); // rbp
+#elif defined(__aarch64__)
+    uptr fp = get_gr(context, 29); // x29
+#else
+#error Unsupported architecture
+#endif
+    uptr sp = get_cfa(context);
+    TagMemory(sp, fp - sp, 0);
+  }
+
+  return rc;
+}


[-- Attachment #2: hwasan-patch06.patch --]
[-- Type: text/plain, Size: 2931 bytes --]

diff --git a/libsanitizer/hwasan/hwasan_exceptions.cpp b/libsanitizer/hwasan/hwasan_exceptions.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..57a1438064cd28bb609359a4c841acf78337ebc3
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_exceptions.cpp
@@ -0,0 +1,64 @@
+//===-- hwasan_exceptions.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer runtime.
+//===----------------------------------------------------------------------===//
+
+#include "hwasan_poisoning.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+#include <unwind.h>
+
+using namespace __hwasan;
+using namespace __sanitizer;
+
+typedef _Unwind_Reason_Code PersonalityFn(int version, _Unwind_Action actions,
+                                          uint64_t exception_class,
+                                          _Unwind_Exception* unwind_exception,
+                                          _Unwind_Context* context);
+
+// Pointers to the _Unwind_GetGR and _Unwind_GetCFA functions are passed in
+// instead of being called directly. This is to handle cases where the unwinder
+// is statically linked and the sanitizer runtime and the program are linked
+// against different unwinders. The _Unwind_Context data structure is opaque so
+// it may be incompatible between unwinders.
+typedef _Unwind_Word GetGRFn(_Unwind_Context* context, int index);
+typedef _Unwind_Word GetCFAFn(_Unwind_Context* context);
+
+extern "C" _Unwind_Reason_Code __hwasan_personality_wrapper(
+    int version, _Unwind_Action actions, uint64_t exception_class,
+    _Unwind_Exception* unwind_exception, _Unwind_Context* context,
+    PersonalityFn* real_personality, GetGRFn* get_gr, GetCFAFn* get_cfa) {
+  _Unwind_Reason_Code rc;
+  if (real_personality)
+    rc = real_personality(version, actions, exception_class, unwind_exception,
+                          context);
+  else
+    rc = _URC_CONTINUE_UNWIND;
+
+  // We only untag frames without a landing pad because landing pads are
+  // responsible for untagging the stack themselves if they resume.
+  //
+  // Here we assume that the frame record appears after any locals. This is not
+  // required by AAPCS but is a requirement for HWASAN instrumented functions.
+  if ((actions & _UA_CLEANUP_PHASE) && rc == _URC_CONTINUE_UNWIND) {
+#if defined(__x86_64__)
+    uptr fp = get_gr(context, 6); // rbp
+#elif defined(__aarch64__)
+    uptr fp = get_gr(context, 29); // x29
+#else
+#error Unsupported architecture
+#endif
+    uptr sp = get_cfa(context);
+    TagMemory(sp, fp - sp, 0);
+  }
+
+  return rc;
+}


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 2/X] [libsanitizer] Introduce libhwasan to GCC tree
  2019-11-05 11:32 ` Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 3/X] [libsanitizer] libhwasan initialisation include kernel syscall ABI relaxation Matthew Malcomson
@ 2019-11-05 11:34   ` Matthew Malcomson
  2019-11-05 11:34   ` [PATCH 6/X] [libsanitizer] Add hwasan_exceptions.cpp file Matthew Malcomson
                     ` (27 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:34 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 1680 bytes --]

Takes the libhwasan library from LLVM and puts it into our source tree
excluding the build system files.

Tieing the source files into our build system is done in a later commit.

We have taken the libsanitizer library from the same SVN revision as
the other sanitizer libraries are taken from (SVN revision 368656 as
mentioned in libsanitizer/MERGE).

libsanitizer/ChangeLog:

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

	* README.gcc: Mention now including lib/hwasan.
	* hwasan/hwasan.cpp: New file.
	* hwasan/hwasan.h: New file.
	* hwasan/hwasan.syms.extra: New file.
	* hwasan/hwasan_allocator.cpp: New file.
	* hwasan/hwasan_allocator.h: New file.
	* hwasan/hwasan_blacklist.txt: New file.
	* hwasan/hwasan_checks.h: New file.
	* hwasan/hwasan_dynamic_shadow.cpp: New file.
	* hwasan/hwasan_dynamic_shadow.h: New file.
	* hwasan/hwasan_flags.h: New file.
	* hwasan/hwasan_flags.inc: New file.
	* hwasan/hwasan_interceptors.cpp: New file.
	* hwasan/hwasan_interceptors_vfork.S: New file.
	* hwasan/hwasan_interface_internal.h: New file.
	* hwasan/hwasan_linux.cpp: New file.
	* hwasan/hwasan_malloc_bisect.h: New file.
	* hwasan/hwasan_mapping.h: New file.
	* hwasan/hwasan_memintrinsics.cpp: New file.
	* hwasan/hwasan_new_delete.cpp: New file.
	* hwasan/hwasan_poisoning.cpp: New file.
	* hwasan/hwasan_poisoning.h: New file.
	* hwasan/hwasan_report.cpp: New file.
	* hwasan/hwasan_report.h: New file.
	* hwasan/hwasan_tag_mismatch_aarch64.S: New file.
	* hwasan/hwasan_thread.cpp: New file.
	* hwasan/hwasan_thread.h: New file.
	* hwasan/hwasan_thread_list.cpp: New file.
	* hwasan/hwasan_thread_list.h: New file.

[-- Attachment #2: hwasan-patch02.patch.gz --]
[-- Type: application/gzip, Size: 35370 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 15/X] [libsanitizer] Add hwasan pass and associated gimple changes
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (17 preceding siblings ...)
  2019-11-05 11:35   ` [PATCH 13/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
@ 2019-11-05 11:35   ` Matthew Malcomson
  2019-11-05 11:50   ` [PATCH 0/X] Introduce HWASAN sanitizer to GCC Matthew Malcomson
                     ` (10 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:35 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 43661 bytes --]

There are four main features to this change:

1) Check pointer tags match address tags.

In the new `hwasan` pass we put HWASAN_CHECK internal functions around
all memory accesses, to check that tags in the pointer being used match
the tag stored in shadow memory for the memory region being used.

These internal functions are expanded into actual checks in the sanopt
pass that happens just before expansion into RTL.

We use the same mechanism that currently inserts ASAN_CHECK internal
functions to insert the new HWASAN_CHECK functions.

2) Instrument known builtin function calls.

Handle all builtin functions that we know use memory accesses.
This commit uses the machinery added for ASAN to identify builtin
functions that access memory.

The main differences between the approaches for HWASAN and ASAN are:
 - libhwasan intercepts much less builtin functions.
 - Alloca needs to be transformed differently (instead of adding
   redzones it needs to colour shadow memory and return a tagged
   pointer).
 - stack_restore needs to uncolour the shadow stack between the current
   position and where it's going.
 - `noreturn` functions can not be handled by simply unpoisoning the
   entire shadow stack -- there is no "always valid" colour.
   (exceptions and things such as longjmp need to be handled in a
   different way).

For hardware implemented checking (such as AArch64's memory tagging
extension) alloca and stack_restore will need to be handled by hooks in
the backend rather than transformation at the gimple level.  This will
allow architecture specific handling of such stack modifications.

3) Introduce HWASAN block-scope poisoning

Here we use exactly the same mechanism as ASAN_MARK to poison/unpoison
variables on entry/exit of a block.

In order to simply use the exact same machinery we're using the same
internal functions until the SANOPT pass.  This means that all handling
of ASAN_MARK is the same.
This has the negative that the naming may be a little confusing, but a
positive that handling of the internal function doesn't have to be
duplicated for a function that behaves exactly the same but has a
different name.

gcc/ChangeLog:

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

	* asan.c (handle_builtin_stack_restore): Account for HWASAN.
	(handle_builtin_alloca): Account for HWASAN.
	(get_mem_refs_of_builtin_call): Special case strlen for HWASAN.
	(report_error_func): Assert not HWASAN.
	(build_check_stmt): Make HWASAN_CHECK instead of ASAN_CHECK.
	(instrument_derefs): HWASAN does not tag globals.
	(maybe_instrument_call): Don't instrument `noreturn` functions.
	(initialize_sanitizer_builtins): Add new type.
	(asan_expand_mark_ifn): Account for HWASAN.
	(asan_expand_check_ifn): Assert never called by HWASAN.
	(asan_expand_poison_ifn): Account for HWASAN.
	(hwasan_instrument): New.
	(hwasan_base): New.
	(hwasan_emit_uncolour_frame): Free block-scope-var hash map.
	(hwasan_check_func): New.
	(hwasan_expand_check_ifn): New.
	(hwasan_expand_mark_ifn): New.
	(gate_hwasan): New.
	(class pass_hwasan): New.
	(make_pass_hwasan): New.
	(class pass_hwasan_O0): New.
	(make_pass_hwasan_O0): New.
	* asan.h (hwasan_base): New decl.
	(hwasan_expand_check_ifn): New decl.
	(hwasan_expand_mark_ifn): New decl.
	(gate_hwasan): New decl.
	(enum hwasan_mark_flags): New.
	(asan_intercepted_p): Always false for hwasan.
	(asan_sanitize_use_after_scope): Account for HWASAN.
	* builtin-types.def (BT_FN_PTR_CONST_PTR_UINT8): New.
	* gimple-pretty-print.c (dump_gimple_call_args): Account for
	HWASAN.
	* gimplify.c (asan_poison_variable): Account for HWASAN.
	(gimplify_function_tree): Remove requirement of
	SANITIZE_ADDRESS, requiring asan or hwasan is accounted for in
	`asan_sanitize_use_after_scope`.
	* internal-fn.c (expand_HWASAN_CHECK): New.
	(expand_HWASAN_CHOOSE_COLOUR): New.
	(expand_HWASAN_MARK): New.
	* internal-fn.def (HWASAN_CHOOSE_COLOUR): New.
	(HWASAN_CHECK): New.
	(HWASAN_MARK): New.
	* passes.def: Add hwasan and hwasan_O0 passes.
	* sanitizer.def (BUILT_IN_HWASAN_LOAD1): New.
	(BUILT_IN_HWASAN_LOAD2): New.
	(BUILT_IN_HWASAN_LOAD4): New.
	(BUILT_IN_HWASAN_LOAD8): New.
	(BUILT_IN_HWASAN_LOAD16): New.
	(BUILT_IN_HWASAN_LOADN): New.
	(BUILT_IN_HWASAN_STORE1): New.
	(BUILT_IN_HWASAN_STORE2): New.
	(BUILT_IN_HWASAN_STORE4): New.
	(BUILT_IN_HWASAN_STORE8): New.
	(BUILT_IN_HWASAN_STORE16): New.
	(BUILT_IN_HWASAN_STOREN): New.
	(BUILT_IN_HWASAN_LOAD1_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD2_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD4_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD8_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD16_NOABORT): New.
	(BUILT_IN_HWASAN_LOADN_NOABORT): New.
	(BUILT_IN_HWASAN_STORE1_NOABORT): New.
	(BUILT_IN_HWASAN_STORE2_NOABORT): New.
	(BUILT_IN_HWASAN_STORE4_NOABORT): New.
	(BUILT_IN_HWASAN_STORE8_NOABORT): New.
	(BUILT_IN_HWASAN_STORE16_NOABORT): New.
	(BUILT_IN_HWASAN_STOREN_NOABORT): New.
	(BUILT_IN_HWASAN_TAG_MISMATCH4): New.
	(BUILT_IN_HWASAN_HANDLE_LONGJMP): New.
	(BUILT_IN_HWASAN_TAG_PTR): New.
	* sanopt.c (sanopt_optimize_walker): Act during hwasan.
	(pass_sanopt::execute): Act during hwasan.
	* toplev.c (compile_file): Use `gate_hwasan` function.
	* tree-pass.h (make_pass_hwasan): Declare passes.
	(make_pass_hwasan_O0): Declare passes.



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


diff --git a/gcc/asan.h b/gcc/asan.h
index 5f069690681cdb533a6ee8a3f8cd302a1866610a..c27d679c117a9cc7b15b7b4c6710cf0b46050089 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -31,10 +31,14 @@ extern rtx hwasan_with_tag (rtx, poly_int64);
 extern void hwasan_tag_init ();
 extern rtx hwasan_create_untagged_base (rtx);
 extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern rtx hwasan_base ();
 extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
 extern rtx_insn *hwasan_emit_uncolour_frame (rtx, rtx, rtx_insn *);
+extern bool hwasan_expand_check_ifn (gimple_stmt_iterator *, bool);
+extern bool hwasan_expand_mark_ifn (gimple_stmt_iterator *);
 extern bool memory_tagging_p (void);
 extern bool hwasan_sanitize_stack_p (void);
+extern bool gate_hwasan (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -131,6 +135,13 @@ enum asan_mark_flags
 #undef DEF
 };
 
+enum hwasan_mark_flags
+{
+#define DEF(X) HWASAN_MARK_##X
+  IFN_ASAN_MARK_FLAGS
+#undef DEF
+};
+
 /* Return true if STMT is ASAN_MARK with FLAG as first argument.  */
 extern bool asan_mark_p (gimple *stmt, enum asan_mark_flags flag);
 
@@ -180,6 +191,9 @@ extern hash_set<tree> *asan_handled_variables;
 static inline bool
 asan_intercepted_p (enum built_in_function fcode)
 {
+  if (memory_tagging_p ())
+    return false;
+
   return fcode == BUILT_IN_INDEX
 	 || fcode == BUILT_IN_MEMCHR
 	 || fcode == BUILT_IN_MEMCMP
@@ -208,7 +222,8 @@ asan_intercepted_p (enum built_in_function fcode)
 static inline bool
 asan_sanitize_use_after_scope (void)
 {
-  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
+  return (flag_sanitize_address_use_after_scope
+	  && (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ()));
 }
 
 /* Return true if DECL should be guarded on the stack.  */
diff --git a/gcc/asan.c b/gcc/asan.c
index fca06159b5bcaf349750103d401c12a965414dcc..edfbf2048b67d3dc7be78a8e9961152c4fb44902 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dojump.h"
 #include "explow.h"
 #include "expr.h"
+#include "except.h"
 #include "output.h"
 #include "langhooks.h"
 #include "cfgloop.h"
@@ -579,15 +580,28 @@ get_last_alloca_addr ()
 static void
 handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_stack_p ()))
     return;
 
-  tree last_alloca = get_last_alloca_addr ();
   tree restored_stack = gimple_call_arg (call, 0);
-  tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
-  gimple *g = gimple_build_call (fn, 2, last_alloca, restored_stack);
-  gsi_insert_before (iter, g, GSI_SAME_STMT);
-  g = gimple_build_assign (last_alloca, restored_stack);
+
+  gimple *g;
+
+  if (hwasan_sanitize_stack_p ())
+    {
+      tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_HANDLE_LONGJMP);
+      g = gimple_build_call (fn, 1, restored_stack);
+    }
+  else
+    {
+      tree last_alloca = get_last_alloca_addr ();
+      tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
+      g = gimple_build_call (fn, 2, last_alloca, restored_stack);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      g = gimple_build_assign (last_alloca, restored_stack);
+    }
+
   gsi_insert_before (iter, g, GSI_SAME_STMT);
 }
 
@@ -617,14 +631,12 @@ handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 static void
 handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_stack_p ()))
     return;
 
   gassign *g;
   gcall *gg;
-  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
-
-  tree last_alloca = get_last_alloca_addr ();
   tree callee = gimple_call_fndecl (call);
   tree old_size = gimple_call_arg (call, 0);
   tree ptr_type = gimple_call_lhs (call) ? TREE_TYPE (gimple_call_lhs (call))
@@ -634,6 +646,85 @@ handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
     = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
       ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
 
+  if (hwasan_sanitize_stack_p ())
+    {
+      /*
+	 HWASAN needs a different expansion.
+
+	 addr = __builtin_alloca (size, align);
+
+	 should be replaced by
+
+	 new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment;
+	 untagged_addr = __builtin_alloca (new_size, align);
+	 colour = __hwasan_choose_alloca_colour ();
+	 addr = __hwasan_tag_pointer (untagged_addr, colour);
+	 __hwasan_tag_memory (addr, colour, new_size);
+	*/
+      /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start on
+	 a tag granule.  */
+      align = align > HWASAN_TAG_GRANULE_SIZE ? align : HWASAN_TAG_GRANULE_SIZE;
+
+      /* tree new_size = (old_size + 15) & ~15;  */
+      uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
+      tree old_size = gimple_call_arg (call, 0);
+      tree tree_mask = build_int_cst (size_type_node, tg_mask);
+      g = gimple_build_assign (make_ssa_name (size_type_node), PLUS_EXPR,
+			       old_size, tree_mask);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree oversize = gimple_assign_lhs (g);
+
+      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_NOT_EXPR,
+			       tree_mask);
+      tree mask = gimple_assign_lhs (g);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+
+      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_AND_EXPR,
+			       oversize, mask);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree new_size = gimple_assign_lhs (g);
+
+      /* emit the alloca call */
+      tree fn = builtin_decl_implicit (BUILT_IN_ALLOCA_WITH_ALIGN);
+      gg = gimple_build_call (fn, 2, new_size,
+			      build_int_cst (size_type_node, align));
+      tree untagged_addr = make_ssa_name (ptr_type, gg);
+      gimple_call_set_lhs (gg, untagged_addr);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code choosing the tag.
+	 Here we use an internal function so we can choose the colour at expand
+	 time.  We need the decision to be made after stack variables have been
+	 assigned their colour (i.e. once the tag_offset variable has been set
+	 to one after the last stack variables tag).  */
+
+      gg = gimple_build_call_internal (IFN_HWASAN_CHOOSE_COLOUR, 0);
+      tree colour = make_ssa_name (unsigned_char_type_node, gg);
+      gimple_call_set_lhs (gg, colour);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code adding tag to pointer.  */
+      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_PTR);
+      gg = gimple_build_call (fn, 2, untagged_addr, colour);
+      tree addr = make_ssa_name (ptr_type, gg);
+      gimple_call_set_lhs (gg, addr);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code colouring shadow memory.
+	 NOTE: require using `untagged_addr` here for libhwasan API.  */
+      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MEM);
+      gg = gimple_build_call (fn, 3, untagged_addr, colour, new_size);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Finally, replace old alloca ptr with NEW_ALLOCA.  */
+      replace_call_with_value (iter, addr);
+      return;
+    }
+
+  tree last_alloca = get_last_alloca_addr ();
+  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
+
+
   /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
      bytes of allocated space.  Otherwise, align alloca to ASAN_RED_ZONE_SIZE
      manually.  */
@@ -786,6 +877,31 @@ get_mem_refs_of_builtin_call (gcall *call,
       break;
 
     case BUILT_IN_STRLEN:
+      /* Special case strlen here since its length is taken from its return
+	 value.
+
+	 The approach taken by the sanitizers is to check a memory access
+	 before it's taken.  For ASAN strlen is intercepted by libasan, so no
+	 check is inserted by the compiler.
+
+	 This function still returns `true` and provides a length to the rest
+	 of the ASAN pass in order to record what areas have been checked,
+	 avoiding superfluous checks later on.
+
+	 HWASAN does not intercept any of these internal functions.
+	 This means that checks for memory accesses must be inserted by the
+	 compiler.
+	 strlen is a special case, because we can tell the length from the
+	 return of the function, but that is not known until after the function
+	 has returned.
+
+	 Hence we can't check the memory access before it happens.
+	 We could check the memory access after it has already happened, but
+	 for now I'm choosing to just ignore `strlen` calls.
+	 This decision was simply made because that means the special case is
+	 limited to this one case of this one function.  */
+      if (memory_tagging_p ())
+	return false;
       source0 = gimple_call_arg (call, 0);
       len = gimple_call_lhs (call);
       break;
@@ -1848,6 +1964,8 @@ static tree
 report_error_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
 		   int *nargs)
 {
+  gcc_assert (!memory_tagging_p ());
+
   static enum built_in_function report[2][2][6]
     = { { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
 	    BUILT_IN_ASAN_REPORT_LOAD4, BUILT_IN_ASAN_REPORT_LOAD8,
@@ -2184,7 +2302,13 @@ build_check_stmt (location_t loc, tree base, tree len,
   if (is_scalar_access)
     flags |= ASAN_CHECK_SCALAR_ACCESS;
 
-  g = gimple_build_call_internal (IFN_ASAN_CHECK, 4,
+  enum internal_fn fn;
+  if (memory_tagging_p ())
+    fn = IFN_HWASAN_CHECK;
+  else
+    fn = IFN_ASAN_CHECK;
+
+  g = gimple_build_call_internal (fn, 4,
 				  build_int_cst (integer_type_node, flags),
 				  base, len,
 				  build_int_cst (integer_type_node,
@@ -2208,10 +2332,13 @@ static void
 instrument_derefs (gimple_stmt_iterator *iter, tree t,
 		   location_t location, bool is_store)
 {
-  if (is_store && !ASAN_INSTRUMENT_WRITES)
-    return;
-  if (!is_store && !ASAN_INSTRUMENT_READS)
-    return;
+  if (! memory_tagging_p ())
+    {
+    if (is_store && !ASAN_INSTRUMENT_WRITES)
+      return;
+    if (!is_store && !ASAN_INSTRUMENT_READS)
+      return;
+    }
 
   tree type, base;
   HOST_WIDE_INT size_in_bytes;
@@ -2271,7 +2398,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
     {
       if (DECL_THREAD_LOCAL_P (inner))
 	return;
-      if (!ASAN_GLOBALS && is_global_var (inner))
+      if ((memory_tagging_p () || !ASAN_GLOBALS)
+	  && is_global_var (inner))
         return;
       if (!TREE_STATIC (inner))
 	{
@@ -2500,10 +2628,13 @@ maybe_instrument_call (gimple_stmt_iterator *iter)
 	      break;
 	    }
 	}
-      tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
-      gimple *g = gimple_build_call (decl, 0);
-      gimple_set_location (g, gimple_location (stmt));
-      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      if (! memory_tagging_p ())
+	{
+	  tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
+	  gimple *g = gimple_build_call (decl, 0);
+	  gimple_set_location (g, gimple_location (stmt));
+	  gsi_insert_before (iter, g, GSI_SAME_STMT);
+	}
     }
 
   bool instrumented = false;
@@ -2902,6 +3033,9 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_PTR_CONST_PTR_UINT8
+    = build_function_type_list (ptr_type_node, const_ptr_type_node,
+				unsigned_char_type_node, NULL_TREE);
   tree BT_FN_VOID_PTR_UINT8_SIZE
     = build_function_type_list (void_type_node, ptr_type_node,
 				unsigned_char_type_node, size_type_node,
@@ -3237,6 +3371,23 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
   unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
   gcc_assert (size_in_bytes);
 
+  if (memory_tagging_p ())
+    {
+      gcc_assert (HWASAN_STACK);
+      /* Here we swap ASAN_MARK calls for HWASAN_MARK.
+	 This is because we are using the approach of using ASAN_MARK as a
+	 synonym until here.
+	 That approach means we don't yet have to duplicate all the special
+	 cases for ASAN_MARK and ASAN_POISON with the exact same handling but
+	 called HWASAN_MARK etc.  */
+      gimple *hw_poison_call
+	= gimple_build_call_internal (IFN_HWASAN_MARK, 3,
+				      gimple_call_arg (g, 0),
+				      base, len);
+      gsi_replace (iter, hw_poison_call, false);
+      return false;
+    }
+
   g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
 			   NOP_EXPR, base);
   gimple_set_location (g, loc);
@@ -3298,6 +3449,7 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 bool
 asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
 {
+  gcc_assert (!memory_tagging_p ());
   gimple *g = gsi_stmt (*iter);
   location_t loc = gimple_location (g);
   bool recover_p;
@@ -3571,11 +3723,37 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
 
       int nargs;
       bool store_p = gimple_call_internal_p (use, IFN_ASAN_POISON_USE);
-      tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
-				    &nargs);
-
-      gcall *call = gimple_build_call (fun, 1,
-				       build_fold_addr_expr (shadow_var));
+      gcall *call;
+      if (memory_tagging_p ())
+	{
+	  tree fun = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MISMATCH4);
+	  /* NOTE: hwasan has no __hwasan_report_* functions like asan does.
+		We use __hwasan_tag_mismatch4 with arguments that tell it the
+		size of access and load to report all tag mismatches.  */
+	  unsigned HOST_WIDE_INT size_in_bytes = tree_to_uhwi (size);
+	  unsigned size_indicator = (size_in_bytes > 16)
+	    ? 0xf
+	    : exact_log2 (size_in_bytes);
+	  unsigned access_info = (0x20 * recover_p)
+	    + (0x10 * store_p)
+	    + (size_indicator);
+	  tree long_pointer_type
+	    = build_pointer_type (long_long_unsigned_type_node);
+	  call = gimple_build_call (fun, 3,
+				    build_fold_addr_expr (shadow_var),
+				    build_int_cst (long_long_unsigned_type_node,
+						   access_info),
+				    build_int_cst (long_pointer_type,
+						   0),
+				    size);
+	}
+      else
+	{
+	  tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
+					&nargs);
+	  call = gimple_build_call (fun, 1,
+				    build_fold_addr_expr (shadow_var));
+	}
       gimple_set_location (call, gimple_location (use));
       gimple *call_to_insert = call;
 
@@ -3709,6 +3887,16 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/*  HWASAN  */
+static unsigned int
+hwasan_instrument (void)
+{
+  transform_statements ();
+  last_alloca_addr = NULL_TREE;
+
+  return 0;
+}
+
 void
 hwasan_record_base (rtx base)
 {
@@ -3727,6 +3915,15 @@ hwasan_current_tag ()
   return tag_offset;
 }
 
+rtx
+hwasan_base ()
+{
+  if (! hwasan_base_ptr)
+    hwasan_record_base (gen_reg_rtx (Pmode));
+
+  return hwasan_base_ptr;
+}
+
 void
 hwasan_increment_tag ()
 {
@@ -3858,6 +4055,12 @@ hwasan_emit_uncolour_frame (rtx dynamic, rtx vars, rtx_insn *before)
   do_pending_stack_adjust ();
   rtx_insn *insns = get_insns ();
   end_sequence ();
+
+  /* Clear the hash_map recording which variables are handled by HWASAN_MARK.
+     The only use in HWASAN is to decide which variables need to be coloured in
+     the prologue and which don't.  */
+  delete asan_handled_variables;
+  asan_handled_variables = NULL;
   return insns;
 }
 
@@ -3894,4 +4097,214 @@ hwasan_finish_file (void)
   flag_sanitize |= SANITIZE_HWADDRESS;
 }
 
+/* Construct a function tree for __hwasan_{load,store}{1,2,4,8,16,_n}.
+   IS_STORE is either 1 (for a store) or 0 (for a load).  */
+
+static tree
+hwasan_check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
+		   int *nargs)
+{
+  static enum built_in_function check[2][2][6]
+    = { { { BUILT_IN_HWASAN_LOAD1, BUILT_IN_HWASAN_LOAD2,
+	    BUILT_IN_HWASAN_LOAD4, BUILT_IN_HWASAN_LOAD8,
+	    BUILT_IN_HWASAN_LOAD16, BUILT_IN_HWASAN_LOADN },
+	  { BUILT_IN_HWASAN_STORE1, BUILT_IN_HWASAN_STORE2,
+	    BUILT_IN_HWASAN_STORE4, BUILT_IN_HWASAN_STORE8,
+	    BUILT_IN_HWASAN_STORE16, BUILT_IN_HWASAN_STOREN } },
+	{ { BUILT_IN_HWASAN_LOAD1_NOABORT,
+	    BUILT_IN_HWASAN_LOAD2_NOABORT,
+	    BUILT_IN_HWASAN_LOAD4_NOABORT,
+	    BUILT_IN_HWASAN_LOAD8_NOABORT,
+	    BUILT_IN_HWASAN_LOAD16_NOABORT,
+	    BUILT_IN_HWASAN_LOADN_NOABORT },
+	  { BUILT_IN_HWASAN_STORE1_NOABORT,
+	    BUILT_IN_HWASAN_STORE2_NOABORT,
+	    BUILT_IN_HWASAN_STORE4_NOABORT,
+	    BUILT_IN_HWASAN_STORE8_NOABORT,
+	    BUILT_IN_HWASAN_STORE16_NOABORT,
+	    BUILT_IN_HWASAN_STOREN_NOABORT } } };
+  if (size_in_bytes == -1)
+    {
+      *nargs = 2;
+      return builtin_decl_implicit (check[recover_p][is_store][5]);
+    }
+  *nargs = 1;
+  int size_log2 = exact_log2 (size_in_bytes);
+  return builtin_decl_implicit (check[recover_p][is_store][size_log2]);
+}
+
+
+bool
+hwasan_expand_check_ifn (gimple_stmt_iterator *iter, bool)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  bool recover_p;
+  if (flag_sanitize & SANITIZE_USER_HWADDRESS)
+    recover_p = (flag_sanitize_recover & SANITIZE_USER_HWADDRESS) != 0;
+  else
+    recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_HWADDRESS) != 0;
+
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_CHECK_LAST);
+  bool is_scalar_access = (flags & ASAN_CHECK_SCALAR_ACCESS) != 0;
+  bool is_store = (flags & ASAN_CHECK_STORE) != 0;
+  bool is_non_zero_len = (flags & ASAN_CHECK_NON_ZERO_LEN) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  tree len = gimple_call_arg (g, 2);
+
+  /* `align` is unused for HWASAN_CHECK, but I pass the argument anyway
+     since that way the arguments match ASAN_CHECK.  */
+  /* HOST_WIDE_INT align = tree_to_shwi (gimple_call_arg (g, 3));  */
+
+  unsigned HOST_WIDE_INT size_in_bytes
+    = is_scalar_access && tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
+
+  gimple_stmt_iterator gsi = *iter;
+
+  if (!is_non_zero_len)
+    {
+      /* So, the length of the memory area to hwasan-protect is
+	 non-constant.  Let's guard the generated instrumentation code
+	 like:
+
+	 if (len != 0)
+	   {
+	     // hwasan instrumentation code goes here.
+	   }
+	 // falltrough instructions, starting with *ITER.  */
+
+      g = gimple_build_cond (NE_EXPR,
+			    len,
+			    build_int_cst (TREE_TYPE (len), 0),
+			    NULL_TREE, NULL_TREE);
+      gimple_set_location (g, loc);
+
+      basic_block then_bb, fallthrough_bb;
+      insert_if_then_before_iter (as_a <gcond *> (g), iter,
+				  /*then_more_likely_p=*/true,
+				  &then_bb, &fallthrough_bb);
+      /* Note that fallthrough_bb starts with the statement that was
+	pointed to by ITER.  */
+
+      /* The 'then block' of the 'if (len != 0) condition is where
+	we'll generate the hwasan instrumentation code now.  */
+      gsi = gsi_last_bb (then_bb);
+    }
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			   NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+  tree base_addr = gimple_assign_lhs (g);
+
+  int nargs = 0;
+  tree fun = hwasan_check_func (is_store, recover_p, size_in_bytes, &nargs);
+  if (nargs == 1)
+    g = gimple_build_call (fun, 1, base_addr);
+  else
+    {
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			       NOP_EXPR, len);
+      gimple_set_location (g, loc);
+      gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+      g = gimple_build_call (fun, nargs, base_addr, sz_arg);
+    }
+
+  gimple_set_location (g, loc);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+  gsi_remove (iter, true);
+  *iter = gsi;
+  return false;
+}
+
+bool
+hwasan_expand_mark_ifn (gimple_stmt_iterator *)
+{
+  /* HWASAN_MARK should only ever be available after the sanopt pass.  */
+  gcc_unreachable ();
+}
+
+bool
+gate_hwasan ()
+{
+  return memory_tagging_p ();
+}
+
+namespace {
+
+const pass_data pass_data_hwasan =
+{
+  GIMPLE_PASS, /* type */
+  "hwasan", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_hwasan : public gimple_opt_pass
+{
+public:
+  pass_hwasan (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_hwasan, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_hwasan (m_ctxt); }
+  virtual bool gate (function *) { return gate_hwasan (); }
+  virtual unsigned int execute (function *) { return hwasan_instrument (); }
+
+}; /* class pass_asan  */
+
+} /* anon namespace  */
+
+gimple_opt_pass *
+make_pass_hwasan (gcc::context *ctxt)
+{
+  return new pass_hwasan (ctxt);
+}
+
+namespace {
+
+const pass_data pass_data_hwasan_O0 =
+{
+  GIMPLE_PASS, /* type */
+  "hwasan_O0", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_hwasan_O0 : public gimple_opt_pass
+{
+public:
+  pass_hwasan_O0 (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_hwasan_O0, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_hwasan_O0 (m_ctxt); }
+  virtual bool gate (function *) { return !optimize && gate_hwasan (); }
+  virtual unsigned int execute (function *) { return hwasan_instrument (); }
+
+}; // class pass_asan
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_hwasan_O0 (gcc::context *ctxt)
+{
+  return new pass_hwasan_O0 (ctxt);
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index d05f597b6434f39fe95d4f28dd2ef3ed463dd925..00592b1eea76164471e281c8922893937cf9bb2e 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -493,6 +493,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_INT_FEXCEPT_T_PTR_INT, BT_INT, BT_FEXCEPT_T_PTR,
 		     BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_FEXCEPT_T_PTR_INT, BT_INT,
 		     BT_CONST_FEXCEPT_T_PTR, BT_INT)
+DEF_FUNCTION_TYPE_2 (BT_FN_PTR_CONST_PTR_UINT8, BT_PTR, BT_CONST_PTR, BT_UINT8)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR)
 
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 2d5ece0680538389118b909951863be50d570326..dba1d58b83a1caf657cbfdd309a3576442fd59d0 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -750,6 +750,7 @@ dump_gimple_call_args (pretty_printer *buffer, gcall *gs, dump_flags_t flags)
 	  limit = ARRAY_SIZE (reduction_args);
 	  break;
 
+	case IFN_HWASAN_MARK:
 	case IFN_ASAN_MARK:
 #define DEF(X) #X
 	  static const char *const asan_mark_args[] = {IFN_ASAN_MARK_FLAGS};
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index daa0b71c191e6d92387180e927b818517b8b58a7..18086416c0f5ac089bdeced6bc9554730c06e5bd 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1230,8 +1230,11 @@ asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
 
   /* It's necessary to have all stack variables aligned to ASAN granularity
      bytes.  */
-  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
-    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+  gcc_assert (!memory_tagging_p () || hwasan_sanitize_stack_p ());
+  unsigned shadow_granularity
+    = memory_tagging_p () ? HWASAN_TAG_GRANULE_SIZE : ASAN_SHADOW_GRANULARITY;
+  if (DECL_ALIGN_UNIT (decl) <= shadow_granularity)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * shadow_granularity);
 
   HOST_WIDE_INT flags = poison ? ASAN_MARK_POISON : ASAN_MARK_UNPOISON;
 
@@ -14520,7 +14523,7 @@ gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
-  if (asan_sanitize_use_after_scope () && sanitize_flags_p (SANITIZE_ADDRESS))
+  if (asan_sanitize_use_after_scope ())
     asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
   if (asan_poisoned_variables)
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 549d6f1153b6e568d1ab8b0e5b8fa8ccc2161b61..c692ae86ec6b5fbe345558d7f412f6ecd666bfa1 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -457,6 +457,74 @@ expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
+expand_HWASAN_CHECK (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
+
+static void
+expand_HWASAN_CHOOSE_COLOUR (internal_fn, gcall *gc)
+{
+  tree colour = gimple_call_lhs (gc);
+  rtx target = expand_expr (colour, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  machine_mode mode = GET_MODE (target);
+  gcc_assert (mode == QImode);
+
+  rtx base_tag = hwasan_extract_tag (hwasan_base ());
+  gcc_assert (base_tag);
+  rtx tag_offset = GEN_INT (hwasan_current_tag ());
+  rtx chosen_tag = expand_simple_binop (QImode, PLUS, base_tag, tag_offset,
+					target, /* unsignedp = */1,
+					OPTAB_WIDEN);
+
+  gcc_assert (chosen_tag);
+  /* Really need to put the tag into the `target` RTX.  */
+  if (chosen_tag != target)
+    {
+      rtx temp = chosen_tag;
+      machine_mode ret_mode = GET_MODE (chosen_tag);
+      if (ret_mode != mode)
+	temp = simplify_gen_unary (TRUNCATE, mode, chosen_tag, ret_mode);
+
+      emit_move_insn (target, temp);
+    }
+
+  hwasan_increment_tag ();
+}
+
+static void
+expand_HWASAN_MARK (internal_fn, gcall *gc)
+{
+  HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
+  bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
+
+  tree base = gimple_call_arg (gc, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  rtx base_rtx = expand_normal (base);
+
+  rtx tag = is_poison ? const0_rtx : hwasan_extract_tag (base_rtx);
+  rtx address = hwasan_create_untagged_base (base_rtx);
+
+  tree len = gimple_call_arg (gc, 2);
+  gcc_assert (tree_fits_shwi_p (len));
+  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
+  gcc_assert (size_in_bytes);
+  size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
+  rtx size = gen_int_mode (size_in_bytes, Pmode);
+
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (func,
+      LCT_NORMAL,
+      VOIDmode,
+      address, ptr_mode,
+      tag, QImode,
+      size, ptr_mode);
+}
+
+/* This should get expanded in the sanopt pass.  */
+
+static void
 expand_ASAN_CHECK (internal_fn, gcall *)
 {
   gcc_unreachable ();
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 49f57978c88a3a8c1a0206d983e1720ed09b0385..917813ca87218aa8cb9127658b9f81057fabdcc4 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -301,6 +301,9 @@ DEF_INTERNAL_FN (UBSAN_PTR, ECF_LEAF | ECF_NOTHROW, ".R.")
 DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
 DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (HWASAN_CHOOSE_COLOUR, ECF_LEAF | ECF_NOTHROW, ".")
+DEF_INTERNAL_FN (HWASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, "..R..")
+DEF_INTERNAL_FN (HWASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, "..R..")
 DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ASAN_POISON, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
diff --git a/gcc/passes.def b/gcc/passes.def
index 93879223a8bc12c3d7b07af0b3c99542d936db4b..544f39a60496dd7c06a6c0b00eec36da746951c7 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -246,6 +246,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_sink_code);
       NEXT_PASS (pass_sancov);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_hwasan);
       NEXT_PASS (pass_tsan);
       NEXT_PASS (pass_dce);
       /* Pass group that runs when 1) enabled, 2) there are loops
@@ -364,6 +365,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_dce);
       NEXT_PASS (pass_sancov);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_hwasan);
       NEXT_PASS (pass_tsan);
       /* ???  We do want some kind of loop invariant motion, but we possibly
          need to adjust LIM to be more friendly towards preserving accurate
@@ -389,6 +391,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_sancov_O0);
   NEXT_PASS (pass_lower_switch_O0);
   NEXT_PASS (pass_asan_O0);
+  NEXT_PASS (pass_hwasan_O0);
   NEXT_PASS (pass_tsan_O0);
   NEXT_PASS (pass_sanopt);
   NEXT_PASS (pass_cleanup_eh);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 7bd50715f24a2cb154b578e2abdea4e8fcdb2107..6cfb7951e21d4ee2f31921097796650afb02b7b0 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -183,6 +183,60 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 /* Hardware Address Sanitizer.  */
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1, "__hwasan_load1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2, "__hwasan_load2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4, "__hwasan_load4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8, "__hwasan_load8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16, "__hwasan_load16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN, "__hwasan_loadN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1, "__hwasan_store1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2, "__hwasan_store2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4, "__hwasan_store4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8, "__hwasan_store8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16, "__hwasan_store16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN, "__hwasan_storeN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1_NOABORT, "__hwasan_load1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2_NOABORT, "__hwasan_load2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4_NOABORT, "__hwasan_load4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8_NOABORT, "__hwasan_load8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16_NOABORT, "__hwasan_load16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN_NOABORT, "__hwasan_loadN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1_NOABORT, "__hwasan_store1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2_NOABORT, "__hwasan_store2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4_NOABORT, "__hwasan_store4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8_NOABORT, "__hwasan_store8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16_NOABORT, "__hwasan_store16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN_NOABORT, "__hwasan_storeN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MISMATCH4, "__hwasan_tag_mismatch4",
+		      BT_FN_VOID_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_HANDLE_LONGJMP, "__hwasan_handle_longjmp",
+		      BT_FN_VOID_CONST_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_PTR, "__hwasan_tag_pointer",
+		      BT_FN_PTR_CONST_PTR_UINT8, ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
 		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 00ade87283223798038e58c37e9138fe483de5a1..756e9ede899f8c87fdfc61277bdb79512107655c 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -773,7 +773,8 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
   basic_block son;
   gimple_stmt_iterator gsi;
   sanopt_info *info = (sanopt_info *) bb->aux;
-  bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0;
+  bool asan_check_optimize
+    = ((flag_sanitize & SANITIZE_ADDRESS) != 0) || memory_tagging_p ();
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
@@ -803,6 +804,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
       if (asan_check_optimize
 	  && gimple_call_builtin_p (stmt, BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT))
 	{
+	  gcc_assert (!memory_tagging_p ());
 	  use_operand_p use;
 	  gimple *use_stmt;
 	  if (single_imm_use (gimple_vdef (stmt), &use, &use_stmt))
@@ -831,6 +833,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
 	  case IFN_UBSAN_PTR:
 	    remove = maybe_optimize_ubsan_ptr_ifn (ctx, stmt);
 	    break;
+	  case IFN_HWASAN_CHECK:
 	  case IFN_ASAN_CHECK:
 	    if (asan_check_optimize)
 	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
@@ -1256,6 +1259,10 @@ sanitize_rewrite_addressable_params (function *fun)
 unsigned int
 pass_sanopt::execute (function *fun)
 {
+  /* n.b. ASAN_MARK is used for both HWASAN and ASAN.
+     asan_num_accesses is hence used to count either HWASAN_CHECK or ASAN_CHECK
+     stuff.  This is fine because you can only have one of these active at a
+     time.  */
   basic_block bb;
   int asan_num_accesses = 0;
   bool contains_asan_mark = false;
@@ -1263,10 +1270,10 @@ pass_sanopt::execute (function *fun)
   /* Try to remove redundant checks.  */
   if (optimize
       && (flag_sanitize
-	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT
+	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_HWADDRESS
 	     | SANITIZE_ADDRESS | SANITIZE_VPTR | SANITIZE_POINTER_OVERFLOW)))
     asan_num_accesses = sanopt_optimize (fun, &contains_asan_mark);
-  else if (flag_sanitize & SANITIZE_ADDRESS)
+  else if (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS))
     {
       gimple_stmt_iterator gsi;
       FOR_EACH_BB_FN (bb, fun)
@@ -1286,7 +1293,7 @@ pass_sanopt::execute (function *fun)
       sanitize_asan_mark_poison ();
     }
 
-  if (asan_sanitize_stack_p ())
+  if (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ())
     sanitize_rewrite_addressable_params (fun);
 
   bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
@@ -1328,6 +1335,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_UBSAN_VPTR:
 		  no_next = ubsan_expand_vptr_ifn (&gsi);
 		  break;
+		case IFN_HWASAN_CHECK:
+		  no_next = hwasan_expand_check_ifn (&gsi, use_calls);
+		  break;
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
@@ -1339,6 +1349,9 @@ pass_sanopt::execute (function *fun)
 						    &need_commit_edge_insert,
 						    shadow_vars_mapping);
 		  break;
+		case IFN_HWASAN_MARK:
+		  no_next = hwasan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 96745deef07c5780bab2e8fcdd5cbda864198cb3..5e077892a6371a7b534007d5dffd421b218ea694 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -508,7 +508,7 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
-      if (flag_sanitize & SANITIZE_HWADDRESS)
+      if (gate_hwasan ())
 	hwasan_finish_file ();
 
       omp_finish_file ();
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 7106eba54af1dcf85a434f0a7ac94a41ddd85f25..a7bc46d29c3275cdc1ba7b49c2fd546454e0a87b 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -341,6 +341,8 @@ extern void register_pass (opt_pass* pass, pass_positioning_ops pos,
 
 extern gimple_opt_pass *make_pass_asan (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_asan_O0 (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_hwasan (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_hwasan_O0 (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tsan (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tsan_O0 (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_sancov (gcc::context *ctxt);


[-- Attachment #2: hwasan-patch15.patch --]
[-- Type: text/plain, Size: 37208 bytes --]

diff --git a/gcc/asan.h b/gcc/asan.h
index 5f069690681cdb533a6ee8a3f8cd302a1866610a..c27d679c117a9cc7b15b7b4c6710cf0b46050089 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -31,10 +31,14 @@ extern rtx hwasan_with_tag (rtx, poly_int64);
 extern void hwasan_tag_init ();
 extern rtx hwasan_create_untagged_base (rtx);
 extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern rtx hwasan_base ();
 extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
 extern rtx_insn *hwasan_emit_uncolour_frame (rtx, rtx, rtx_insn *);
+extern bool hwasan_expand_check_ifn (gimple_stmt_iterator *, bool);
+extern bool hwasan_expand_mark_ifn (gimple_stmt_iterator *);
 extern bool memory_tagging_p (void);
 extern bool hwasan_sanitize_stack_p (void);
+extern bool gate_hwasan (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -131,6 +135,13 @@ enum asan_mark_flags
 #undef DEF
 };
 
+enum hwasan_mark_flags
+{
+#define DEF(X) HWASAN_MARK_##X
+  IFN_ASAN_MARK_FLAGS
+#undef DEF
+};
+
 /* Return true if STMT is ASAN_MARK with FLAG as first argument.  */
 extern bool asan_mark_p (gimple *stmt, enum asan_mark_flags flag);
 
@@ -180,6 +191,9 @@ extern hash_set<tree> *asan_handled_variables;
 static inline bool
 asan_intercepted_p (enum built_in_function fcode)
 {
+  if (memory_tagging_p ())
+    return false;
+
   return fcode == BUILT_IN_INDEX
 	 || fcode == BUILT_IN_MEMCHR
 	 || fcode == BUILT_IN_MEMCMP
@@ -208,7 +222,8 @@ asan_intercepted_p (enum built_in_function fcode)
 static inline bool
 asan_sanitize_use_after_scope (void)
 {
-  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
+  return (flag_sanitize_address_use_after_scope
+	  && (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ()));
 }
 
 /* Return true if DECL should be guarded on the stack.  */
diff --git a/gcc/asan.c b/gcc/asan.c
index fca06159b5bcaf349750103d401c12a965414dcc..edfbf2048b67d3dc7be78a8e9961152c4fb44902 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dojump.h"
 #include "explow.h"
 #include "expr.h"
+#include "except.h"
 #include "output.h"
 #include "langhooks.h"
 #include "cfgloop.h"
@@ -579,15 +580,28 @@ get_last_alloca_addr ()
 static void
 handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_stack_p ()))
     return;
 
-  tree last_alloca = get_last_alloca_addr ();
   tree restored_stack = gimple_call_arg (call, 0);
-  tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
-  gimple *g = gimple_build_call (fn, 2, last_alloca, restored_stack);
-  gsi_insert_before (iter, g, GSI_SAME_STMT);
-  g = gimple_build_assign (last_alloca, restored_stack);
+
+  gimple *g;
+
+  if (hwasan_sanitize_stack_p ())
+    {
+      tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_HANDLE_LONGJMP);
+      g = gimple_build_call (fn, 1, restored_stack);
+    }
+  else
+    {
+      tree last_alloca = get_last_alloca_addr ();
+      tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
+      g = gimple_build_call (fn, 2, last_alloca, restored_stack);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      g = gimple_build_assign (last_alloca, restored_stack);
+    }
+
   gsi_insert_before (iter, g, GSI_SAME_STMT);
 }
 
@@ -617,14 +631,12 @@ handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 static void
 handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_stack_p ()))
     return;
 
   gassign *g;
   gcall *gg;
-  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
-
-  tree last_alloca = get_last_alloca_addr ();
   tree callee = gimple_call_fndecl (call);
   tree old_size = gimple_call_arg (call, 0);
   tree ptr_type = gimple_call_lhs (call) ? TREE_TYPE (gimple_call_lhs (call))
@@ -634,6 +646,85 @@ handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
     = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
       ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
 
+  if (hwasan_sanitize_stack_p ())
+    {
+      /*
+	 HWASAN needs a different expansion.
+
+	 addr = __builtin_alloca (size, align);
+
+	 should be replaced by
+
+	 new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment;
+	 untagged_addr = __builtin_alloca (new_size, align);
+	 colour = __hwasan_choose_alloca_colour ();
+	 addr = __hwasan_tag_pointer (untagged_addr, colour);
+	 __hwasan_tag_memory (addr, colour, new_size);
+	*/
+      /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start on
+	 a tag granule.  */
+      align = align > HWASAN_TAG_GRANULE_SIZE ? align : HWASAN_TAG_GRANULE_SIZE;
+
+      /* tree new_size = (old_size + 15) & ~15;  */
+      uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
+      tree old_size = gimple_call_arg (call, 0);
+      tree tree_mask = build_int_cst (size_type_node, tg_mask);
+      g = gimple_build_assign (make_ssa_name (size_type_node), PLUS_EXPR,
+			       old_size, tree_mask);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree oversize = gimple_assign_lhs (g);
+
+      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_NOT_EXPR,
+			       tree_mask);
+      tree mask = gimple_assign_lhs (g);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+
+      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_AND_EXPR,
+			       oversize, mask);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree new_size = gimple_assign_lhs (g);
+
+      /* emit the alloca call */
+      tree fn = builtin_decl_implicit (BUILT_IN_ALLOCA_WITH_ALIGN);
+      gg = gimple_build_call (fn, 2, new_size,
+			      build_int_cst (size_type_node, align));
+      tree untagged_addr = make_ssa_name (ptr_type, gg);
+      gimple_call_set_lhs (gg, untagged_addr);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code choosing the tag.
+	 Here we use an internal function so we can choose the colour at expand
+	 time.  We need the decision to be made after stack variables have been
+	 assigned their colour (i.e. once the tag_offset variable has been set
+	 to one after the last stack variables tag).  */
+
+      gg = gimple_build_call_internal (IFN_HWASAN_CHOOSE_COLOUR, 0);
+      tree colour = make_ssa_name (unsigned_char_type_node, gg);
+      gimple_call_set_lhs (gg, colour);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code adding tag to pointer.  */
+      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_PTR);
+      gg = gimple_build_call (fn, 2, untagged_addr, colour);
+      tree addr = make_ssa_name (ptr_type, gg);
+      gimple_call_set_lhs (gg, addr);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code colouring shadow memory.
+	 NOTE: require using `untagged_addr` here for libhwasan API.  */
+      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MEM);
+      gg = gimple_build_call (fn, 3, untagged_addr, colour, new_size);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Finally, replace old alloca ptr with NEW_ALLOCA.  */
+      replace_call_with_value (iter, addr);
+      return;
+    }
+
+  tree last_alloca = get_last_alloca_addr ();
+  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
+
+
   /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
      bytes of allocated space.  Otherwise, align alloca to ASAN_RED_ZONE_SIZE
      manually.  */
@@ -786,6 +877,31 @@ get_mem_refs_of_builtin_call (gcall *call,
       break;
 
     case BUILT_IN_STRLEN:
+      /* Special case strlen here since its length is taken from its return
+	 value.
+
+	 The approach taken by the sanitizers is to check a memory access
+	 before it's taken.  For ASAN strlen is intercepted by libasan, so no
+	 check is inserted by the compiler.
+
+	 This function still returns `true` and provides a length to the rest
+	 of the ASAN pass in order to record what areas have been checked,
+	 avoiding superfluous checks later on.
+
+	 HWASAN does not intercept any of these internal functions.
+	 This means that checks for memory accesses must be inserted by the
+	 compiler.
+	 strlen is a special case, because we can tell the length from the
+	 return of the function, but that is not known until after the function
+	 has returned.
+
+	 Hence we can't check the memory access before it happens.
+	 We could check the memory access after it has already happened, but
+	 for now I'm choosing to just ignore `strlen` calls.
+	 This decision was simply made because that means the special case is
+	 limited to this one case of this one function.  */
+      if (memory_tagging_p ())
+	return false;
       source0 = gimple_call_arg (call, 0);
       len = gimple_call_lhs (call);
       break;
@@ -1848,6 +1964,8 @@ static tree
 report_error_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
 		   int *nargs)
 {
+  gcc_assert (!memory_tagging_p ());
+
   static enum built_in_function report[2][2][6]
     = { { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
 	    BUILT_IN_ASAN_REPORT_LOAD4, BUILT_IN_ASAN_REPORT_LOAD8,
@@ -2184,7 +2302,13 @@ build_check_stmt (location_t loc, tree base, tree len,
   if (is_scalar_access)
     flags |= ASAN_CHECK_SCALAR_ACCESS;
 
-  g = gimple_build_call_internal (IFN_ASAN_CHECK, 4,
+  enum internal_fn fn;
+  if (memory_tagging_p ())
+    fn = IFN_HWASAN_CHECK;
+  else
+    fn = IFN_ASAN_CHECK;
+
+  g = gimple_build_call_internal (fn, 4,
 				  build_int_cst (integer_type_node, flags),
 				  base, len,
 				  build_int_cst (integer_type_node,
@@ -2208,10 +2332,13 @@ static void
 instrument_derefs (gimple_stmt_iterator *iter, tree t,
 		   location_t location, bool is_store)
 {
-  if (is_store && !ASAN_INSTRUMENT_WRITES)
-    return;
-  if (!is_store && !ASAN_INSTRUMENT_READS)
-    return;
+  if (! memory_tagging_p ())
+    {
+    if (is_store && !ASAN_INSTRUMENT_WRITES)
+      return;
+    if (!is_store && !ASAN_INSTRUMENT_READS)
+      return;
+    }
 
   tree type, base;
   HOST_WIDE_INT size_in_bytes;
@@ -2271,7 +2398,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
     {
       if (DECL_THREAD_LOCAL_P (inner))
 	return;
-      if (!ASAN_GLOBALS && is_global_var (inner))
+      if ((memory_tagging_p () || !ASAN_GLOBALS)
+	  && is_global_var (inner))
         return;
       if (!TREE_STATIC (inner))
 	{
@@ -2500,10 +2628,13 @@ maybe_instrument_call (gimple_stmt_iterator *iter)
 	      break;
 	    }
 	}
-      tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
-      gimple *g = gimple_build_call (decl, 0);
-      gimple_set_location (g, gimple_location (stmt));
-      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      if (! memory_tagging_p ())
+	{
+	  tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
+	  gimple *g = gimple_build_call (decl, 0);
+	  gimple_set_location (g, gimple_location (stmt));
+	  gsi_insert_before (iter, g, GSI_SAME_STMT);
+	}
     }
 
   bool instrumented = false;
@@ -2902,6 +3033,9 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_PTR_CONST_PTR_UINT8
+    = build_function_type_list (ptr_type_node, const_ptr_type_node,
+				unsigned_char_type_node, NULL_TREE);
   tree BT_FN_VOID_PTR_UINT8_SIZE
     = build_function_type_list (void_type_node, ptr_type_node,
 				unsigned_char_type_node, size_type_node,
@@ -3237,6 +3371,23 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
   unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
   gcc_assert (size_in_bytes);
 
+  if (memory_tagging_p ())
+    {
+      gcc_assert (HWASAN_STACK);
+      /* Here we swap ASAN_MARK calls for HWASAN_MARK.
+	 This is because we are using the approach of using ASAN_MARK as a
+	 synonym until here.
+	 That approach means we don't yet have to duplicate all the special
+	 cases for ASAN_MARK and ASAN_POISON with the exact same handling but
+	 called HWASAN_MARK etc.  */
+      gimple *hw_poison_call
+	= gimple_build_call_internal (IFN_HWASAN_MARK, 3,
+				      gimple_call_arg (g, 0),
+				      base, len);
+      gsi_replace (iter, hw_poison_call, false);
+      return false;
+    }
+
   g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
 			   NOP_EXPR, base);
   gimple_set_location (g, loc);
@@ -3298,6 +3449,7 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 bool
 asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
 {
+  gcc_assert (!memory_tagging_p ());
   gimple *g = gsi_stmt (*iter);
   location_t loc = gimple_location (g);
   bool recover_p;
@@ -3571,11 +3723,37 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
 
       int nargs;
       bool store_p = gimple_call_internal_p (use, IFN_ASAN_POISON_USE);
-      tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
-				    &nargs);
-
-      gcall *call = gimple_build_call (fun, 1,
-				       build_fold_addr_expr (shadow_var));
+      gcall *call;
+      if (memory_tagging_p ())
+	{
+	  tree fun = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MISMATCH4);
+	  /* NOTE: hwasan has no __hwasan_report_* functions like asan does.
+		We use __hwasan_tag_mismatch4 with arguments that tell it the
+		size of access and load to report all tag mismatches.  */
+	  unsigned HOST_WIDE_INT size_in_bytes = tree_to_uhwi (size);
+	  unsigned size_indicator = (size_in_bytes > 16)
+	    ? 0xf
+	    : exact_log2 (size_in_bytes);
+	  unsigned access_info = (0x20 * recover_p)
+	    + (0x10 * store_p)
+	    + (size_indicator);
+	  tree long_pointer_type
+	    = build_pointer_type (long_long_unsigned_type_node);
+	  call = gimple_build_call (fun, 3,
+				    build_fold_addr_expr (shadow_var),
+				    build_int_cst (long_long_unsigned_type_node,
+						   access_info),
+				    build_int_cst (long_pointer_type,
+						   0),
+				    size);
+	}
+      else
+	{
+	  tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
+					&nargs);
+	  call = gimple_build_call (fun, 1,
+				    build_fold_addr_expr (shadow_var));
+	}
       gimple_set_location (call, gimple_location (use));
       gimple *call_to_insert = call;
 
@@ -3709,6 +3887,16 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/*  HWASAN  */
+static unsigned int
+hwasan_instrument (void)
+{
+  transform_statements ();
+  last_alloca_addr = NULL_TREE;
+
+  return 0;
+}
+
 void
 hwasan_record_base (rtx base)
 {
@@ -3727,6 +3915,15 @@ hwasan_current_tag ()
   return tag_offset;
 }
 
+rtx
+hwasan_base ()
+{
+  if (! hwasan_base_ptr)
+    hwasan_record_base (gen_reg_rtx (Pmode));
+
+  return hwasan_base_ptr;
+}
+
 void
 hwasan_increment_tag ()
 {
@@ -3858,6 +4055,12 @@ hwasan_emit_uncolour_frame (rtx dynamic, rtx vars, rtx_insn *before)
   do_pending_stack_adjust ();
   rtx_insn *insns = get_insns ();
   end_sequence ();
+
+  /* Clear the hash_map recording which variables are handled by HWASAN_MARK.
+     The only use in HWASAN is to decide which variables need to be coloured in
+     the prologue and which don't.  */
+  delete asan_handled_variables;
+  asan_handled_variables = NULL;
   return insns;
 }
 
@@ -3894,4 +4097,214 @@ hwasan_finish_file (void)
   flag_sanitize |= SANITIZE_HWADDRESS;
 }
 
+/* Construct a function tree for __hwasan_{load,store}{1,2,4,8,16,_n}.
+   IS_STORE is either 1 (for a store) or 0 (for a load).  */
+
+static tree
+hwasan_check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
+		   int *nargs)
+{
+  static enum built_in_function check[2][2][6]
+    = { { { BUILT_IN_HWASAN_LOAD1, BUILT_IN_HWASAN_LOAD2,
+	    BUILT_IN_HWASAN_LOAD4, BUILT_IN_HWASAN_LOAD8,
+	    BUILT_IN_HWASAN_LOAD16, BUILT_IN_HWASAN_LOADN },
+	  { BUILT_IN_HWASAN_STORE1, BUILT_IN_HWASAN_STORE2,
+	    BUILT_IN_HWASAN_STORE4, BUILT_IN_HWASAN_STORE8,
+	    BUILT_IN_HWASAN_STORE16, BUILT_IN_HWASAN_STOREN } },
+	{ { BUILT_IN_HWASAN_LOAD1_NOABORT,
+	    BUILT_IN_HWASAN_LOAD2_NOABORT,
+	    BUILT_IN_HWASAN_LOAD4_NOABORT,
+	    BUILT_IN_HWASAN_LOAD8_NOABORT,
+	    BUILT_IN_HWASAN_LOAD16_NOABORT,
+	    BUILT_IN_HWASAN_LOADN_NOABORT },
+	  { BUILT_IN_HWASAN_STORE1_NOABORT,
+	    BUILT_IN_HWASAN_STORE2_NOABORT,
+	    BUILT_IN_HWASAN_STORE4_NOABORT,
+	    BUILT_IN_HWASAN_STORE8_NOABORT,
+	    BUILT_IN_HWASAN_STORE16_NOABORT,
+	    BUILT_IN_HWASAN_STOREN_NOABORT } } };
+  if (size_in_bytes == -1)
+    {
+      *nargs = 2;
+      return builtin_decl_implicit (check[recover_p][is_store][5]);
+    }
+  *nargs = 1;
+  int size_log2 = exact_log2 (size_in_bytes);
+  return builtin_decl_implicit (check[recover_p][is_store][size_log2]);
+}
+
+
+bool
+hwasan_expand_check_ifn (gimple_stmt_iterator *iter, bool)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  bool recover_p;
+  if (flag_sanitize & SANITIZE_USER_HWADDRESS)
+    recover_p = (flag_sanitize_recover & SANITIZE_USER_HWADDRESS) != 0;
+  else
+    recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_HWADDRESS) != 0;
+
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_CHECK_LAST);
+  bool is_scalar_access = (flags & ASAN_CHECK_SCALAR_ACCESS) != 0;
+  bool is_store = (flags & ASAN_CHECK_STORE) != 0;
+  bool is_non_zero_len = (flags & ASAN_CHECK_NON_ZERO_LEN) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  tree len = gimple_call_arg (g, 2);
+
+  /* `align` is unused for HWASAN_CHECK, but I pass the argument anyway
+     since that way the arguments match ASAN_CHECK.  */
+  /* HOST_WIDE_INT align = tree_to_shwi (gimple_call_arg (g, 3));  */
+
+  unsigned HOST_WIDE_INT size_in_bytes
+    = is_scalar_access && tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
+
+  gimple_stmt_iterator gsi = *iter;
+
+  if (!is_non_zero_len)
+    {
+      /* So, the length of the memory area to hwasan-protect is
+	 non-constant.  Let's guard the generated instrumentation code
+	 like:
+
+	 if (len != 0)
+	   {
+	     // hwasan instrumentation code goes here.
+	   }
+	 // falltrough instructions, starting with *ITER.  */
+
+      g = gimple_build_cond (NE_EXPR,
+			    len,
+			    build_int_cst (TREE_TYPE (len), 0),
+			    NULL_TREE, NULL_TREE);
+      gimple_set_location (g, loc);
+
+      basic_block then_bb, fallthrough_bb;
+      insert_if_then_before_iter (as_a <gcond *> (g), iter,
+				  /*then_more_likely_p=*/true,
+				  &then_bb, &fallthrough_bb);
+      /* Note that fallthrough_bb starts with the statement that was
+	pointed to by ITER.  */
+
+      /* The 'then block' of the 'if (len != 0) condition is where
+	we'll generate the hwasan instrumentation code now.  */
+      gsi = gsi_last_bb (then_bb);
+    }
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			   NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+  tree base_addr = gimple_assign_lhs (g);
+
+  int nargs = 0;
+  tree fun = hwasan_check_func (is_store, recover_p, size_in_bytes, &nargs);
+  if (nargs == 1)
+    g = gimple_build_call (fun, 1, base_addr);
+  else
+    {
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			       NOP_EXPR, len);
+      gimple_set_location (g, loc);
+      gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+      g = gimple_build_call (fun, nargs, base_addr, sz_arg);
+    }
+
+  gimple_set_location (g, loc);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+  gsi_remove (iter, true);
+  *iter = gsi;
+  return false;
+}
+
+bool
+hwasan_expand_mark_ifn (gimple_stmt_iterator *)
+{
+  /* HWASAN_MARK should only ever be available after the sanopt pass.  */
+  gcc_unreachable ();
+}
+
+bool
+gate_hwasan ()
+{
+  return memory_tagging_p ();
+}
+
+namespace {
+
+const pass_data pass_data_hwasan =
+{
+  GIMPLE_PASS, /* type */
+  "hwasan", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_hwasan : public gimple_opt_pass
+{
+public:
+  pass_hwasan (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_hwasan, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_hwasan (m_ctxt); }
+  virtual bool gate (function *) { return gate_hwasan (); }
+  virtual unsigned int execute (function *) { return hwasan_instrument (); }
+
+}; /* class pass_asan  */
+
+} /* anon namespace  */
+
+gimple_opt_pass *
+make_pass_hwasan (gcc::context *ctxt)
+{
+  return new pass_hwasan (ctxt);
+}
+
+namespace {
+
+const pass_data pass_data_hwasan_O0 =
+{
+  GIMPLE_PASS, /* type */
+  "hwasan_O0", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_hwasan_O0 : public gimple_opt_pass
+{
+public:
+  pass_hwasan_O0 (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_hwasan_O0, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_hwasan_O0 (m_ctxt); }
+  virtual bool gate (function *) { return !optimize && gate_hwasan (); }
+  virtual unsigned int execute (function *) { return hwasan_instrument (); }
+
+}; // class pass_asan
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_hwasan_O0 (gcc::context *ctxt)
+{
+  return new pass_hwasan_O0 (ctxt);
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index d05f597b6434f39fe95d4f28dd2ef3ed463dd925..00592b1eea76164471e281c8922893937cf9bb2e 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -493,6 +493,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_INT_FEXCEPT_T_PTR_INT, BT_INT, BT_FEXCEPT_T_PTR,
 		     BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_FEXCEPT_T_PTR_INT, BT_INT,
 		     BT_CONST_FEXCEPT_T_PTR, BT_INT)
+DEF_FUNCTION_TYPE_2 (BT_FN_PTR_CONST_PTR_UINT8, BT_PTR, BT_CONST_PTR, BT_UINT8)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR)
 
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 2d5ece0680538389118b909951863be50d570326..dba1d58b83a1caf657cbfdd309a3576442fd59d0 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -750,6 +750,7 @@ dump_gimple_call_args (pretty_printer *buffer, gcall *gs, dump_flags_t flags)
 	  limit = ARRAY_SIZE (reduction_args);
 	  break;
 
+	case IFN_HWASAN_MARK:
 	case IFN_ASAN_MARK:
 #define DEF(X) #X
 	  static const char *const asan_mark_args[] = {IFN_ASAN_MARK_FLAGS};
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index daa0b71c191e6d92387180e927b818517b8b58a7..18086416c0f5ac089bdeced6bc9554730c06e5bd 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1230,8 +1230,11 @@ asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
 
   /* It's necessary to have all stack variables aligned to ASAN granularity
      bytes.  */
-  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
-    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+  gcc_assert (!memory_tagging_p () || hwasan_sanitize_stack_p ());
+  unsigned shadow_granularity
+    = memory_tagging_p () ? HWASAN_TAG_GRANULE_SIZE : ASAN_SHADOW_GRANULARITY;
+  if (DECL_ALIGN_UNIT (decl) <= shadow_granularity)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * shadow_granularity);
 
   HOST_WIDE_INT flags = poison ? ASAN_MARK_POISON : ASAN_MARK_UNPOISON;
 
@@ -14520,7 +14523,7 @@ gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
-  if (asan_sanitize_use_after_scope () && sanitize_flags_p (SANITIZE_ADDRESS))
+  if (asan_sanitize_use_after_scope ())
     asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
   if (asan_poisoned_variables)
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 549d6f1153b6e568d1ab8b0e5b8fa8ccc2161b61..c692ae86ec6b5fbe345558d7f412f6ecd666bfa1 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -457,6 +457,74 @@ expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
+expand_HWASAN_CHECK (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
+
+static void
+expand_HWASAN_CHOOSE_COLOUR (internal_fn, gcall *gc)
+{
+  tree colour = gimple_call_lhs (gc);
+  rtx target = expand_expr (colour, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  machine_mode mode = GET_MODE (target);
+  gcc_assert (mode == QImode);
+
+  rtx base_tag = hwasan_extract_tag (hwasan_base ());
+  gcc_assert (base_tag);
+  rtx tag_offset = GEN_INT (hwasan_current_tag ());
+  rtx chosen_tag = expand_simple_binop (QImode, PLUS, base_tag, tag_offset,
+					target, /* unsignedp = */1,
+					OPTAB_WIDEN);
+
+  gcc_assert (chosen_tag);
+  /* Really need to put the tag into the `target` RTX.  */
+  if (chosen_tag != target)
+    {
+      rtx temp = chosen_tag;
+      machine_mode ret_mode = GET_MODE (chosen_tag);
+      if (ret_mode != mode)
+	temp = simplify_gen_unary (TRUNCATE, mode, chosen_tag, ret_mode);
+
+      emit_move_insn (target, temp);
+    }
+
+  hwasan_increment_tag ();
+}
+
+static void
+expand_HWASAN_MARK (internal_fn, gcall *gc)
+{
+  HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
+  bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
+
+  tree base = gimple_call_arg (gc, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  rtx base_rtx = expand_normal (base);
+
+  rtx tag = is_poison ? const0_rtx : hwasan_extract_tag (base_rtx);
+  rtx address = hwasan_create_untagged_base (base_rtx);
+
+  tree len = gimple_call_arg (gc, 2);
+  gcc_assert (tree_fits_shwi_p (len));
+  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
+  gcc_assert (size_in_bytes);
+  size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
+  rtx size = gen_int_mode (size_in_bytes, Pmode);
+
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (func,
+      LCT_NORMAL,
+      VOIDmode,
+      address, ptr_mode,
+      tag, QImode,
+      size, ptr_mode);
+}
+
+/* This should get expanded in the sanopt pass.  */
+
+static void
 expand_ASAN_CHECK (internal_fn, gcall *)
 {
   gcc_unreachable ();
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 49f57978c88a3a8c1a0206d983e1720ed09b0385..917813ca87218aa8cb9127658b9f81057fabdcc4 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -301,6 +301,9 @@ DEF_INTERNAL_FN (UBSAN_PTR, ECF_LEAF | ECF_NOTHROW, ".R.")
 DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
 DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (HWASAN_CHOOSE_COLOUR, ECF_LEAF | ECF_NOTHROW, ".")
+DEF_INTERNAL_FN (HWASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, "..R..")
+DEF_INTERNAL_FN (HWASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, "..R..")
 DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ASAN_POISON, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
diff --git a/gcc/passes.def b/gcc/passes.def
index 93879223a8bc12c3d7b07af0b3c99542d936db4b..544f39a60496dd7c06a6c0b00eec36da746951c7 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -246,6 +246,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_sink_code);
       NEXT_PASS (pass_sancov);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_hwasan);
       NEXT_PASS (pass_tsan);
       NEXT_PASS (pass_dce);
       /* Pass group that runs when 1) enabled, 2) there are loops
@@ -364,6 +365,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_dce);
       NEXT_PASS (pass_sancov);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_hwasan);
       NEXT_PASS (pass_tsan);
       /* ???  We do want some kind of loop invariant motion, but we possibly
          need to adjust LIM to be more friendly towards preserving accurate
@@ -389,6 +391,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_sancov_O0);
   NEXT_PASS (pass_lower_switch_O0);
   NEXT_PASS (pass_asan_O0);
+  NEXT_PASS (pass_hwasan_O0);
   NEXT_PASS (pass_tsan_O0);
   NEXT_PASS (pass_sanopt);
   NEXT_PASS (pass_cleanup_eh);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 7bd50715f24a2cb154b578e2abdea4e8fcdb2107..6cfb7951e21d4ee2f31921097796650afb02b7b0 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -183,6 +183,60 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 /* Hardware Address Sanitizer.  */
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1, "__hwasan_load1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2, "__hwasan_load2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4, "__hwasan_load4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8, "__hwasan_load8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16, "__hwasan_load16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN, "__hwasan_loadN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1, "__hwasan_store1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2, "__hwasan_store2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4, "__hwasan_store4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8, "__hwasan_store8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16, "__hwasan_store16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN, "__hwasan_storeN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1_NOABORT, "__hwasan_load1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2_NOABORT, "__hwasan_load2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4_NOABORT, "__hwasan_load4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8_NOABORT, "__hwasan_load8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16_NOABORT, "__hwasan_load16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN_NOABORT, "__hwasan_loadN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1_NOABORT, "__hwasan_store1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2_NOABORT, "__hwasan_store2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4_NOABORT, "__hwasan_store4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8_NOABORT, "__hwasan_store8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16_NOABORT, "__hwasan_store16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN_NOABORT, "__hwasan_storeN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MISMATCH4, "__hwasan_tag_mismatch4",
+		      BT_FN_VOID_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_HANDLE_LONGJMP, "__hwasan_handle_longjmp",
+		      BT_FN_VOID_CONST_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_PTR, "__hwasan_tag_pointer",
+		      BT_FN_PTR_CONST_PTR_UINT8, ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
 		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 00ade87283223798038e58c37e9138fe483de5a1..756e9ede899f8c87fdfc61277bdb79512107655c 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -773,7 +773,8 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
   basic_block son;
   gimple_stmt_iterator gsi;
   sanopt_info *info = (sanopt_info *) bb->aux;
-  bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0;
+  bool asan_check_optimize
+    = ((flag_sanitize & SANITIZE_ADDRESS) != 0) || memory_tagging_p ();
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
@@ -803,6 +804,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
       if (asan_check_optimize
 	  && gimple_call_builtin_p (stmt, BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT))
 	{
+	  gcc_assert (!memory_tagging_p ());
 	  use_operand_p use;
 	  gimple *use_stmt;
 	  if (single_imm_use (gimple_vdef (stmt), &use, &use_stmt))
@@ -831,6 +833,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
 	  case IFN_UBSAN_PTR:
 	    remove = maybe_optimize_ubsan_ptr_ifn (ctx, stmt);
 	    break;
+	  case IFN_HWASAN_CHECK:
 	  case IFN_ASAN_CHECK:
 	    if (asan_check_optimize)
 	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
@@ -1256,6 +1259,10 @@ sanitize_rewrite_addressable_params (function *fun)
 unsigned int
 pass_sanopt::execute (function *fun)
 {
+  /* n.b. ASAN_MARK is used for both HWASAN and ASAN.
+     asan_num_accesses is hence used to count either HWASAN_CHECK or ASAN_CHECK
+     stuff.  This is fine because you can only have one of these active at a
+     time.  */
   basic_block bb;
   int asan_num_accesses = 0;
   bool contains_asan_mark = false;
@@ -1263,10 +1270,10 @@ pass_sanopt::execute (function *fun)
   /* Try to remove redundant checks.  */
   if (optimize
       && (flag_sanitize
-	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT
+	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_HWADDRESS
 	     | SANITIZE_ADDRESS | SANITIZE_VPTR | SANITIZE_POINTER_OVERFLOW)))
     asan_num_accesses = sanopt_optimize (fun, &contains_asan_mark);
-  else if (flag_sanitize & SANITIZE_ADDRESS)
+  else if (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS))
     {
       gimple_stmt_iterator gsi;
       FOR_EACH_BB_FN (bb, fun)
@@ -1286,7 +1293,7 @@ pass_sanopt::execute (function *fun)
       sanitize_asan_mark_poison ();
     }
 
-  if (asan_sanitize_stack_p ())
+  if (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ())
     sanitize_rewrite_addressable_params (fun);
 
   bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
@@ -1328,6 +1335,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_UBSAN_VPTR:
 		  no_next = ubsan_expand_vptr_ifn (&gsi);
 		  break;
+		case IFN_HWASAN_CHECK:
+		  no_next = hwasan_expand_check_ifn (&gsi, use_calls);
+		  break;
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
@@ -1339,6 +1349,9 @@ pass_sanopt::execute (function *fun)
 						    &need_commit_edge_insert,
 						    shadow_vars_mapping);
 		  break;
+		case IFN_HWASAN_MARK:
+		  no_next = hwasan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 96745deef07c5780bab2e8fcdd5cbda864198cb3..5e077892a6371a7b534007d5dffd421b218ea694 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -508,7 +508,7 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
-      if (flag_sanitize & SANITIZE_HWADDRESS)
+      if (gate_hwasan ())
 	hwasan_finish_file ();
 
       omp_finish_file ();
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 7106eba54af1dcf85a434f0a7ac94a41ddd85f25..a7bc46d29c3275cdc1ba7b49c2fd546454e0a87b 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -341,6 +341,8 @@ extern void register_pass (opt_pass* pass, pass_positioning_ops pos,
 
 extern gimple_opt_pass *make_pass_asan (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_asan_O0 (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_hwasan (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_hwasan_O0 (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tsan (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tsan_O0 (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_sancov (gcc::context *ctxt);


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 14/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (12 preceding siblings ...)
  2019-11-05 11:35   ` [PATCH 16/X] [libsanitizer] Add tests Matthew Malcomson
@ 2019-11-05 11:35   ` Matthew Malcomson
  2019-11-05 11:35   ` [PATCH 17/X] [libsanitizer] Add hwasan Exception handling Matthew Malcomson
                     ` (15 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:35 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 40683 bytes --]

Handling stack variables has three features.

1) Ensure HWASAN required alignment for stack variables

When colouring shadow memory, we need to ensure that each tag granule
is only used by one variable at a time.

This is done by ensuring that each coloured variable is aligned to the
tag granule representation size and also ensure that the end of each
variable as an alignment boundary between the end and the start of any
other data stored on the stack.

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

2) Put tags into each stack variable pointer

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

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

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

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

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

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

3) For each stack variable, colour and uncolour the shadow stack on
   function prologue and epilogue.

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

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

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

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

gcc/ChangeLog:

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

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



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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -1,7 +1,11 @@
 # This option enables -fsanitize=hwaddress for stage2 and stage3.
+# We need to disable random frame tags for bootstrap since the autoconf check
+# for which direction the stack is growing has UB that a random frame tag
+# breaks.  Running with a random frame tag gives approx. 50% chance of
+# bootstrap comparison diff in libiberty/alloca.c.
 
-STAGE2_CFLAGS += -fsanitize=hwaddress
-STAGE3_CFLAGS += -fsanitize=hwaddress
+STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
 POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
diff --git a/gcc/asan.h b/gcc/asan.h
index 7675f18a84ee3f187ba4cb40db0ce232f3958762..5f069690681cdb533a6ee8a3f8cd302a1866610a 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -23,6 +23,18 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void asan_function_start (void);
 extern void asan_finish_file (void);
+extern void hwasan_finish_file (void);
+extern void hwasan_record_base (rtx);
+extern uint8_t hwasan_current_tag ();
+extern void hwasan_increment_tag ();
+extern rtx hwasan_with_tag (rtx, poly_int64);
+extern void hwasan_tag_init ();
+extern rtx hwasan_create_untagged_base (rtx);
+extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
+extern rtx_insn *hwasan_emit_uncolour_frame (rtx, rtx, rtx_insn *);
+extern bool memory_tagging_p (void);
+extern bool hwasan_sanitize_stack_p (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -75,6 +87,31 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The values below define an ABI and are hard-coded to these values in
+   libhwasan, hence they can't be changed independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).  */
+#define HWASAN_TAG_SIZE 8
+/* Tag Granule of HWASAN shadow stack.
+   This is the size in real memory that each byte in the shadow memory refers
+   to.  I.e. if a variable is X bytes long in memory then it's colour in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   Most variables will need to be aligned to this amount since two variables
+   that are neighbours in memory and share a tag granule would need to share
+   the same colour (the shared tag granule can only store one colour).  */
+#define HWASAN_TAG_SHIFT_SIZE 4
+#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SHIFT_SIZE)
+/* Define the tag for the stack background.
+   This defines what colour the stack pointer will be and hence what tag all
+   variables that are not given special tags are (e.g. spilled registers,
+   and parameters passed on the stack).  */
+#define HWASAN_STACK_BACKGROUND 0
+/* How many bits to shift in order to access the tag bits.
+   The tag is stored in the top 8 bits of a pointer hence shifting 56 bits will
+   leave just the tag.  */
+#define HWASAN_SHIFT 56
+#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/asan.c b/gcc/asan.c
index a731bd490b4e78e916ae20fc9a0249c1fc04daa5..fca06159b5bcaf349750103d401c12a965414dcc 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -257,6 +257,9 @@ hash_set<tree> *asan_handled_variables = NULL;
 
 hash_set <tree> *asan_used_labels = NULL;
 
+static uint8_t tag_offset = 0;
+static rtx hwasan_base_ptr = NULL_RTX;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -1352,6 +1355,21 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+/* Returns whether we are tagging pointers and checking those tags on memory
+   access.  This is true when checking with either in software or hardware.  */
+bool
+memory_tagging_p ()
+{
+    return sanitize_flags_p (SANITIZE_HWADDRESS);
+}
+
+/* Are we tagging the stack?  */
+bool
+hwasan_sanitize_stack_p ()
+{
+  return (memory_tagging_p () && HWASAN_STACK);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -2884,6 +2902,11 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_VOID_PTR_UINT8_SIZE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				unsigned_char_type_node, size_type_node,
+				NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
@@ -2934,6 +2957,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NOTHROW_LIST
+#define ATTR_NOTHROW_LIST ECF_NOTHROW
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
@@ -3684,4 +3709,189 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+void
+hwasan_record_base (rtx base)
+{
+  /* Initialise base colour of the base register.
+     This has to be done as soon as the stack is getting expanded to ensure
+     anything emitted with `get_dynamic_stack_base` will use the value set here
+     instead of using a register without a value.
+     Especially note that RTL expansion of large aligned values does that.  */
+  targetm.memtag.gentag (base, virtual_stack_vars_rtx);
+  hwasan_base_ptr = base;
+}
+
+uint8_t
+hwasan_current_tag ()
+{
+  return tag_offset;
+}
+
+void
+hwasan_increment_tag ()
+{
+  uint8_t tag_bits = HWASAN_TAG_SIZE;
+  tag_offset = (tag_offset + 1) % (1 << tag_bits);
+}
+
+rtx
+hwasan_with_tag (rtx base, poly_int64 offset)
+{
+  gcc_assert (tag_offset < (1 << HWASAN_TAG_SIZE));
+  return targetm.memtag.addtag (base, offset, tag_offset);
+}
+
+/* Clear internal state for the next function.
+   This function is called before variables on the stack get expanded, in
+   `init_vars_expansion`.  */
+void
+hwasan_tag_init ()
+{
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
+  hwasan_base_ptr = NULL_RTX;
+  tag_offset = HWASAN_STACK_BACKGROUND + 1;
+}
+
+rtx
+hwasan_extract_tag (rtx tagged_pointer)
+{
+  rtx tag = expand_simple_binop (Pmode,
+				 LSHIFTRT,
+				 tagged_pointer,
+				 HWASAN_SHIFT_RTX,
+				 NULL_RTX,
+				 /* unsignedp = */0,
+				 OPTAB_DIRECT);
+  return gen_lowpart (QImode, tag);
+}
+
+void
+hwasan_emit_prologue (rtx *bases,
+		      rtx *untagged_bases,
+		      poly_int64 *offsets,
+		      uint8_t *tags,
+		      size_t length)
+{
+  /* We need untagged base pointers since libhwasan only accepts untagged
+    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
+    the base tag for an offset.  */
+  for (size_t i = 0; (i * 2) + 1 < length; i++)
+    {
+      poly_int64 start = offsets[i * 2];
+      poly_int64 end = offsets[(i * 2) + 1];
+
+      poly_int64 bot, top;
+      if (known_ge (start, end))
+	{
+	  top = start;
+	  bot = end;
+	}
+      else
+	{
+	  top = end;
+	  bot = start;
+	}
+      poly_int64 size = (top - bot);
+
+      /* Can't check that all poly_int64's are aligned, but still nice
+	 to check those that are compile-time constants.  */
+      HOST_WIDE_INT tmp;
+      if (top.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (bot.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (size.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      rtx base_tag = hwasan_extract_tag (bases[i]);
+      /* In the case of tag overflow we would want modulo wrapping -- which
+	 should be given from the `plus_constant` in QImode.  */
+      rtx tag_colour = plus_constant (QImode, base_tag, tags[i]);
+      emit_library_call (ret,
+			 LCT_NORMAL,
+			 VOIDmode,
+			 plus_constant (ptr_mode, untagged_bases[i], bot),
+			 ptr_mode,
+			 tag_colour,
+			 QImode,
+			 gen_int_mode (size, ptr_mode),
+			 ptr_mode);
+    }
+}
+
+rtx_insn *
+hwasan_emit_uncolour_frame (rtx dynamic, rtx vars, rtx_insn *before)
+{
+  if (before)
+    push_to_sequence (before);
+  else
+    start_sequence ();
+
+  dynamic = convert_memory_address (ptr_mode, dynamic);
+  vars = convert_memory_address (ptr_mode, vars);
+
+  rtx top_rtx;
+  rtx bot_rtx;
+  if (STACK_GROWS_DOWNWARD)
+    {
+      top_rtx = vars;
+      bot_rtx = dynamic;
+    }
+  else
+    {
+      top_rtx = dynamic;
+      bot_rtx = vars;
+    }
+
+  rtx size_rtx = expand_simple_binop (Pmode, MINUS, top_rtx, bot_rtx,
+				  NULL_RTX, /* unsignedp = */0, OPTAB_DIRECT);
+
+  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (ret, LCT_NORMAL, VOIDmode,
+      bot_rtx, ptr_mode,
+      const0_rtx, QImode,
+      size_rtx, ptr_mode);
+
+  do_pending_stack_adjust ();
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+rtx
+hwasan_create_untagged_base (rtx orig_base)
+{
+  rtx untagged_base = gen_reg_rtx (Pmode);
+  rtx tag_mask = gen_int_mode ((1ULL << HWASAN_SHIFT) - 1, Pmode);
+  untagged_base = expand_binop (Pmode, and_optab,
+				orig_base, tag_mask,
+				untagged_base, true, OPTAB_DIRECT);
+  gcc_assert (untagged_base);
+  return untagged_base;
+}
+
+/* Needs to be GTY(()), because cgraph_build_static_cdtor may
+   invoke ggc_collect.  */
+static GTY(()) tree hwasan_ctor_statements;
+
+void
+hwasan_finish_file (void)
+{
+  /* Do not emit constructor initialisation for the kernel.
+     (the kernel has its own initialisation already).  */
+  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+    return;
+
+  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
+  flag_sanitize &= ~SANITIZE_HWADDRESS;
+  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
+  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
+  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
+  flag_sanitize |= SANITIZE_HWADDRESS;
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index e5c9e063c480d1392b6c2b395ec9d029b6d94209..d05f597b6434f39fe95d4f28dd2ef3ed463dd925 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -625,6 +625,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_UINT32_UINT32_PTR,
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
 		     BT_PTR)
 DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_SIZE, BT_VOID, BT_PTR, BT_UINT8,
+		     BT_SIZE)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 8bb7027aac7d8e5f06612946b567100faabaeb62..d030b9214936ce82727d01d063f24f6de6e2e935 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -237,6 +237,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
+				| SANITIZE_HWADDRESS \
 				| SANITIZE_UNDEFINED \
 				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 5a93447f520f1ea8b739249bfdfec56775c91f79..1e4d171e651ae10c5829e52248629c04b03c19f1 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -378,7 +378,14 @@ align_local_variable (tree decl, bool really_expand)
       if (really_expand)
 	SET_DECL_ALIGN (decl, align);
     }
-  return align / BITS_PER_UNIT;
+
+  unsigned int ret_align = align / BITS_PER_UNIT;
+
+  if (hwasan_sanitize_stack_p ())
+    ret_align = ret_align > HWASAN_TAG_GRANULE_SIZE
+      ? ret_align
+      : HWASAN_TAG_GRANULE_SIZE;
+  return ret_align;
 }
 
 /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
@@ -986,7 +993,7 @@ dump_stack_var_partition (void)
 
 static void
 expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
-			 poly_int64 offset)
+			 poly_int64 offset, rtx stack_base)
 {
   unsigned align;
   rtx x;
@@ -994,7 +1001,11 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   /* If this fails, we've overflowed the stack frame.  Error nicely?  */
   gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
 
-  x = plus_constant (Pmode, base, offset);
+  if (hwasan_sanitize_stack_p ())
+    x = hwasan_with_tag (base, offset);
+  else
+    x = plus_constant (Pmode, base, offset);
+
   x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
 		   ? TYPE_MODE (TREE_TYPE (decl))
 		   : DECL_MODE (SSAVAR (decl)), x);
@@ -1004,7 +1015,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
       /* Set alignment we actually gave this decl if it isn't an SSA name.
          If it is we generate stack slots only accidentally so it isn't as
 	 important, we'll simply use the alignment that is already set.  */
-      if (base == virtual_stack_vars_rtx)
+      if (base == stack_base)
 	offset -= frame_phase;
       align = known_alignment (offset);
       align *= BITS_PER_UNIT;
@@ -1030,9 +1041,19 @@ public:
      The vector is in reversed, highest offset pairs come first.  */
   auto_vec<HOST_WIDE_INT> asan_vec;
 
+  /* HWASAN records the poly_int64 so it can handle any stack variable.  */
+  auto_vec<poly_int64> hwasan_vec;
+  auto_vec<rtx> hwasan_untagged_base_vec;
+  auto_vec<rtx> hwasan_base_vec;
+
   /* Vector of partition representative decls in between the paddings.  */
   auto_vec<tree> asan_decl_vec;
 
+  /* Vector of tag offsets representing the tag for each stack variable.
+     Each offset determines the difference between the randomly generated
+     tag for the current frame and the tag for this stack variable.  */
+  auto_vec<uint8_t> hwasan_tag_vec;
+
   /* Base pseudo register for Address Sanitizer protected automatic vars.  */
   rtx asan_base;
 
@@ -1050,6 +1071,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
   size_t si, i, j, n = stack_vars_num;
   poly_uint64 large_size = 0, large_alloc = 0;
   rtx large_base = NULL;
+  rtx large_untagged_base = NULL;
   unsigned large_align = 0;
   bool large_allocation_done = false;
   tree decl;
@@ -1096,11 +1118,17 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	}
     }
 
+  if (hwasan_sanitize_stack_p () && data->asan_base == NULL)
+    {
+      data->asan_base = gen_reg_rtx (Pmode);
+      hwasan_record_base (data->asan_base);
+    }
+
   for (si = 0; si < n; ++si)
     {
       rtx base;
       unsigned base_align, alignb;
-      poly_int64 offset;
+      poly_int64 offset = 0;
 
       i = stack_vars_sorted[si];
 
@@ -1121,10 +1149,36 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       if (pred && !pred (i))
 	continue;
 
+      base = hwasan_sanitize_stack_p ()
+	? data->asan_base
+	: virtual_stack_vars_rtx;
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  base = virtual_stack_vars_rtx;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Allocate zero bytes to take advantage of the
+		 alloc_stack_frame_space logic of ensuring the stack is aligned
+		 despite having poly_int64's to deal with.
+
+		 There must be no tag granule "shared" between different
+		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
+		 chunk can have more than one object in it.
+
+		 We ensure this by forcing the end of the last bit of data to
+		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
+		 the start of each variable to be aligned to
+		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
+
+		 We can't align just one of the start or end, since there are
+		 untagged things stored on the stack that we have no control on
+		 the alignment and these can't share a tag granule with a
+		 tagged variable.  */
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	      offset = alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (offset);
+	      data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
+	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
 	  HOST_WIDE_INT prev_offset;
@@ -1204,6 +1258,9 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
 	      base_align = crtl->max_used_stack_slot_alignment;
 	    }
+
+	  if (hwasan_sanitize_stack_p ())
+	    data->hwasan_vec.safe_push (offset);
 	}
       else
 	{
@@ -1223,14 +1280,31 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      loffset = alloc_stack_frame_space
 		(rtx_to_poly_int64 (large_allocsize),
 		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
-	      large_base = get_dynamic_stack_base (loffset, large_align);
+	      large_base = get_dynamic_stack_base (loffset, large_align, base);
 	      large_allocation_done = true;
 	    }
-	  gcc_assert (large_base != NULL);
 
+	  gcc_assert (large_base != NULL);
 	  large_alloc = aligned_upper_bound (large_alloc, alignb);
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* An object with a large alignment requirement means that the
+		 alignment requirement is greater than the required alignment
+		 for tags.  */
+	      if (!large_untagged_base)
+		large_untagged_base = hwasan_create_untagged_base (large_base);
+	      data->hwasan_vec.safe_push (large_alloc);
+	      data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
+	    }
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Ensure the end of the variable is also aligned correctly.  */
+	      poly_int64 align_again =
+		aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (align_again);
+	    }
 
 	  base = large_base;
 	  base_align = large_align;
@@ -1242,7 +1316,21 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	{
 	  expand_one_stack_var_at (stack_vars[j].decl,
 				   base, base_align,
-				   offset);
+				   offset,
+				   hwasan_sanitize_stack_p ()
+				   ? data->asan_base
+				   : virtual_stack_vars_rtx);
+	}
+
+      if (hwasan_sanitize_stack_p ())
+	{
+	  /* Record the tag for this object in `data` so the prologue knows
+	     what tag to put in the shadow memory during cfgexpand.c.
+	     Then increment the tag so that the next object has a different
+	     tag to this object.  */
+	  data->hwasan_base_vec.safe_push (base);
+	  data->hwasan_tag_vec.safe_push (hwasan_current_tag ());
+	  hwasan_increment_tag ();
 	}
     }
 
@@ -1339,7 +1427,8 @@ expand_one_stack_var_1 (tree var)
   offset = alloc_stack_frame_space (size, byte_align);
 
   expand_one_stack_var_at (var, virtual_stack_vars_rtx,
-			   crtl->max_used_stack_slot_alignment, offset);
+			   crtl->max_used_stack_slot_alignment, offset,
+			   virtual_stack_vars_rtx);
 }
 
 /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
@@ -1552,8 +1641,13 @@ defer_stack_allocation (tree var, bool toplevel)
 
   /* If stack protection is enabled, *all* stack variables must be deferred,
      so that we can re-order the strings to the top of the frame.
-     Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || asan_sanitize_stack_p ())
+     Similarly for Address Sanitizer.
+     When tagging memory we defer all stack variables so we can handle them in
+     one place (handle here meaning ensure they are aligned and record
+     information on each variables position in the stack).  */
+  if (flag_stack_protect
+      || asan_sanitize_stack_p ()
+      || hwasan_sanitize_stack_p ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME
@@ -1938,6 +2032,8 @@ init_vars_expansion (void)
   /* Initialize local stack smashing state.  */
   has_protected_decls = false;
   has_short_buffer = false;
+  if (hwasan_sanitize_stack_p ())
+    hwasan_tag_init ();
 }
 
 /* Free up stack variable graph data.  */
@@ -2297,12 +2393,27 @@ expand_used_vars (void)
 	}
 
       expand_stack_vars (NULL, &data);
+
+      if (hwasan_sanitize_stack_p ())
+	hwasan_emit_prologue (data.hwasan_base_vec.address (),
+			      data.hwasan_untagged_base_vec.address (),
+			      data.hwasan_vec.address (),
+			      data.hwasan_tag_vec.address (),
+			      data.hwasan_vec.length ());
     }
 
   if (asan_sanitize_allocas_p () && cfun->calls_alloca)
     var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
 					      virtual_stack_vars_rtx,
 					      var_end_seq);
+  /* Here we clear tags fro the entire frame of this function.
+     We need to clear tags of *something* if we have tagged either local
+     variables or alloca objects.  */
+  else if (hwasan_sanitize_stack_p ()
+	   && (cfun->calls_alloca || stack_vars_num > 0))
+    var_end_seq = hwasan_emit_uncolour_frame (virtual_stack_dynamic_rtx,
+					      virtual_stack_vars_rtx,
+					      var_end_seq);
 
   fini_vars_expansion ();
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index bf53df715391128d6fbe9be4e77906650309ab2e..99949ba0b9317a89019ab5a6d9383e89f2d6ce3c 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2976,6 +2976,31 @@ A target hook which lets a backend compute the set of pressure classes to  be us
 True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that -fsanitize=hwaddress can work.
 @end deftypefn
 
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
+Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by TAG_OFFSET.
+The resulting RTX must either be a valid memory address or be able to get
+put into an operand with force_operand.  If overridden the more common case
+is that we force this into an operand using the backend hook
+"addtag_force_operand" that is called in force_operand.
+
+It is expected that that "addtag_force_operand" recognises the RTX
+generated by "addtag" and emits code to force that RTX into an operand.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG_FORCE_OPERAND (rtx @var{oper}, rtx @var{target})
+If the RTL expression OPER is of the form generated by targetm.memtag.addtag,
+then emit instructions to move the value into an operand (i.e. for
+force_operand).
+TARGET is an RTX suggestion of where to generate the value.
+This hook is most often implemented by emitting instructions to put the
+expression into a pseudo register, then returning that pseudo register.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_MEMTAG_GENTAG (rtx @var{base}, rtx @var{untagged})
+Set the BASE argument to UNTAGGED with some random tag.
+This function is used to generate a tagged base for the current stack frame.
+@end deftypefn
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 005cef05999d7c334f16ffa368903c3b66806231..ab18039b09f9b0a93338fa716d5d044555371ddc 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2376,6 +2376,12 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_ADDTAG
+
+@hook TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
+
+@hook TARGET_MEMTAG_GENTAG
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/explow.h b/gcc/explow.h
index 5110ad82d6a024fda1d3a3eaf80de40c5e6ad3b6..333948e0c69a1b1132e9a1d06707dc63f1226262 100644
--- a/gcc/explow.h
+++ b/gcc/explow.h
@@ -102,7 +102,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
 extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
 
 /* Returns the address of the dynamic stack space without allocating it.  */
-extern rtx get_dynamic_stack_base (poly_int64, unsigned);
+extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
 
 /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
 extern rtx align_dynamic_address (rtx, unsigned);
diff --git a/gcc/explow.c b/gcc/explow.c
index 7eb854bca4a6dcc5b15e5c42df1a5e88a19f2464..2728f7a4b1ee8ed0d20287b716a3d0ad5a97d84b 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1577,10 +1577,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
    OFFSET is the offset of the area into the virtual stack vars area.
 
    REQUIRED_ALIGN is the alignment (in bits) required for the region
-   of memory.  */
+   of memory.
+
+   BASE is the rtx of the base of this virtual stack vars area.
+   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+   on the stack.  */
 
 rtx
-get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
 {
   rtx target;
 
@@ -1588,7 +1592,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
     crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
 
   target = gen_reg_rtx (Pmode);
-  emit_move_insn (target, virtual_stack_vars_rtx);
+  emit_move_insn (target, base);
   target = expand_binop (Pmode, add_optab, target,
 			 gen_int_mode (offset, Pmode),
 			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
diff --git a/gcc/expr.c b/gcc/expr.c
index 7dc37a288ebffb99c990442cf339b848c5fa9d2e..2cd4bcb74cf92ccd7bd88ccb556b7e1eed2c6e06 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -7467,6 +7467,13 @@ force_operand (rtx value, rtx target)
       return subtarget;
     }
 
+  if (targetm.memtag.addtag_force_operand)
+    {
+      rtx ret = targetm.memtag.addtag_force_operand (value, target);
+      if (ret)
+	return ret;
+    }
+
   if (ARITHMETIC_P (value))
     {
       op2 = XEXP (value, 1);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 374d15007d868363d9b4fbf467e1e462abbca61a..7bd50715f24a2cb154b578e2abdea4e8fcdb2107 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
+/* Hardware Address Sanitizer.  */
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
+		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
+
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/target.def b/gcc/target.def
index 0ade31accab25bf121f135cbf02c6adfcd6e1476..badae860335e4a570f189c9f8011da5ab8c15439 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6715,6 +6715,37 @@ DEFHOOK
  pointers.  This feature means that -fsanitize=hwaddress can work.",
  bool, (), default_memtag_can_tag_addresses)
 
+DEFHOOK
+(addtag,
+ "Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by\
+ TAG_OFFSET.\n\
+The resulting RTX must either be a valid memory address or be able to get\n\
+put into an operand with force_operand.  If overridden the more common case\n\
+is that we force this into an operand using the backend hook\n\
+\"addtag_force_operand\" that is called in force_operand.\n\
+\n\
+It is expected that that \"addtag_force_operand\" recognises the RTX\n\
+generated by \"addtag\" and emits code to force that RTX into an operand.",
+rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
+default_memtag_addtag)
+
+DEFHOOK
+(addtag_force_operand,
+ "If the RTL expression OPER is of the form generated by targetm.memtag.addtag,\n\
+then emit instructions to move the value into an operand (i.e. for\n\
+force_operand).\n\
+TARGET is an RTX suggestion of where to generate the value.\n\
+This hook is most often implemented by emitting instructions to put the\n\
+expression into a pseudo register, then returning that pseudo register.",
+rtx, (rtx oper, rtx target), NULL)
+
+DEFHOOK
+(gentag,
+ "Set the BASE argument to UNTAGGED with some random tag.\n\
+This function is used to generate a tagged base for the current stack frame.",
+  void, (rtx base, rtx untagged),
+  default_memtag_gentag)
+
 HOOK_VECTOR_END (memtag)
 #undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 5aba67660f85406b9fd475e75a3cc65b0d1952f5..775e7abfba5ddd8f5ea774f0a4bcaf588a34dfbb 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -284,4 +284,6 @@ 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 void default_memtag_gentag (rtx, rtx);
+extern rtx default_memtag_addtag (rtx, poly_int64, uint8_t);
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index d7dd07db65c8248c2f170466db21449a56713d69..048c7f3cff5d87d0d40b93f6cf8cb41de670711d 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "flags.h"
 #include "explow.h"
+#include "expmed.h"
 #include "calls.h"
 #include "expr.h"
 #include "output.h"
@@ -83,6 +84,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "real.h"
 #include "langhooks.h"
 #include "sbitmap.h"
+#include "attribs.h"
+#include "asan.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -2374,4 +2377,78 @@ default_memtag_can_tag_addresses ()
   return false;
 }
 
+void
+default_memtag_gentag (rtx base, rtx untagged)
+{
+  gcc_assert (HWASAN_STACK);
+  if (HWASAN_RANDOM_FRAME_TAG)
+    {
+    rtx temp = gen_reg_rtx (QImode);
+    rtx ret = init_one_libfunc ("__hwasan_generate_tag");
+    rtx new_tag = emit_library_call_value (ret, temp, LCT_NORMAL, QImode);
+    emit_move_insn (base, untagged);
+    /* We know that `base` is not the stack pointer, since we never want to put
+      a randomly generated tag into the stack pointer.  Hence we can use
+      `store_bit_field` which on aarch64 generates a `bfi` which can not act on
+      the stack pointer.  */
+    store_bit_field (base, 8, 56, 0, 0, QImode, new_tag, false);
+    }
+  else
+    {
+      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
+	 In the future we may add the option emit random tags with inline
+	 instrumentation instead of function calls.  This would be the same
+	 between the kernel and userland.  */
+      emit_move_insn (base, untagged);
+    }
+}
+
+rtx
+default_memtag_addtag (rtx base, poly_int64 offset, uint8_t tag_offset)
+{
+  /* Need to look into what the most efficient code sequence is.
+     This is a code sequence that would be emitted *many* times, so we
+     want it as small as possible.
+
+     If the tag offset is greater that (1 << 7) then the most efficient
+     sequence here would give UB from signed integer overflow in the
+     poly_int64.  Hence in that case we emit the slightly less efficient
+     sequence.
+
+     There are two places where tag overflow is a question:
+       - Tagging the shadow stack.
+	  (both tagging and untagging).
+       - Tagging addressable pointers.
+
+     We need to ensure both behaviours are the same (i.e. that the tag that
+     ends up in a pointer after "overflowing" the tag bits with a tag addition
+     is the same that ends up in the shadow space).
+
+     The aim is that the behaviour of tag addition should follow modulo
+     wrapping in both instances.
+
+     The libhwasan code doesn't have any path that increments a pointers tag,
+     which means it has no opinion on what happens when a tag increment
+     overflows (and hence we can choose our own behaviour).  */
+
+  if (tag_offset < (1 << 7))
+    {
+      offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
+      return plus_constant (Pmode, base, offset);
+    }
+  else
+    {
+      /* This is the fallback, it would be nice if it had less instructions,
+	 but we can look for cleverer ways later.  */
+      uint64_t tag_mask = ~(0xFFUL << HWASAN_SHIFT);
+      rtx untagged_base = gen_rtx_AND (Pmode, GEN_INT (tag_mask), base);
+      rtx new_addr = plus_constant (Pmode, untagged_base, offset);
+
+      rtx original_tag_value = gen_rtx_LSHIFTRT (Pmode, base, GEN_INT (HWASAN_SHIFT));
+      rtx new_tag_value = plus_constant (Pmode, original_tag_value, tag_offset);
+      rtx new_tag = gen_rtx_ASHIFT (Pmode, new_tag_value, GEN_INT (HWASAN_SHIFT));
+      return gen_rtx_IOR (Pmode, new_addr, new_tag);
+    }
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 3920ef5c40f27b27a449dc6bf1da795f0d40e77b..96745deef07c5780bab2e8fcdd5cbda864198cb3 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -508,6 +508,9 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      if (flag_sanitize & SANITIZE_HWADDRESS)
+	hwasan_finish_file ();
+
       omp_finish_file ();
 
       hsa_output_brig ();


[-- Attachment #2: hwasan-patch14.patch --]
[-- Type: text/plain, Size: 34074 bytes --]

diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -1,7 +1,11 @@
 # This option enables -fsanitize=hwaddress for stage2 and stage3.
+# We need to disable random frame tags for bootstrap since the autoconf check
+# for which direction the stack is growing has UB that a random frame tag
+# breaks.  Running with a random frame tag gives approx. 50% chance of
+# bootstrap comparison diff in libiberty/alloca.c.
 
-STAGE2_CFLAGS += -fsanitize=hwaddress
-STAGE3_CFLAGS += -fsanitize=hwaddress
+STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
 POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
diff --git a/gcc/asan.h b/gcc/asan.h
index 7675f18a84ee3f187ba4cb40db0ce232f3958762..5f069690681cdb533a6ee8a3f8cd302a1866610a 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -23,6 +23,18 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void asan_function_start (void);
 extern void asan_finish_file (void);
+extern void hwasan_finish_file (void);
+extern void hwasan_record_base (rtx);
+extern uint8_t hwasan_current_tag ();
+extern void hwasan_increment_tag ();
+extern rtx hwasan_with_tag (rtx, poly_int64);
+extern void hwasan_tag_init ();
+extern rtx hwasan_create_untagged_base (rtx);
+extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
+extern rtx_insn *hwasan_emit_uncolour_frame (rtx, rtx, rtx_insn *);
+extern bool memory_tagging_p (void);
+extern bool hwasan_sanitize_stack_p (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -75,6 +87,31 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The values below define an ABI and are hard-coded to these values in
+   libhwasan, hence they can't be changed independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).  */
+#define HWASAN_TAG_SIZE 8
+/* Tag Granule of HWASAN shadow stack.
+   This is the size in real memory that each byte in the shadow memory refers
+   to.  I.e. if a variable is X bytes long in memory then it's colour in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   Most variables will need to be aligned to this amount since two variables
+   that are neighbours in memory and share a tag granule would need to share
+   the same colour (the shared tag granule can only store one colour).  */
+#define HWASAN_TAG_SHIFT_SIZE 4
+#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SHIFT_SIZE)
+/* Define the tag for the stack background.
+   This defines what colour the stack pointer will be and hence what tag all
+   variables that are not given special tags are (e.g. spilled registers,
+   and parameters passed on the stack).  */
+#define HWASAN_STACK_BACKGROUND 0
+/* How many bits to shift in order to access the tag bits.
+   The tag is stored in the top 8 bits of a pointer hence shifting 56 bits will
+   leave just the tag.  */
+#define HWASAN_SHIFT 56
+#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/asan.c b/gcc/asan.c
index a731bd490b4e78e916ae20fc9a0249c1fc04daa5..fca06159b5bcaf349750103d401c12a965414dcc 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -257,6 +257,9 @@ hash_set<tree> *asan_handled_variables = NULL;
 
 hash_set <tree> *asan_used_labels = NULL;
 
+static uint8_t tag_offset = 0;
+static rtx hwasan_base_ptr = NULL_RTX;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -1352,6 +1355,21 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+/* Returns whether we are tagging pointers and checking those tags on memory
+   access.  This is true when checking with either in software or hardware.  */
+bool
+memory_tagging_p ()
+{
+    return sanitize_flags_p (SANITIZE_HWADDRESS);
+}
+
+/* Are we tagging the stack?  */
+bool
+hwasan_sanitize_stack_p ()
+{
+  return (memory_tagging_p () && HWASAN_STACK);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -2884,6 +2902,11 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_VOID_PTR_UINT8_SIZE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				unsigned_char_type_node, size_type_node,
+				NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
@@ -2934,6 +2957,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NOTHROW_LIST
+#define ATTR_NOTHROW_LIST ECF_NOTHROW
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
@@ -3684,4 +3709,189 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+void
+hwasan_record_base (rtx base)
+{
+  /* Initialise base colour of the base register.
+     This has to be done as soon as the stack is getting expanded to ensure
+     anything emitted with `get_dynamic_stack_base` will use the value set here
+     instead of using a register without a value.
+     Especially note that RTL expansion of large aligned values does that.  */
+  targetm.memtag.gentag (base, virtual_stack_vars_rtx);
+  hwasan_base_ptr = base;
+}
+
+uint8_t
+hwasan_current_tag ()
+{
+  return tag_offset;
+}
+
+void
+hwasan_increment_tag ()
+{
+  uint8_t tag_bits = HWASAN_TAG_SIZE;
+  tag_offset = (tag_offset + 1) % (1 << tag_bits);
+}
+
+rtx
+hwasan_with_tag (rtx base, poly_int64 offset)
+{
+  gcc_assert (tag_offset < (1 << HWASAN_TAG_SIZE));
+  return targetm.memtag.addtag (base, offset, tag_offset);
+}
+
+/* Clear internal state for the next function.
+   This function is called before variables on the stack get expanded, in
+   `init_vars_expansion`.  */
+void
+hwasan_tag_init ()
+{
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
+  hwasan_base_ptr = NULL_RTX;
+  tag_offset = HWASAN_STACK_BACKGROUND + 1;
+}
+
+rtx
+hwasan_extract_tag (rtx tagged_pointer)
+{
+  rtx tag = expand_simple_binop (Pmode,
+				 LSHIFTRT,
+				 tagged_pointer,
+				 HWASAN_SHIFT_RTX,
+				 NULL_RTX,
+				 /* unsignedp = */0,
+				 OPTAB_DIRECT);
+  return gen_lowpart (QImode, tag);
+}
+
+void
+hwasan_emit_prologue (rtx *bases,
+		      rtx *untagged_bases,
+		      poly_int64 *offsets,
+		      uint8_t *tags,
+		      size_t length)
+{
+  /* We need untagged base pointers since libhwasan only accepts untagged
+    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
+    the base tag for an offset.  */
+  for (size_t i = 0; (i * 2) + 1 < length; i++)
+    {
+      poly_int64 start = offsets[i * 2];
+      poly_int64 end = offsets[(i * 2) + 1];
+
+      poly_int64 bot, top;
+      if (known_ge (start, end))
+	{
+	  top = start;
+	  bot = end;
+	}
+      else
+	{
+	  top = end;
+	  bot = start;
+	}
+      poly_int64 size = (top - bot);
+
+      /* Can't check that all poly_int64's are aligned, but still nice
+	 to check those that are compile-time constants.  */
+      HOST_WIDE_INT tmp;
+      if (top.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (bot.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (size.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      rtx base_tag = hwasan_extract_tag (bases[i]);
+      /* In the case of tag overflow we would want modulo wrapping -- which
+	 should be given from the `plus_constant` in QImode.  */
+      rtx tag_colour = plus_constant (QImode, base_tag, tags[i]);
+      emit_library_call (ret,
+			 LCT_NORMAL,
+			 VOIDmode,
+			 plus_constant (ptr_mode, untagged_bases[i], bot),
+			 ptr_mode,
+			 tag_colour,
+			 QImode,
+			 gen_int_mode (size, ptr_mode),
+			 ptr_mode);
+    }
+}
+
+rtx_insn *
+hwasan_emit_uncolour_frame (rtx dynamic, rtx vars, rtx_insn *before)
+{
+  if (before)
+    push_to_sequence (before);
+  else
+    start_sequence ();
+
+  dynamic = convert_memory_address (ptr_mode, dynamic);
+  vars = convert_memory_address (ptr_mode, vars);
+
+  rtx top_rtx;
+  rtx bot_rtx;
+  if (STACK_GROWS_DOWNWARD)
+    {
+      top_rtx = vars;
+      bot_rtx = dynamic;
+    }
+  else
+    {
+      top_rtx = dynamic;
+      bot_rtx = vars;
+    }
+
+  rtx size_rtx = expand_simple_binop (Pmode, MINUS, top_rtx, bot_rtx,
+				  NULL_RTX, /* unsignedp = */0, OPTAB_DIRECT);
+
+  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (ret, LCT_NORMAL, VOIDmode,
+      bot_rtx, ptr_mode,
+      const0_rtx, QImode,
+      size_rtx, ptr_mode);
+
+  do_pending_stack_adjust ();
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+rtx
+hwasan_create_untagged_base (rtx orig_base)
+{
+  rtx untagged_base = gen_reg_rtx (Pmode);
+  rtx tag_mask = gen_int_mode ((1ULL << HWASAN_SHIFT) - 1, Pmode);
+  untagged_base = expand_binop (Pmode, and_optab,
+				orig_base, tag_mask,
+				untagged_base, true, OPTAB_DIRECT);
+  gcc_assert (untagged_base);
+  return untagged_base;
+}
+
+/* Needs to be GTY(()), because cgraph_build_static_cdtor may
+   invoke ggc_collect.  */
+static GTY(()) tree hwasan_ctor_statements;
+
+void
+hwasan_finish_file (void)
+{
+  /* Do not emit constructor initialisation for the kernel.
+     (the kernel has its own initialisation already).  */
+  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+    return;
+
+  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
+  flag_sanitize &= ~SANITIZE_HWADDRESS;
+  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
+  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
+  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
+  flag_sanitize |= SANITIZE_HWADDRESS;
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index e5c9e063c480d1392b6c2b395ec9d029b6d94209..d05f597b6434f39fe95d4f28dd2ef3ed463dd925 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -625,6 +625,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_UINT32_UINT32_PTR,
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
 		     BT_PTR)
 DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_SIZE, BT_VOID, BT_PTR, BT_UINT8,
+		     BT_SIZE)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 8bb7027aac7d8e5f06612946b567100faabaeb62..d030b9214936ce82727d01d063f24f6de6e2e935 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -237,6 +237,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
+				| SANITIZE_HWADDRESS \
 				| SANITIZE_UNDEFINED \
 				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 5a93447f520f1ea8b739249bfdfec56775c91f79..1e4d171e651ae10c5829e52248629c04b03c19f1 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -378,7 +378,14 @@ align_local_variable (tree decl, bool really_expand)
       if (really_expand)
 	SET_DECL_ALIGN (decl, align);
     }
-  return align / BITS_PER_UNIT;
+
+  unsigned int ret_align = align / BITS_PER_UNIT;
+
+  if (hwasan_sanitize_stack_p ())
+    ret_align = ret_align > HWASAN_TAG_GRANULE_SIZE
+      ? ret_align
+      : HWASAN_TAG_GRANULE_SIZE;
+  return ret_align;
 }
 
 /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
@@ -986,7 +993,7 @@ dump_stack_var_partition (void)
 
 static void
 expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
-			 poly_int64 offset)
+			 poly_int64 offset, rtx stack_base)
 {
   unsigned align;
   rtx x;
@@ -994,7 +1001,11 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   /* If this fails, we've overflowed the stack frame.  Error nicely?  */
   gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
 
-  x = plus_constant (Pmode, base, offset);
+  if (hwasan_sanitize_stack_p ())
+    x = hwasan_with_tag (base, offset);
+  else
+    x = plus_constant (Pmode, base, offset);
+
   x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
 		   ? TYPE_MODE (TREE_TYPE (decl))
 		   : DECL_MODE (SSAVAR (decl)), x);
@@ -1004,7 +1015,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
       /* Set alignment we actually gave this decl if it isn't an SSA name.
          If it is we generate stack slots only accidentally so it isn't as
 	 important, we'll simply use the alignment that is already set.  */
-      if (base == virtual_stack_vars_rtx)
+      if (base == stack_base)
 	offset -= frame_phase;
       align = known_alignment (offset);
       align *= BITS_PER_UNIT;
@@ -1030,9 +1041,19 @@ public:
      The vector is in reversed, highest offset pairs come first.  */
   auto_vec<HOST_WIDE_INT> asan_vec;
 
+  /* HWASAN records the poly_int64 so it can handle any stack variable.  */
+  auto_vec<poly_int64> hwasan_vec;
+  auto_vec<rtx> hwasan_untagged_base_vec;
+  auto_vec<rtx> hwasan_base_vec;
+
   /* Vector of partition representative decls in between the paddings.  */
   auto_vec<tree> asan_decl_vec;
 
+  /* Vector of tag offsets representing the tag for each stack variable.
+     Each offset determines the difference between the randomly generated
+     tag for the current frame and the tag for this stack variable.  */
+  auto_vec<uint8_t> hwasan_tag_vec;
+
   /* Base pseudo register for Address Sanitizer protected automatic vars.  */
   rtx asan_base;
 
@@ -1050,6 +1071,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
   size_t si, i, j, n = stack_vars_num;
   poly_uint64 large_size = 0, large_alloc = 0;
   rtx large_base = NULL;
+  rtx large_untagged_base = NULL;
   unsigned large_align = 0;
   bool large_allocation_done = false;
   tree decl;
@@ -1096,11 +1118,17 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	}
     }
 
+  if (hwasan_sanitize_stack_p () && data->asan_base == NULL)
+    {
+      data->asan_base = gen_reg_rtx (Pmode);
+      hwasan_record_base (data->asan_base);
+    }
+
   for (si = 0; si < n; ++si)
     {
       rtx base;
       unsigned base_align, alignb;
-      poly_int64 offset;
+      poly_int64 offset = 0;
 
       i = stack_vars_sorted[si];
 
@@ -1121,10 +1149,36 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       if (pred && !pred (i))
 	continue;
 
+      base = hwasan_sanitize_stack_p ()
+	? data->asan_base
+	: virtual_stack_vars_rtx;
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  base = virtual_stack_vars_rtx;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Allocate zero bytes to take advantage of the
+		 alloc_stack_frame_space logic of ensuring the stack is aligned
+		 despite having poly_int64's to deal with.
+
+		 There must be no tag granule "shared" between different
+		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
+		 chunk can have more than one object in it.
+
+		 We ensure this by forcing the end of the last bit of data to
+		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
+		 the start of each variable to be aligned to
+		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
+
+		 We can't align just one of the start or end, since there are
+		 untagged things stored on the stack that we have no control on
+		 the alignment and these can't share a tag granule with a
+		 tagged variable.  */
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	      offset = alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (offset);
+	      data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
+	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
 	  HOST_WIDE_INT prev_offset;
@@ -1204,6 +1258,9 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
 	      base_align = crtl->max_used_stack_slot_alignment;
 	    }
+
+	  if (hwasan_sanitize_stack_p ())
+	    data->hwasan_vec.safe_push (offset);
 	}
       else
 	{
@@ -1223,14 +1280,31 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      loffset = alloc_stack_frame_space
 		(rtx_to_poly_int64 (large_allocsize),
 		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
-	      large_base = get_dynamic_stack_base (loffset, large_align);
+	      large_base = get_dynamic_stack_base (loffset, large_align, base);
 	      large_allocation_done = true;
 	    }
-	  gcc_assert (large_base != NULL);
 
+	  gcc_assert (large_base != NULL);
 	  large_alloc = aligned_upper_bound (large_alloc, alignb);
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* An object with a large alignment requirement means that the
+		 alignment requirement is greater than the required alignment
+		 for tags.  */
+	      if (!large_untagged_base)
+		large_untagged_base = hwasan_create_untagged_base (large_base);
+	      data->hwasan_vec.safe_push (large_alloc);
+	      data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
+	    }
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Ensure the end of the variable is also aligned correctly.  */
+	      poly_int64 align_again =
+		aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (align_again);
+	    }
 
 	  base = large_base;
 	  base_align = large_align;
@@ -1242,7 +1316,21 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	{
 	  expand_one_stack_var_at (stack_vars[j].decl,
 				   base, base_align,
-				   offset);
+				   offset,
+				   hwasan_sanitize_stack_p ()
+				   ? data->asan_base
+				   : virtual_stack_vars_rtx);
+	}
+
+      if (hwasan_sanitize_stack_p ())
+	{
+	  /* Record the tag for this object in `data` so the prologue knows
+	     what tag to put in the shadow memory during cfgexpand.c.
+	     Then increment the tag so that the next object has a different
+	     tag to this object.  */
+	  data->hwasan_base_vec.safe_push (base);
+	  data->hwasan_tag_vec.safe_push (hwasan_current_tag ());
+	  hwasan_increment_tag ();
 	}
     }
 
@@ -1339,7 +1427,8 @@ expand_one_stack_var_1 (tree var)
   offset = alloc_stack_frame_space (size, byte_align);
 
   expand_one_stack_var_at (var, virtual_stack_vars_rtx,
-			   crtl->max_used_stack_slot_alignment, offset);
+			   crtl->max_used_stack_slot_alignment, offset,
+			   virtual_stack_vars_rtx);
 }
 
 /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
@@ -1552,8 +1641,13 @@ defer_stack_allocation (tree var, bool toplevel)
 
   /* If stack protection is enabled, *all* stack variables must be deferred,
      so that we can re-order the strings to the top of the frame.
-     Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || asan_sanitize_stack_p ())
+     Similarly for Address Sanitizer.
+     When tagging memory we defer all stack variables so we can handle them in
+     one place (handle here meaning ensure they are aligned and record
+     information on each variables position in the stack).  */
+  if (flag_stack_protect
+      || asan_sanitize_stack_p ()
+      || hwasan_sanitize_stack_p ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME
@@ -1938,6 +2032,8 @@ init_vars_expansion (void)
   /* Initialize local stack smashing state.  */
   has_protected_decls = false;
   has_short_buffer = false;
+  if (hwasan_sanitize_stack_p ())
+    hwasan_tag_init ();
 }
 
 /* Free up stack variable graph data.  */
@@ -2297,12 +2393,27 @@ expand_used_vars (void)
 	}
 
       expand_stack_vars (NULL, &data);
+
+      if (hwasan_sanitize_stack_p ())
+	hwasan_emit_prologue (data.hwasan_base_vec.address (),
+			      data.hwasan_untagged_base_vec.address (),
+			      data.hwasan_vec.address (),
+			      data.hwasan_tag_vec.address (),
+			      data.hwasan_vec.length ());
     }
 
   if (asan_sanitize_allocas_p () && cfun->calls_alloca)
     var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
 					      virtual_stack_vars_rtx,
 					      var_end_seq);
+  /* Here we clear tags fro the entire frame of this function.
+     We need to clear tags of *something* if we have tagged either local
+     variables or alloca objects.  */
+  else if (hwasan_sanitize_stack_p ()
+	   && (cfun->calls_alloca || stack_vars_num > 0))
+    var_end_seq = hwasan_emit_uncolour_frame (virtual_stack_dynamic_rtx,
+					      virtual_stack_vars_rtx,
+					      var_end_seq);
 
   fini_vars_expansion ();
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index bf53df715391128d6fbe9be4e77906650309ab2e..99949ba0b9317a89019ab5a6d9383e89f2d6ce3c 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2976,6 +2976,31 @@ A target hook which lets a backend compute the set of pressure classes to  be us
 True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that -fsanitize=hwaddress can work.
 @end deftypefn
 
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
+Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by TAG_OFFSET.
+The resulting RTX must either be a valid memory address or be able to get
+put into an operand with force_operand.  If overridden the more common case
+is that we force this into an operand using the backend hook
+"addtag_force_operand" that is called in force_operand.
+
+It is expected that that "addtag_force_operand" recognises the RTX
+generated by "addtag" and emits code to force that RTX into an operand.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG_FORCE_OPERAND (rtx @var{oper}, rtx @var{target})
+If the RTL expression OPER is of the form generated by targetm.memtag.addtag,
+then emit instructions to move the value into an operand (i.e. for
+force_operand).
+TARGET is an RTX suggestion of where to generate the value.
+This hook is most often implemented by emitting instructions to put the
+expression into a pseudo register, then returning that pseudo register.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_MEMTAG_GENTAG (rtx @var{base}, rtx @var{untagged})
+Set the BASE argument to UNTAGGED with some random tag.
+This function is used to generate a tagged base for the current stack frame.
+@end deftypefn
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 005cef05999d7c334f16ffa368903c3b66806231..ab18039b09f9b0a93338fa716d5d044555371ddc 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2376,6 +2376,12 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_ADDTAG
+
+@hook TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
+
+@hook TARGET_MEMTAG_GENTAG
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/explow.h b/gcc/explow.h
index 5110ad82d6a024fda1d3a3eaf80de40c5e6ad3b6..333948e0c69a1b1132e9a1d06707dc63f1226262 100644
--- a/gcc/explow.h
+++ b/gcc/explow.h
@@ -102,7 +102,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
 extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
 
 /* Returns the address of the dynamic stack space without allocating it.  */
-extern rtx get_dynamic_stack_base (poly_int64, unsigned);
+extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
 
 /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
 extern rtx align_dynamic_address (rtx, unsigned);
diff --git a/gcc/explow.c b/gcc/explow.c
index 7eb854bca4a6dcc5b15e5c42df1a5e88a19f2464..2728f7a4b1ee8ed0d20287b716a3d0ad5a97d84b 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1577,10 +1577,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
    OFFSET is the offset of the area into the virtual stack vars area.
 
    REQUIRED_ALIGN is the alignment (in bits) required for the region
-   of memory.  */
+   of memory.
+
+   BASE is the rtx of the base of this virtual stack vars area.
+   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+   on the stack.  */
 
 rtx
-get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
 {
   rtx target;
 
@@ -1588,7 +1592,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
     crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
 
   target = gen_reg_rtx (Pmode);
-  emit_move_insn (target, virtual_stack_vars_rtx);
+  emit_move_insn (target, base);
   target = expand_binop (Pmode, add_optab, target,
 			 gen_int_mode (offset, Pmode),
 			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
diff --git a/gcc/expr.c b/gcc/expr.c
index 7dc37a288ebffb99c990442cf339b848c5fa9d2e..2cd4bcb74cf92ccd7bd88ccb556b7e1eed2c6e06 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -7467,6 +7467,13 @@ force_operand (rtx value, rtx target)
       return subtarget;
     }
 
+  if (targetm.memtag.addtag_force_operand)
+    {
+      rtx ret = targetm.memtag.addtag_force_operand (value, target);
+      if (ret)
+	return ret;
+    }
+
   if (ARITHMETIC_P (value))
     {
       op2 = XEXP (value, 1);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 374d15007d868363d9b4fbf467e1e462abbca61a..7bd50715f24a2cb154b578e2abdea4e8fcdb2107 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
+/* Hardware Address Sanitizer.  */
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
+		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
+
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/target.def b/gcc/target.def
index 0ade31accab25bf121f135cbf02c6adfcd6e1476..badae860335e4a570f189c9f8011da5ab8c15439 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6715,6 +6715,37 @@ DEFHOOK
  pointers.  This feature means that -fsanitize=hwaddress can work.",
  bool, (), default_memtag_can_tag_addresses)
 
+DEFHOOK
+(addtag,
+ "Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by\
+ TAG_OFFSET.\n\
+The resulting RTX must either be a valid memory address or be able to get\n\
+put into an operand with force_operand.  If overridden the more common case\n\
+is that we force this into an operand using the backend hook\n\
+\"addtag_force_operand\" that is called in force_operand.\n\
+\n\
+It is expected that that \"addtag_force_operand\" recognises the RTX\n\
+generated by \"addtag\" and emits code to force that RTX into an operand.",
+rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
+default_memtag_addtag)
+
+DEFHOOK
+(addtag_force_operand,
+ "If the RTL expression OPER is of the form generated by targetm.memtag.addtag,\n\
+then emit instructions to move the value into an operand (i.e. for\n\
+force_operand).\n\
+TARGET is an RTX suggestion of where to generate the value.\n\
+This hook is most often implemented by emitting instructions to put the\n\
+expression into a pseudo register, then returning that pseudo register.",
+rtx, (rtx oper, rtx target), NULL)
+
+DEFHOOK
+(gentag,
+ "Set the BASE argument to UNTAGGED with some random tag.\n\
+This function is used to generate a tagged base for the current stack frame.",
+  void, (rtx base, rtx untagged),
+  default_memtag_gentag)
+
 HOOK_VECTOR_END (memtag)
 #undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 5aba67660f85406b9fd475e75a3cc65b0d1952f5..775e7abfba5ddd8f5ea774f0a4bcaf588a34dfbb 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -284,4 +284,6 @@ 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 void default_memtag_gentag (rtx, rtx);
+extern rtx default_memtag_addtag (rtx, poly_int64, uint8_t);
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index d7dd07db65c8248c2f170466db21449a56713d69..048c7f3cff5d87d0d40b93f6cf8cb41de670711d 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "flags.h"
 #include "explow.h"
+#include "expmed.h"
 #include "calls.h"
 #include "expr.h"
 #include "output.h"
@@ -83,6 +84,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "real.h"
 #include "langhooks.h"
 #include "sbitmap.h"
+#include "attribs.h"
+#include "asan.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -2374,4 +2377,78 @@ default_memtag_can_tag_addresses ()
   return false;
 }
 
+void
+default_memtag_gentag (rtx base, rtx untagged)
+{
+  gcc_assert (HWASAN_STACK);
+  if (HWASAN_RANDOM_FRAME_TAG)
+    {
+    rtx temp = gen_reg_rtx (QImode);
+    rtx ret = init_one_libfunc ("__hwasan_generate_tag");
+    rtx new_tag = emit_library_call_value (ret, temp, LCT_NORMAL, QImode);
+    emit_move_insn (base, untagged);
+    /* We know that `base` is not the stack pointer, since we never want to put
+      a randomly generated tag into the stack pointer.  Hence we can use
+      `store_bit_field` which on aarch64 generates a `bfi` which can not act on
+      the stack pointer.  */
+    store_bit_field (base, 8, 56, 0, 0, QImode, new_tag, false);
+    }
+  else
+    {
+      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
+	 In the future we may add the option emit random tags with inline
+	 instrumentation instead of function calls.  This would be the same
+	 between the kernel and userland.  */
+      emit_move_insn (base, untagged);
+    }
+}
+
+rtx
+default_memtag_addtag (rtx base, poly_int64 offset, uint8_t tag_offset)
+{
+  /* Need to look into what the most efficient code sequence is.
+     This is a code sequence that would be emitted *many* times, so we
+     want it as small as possible.
+
+     If the tag offset is greater that (1 << 7) then the most efficient
+     sequence here would give UB from signed integer overflow in the
+     poly_int64.  Hence in that case we emit the slightly less efficient
+     sequence.
+
+     There are two places where tag overflow is a question:
+       - Tagging the shadow stack.
+	  (both tagging and untagging).
+       - Tagging addressable pointers.
+
+     We need to ensure both behaviours are the same (i.e. that the tag that
+     ends up in a pointer after "overflowing" the tag bits with a tag addition
+     is the same that ends up in the shadow space).
+
+     The aim is that the behaviour of tag addition should follow modulo
+     wrapping in both instances.
+
+     The libhwasan code doesn't have any path that increments a pointers tag,
+     which means it has no opinion on what happens when a tag increment
+     overflows (and hence we can choose our own behaviour).  */
+
+  if (tag_offset < (1 << 7))
+    {
+      offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
+      return plus_constant (Pmode, base, offset);
+    }
+  else
+    {
+      /* This is the fallback, it would be nice if it had less instructions,
+	 but we can look for cleverer ways later.  */
+      uint64_t tag_mask = ~(0xFFUL << HWASAN_SHIFT);
+      rtx untagged_base = gen_rtx_AND (Pmode, GEN_INT (tag_mask), base);
+      rtx new_addr = plus_constant (Pmode, untagged_base, offset);
+
+      rtx original_tag_value = gen_rtx_LSHIFTRT (Pmode, base, GEN_INT (HWASAN_SHIFT));
+      rtx new_tag_value = plus_constant (Pmode, original_tag_value, tag_offset);
+      rtx new_tag = gen_rtx_ASHIFT (Pmode, new_tag_value, GEN_INT (HWASAN_SHIFT));
+      return gen_rtx_IOR (Pmode, new_addr, new_tag);
+    }
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 3920ef5c40f27b27a449dc6bf1da795f0d40e77b..96745deef07c5780bab2e8fcdd5cbda864198cb3 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -508,6 +508,9 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      if (flag_sanitize & SANITIZE_HWADDRESS)
+	hwasan_finish_file ();
+
       omp_finish_file ();
 
       hsa_output_brig ();


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 9/X] [libsanitizer] Remove lazy thread initialisation
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (14 preceding siblings ...)
  2019-11-05 11:35   ` [PATCH 17/X] [libsanitizer] Add hwasan Exception handling Matthew Malcomson
@ 2019-11-05 11:35   ` Matthew Malcomson
  2019-11-05 11:35   ` [PATCH 18/X] [libsanitizer] Add in MTE stubs Matthew Malcomson
                     ` (13 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:35 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 3361 bytes --]

Backport from llvm upstream (monorepo revision 91167e2).
This was an experiment made possible by a non-standard feature of the
Android dynamic loader.

libsanitizer/ChangeLog:

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

	* hwasan/hwasan_interceptors.cpp (HwasanThreadStartFunc):
	Re-introduce.
	(pthread_create): Use HwasanThreadStartFunc to initialise the
	sanitizer for each thread as it starts.
	* hwasan/hwasan_linux.cpp (GetCurrentThread): Assume thread is
	initialised.



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


diff --git a/libsanitizer/hwasan/hwasan_interceptors.cpp b/libsanitizer/hwasan/hwasan_interceptors.cpp
index 4f9bd3469eb10ca2cf3108326308e45e7a9d38b6..44e569ee6d721a99a3333a21ebf1a51fb33b6e7a 100644
--- a/libsanitizer/hwasan/hwasan_interceptors.cpp
+++ b/libsanitizer/hwasan/hwasan_interceptors.cpp
@@ -202,23 +202,33 @@ INTERCEPTOR_ALIAS(__sanitizer_struct_mallinfo, mallinfo);
 INTERCEPTOR_ALIAS(int, mallopt, int cmd, int value);
 INTERCEPTOR_ALIAS(void, malloc_stats, void);
 #endif
-#endif // HWASAN_WITH_INTERCEPTORS
 
+struct ThreadStartArg {
+  thread_callback_t callback;
+  void *param;
+};
+
+static void *HwasanThreadStartFunc(void *arg) {
+  __hwasan_thread_enter();
+  ThreadStartArg A = *reinterpret_cast<ThreadStartArg*>(arg);
+  UnmapOrDie(arg, GetPageSizeCached());
+  return A.callback(A.param);
+}
 
-#if HWASAN_WITH_INTERCEPTORS && !defined(__aarch64__)
-INTERCEPTOR(int, pthread_create, void *th, void *attr,
-            void *(*callback)(void *), void *param) {
+INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
+            void * param) {
   ScopedTaggingDisabler disabler;
+  ThreadStartArg *A = reinterpret_cast<ThreadStartArg *> (MmapOrDie(
+      GetPageSizeCached(), "pthread_create"));
+  *A = {callback, param};
   int res = REAL(pthread_create)(UntagPtr(th), UntagPtr(attr),
-                                 callback, param);
+                                 &HwasanThreadStartFunc, A);
   return res;
 }
-#endif
 
-#if HWASAN_WITH_INTERCEPTORS
 DEFINE_REAL(int, vfork)
 DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
-#endif
+#endif // HWASAN_WITH_INTERCEPTORS
 
 #if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
 // Get and/or change the set of blocked signals.
@@ -331,9 +341,7 @@ void InitializeInterceptors() {
 #if defined(__linux__)
   INTERCEPT_FUNCTION(vfork);
 #endif  // __linux__
-#if !defined(__aarch64__)
   INTERCEPT_FUNCTION(pthread_create);
-#endif  // __aarch64__
 #endif
 
   inited = 1;
diff --git a/libsanitizer/hwasan/hwasan_linux.cpp b/libsanitizer/hwasan/hwasan_linux.cpp
index dfef11883a284dae0c96cfcc6a8fd1cc06c24d71..ed0f30161b023bf5927aa4a471f6a7c3edc8edf6 100644
--- a/libsanitizer/hwasan/hwasan_linux.cpp
+++ b/libsanitizer/hwasan/hwasan_linux.cpp
@@ -354,12 +354,7 @@ void AndroidTestTlsSlot() {}
 #endif
 
 Thread *GetCurrentThread() {
-  uptr *ThreadLong = GetCurrentThreadLongPtr();
-#if HWASAN_WITH_INTERCEPTORS
-  if (!*ThreadLong)
-    __hwasan_thread_enter();
-#endif
-  auto *R = (StackAllocationsRingBuffer *)ThreadLong;
+  auto *R = (StackAllocationsRingBuffer *)GetCurrentThreadLongPtr();
   return hwasanThreadList().GetThreadByBufferAddress((uptr)(R->Next()));
 }
 


[-- Attachment #2: hwasan-patch09.patch --]
[-- Type: text/plain, Size: 2687 bytes --]

diff --git a/libsanitizer/hwasan/hwasan_interceptors.cpp b/libsanitizer/hwasan/hwasan_interceptors.cpp
index 4f9bd3469eb10ca2cf3108326308e45e7a9d38b6..44e569ee6d721a99a3333a21ebf1a51fb33b6e7a 100644
--- a/libsanitizer/hwasan/hwasan_interceptors.cpp
+++ b/libsanitizer/hwasan/hwasan_interceptors.cpp
@@ -202,23 +202,33 @@ INTERCEPTOR_ALIAS(__sanitizer_struct_mallinfo, mallinfo);
 INTERCEPTOR_ALIAS(int, mallopt, int cmd, int value);
 INTERCEPTOR_ALIAS(void, malloc_stats, void);
 #endif
-#endif // HWASAN_WITH_INTERCEPTORS
 
+struct ThreadStartArg {
+  thread_callback_t callback;
+  void *param;
+};
+
+static void *HwasanThreadStartFunc(void *arg) {
+  __hwasan_thread_enter();
+  ThreadStartArg A = *reinterpret_cast<ThreadStartArg*>(arg);
+  UnmapOrDie(arg, GetPageSizeCached());
+  return A.callback(A.param);
+}
 
-#if HWASAN_WITH_INTERCEPTORS && !defined(__aarch64__)
-INTERCEPTOR(int, pthread_create, void *th, void *attr,
-            void *(*callback)(void *), void *param) {
+INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
+            void * param) {
   ScopedTaggingDisabler disabler;
+  ThreadStartArg *A = reinterpret_cast<ThreadStartArg *> (MmapOrDie(
+      GetPageSizeCached(), "pthread_create"));
+  *A = {callback, param};
   int res = REAL(pthread_create)(UntagPtr(th), UntagPtr(attr),
-                                 callback, param);
+                                 &HwasanThreadStartFunc, A);
   return res;
 }
-#endif
 
-#if HWASAN_WITH_INTERCEPTORS
 DEFINE_REAL(int, vfork)
 DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
-#endif
+#endif // HWASAN_WITH_INTERCEPTORS
 
 #if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
 // Get and/or change the set of blocked signals.
@@ -331,9 +341,7 @@ void InitializeInterceptors() {
 #if defined(__linux__)
   INTERCEPT_FUNCTION(vfork);
 #endif  // __linux__
-#if !defined(__aarch64__)
   INTERCEPT_FUNCTION(pthread_create);
-#endif  // __aarch64__
 #endif
 
   inited = 1;
diff --git a/libsanitizer/hwasan/hwasan_linux.cpp b/libsanitizer/hwasan/hwasan_linux.cpp
index dfef11883a284dae0c96cfcc6a8fd1cc06c24d71..ed0f30161b023bf5927aa4a471f6a7c3edc8edf6 100644
--- a/libsanitizer/hwasan/hwasan_linux.cpp
+++ b/libsanitizer/hwasan/hwasan_linux.cpp
@@ -354,12 +354,7 @@ void AndroidTestTlsSlot() {}
 #endif
 
 Thread *GetCurrentThread() {
-  uptr *ThreadLong = GetCurrentThreadLongPtr();
-#if HWASAN_WITH_INTERCEPTORS
-  if (!*ThreadLong)
-    __hwasan_thread_enter();
-#endif
-  auto *R = (StackAllocationsRingBuffer *)ThreadLong;
+  auto *R = (StackAllocationsRingBuffer *)GetCurrentThreadLongPtr();
   return hwasanThreadList().GetThreadByBufferAddress((uptr)(R->Next()));
 }
 


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 18/X] [libsanitizer] Add in MTE stubs
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (15 preceding siblings ...)
  2019-11-05 11:35   ` [PATCH 9/X] [libsanitizer] Remove lazy thread initialisation Matthew Malcomson
@ 2019-11-05 11:35   ` Matthew Malcomson
  2019-11-05 11:35   ` [PATCH 13/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
                     ` (12 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:35 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 39376 bytes --]

This patch in the series is just for demonstration, here we add stubs
where MTE would be implemented.

We also add a new flag to request memory tagging as a sanitizer option.
The new flag for memory tagging is `-fsanitize=memtag`, which is in line
with the flag clang uses to request memory tagging.

At the moment all implementations are dummies of some sort, the assembly
generated uses `mov` instead of `irg`, `add` instead of `addg`, and
`sub` instead of `subg`.  This should mean the binaries behave the same
as MTE binaries but for ignoring tags.

For a hardware implementation of memory tagging checks are done
automatically so adding HWASAN_CHECK is not needed.  This means that the
`hwasan` pass is not needed.
Similarly, much of the `sanopt` pass is not needed when compiling for
hardware memory tagging -- though there is still need for handling
HWASAN_MARK.

This patch gives backends extra control over how a tag is stored in a
pointer and how many real-memory bytes is represented by each byte in
the shadow space.

One final difference between memtag and hwasan is that memtag can't use
the ASAN_POISON optimisation.
This optimisation replaces accesses to a variable that has just been
poisoned with an internal function that will be used to report an error
without needing to check the access.

This provides no benefit for memtag since there tends to be no
instructions allowing a report of a memory fault outside of mis-tagging
some memory and attempting to access it.

The optimisation is hence disabled for memory tagging since it provides
no benefit and would require all backends that wanted this feature to
implement a similar dummy hook.

gcc/ChangeLog:

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

	* asan.c (hwasan_tag_init): Choose initialisation value based on
	memtag vs hwasan.
	(memory_tagging_p): Check for either hwaddress or memtag.
	(hwasan_emit_prologue): Account for memtag.
	(hwasan_emit_uncolour_frame): Account for memtag.
	(hwasan_finish_file): Assert not called for memtag.
	(hwasan_expand_check_ifn): Assert not called for memtag.
	(gate_hwasan): Don't run when have memtag.
	* asan.h (HWASAN_TAG_SIZE): Use backend hook if memtag.
	(HWASAN_TAG_GRANULE_SIZE): Use backend hook if memtag.
	(HWASAN_SHIFT): New.
	(HWASAN_SHIFT_RTX): New.
	(HWASAN_TAG_SHIFT_SIZE): New.
	* builtins.c (expand_builtin_alloca): Extra TODO comment.
	(expand_stack_restore): Extra TODO comment.
	* cfgexpand.c (expand_stack_vars): Only bother untagging bases
	for hwasan.
	* config/aarch64/aarch64.c (aarch64_classify_address): Account
	for addtag unspec marker.
	(aarch64_has_memtag_isa): New hook.
	(aarch64_tag_memory): Add dummy hook.
	(aarch64_gentag): Add dummy hook.
	(aarch64_addtag): New hook.
	(aarch64_addtag_force_operand): New hook.
	(TARGET_MEMTAG_HAS_MEMORY_TAGGING): New.
	(TARGET_MEMTAG_TAG): New.
	(TARGET_MEMTAG_GENTAG): New.
	(TARGET_MEMTAG_ADDTAG): New.
	(TARGET_MEMTAG_ADDTAG_FORCE_OPERAND): New.
	* config/aarch64/aarch64.h (AARCH64_ISA_MEMTAG): New macro.
	* config/aarch64/aarch64.md (random_tag, plain_offset_tagdi):
	New.
	(unspec enum): Add GENTAG and ADDTAG markers.
	* config/aarch64/predicates.md (aarch64_MTE_add_temp,
	aarch64_MTE_tag_offset, aarch64_MTE_value_offset): New.
	* doc/tm.texi: Document new hooks.
	* doc/tm.texi.in: Document new hooks.
	* flag-types.h (enum sanitize_code): Add MEMTAG enum.
	* gcc.c (sanitize_spec_function): Account for MEMTAG option.
	* internal-fn.c (expand_HWASAN_MARK): Account for memtag.
	* opts.c (finish_options): Ensure MEMTAG conflicts with ASAN,
	HWASAN, and THREAD.
	(finish_options): Turn on stack tagging for memtag.
	(sanitizer_opts): Add MEMTAG option.
	* target.def (targetm.memtag.has_memory_tagging): New.
	(targetm.memtag.tag_size): New.
	(targetm.memtag.granule_size): New.
	(targetm.memtag.copy_tag): New.
	(targetm.memtag.tag): New.
	* targhooks.c (default_memtag_has_memory_tagging): New.
	(default_memtag_tag_size): New.
	(default_memtag_granule_size): New.
	(default_memtag_copy_tag): New.
	* targhooks.h (default_memtag_tag_size): New decl.
	(default_memtag_granule_size): New decl.
	(default_memtag_copy_tag): New decl.
	* tree-ssa.c (execute_update_addresses_taken): Avoid ASAN_POISON
	optimisation for memtag.

gcc/testsuite/ChangeLog:

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

	* gcc.dg/hwasan/poly-int-stack-vars.c: New test.



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


diff --git a/gcc/asan.h b/gcc/asan.h
index ff6adf2391ee1602a3c15755312a04f82d6369ce..71dbaee708d0e64911f568503655478b8720f494 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -27,10 +27,10 @@ extern void hwasan_finish_file (void);
 extern void hwasan_record_base (rtx);
 extern uint8_t hwasan_current_tag ();
 extern void hwasan_increment_tag ();
+extern rtx hwasan_extract_tag (rtx);
 extern rtx hwasan_with_tag (rtx, poly_int64);
 extern void hwasan_tag_init ();
 extern rtx hwasan_create_untagged_base (rtx);
-extern rtx hwasan_extract_tag (rtx tagged_pointer);
 extern rtx hwasan_base ();
 extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
 extern rtx_insn *hwasan_emit_uncolour_frame (rtx, rtx, rtx_insn *);
@@ -95,8 +95,12 @@ extern hash_set <tree> *asan_used_labels;
 /* NOTE: The values below define an ABI and are hard-coded to these values in
    libhwasan, hence they can't be changed independently here.  */
 /* How many bits are used to store a tag in a pointer.
-   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).  */
-#define HWASAN_TAG_SIZE 8
+   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).
+   For aarch64 MTE we have 4 bits per colour and that is advertised by the
+   backend hook.  */
+#define HWASAN_TAG_SIZE (sanitize_flags_p (SANITIZE_MEMTAG) \
+			 ? targetm.memtag.tag_size () \
+			 : 8)
 /* Tag Granule of HWASAN shadow stack.
    This is the size in real memory that each byte in the shadow memory refers
    to.  I.e. if a variable is X bytes long in memory then it's colour in shadow
@@ -105,7 +109,12 @@ extern hash_set <tree> *asan_used_labels;
    that are neighbours in memory and share a tag granule would need to share
    the same colour (the shared tag granule can only store one colour).  */
 #define HWASAN_TAG_SHIFT_SIZE 4
-#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SHIFT_SIZE)
+#define HWASAN_TAG_GRANULE_SIZE (sanitize_flags_p (SANITIZE_MEMTAG) \
+				 ? targetm.memtag.granule_size () \
+				 : (1ULL << HWASAN_TAG_SHIFT_SIZE))
+
+/* The following HWASAN_* macros are only used for HWASAN (not MEMTAG), which
+   is why there is no predicate.  */
 /* Define the tag for the stack background.
    This defines what colour the stack pointer will be and hence what tag all
    variables that are not given special tags are (e.g. spilled registers,
diff --git a/gcc/asan.c b/gcc/asan.c
index ef7c90e3358c8fa880b8e4002996f27541c26953..5769d1236908e6d8c75018f04f855928665e4126 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -1480,7 +1480,8 @@ asan_redzone_buffer::flush_if_full (void)
 bool
 memory_tagging_p ()
 {
-    return sanitize_flags_p (SANITIZE_HWADDRESS);
+    return sanitize_flags_p (SANITIZE_HWADDRESS)
+      || sanitize_flags_p (SANITIZE_MEMTAG);
 }
 
 /* Are we tagging the stack?  */
@@ -3952,7 +3953,9 @@ hwasan_tag_init ()
   asan_used_labels = NULL;
 
   hwasan_base_ptr = NULL_RTX;
-  tag_offset = HWASAN_STACK_BACKGROUND + 1;
+  tag_offset = sanitize_flags_p (SANITIZE_MEMTAG)
+    ? 0
+    : HWASAN_STACK_BACKGROUND + 1;
 }
 
 tree
@@ -4220,20 +4223,31 @@ hwasan_emit_prologue (rtx *bases,
       if (size.is_constant (&tmp))
 	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
 
-      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
-      rtx base_tag = hwasan_extract_tag (bases[i]);
-      /* In the case of tag overflow we would want modulo wrapping -- which
-	 should be given from the `plus_constant` in QImode.  */
-      rtx tag_colour = plus_constant (QImode, base_tag, tags[i]);
-      emit_library_call (ret,
-			 LCT_NORMAL,
-			 VOIDmode,
-			 plus_constant (ptr_mode, untagged_bases[i], bot),
-			 ptr_mode,
-			 tag_colour,
-			 QImode,
-			 gen_int_mode (size, ptr_mode),
-			 ptr_mode);
+      if (sanitize_flags_p (SANITIZE_HWADDRESS))
+	{
+	  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+	  rtx base_tag = hwasan_extract_tag (bases[i]);
+	  /* In the case of tag overflow we would want modulo wrapping -- which
+	     should be given from the `plus_constant` in QImode.  */
+	  rtx tag_colour = plus_constant (QImode, base_tag, tags[i]);
+	  emit_library_call (ret,
+			     LCT_NORMAL,
+			     VOIDmode,
+			     plus_constant (ptr_mode, untagged_bases[i], bot),
+			     ptr_mode,
+			     tag_colour,
+			     QImode,
+			     gen_int_mode (size, ptr_mode),
+			     ptr_mode);
+	}
+      else
+	{
+	  gcc_assert (sanitize_flags_p (SANITIZE_MEMTAG));
+	  targetm.memtag.tag (bases[i],
+			      bot,
+			      tags[i],
+			      gen_int_mode (size, ptr_mode));
+	}
     }
 }
 
@@ -4264,11 +4278,20 @@ hwasan_emit_uncolour_frame (rtx dynamic, rtx vars, rtx_insn *before)
   rtx size_rtx = expand_simple_binop (Pmode, MINUS, top_rtx, bot_rtx,
 				  NULL_RTX, /* unsignedp = */0, OPTAB_DIRECT);
 
-  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
-  emit_library_call (ret, LCT_NORMAL, VOIDmode,
-      bot_rtx, ptr_mode,
-      const0_rtx, QImode,
-      size_rtx, ptr_mode);
+  if (sanitize_flags_p (SANITIZE_HWADDRESS))
+    {
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      emit_library_call (ret, LCT_NORMAL, VOIDmode,
+			 bot_rtx, ptr_mode,
+			 const0_rtx, QImode,
+			 size_rtx, ptr_mode);
+    }
+  else
+    {
+      gcc_assert (sanitize_flags_p (SANITIZE_MEMTAG));
+      targetm.memtag.copy_tag (bot_rtx, stack_pointer_rtx);
+      targetm.memtag.tag (bot_rtx, 0, 0, size_rtx);
+    }
 
   do_pending_stack_adjust ();
   rtx_insn *insns = get_insns ();
@@ -4301,6 +4324,8 @@ static GTY(()) tree hwasan_ctor_statements;
 void
 hwasan_finish_file (void)
 {
+  gcc_assert (sanitize_flags_p (SANITIZE_HWADDRESS));
+
   /* Do not emit constructor initialisation for the kernel.
      (the kernel has its own initialisation already).  */
   if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
@@ -4355,6 +4380,7 @@ hwasan_check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
 bool
 hwasan_expand_check_ifn (gimple_stmt_iterator *iter, bool)
 {
+  gcc_assert (sanitize_flags_p (SANITIZE_HWADDRESS));
   gimple *g = gsi_stmt (*iter);
   location_t loc = gimple_location (g);
   bool recover_p;
@@ -4448,7 +4474,7 @@ hwasan_expand_mark_ifn (gimple_stmt_iterator *)
 bool
 gate_hwasan ()
 {
-  return memory_tagging_p ();
+  return sanitize_flags_p (SANITIZE_HWADDRESS);
 }
 
 namespace {
diff --git a/gcc/builtins.c b/gcc/builtins.c
index f8063c138a340a06d45b01c9bb7f43caf75e78b2..416ee2b631d22ffab0ca428bc7fec9127382ef3e 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -5391,6 +5391,17 @@ expand_builtin_frame_address (tree fndecl, tree exp)
 static rtx
 expand_builtin_alloca (tree exp)
 {
+  /* TODO For hardware memory tagging we will need to call the backend to tag
+     this memory since the `hwasan` pass will not be run.
+
+     The `hwasan` pass is mainly to add HWASAN_CHECK internal functions where
+     checks should be made.  With hardware memory tagging the checks are done
+     automatically by the architecture.
+
+     The `hwasan` pass also modifies the behaviour of the alloca builtin
+     function in a target-independent manner, but when memory tagging is
+     handled by the backend it is more convenient to handle the tagging in the
+     alloca hook.  */
   rtx op0;
   rtx result;
   unsigned int align;
@@ -7012,6 +7023,9 @@ expand_builtin_set_thread_pointer (tree exp)
 static void
 expand_stack_restore (tree var)
 {
+  /* TODO If memory tagging is enabled through the hardware we need to uncolour
+     the stack from where we are to where we're going. (i.e. colour in the
+     background stack colour).  */
   rtx_insn *prev;
   rtx sa = expand_normal (var);
 
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 1e4d171e651ae10c5829e52248629c04b03c19f1..53b4658aa74c1e369fb139b0a29cdb6dea41dc3b 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -1043,6 +1043,8 @@ public:
 
   /* HWASAN records the poly_int64 so it can handle any stack variable.  */
   auto_vec<poly_int64> hwasan_vec;
+  /* HWASAN needs to record untagged base pointers when there isn't hardware
+     memory tagging enabled by the architecture.  */
   auto_vec<rtx> hwasan_untagged_base_vec;
   auto_vec<rtx> hwasan_base_vec;
 
@@ -1177,7 +1179,8 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
 	      offset = alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
 	      data->hwasan_vec.safe_push (offset);
-	      data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
+	      if (sanitize_flags_p (SANITIZE_HWADDRESS))
+		data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
 	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
@@ -1291,10 +1294,18 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      /* An object with a large alignment requirement means that the
 		 alignment requirement is greater than the required alignment
 		 for tags.  */
-	      if (!large_untagged_base)
-		large_untagged_base = hwasan_create_untagged_base (large_base);
 	      data->hwasan_vec.safe_push (large_alloc);
-	      data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
+
+	      if (sanitize_flags_p (SANITIZE_HWADDRESS))
+	      {
+		/* We only need to record the untagged bases for HWASAN, since
+		   the runtime library for that doesn't accept tagged pointers.
+		   For hardware implementations of memory tagging there is no
+		   use of recording these untagged versions.  */
+		if (!large_untagged_base)
+		  large_untagged_base = hwasan_create_untagged_base (large_base);
+		data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
+	      }
 	    }
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
index 7bbeed453cf87382b1776ff52991b5cf6ab9204e..7f23d377308f3b517e4ae08eba3a56e8c6565e8a 100644
--- a/gcc/config/aarch64/aarch64.h
+++ b/gcc/config/aarch64/aarch64.h
@@ -247,6 +247,7 @@ extern unsigned aarch64_architecture_version;
 #define AARCH64_ISA_RCPC8_4	   (aarch64_isa_flags & AARCH64_FL_RCPC8_4)
 #define AARCH64_ISA_V8_5	   (aarch64_isa_flags & AARCH64_FL_V8_5)
 #define AARCH64_ISA_TME		   (aarch64_isa_flags & AARCH64_FL_TME)
+#define AARCH64_ISA_MEMTAG	   (aarch64_isa_flags & AARCH64_FL_MEMTAG)
 
 /* Crypto is an optional extension to AdvSIMD.  */
 #define TARGET_CRYPTO (TARGET_SIMD && AARCH64_ISA_CRYPTO)
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index c556bcd1c37c3c4fdd9a829a28ee4ff56819b89e..a21b5918859305dd6301ac7cb3a4e16271b3cb10 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -7459,6 +7459,12 @@ aarch64_classify_address (struct aarch64_address_info *info,
       && (code != POST_INC && code != REG))
     return false;
 
+  /* MTE unspec is not a valid address directly.  It must first be put into a
+     register.  */
+  if (GET_CODE (x) == UNSPEC
+      && XINT (x, 1) == UNSPEC_ADDTAG)
+    return false;
+
   gcc_checking_assert (GET_MODE (x) == VOIDmode
 		       || SCALAR_INT_MODE_P (GET_MODE (x)));
 
@@ -20281,6 +20287,76 @@ aarch64_can_tag_addresses ()
   return true;
 }
 
+/* Implement TARGET_MEMTAG_HAS_MEMORY_TAGGING.  We support automatic memory
+   tagging and tag checking if we have AARCH64_ISA_MEMTAG.  */
+bool
+aarch64_has_memtag_isa ()
+{
+  return AARCH64_ISA_MEMTAG;
+}
+
+/* Implement TARGET_MEMTAG_TAG for AArch64. This is only available when
+   AARCH64_ISA_MEMTAG is available.  TODO Eventually we would just want
+   something to emit a loop of STG or ST2G.  Currently unimplemented.  */
+void
+aarch64_tag_memory (rtx tagged_start, poly_int64 address_offset, uint8_t tag_offset,
+		    rtx size)
+{
+  return;
+}
+
+void
+aarch64_gentag (rtx a, rtx b)
+{
+  if ( ! AARCH64_ISA_MEMTAG)
+    return default_memtag_gentag (a, b);
+
+  emit_insn (gen_random_tag (a, b));
+}
+
+rtx
+aarch64_addtag (rtx base, poly_int64 addr_offset, uint8_t tag_offset)
+{
+  /* Handle problems like the offset is too large by creating  */
+  if (! AARCH64_ISA_MEMTAG)
+    return default_memtag_addtag (base, addr_offset, tag_offset);
+
+  /* If the tag offset is zero then leave it as a PLUS.
+     This can be optimised easier by the RTL backends.  */
+  if (tag_offset == 0)
+    return plus_constant (Pmode, base, addr_offset);
+  return gen_rtx_UNSPEC (DImode,
+			 gen_rtvec (3,
+				    base,
+				    gen_int_mode (addr_offset, DImode),
+				    GEN_INT (tag_offset)),
+			 UNSPEC_ADDTAG);
+}
+
+rtx
+aarch64_addtag_force_operand (rtx oper, rtx target)
+{
+  if (GET_CODE (oper) == UNSPEC
+      && XINT (oper, 1) == UNSPEC_ADDTAG)
+    {
+      rtx base = XVECEXP (oper, 0, 0);
+      rtx offset = XVECEXP (oper, 0, 1);
+      rtx tag_offset = XVECEXP (oper, 0, 2);
+      if (! aarch64_MTE_value_offset (offset, DImode))
+	{
+	  rtx newreg = gen_reg_rtx (DImode);
+	  emit_insn (gen_adddi3 (newreg, base, offset));
+	  offset = const0_rtx;
+	  base = newreg;
+	}
+
+      rtx temp_reg = (target && REG_P (target)) ? target : gen_reg_rtx (DImode);
+      emit_insn (gen_plain_offset_tagdi (temp_reg, base, offset, tag_offset));
+      return temp_reg;
+    }
+  return NULL_RTX;
+}
+
 /* 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
@@ -20851,6 +20927,21 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
 #define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
 
+#undef TARGET_MEMTAG_HAS_MEMORY_TAGGING
+#define TARGET_MEMTAG_HAS_MEMORY_TAGGING aarch64_has_memtag_isa
+
+#undef TARGET_MEMTAG_TAG
+#define TARGET_MEMTAG_TAG aarch64_tag_memory
+
+#undef TARGET_MEMTAG_GENTAG
+#define TARGET_MEMTAG_GENTAG aarch64_gentag
+
+#undef TARGET_MEMTAG_ADDTAG
+#define TARGET_MEMTAG_ADDTAG aarch64_addtag
+
+#undef TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
+#define TARGET_MEMTAG_ADDTAG_FORCE_OPERAND aarch64_addtag_force_operand
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index e4f9005c27f6f57efba31004389dbed9fd91a360..880d2b40d09b9b229e03aa9bf56ce5ae77a0d350 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -243,6 +243,8 @@ (define_c_enum "unspec" [
     UNSPEC_SPECULATION_TRACKER
     UNSPEC_COPYSIGN
     UNSPEC_TTEST		; Represent transaction test.
+    UNSPEC_GENTAG
+    UNSPEC_ADDTAG
 ])
 
 (define_c_enum "unspecv" [
@@ -445,6 +447,26 @@ (define_expand "cbranch<mode>4"
   "
 )
 
+(define_insn "random_tag"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(unspec:DI [(match_operand:DI 1 "register_operand" "r")] UNSPEC_GENTAG))]
+  "AARCH64_ISA_MEMTAG"
+  "mov\\t%0, %1 // irg\\t%0, %1"
+)
+
+(define_insn "plain_offset_tagdi"
+  [(set (match_operand:DI 0 "register_operand" "=r,r")
+    (unspec:DI
+	[(match_operand:DI 1 "register_operand" "r,r")
+	 (match_operand:DI 2 "aarch64_MTE_value_offset" "I,J")
+	 (match_operand:DI 3 "aarch64_MTE_tag_offset" "i,i")]
+      UNSPEC_ADDTAG))]
+  "AARCH64_ISA_MEMTAG"
+  "@
+  add\\t%0, %1, %2     // addg\\t%0, %1, %2, %3
+  sub\\t%0, %1, #%n2   // subg\\t%0, %1, #%n2, %3"
+)
+
 (define_expand "cbranchcc4"
   [(set (pc) (if_then_else
 	      (match_operator 0 "aarch64_comparison_operator"
diff --git a/gcc/config/aarch64/predicates.md b/gcc/config/aarch64/predicates.md
index d8c377994d6f11a58683b19d7ae9d594e5033561..ede9aa49ef14b8cc453098beac613cc3ed181718 100644
--- a/gcc/config/aarch64/predicates.md
+++ b/gcc/config/aarch64/predicates.md
@@ -144,6 +144,18 @@ (define_predicate "aarch64_pluslong_immediate"
   (and (match_code "const_int")
        (match_test "(INTVAL (op) < 0xffffff && INTVAL (op) > -0xffffff)")))
 
+(define_predicate "aarch64_MTE_add_temp"
+  (ior (match_code "const_int") (match_code "const_poly_int")))
+
+(define_predicate "aarch64_MTE_tag_offset"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 16)")))
+
+(define_predicate "aarch64_MTE_value_offset"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 1008) && !(INTVAL (op) & 0xf)")))
+
+
 (define_predicate "aarch64_pluslong_strict_immedate"
   (and (match_operand 0 "aarch64_pluslong_immediate")
        (not (match_operand 0 "aarch64_plus_immediate"))))
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 99949ba0b9317a89019ab5a6d9383e89f2d6ce3c..e5f83932b1d0c93b97e58b4e2cdc57f45617bfa3 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2976,6 +2976,25 @@ A target hook which lets a backend compute the set of pressure classes to  be us
 True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that -fsanitize=hwaddress can work.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_MEMTAG_HAS_MEMORY_TAGGING ()
+True if backend architecture naturally supports tagging addresses and checking those tags.  This feature means that -fsanitize=memtag can work.
+@end deftypefn
+
+@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_TAG_SIZE ()
+Return the size in bits of a tag for this platform.
+@end deftypefn
+
+@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_GRANULE_SIZE ()
+Return how many bytes in real memory each byte in shadow memory represents.
+I.e. one byte in shadow memory being colour 1 implies the assocaiated
+targetm.memtag.granule_size () bytes in real memory must all be accessed by
+pointers tagged as 1.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_MEMTAG_COPY_TAG (rtx @var{to}, rtx @var{from})
+Emit insns to copy the tag in FROM to TO.
+@end deftypefn
+
 @deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
 Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by TAG_OFFSET.
 The resulting RTX must either be a valid memory address or be able to get
@@ -2996,6 +3015,15 @@ This hook is most often implemented by emitting instructions to put the
 expression into a pseudo register, then returning that pseudo register.
 @end deftypefn
 
+@deftypefn {Target Hook} void TARGET_MEMTAG_TAG (rtx @var{tagged_start}, poly_int64 @var{address_offset}, uint8_t @var{tag_offset}, rtx @var{size})
+This function should emit an RTX to colour memory.
+It's given arguments TAGGED_START, ADDRESS_OFFSET, TAG_OFFSET, SIZE, where
+TAGGED_START and SIZE are RTL expressions, ADDRESS_OFFSET is a poly_int64
+and TAG_OFFSET is a uint8_t.
+It should emit RTL to colour "shadow memory" for the relevant range the
+colour of the tag it was given.
+@end deftypefn
+
 @deftypefn {Target Hook} void TARGET_MEMTAG_GENTAG (rtx @var{base}, rtx @var{untagged})
 Set the BASE argument to UNTAGGED with some random tag.
 This function is used to generate a tagged base for the current stack frame.
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index ab18039b09f9b0a93338fa716d5d044555371ddc..659a07d8b9fb4e2b2c5b7d6c9899be9c723c4c09 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2376,10 +2376,20 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_HAS_MEMORY_TAGGING
+
+@hook TARGET_MEMTAG_TAG_SIZE
+
+@hook TARGET_MEMTAG_GRANULE_SIZE
+
+@hook TARGET_MEMTAG_COPY_TAG
+
 @hook TARGET_MEMTAG_ADDTAG
 
 @hook TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
 
+@hook TARGET_MEMTAG_TAG
+
 @hook TARGET_MEMTAG_GENTAG
 
 @node Stack and Calling
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 57d8ff9a1a010409d966230140df1017bc3584a8..4ab2bf2f466a7ad509d20e8e4bcfb9df72dc1335 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -259,6 +259,7 @@ enum sanitize_code {
   SANITIZE_HWADDRESS = 1UL << 28,
   SANITIZE_USER_HWADDRESS = 1UL << 29,
   SANITIZE_KERNEL_HWADDRESS = 1UL << 30,
+  SANITIZE_MEMTAG = 1UL << 31,
   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 cf1bd9de660f32f060b9277f89a562873a48684a..2e926c2c3da22ea17cc69b3c8d6cf18b07f93dbd 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -9463,6 +9463,8 @@ sanitize_spec_function (int argc, const char **argv)
     return (flag_sanitize & SANITIZE_KERNEL_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "thread") == 0)
     return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
+  if (strcmp (argv[0], "memtag") == 0)
+    return (flag_sanitize & SANITIZE_MEMTAG) ? "" : NULL;
   if (strcmp (argv[0], "undefined") == 0)
     return ((flag_sanitize
 	     & (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT))
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index c692ae86ec6b5fbe345558d7f412f6ecd666bfa1..64d48813c3d16d9fd1888cf74597cf10d0dd3b83 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -502,9 +502,6 @@ expand_HWASAN_MARK (internal_fn, gcall *gc)
   gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
   rtx base_rtx = expand_normal (base);
 
-  rtx tag = is_poison ? const0_rtx : hwasan_extract_tag (base_rtx);
-  rtx address = hwasan_create_untagged_base (base_rtx);
-
   tree len = gimple_call_arg (gc, 2);
   gcc_assert (tree_fits_shwi_p (len));
   unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
@@ -513,13 +510,25 @@ expand_HWASAN_MARK (internal_fn, gcall *gc)
   size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
   rtx size = gen_int_mode (size_in_bytes, Pmode);
 
-  rtx func = init_one_libfunc ("__hwasan_tag_memory");
-  emit_library_call (func,
-      LCT_NORMAL,
-      VOIDmode,
-      address, ptr_mode,
-      tag, QImode,
-      size, ptr_mode);
+  if (sanitize_flags_p (SANITIZE_HWADDRESS))
+    {
+      rtx func = init_one_libfunc ("__hwasan_tag_memory");
+      rtx address = hwasan_create_untagged_base (base_rtx);
+      rtx tag = is_poison ? const0_rtx : hwasan_extract_tag (base_rtx);
+      emit_library_call (func,
+			 LCT_NORMAL,
+			 VOIDmode,
+			 address, ptr_mode,
+			 tag, QImode,
+			 size, ptr_mode);
+    }
+  else
+    {
+      gcc_assert (sanitize_flags_p (SANITIZE_MEMTAG));
+      if (is_poison)
+	targetm.memtag.copy_tag (base_rtx, stack_pointer_rtx);
+      targetm.memtag.tag (base_rtx, 0, 0, size);
+    }
 }
 
 /* This should get expanded in the sanopt pass.  */
diff --git a/gcc/opts.c b/gcc/opts.c
index 88a94286e71f61f2dce907018e5185f63a830804..659eeb0a62344c250314892a974d245d72b9a84e 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1200,6 +1200,27 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 	      "%<-fsanitize=hwaddress%> is incompatible with "
 	      "%<-fsanitize=thread%>");
 
+  /* Memtag and ASan conflict with each other.  */
+  if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_MEMTAG))
+    error_at (loc,
+	      "%<-fsanitize=memtag%> is incompatible with both "
+	      "%<-fsanitize=address%> and %<-fsanitize=kernel-address%>");
+
+  /* Memtag and HWASan conflict with each other.  */
+  if ((opts->x_flag_sanitize & SANITIZE_HWADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_MEMTAG))
+    error_at (loc,
+	      "%<-fsanitize=memtag%> is incompatible with both "
+	      "%<-fsanitize=hwaddress%> and %<-fsanitize=kernel-hwaddress%>");
+
+  /* Memtag conflicts with TSan.  */
+  if ((opts->x_flag_sanitize & SANITIZE_MEMTAG)
+      && (opts->x_flag_sanitize & SANITIZE_THREAD))
+    error_at (loc,
+	      "%<-fsanitize=memtag%> 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)
@@ -1220,7 +1241,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)
-       || (opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS))
+       || (opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS)
+       || (opts->x_flag_sanitize & SANITIZE_MEMTAG))
       && !opts_set->x_flag_sanitize_address_use_after_scope)
     opts->x_flag_sanitize_address_use_after_scope = true;
 
@@ -1849,6 +1871,7 @@ 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 (memtag, (SANITIZE_MEMTAG), true),
   SANITIZER_OPT (hwaddress, (SANITIZE_HWADDRESS | SANITIZE_USER_HWADDRESS),
 		 true),
   SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
diff --git a/gcc/target.def b/gcc/target.def
index badae860335e4a570f189c9f8011da5ab8c15439..2a366b7b58ed459574ab9aa3f008ed0f05bf2666 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6716,6 +6716,30 @@ DEFHOOK
  bool, (), default_memtag_can_tag_addresses)
 
 DEFHOOK
+(has_memory_tagging,
+ "True if backend architecture naturally supports tagging addresses and\
+ checking those tags.  This feature means that -fsanitize=memtag can work.",
+ bool, (), default_memtag_has_memory_tagging)
+
+DEFHOOK
+(tag_size,
+ "Return the size in bits of a tag for this platform.",
+ uint8_t, (), default_memtag_tag_size)
+
+DEFHOOK
+(granule_size,
+ "Return how many bytes in real memory each byte in shadow memory represents.\n\
+I.e. one byte in shadow memory being colour 1 implies the assocaiated\n\
+targetm.memtag.granule_size () bytes in real memory must all be accessed by\n\
+pointers tagged as 1.",
+uint8_t, (), default_memtag_granule_size)
+
+DEFHOOK
+(copy_tag,
+ "Emit insns to copy the tag in FROM to TO.",
+void, (rtx to, rtx from), default_memtag_copy_tag)
+
+DEFHOOK
 (addtag,
  "Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by\
  TAG_OFFSET.\n\
@@ -6740,6 +6764,17 @@ expression into a pseudo register, then returning that pseudo register.",
 rtx, (rtx oper, rtx target), NULL)
 
 DEFHOOK
+(tag,
+ "This function should emit an RTX to colour memory.\n\
+It's given arguments TAGGED_START, ADDRESS_OFFSET, TAG_OFFSET, SIZE, where\n\
+TAGGED_START and SIZE are RTL expressions, ADDRESS_OFFSET is a poly_int64\n\
+and TAG_OFFSET is a uint8_t.\n\
+It should emit RTL to colour \"shadow memory\" for the relevant range the\n\
+colour of the tag it was given.",
+  void, (rtx tagged_start, poly_int64 address_offset, uint8_t tag_offset, rtx size),
+NULL)
+
+DEFHOOK
 (gentag,
  "Set the BASE argument to UNTAGGED with some random tag.\n\
 This function is used to generate a tagged base for the current stack frame.",
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 4418db74f52f669c22702f5a4a093172f48a1b46..9c69589e33121638d349b882b4f26d29ac449d20 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -285,6 +285,10 @@ extern void default_remove_extra_call_preserved_regs (rtx_insn *,
 						      HARD_REG_SET *);
 
 extern bool default_memtag_can_tag_addresses ();
+extern bool default_memtag_has_memory_tagging ();
+extern uint8_t default_memtag_tag_size ();
+extern uint8_t default_memtag_granule_size ();
 extern void default_memtag_gentag (rtx, rtx);
+extern void default_memtag_copy_tag (rtx, rtx);
 extern rtx default_memtag_addtag (rtx, poly_int64, uint8_t);
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 048c7f3cff5d87d0d40b93f6cf8cb41de670711d..b8a74a5f3750dad102311f1e4298a63416f1261b 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2377,6 +2377,24 @@ default_memtag_can_tag_addresses ()
   return false;
 }
 
+bool
+default_memtag_has_memory_tagging ()
+{
+  return false;
+}
+
+uint8_t
+default_memtag_tag_size ()
+{
+  return 8;
+}
+
+uint8_t
+default_memtag_granule_size ()
+{
+  return 16;
+}
+
 void
 default_memtag_gentag (rtx base, rtx untagged)
 {
@@ -2403,6 +2421,70 @@ default_memtag_gentag (rtx base, rtx untagged)
     }
 }
 
+void
+default_memtag_copy_tag (rtx to, rtx from)
+{
+  /* TODO:
+      I want to have a sequence as minimal as possible here, since this code
+      sequence could be emitted many times.
+
+      My first attempt was the below,
+
+	  rtx temp = hwasan_extract_tag (from);
+	  store_bit_field (to, 8, 56, 0, 0,
+			  QImode, temp, false);
+      
+     Unfortunately, despite having much less instructions, for AArch64 this can
+     cause a problem in LRA if the `to` RTX eventually resolves to being the
+     stack pointer.
+     This happens because the instruction that gets emitted from
+     `store_bit_field` corresponds to a pattern that can't handle the stack
+     pointer and LRA can't figure out to use a temporary register in the `bfi`
+     instruction's place.
+
+     This doesn't cause a problem at the moment since there's currently no way
+     the stack pointer should be given to this function.  The hook is only used
+     when poisoning variables with HWASAN_MARK, and in that function the `to`
+     RTX should always be pointing to a tagged variable on the stack (since
+     the variable is tagged it can't be the stack pointer since that is
+     untagged).
+
+     Eventually we will be generating random tags as the "start" tag for each
+     frame.  When this happens we can no longer avoid the background colour at
+     compile time since we will not know what offset to avoid.
+     This will mean we no longer avoid a `tag_offset` of 0, and hence
+     `hwasan_with_tag` could emit simple PLUS statements.
+
+     When that happens, the last variable on the stack could very well have
+     a zero tag offset and somewhere else in the compiler could optimise that
+     to simply use the stack pointer.
+
+     That would trigger an ICE due to LRA being unable to reload the
+     `insv_regdi` pattern.
+
+     The code sequence I'm emitting at the moment works just fine in all
+     circumstances, but it would be nice to find a smaller sequence.  */
+  rtx temp = gen_reg_rtx (Pmode);
+  uint64_t tag_mask = 0xFFUL << HWASAN_SHIFT;
+  emit_insn (gen_anddi3 (to, to, GEN_INT (~tag_mask)));
+  /* Can't use GEN_INT (tag_mask) since GEN_INT calls `gen_rtx_CONST_INT` which
+     takes a `HOST_WIDE_INT`.
+     HOST_WIDE_INT can't hold a uint64_t with the top bit set, hence in order
+     to avoid UB we have to emit instructions for the machine to use some
+     uint64_t arithmetic.
+
+     The extra instructions seem to eventually end up with the same output in
+     most cases (I've not yet seen a case where the generation of the mask in
+     three or one `emit_insn` calls changes the codegen).  */
+
+  /* emit_insn (gen_anddi3 (temp, from, GEN_INT (tag_mask)));  */
+  emit_move_insn (temp, GEN_INT (0xff));
+  emit_insn (gen_ashldi3 (temp, temp, HWASAN_SHIFT_RTX));
+  emit_insn (gen_anddi3 (temp, from, temp));
+
+  emit_insn (gen_iordi3 (to, to, from));
+}
+
 rtx
 default_memtag_addtag (rtx base, poly_int64 offset, uint8_t tag_offset)
 {
diff --git a/gcc/testsuite/gcc.dg/hwasan/poly-int-stack-vars.c b/gcc/testsuite/gcc.dg/hwasan/poly-int-stack-vars.c
new file mode 100644
index 0000000000000000000000000000000000000000..cb0ca7d3a06c5a2de258ba20be974009410a7a44
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/poly-int-stack-vars.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+
+/* TODO Need to give this test the correct arguments
+   (i.e. compile with memory tagging enabled).
+
+   This is needed because the introduction of HWASAN_CHECK statements stops the
+   compiler from being able to vectorise the loop with SVE, hence stopping the
+   introduction of `addvl` instructions.
+
+   In other words, this test doesn't really belong in this directory, but I
+   haven't yet created the directory for checking memtag code generation and
+   this is a dummy commit anyway.  */
+/* Non-constant sizes.
+   Code designed to store SVE registers on the stack.
+   This is needed to exercise the poly_int64 handling for HWASAN and MTE
+   instrumentation. */
+int u;
+
+void
+foo_sve (int *p)
+{
+  int i;
+  #pragma omp for simd lastprivate(u) schedule (static, 32)
+  for (i = 0; i < 1024; i++)
+    u = p[i];
+}
+
+void
+bar_sve (int *p)
+{
+  int i;
+  #pragma omp taskloop simd lastprivate(u)
+  for (i = 0; i < 1024; i++)
+    u = p[i];
+}
+
+/* Ensure we are storing SVE vectors on the stack -- otherwise we're not
+   exercising the code path for poly_int64's.  */
+/* { dg-final { scan-assembler "addvl" } } */
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 5e077892a6371a7b534007d5dffd421b218ea694..6dcd830c3a93a93ce27d9b80137af6d9d1288ff7 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1765,6 +1765,16 @@ process_options (void)
       flag_sanitize &= ~SANITIZE_HWADDRESS;
     }
 
+  /* Memtag requires hardware support.  */
+  if (flag_sanitize & SANITIZE_MEMTAG && !targetm.memtag.has_memory_tagging ())
+    {
+      warning_at (UNKNOWN_LOCATION, 0,
+		  "%<-fsanitize=memtag%> requires hardware support "
+		  "that is not advertised for this target");
+      flag_sanitize &= ~SANITIZE_MEMTAG;
+    }
+
+
  /* Do not use IPA optimizations for register allocation if profiler is active
     or patchable function entries are inserted for run-time instrumentation
     or port does not emit prologue and epilogue as RTL.  */
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index b4b5c903e13b1695daeff66a680c64fa7da0829d..73ff987544487e44c4c14419fdc2b27b8b6ddb25 100644
--- a/gcc/tree-ssa.c
+++ b/gcc/tree-ssa.c
@@ -1704,7 +1704,9 @@ execute_update_addresses_taken (void)
 		  gimple_ior_addresses_taken (addresses_taken, stmt);
 		  gimple_call_set_arg (stmt, 1, arg);
 		}
-	      else if (is_asan_mark_p (stmt)
+	      else if ((is_asan_mark_p (stmt)
+			&& (!sanitize_flags_p (SANITIZE_MEMTAG)
+			    || !asan_mark_p (stmt, ASAN_MARK_POISON)))
 		       || gimple_call_internal_p (stmt, IFN_GOMP_SIMT_ENTER))
 		;
 	      else


[-- Attachment #2: hwasan-patch18.patch --]
[-- Type: text/plain, Size: 33923 bytes --]

diff --git a/gcc/asan.h b/gcc/asan.h
index ff6adf2391ee1602a3c15755312a04f82d6369ce..71dbaee708d0e64911f568503655478b8720f494 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -27,10 +27,10 @@ extern void hwasan_finish_file (void);
 extern void hwasan_record_base (rtx);
 extern uint8_t hwasan_current_tag ();
 extern void hwasan_increment_tag ();
+extern rtx hwasan_extract_tag (rtx);
 extern rtx hwasan_with_tag (rtx, poly_int64);
 extern void hwasan_tag_init ();
 extern rtx hwasan_create_untagged_base (rtx);
-extern rtx hwasan_extract_tag (rtx tagged_pointer);
 extern rtx hwasan_base ();
 extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
 extern rtx_insn *hwasan_emit_uncolour_frame (rtx, rtx, rtx_insn *);
@@ -95,8 +95,12 @@ extern hash_set <tree> *asan_used_labels;
 /* NOTE: The values below define an ABI and are hard-coded to these values in
    libhwasan, hence they can't be changed independently here.  */
 /* How many bits are used to store a tag in a pointer.
-   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).  */
-#define HWASAN_TAG_SIZE 8
+   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).
+   For aarch64 MTE we have 4 bits per colour and that is advertised by the
+   backend hook.  */
+#define HWASAN_TAG_SIZE (sanitize_flags_p (SANITIZE_MEMTAG) \
+			 ? targetm.memtag.tag_size () \
+			 : 8)
 /* Tag Granule of HWASAN shadow stack.
    This is the size in real memory that each byte in the shadow memory refers
    to.  I.e. if a variable is X bytes long in memory then it's colour in shadow
@@ -105,7 +109,12 @@ extern hash_set <tree> *asan_used_labels;
    that are neighbours in memory and share a tag granule would need to share
    the same colour (the shared tag granule can only store one colour).  */
 #define HWASAN_TAG_SHIFT_SIZE 4
-#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SHIFT_SIZE)
+#define HWASAN_TAG_GRANULE_SIZE (sanitize_flags_p (SANITIZE_MEMTAG) \
+				 ? targetm.memtag.granule_size () \
+				 : (1ULL << HWASAN_TAG_SHIFT_SIZE))
+
+/* The following HWASAN_* macros are only used for HWASAN (not MEMTAG), which
+   is why there is no predicate.  */
 /* Define the tag for the stack background.
    This defines what colour the stack pointer will be and hence what tag all
    variables that are not given special tags are (e.g. spilled registers,
diff --git a/gcc/asan.c b/gcc/asan.c
index ef7c90e3358c8fa880b8e4002996f27541c26953..5769d1236908e6d8c75018f04f855928665e4126 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -1480,7 +1480,8 @@ asan_redzone_buffer::flush_if_full (void)
 bool
 memory_tagging_p ()
 {
-    return sanitize_flags_p (SANITIZE_HWADDRESS);
+    return sanitize_flags_p (SANITIZE_HWADDRESS)
+      || sanitize_flags_p (SANITIZE_MEMTAG);
 }
 
 /* Are we tagging the stack?  */
@@ -3952,7 +3953,9 @@ hwasan_tag_init ()
   asan_used_labels = NULL;
 
   hwasan_base_ptr = NULL_RTX;
-  tag_offset = HWASAN_STACK_BACKGROUND + 1;
+  tag_offset = sanitize_flags_p (SANITIZE_MEMTAG)
+    ? 0
+    : HWASAN_STACK_BACKGROUND + 1;
 }
 
 tree
@@ -4220,20 +4223,31 @@ hwasan_emit_prologue (rtx *bases,
       if (size.is_constant (&tmp))
 	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
 
-      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
-      rtx base_tag = hwasan_extract_tag (bases[i]);
-      /* In the case of tag overflow we would want modulo wrapping -- which
-	 should be given from the `plus_constant` in QImode.  */
-      rtx tag_colour = plus_constant (QImode, base_tag, tags[i]);
-      emit_library_call (ret,
-			 LCT_NORMAL,
-			 VOIDmode,
-			 plus_constant (ptr_mode, untagged_bases[i], bot),
-			 ptr_mode,
-			 tag_colour,
-			 QImode,
-			 gen_int_mode (size, ptr_mode),
-			 ptr_mode);
+      if (sanitize_flags_p (SANITIZE_HWADDRESS))
+	{
+	  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+	  rtx base_tag = hwasan_extract_tag (bases[i]);
+	  /* In the case of tag overflow we would want modulo wrapping -- which
+	     should be given from the `plus_constant` in QImode.  */
+	  rtx tag_colour = plus_constant (QImode, base_tag, tags[i]);
+	  emit_library_call (ret,
+			     LCT_NORMAL,
+			     VOIDmode,
+			     plus_constant (ptr_mode, untagged_bases[i], bot),
+			     ptr_mode,
+			     tag_colour,
+			     QImode,
+			     gen_int_mode (size, ptr_mode),
+			     ptr_mode);
+	}
+      else
+	{
+	  gcc_assert (sanitize_flags_p (SANITIZE_MEMTAG));
+	  targetm.memtag.tag (bases[i],
+			      bot,
+			      tags[i],
+			      gen_int_mode (size, ptr_mode));
+	}
     }
 }
 
@@ -4264,11 +4278,20 @@ hwasan_emit_uncolour_frame (rtx dynamic, rtx vars, rtx_insn *before)
   rtx size_rtx = expand_simple_binop (Pmode, MINUS, top_rtx, bot_rtx,
 				  NULL_RTX, /* unsignedp = */0, OPTAB_DIRECT);
 
-  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
-  emit_library_call (ret, LCT_NORMAL, VOIDmode,
-      bot_rtx, ptr_mode,
-      const0_rtx, QImode,
-      size_rtx, ptr_mode);
+  if (sanitize_flags_p (SANITIZE_HWADDRESS))
+    {
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      emit_library_call (ret, LCT_NORMAL, VOIDmode,
+			 bot_rtx, ptr_mode,
+			 const0_rtx, QImode,
+			 size_rtx, ptr_mode);
+    }
+  else
+    {
+      gcc_assert (sanitize_flags_p (SANITIZE_MEMTAG));
+      targetm.memtag.copy_tag (bot_rtx, stack_pointer_rtx);
+      targetm.memtag.tag (bot_rtx, 0, 0, size_rtx);
+    }
 
   do_pending_stack_adjust ();
   rtx_insn *insns = get_insns ();
@@ -4301,6 +4324,8 @@ static GTY(()) tree hwasan_ctor_statements;
 void
 hwasan_finish_file (void)
 {
+  gcc_assert (sanitize_flags_p (SANITIZE_HWADDRESS));
+
   /* Do not emit constructor initialisation for the kernel.
      (the kernel has its own initialisation already).  */
   if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
@@ -4355,6 +4380,7 @@ hwasan_check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
 bool
 hwasan_expand_check_ifn (gimple_stmt_iterator *iter, bool)
 {
+  gcc_assert (sanitize_flags_p (SANITIZE_HWADDRESS));
   gimple *g = gsi_stmt (*iter);
   location_t loc = gimple_location (g);
   bool recover_p;
@@ -4448,7 +4474,7 @@ hwasan_expand_mark_ifn (gimple_stmt_iterator *)
 bool
 gate_hwasan ()
 {
-  return memory_tagging_p ();
+  return sanitize_flags_p (SANITIZE_HWADDRESS);
 }
 
 namespace {
diff --git a/gcc/builtins.c b/gcc/builtins.c
index f8063c138a340a06d45b01c9bb7f43caf75e78b2..416ee2b631d22ffab0ca428bc7fec9127382ef3e 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -5391,6 +5391,17 @@ expand_builtin_frame_address (tree fndecl, tree exp)
 static rtx
 expand_builtin_alloca (tree exp)
 {
+  /* TODO For hardware memory tagging we will need to call the backend to tag
+     this memory since the `hwasan` pass will not be run.
+
+     The `hwasan` pass is mainly to add HWASAN_CHECK internal functions where
+     checks should be made.  With hardware memory tagging the checks are done
+     automatically by the architecture.
+
+     The `hwasan` pass also modifies the behaviour of the alloca builtin
+     function in a target-independent manner, but when memory tagging is
+     handled by the backend it is more convenient to handle the tagging in the
+     alloca hook.  */
   rtx op0;
   rtx result;
   unsigned int align;
@@ -7012,6 +7023,9 @@ expand_builtin_set_thread_pointer (tree exp)
 static void
 expand_stack_restore (tree var)
 {
+  /* TODO If memory tagging is enabled through the hardware we need to uncolour
+     the stack from where we are to where we're going. (i.e. colour in the
+     background stack colour).  */
   rtx_insn *prev;
   rtx sa = expand_normal (var);
 
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 1e4d171e651ae10c5829e52248629c04b03c19f1..53b4658aa74c1e369fb139b0a29cdb6dea41dc3b 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -1043,6 +1043,8 @@ public:
 
   /* HWASAN records the poly_int64 so it can handle any stack variable.  */
   auto_vec<poly_int64> hwasan_vec;
+  /* HWASAN needs to record untagged base pointers when there isn't hardware
+     memory tagging enabled by the architecture.  */
   auto_vec<rtx> hwasan_untagged_base_vec;
   auto_vec<rtx> hwasan_base_vec;
 
@@ -1177,7 +1179,8 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
 	      offset = alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
 	      data->hwasan_vec.safe_push (offset);
-	      data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
+	      if (sanitize_flags_p (SANITIZE_HWADDRESS))
+		data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
 	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
@@ -1291,10 +1294,18 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      /* An object with a large alignment requirement means that the
 		 alignment requirement is greater than the required alignment
 		 for tags.  */
-	      if (!large_untagged_base)
-		large_untagged_base = hwasan_create_untagged_base (large_base);
 	      data->hwasan_vec.safe_push (large_alloc);
-	      data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
+
+	      if (sanitize_flags_p (SANITIZE_HWADDRESS))
+	      {
+		/* We only need to record the untagged bases for HWASAN, since
+		   the runtime library for that doesn't accept tagged pointers.
+		   For hardware implementations of memory tagging there is no
+		   use of recording these untagged versions.  */
+		if (!large_untagged_base)
+		  large_untagged_base = hwasan_create_untagged_base (large_base);
+		data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
+	      }
 	    }
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
index 7bbeed453cf87382b1776ff52991b5cf6ab9204e..7f23d377308f3b517e4ae08eba3a56e8c6565e8a 100644
--- a/gcc/config/aarch64/aarch64.h
+++ b/gcc/config/aarch64/aarch64.h
@@ -247,6 +247,7 @@ extern unsigned aarch64_architecture_version;
 #define AARCH64_ISA_RCPC8_4	   (aarch64_isa_flags & AARCH64_FL_RCPC8_4)
 #define AARCH64_ISA_V8_5	   (aarch64_isa_flags & AARCH64_FL_V8_5)
 #define AARCH64_ISA_TME		   (aarch64_isa_flags & AARCH64_FL_TME)
+#define AARCH64_ISA_MEMTAG	   (aarch64_isa_flags & AARCH64_FL_MEMTAG)
 
 /* Crypto is an optional extension to AdvSIMD.  */
 #define TARGET_CRYPTO (TARGET_SIMD && AARCH64_ISA_CRYPTO)
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index c556bcd1c37c3c4fdd9a829a28ee4ff56819b89e..a21b5918859305dd6301ac7cb3a4e16271b3cb10 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -7459,6 +7459,12 @@ aarch64_classify_address (struct aarch64_address_info *info,
       && (code != POST_INC && code != REG))
     return false;
 
+  /* MTE unspec is not a valid address directly.  It must first be put into a
+     register.  */
+  if (GET_CODE (x) == UNSPEC
+      && XINT (x, 1) == UNSPEC_ADDTAG)
+    return false;
+
   gcc_checking_assert (GET_MODE (x) == VOIDmode
 		       || SCALAR_INT_MODE_P (GET_MODE (x)));
 
@@ -20281,6 +20287,76 @@ aarch64_can_tag_addresses ()
   return true;
 }
 
+/* Implement TARGET_MEMTAG_HAS_MEMORY_TAGGING.  We support automatic memory
+   tagging and tag checking if we have AARCH64_ISA_MEMTAG.  */
+bool
+aarch64_has_memtag_isa ()
+{
+  return AARCH64_ISA_MEMTAG;
+}
+
+/* Implement TARGET_MEMTAG_TAG for AArch64. This is only available when
+   AARCH64_ISA_MEMTAG is available.  TODO Eventually we would just want
+   something to emit a loop of STG or ST2G.  Currently unimplemented.  */
+void
+aarch64_tag_memory (rtx tagged_start, poly_int64 address_offset, uint8_t tag_offset,
+		    rtx size)
+{
+  return;
+}
+
+void
+aarch64_gentag (rtx a, rtx b)
+{
+  if ( ! AARCH64_ISA_MEMTAG)
+    return default_memtag_gentag (a, b);
+
+  emit_insn (gen_random_tag (a, b));
+}
+
+rtx
+aarch64_addtag (rtx base, poly_int64 addr_offset, uint8_t tag_offset)
+{
+  /* Handle problems like the offset is too large by creating  */
+  if (! AARCH64_ISA_MEMTAG)
+    return default_memtag_addtag (base, addr_offset, tag_offset);
+
+  /* If the tag offset is zero then leave it as a PLUS.
+     This can be optimised easier by the RTL backends.  */
+  if (tag_offset == 0)
+    return plus_constant (Pmode, base, addr_offset);
+  return gen_rtx_UNSPEC (DImode,
+			 gen_rtvec (3,
+				    base,
+				    gen_int_mode (addr_offset, DImode),
+				    GEN_INT (tag_offset)),
+			 UNSPEC_ADDTAG);
+}
+
+rtx
+aarch64_addtag_force_operand (rtx oper, rtx target)
+{
+  if (GET_CODE (oper) == UNSPEC
+      && XINT (oper, 1) == UNSPEC_ADDTAG)
+    {
+      rtx base = XVECEXP (oper, 0, 0);
+      rtx offset = XVECEXP (oper, 0, 1);
+      rtx tag_offset = XVECEXP (oper, 0, 2);
+      if (! aarch64_MTE_value_offset (offset, DImode))
+	{
+	  rtx newreg = gen_reg_rtx (DImode);
+	  emit_insn (gen_adddi3 (newreg, base, offset));
+	  offset = const0_rtx;
+	  base = newreg;
+	}
+
+      rtx temp_reg = (target && REG_P (target)) ? target : gen_reg_rtx (DImode);
+      emit_insn (gen_plain_offset_tagdi (temp_reg, base, offset, tag_offset));
+      return temp_reg;
+    }
+  return NULL_RTX;
+}
+
 /* 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
@@ -20851,6 +20927,21 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
 #define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
 
+#undef TARGET_MEMTAG_HAS_MEMORY_TAGGING
+#define TARGET_MEMTAG_HAS_MEMORY_TAGGING aarch64_has_memtag_isa
+
+#undef TARGET_MEMTAG_TAG
+#define TARGET_MEMTAG_TAG aarch64_tag_memory
+
+#undef TARGET_MEMTAG_GENTAG
+#define TARGET_MEMTAG_GENTAG aarch64_gentag
+
+#undef TARGET_MEMTAG_ADDTAG
+#define TARGET_MEMTAG_ADDTAG aarch64_addtag
+
+#undef TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
+#define TARGET_MEMTAG_ADDTAG_FORCE_OPERAND aarch64_addtag_force_operand
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index e4f9005c27f6f57efba31004389dbed9fd91a360..880d2b40d09b9b229e03aa9bf56ce5ae77a0d350 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -243,6 +243,8 @@ (define_c_enum "unspec" [
     UNSPEC_SPECULATION_TRACKER
     UNSPEC_COPYSIGN
     UNSPEC_TTEST		; Represent transaction test.
+    UNSPEC_GENTAG
+    UNSPEC_ADDTAG
 ])
 
 (define_c_enum "unspecv" [
@@ -445,6 +447,26 @@ (define_expand "cbranch<mode>4"
   "
 )
 
+(define_insn "random_tag"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(unspec:DI [(match_operand:DI 1 "register_operand" "r")] UNSPEC_GENTAG))]
+  "AARCH64_ISA_MEMTAG"
+  "mov\\t%0, %1 // irg\\t%0, %1"
+)
+
+(define_insn "plain_offset_tagdi"
+  [(set (match_operand:DI 0 "register_operand" "=r,r")
+    (unspec:DI
+	[(match_operand:DI 1 "register_operand" "r,r")
+	 (match_operand:DI 2 "aarch64_MTE_value_offset" "I,J")
+	 (match_operand:DI 3 "aarch64_MTE_tag_offset" "i,i")]
+      UNSPEC_ADDTAG))]
+  "AARCH64_ISA_MEMTAG"
+  "@
+  add\\t%0, %1, %2     // addg\\t%0, %1, %2, %3
+  sub\\t%0, %1, #%n2   // subg\\t%0, %1, #%n2, %3"
+)
+
 (define_expand "cbranchcc4"
   [(set (pc) (if_then_else
 	      (match_operator 0 "aarch64_comparison_operator"
diff --git a/gcc/config/aarch64/predicates.md b/gcc/config/aarch64/predicates.md
index d8c377994d6f11a58683b19d7ae9d594e5033561..ede9aa49ef14b8cc453098beac613cc3ed181718 100644
--- a/gcc/config/aarch64/predicates.md
+++ b/gcc/config/aarch64/predicates.md
@@ -144,6 +144,18 @@ (define_predicate "aarch64_pluslong_immediate"
   (and (match_code "const_int")
        (match_test "(INTVAL (op) < 0xffffff && INTVAL (op) > -0xffffff)")))
 
+(define_predicate "aarch64_MTE_add_temp"
+  (ior (match_code "const_int") (match_code "const_poly_int")))
+
+(define_predicate "aarch64_MTE_tag_offset"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 16)")))
+
+(define_predicate "aarch64_MTE_value_offset"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 1008) && !(INTVAL (op) & 0xf)")))
+
+
 (define_predicate "aarch64_pluslong_strict_immedate"
   (and (match_operand 0 "aarch64_pluslong_immediate")
        (not (match_operand 0 "aarch64_plus_immediate"))))
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 99949ba0b9317a89019ab5a6d9383e89f2d6ce3c..e5f83932b1d0c93b97e58b4e2cdc57f45617bfa3 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2976,6 +2976,25 @@ A target hook which lets a backend compute the set of pressure classes to  be us
 True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that -fsanitize=hwaddress can work.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_MEMTAG_HAS_MEMORY_TAGGING ()
+True if backend architecture naturally supports tagging addresses and checking those tags.  This feature means that -fsanitize=memtag can work.
+@end deftypefn
+
+@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_TAG_SIZE ()
+Return the size in bits of a tag for this platform.
+@end deftypefn
+
+@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_GRANULE_SIZE ()
+Return how many bytes in real memory each byte in shadow memory represents.
+I.e. one byte in shadow memory being colour 1 implies the assocaiated
+targetm.memtag.granule_size () bytes in real memory must all be accessed by
+pointers tagged as 1.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_MEMTAG_COPY_TAG (rtx @var{to}, rtx @var{from})
+Emit insns to copy the tag in FROM to TO.
+@end deftypefn
+
 @deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
 Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by TAG_OFFSET.
 The resulting RTX must either be a valid memory address or be able to get
@@ -2996,6 +3015,15 @@ This hook is most often implemented by emitting instructions to put the
 expression into a pseudo register, then returning that pseudo register.
 @end deftypefn
 
+@deftypefn {Target Hook} void TARGET_MEMTAG_TAG (rtx @var{tagged_start}, poly_int64 @var{address_offset}, uint8_t @var{tag_offset}, rtx @var{size})
+This function should emit an RTX to colour memory.
+It's given arguments TAGGED_START, ADDRESS_OFFSET, TAG_OFFSET, SIZE, where
+TAGGED_START and SIZE are RTL expressions, ADDRESS_OFFSET is a poly_int64
+and TAG_OFFSET is a uint8_t.
+It should emit RTL to colour "shadow memory" for the relevant range the
+colour of the tag it was given.
+@end deftypefn
+
 @deftypefn {Target Hook} void TARGET_MEMTAG_GENTAG (rtx @var{base}, rtx @var{untagged})
 Set the BASE argument to UNTAGGED with some random tag.
 This function is used to generate a tagged base for the current stack frame.
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index ab18039b09f9b0a93338fa716d5d044555371ddc..659a07d8b9fb4e2b2c5b7d6c9899be9c723c4c09 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2376,10 +2376,20 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_HAS_MEMORY_TAGGING
+
+@hook TARGET_MEMTAG_TAG_SIZE
+
+@hook TARGET_MEMTAG_GRANULE_SIZE
+
+@hook TARGET_MEMTAG_COPY_TAG
+
 @hook TARGET_MEMTAG_ADDTAG
 
 @hook TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
 
+@hook TARGET_MEMTAG_TAG
+
 @hook TARGET_MEMTAG_GENTAG
 
 @node Stack and Calling
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 57d8ff9a1a010409d966230140df1017bc3584a8..4ab2bf2f466a7ad509d20e8e4bcfb9df72dc1335 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -259,6 +259,7 @@ enum sanitize_code {
   SANITIZE_HWADDRESS = 1UL << 28,
   SANITIZE_USER_HWADDRESS = 1UL << 29,
   SANITIZE_KERNEL_HWADDRESS = 1UL << 30,
+  SANITIZE_MEMTAG = 1UL << 31,
   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 cf1bd9de660f32f060b9277f89a562873a48684a..2e926c2c3da22ea17cc69b3c8d6cf18b07f93dbd 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -9463,6 +9463,8 @@ sanitize_spec_function (int argc, const char **argv)
     return (flag_sanitize & SANITIZE_KERNEL_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "thread") == 0)
     return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
+  if (strcmp (argv[0], "memtag") == 0)
+    return (flag_sanitize & SANITIZE_MEMTAG) ? "" : NULL;
   if (strcmp (argv[0], "undefined") == 0)
     return ((flag_sanitize
 	     & (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT))
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index c692ae86ec6b5fbe345558d7f412f6ecd666bfa1..64d48813c3d16d9fd1888cf74597cf10d0dd3b83 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -502,9 +502,6 @@ expand_HWASAN_MARK (internal_fn, gcall *gc)
   gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
   rtx base_rtx = expand_normal (base);
 
-  rtx tag = is_poison ? const0_rtx : hwasan_extract_tag (base_rtx);
-  rtx address = hwasan_create_untagged_base (base_rtx);
-
   tree len = gimple_call_arg (gc, 2);
   gcc_assert (tree_fits_shwi_p (len));
   unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
@@ -513,13 +510,25 @@ expand_HWASAN_MARK (internal_fn, gcall *gc)
   size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
   rtx size = gen_int_mode (size_in_bytes, Pmode);
 
-  rtx func = init_one_libfunc ("__hwasan_tag_memory");
-  emit_library_call (func,
-      LCT_NORMAL,
-      VOIDmode,
-      address, ptr_mode,
-      tag, QImode,
-      size, ptr_mode);
+  if (sanitize_flags_p (SANITIZE_HWADDRESS))
+    {
+      rtx func = init_one_libfunc ("__hwasan_tag_memory");
+      rtx address = hwasan_create_untagged_base (base_rtx);
+      rtx tag = is_poison ? const0_rtx : hwasan_extract_tag (base_rtx);
+      emit_library_call (func,
+			 LCT_NORMAL,
+			 VOIDmode,
+			 address, ptr_mode,
+			 tag, QImode,
+			 size, ptr_mode);
+    }
+  else
+    {
+      gcc_assert (sanitize_flags_p (SANITIZE_MEMTAG));
+      if (is_poison)
+	targetm.memtag.copy_tag (base_rtx, stack_pointer_rtx);
+      targetm.memtag.tag (base_rtx, 0, 0, size);
+    }
 }
 
 /* This should get expanded in the sanopt pass.  */
diff --git a/gcc/opts.c b/gcc/opts.c
index 88a94286e71f61f2dce907018e5185f63a830804..659eeb0a62344c250314892a974d245d72b9a84e 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1200,6 +1200,27 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 	      "%<-fsanitize=hwaddress%> is incompatible with "
 	      "%<-fsanitize=thread%>");
 
+  /* Memtag and ASan conflict with each other.  */
+  if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_MEMTAG))
+    error_at (loc,
+	      "%<-fsanitize=memtag%> is incompatible with both "
+	      "%<-fsanitize=address%> and %<-fsanitize=kernel-address%>");
+
+  /* Memtag and HWASan conflict with each other.  */
+  if ((opts->x_flag_sanitize & SANITIZE_HWADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_MEMTAG))
+    error_at (loc,
+	      "%<-fsanitize=memtag%> is incompatible with both "
+	      "%<-fsanitize=hwaddress%> and %<-fsanitize=kernel-hwaddress%>");
+
+  /* Memtag conflicts with TSan.  */
+  if ((opts->x_flag_sanitize & SANITIZE_MEMTAG)
+      && (opts->x_flag_sanitize & SANITIZE_THREAD))
+    error_at (loc,
+	      "%<-fsanitize=memtag%> 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)
@@ -1220,7 +1241,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)
-       || (opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS))
+       || (opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS)
+       || (opts->x_flag_sanitize & SANITIZE_MEMTAG))
       && !opts_set->x_flag_sanitize_address_use_after_scope)
     opts->x_flag_sanitize_address_use_after_scope = true;
 
@@ -1849,6 +1871,7 @@ 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 (memtag, (SANITIZE_MEMTAG), true),
   SANITIZER_OPT (hwaddress, (SANITIZE_HWADDRESS | SANITIZE_USER_HWADDRESS),
 		 true),
   SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
diff --git a/gcc/target.def b/gcc/target.def
index badae860335e4a570f189c9f8011da5ab8c15439..2a366b7b58ed459574ab9aa3f008ed0f05bf2666 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6716,6 +6716,30 @@ DEFHOOK
  bool, (), default_memtag_can_tag_addresses)
 
 DEFHOOK
+(has_memory_tagging,
+ "True if backend architecture naturally supports tagging addresses and\
+ checking those tags.  This feature means that -fsanitize=memtag can work.",
+ bool, (), default_memtag_has_memory_tagging)
+
+DEFHOOK
+(tag_size,
+ "Return the size in bits of a tag for this platform.",
+ uint8_t, (), default_memtag_tag_size)
+
+DEFHOOK
+(granule_size,
+ "Return how many bytes in real memory each byte in shadow memory represents.\n\
+I.e. one byte in shadow memory being colour 1 implies the assocaiated\n\
+targetm.memtag.granule_size () bytes in real memory must all be accessed by\n\
+pointers tagged as 1.",
+uint8_t, (), default_memtag_granule_size)
+
+DEFHOOK
+(copy_tag,
+ "Emit insns to copy the tag in FROM to TO.",
+void, (rtx to, rtx from), default_memtag_copy_tag)
+
+DEFHOOK
 (addtag,
  "Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by\
  TAG_OFFSET.\n\
@@ -6740,6 +6764,17 @@ expression into a pseudo register, then returning that pseudo register.",
 rtx, (rtx oper, rtx target), NULL)
 
 DEFHOOK
+(tag,
+ "This function should emit an RTX to colour memory.\n\
+It's given arguments TAGGED_START, ADDRESS_OFFSET, TAG_OFFSET, SIZE, where\n\
+TAGGED_START and SIZE are RTL expressions, ADDRESS_OFFSET is a poly_int64\n\
+and TAG_OFFSET is a uint8_t.\n\
+It should emit RTL to colour \"shadow memory\" for the relevant range the\n\
+colour of the tag it was given.",
+  void, (rtx tagged_start, poly_int64 address_offset, uint8_t tag_offset, rtx size),
+NULL)
+
+DEFHOOK
 (gentag,
  "Set the BASE argument to UNTAGGED with some random tag.\n\
 This function is used to generate a tagged base for the current stack frame.",
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 4418db74f52f669c22702f5a4a093172f48a1b46..9c69589e33121638d349b882b4f26d29ac449d20 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -285,6 +285,10 @@ extern void default_remove_extra_call_preserved_regs (rtx_insn *,
 						      HARD_REG_SET *);
 
 extern bool default_memtag_can_tag_addresses ();
+extern bool default_memtag_has_memory_tagging ();
+extern uint8_t default_memtag_tag_size ();
+extern uint8_t default_memtag_granule_size ();
 extern void default_memtag_gentag (rtx, rtx);
+extern void default_memtag_copy_tag (rtx, rtx);
 extern rtx default_memtag_addtag (rtx, poly_int64, uint8_t);
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 048c7f3cff5d87d0d40b93f6cf8cb41de670711d..b8a74a5f3750dad102311f1e4298a63416f1261b 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2377,6 +2377,24 @@ default_memtag_can_tag_addresses ()
   return false;
 }
 
+bool
+default_memtag_has_memory_tagging ()
+{
+  return false;
+}
+
+uint8_t
+default_memtag_tag_size ()
+{
+  return 8;
+}
+
+uint8_t
+default_memtag_granule_size ()
+{
+  return 16;
+}
+
 void
 default_memtag_gentag (rtx base, rtx untagged)
 {
@@ -2403,6 +2421,70 @@ default_memtag_gentag (rtx base, rtx untagged)
     }
 }
 
+void
+default_memtag_copy_tag (rtx to, rtx from)
+{
+  /* TODO:
+      I want to have a sequence as minimal as possible here, since this code
+      sequence could be emitted many times.
+
+      My first attempt was the below,
+
+	  rtx temp = hwasan_extract_tag (from);
+	  store_bit_field (to, 8, 56, 0, 0,
+			  QImode, temp, false);
+      
+     Unfortunately, despite having much less instructions, for AArch64 this can
+     cause a problem in LRA if the `to` RTX eventually resolves to being the
+     stack pointer.
+     This happens because the instruction that gets emitted from
+     `store_bit_field` corresponds to a pattern that can't handle the stack
+     pointer and LRA can't figure out to use a temporary register in the `bfi`
+     instruction's place.
+
+     This doesn't cause a problem at the moment since there's currently no way
+     the stack pointer should be given to this function.  The hook is only used
+     when poisoning variables with HWASAN_MARK, and in that function the `to`
+     RTX should always be pointing to a tagged variable on the stack (since
+     the variable is tagged it can't be the stack pointer since that is
+     untagged).
+
+     Eventually we will be generating random tags as the "start" tag for each
+     frame.  When this happens we can no longer avoid the background colour at
+     compile time since we will not know what offset to avoid.
+     This will mean we no longer avoid a `tag_offset` of 0, and hence
+     `hwasan_with_tag` could emit simple PLUS statements.
+
+     When that happens, the last variable on the stack could very well have
+     a zero tag offset and somewhere else in the compiler could optimise that
+     to simply use the stack pointer.
+
+     That would trigger an ICE due to LRA being unable to reload the
+     `insv_regdi` pattern.
+
+     The code sequence I'm emitting at the moment works just fine in all
+     circumstances, but it would be nice to find a smaller sequence.  */
+  rtx temp = gen_reg_rtx (Pmode);
+  uint64_t tag_mask = 0xFFUL << HWASAN_SHIFT;
+  emit_insn (gen_anddi3 (to, to, GEN_INT (~tag_mask)));
+  /* Can't use GEN_INT (tag_mask) since GEN_INT calls `gen_rtx_CONST_INT` which
+     takes a `HOST_WIDE_INT`.
+     HOST_WIDE_INT can't hold a uint64_t with the top bit set, hence in order
+     to avoid UB we have to emit instructions for the machine to use some
+     uint64_t arithmetic.
+
+     The extra instructions seem to eventually end up with the same output in
+     most cases (I've not yet seen a case where the generation of the mask in
+     three or one `emit_insn` calls changes the codegen).  */
+
+  /* emit_insn (gen_anddi3 (temp, from, GEN_INT (tag_mask)));  */
+  emit_move_insn (temp, GEN_INT (0xff));
+  emit_insn (gen_ashldi3 (temp, temp, HWASAN_SHIFT_RTX));
+  emit_insn (gen_anddi3 (temp, from, temp));
+
+  emit_insn (gen_iordi3 (to, to, from));
+}
+
 rtx
 default_memtag_addtag (rtx base, poly_int64 offset, uint8_t tag_offset)
 {
diff --git a/gcc/testsuite/gcc.dg/hwasan/poly-int-stack-vars.c b/gcc/testsuite/gcc.dg/hwasan/poly-int-stack-vars.c
new file mode 100644
index 0000000000000000000000000000000000000000..cb0ca7d3a06c5a2de258ba20be974009410a7a44
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/poly-int-stack-vars.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+
+/* TODO Need to give this test the correct arguments
+   (i.e. compile with memory tagging enabled).
+
+   This is needed because the introduction of HWASAN_CHECK statements stops the
+   compiler from being able to vectorise the loop with SVE, hence stopping the
+   introduction of `addvl` instructions.
+
+   In other words, this test doesn't really belong in this directory, but I
+   haven't yet created the directory for checking memtag code generation and
+   this is a dummy commit anyway.  */
+/* Non-constant sizes.
+   Code designed to store SVE registers on the stack.
+   This is needed to exercise the poly_int64 handling for HWASAN and MTE
+   instrumentation. */
+int u;
+
+void
+foo_sve (int *p)
+{
+  int i;
+  #pragma omp for simd lastprivate(u) schedule (static, 32)
+  for (i = 0; i < 1024; i++)
+    u = p[i];
+}
+
+void
+bar_sve (int *p)
+{
+  int i;
+  #pragma omp taskloop simd lastprivate(u)
+  for (i = 0; i < 1024; i++)
+    u = p[i];
+}
+
+/* Ensure we are storing SVE vectors on the stack -- otherwise we're not
+   exercising the code path for poly_int64's.  */
+/* { dg-final { scan-assembler "addvl" } } */
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 5e077892a6371a7b534007d5dffd421b218ea694..6dcd830c3a93a93ce27d9b80137af6d9d1288ff7 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1765,6 +1765,16 @@ process_options (void)
       flag_sanitize &= ~SANITIZE_HWADDRESS;
     }
 
+  /* Memtag requires hardware support.  */
+  if (flag_sanitize & SANITIZE_MEMTAG && !targetm.memtag.has_memory_tagging ())
+    {
+      warning_at (UNKNOWN_LOCATION, 0,
+		  "%<-fsanitize=memtag%> requires hardware support "
+		  "that is not advertised for this target");
+      flag_sanitize &= ~SANITIZE_MEMTAG;
+    }
+
+
  /* Do not use IPA optimizations for register allocation if profiler is active
     or patchable function entries are inserted for run-time instrumentation
     or port does not emit prologue and epilogue as RTL.  */
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index b4b5c903e13b1695daeff66a680c64fa7da0829d..73ff987544487e44c4c14419fdc2b27b8b6ddb25 100644
--- a/gcc/tree-ssa.c
+++ b/gcc/tree-ssa.c
@@ -1704,7 +1704,9 @@ execute_update_addresses_taken (void)
 		  gimple_ior_addresses_taken (addresses_taken, stmt);
 		  gimple_call_set_arg (stmt, 1, arg);
 		}
-	      else if (is_asan_mark_p (stmt)
+	      else if ((is_asan_mark_p (stmt)
+			&& (!sanitize_flags_p (SANITIZE_MEMTAG)
+			    || !asan_mark_p (stmt, ASAN_MARK_POISON)))
 		       || gimple_call_internal_p (stmt, IFN_GOMP_SIMT_ENTER))
 		;
 	      else


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 17/X] [libsanitizer] Add hwasan Exception handling
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (13 preceding siblings ...)
  2019-11-05 11:35   ` [PATCH 14/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN Matthew Malcomson
@ 2019-11-05 11:35   ` Matthew Malcomson
  2019-11-05 11:35   ` [PATCH 9/X] [libsanitizer] Remove lazy thread initialisation Matthew Malcomson
                     ` (14 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:35 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub, Evgenii Stepanov, Peter Collingbourne

[-- Attachment #1: Type: text/plain, Size: 15010 bytes --]

When tagging the stack we need to ensure that any stack frames are
untagged during unwinding of any sort.  If this is not done, then
functions called later which re-use the stack can observe tag mismatches
on accesses to memory they have not tagged but that has been tagged
previously.

This applies equally to C++ exceptions as it does to longjmp and normal
function return.
The approach that LLVM has taken to account for C++ exceptions, is to
add a new personality routine on every function.  This new personality
routine calls the original personality routine, then untags the stack of
that function.

In order to untag the stack of the function currently being unwound, the
new personality wrapper needs to know the start and end of the current
stack frame.  The current implementation in libhwasan uses the frame
pointer to find the start of the stack frame.
https://github.com/llvm-mirror/compiler-rt/blob/69445f095c22aac2388f939bedebf224a6efcdaf/lib/hwasan/hwasan_exceptions.cpp#L58
This does not work for GCC, where the frame pointer is usually the same
as the stack pointer.

This patch demonstrates how adding C++ exception handling into GCC might
work, but currently does not work.  I intend to work on getting this
functionality, but do not consider it a blocker.

The current implementation ensures every function has a wrapped
personality function by modifying `get_personality_function` to check
for if we're handling exceptions and sanitizing the code with hwasan.
If so it returns a specially generated personality function that calls
`__hwasan_personality_wrapper` in libhwasan.


I've been testing the compiler instrumentation with a hack in libhwasan shown
below and things are mostly working, but I don't feel it's a good way forward.

I was wondering -- does anyone have any better ideas to keep this personality
method working for gcc?
I've also included the people that worked on exception handling in LLVM.  I
figure you may have tried other things before and hence have a good idea of
the pitfalls in this area.

My main aim is to get software tagging working for the kernel, so to me
exception handling is not a deal-breaker, but I would really like to get this
feature working.


Patch to libhwasan that I've been testing the instrumentation with.
######
diff --git a/libsanitizer/hwasan/hwasan_exceptions.cpp b/libsanitizer/hwasan/hwasan_exceptions.cpp
index 169e787..faec2e8 100644
--- a/libsanitizer/hwasan/hwasan_exceptions.cpp
+++ b/libsanitizer/hwasan/hwasan_exceptions.cpp
@@ -52,14 +52,16 @@ __hwasan_personality_wrapper(int version, _Unwind_Action actions,
   // Here we assume that the frame record appears after any locals. This is not
   // required by AAPCS but is a requirement for HWASAN instrumented functions.
   if ((actions & _UA_CLEANUP_PHASE) && rc == _URC_CONTINUE_UNWIND) {
+    uptr sp = get_cfa(context);
 #if defined(__x86_64__)
     uptr fp = get_gr(context, 6); // rbp
 #elif defined(__aarch64__)
-    uptr fp = get_gr(context, 29); // x29
+    uptr fp = *(uptr *)sp;
+    if (fp == 0)
+      return rc;
 #else
 #error Unsupported architecture
 #endif
-    uptr sp = get_cfa(context);
     TagMemory(sp, fp - sp, 0);
   }
######



gcc/ChangeLog:

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

	* asan.c (hwasan_create_personality_thunk): New.
	* asan.h (hwasan_create_personality_thunk): New.
	* expr.c (get_personality_function): Add special function if
	using hwasan.



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


diff --git a/gcc/asan.h b/gcc/asan.h
index c27d679c117a9cc7b15b7b4c6710cf0b46050089..ff6adf2391ee1602a3c15755312a04f82d6369ce 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -36,6 +36,7 @@ extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t)
 extern rtx_insn *hwasan_emit_uncolour_frame (rtx, rtx, rtx_insn *);
 extern bool hwasan_expand_check_ifn (gimple_stmt_iterator *, bool);
 extern bool hwasan_expand_mark_ifn (gimple_stmt_iterator *);
+extern tree hwasan_create_personality_thunk (tree);
 extern bool memory_tagging_p (void);
 extern bool hwasan_sanitize_stack_p (void);
 extern bool gate_hwasan (void);
diff --git a/gcc/asan.c b/gcc/asan.c
index edfbf2048b67d3dc7be78a8e9961152c4fb44902..ef7c90e3358c8fa880b8e4002996f27541c26953 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -260,6 +260,10 @@ hash_set <tree> *asan_used_labels = NULL;
 
 static uint8_t tag_offset = 0;
 static rtx hwasan_base_ptr = NULL_RTX;
+static hash_map <uintptr_t, uintptr_t, simple_hashmap_traits<int_hash <uintptr_t, ~0ULL>, uintptr_t> >
+		*hwasan_wrapped_personalities = NULL;
+static tree hwasan_gr_decl = NULL_TREE;
+static tree hwasan_cfa_decl = NULL_TREE;
 
 /* Sets shadow offset to value in string VAL.  */
 
@@ -3951,6 +3955,220 @@ hwasan_tag_init ()
   tag_offset = HWASAN_STACK_BACKGROUND + 1;
 }
 
+tree
+hwasan_create_personality_thunk (tree orig_personality_decl)
+{
+  /* Only works with DWARF2 debugging.  */
+  /* Create a function called __hwasan_personality_thunk<orig_personality_name>.
+     (should be bare __hwasan_personality_thunk if function has no
+     personality function already).
+     That function should call __hwasan_personality_wrapper with arguments
+     - Same first 5 arguments.
+     - Original personality function argument.
+     - Unwind_GetGR function
+     - Unwind_GetCFA function
+     And then return.
+     TODO the personality_wrapper should be a tail-call.  */
+
+  if (! sanitize_flags_p (SANITIZE_HWADDRESS, orig_personality_decl))
+    return orig_personality_decl;
+  if (! hwasan_wrapped_personalities)
+    hwasan_wrapped_personalities = new hash_map<uintptr_t, uintptr_t, simple_hashmap_traits<int_hash <uintptr_t, ~0ULL>, uintptr_t> > (16);
+
+  tree *personality_wrapper
+    = (tree *)hwasan_wrapped_personalities->get
+		    ((uintptr_t)orig_personality_decl);
+  if (personality_wrapper)
+    return *personality_wrapper;
+
+
+  const char *name;
+  if (! orig_personality_decl)
+    name = "__hwasan_personality_thunk";
+  else
+    name = ACONCAT (("__hwasan_personality_thunk.",
+		     IDENTIFIER_POINTER (DECL_NAME (orig_personality_decl)),
+		     NULL));
+
+  /* Create a new function.  */
+  tree thunk_type = build_function_type_list (unsigned_type_node,
+					      integer_type_node,
+					      integer_type_node,
+					      long_long_unsigned_type_node,
+					      ptr_type_node, ptr_type_node,
+					      NULL_TREE);
+  tree thunk_decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL,
+				get_identifier (name), thunk_type);
+  DECL_ARTIFICIAL (thunk_decl) = 1;
+  /* External linkage ??? (NO) */
+  DECL_EXTERNAL (thunk_decl) = 0;
+  /* Visible outside this translation unit??? */
+  TREE_PUBLIC (thunk_decl) = 0;
+  TREE_USED (thunk_decl) = 1;
+  DECL_IGNORED_P (thunk_decl) = 1;
+  DECL_INITIAL (thunk_decl) = make_node (BLOCK);
+  BLOCK_SUPERCONTEXT (DECL_INITIAL (thunk_decl)) = thunk_decl;
+  TREE_USED (DECL_INITIAL (thunk_decl)) = 1;
+  DECL_PRESERVE_P (thunk_decl) = 1;
+  DECL_UNINLINABLE (thunk_decl) = 1;
+  /* Having the `no_sanitize` attribute is partly to ensure the internal
+     function doesn't have a personality attributed to it.  */
+  DECL_ATTRIBUTES (thunk_decl)
+    = tree_cons (get_identifier ("no_sanitize"),
+		 build_int_cst (unsigned_type_node, SANITIZE_HWADDRESS),
+		 DECL_ATTRIBUTES (thunk_decl));
+  /* For arguments:
+	- Create a chain of ..._DECL nodes.
+	- Use DECL_ARGUMENTS (thunk_decl) = that_chain  */
+  tree arg[5] = {0};
+  const char* names[5] = {
+      "version",
+      "actions",
+      "exception_class",
+      "unwind_exception",
+      "context"
+  };
+  tree thunk_arg_types = TYPE_ARG_TYPES (thunk_type);
+  for (size_t i = 0; i < 5; i++)
+    {
+      tree type = TREE_VALUE (thunk_arg_types);
+      thunk_arg_types = TREE_CHAIN (thunk_arg_types);
+      arg[i] = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			   get_identifier (names[i]),
+			   type);
+      DECL_ARG_TYPE (arg[i]) = type;
+      DECL_CONTEXT (arg[i]) = thunk_decl;
+      DECL_ARTIFICIAL (arg[i]) = 1;
+      TREE_READONLY (arg[i]) = 0;
+      TREE_USED (arg[i]) = 0;
+    }
+
+  tree thunk_decl_chain = NULL_TREE;
+  for (int j = 4; j >= 0; j--)
+    {
+      DECL_CHAIN (arg[j]) = thunk_decl_chain;
+      thunk_decl_chain = arg[j];
+    }
+  DECL_ARGUMENTS (thunk_decl) = thunk_decl_chain;
+
+  tree resdecl
+    = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, unsigned_type_node);
+  DECL_RESULT (thunk_decl) = resdecl;
+  DECL_CONTEXT (resdecl) = thunk_decl;
+
+
+  /* Declaration for the wrapper we want to call (this function is provided in
+     libhwasan).  */
+  tree wrap_type = build_function_type_list (unsigned_type_node,
+					     integer_type_node,
+					     integer_type_node,
+					     long_long_unsigned_type_node,
+					     ptr_type_node, ptr_type_node,
+					     ptr_type_node, ptr_type_node,
+					     ptr_type_node, NULL_TREE);
+  tree wrap_decl = build_decl (UNKNOWN_LOCATION,
+			       FUNCTION_DECL,
+			       get_identifier ("__hwasan_personality_wrapper"),
+			       wrap_type);
+  DECL_ARTIFICIAL (wrap_decl) = 1;
+  DECL_EXTERNAL (wrap_decl) = 1;
+  TREE_PUBLIC (wrap_decl) = 1;
+
+  /* Create declarations for the functions we need to provide
+     __hwasan_personality_wrapper.  */
+  tree gr_decl;
+  if (hwasan_gr_decl)
+    gr_decl = hwasan_gr_decl;
+  else
+    {
+      gr_decl = build_decl (UNKNOWN_LOCATION,
+			    FUNCTION_DECL,
+			    get_identifier ("_Unwind_GetGR"),
+			    build_function_type_list (unsigned_type_node,
+						      ptr_type_node,
+						      integer_type_node,
+						      NULL_TREE));
+      DECL_ARTIFICIAL (gr_decl) = 1;
+      DECL_EXTERNAL (gr_decl) = 1;
+      TREE_PUBLIC (gr_decl) = 0;
+      hwasan_gr_decl = gr_decl;
+    }
+
+  tree cfa_decl;
+  if (hwasan_cfa_decl)
+    cfa_decl = hwasan_cfa_decl;
+  else
+    {
+      cfa_decl = build_decl (UNKNOWN_LOCATION,
+				  FUNCTION_DECL,
+				  get_identifier ("_Unwind_GetCFA"),
+				  build_function_type_list (unsigned_type_node,
+							    ptr_type_node,
+							    NULL_TREE));
+      DECL_ARTIFICIAL (cfa_decl) = 1;
+      DECL_EXTERNAL (cfa_decl) = 1;
+      TREE_PUBLIC (cfa_decl) = 0;
+      hwasan_cfa_decl = cfa_decl;
+    }
+
+  /* The below code partly taken from `cgraph_build_static_ctor_1`.
+     The aim is to create a new function using the gimple statements `body`
+     created above.  */
+  tree saved_current_decl = current_function_decl;
+  struct function *old_cfun = cfun;
+
+  current_function_decl = thunk_decl;
+  /* Sets `cfun` to the newly allocated function structure.  */
+  allocate_struct_function (thunk_decl, false);
+
+  /* Call the personality wrapper with the personality arguments plus pointers
+     to the relevant unwinder functions.
+     Put that call into the `body` tree that we will assign to the `thunk_decl`
+     function.  */
+  tree body = NULL_TREE;
+  tree personality_addr;
+  if (orig_personality_decl)
+    personality_addr = build_fold_addr_expr (orig_personality_decl);
+  else
+    {
+      tree ptr_type = build_pointer_type (TREE_TYPE (thunk_decl));
+      personality_addr = build_int_cst (ptr_type, 0);
+    }
+  tree call_expr = build_call_expr (wrap_decl, 8,
+				    /* 5 arguments from personality.  */
+				    arg[0], arg[1], arg[2],
+				    arg[3], arg[4],
+				    /* 3 arguments from compilers
+				       knowledge.  */
+				    personality_addr,
+				    build_fold_addr_expr (gr_decl),
+				    build_fold_addr_expr (cfa_decl));
+  tree ret_assign = build2 (INIT_EXPR, TREE_TYPE (resdecl), resdecl, call_expr);
+  tree return_statement = build1 (RETURN_EXPR, void_type_node, ret_assign);
+  append_to_statement_list (return_statement, &body);
+  DECL_SAVED_TREE (thunk_decl) = body;
+
+  gimplify_function_tree (thunk_decl);
+
+  opt_pass *saved_current_pass = current_pass;
+  cgraph_node::add_new_function (thunk_decl, false);
+  /* We save and restore `current_pass` because the value gets set to NULL
+     somewhere in `add_new_function` and hence we get a segmentation fault
+     later on.
+
+     TODO There"s probably a better way to do this, since I don't see other
+     functions using `cgraph_node::add_new_function` doing this save and
+     restore.  */
+  current_pass = saved_current_pass;
+  set_cfun (old_cfun);
+  current_function_decl = saved_current_decl;
+
+  gcc_assert (! hwasan_wrapped_personalities->put
+		    ((uintptr_t)orig_personality_decl,
+		     (uintptr_t)thunk_decl));
+  return thunk_decl;
+}
+
 rtx
 hwasan_extract_tag (rtx tagged_pointer)
 {
diff --git a/gcc/expr.c b/gcc/expr.c
index 2cd4bcb74cf92ccd7bd88ccb556b7e1eed2c6e06..46e3a75c6c6d66fd4f550fbd5b9f9c40837d2982 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -62,6 +62,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ccmp.h"
 #include "gimple-fold.h"
 #include "rtx-vector-builder.h"
+#include "asan.h"
 
 
 /* If this is nonzero, we do not bother generating VOLATILE
@@ -12558,10 +12559,35 @@ get_personality_function (tree decl)
   tree personality = DECL_FUNCTION_PERSONALITY (decl);
   enum eh_personality_kind pk;
 
-  pk = function_needs_eh_personality (DECL_STRUCT_FUNCTION (decl));
+  /* Need to have a personality function on pretty much every function that can
+     allocate stack space for HWASAN.  This is because HWASAN needs to clear
+     the shadow stack of every function frame an exception goes through -- no
+     matter whether there is a cleanup to run or not.
+     Hence, if we're compiling with exceptions, and we're sanitizing this
+     function (because if we're not sanitizing this function the frame doesn't
+     have any coloured shadow stack) then we ensure we add a personality
+     function to it.  */
+  if (flag_exceptions && sanitize_flags_p (SANITIZE_HWADDRESS, decl))
+    pk = eh_personality_any;
+  else
+    pk = function_needs_eh_personality (DECL_STRUCT_FUNCTION (decl));
+
   if (pk == eh_personality_none)
     return NULL;
 
+  /* Want to add a personality routine to every function *except* those that
+     are specially marked.  */
+  if (sanitize_flags_p (SANITIZE_HWADDRESS, decl))
+    {
+      /* Set the personality for this function to be a wrapper around whatever
+	 the current personality is.  If this function has no associated
+	 personality then add a personality function to only clear shadow
+	 stack.  */
+      tree t = DECL_FUNCTION_PERSONALITY (decl);
+      personality = hwasan_create_personality_thunk (t);
+      DECL_FUNCTION_PERSONALITY (decl) = personality;
+    }
+
   if (!personality
       && pk == eh_personality_any)
     personality = lang_hooks.eh_personality ();


[-- Attachment #2: hwasan-patch17.patch --]
[-- Type: text/plain, Size: 11096 bytes --]

diff --git a/gcc/asan.h b/gcc/asan.h
index c27d679c117a9cc7b15b7b4c6710cf0b46050089..ff6adf2391ee1602a3c15755312a04f82d6369ce 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -36,6 +36,7 @@ extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t)
 extern rtx_insn *hwasan_emit_uncolour_frame (rtx, rtx, rtx_insn *);
 extern bool hwasan_expand_check_ifn (gimple_stmt_iterator *, bool);
 extern bool hwasan_expand_mark_ifn (gimple_stmt_iterator *);
+extern tree hwasan_create_personality_thunk (tree);
 extern bool memory_tagging_p (void);
 extern bool hwasan_sanitize_stack_p (void);
 extern bool gate_hwasan (void);
diff --git a/gcc/asan.c b/gcc/asan.c
index edfbf2048b67d3dc7be78a8e9961152c4fb44902..ef7c90e3358c8fa880b8e4002996f27541c26953 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -260,6 +260,10 @@ hash_set <tree> *asan_used_labels = NULL;
 
 static uint8_t tag_offset = 0;
 static rtx hwasan_base_ptr = NULL_RTX;
+static hash_map <uintptr_t, uintptr_t, simple_hashmap_traits<int_hash <uintptr_t, ~0ULL>, uintptr_t> >
+		*hwasan_wrapped_personalities = NULL;
+static tree hwasan_gr_decl = NULL_TREE;
+static tree hwasan_cfa_decl = NULL_TREE;
 
 /* Sets shadow offset to value in string VAL.  */
 
@@ -3951,6 +3955,220 @@ hwasan_tag_init ()
   tag_offset = HWASAN_STACK_BACKGROUND + 1;
 }
 
+tree
+hwasan_create_personality_thunk (tree orig_personality_decl)
+{
+  /* Only works with DWARF2 debugging.  */
+  /* Create a function called __hwasan_personality_thunk<orig_personality_name>.
+     (should be bare __hwasan_personality_thunk if function has no
+     personality function already).
+     That function should call __hwasan_personality_wrapper with arguments
+     - Same first 5 arguments.
+     - Original personality function argument.
+     - Unwind_GetGR function
+     - Unwind_GetCFA function
+     And then return.
+     TODO the personality_wrapper should be a tail-call.  */
+
+  if (! sanitize_flags_p (SANITIZE_HWADDRESS, orig_personality_decl))
+    return orig_personality_decl;
+  if (! hwasan_wrapped_personalities)
+    hwasan_wrapped_personalities = new hash_map<uintptr_t, uintptr_t, simple_hashmap_traits<int_hash <uintptr_t, ~0ULL>, uintptr_t> > (16);
+
+  tree *personality_wrapper
+    = (tree *)hwasan_wrapped_personalities->get
+		    ((uintptr_t)orig_personality_decl);
+  if (personality_wrapper)
+    return *personality_wrapper;
+
+
+  const char *name;
+  if (! orig_personality_decl)
+    name = "__hwasan_personality_thunk";
+  else
+    name = ACONCAT (("__hwasan_personality_thunk.",
+		     IDENTIFIER_POINTER (DECL_NAME (orig_personality_decl)),
+		     NULL));
+
+  /* Create a new function.  */
+  tree thunk_type = build_function_type_list (unsigned_type_node,
+					      integer_type_node,
+					      integer_type_node,
+					      long_long_unsigned_type_node,
+					      ptr_type_node, ptr_type_node,
+					      NULL_TREE);
+  tree thunk_decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL,
+				get_identifier (name), thunk_type);
+  DECL_ARTIFICIAL (thunk_decl) = 1;
+  /* External linkage ??? (NO) */
+  DECL_EXTERNAL (thunk_decl) = 0;
+  /* Visible outside this translation unit??? */
+  TREE_PUBLIC (thunk_decl) = 0;
+  TREE_USED (thunk_decl) = 1;
+  DECL_IGNORED_P (thunk_decl) = 1;
+  DECL_INITIAL (thunk_decl) = make_node (BLOCK);
+  BLOCK_SUPERCONTEXT (DECL_INITIAL (thunk_decl)) = thunk_decl;
+  TREE_USED (DECL_INITIAL (thunk_decl)) = 1;
+  DECL_PRESERVE_P (thunk_decl) = 1;
+  DECL_UNINLINABLE (thunk_decl) = 1;
+  /* Having the `no_sanitize` attribute is partly to ensure the internal
+     function doesn't have a personality attributed to it.  */
+  DECL_ATTRIBUTES (thunk_decl)
+    = tree_cons (get_identifier ("no_sanitize"),
+		 build_int_cst (unsigned_type_node, SANITIZE_HWADDRESS),
+		 DECL_ATTRIBUTES (thunk_decl));
+  /* For arguments:
+	- Create a chain of ..._DECL nodes.
+	- Use DECL_ARGUMENTS (thunk_decl) = that_chain  */
+  tree arg[5] = {0};
+  const char* names[5] = {
+      "version",
+      "actions",
+      "exception_class",
+      "unwind_exception",
+      "context"
+  };
+  tree thunk_arg_types = TYPE_ARG_TYPES (thunk_type);
+  for (size_t i = 0; i < 5; i++)
+    {
+      tree type = TREE_VALUE (thunk_arg_types);
+      thunk_arg_types = TREE_CHAIN (thunk_arg_types);
+      arg[i] = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			   get_identifier (names[i]),
+			   type);
+      DECL_ARG_TYPE (arg[i]) = type;
+      DECL_CONTEXT (arg[i]) = thunk_decl;
+      DECL_ARTIFICIAL (arg[i]) = 1;
+      TREE_READONLY (arg[i]) = 0;
+      TREE_USED (arg[i]) = 0;
+    }
+
+  tree thunk_decl_chain = NULL_TREE;
+  for (int j = 4; j >= 0; j--)
+    {
+      DECL_CHAIN (arg[j]) = thunk_decl_chain;
+      thunk_decl_chain = arg[j];
+    }
+  DECL_ARGUMENTS (thunk_decl) = thunk_decl_chain;
+
+  tree resdecl
+    = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, unsigned_type_node);
+  DECL_RESULT (thunk_decl) = resdecl;
+  DECL_CONTEXT (resdecl) = thunk_decl;
+
+
+  /* Declaration for the wrapper we want to call (this function is provided in
+     libhwasan).  */
+  tree wrap_type = build_function_type_list (unsigned_type_node,
+					     integer_type_node,
+					     integer_type_node,
+					     long_long_unsigned_type_node,
+					     ptr_type_node, ptr_type_node,
+					     ptr_type_node, ptr_type_node,
+					     ptr_type_node, NULL_TREE);
+  tree wrap_decl = build_decl (UNKNOWN_LOCATION,
+			       FUNCTION_DECL,
+			       get_identifier ("__hwasan_personality_wrapper"),
+			       wrap_type);
+  DECL_ARTIFICIAL (wrap_decl) = 1;
+  DECL_EXTERNAL (wrap_decl) = 1;
+  TREE_PUBLIC (wrap_decl) = 1;
+
+  /* Create declarations for the functions we need to provide
+     __hwasan_personality_wrapper.  */
+  tree gr_decl;
+  if (hwasan_gr_decl)
+    gr_decl = hwasan_gr_decl;
+  else
+    {
+      gr_decl = build_decl (UNKNOWN_LOCATION,
+			    FUNCTION_DECL,
+			    get_identifier ("_Unwind_GetGR"),
+			    build_function_type_list (unsigned_type_node,
+						      ptr_type_node,
+						      integer_type_node,
+						      NULL_TREE));
+      DECL_ARTIFICIAL (gr_decl) = 1;
+      DECL_EXTERNAL (gr_decl) = 1;
+      TREE_PUBLIC (gr_decl) = 0;
+      hwasan_gr_decl = gr_decl;
+    }
+
+  tree cfa_decl;
+  if (hwasan_cfa_decl)
+    cfa_decl = hwasan_cfa_decl;
+  else
+    {
+      cfa_decl = build_decl (UNKNOWN_LOCATION,
+				  FUNCTION_DECL,
+				  get_identifier ("_Unwind_GetCFA"),
+				  build_function_type_list (unsigned_type_node,
+							    ptr_type_node,
+							    NULL_TREE));
+      DECL_ARTIFICIAL (cfa_decl) = 1;
+      DECL_EXTERNAL (cfa_decl) = 1;
+      TREE_PUBLIC (cfa_decl) = 0;
+      hwasan_cfa_decl = cfa_decl;
+    }
+
+  /* The below code partly taken from `cgraph_build_static_ctor_1`.
+     The aim is to create a new function using the gimple statements `body`
+     created above.  */
+  tree saved_current_decl = current_function_decl;
+  struct function *old_cfun = cfun;
+
+  current_function_decl = thunk_decl;
+  /* Sets `cfun` to the newly allocated function structure.  */
+  allocate_struct_function (thunk_decl, false);
+
+  /* Call the personality wrapper with the personality arguments plus pointers
+     to the relevant unwinder functions.
+     Put that call into the `body` tree that we will assign to the `thunk_decl`
+     function.  */
+  tree body = NULL_TREE;
+  tree personality_addr;
+  if (orig_personality_decl)
+    personality_addr = build_fold_addr_expr (orig_personality_decl);
+  else
+    {
+      tree ptr_type = build_pointer_type (TREE_TYPE (thunk_decl));
+      personality_addr = build_int_cst (ptr_type, 0);
+    }
+  tree call_expr = build_call_expr (wrap_decl, 8,
+				    /* 5 arguments from personality.  */
+				    arg[0], arg[1], arg[2],
+				    arg[3], arg[4],
+				    /* 3 arguments from compilers
+				       knowledge.  */
+				    personality_addr,
+				    build_fold_addr_expr (gr_decl),
+				    build_fold_addr_expr (cfa_decl));
+  tree ret_assign = build2 (INIT_EXPR, TREE_TYPE (resdecl), resdecl, call_expr);
+  tree return_statement = build1 (RETURN_EXPR, void_type_node, ret_assign);
+  append_to_statement_list (return_statement, &body);
+  DECL_SAVED_TREE (thunk_decl) = body;
+
+  gimplify_function_tree (thunk_decl);
+
+  opt_pass *saved_current_pass = current_pass;
+  cgraph_node::add_new_function (thunk_decl, false);
+  /* We save and restore `current_pass` because the value gets set to NULL
+     somewhere in `add_new_function` and hence we get a segmentation fault
+     later on.
+
+     TODO There"s probably a better way to do this, since I don't see other
+     functions using `cgraph_node::add_new_function` doing this save and
+     restore.  */
+  current_pass = saved_current_pass;
+  set_cfun (old_cfun);
+  current_function_decl = saved_current_decl;
+
+  gcc_assert (! hwasan_wrapped_personalities->put
+		    ((uintptr_t)orig_personality_decl,
+		     (uintptr_t)thunk_decl));
+  return thunk_decl;
+}
+
 rtx
 hwasan_extract_tag (rtx tagged_pointer)
 {
diff --git a/gcc/expr.c b/gcc/expr.c
index 2cd4bcb74cf92ccd7bd88ccb556b7e1eed2c6e06..46e3a75c6c6d66fd4f550fbd5b9f9c40837d2982 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -62,6 +62,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ccmp.h"
 #include "gimple-fold.h"
 #include "rtx-vector-builder.h"
+#include "asan.h"
 
 
 /* If this is nonzero, we do not bother generating VOLATILE
@@ -12558,10 +12559,35 @@ get_personality_function (tree decl)
   tree personality = DECL_FUNCTION_PERSONALITY (decl);
   enum eh_personality_kind pk;
 
-  pk = function_needs_eh_personality (DECL_STRUCT_FUNCTION (decl));
+  /* Need to have a personality function on pretty much every function that can
+     allocate stack space for HWASAN.  This is because HWASAN needs to clear
+     the shadow stack of every function frame an exception goes through -- no
+     matter whether there is a cleanup to run or not.
+     Hence, if we're compiling with exceptions, and we're sanitizing this
+     function (because if we're not sanitizing this function the frame doesn't
+     have any coloured shadow stack) then we ensure we add a personality
+     function to it.  */
+  if (flag_exceptions && sanitize_flags_p (SANITIZE_HWADDRESS, decl))
+    pk = eh_personality_any;
+  else
+    pk = function_needs_eh_personality (DECL_STRUCT_FUNCTION (decl));
+
   if (pk == eh_personality_none)
     return NULL;
 
+  /* Want to add a personality routine to every function *except* those that
+     are specially marked.  */
+  if (sanitize_flags_p (SANITIZE_HWADDRESS, decl))
+    {
+      /* Set the personality for this function to be a wrapper around whatever
+	 the current personality is.  If this function has no associated
+	 personality then add a personality function to only clear shadow
+	 stack.  */
+      tree t = DECL_FUNCTION_PERSONALITY (decl);
+      personality = hwasan_create_personality_thunk (t);
+      DECL_FUNCTION_PERSONALITY (decl) = personality;
+    }
+
   if (!personality
       && pk == eh_personality_any)
     personality = lang_hooks.eh_personality ();


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 16/X] [libsanitizer] Add tests
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (11 preceding siblings ...)
  2019-11-05 11:34   ` [aarch64] Allocate enough space for err_str in aarch64_handle_attr_branch_protection Matthew Malcomson
@ 2019-11-05 11:35   ` Matthew Malcomson
  2019-11-05 11:35   ` [PATCH 14/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN Matthew Malcomson
                     ` (16 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:35 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 56284 bytes --]

Adding hwasan tests.

Frankly, these could be tidied up a little.
I will be tidying them up while getting feedback on the hwasan introduction.


gcc/testsuite/ChangeLog:

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

	* c-c++-common/hwasan/arguments.c: New test.
	* c-c++-common/hwasan/halt_on_error-1.c: New test.
	* g++.dg/hwasan/rvo-handled.c: New test.
	* g++.dg/hwasan/try-catch-0.cpp: New test.
	* g++.dg/hwasan/try-catch-1.cpp: New test.
	* gcc.dg/hwasan/aligned-alloc.c: New test.
	* gcc.dg/hwasan/alloca-array-accessible.c: New test.
	* gcc.dg/hwasan/alloca-gets-different-tag.c: New test.
	* gcc.dg/hwasan/alloca-outside-caught.c: New test.
	* gcc.dg/hwasan/bitfield-1.c: New test.
	* gcc.dg/hwasan/bitfield-2.c: New test.
	* gcc.dg/hwasan/builtin-special-handling.c: New test.
	* gcc.dg/hwasan/check-interface.c: New test.
	* gcc.dg/hwasan/hwasan-poison-optimisation.c: New test.
	* gcc.dg/hwasan/hwasan-thread-access-parent.c: New test.
	* gcc.dg/hwasan/hwasan-thread-basic-failure.c: New test.
	* gcc.dg/hwasan/hwasan-thread-clears-stack.c: New test.
	* gcc.dg/hwasan/hwasan-thread-success.c: New test.
	* gcc.dg/hwasan/hwasan.exp: New file.
	* gcc.dg/hwasan/kernel-defaults.c: New test.
	* gcc.dg/hwasan/large-aligned-0.c: New test.
	* gcc.dg/hwasan/large-aligned-1.c: New test.
	* gcc.dg/hwasan/macro-definition.c: New test.
	* gcc.dg/hwasan/nested-functions-0.c: New test.
	* gcc.dg/hwasan/nested-functions-1.c: New test.
	* gcc.dg/hwasan/nested-functions-2.c: New test.
	* gcc.dg/hwasan/no-sanitize-attribute.c: New test.
	* gcc.dg/hwasan/random-frame-tag.c: New test.
	* gcc.dg/hwasan/setjmp-longjmp-0.c: New test.
	* gcc.dg/hwasan/setjmp-longjmp-1.c: New test.
	* gcc.dg/hwasan/stack-tagging-basic-0.c: New test.
	* gcc.dg/hwasan/stack-tagging-basic-1.c: New test.
	* gcc.dg/hwasan/stack-tagging-disable.c: New test.
	* gcc.dg/hwasan/vararray-outside-caught.c: New test.
	* gcc.dg/hwasan/very-large-objects.c: New test.
	* lib/hwasan-dg.exp: New file.



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


diff --git a/gcc/testsuite/c-c++-common/hwasan/arguments.c b/gcc/testsuite/c-c++-common/hwasan/arguments.c
new file mode 100644
index 0000000000000000000000000000000000000000..2d563eb8541694d501b021babd9452fd7fd502a3
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/arguments.c
@@ -0,0 +1,7 @@
+/*
+   TODO
+   Somehow test the conflict of arguments
+   -fsanitize=hwaddress -fsanitize=kernel-address
+   -fsanitize=hwaddress -fsanitize=address
+   -fsanitize=hwaddress -fsanitize=thread
+ */
diff --git a/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c b/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..118191e2e00bd07bd4839888d2fb29baec926c60
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c
@@ -0,0 +1,25 @@
+/* Test recovery mode.  */
+/* { dg-do run } */
+/* { dg-options "-fsanitize-recover=hwaddress" } */
+/* { dg-set-target-env-var HWASAN_OPTIONS "halt_on_error=false" } */
+/* { dg-shouldfail "hwasan" } */
+
+#include <string.h>
+
+volatile int ten = 16;
+
+int main() {
+  char x[10];
+  __builtin_memset(x, 0, ten + 1);
+  asm volatile ("" : : : "memory");
+  volatile int res = x[ten];
+  x[ten] = res + 3;
+  res = x[ten];
+  return 0;
+}
+
+/* { dg-output "WRITE of size 17 at 0x\[0-9a-f\]+.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+.*" } */
+/* { dg-output "WRITE of size 1 at 0x\[0-9a-f\]+.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+.*" } */
+
diff --git a/gcc/testsuite/g++.dg/hwasan/rvo-handled.c b/gcc/testsuite/g++.dg/hwasan/rvo-handled.c
new file mode 100644
index 0000000000000000000000000000000000000000..6e6934a0be1b0ce14c459555168f6a2590a8ec7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/hwasan/rvo-handled.c
@@ -0,0 +1,56 @@
+/* { dg-do run } */
+/* TODO Ensure this test has enough optimisation to get RVO. */
+
+#define assert(x) if (!(x)) __builtin_abort ()
+
+struct big_struct {
+    int left;
+    int right;
+    void *ptr;
+    int big_array[100];
+};
+
+/*
+   Tests for RVO (basically, checking -fsanitize=hwaddress has not broken RVO
+   in any way).
+
+   0) The value is accessible in both functions without a hwasan complaint.
+   1) RVO does happen.
+ */
+
+struct big_struct __attribute__ ((noinline))
+return_on_stack()
+{
+  struct big_struct x;
+  x.left = 100;
+  x.right = 20;
+  x.big_array[10] = 30;
+  x.ptr = &x;
+  return x;
+}
+
+struct big_struct __attribute__ ((noinline))
+unnamed_return_on_stack()
+{
+  return (struct big_struct){
+      .left = 100,
+      .right = 20,
+      .ptr = __builtin_frame_address (0),
+      .big_array = {0}
+  };
+}
+
+int main()
+{
+  struct big_struct x;
+  x = return_on_stack();
+  /* Check that RVO happens by checking the address that the callee saw.  */
+  assert (x.ptr == &x);
+  struct big_struct y;
+  y = unnamed_return_on_stack();
+  /* Know only running this on AArch64, which means stack grows downwards,
+     We're checking that the frame of the callee function is below the address
+     of this variable, which means that the callee function used RVO.  */
+  assert (y.ptr < (void *)&y);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/hwasan/try-catch-0.cpp b/gcc/testsuite/g++.dg/hwasan/try-catch-0.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d5bbc93564c7f32a029be1a1e071f44188c91da5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/hwasan/try-catch-0.cpp
@@ -0,0 +1,65 @@
+/* { dg-do run } */
+
+/* This version should work just fine.  */
+#include <stdexcept>
+
+char *intermediate_pointer = NULL;
+static void optimization_barrier(void* arg) {
+  asm volatile("" : : "r"(arg) : "memory");
+}
+
+__attribute__((noinline))
+void h() {
+  char x[1000];
+  intermediate_pointer = (void *)&x;
+  optimization_barrier(x);
+  throw std::runtime_error("hello");
+}
+
+__attribute__((noinline))
+void g() {
+  char x[1000];
+  optimization_barrier(x);
+  h();
+  optimization_barrier(x);
+}
+
+__attribute__((noinline))
+void hwasan_read(char *p, int size) {
+  char volatile sink;
+  for (int i = 0; i < size; ++i)
+    sink = p[i];
+}
+
+__attribute__((noinline, no_sanitize("hwaddress"))) void after_catch() {
+  char x[10000];
+  hwasan_read(&x[0], sizeof(x));
+}
+
+
+__attribute__((noinline))
+void f() {
+  char x[1000];
+  try {
+    // Put two tagged frames on the stack, throw an exception from the deepest one.
+    g();
+  } catch (const std::runtime_error &e) {
+    // Put an untagged frame on stack, check that it is indeed untagged.
+    // This relies on exception support zeroing out stack tags.
+    // BAD: tag-mismatch
+    after_catch();
+    // Check that an in-scope stack allocation is still tagged.
+    // This relies on exception support not zeroing too much.
+    hwasan_read(&x[0], sizeof(x));
+#ifdef CLEARED_ACCESS_CATCH
+    return (int)(intermediate_pointer[1]);
+#else
+    return 0;
+#endif
+  }
+  __builtin_abort ();
+}
+
+int main() {
+  f();
+}
diff --git a/gcc/testsuite/g++.dg/hwasan/try-catch-1.cpp b/gcc/testsuite/g++.dg/hwasan/try-catch-1.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b124d29d33613794f569646b932b42c958b52348
--- /dev/null
+++ b/gcc/testsuite/g++.dg/hwasan/try-catch-1.cpp
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+
+/* This version should catch the invalid access.  */
+#define CLEARED_ACCESS_CATCH
+#include "try-catch-0.cpp"
+#undef CLEARED_ACCESS_CATCH
diff --git a/gcc/testsuite/gcc.dg/hwasan/aligned-alloc.c b/gcc/testsuite/gcc.dg/hwasan/aligned-alloc.c
new file mode 100644
index 0000000000000000000000000000000000000000..49b317cce7516845944e4b74ece94af0a8570076
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/aligned-alloc.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/*
+   TODO
+    This alignment isn't handled by the sanitizer interceptor alloc.
+    At the moment this program fails at runtime in the libhwasan library.
+
+    LLVM catches this problem at compile-time.
+ */
+
+int
+main ()
+{
+  void *p = __builtin_aligned_alloc (17, 100);
+  if ((unsigned long long)p & 0x10 == 0)
+    return 0;
+  __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/gcc.dg/hwasan/alloca-array-accessible.c b/gcc/testsuite/gcc.dg/hwasan/alloca-array-accessible.c
new file mode 100644
index 0000000000000000000000000000000000000000..c1d7c3bb0d1de7b8e6aa2ba7cf114604583e3ba3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/alloca-array-accessible.c
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+
+#define alloca __builtin_alloca
+
+int __attribute__ ((noinline))
+using_alloca (int num)
+{
+  int retval = 0;
+  int *big_array = alloca (num * sizeof (int));
+  for (int i = 0; i < num; ++i) {
+      retval += big_array[i];
+  }
+  return retval;
+}
+
+int __attribute__ ((noinline))
+using_vararray (int num)
+{
+  int retval = 0;
+  int big_array[num];
+  for (int i = 0; i < num; ++i) {
+      retval += big_array[i];
+  }
+  return retval;
+}
+
+int main()
+{
+  using_alloca (16);
+  using_vararray (12);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/hwasan/alloca-gets-different-tag.c b/gcc/testsuite/gcc.dg/hwasan/alloca-gets-different-tag.c
new file mode 100644
index 0000000000000000000000000000000000000000..d6dcb6fdf4ec535fef475d51de964bcc13337b67
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/alloca-gets-different-tag.c
@@ -0,0 +1,64 @@
+/* { dg-do run } */
+
+/* Alloca is given a different tag to other variables.
+   vararray should behave in the same way.  */
+
+#define alloca __builtin_alloca
+#define assert(x) if (!(x)) __builtin_abort ()
+
+struct two_values {
+    int left;
+    int right;
+};
+
+/* Require default hwasan tag ABI.
+   Know we're using AArch64 since that's the only architecture we run hwasan
+   tests on.  */
+char tag_of (void * x) { return ((unsigned long long)x) >> 56; }
+
+int __attribute__ ((noinline))
+alloca_different_tag (int num)
+{
+  struct two_values tmp_object = {
+      .left = 100,
+      .right = num,
+  };
+  int *big_array = alloca (num * sizeof (int));
+  int other_array[100];
+  
+  char first_tag = tag_of (&tmp_object);
+  char second_tag = tag_of (big_array);
+  char other_tag = tag_of (other_array);
+  assert (first_tag != second_tag);
+  assert (second_tag != other_tag);
+  assert (first_tag != other_tag);
+  return 0;
+}
+
+int __attribute__ ((noinline))
+vararray_different_tag (int num)
+{
+  struct two_values tmp_object = {
+      .left = 100,
+      .right = num,
+  };
+  int big_array[num];
+  int other_array[100];
+  
+  char first_tag = tag_of (&tmp_object);
+  char second_tag = tag_of (big_array);
+  char other_tag = tag_of (other_array);
+  assert (first_tag != second_tag);
+  assert (second_tag != other_tag);
+  assert (first_tag != other_tag);
+  return 0;
+}
+
+int __attribute__ ((noinline))
+main ()
+{
+  alloca_different_tag (10);
+  vararray_different_tag (8);
+  return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/hwasan/alloca-outside-caught.c b/gcc/testsuite/gcc.dg/hwasan/alloca-outside-caught.c
new file mode 100644
index 0000000000000000000000000000000000000000..c794d7900a80ffd42d46ee492b9d53795ba914b5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/alloca-outside-caught.c
@@ -0,0 +1,24 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+#define alloca __builtin_alloca
+
+int __attribute__ ((noinline))
+check_alloca (int num)
+{
+  volatile int *allocd_array = alloca (num * sizeof(int));
+  int other_array[10];
+  return allocd_array[12];
+}
+
+int __attribute__ ((noinline))
+main ()
+{
+  check_alloca (3);
+  return 1;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/bitfield-1.c b/gcc/testsuite/gcc.dg/hwasan/bitfield-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..4cabb1c8a1578bcfb230be40fb54551e010c85b7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/bitfield-1.c
@@ -0,0 +1,30 @@
+/* { dg-do run } */
+
+struct bitmapped_struct {
+    unsigned one : 1;
+    unsigned two : 1;
+    unsigned three : 1;
+    unsigned four : 1;
+    unsigned five : 1;
+    unsigned six : 1;
+    unsigned seven : 1;
+    unsigned eight : 1;
+};
+
+/* Check that hwasan allows valid bitfield accesses. */
+int __attribute__ ((noinline))
+handle_unaligned_access (struct bitmapped_struct *foo)
+{
+  if (foo->three)
+    return foo->four;
+
+  foo->five = 1;
+  return 1;
+}
+
+int main()
+{
+  struct bitmapped_struct myvar = {0};
+  handle_unaligned_access (&myvar);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/hwasan/bitfield-2.c b/gcc/testsuite/gcc.dg/hwasan/bitfield-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..4bd53bf8c3e4337d4dccd7f966908f50a9ace08b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/bitfield-2.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+struct A
+{
+  /* Ensure the offset from the start of this struct to the bitfield we access
+     is large enough to be in a different tag.  */
+  char base[16];
+  int : 4;
+  long x : 7;
+};
+
+int __attribute__ ((noinline, noclone))
+f (void *p) {
+  return ((struct A *)p)->x;
+}
+
+int
+main ()
+{
+  char a = 0;
+  return f (&a);
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 2 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/builtin-special-handling.c b/gcc/testsuite/gcc.dg/hwasan/builtin-special-handling.c
new file mode 100644
index 0000000000000000000000000000000000000000..9458e338f437b64298143f3c81459c1221bf6537
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/builtin-special-handling.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-fdump-tree-hwasan_O0 -fdump-tree-hwasan" } */
+
+typedef __SIZE_TYPE__ size_t;
+/* Functions to observe that HWASAN instruments memory builtins in the expected
+   manner.  */
+void * __attribute__((noinline))
+memset_builtin (void *dest, int value, size_t len)
+{
+  return __builtin_memset (dest, value, len);
+}
+
+/* HWASAN avoids strlen because it doesn't know the size of the memory access
+   until *after* the function call.  */
+size_t __attribute__ ((noinline))
+strlen_builtin (char *element)
+{
+  return __builtin_strlen (element);
+}
+
+/* First test ensures that the HWASAN_CHECK was emitted before the
+   builtin_memset.  Second test ensures there was only HWASAN_CHECK (which
+   demonstrates that builtin_strlen was not instrumented).  */
+/* { dg-final { scan-tree-dump-times "HWASAN_CHECK.*builtin_memset" 1 "hwasan1" } } */
+/* TODO It may be that the DejaGNU framework checks the *remaining* output
+   instead of the entire output in the second option.
+   Basically, check this test passes and it may work nicely.  */
+/* { dg-final { scan-tree-dump-times "HWASAN_CHECK" 1 "hwasan1" } } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/check-interface.c b/gcc/testsuite/gcc.dg/hwasan/check-interface.c
new file mode 100644
index 0000000000000000000000000000000000000000..dedb9174726ba031f1727c2617e7c9fc6dc4487d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/check-interface.c
@@ -0,0 +1,22 @@
+/* { dg-do run } */
+/*
+   Test taken from LLVM
+    compiler-rt/test/hwasan/TestCases/check-interface.cpp
+ */
+// Utilizes all flavors of __hwasan_load/store interface functions to verify
+// that the instrumentation and the interface provided by HWASan do match.
+// In case of a discrepancy, this test fails to link.
+
+#include <sanitizer/hwasan_interface.h>
+
+#define F(T) void f_##T(T *a, T *b) { *a = *b; }
+
+F(uint8_t)
+F(uint16_t)
+F(uint32_t)
+F(uint64_t)
+
+typedef unsigned V32 __attribute__((__vector_size__(32)));
+F(V32)
+
+int main() {}
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-poison-optimisation.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-poison-optimisation.c
new file mode 100644
index 0000000000000000000000000000000000000000..6ca716b78f430b726d6211aa4fb271f3a4116588
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-poison-optimisation.c
@@ -0,0 +1,35 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+/* { dg-additional-options "-fdump-tree-hwasan1 -save-temps" } */
+
+/* Here to check that the ASAN_POISON stuff works just fine.
+   This mechanism isn't very often used, but I should at least go through the
+   code-path once in my testfile.  */
+int
+main ()
+{
+  int *ptr = 0;
+
+  {
+    int a;
+    ptr = &a;
+    *ptr = 12345;
+  }
+
+  return *ptr;
+}
+
+/* TODO This is a pain around LTO.
+   I want to have a test that works for everything (including -flto functions),
+   but can't use any directive that works for both with and without -flto.  */
+/* { dg-final { scan-tree-dump-times "ASAN_POISON" 1 "hwasan1" }  } */
+/* TODO At the moment I simply abort, but in the future we should call some sort of
+   reporting function.
+   There is hence no output to look for right now, but will be in the future.
+*/
+/* { dg-final { scan-assembler-times "bl\\s*__hwasan_tag_mismatch4" 1 } } */
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-access-parent.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-access-parent.c
new file mode 100644
index 0000000000000000000000000000000000000000..107430a90245758637e14cca98be4a1cfe4faa13
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-access-parent.c
@@ -0,0 +1,44 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "-lpthread" } */
+
+#include <pthread.h>
+
+extern int printf (const char *, ...);
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+/* Test that tags are checked across different threads.
+   i.e. if this thread tries to access a different threads memory with the
+   incorrect tag, then this thread fails.  */
+void *
+failing_thread_function (void *argument)
+{
+    void * other = (void *)((uint64_t)argument & 0xffffffffffffffULL);
+    int *num = argument;
+    printf ("(should succeed): first number = %d\n", num[0]);
+    printf ("(now should fail):\n");
+
+    int *othernum = other;
+    printf (" second number = %d\n", othernum[0]);
+    return (void *)1;
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, failing_thread_function, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    return (uintptr_t)retval;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: 00/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T1.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-basic-failure.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-basic-failure.c
new file mode 100644
index 0000000000000000000000000000000000000000..baf4e57884ded6261052c41c94ea5c829372db28
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-basic-failure.c
@@ -0,0 +1,41 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "-lpthread" } */
+
+/* Ensure the failure mode for hwasan under pthreads looks sane.
+   (Looks sane means that the same error message is printed out rather than an
+   opaque message due to mishandling).  */
+
+#include <pthread.h>
+
+extern int printf (const char *, ...);
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+void *
+failing_from_stack (void * argument)
+{
+    int internal_array[16] = {0};
+    printf ("(now should fail):");
+    printf (" problem number is %d\n", internal_array[17]);
+    return (void *)1;
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, failing_from_stack, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    return (uintptr_t)retval;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T1.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T1.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-clears-stack.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-clears-stack.c
new file mode 100644
index 0000000000000000000000000000000000000000..9d07447fae1866bfdf271a6fa2e61a1e3185a48f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-clears-stack.c
@@ -0,0 +1,47 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "-lpthread" } */
+
+/* This checks the interceptor ABI pthread hooks.  */
+
+#include <pthread.h>
+
+extern int printf (const char *, ...);
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+__attribute__ ((noinline))
+void * Ident (void * argument)
+{
+	return argument;
+}
+
+void *
+pthread_stack_is_cleared (void *argument)
+{
+   (void)argument;
+   int internal_array[16] = {0};
+   return Ident((void*)internal_array);
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, pthread_stack_is_cleared, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    printf ("(should fail): ");
+    printf ("value left in stack is: %d\n", ((int *)retval)[0]);
+
+    return (uintptr_t)retval;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "HWAddressSanitizer can not describe address in more detail\..*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-success.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-success.c
new file mode 100644
index 0000000000000000000000000000000000000000..cf8f73495d0da3e734521a488da064f3fe287fcb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-success.c
@@ -0,0 +1,34 @@
+/* { dg-do run } */
+/* { dg-additional-options "-lpthread" } */
+
+/* Just ensure that a basic threaded program works while running with hwasan.
+   */
+
+#include <pthread.h>
+
+extern int printf (const char *, ...);
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+void *
+successful_thread_function (void * argument)
+{
+    int *deref = (int *)argument;
+    if (deref[0] == 100)
+      deref[1] = 10;
+    return (void *)0;
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, successful_thread_function, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    return (uintptr_t)retval;
+}
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan.exp b/gcc/testsuite/gcc.dg/hwasan/hwasan.exp
new file mode 100644
index 0000000000000000000000000000000000000000..f90fd4a8e3e900a36935f6869f011051c1f40882
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan.exp
@@ -0,0 +1,42 @@
+# Copyright (C) 2012-2019 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# TODO If target is not AArch64, then exit.
+# TODO If target does not have special kernel then exit.
+# TODO Probably best if I just check that compiling some basic file with
+# ~-fsanitize=hwaddress~ works and doesn't crash when using a syscall with a
+# tagged pointer.
+
+# Load support procs.
+load_lib gcc-dg.exp
+load_lib hwasan-dg.exp
+
+# Initialize `dg'.
+dg-init
+hwasan_init
+
+# Main loop.
+if [check_effective_target_fsanitize_hwaddress] {
+  gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c $srcdir/c-c++-common/hwasan/*.c]] "" ""
+}
+
+# All done.
+hwasan_finish
+dg-finish
diff --git a/gcc/testsuite/gcc.dg/hwasan/kernel-defaults.c b/gcc/testsuite/gcc.dg/hwasan/kernel-defaults.c
new file mode 100644
index 0000000000000000000000000000000000000000..be021464b8cea548467ac7ea53c67a30b6b79943
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/kernel-defaults.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-fno-sanitize=hwaddress -fsanitize=kernel-hwaddress" } */
+
+
+/* Defaults to check for kernel-hwaddress.
+   1) No stack tagging => no calls to __hwasan_tag_memory.
+   2) No block scope tagging (same again).
+   3) Use sanitize-recover by default (everything ends in noabort). */
+int __attribute__ ((noinline))
+accessing_pointers (int *left, int *right)
+{
+  int x = right[2];
+  left[3] = right[1];
+  return right[1] + left[2];
+}
+
+int __attribute__ ((noinline))
+using_stack (int num)
+{
+  int big_array[10];
+  int other_array[20];
+  accessing_pointers(other_array, big_array);
+  return big_array[num];
+}
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  using_stack (ARG);
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "__hwasan_tag_memory" } } */
+/* { dg-final { scan-assembler-not "__hwasan_(load|store)\\d(?!_noabort)" } } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c b/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..aba79e0acf36dd9fa20755d3cfcab29f3cb5625e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+
+/* Handling large aligned variables.
+   Large aligned variables take a different code-path through expand_stack_vars
+   in cfgexpand.c.  This testcase is just to exercise that code-path.
+
+   The alternate code-path produces a second base-pointer through some
+   instructions emitted in the prologue.
+   
+   Test cases are:
+   0) Valid access works without complaint.
+   1) Invalid access is caught.  */
+int __attribute__ ((noinline))
+handle_large_alignment (int num)
+{
+  int other_array[10];
+  int big_array[100] __attribute__ ((aligned (32)));
+  return big_array[num] + other_array[num];
+}
+
+#ifndef ARG
+#define ARG 1
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  handle_large_alignment (ARG);
+  return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/hwasan/large-aligned-1.c b/gcc/testsuite/gcc.dg/hwasan/large-aligned-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..abea810bee9d0418493cca72c80d61dfd61a03d7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/large-aligned-1.c
@@ -0,0 +1,13 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+#define ARG 12
+#include "large-aligned-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* NOTE: This assumes the current tagging mechanism (one at a time from the
+   base and large aligned variables being handled first).  */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/macro-definition.c b/gcc/testsuite/gcc.dg/hwasan/macro-definition.c
new file mode 100644
index 0000000000000000000000000000000000000000..5f654f557821f2dbe060e9976fbca7e5770f274c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/macro-definition.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+extern void testfunc(int);
+int foo()
+{
+#ifndef __SANITIZE_HWADDRESS__
+  testfunc(1);
+#endif
+  return 1;
+}
+
+/* { dg-final { scan-assembler-not "testfunc" } } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..1fce3ec5fd3b092969660f2ee1fa9ac3633747fc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c
@@ -0,0 +1,52 @@
+/* { dg-do run } */
+
+/*
+   Tests of nested funtions are:
+    0) Accessing closed over variables works.
+    1) Accesses outside of variables is caught.
+    2) Accessing variable out of scope is caught.
+
+    Here we test that accessing closed over variables works.
+ */
+
+/* We need a second layer of indirection so that GCC doesn't notice we're
+   returning the address of a local variable and put 0 in it's place.  */
+__attribute__((noinline))
+int *Ident(void *x) {
+  return x;
+}
+
+int __attribute__ ((noinline))
+intermediate (void (*f) (int, char),
+	      char num)
+{
+  if (num == 1)
+    /* NOTE: We need to overrun by an amount greater than the "extra data" in a
+       nonlocal goto structure.  The entire structure is allocated on the stack
+       with a single colour, which means we can't tell if a closed-over buffer
+       was overrun by an amount small enough that the access was still to some
+       data in that nonlocal goto structure.  */
+    f (100, 100);
+  else
+    f (3, 100);
+  /* Just return something ... */
+  return num % 3;
+}
+
+int* __attribute__ ((noinline))
+nested_function (char num)
+{
+  int big_array[16];
+  int other_array[16];
+  void store (int index, char value)
+    { big_array[index] = value; }
+  return Ident(&other_array[intermediate (store, num)]);
+}
+
+#ifndef MAIN
+int main ()
+{
+  nested_function (0);
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..7d5e80e8a9cb6a1e5a2fb87f78f140c37cd88841
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c
@@ -0,0 +1,26 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/*
+   Tests of nested funtions are:
+    0) Accessing closed over variables works.
+    1) Accesses outside of variables is caught.
+    2) Accessing variable out of scope is caught.
+
+    Here we test option 1.
+ */
+
+#define MAIN 0
+#include "nested-functions-0.c"
+#undef MAIN
+
+int main ()
+{
+  nested_function (1);
+  return 0;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "WRITE of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..be7942d4fb1bf0dec657396e0bd3ca2f43b78b36
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c
@@ -0,0 +1,27 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/*
+   Tests of nested funtions are:
+    0) Accessing closed over variables works.
+    1) Accesses outside of variables is caught.
+    2) Accessing variable out of scope is caught.
+
+    Here we test option 2.
+ */
+
+#define MAIN 0
+#include "nested-functions-0.c"
+#undef MAIN
+
+int main ()
+{
+  int *retval = nested_function (2);
+  *retval = 100;
+  return 0;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "WRITE of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/no-sanitize-attribute.c b/gcc/testsuite/gcc.dg/hwasan/no-sanitize-attribute.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5ff4aec06c8bc6c80fe512061ccc8908e321153
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/no-sanitize-attribute.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+
+__attribute__((no_sanitize_hwaddress)) int
+f1 (int *p, int *q)
+{
+  *p = 42;
+  return *q;
+}
+
+__attribute__((no_sanitize("hwaddress"))) int
+f2 (int *p, int *q)
+{
+  *p = 42;
+  return *q;
+}
+
+/* Only have one instance of __hwasan, it is __hwasan_init (the module
+ * constructor) there is no instrumentation in the functions.  */
+/* { dg-final { scan-assembler-times "__hwasan" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/random-frame-tag.c b/gcc/testsuite/gcc.dg/hwasan/random-frame-tag.c
new file mode 100644
index 0000000000000000000000000000000000000000..6dba71bf39558a945d10007b5437651fb7871626
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/random-frame-tag.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-options "--param hwasan-random-frame-tag=1" } */
+
+#include "stack-tagging-basic-0.c"
+
+/* Random frame tag => call to __hwasan_generate_tag.  */
+/* { dg-final { scan-assembler "__hwasan_generate_tag" } } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-0.c b/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..3c7e9c5b3fec8677440b81a2e029427957b359ba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-0.c
@@ -0,0 +1,53 @@
+/* { dg-do run } */
+
+#include <setjmp.h>
+#include <stdio.h>
+
+/*
+   Testing longjmp/setjmp should test.
+
+   0) Nothing special happens with the jmp_buf.
+   1) Accesses to scopes jmp'd over are caught.
+ */
+int __attribute__ ((noinline))
+uses_longjmp (int **other_array, int num, jmp_buf env)
+{
+  int internal_array[100] = {0};
+  *other_array = &internal_array[0];
+  if (num % 2)
+    longjmp (env, num);
+  else
+    return num % 8;
+}
+
+int __attribute__ ((noinline))
+uses_setjmp (int num)
+{ 
+  int big_array[100];
+  int *other_array = NULL;
+  sigjmp_buf cur_env;
+  int temp = 0;
+  if ((temp = sigsetjmp (cur_env, 1)) != 0)
+    { 
+      if (other_array != NULL)
+        printf ("Value pointed to in other_array[0]: %d\n",
+                other_array[0]);
+  
+      printf ("Longjmp returned %d.\n", temp);
+      return 10;
+    }
+  else
+    {
+      return uses_longjmp (&other_array, num, cur_env);
+    } 
+} 
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  uses_setjmp (ARG);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-1.c b/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..a32b5063e556f0e45e83d657aa312fb4de860442
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-1.c
@@ -0,0 +1,18 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/*
+   Testing longjmp/setjmp should test.
+
+   0) Nothing special happens with the jmp_buf.
+   1) Accesses to scopes jmp'd over are caught.
+ */
+
+#define ARG 1
+#include "setjmp-longjmp-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..050a3eda7aa421b6b955aedd900f6fbb24a74a61
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c
@@ -0,0 +1,33 @@
+/* { dg-do run } */
+
+/* Basic tests for stack tagging.
+
+   0) Valid accesses work.
+   1) Accesses outside of a variable crash.
+*/
+int __attribute__ ((noinline))
+accessing_pointers (int *left, int *right)
+{
+  int x = right[2];
+  left[3] = right[1];
+  return right[1] + left[2];
+}
+
+int __attribute__ ((noinline))
+using_stack (int num)
+{
+  int big_array[10];
+  int other_array[20];
+  accessing_pointers(other_array, big_array);
+  return big_array[num];
+}
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  using_stack (ARG);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-1.c b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..0db6bc4bd220e99bcd695ee93fcc9f4118d09457
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-1.c
@@ -0,0 +1,17 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/* Basic tests for stack tagging.
+
+   0) Accesses outside of a variable crash.
+   1) Valid accesses work.
+*/
+
+#define ARG 17
+#include "stack-tagging-basic-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/stack-tagging-disable.c b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-disable.c
new file mode 100644
index 0000000000000000000000000000000000000000..30f45062d070c482be34f9c824c57f4bd9cc3e10
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-disable.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "--param hwasan-stack=0" } */
+
+
+/* No stack tagging => no calls to __hwasan_tag_memory.  */
+int __attribute__ ((noinline))
+accessing_pointers (int *left, int *right)
+{
+  int x = right[2];
+  left[3] = right[1];
+  return right[1] + left[2];
+}
+
+int __attribute__ ((noinline))
+using_stack (int num)
+{
+  int big_array[10];
+  int other_array[20];
+  accessing_pointers(other_array, big_array);
+  return big_array[num];
+}
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  using_stack (ARG);
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "__hwasan_tag_memory" } } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/vararray-outside-caught.c b/gcc/testsuite/gcc.dg/hwasan/vararray-outside-caught.c
new file mode 100644
index 0000000000000000000000000000000000000000..858bfcd7e596a90c6bd1d68667259a735bb4dce7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/vararray-outside-caught.c
@@ -0,0 +1,21 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+int __attribute__ ((noinline))
+check_vararray (int num)
+{
+  int var_array[num];
+  int other_array[10];
+  return var_array[12];
+}
+
+int __attribute__ ((noinline))
+main ()
+{
+  return check_vararray (3);
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/very-large-objects.c b/gcc/testsuite/gcc.dg/hwasan/very-large-objects.c
new file mode 100644
index 0000000000000000000000000000000000000000..55265353369540872e8fba4da99d9be92a7ad99b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/very-large-objects.c
@@ -0,0 +1,68 @@
+/* { dg-do compile } */
+
+/* Ensure the sanitizer can handle very large offsets (i.e. that the hooks
+   handle offsets too large for the relevant instructions).
+   Just want to make sure this compiles without an ICE.  */
+#ifndef ASIZE
+# define ASIZE 0x10000000000UL
+#endif
+
+typedef __UINT64_TYPE__ uint64_t;
+
+#if __LONG_MAX__ < 8 * ASIZE
+# undef ASIZE
+# define ASIZE 4096
+#endif
+
+extern void abort (void);
+
+int __attribute__((noinline))
+foo (const char *s)
+{
+  if (!s)
+    return 1;
+  if (s[0] != 'a')
+    abort ();
+  s += ASIZE - 1;
+  if (s[0] != 'b')
+    abort ();
+  return 0;
+}
+
+int (*fn) (const char *) = foo;
+
+int __attribute__((noinline))
+bar (void)
+{
+  char s[ASIZE];
+  s[0] = 'a';
+  s[ASIZE - 1] = 'b';
+  foo (s);
+  foo (s);
+  return 0;
+}
+
+int __attribute__((noinline))
+baz (long i)
+{
+  if (i)
+    return fn (0);
+  else
+    {
+      char s[ASIZE];
+      s[0] = 'a';
+      s[ASIZE - 1] = 'b';
+      foo (s);
+      foo (s);
+      return fn (0);
+    }
+}
+
+int __attribute__((noinline))
+very_large_offset (int *p)
+{
+  char init_array[(uint64_t)0xfefefef];
+  char other_array[(uint64_t)0xfefefef];
+  return (int)init_array[p[1]] + (int)other_array[p[0]];
+}
+
diff --git a/gcc/testsuite/lib/hwasan-dg.exp b/gcc/testsuite/lib/hwasan-dg.exp
new file mode 100644
index 0000000000000000000000000000000000000000..13cb94b02943f3186cd18f5913bfbd24089af75a
--- /dev/null
+++ b/gcc/testsuite/lib/hwasan-dg.exp
@@ -0,0 +1,352 @@
+# Copyright (C) 2012-2019 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Return 1 if compilation with -fsanitize=hwaddress is error-free for trivial
+# code, 0 otherwise.
+
+proc check_effective_target_fsanitize_hwaddress {} {
+    if ![check_no_compiler_messages fsanitize_hwaddress executable {
+	int main (void) { return 0; }
+    }] {
+	return 0;
+    }
+
+    # hwasan doesn't work if there's a ulimit on virtual memory.
+    if ![is_remote target] {
+	if [catch {exec sh -c "ulimit -v"} ulimit_v] {
+	    # failed to get ulimit
+	} elseif [regexp {^[0-9]+$} $ulimit_v] {
+	    # ulimit -v gave a numeric limit
+	    warning "skipping hwasan tests due to ulimit -v"
+	    return 0;
+	}
+    }
+
+    return 1;
+}
+
+proc hwasan_include_flags {} {
+    global srcdir
+    global TESTING_IN_BUILD_TREE
+
+    set flags ""
+
+    if { [is_remote host] || ! [info exists TESTING_IN_BUILD_TREE] } {
+      return "${flags}"
+    }
+
+    set flags "-I$srcdir/../../libsanitizer/include"
+
+    return "$flags"
+}
+
+#
+# hwasan_link_flags -- compute library path and flags to find libhwasan.
+# (originally from g++.exp)
+#
+
+proc hwasan_link_flags { paths } {
+    global srcdir
+    global ld_library_path
+    global shlib_ext
+    global hwasan_saved_library_path
+
+    set gccpath ${paths}
+    set flags ""
+
+    set shlib_ext [get_shlib_extension]
+    set hwasan_saved_library_path $ld_library_path
+
+    if { $gccpath != "" } {
+      if { [file exists "${gccpath}/libsanitizer/hwasan/.libs/libhwasan.a"]
+	   || [file exists "${gccpath}/libsanitizer/hwasan/.libs/libhwasan.${shlib_ext}"] } {
+	  append flags " -B${gccpath}/libsanitizer/ "
+	  append flags " -B${gccpath}/libsanitizer/hwasan/ "
+	  append flags " -L${gccpath}/libsanitizer/hwasan/.libs "
+	  append ld_library_path ":${gccpath}/libsanitizer/hwasan/.libs"
+      }
+    } else {
+      global tool_root_dir
+
+      set libhwasan [lookfor_file ${tool_root_dir} libhwasan]
+      if { $libhwasan != "" } {
+	  append flags "-L${libhwasan} "
+	  append ld_library_path ":${libhwasan}"
+      }
+    }
+
+    set_ld_library_path_env_vars
+
+    return "$flags"
+}
+
+#
+# hwasan_init -- called at the start of each subdir of tests
+#
+
+proc hwasan_init { args } {
+    global TEST_ALWAYS_FLAGS
+    global ALWAYS_CXXFLAGS
+    global TOOL_OPTIONS
+    global hwasan_saved_TEST_ALWAYS_FLAGS
+    global hwasan_saved_ALWAYS_CXXFLAGS
+
+    set link_flags ""
+    if ![is_remote host] {
+	if [info exists TOOL_OPTIONS] {
+	    set link_flags "[hwasan_link_flags [get_multilibs ${TOOL_OPTIONS}]]"
+	} else {
+	    set link_flags "[hwasan_link_flags [get_multilibs]]"
+	}
+    }
+
+    set include_flags "[hwasan_include_flags]"
+
+    if [info exists TEST_ALWAYS_FLAGS] {
+	set hwasan_saved_TEST_ALWAYS_FLAGS $TEST_ALWAYS_FLAGS
+    }
+    if [info exists ALWAYS_CXXFLAGS] {
+	set hwasan_saved_ALWAYS_CXXFLAGS $ALWAYS_CXXFLAGS
+	set ALWAYS_CXXFLAGS [concat "{ldflags=$link_flags}" $ALWAYS_CXXFLAGS]
+	set ALWAYS_CXXFLAGS [concat "{additional_flags=-fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags}" $ALWAYS_CXXFLAGS]
+    } else {
+	if [info exists TEST_ALWAYS_FLAGS] {
+	    set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags $TEST_ALWAYS_FLAGS"
+	} else {
+	    set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags"
+	}
+    }
+}
+
+#
+# hwasan_finish -- called at the start of each subdir of tests
+#
+
+proc hwasan_finish { args } {
+    global TEST_ALWAYS_FLAGS
+    global hwasan_saved_TEST_ALWAYS_FLAGS
+    global hwasan_saved_ALWAYS_CXXFLAGS
+    global hwasan_saved_library_path
+    global ld_library_path
+
+    if [info exists hwasan_saved_ALWAYS_CXXFLAGS ] {
+	set ALWAYS_CXXFLAGS $hwasan_saved_ALWAYS_CXXFLAGS
+    } else {
+	if [info exists hwasan_saved_TEST_ALWAYS_FLAGS] {
+	    set TEST_ALWAYS_FLAGS $hwasan_saved_TEST_ALWAYS_FLAGS
+	} else {
+	    unset TEST_ALWAYS_FLAGS
+	}
+    }
+    set ld_library_path $hwasan_saved_library_path
+    set_ld_library_path_env_vars
+    clear_effective_target_cache
+}
+
+# Symbolize lines like
+#   #2 0xdeadbeef (/some/path/libsanitizer.so.0.0.0+0xbeef)
+# in $output using addr2line to
+#   #2 0xdeadbeef in foobar file:123
+proc hwasan_symbolize { output } {
+    set addresses [regexp -inline -all -line "^ *#\[0-9\]+ 0x\[0-9a-f\]+ \[(\](\[^)\]+)\[+\](0x\[0-9a-f\]+)\[)\]$" "$output"]
+    if { [llength $addresses] > 0 } {
+	set addr2line_name [find_binutils_prog addr2line]
+	set idx 1
+	while { $idx < [llength $addresses] } {
+	    set key [regsub -all "\[\]\[\]" [lindex $addresses $idx] "\\\\&"]
+	    set val [lindex $addresses [expr $idx + 1]]
+	    lappend arr($key) $val
+	    set idx [expr $idx + 3]
+	}
+	foreach key [array names arr] {
+	    set args "-f -e $key $arr($key)"
+	    set status [remote_exec host "$addr2line_name" "$args"]
+	    if { [lindex $status 0] > 0 } continue
+	    regsub -all "\r\n" [lindex $status 1] "\n" addr2line_output
+	    regsub -all "\[\n\r\]BFD: \[^\n\r\]*" $addr2line_output "" addr2line_output
+	    regsub -all "^BFD: \[^\n\r\]*\[\n\r\]" $addr2line_output "" addr2line_output
+	    set addr2line_output [regexp -inline -all -line "^\[^\n\r]*" $addr2line_output]
+	    set idx 0
+	    foreach val $arr($key) {
+		if { [expr $idx + 1] < [llength $addr2line_output] } {
+		    set fnname [lindex $addr2line_output $idx]
+		    set fileline [lindex $addr2line_output [expr $idx + 1]]
+		    if { "$fnname" != "??" } {
+			set newkey "$key+$val"
+			set repl($newkey) "$fnname $fileline"
+		    }
+		    set idx [expr $idx + 2]
+		}
+	    }
+	}
+	set idx 0
+	set new_output ""
+	while {[regexp -start $idx -indices " #\[0-9\]+ 0x\[0-9a-f\]+ \[(\](\[^)\]+\[+\]0x\[0-9a-f\]+)\[)\]" "$output" -> addr] > 0} {
+	    set low [lindex $addr 0]
+	    set high [lindex $addr 1]
+	    set val [string range "$output" $low $high]
+	    append new_output [string range "$output" $idx [expr $low - 2]]
+	    if [info exists repl($val)] {
+		append new_output "in $repl($val)"
+	    } else {
+		append new_output "($val)"
+	    }
+	    set idx [expr $high + 2]
+	}
+	append new_output [string range "$output" $idx [string length "$output"]]
+	return "$new_output"
+    }
+    return "$output"
+}
+
+# Return a list of gtest tests, printed in the form
+# DEJAGNU_GTEST_TEST AddressSanitizer_SimpleDeathTest
+# DEJAGNU_GTEST_TEST AddressSanitizer_VariousMallocsTest
+proc hwasan_get_gtest_test_list { output } {
+    set idx 0
+    set ret ""
+    while {[regexp -start $idx -indices "DEJAGNU_GTEST_TEST (\[^\n\r\]*)(\r\n|\n|\r)" "$output" -> testname] > 0} {
+	set low [lindex $testname 0]
+	set high [lindex $testname 1]
+	set val [string range "$output" $low $high]
+	lappend ret $val
+	set idx [expr $high + 1]
+    }
+    return $ret
+}
+
+# Return a list of gtest EXPECT_DEATH tests, printed in the form
+# DEJAGNU_GTEST_EXPECT_DEATH1 statement DEJAGNU_GTEST_EXPECT_DEATH1 regexp DEJAGNU_GTEST_EXPECT_DEATH1
+# DEJAGNU_GTEST_EXPECT_DEATH2 other statement DEJAGNU_GTEST_EXPECT_DEATH2 other regexp DEJAGNU_GTEST_EXPECT_DEATH2
+proc hwasan_get_gtest_expect_death_list { output } {
+    set idx 0
+    set ret ""
+    while {[regexp -start $idx -indices "DEJAGNU_GTEST_EXPECT_DEATH(\[0-9\]*)" "$output" -> id ] > 0} {
+	set low [lindex $id 0]
+	set high [lindex $id 1]
+	set val_id [string range "$output" $low $high]
+	if {[regexp -start $low -indices "$val_id (.*) DEJAGNU_GTEST_EXPECT_DEATH$val_id (.*) DEJAGNU_GTEST_EXPECT_DEATH$val_id\[\n\r\]" "$output" whole statement regexpr ] == 0} { break }
+	set low [lindex $statement 0]
+	set high [lindex $statement 1]
+	set val_statement [string range "$output" $low $high]
+	set low [lindex $regexpr 0]
+	set high [lindex $regexpr 1]
+	set val_regexpr [string range "$output" $low $high]
+	lappend ret [list "$val_id" "$val_statement" "$val_regexpr"]
+	set idx [lindex $whole 1]
+    }
+    return $ret
+}
+
+# Replace ${tool}_load with a wrapper so that we can symbolize the output.
+if { [info procs ${tool}_load] != [list] \
+      && [info procs saved_hwasan_${tool}_load] == [list] } {
+    rename ${tool}_load saved_hwasan_${tool}_load
+
+    proc ${tool}_load { program args } {
+	global tool
+	global hwasan_last_gtest_test_list
+	global hwasan_last_gtest_expect_death_list
+	set result [eval [list saved_hwasan_${tool}_load $program] $args]
+	set output [lindex $result 1]
+	set symbolized_output [hwasan_symbolize "$output"]
+	set hwasan_last_gtest_test_list [hwasan_get_gtest_test_list "$output"]
+	set hwasan_last_gtest_expect_death_list [hwasan_get_gtest_expect_death_list "$output"]
+	set result [list [lindex $result 0] $symbolized_output]
+	return $result
+    }
+}
+
+# Utility for running gtest hwasan emulation under dejagnu, invoked via dg-final.
+# Call pass if variable has the desired value, otherwise fail.
+#
+# Argument 0 handles expected failures and the like
+proc hwasan-gtest { args } {
+    global tool
+    global hwasan_last_gtest_test_list
+    global hwasan_last_gtest_expect_death_list
+
+    if { ![info exists hwasan_last_gtest_test_list] } { return }
+    if { [llength $hwasan_last_gtest_test_list] == 0 } { return }
+    if { ![isnative] || [is_remote target] } { return }
+
+    set gtest_test_list $hwasan_last_gtest_test_list
+    unset hwasan_last_gtest_test_list
+
+    if { [llength $args] >= 1 } {
+	switch [dg-process-target [lindex $args 0]] {
+	    "S" { }
+	    "N" { return }
+	    "F" { setup_xfail "*-*-*" }
+	    "P" { }
+	}
+    }
+
+    # This assumes that we are three frames down from dg-test, and that
+    # it still stores the filename of the testcase in a local variable "name".
+    # A cleaner solution would require a new DejaGnu release.
+    upvar 2 name testcase
+    upvar 2 prog prog
+
+    set output_file "[file rootname [file tail $prog]].exe"
+
+    foreach gtest $gtest_test_list {
+	set testname "$testcase $gtest"
+	set status -1
+
+	setenv DEJAGNU_GTEST_ARG "$gtest"
+	set result [${tool}_load ./$output_file $gtest]
+	unsetenv DEJAGNU_GTEST_ARG
+	set status [lindex $result 0]
+	set output [lindex $result 1]
+	if { "$status" == "pass" } {
+	    pass "$testname execution test"
+	    if { [info exists hwasan_last_gtest_expect_death_list] } {
+		set gtest_expect_death_list $hwasan_last_gtest_expect_death_list
+		foreach gtest_death $gtest_expect_death_list {
+		    set id [lindex $gtest_death 0]
+		    set testname "$testcase $gtest [lindex $gtest_death 1]"
+		    set regexpr [lindex $gtest_death 2]
+		    set status -1
+
+		    setenv DEJAGNU_GTEST_ARG "$gtest:$id"
+		    set result [${tool}_load ./$output_file "$gtest:$id"]
+		    unsetenv DEJAGNU_GTEST_ARG
+		    set status [lindex $result 0]
+		    set output [lindex $result 1]
+		    if { "$status" == "fail" } {
+			pass "$testname execution test"
+			if { ![regexp $regexpr ${output}] } {
+			    fail "$testname output pattern test"
+			    send_log "Output should match: $regexpr\n"
+			} else {
+			    pass "$testname output pattern test"
+			}
+		    } elseif { "$status" == "pass" } {
+			fail "$testname execution test"
+		    } else {
+			$status "$testname execution test"
+		    }
+		}
+	    }
+	} else {
+	    $status "$testname execution test"
+	}
+	unset hwasan_last_gtest_expect_death_list
+    }
+
+    return
+}


[-- Attachment #2: hwasan-patch16.patch.gz --]
[-- Type: application/gzip, Size: 12244 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 13/X] [libsanitizer][options] Add hwasan flags and argument parsing
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (16 preceding siblings ...)
  2019-11-05 11:35   ` [PATCH 18/X] [libsanitizer] Add in MTE stubs Matthew Malcomson
@ 2019-11-05 11:35   ` Matthew Malcomson
  2019-11-05 13:11     ` Andrey Konovalov via gcc-patches
  2019-11-05 18:32     ` Joseph Myers
  2019-11-05 11:35   ` [PATCH 15/X] [libsanitizer] Add hwasan pass and associated gimple changes Matthew Malcomson
                     ` (11 subsequent siblings)
  29 siblings, 2 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:35 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub, kasan-dev, Andrey Konovalov, Dmitry Vyukov

[-- Attachment #1: Type: text/plain, Size: 21740 bytes --]

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.  */


[-- Attachment #2: hwasan-patch13.patch --]
[-- Type: text/plain, Size: 17711 bytes --]

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.  */


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [aarch64] Allocate enough space for err_str in aarch64_handle_attr_branch_protection
  2019-11-05 11:34   ` [aarch64] Allocate enough space for err_str in aarch64_handle_attr_branch_protection Matthew Malcomson
@ 2019-11-05 11:38     ` Kyrylo Tkachov
  0 siblings, 0 replies; 150+ messages in thread
From: Kyrylo Tkachov @ 2019-11-05 11:38 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: rguenther, James Greenhalgh, Martin Liska, Richard Earnshaw,
	Marcus Shawcroft

Hi Matthew,

On 11/5/19 11:33 AM, Matthew Malcomson wrote:
> -fsanitize=hwaddress found a one-byte overwrite when running the
> testsuite here.  aarch64_handle_attr_branch_protection allocates
> `strlen(str)` bytes for an error string, which is populated by
> `strcpy(..., str)` in the case where the branch protection string is
> completely invalid.
>
> Tested on aarch64 with hwasan (though not a full bootstrap since it's
> obvious).
>
Nice to see hwasan catching these things!

Ok.

Thanks,

Kyrill



> gcc/ChangeLog:
>
> 2019-11-05  Matthew Malcomson <matthew.malcomson@arm.com>
>
>         * config/aarch64/aarch64.c (aarch64_handle_attr_cpu): Allocate
>         enough bytes for the NULL character.
>
>
>
> ###############     Attachment also inlined for ease of reply    
> ###############
>
>
> diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
> index 
> 232317d4a5a4a16529f573eef5a8d7a068068207..fc03faa8f8d459a84024d4394fff375b72d31264 
> 100644
> --- a/gcc/config/aarch64/aarch64.c
> +++ b/gcc/config/aarch64/aarch64.c
> @@ -13298,7 +13298,7 @@ aarch64_handle_attr_cpu (const char *str)
>   static bool
>   aarch64_handle_attr_branch_protection (const char* str)
>   {
> -  char *err_str = (char *) xmalloc (strlen (str));
> +  char *err_str = (char *) xmalloc (strlen (str) + 1);
>    enum aarch64_parse_opt_result res = aarch64_parse_branch_protection 
> (str,
> &err_str);
>    bool success = false;
>

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 1/X][mid-end] Fix declared type of personality functions
  2019-11-05 11:34   ` [PATCH 1/X][mid-end] Fix declared type of personality functions Matthew Malcomson
@ 2019-11-05 11:41     ` Richard Biener
  0 siblings, 0 replies; 150+ messages in thread
From: Richard Biener @ 2019-11-05 11:41 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, nd, law, Martin Liska, Richard Earnshaw,
	Kyrylo Tkachov, ian

[-- Attachment #1: Type: text/plain, Size: 1869 bytes --]

On Tue, 5 Nov 2019, Matthew Malcomson wrote:

> `build_personality_function` generates a declaration for a personality
> function.  The type it declares for these functions doesn't match the
> type of the actual personality functions that are defined by the C++
> unwinding ABI.
> 
> This doesn't cause any crashes since the compiler never generates a call
> to these decl's, and hence the type of the function is never used.
> Nonetheless, for the sake of consistency and readability we update the
> type of this declaration.
> 
> (See libstdc++-v3/libsupc++/unwind-cxx.h for declaration of _gxx_personality_v0
> to compare types).

OK.  I believe _Unwind_Personality_Fn in libgcc/unwind-generic.h is the
correct reference.

Thanks,
Richard.

> gcc/ChangeLog:
> 
> 2019-11-05  Matthew Malcomson  <matthew.malcomson@arm.com>
> 
> 	* expr.c (build_personality_function): Fix generated type to
> 	match actual personality functions.
> 
> 
> 
> ###############     Attachment also inlined for ease of reply    ###############
> 
> 
> diff --git a/gcc/expr.c b/gcc/expr.c
> index 2f2b53f8b6905013b4214eea137d67c666b0c795..7dc37a288ebffb99c990442cf339b848c5fa9d2e 100644
> --- a/gcc/expr.c
> +++ b/gcc/expr.c
> @@ -12525,7 +12525,8 @@ build_personality_function (const char *lang)
>  
>    name = ACONCAT (("__", lang, "_personality", unwind_and_version, NULL));
>  
> -  type = build_function_type_list (integer_type_node, integer_type_node,
> +  type = build_function_type_list (unsigned_type_node,
> +				   integer_type_node, integer_type_node,
>  				   long_long_unsigned_type_node,
>  				   ptr_type_node, ptr_type_node, NULL_TREE);
>    decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL,
> 
> 

-- 
Richard Biener <rguenther@suse.de>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Felix Imendörffer; HRB 36809 (AG Nuernberg)

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (18 preceding siblings ...)
  2019-11-05 11:35   ` [PATCH 15/X] [libsanitizer] Add hwasan pass and associated gimple changes Matthew Malcomson
@ 2019-11-05 11:50   ` Matthew Malcomson
  2019-11-05 15:11   ` Martin Liška
                     ` (9 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 11:50 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov, Martin Liska,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 05/11/2019 11:32, Matthew Malcomson wrote:
> 
> Testing done:
> Full bootstrap and regtest on x86_64 (no difference -- hwasan not used).
> 
> Full bootstrap and regtest on AArch64 sanitizing with hwasan and running
> on recent kernel.
> Regressions all accounted for:
>    1) tests under plugin/
>       testism where hwasan library is not linked in.
>       (same appears to happen for asan)
>    2) branch-protection-attr.c
>       New bug found by hwasan, fix in this patch series.
>    3) pr88597.c
>       timeout, can run manually and everything works (but is very slow)
>    4) aarch64/long_branch_1.c
>       timeout, as above.
>    5) gfortran/class_61.f90
>       bug already caught by ASAN and reported upstream
>       https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89661
>    6) gfortran/dec_type_print_2.f03
>       bug already caught by ASAN and reported upstream
>       https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86657
>    7) gfortran/minlocval_3.f90
>       timeout, can run manually and passes (but is very slow)
> 

Oh, and I also tested by compiling the kernel for software memory 
tagging (after a quick hack to use __SANITIZE_HWADDRESS__ and pass the 
correct flags for gcc) then using the kernel sanitizer testing module 
test_kasan.

The kernel was tested without stack-tagging, and we caught the tests 
which didn't access within a valid 16 byte granule (i.e. caught those 
that were expected).

I've been running this sanitized kernel on my test machine without 
problems for a week and a bit.

> 
> Entire patch series attached to cover letter.
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 13/X] [libsanitizer][options] Add hwasan flags and argument parsing
  2019-11-05 11:35   ` [PATCH 13/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
@ 2019-11-05 13:11     ` Andrey Konovalov via gcc-patches
  2019-11-07 12:48       ` Matthew Malcomson
  2019-11-05 18:32     ` Joseph Myers
  1 sibling, 1 reply; 150+ messages in thread
From: Andrey Konovalov via gcc-patches @ 2019-11-05 13:11 UTC (permalink / raw)
  To: Matthew Malcomson, kcc, dvyukov, Evgenii Stepanov
  Cc: gcc-patches, nd, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub, kasan-dev

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.  */
>

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (19 preceding siblings ...)
  2019-11-05 11:50   ` [PATCH 0/X] Introduce HWASAN sanitizer to GCC Matthew Malcomson
@ 2019-11-05 15:11   ` Martin Liška
  2019-11-05 16:11     ` Matthew Malcomson
  2019-11-05 18:30   ` Joseph Myers
                     ` (8 subsequent siblings)
  29 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2019-11-05 15:11 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 11/5/19 12:32 PM, Matthew Malcomson wrote:
> Hello,
> 
> This patch series adds the LLVM hardware address sanitizer (HWASAN) to
> GCC.  The document describing HWASAN can be found here
> http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html.

Hello.

Thank you for the patch. I've just updated libsanitizer in GCC and I also
prepared a patch which you have included in your series (but using an
older libsanitizer):
https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00270.html.

Can you please rebase the series on top of the patch and exclude all the cherry picks from
upstream? Plus you can now install
"[aarch64] Allocate enough space for err_str in aarch64_handle_attr_branch_protection"
patch and exclude it from the series as well.

Thanks,
Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-05 15:11   ` Martin Liška
@ 2019-11-05 16:11     ` Matthew Malcomson
  2019-11-05 17:22       ` Martin Liška
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 16:11 UTC (permalink / raw)
  To: Martin Liška, gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 05/11/2019 15:10, Martin Liška wrote:
> On 11/5/19 12:32 PM, Matthew Malcomson wrote:
>> Hello,
>>
>> This patch series adds the LLVM hardware address sanitizer (HWASAN) to
>> GCC.  The document describing HWASAN can be found here
>> http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html.
> 
> Hello.
> 
> Thank you for the patch. I've just updated libsanitizer in GCC and I also
> prepared a patch which you have included in your series (but using an
> older libsanitizer):
> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00270.html.
> 
> Can you please rebase the series on top of the patch and exclude all the 
> cherry picks from
> upstream? Plus you can now install
> "[aarch64] Allocate enough space for err_str in 
> aarch64_handle_attr_branch_protection"
> patch and exclude it from the series as well.
Will do!

I'm currently rebasing, but thought I should mention just in case you 
didn't know that LLVM have fully moved over to git recently.

There seem to be no commits on the LLVM svn repo since 2019-10-22, so 
there are some cherry-picked hwasan patches that are not in at the point 
you merged.

Is this known, and we're taking the last SVN commit?

Cheers,
Matthew

> 
> Thanks,
> Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-05 16:11     ` Matthew Malcomson
@ 2019-11-05 17:22       ` Martin Liška
  2019-11-05 17:35         ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2019-11-05 17:22 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 11/5/19 5:11 PM, Matthew Malcomson wrote:
> On 05/11/2019 15:10, Martin Liška wrote:
>> On 11/5/19 12:32 PM, Matthew Malcomson wrote:
>>> Hello,
>>>
>>> This patch series adds the LLVM hardware address sanitizer (HWASAN) to
>>> GCC.  The document describing HWASAN can be found here
>>> http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html.
>>
>> Hello.
>>
>> Thank you for the patch. I've just updated libsanitizer in GCC and I also
>> prepared a patch which you have included in your series (but using an
>> older libsanitizer):
>> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00270.html.
>>
>> Can you please rebase the series on top of the patch and exclude all the
>> cherry picks from
>> upstream? Plus you can now install
>> "[aarch64] Allocate enough space for err_str in
>> aarch64_handle_attr_branch_protection"
>> patch and exclude it from the series as well.
> Will do!
> 
> I'm currently rebasing, but thought I should mention just in case you
> didn't know that LLVM have fully moved over to git recently.

Hello.

You are right, I haven't noticed that.

> 
> There seem to be no commits on the LLVM svn repo since 2019-10-22, so
> there are some cherry-picked hwasan patches that are not in at the point
> you merged.

Do you miss any revisions that were installed in the new git repository
and are not in current GCC master? If so, I can update the merge.sh script
and do one more merge from trunk.

Martin

> 
> Is this known, and we're taking the last SVN commit?
> 
> Cheers,
> Matthew
> 
>>
>> Thanks,
>> Martin
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-05 17:22       ` Martin Liška
@ 2019-11-05 17:35         ` Matthew Malcomson
  2019-11-05 18:15           ` Martin Liška
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-05 17:35 UTC (permalink / raw)
  To: Martin Liška, gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 05/11/2019 17:22, Martin Liška wrote:
> On 11/5/19 5:11 PM, Matthew Malcomson wrote:
>> On 05/11/2019 15:10, Martin Liška wrote:
>>> On 11/5/19 12:32 PM, Matthew Malcomson wrote:
>>>> Hello,
>>>>
>>>> This patch series adds the LLVM hardware address sanitizer (HWASAN) to
>>>> GCC.  The document describing HWASAN can be found here
>>>> http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html.
>>>
>>> Hello.
>>>
>>> Thank you for the patch. I've just updated libsanitizer in GCC and I 
>>> also
>>> prepared a patch which you have included in your series (but using an
>>> older libsanitizer):
>>> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00270.html.
>>>
>>> Can you please rebase the series on top of the patch and exclude all the
>>> cherry picks from
>>> upstream? Plus you can now install
>>> "[aarch64] Allocate enough space for err_str in
>>> aarch64_handle_attr_branch_protection"
>>> patch and exclude it from the series as well.
>> Will do!
>>
>> I'm currently rebasing, but thought I should mention just in case you
>> didn't know that LLVM have fully moved over to git recently.
> 
> Hello.
> 
> You are right, I haven't noticed that.
> 
>>
>> There seem to be no commits on the LLVM svn repo since 2019-10-22, so
>> there are some cherry-picked hwasan patches that are not in at the point
>> you merged.
> 
> Do you miss any revisions that were installed in the new git repository
> and are not in current GCC master? If so, I can update the merge.sh script
> and do one more merge from trunk.
> 
> Martin

Yes, there are three commits in the new git repository and not in GCC 
master that I need.  The most recent commit I need is 91167e2.

Thanks,
Matthew.

> 
>>
>> Is this known, and we're taking the last SVN commit?
>>
>> Cheers,
>> Matthew
>>
>>>
>>> Thanks,
>>> Martin
>>
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-05 17:35         ` Matthew Malcomson
@ 2019-11-05 18:15           ` Martin Liška
  0 siblings, 0 replies; 150+ messages in thread
From: Martin Liška @ 2019-11-05 18:15 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 11/5/19 6:35 PM, Matthew Malcomson wrote:
> On 05/11/2019 17:22, Martin Liška wrote:
>> On 11/5/19 5:11 PM, Matthew Malcomson wrote:
>>> On 05/11/2019 15:10, Martin Liška wrote:
>>>> On 11/5/19 12:32 PM, Matthew Malcomson wrote:
>>>>> Hello,
>>>>>
>>>>> This patch series adds the LLVM hardware address sanitizer (HWASAN) to
>>>>> GCC.  The document describing HWASAN can be found here
>>>>> http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html.
>>>>
>>>> Hello.
>>>>
>>>> Thank you for the patch. I've just updated libsanitizer in GCC and I
>>>> also
>>>> prepared a patch which you have included in your series (but using an
>>>> older libsanitizer):
>>>> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00270.html.
>>>>
>>>> Can you please rebase the series on top of the patch and exclude all the
>>>> cherry picks from
>>>> upstream? Plus you can now install
>>>> "[aarch64] Allocate enough space for err_str in
>>>> aarch64_handle_attr_branch_protection"
>>>> patch and exclude it from the series as well.
>>> Will do!
>>>
>>> I'm currently rebasing, but thought I should mention just in case you
>>> didn't know that LLVM have fully moved over to git recently.
>>
>> Hello.
>>
>> You are right, I haven't noticed that.
>>
>>>
>>> There seem to be no commits on the LLVM svn repo since 2019-10-22, so
>>> there are some cherry-picked hwasan patches that are not in at the point
>>> you merged.
>>
>> Do you miss any revisions that were installed in the new git repository
>> and are not in current GCC master? If so, I can update the merge.sh script
>> and do one more merge from trunk.
>>
>> Martin
> 
> Yes, there are three commits in the new git repository and not in GCC
> master that I need.  The most recent commit I need is 91167e2.

Good, I've just rewritten merge.sh to use the LLVM mono repository.
Tomorrow I'll test and install update from revision 82588e05cc32bb30807e480abd4e689b0dee132a.

Thanks,
Martin

> 
> Thanks,
> Matthew.
> 
>>
>>>
>>> Is this known, and we're taking the last SVN commit?
>>>
>>> Cheers,
>>> Matthew
>>>
>>>>
>>>> Thanks,
>>>> Martin
>>>
>>
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (20 preceding siblings ...)
  2019-11-05 15:11   ` Martin Liška
@ 2019-11-05 18:30   ` Joseph Myers
  2019-11-07 18:37   ` [PATCH 4/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
                     ` (7 subsequent siblings)
  29 siblings, 0 replies; 150+ messages in thread
From: Joseph Myers @ 2019-11-05 18:30 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, nd, rguenther, James Greenhalgh, kcc, law, dvyukov,
	Martin Liska, Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft,
	ian, dodji, jakub

On Tue, 5 Nov 2019, Matthew Malcomson wrote:

> Hello,
> 
> This patch series adds the LLVM hardware address sanitizer (HWASAN) to
> GCC.  The document describing HWASAN can be found here
> http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html.

General observations on the patch series:

* You're missing user documentation, in invoke.texi or elsewhere, to 
explain new options relating to using this new feature.

* You're regenerating configure scripts with a patched autoconf version 
that adds --runstatedir options.  You need to regenerate with *unpatched* 
auto*.

* You're missing documentation of bootstrap-hwasan (in install.texi).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 13/X] [libsanitizer][options] Add hwasan flags and argument parsing
  2019-11-05 11:35   ` [PATCH 13/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
  2019-11-05 13:11     ` Andrey Konovalov via gcc-patches
@ 2019-11-05 18:32     ` Joseph Myers
  1 sibling, 0 replies; 150+ messages in thread
From: Joseph Myers @ 2019-11-05 18:32 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, nd, kcc, dvyukov, Martin Liska, Richard Earnshaw,
	Kyrylo Tkachov, dodji, jakub, kasan-dev, Andrey Konovalov,
	Dmitry Vyukov

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

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 13/X] [libsanitizer][options] Add hwasan flags and argument parsing
  2019-11-05 13:11     ` Andrey Konovalov via gcc-patches
@ 2019-11-07 12:48       ` Matthew Malcomson
  2019-11-07 15:51         ` Andrey Konovalov via gcc-patches
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-07 12:48 UTC (permalink / raw)
  To: Andrey Konovalov, kcc, dvyukov, Evgenii Stepanov
  Cc: gcc-patches, nd, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub, kasan-dev

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


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 13/X] [libsanitizer][options] Add hwasan flags and argument parsing
  2019-11-07 12:48       ` Matthew Malcomson
@ 2019-11-07 15:51         ` Andrey Konovalov via gcc-patches
  2019-11-07 18:01           ` Evgenii Stepanov via gcc-patches
  0 siblings, 1 reply; 150+ messages in thread
From: Andrey Konovalov via gcc-patches @ 2019-11-07 15:51 UTC (permalink / raw)
  To: Matthew Malcomson, Evgenii Stepanov
  Cc: kcc, dvyukov, gcc-patches, nd, Martin Liska, Richard Earnshaw,
	Kyrylo Tkachov, dodji, jakub, kasan-dev

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?

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 13/X] [libsanitizer][options] Add hwasan flags and argument parsing
  2019-11-07 15:51         ` Andrey Konovalov via gcc-patches
@ 2019-11-07 18:01           ` Evgenii Stepanov via gcc-patches
  2019-11-08 13:56             ` Andrey Konovalov via gcc-patches
  0 siblings, 1 reply; 150+ messages in thread
From: Evgenii Stepanov via gcc-patches @ 2019-11-07 18:01 UTC (permalink / raw)
  To: Andrey Konovalov
  Cc: Matthew Malcomson, kcc, dvyukov, gcc-patches, nd, Martin Liska,
	Richard Earnshaw, Kyrylo Tkachov, dodji, jakub, kasan-dev

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?
>

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 1/X] [libsanitizer] Tie the hwasan library into our build system
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (25 preceding siblings ...)
  2019-11-07 18:37   ` [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes Matthew Malcomson
@ 2019-11-07 18:37   ` Matthew Malcomson
  2019-11-20 11:13     ` Martin Liška
  2019-11-07 18:37   ` [PATCH 2/X] [libsanitizer] Only build libhwasan when targeting AArch64 Matthew Malcomson
                     ` (2 subsequent siblings)
  29 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-07 18:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 45623 bytes --]

This patch does tries to tie libhwasan into the GCC build system in the
same way that the other sanitizer runtime libraries are handled.

libsanitizer/ChangeLog:

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

	* Makefile.am:  Build libhwasan.
	* Makefile.in:  Build libhwasan.
	* asan/Makefile.in:  Build libhwasan.
	* configure:  Build libhwasan.
	* configure.ac:  Build libhwasan.
	* hwasan/Makefile.am: New file.
	* hwasan/Makefile.in: New file.
	* hwasan/libtool-version: New file.
	* interception/Makefile.in: Build libhwasan.
	* libbacktrace/Makefile.in: Build libhwasan.
	* libsanitizer.spec.in: Build libhwasan.
	* lsan/Makefile.in: Build libhwasan.
	* merge.sh: Build libhwasan.
	* sanitizer_common/Makefile.in: Build libhwasan.
	* tsan/Makefile.in: Build libhwasan.
	* ubsan/Makefile.in: Build libhwasan.



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


diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 65ed1e712378ef453f820f86c4d3221f9dee5f2c..2a7e8e1debe838719db0f0fad218b2543cc3111b 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,11 +14,12 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan
+SUBDIRS += lsan asan ubsan hwasan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
-  include/sanitizer/tsan_interface.h
+  include/sanitizer/tsan_interface.h \
+  include/sanitizer/hwasan_interface.h
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 0d789b3a59d21ea2e5a23057ca3afe15425feec4..36aa952af7e04bc0e4fb94cdcd584d539193d781 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -92,7 +92,8 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@am__append_1 = include/sanitizer/common_interface_defs.h \
 @SANITIZER_SUPPORTED_TRUE@	include/sanitizer/lsan_interface.h \
 @SANITIZER_SUPPORTED_TRUE@	include/sanitizer/asan_interface.h \
-@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/tsan_interface.h
+@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/tsan_interface.h \
+@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/hwasan_interface.h
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
@@ -206,7 +207,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan tsan
+	ubsan hwasan tsan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -328,6 +329,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
@@ -361,7 +363,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	$(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in
index 00b6082da5372efd679ddc230f588bbc58161ef6..76689c3b224b1fb04895ae48829eac4b6784cd84 100644
--- a/libsanitizer/asan/Makefile.in
+++ b/libsanitizer/asan/Makefile.in
@@ -382,6 +382,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/configure b/libsanitizer/configure
index 79b5c1eadb59018bca13a33f19f3494c170365ee..ff72af73e6f77aaf93bf39e6799f896851a377dd 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -657,6 +657,7 @@ USING_MAC_INTERPOSE_TRUE
 link_liblsan
 link_libubsan
 link_libtsan
+link_libhwasan
 link_libasan
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
@@ -12334,7 +12335,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12337 "configure"
+#line 12338 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12440,7 +12441,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12443 "configure"
+#line 12444 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15916,6 +15917,10 @@ fi
 link_libasan=$link_sanitizer_common
 
 
+# Set up the set of additional libraries that we need to link against for libhwasan.
+link_libhwasan=$link_sanitizer_common
+
+
 # Set up the set of additional libraries that we need to link against for libtsan.
 link_libtsan=$link_sanitizer_common
 
@@ -16786,7 +16791,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -18063,6 +18068,7 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
 
@@ -19998,6 +20004,17 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+   sed -f vpsed$$ $ac_file > tmp$$
+   mv tmp$$ $ac_file
+   rm vpsed$$
+   echo 'MULTISUBDIR =' >> $ac_file
+   ml_norecursion=yes
+   . ${multi_basedir}/config-ml.in
+   { ml_norecursion=; unset ml_norecursion;}
+ ;;
     "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index 8aac33cb5c980db77e31e8d799ab5a12f3e3a404..02c0c235171e4c8249821c5db91414f8e4bfb3ee 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -111,6 +111,10 @@ AC_CHECK_LIB(dl, dlsym,
 link_libasan=$link_sanitizer_common
 AC_SUBST(link_libasan)
 
+# Set up the set of additional libraries that we need to link against for libhwasan.
+link_libhwasan=$link_sanitizer_common
+AC_SUBST(link_libhwasan)
+
 # Set up the set of additional libraries that we need to link against for libtsan.
 link_libtsan=$link_sanitizer_common
 AC_SUBST(link_libtsan)
@@ -339,7 +343,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
diff --git a/libsanitizer/hwasan/Makefile.am b/libsanitizer/hwasan/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..a20705ae1277b49d2edc464e23b106c5e6086d35
--- /dev/null
+++ b/libsanitizer/hwasan/Makefile.am
@@ -0,0 +1,88 @@
+AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
+ 
+# May be used by toolexeclibdir.
+gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
+
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -fno-ipa-icf
+AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+AM_CXXFLAGS += -std=gnu++11
+AM_CXXFLAGS += $(EXTRA_CXXFLAGS)
+ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
+
+toolexeclib_LTLIBRARIES = libhwasan.la
+
+hwasan_files = \
+	       hwasan_allocator.cpp \
+	       hwasan.cpp \
+	       hwasan_dynamic_shadow.cpp \
+	       hwasan_exceptions.cpp \
+	       hwasan_flags.inc \
+	       hwasan_interceptors.cpp \
+	       hwasan_interceptors_vfork.S \
+	       hwasan_linux.cpp \
+	       hwasan_memintrinsics.cpp \
+	       hwasan_new_delete.cpp \
+	       hwasan_poisoning.cpp \
+	       hwasan_report.cpp \
+	       hwasan_setjmp.S \
+	       hwasan_tag_mismatch_aarch64.S \
+	       hwasan_thread.cpp \
+	       hwasan_thread_list.cpp \
+	       hwasan_type_test.cpp
+
+libhwasan_la_SOURCES = $(hwasan_files)
+libhwasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la
+if !USING_MAC_INTERPOSE
+libhwasan_la_LIBADD += $(top_builddir)/interception/libinterception.la
+endif
+if LIBBACKTRACE_SUPPORTED
+libhwasan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
+endif
+libhwasan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+
+libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan)
+
+# Work around what appears to be a GNU make bug handling MAKEFLAGS
+# values defined in terms of make variables, as is the case for CC and
+# friends when we are called from the top level Makefile.
+AM_MAKEFLAGS = \
+	"AR_FLAGS=$(AR_FLAGS)" \
+	"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
+	"CFLAGS=$(CFLAGS)" \
+	"CXXFLAGS=$(CXXFLAGS)" \
+	"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
+	"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
+	"INSTALL=$(INSTALL)" \
+	"INSTALL_DATA=$(INSTALL_DATA)" \
+	"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
+	"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
+	"JC1FLAGS=$(JC1FLAGS)" \
+	"LDFLAGS=$(LDFLAGS)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
+	"MAKE=$(MAKE)" \
+	"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
+	"PICFLAG=$(PICFLAG)" \
+	"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
+	"SHELL=$(SHELL)" \
+	"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
+	"exec_prefix=$(exec_prefix)" \
+	"infodir=$(infodir)" \
+	"libdir=$(libdir)" \
+	"prefix=$(prefix)" \
+	"includedir=$(includedir)" \
+	"AR=$(AR)" \
+	"AS=$(AS)" \
+	"LD=$(LD)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"NM=$(NM)" \
+	"PICFLAG=$(PICFLAG)" \
+	"RANLIB=$(RANLIB)" \
+	"DESTDIR=$(DESTDIR)"
+
+MAKEOVERRIDES=
+
+## ################################################################
+
+
diff --git a/libsanitizer/hwasan/Makefile.in b/libsanitizer/hwasan/Makefile.in
new file mode 100644
index 0000000000000000000000000000000000000000..56a0a9553d642ac6ef6c4ac7484eac2b08548e6a
--- /dev/null
+++ b/libsanitizer/hwasan/Makefile.in
@@ -0,0 +1,800 @@
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+@USING_MAC_INTERPOSE_FALSE@am__append_1 = $(top_builddir)/interception/libinterception.la
+@LIBBACKTRACE_SUPPORTED_TRUE@am__append_2 = $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
+subdir = hwasan
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
+	$(top_srcdir)/../config/depstand.m4 \
+	$(top_srcdir)/../config/lead-dot.m4 \
+	$(top_srcdir)/../config/libstdc++-raw-cxx.m4 \
+	$(top_srcdir)/../config/multi.m4 \
+	$(top_srcdir)/../config/override.m4 \
+	$(top_srcdir)/../config/stdint.m4 \
+	$(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \
+	$(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \
+	$(top_srcdir)/acinclude.m4 $(top_srcdir)/../libtool.m4 \
+	$(top_srcdir)/../config/enable.m4 \
+	$(top_srcdir)/../config/cet.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am
+mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(toolexeclibdir)"
+LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libhwasan_la_DEPENDENCIES =  \
+	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
+	$(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1)
+am__objects_1 = hwasan_allocator.lo hwasan.lo hwasan_dynamic_shadow.lo \
+	hwasan_exceptions.lo hwasan_interceptors.lo \
+	hwasan_interceptors_vfork.lo hwasan_linux.lo \
+	hwasan_memintrinsics.lo hwasan_new_delete.lo \
+	hwasan_poisoning.lo hwasan_report.lo hwasan_setjmp.lo \
+	hwasan_tag_mismatch_aarch64.lo hwasan_thread.lo \
+	hwasan_thread_list.lo hwasan_type_test.lo
+am_libhwasan_la_OBJECTS = $(am__objects_1)
+libhwasan_la_OBJECTS = $(am_libhwasan_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libhwasan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+	$(CXXFLAGS) $(libhwasan_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/../depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS)
+LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CCAS) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CCASFLAGS) $(CCASFLAGS)
+AM_V_CPPAS = $(am__v_CPPAS_@AM_V@)
+am__v_CPPAS_ = $(am__v_CPPAS_@AM_DEFAULT_V@)
+am__v_CPPAS_0 = @echo "  CPPAS   " $@;
+am__v_CPPAS_1 = 
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo "  CXX     " $@;
+am__v_CXX_1 = 
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+	$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo "  CXXLD   " $@;
+am__v_CXXLD_1 = 
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libhwasan_la_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+ACLOCAL = @ACLOCAL@
+ALLOC_FILE = @ALLOC_FILE@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BACKTRACE_SUPPORTED = @BACKTRACE_SUPPORTED@
+BACKTRACE_SUPPORTS_THREADS = @BACKTRACE_SUPPORTS_THREADS@
+BACKTRACE_USES_MALLOC = @BACKTRACE_USES_MALLOC@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXTRA_ASFLAGS = @EXTRA_ASFLAGS@
+EXTRA_CFLAGS = @EXTRA_CFLAGS@
+EXTRA_CXXFLAGS = @EXTRA_CXXFLAGS@
+FGREP = @FGREP@
+FORMAT_FILE = @FORMAT_FILE@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSTDCXX_RAW_CXX_CXXFLAGS = @LIBSTDCXX_RAW_CXX_CXXFLAGS@
+LIBSTDCXX_RAW_CXX_LDFLAGS = @LIBSTDCXX_RAW_CXX_LDFLAGS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
+SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS = @SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
+VERSION = @VERSION@
+VIEW_FILE = @VIEW_FILE@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+enable_shared = @enable_shared@
+enable_static = @enable_static@
+exec_prefix = @exec_prefix@
+get_gcc_base_ver = @get_gcc_base_ver@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
+link_liblsan = @link_liblsan@
+link_libtsan = @link_libtsan@
+link_libubsan = @link_libubsan@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+multi_basedir = @multi_basedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_noncanonical = @target_noncanonical@
+target_os = @target_os@
+target_vendor = @target_vendor@
+toolexecdir = @toolexecdir@
+toolexeclibdir = @toolexeclibdir@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
+
+# May be used by toolexeclibdir.
+gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
+	-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
+	-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
+	-Wno-variadic-macros -fno-ipa-icf \
+	$(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=gnu++11 $(EXTRA_CXXFLAGS)
+ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
+toolexeclib_LTLIBRARIES = libhwasan.la
+hwasan_files = \
+	       hwasan_allocator.cpp \
+	       hwasan.cpp \
+	       hwasan_dynamic_shadow.cpp \
+	       hwasan_exceptions.cpp \
+	       hwasan_flags.inc \
+	       hwasan_interceptors.cpp \
+	       hwasan_interceptors_vfork.S \
+	       hwasan_linux.cpp \
+	       hwasan_memintrinsics.cpp \
+	       hwasan_new_delete.cpp \
+	       hwasan_poisoning.cpp \
+	       hwasan_report.cpp \
+	       hwasan_setjmp.S \
+	       hwasan_tag_mismatch_aarch64.S \
+	       hwasan_thread.cpp \
+	       hwasan_thread_list.cpp \
+	       hwasan_type_test.cpp
+
+libhwasan_la_SOURCES = $(hwasan_files)
+libhwasan_la_LIBADD =  \
+	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
+	$(am__append_1) $(am__append_2) $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan)
+
+# Work around what appears to be a GNU make bug handling MAKEFLAGS
+# values defined in terms of make variables, as is the case for CC and
+# friends when we are called from the top level Makefile.
+AM_MAKEFLAGS = \
+	"AR_FLAGS=$(AR_FLAGS)" \
+	"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
+	"CFLAGS=$(CFLAGS)" \
+	"CXXFLAGS=$(CXXFLAGS)" \
+	"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
+	"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
+	"INSTALL=$(INSTALL)" \
+	"INSTALL_DATA=$(INSTALL_DATA)" \
+	"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
+	"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
+	"JC1FLAGS=$(JC1FLAGS)" \
+	"LDFLAGS=$(LDFLAGS)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
+	"MAKE=$(MAKE)" \
+	"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
+	"PICFLAG=$(PICFLAG)" \
+	"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
+	"SHELL=$(SHELL)" \
+	"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
+	"exec_prefix=$(exec_prefix)" \
+	"infodir=$(infodir)" \
+	"libdir=$(libdir)" \
+	"prefix=$(prefix)" \
+	"includedir=$(includedir)" \
+	"AR=$(AR)" \
+	"AS=$(AS)" \
+	"LD=$(LD)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"NM=$(NM)" \
+	"PICFLAG=$(PICFLAG)" \
+	"RANLIB=$(RANLIB)" \
+	"DESTDIR=$(DESTDIR)"
+
+MAKEOVERRIDES = 
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .S .cpp .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign hwasan/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign hwasan/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-toolexeclibLTLIBRARIES: $(toolexeclib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(toolexeclibdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(toolexeclibdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(toolexeclibdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(toolexeclibdir)"; \
+	}
+
+uninstall-toolexeclibLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(toolexeclibdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(toolexeclibdir)/$$f"; \
+	done
+
+clean-toolexeclibLTLIBRARIES:
+	-test -z "$(toolexeclib_LTLIBRARIES)" || rm -f $(toolexeclib_LTLIBRARIES)
+	@list='$(toolexeclib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libhwasan.la: $(libhwasan_la_OBJECTS) $(libhwasan_la_DEPENDENCIES) $(EXTRA_libhwasan_la_DEPENDENCIES) 
+	$(AM_V_CXXLD)$(libhwasan_la_LINK) -rpath $(toolexeclibdir) $(libhwasan_la_OBJECTS) $(libhwasan_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_allocator.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_dynamic_shadow.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_exceptions.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors_vfork.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_linux.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_memintrinsics.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_new_delete.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_poisoning.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_report.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_setjmp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_tag_mismatch_aarch64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread_list.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_type_test.Plo@am__quote@
+
+.S.o:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ $<
+
+.S.obj:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.S.lo:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(LTCPPASCOMPILE) -c -o $@ $<
+
+.cpp.o:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+	for dir in "$(DESTDIR)$(toolexeclibdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-toolexeclibLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-toolexeclibLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-toolexeclibLTLIBRARIES cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags dvi dvi-am html html-am info \
+	info-am install install-am install-data install-data-am \
+	install-dvi install-dvi-am install-exec install-exec-am \
+	install-html install-html-am install-info install-info-am \
+	install-man install-pdf install-pdf-am install-ps \
+	install-ps-am install-strip install-toolexeclibLTLIBRARIES \
+	installcheck installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am \
+	uninstall-toolexeclibLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libsanitizer/hwasan/libtool-version b/libsanitizer/hwasan/libtool-version
new file mode 100644
index 0000000000000000000000000000000000000000..204fdd2d8e5fe21a5a9f9e23d6a6cf17d353362a
--- /dev/null
+++ b/libsanitizer/hwasan/libtool-version
@@ -0,0 +1,6 @@
+# This file is used to maintain libtool version info for libmudflap.  See
+# the libtool manual to understand the meaning of the fields.  This is
+# a separate file so that version updates don't involve re-running
+# automake.
+# CURRENT:REVISION:AGE
+0:0:0
diff --git a/libsanitizer/interception/Makefile.in b/libsanitizer/interception/Makefile.in
index 358afe9d8844e411897eb24cd8f0960d0758e375..ff21143d2fc371d76554d0cb57f49d0def1b5a6f 100644
--- a/libsanitizer/interception/Makefile.in
+++ b/libsanitizer/interception/Makefile.in
@@ -300,6 +300,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/libbacktrace/Makefile.in b/libsanitizer/libbacktrace/Makefile.in
index f0e190a81d82ddc86a3e39a5dc41e864190582c0..f2614e272b8e22028ffdd4e3a06083bf42efced0 100644
--- a/libsanitizer/libbacktrace/Makefile.in
+++ b/libsanitizer/libbacktrace/Makefile.in
@@ -350,6 +350,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/libsanitizer.spec.in b/libsanitizer/libsanitizer.spec.in
index a4fa87d8dfc51549b0f932b31a610db1a2e86c29..70a33574d7b1d494c4875cfc41f29606bde2172e 100644
--- a/libsanitizer/libsanitizer.spec.in
+++ b/libsanitizer/libsanitizer.spec.in
@@ -3,6 +3,8 @@
 
 *link_libasan: @link_libasan@
 
+*link_libhwasan: @link_libhwasan@
+
 *link_libtsan: @link_libtsan@
 
 *link_libubsan: @link_libubsan@
diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in
index 3ebf4ba08f6ac0c323dcdd5e3c1b518fcbd7a67f..0e263e8f14c3af3f2e8b5b4a4468aed0db086fcd 100644
--- a/libsanitizer/lsan/Makefile.in
+++ b/libsanitizer/lsan/Makefile.in
@@ -345,6 +345,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/merge.sh b/libsanitizer/merge.sh
index 82e722dbec7d0edb71b03dd44cf779d522ec7984..0b3c1bbbce09353f0a3268cf8978d31270a9ee80 100755
--- a/libsanitizer/merge.sh
+++ b/libsanitizer/merge.sh
@@ -68,6 +68,7 @@ CUR_REV=$(get_current_rev)
 echo Current upstream revision: $CUR_REV
 merge include/sanitizer include/sanitizer
 merge lib/asan asan
+merge lib/hwasan hwasan
 merge lib/lsan lsan
 merge lib/tsan/rtl tsan
 merge lib/sanitizer_common sanitizer_common
diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in
index 0fcebc89bc97da31a62bd58e7802d7a8d20007ec..47206082ac1bd5a008b962427db80f8190fef3ec 100644
--- a/libsanitizer/sanitizer_common/Makefile.in
+++ b/libsanitizer/sanitizer_common/Makefile.in
@@ -336,6 +336,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in
index 3d1d9565e4774701eda051b8d8c73f425e0d80aa..36c3579c65d834c7dd6701b2ffb6dcdaadd10aa5 100644
--- a/libsanitizer/tsan/Makefile.in
+++ b/libsanitizer/tsan/Makefile.in
@@ -374,6 +374,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/ubsan/Makefile.in b/libsanitizer/ubsan/Makefile.in
index 71d2626be63d11d2700d0fa729b1fae7956ee7da..3f10c4545bd7ee6caedcd177b743498872571ad0 100644
--- a/libsanitizer/ubsan/Makefile.in
+++ b/libsanitizer/ubsan/Makefile.in
@@ -339,6 +339,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@


[-- Attachment #2: hwasan-patch0.patch.gz --]
[-- Type: application/gzip, Size: 10825 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 2/X] [libsanitizer] Only build libhwasan when targeting AArch64
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (26 preceding siblings ...)
  2019-11-07 18:37   ` [PATCH 1/X] [libsanitizer] Tie the hwasan library into our build system Matthew Malcomson
@ 2019-11-07 18:37   ` Matthew Malcomson
  2019-11-07 18:38   ` [PATCH 7/X] [libsanitizer] Add tests Matthew Malcomson
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
  29 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-07 18:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 9744 bytes --]

Though the library has limited support for x86, we don't have any
support for generating code targeting x86 so there is no point building
for that target.

libsanitizer/ChangeLog:

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

	* Makefile.am: Condition building hwasan directory.
	* Makefile.in: Regenerate.
	* configure: Regenerate.
	* configure.ac: Set HWASAN_SUPPORTED based on target
	architecture.
	* configure.tgt: Likewise.



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


diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 2a7e8e1debe838719db0f0fad218b2543cc3111b..065a65e78d49f7689a01ecb64db1f07ca83aa987 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,7 +14,7 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan hwasan
+SUBDIRS += lsan asan ubsan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
@@ -23,6 +23,9 @@ nodist_saninclude_HEADERS += \
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
+if HWASAN_SUPPORTED
+SUBDIRS += hwasan
+endif
 endif
 
 ## May be used by toolexeclibdir.
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 36aa952af7e04bc0e4fb94cdcd584d539193d781..75a99491cb1d4422fd5e2d93cae93eb883ae0963 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -97,6 +97,7 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
+@HWASAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_5 = hwasan
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
@@ -207,7 +208,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan hwasan tsan
+	ubsan tsan hwasan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -363,7 +364,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	$(am__append_4) $(am__append_5)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/configure b/libsanitizer/configure
index ff72af73e6f77aaf93bf39e6799f896851a377dd..4e95194fe3567b1227c4036c2f5bf6540f735975 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -659,6 +659,8 @@ link_libubsan
 link_libtsan
 link_libhwasan
 link_libasan
+HWASAN_SUPPORTED_FALSE
+HWASAN_SUPPORTED_TRUE
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
 TSAN_SUPPORTED_FALSE
@@ -12335,7 +12337,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12338 "configure"
+#line 12340 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12441,7 +12443,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12444 "configure"
+#line 12446 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15792,6 +15794,7 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
  if test "x$TSAN_SUPPORTED" = "xyes"; then
   TSAN_SUPPORTED_TRUE=
@@ -15809,6 +15812,14 @@ else
   LSAN_SUPPORTED_FALSE=
 fi
 
+ if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  HWASAN_SUPPORTED_TRUE=
+  HWASAN_SUPPORTED_FALSE='#'
+else
+  HWASAN_SUPPORTED_TRUE='#'
+  HWASAN_SUPPORTED_FALSE=
+fi
+
 
 # Check for functions needed.
 for ac_func in clock_getres clock_gettime clock_settime lstat readlink
@@ -16791,7 +16802,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -16799,6 +16810,11 @@ if test "x$TSAN_SUPPORTED" = "xyes"; then
 
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  ac_config_files="$ac_config_files hwasan/Makefile"
+
+fi
+
 
 
 
@@ -17059,6 +17075,10 @@ if test -z "${LSAN_SUPPORTED_TRUE}" && test -z "${LSAN_SUPPORTED_FALSE}"; then
   as_fn_error $? "conditional \"LSAN_SUPPORTED\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HWASAN_SUPPORTED_TRUE}" && test -z "${HWASAN_SUPPORTED_FALSE}"; then
+  as_fn_error $? "conditional \"HWASAN_SUPPORTED\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${USING_MAC_INTERPOSE_TRUE}" && test -z "${USING_MAC_INTERPOSE_FALSE}"; then
   as_fn_error $? "conditional \"USING_MAC_INTERPOSE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -18068,9 +18088,9 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
-    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
@@ -20004,7 +20024,7 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
    sed -f vpsed$$ $ac_file > tmp$$
@@ -20015,18 +20035,18 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
-   sed -f vpsed$$ $ac_file > tmp$$
-   mv tmp$$ $ac_file
-   rm vpsed$$
-   echo 'MULTISUBDIR =' >> $ac_file
-   ml_norecursion=yes
-   . ${multi_basedir}/config-ml.in
-   { ml_norecursion=; unset ml_norecursion;}
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
     sed -f vpsed$$ $ac_file > tmp$$
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index 02c0c235171e4c8249821c5db91414f8e4bfb3ee..ba56e911ad59fc4f3aa88fa01ea4201b9549cd44 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -87,9 +87,11 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
 AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
 AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"])
+AM_CONDITIONAL(HWASAN_SUPPORTED, [test "x$HWASAN_SUPPORTED" = "xyes"])
 
 # Check for functions needed.
 AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime lstat readlink)
@@ -343,7 +345,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
@@ -371,6 +373,21 @@ _EOF
 ])
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  AC_CONFIG_FILES(AC_FOREACH([DIR], [hwasan], [DIR/Makefile ]), 
+    [cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    AS_UNSET([ml_norecursion])
+])
+fi
+
 AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS])
 AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS])
 
diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt
index 714f2923605011329b54be7e9fcf7af44a0593fd..92f4e9ff507b2972d1993ee288ea9ccef0103ec3 100644
--- a/libsanitizer/configure.tgt
+++ b/libsanitizer/configure.tgt
@@ -59,6 +59,7 @@ case "${target}" in
 		LSAN_SUPPORTED=yes
 		TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
 	fi
+	HWASAN_SUPPORTED=yes
 	;;
   x86_64-*-darwin[1]* | i?86-*-darwin[1]*)
 	TSAN_SUPPORTED=no


[-- Attachment #2: hwasan-patch1.patch --]
[-- Type: text/plain, Size: 8946 bytes --]

diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 2a7e8e1debe838719db0f0fad218b2543cc3111b..065a65e78d49f7689a01ecb64db1f07ca83aa987 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,7 +14,7 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan hwasan
+SUBDIRS += lsan asan ubsan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
@@ -23,6 +23,9 @@ nodist_saninclude_HEADERS += \
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
+if HWASAN_SUPPORTED
+SUBDIRS += hwasan
+endif
 endif
 
 ## May be used by toolexeclibdir.
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 36aa952af7e04bc0e4fb94cdcd584d539193d781..75a99491cb1d4422fd5e2d93cae93eb883ae0963 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -97,6 +97,7 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
+@HWASAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_5 = hwasan
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
@@ -207,7 +208,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan hwasan tsan
+	ubsan tsan hwasan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -363,7 +364,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	$(am__append_4) $(am__append_5)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/configure b/libsanitizer/configure
index ff72af73e6f77aaf93bf39e6799f896851a377dd..4e95194fe3567b1227c4036c2f5bf6540f735975 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -659,6 +659,8 @@ link_libubsan
 link_libtsan
 link_libhwasan
 link_libasan
+HWASAN_SUPPORTED_FALSE
+HWASAN_SUPPORTED_TRUE
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
 TSAN_SUPPORTED_FALSE
@@ -12335,7 +12337,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12338 "configure"
+#line 12340 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12441,7 +12443,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12444 "configure"
+#line 12446 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15792,6 +15794,7 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
  if test "x$TSAN_SUPPORTED" = "xyes"; then
   TSAN_SUPPORTED_TRUE=
@@ -15809,6 +15812,14 @@ else
   LSAN_SUPPORTED_FALSE=
 fi
 
+ if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  HWASAN_SUPPORTED_TRUE=
+  HWASAN_SUPPORTED_FALSE='#'
+else
+  HWASAN_SUPPORTED_TRUE='#'
+  HWASAN_SUPPORTED_FALSE=
+fi
+
 
 # Check for functions needed.
 for ac_func in clock_getres clock_gettime clock_settime lstat readlink
@@ -16791,7 +16802,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -16799,6 +16810,11 @@ if test "x$TSAN_SUPPORTED" = "xyes"; then
 
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  ac_config_files="$ac_config_files hwasan/Makefile"
+
+fi
+
 
 
 
@@ -17059,6 +17075,10 @@ if test -z "${LSAN_SUPPORTED_TRUE}" && test -z "${LSAN_SUPPORTED_FALSE}"; then
   as_fn_error $? "conditional \"LSAN_SUPPORTED\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HWASAN_SUPPORTED_TRUE}" && test -z "${HWASAN_SUPPORTED_FALSE}"; then
+  as_fn_error $? "conditional \"HWASAN_SUPPORTED\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${USING_MAC_INTERPOSE_TRUE}" && test -z "${USING_MAC_INTERPOSE_FALSE}"; then
   as_fn_error $? "conditional \"USING_MAC_INTERPOSE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -18068,9 +18088,9 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
-    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
@@ -20004,7 +20024,7 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
    sed -f vpsed$$ $ac_file > tmp$$
@@ -20015,18 +20035,18 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
-   sed -f vpsed$$ $ac_file > tmp$$
-   mv tmp$$ $ac_file
-   rm vpsed$$
-   echo 'MULTISUBDIR =' >> $ac_file
-   ml_norecursion=yes
-   . ${multi_basedir}/config-ml.in
-   { ml_norecursion=; unset ml_norecursion;}
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
     sed -f vpsed$$ $ac_file > tmp$$
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index 02c0c235171e4c8249821c5db91414f8e4bfb3ee..ba56e911ad59fc4f3aa88fa01ea4201b9549cd44 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -87,9 +87,11 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
 AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
 AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"])
+AM_CONDITIONAL(HWASAN_SUPPORTED, [test "x$HWASAN_SUPPORTED" = "xyes"])
 
 # Check for functions needed.
 AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime lstat readlink)
@@ -343,7 +345,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
@@ -371,6 +373,21 @@ _EOF
 ])
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  AC_CONFIG_FILES(AC_FOREACH([DIR], [hwasan], [DIR/Makefile ]), 
+    [cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    AS_UNSET([ml_norecursion])
+])
+fi
+
 AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS])
 AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS])
 
diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt
index 714f2923605011329b54be7e9fcf7af44a0593fd..92f4e9ff507b2972d1993ee288ea9ccef0103ec3 100644
--- a/libsanitizer/configure.tgt
+++ b/libsanitizer/configure.tgt
@@ -59,6 +59,7 @@ case "${target}" in
 		LSAN_SUPPORTED=yes
 		TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
 	fi
+	HWASAN_SUPPORTED=yes
 	;;
   x86_64-*-darwin[1]* | i?86-*-darwin[1]*)
 	TSAN_SUPPORTED=no


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (24 preceding siblings ...)
  2019-11-07 18:37   ` [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN Matthew Malcomson
@ 2019-11-07 18:37   ` Matthew Malcomson
  2019-11-20 14:11     ` Martin Liška
  2019-11-07 18:37   ` [PATCH 1/X] [libsanitizer] Tie the hwasan library into our build system Matthew Malcomson
                     ` (3 subsequent siblings)
  29 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-07 18:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 51046 bytes --]

There are four main features to this change:

1) Check pointer tags match address tags.

In the new `hwasan` pass we put HWASAN_CHECK internal functions around
all memory accesses, to check that tags in the pointer being used match
the tag stored in shadow memory for the memory region being used.

These internal functions are expanded into actual checks in the sanopt
pass that happens just before expansion into RTL.

We use the same mechanism that currently inserts ASAN_CHECK internal
functions to insert the new HWASAN_CHECK functions.

2) Instrument known builtin function calls.

Handle all builtin functions that we know use memory accesses.
This commit uses the machinery added for ASAN to identify builtin
functions that access memory.

The main differences between the approaches for HWASAN and ASAN are:
 - libhwasan intercepts much less builtin functions.
 - Alloca needs to be transformed differently (instead of adding
   redzones it needs to tag shadow memory and return a tagged pointer).
 - stack_restore needs to untag the shadow stack between the current
   position and where it's going.
 - `noreturn` functions can not be handled by simply unpoisoning the
   entire shadow stack -- there is no "always valid" tag.
   (exceptions and things such as longjmp need to be handled in a
   different way).

For hardware implemented checking (such as AArch64's memory tagging
extension) alloca and stack_restore will need to be handled by hooks in
the backend rather than transformation at the gimple level.  This will
allow architecture specific handling of such stack modifications.

3) Introduce HWASAN block-scope poisoning

Here we use exactly the same mechanism as ASAN_MARK to poison/unpoison
variables on entry/exit of a block.

In order to simply use the exact same machinery we're using the same
internal functions until the SANOPT pass.  This means that all handling
of ASAN_MARK is the same.
This has the negative that the naming may be a little confusing, but a
positive that handling of the internal function doesn't have to be
duplicated for a function that behaves exactly the same but has a
different name.

gcc/ChangeLog:

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

	* asan.c (handle_builtin_stack_restore): Account for HWASAN.
	(handle_builtin_alloca): Account for HWASAN.
	(get_mem_refs_of_builtin_call): Special case strlen for HWASAN.
	(report_error_func): Assert not HWASAN.
	(build_check_stmt): Make HWASAN_CHECK instead of ASAN_CHECK.
	(instrument_derefs): HWASAN does not tag globals.
	(maybe_instrument_call): Don't instrument `noreturn` functions.
	(initialize_sanitizer_builtins): Add new type.
	(asan_expand_mark_ifn): Account for HWASAN.
	(asan_expand_check_ifn): Assert never called by HWASAN.
	(asan_expand_poison_ifn): Account for HWASAN.
	(hwasan_instrument): New.
	(hwasan_base): New.
	(hwasan_emit_untag_frame): Free block-scope-var hash map.
	(hwasan_check_func): New.
	(hwasan_expand_check_ifn): New.
	(hwasan_expand_mark_ifn): New.
	(gate_hwasan): New.
	(class pass_hwasan): New.
	(make_pass_hwasan): New.
	(class pass_hwasan_O0): New.
	(make_pass_hwasan_O0): New.
	* asan.h (hwasan_base): New decl.
	(hwasan_expand_check_ifn): New decl.
	(hwasan_expand_mark_ifn): New decl.
	(gate_hwasan): New decl.
	(enum hwasan_mark_flags): New.
	(asan_intercepted_p): Always false for hwasan.
	(asan_sanitize_use_after_scope): Account for HWASAN.
	* builtin-types.def (BT_FN_PTR_CONST_PTR_UINT8): New.
	* gimple-pretty-print.c (dump_gimple_call_args): Account for
	HWASAN.
	* gimplify.c (asan_poison_variable): Account for HWASAN.
	(gimplify_function_tree): Remove requirement of
	SANITIZE_ADDRESS, requiring asan or hwasan is accounted for in
	`asan_sanitize_use_after_scope`.
	* internal-fn.c (expand_HWASAN_CHECK): New.
	(expand_HWASAN_CHOOSE_TAG): New.
	(expand_HWASAN_MARK): New.
	* internal-fn.def (HWASAN_CHOOSE_TAG): New.
	(HWASAN_CHECK): New.
	(HWASAN_MARK): New.
	* passes.def: Add hwasan and hwasan_O0 passes.
	* sanitizer.def (BUILT_IN_HWASAN_LOAD1): New.
	(BUILT_IN_HWASAN_LOAD2): New.
	(BUILT_IN_HWASAN_LOAD4): New.
	(BUILT_IN_HWASAN_LOAD8): New.
	(BUILT_IN_HWASAN_LOAD16): New.
	(BUILT_IN_HWASAN_LOADN): New.
	(BUILT_IN_HWASAN_STORE1): New.
	(BUILT_IN_HWASAN_STORE2): New.
	(BUILT_IN_HWASAN_STORE4): New.
	(BUILT_IN_HWASAN_STORE8): New.
	(BUILT_IN_HWASAN_STORE16): New.
	(BUILT_IN_HWASAN_STOREN): New.
	(BUILT_IN_HWASAN_LOAD1_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD2_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD4_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD8_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD16_NOABORT): New.
	(BUILT_IN_HWASAN_LOADN_NOABORT): New.
	(BUILT_IN_HWASAN_STORE1_NOABORT): New.
	(BUILT_IN_HWASAN_STORE2_NOABORT): New.
	(BUILT_IN_HWASAN_STORE4_NOABORT): New.
	(BUILT_IN_HWASAN_STORE8_NOABORT): New.
	(BUILT_IN_HWASAN_STORE16_NOABORT): New.
	(BUILT_IN_HWASAN_STOREN_NOABORT): New.
	(BUILT_IN_HWASAN_TAG_MISMATCH4): New.
	(BUILT_IN_HWASAN_HANDLE_LONGJMP): New.
	(BUILT_IN_HWASAN_TAG_PTR): New.
	* sanopt.c (sanopt_optimize_walker): Act during hwasan.
	(pass_sanopt::execute): Act during hwasan.
	* toplev.c (compile_file): Use `gate_hwasan` function.
	* tree-pass.h (make_pass_hwasan): Declare passes.
	(make_pass_hwasan_O0): Declare passes.



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


diff --git a/gcc/asan.h b/gcc/asan.h
index 467231f8dad031a6176aeaddb9414f768b2af3fc..179f0369b68cf1aceab00e66004332b889cd890c 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -31,10 +31,14 @@ extern rtx hwasan_with_tag (rtx, poly_int64);
 extern void hwasan_tag_init ();
 extern rtx hwasan_create_untagged_base (rtx);
 extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern rtx hwasan_base ();
 extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
 extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx, rtx_insn *);
+extern bool hwasan_expand_check_ifn (gimple_stmt_iterator *, bool);
+extern bool hwasan_expand_mark_ifn (gimple_stmt_iterator *);
 extern bool memory_tagging_p (void);
 extern bool hwasan_sanitize_stack_p (void);
+extern bool gate_hwasan (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -131,6 +135,13 @@ enum asan_mark_flags
 #undef DEF
 };
 
+enum hwasan_mark_flags
+{
+#define DEF(X) HWASAN_MARK_##X
+  IFN_ASAN_MARK_FLAGS
+#undef DEF
+};
+
 /* Return true if STMT is ASAN_MARK with FLAG as first argument.  */
 extern bool asan_mark_p (gimple *stmt, enum asan_mark_flags flag);
 
@@ -180,6 +191,9 @@ extern hash_set<tree> *asan_handled_variables;
 static inline bool
 asan_intercepted_p (enum built_in_function fcode)
 {
+  if (memory_tagging_p ())
+    return false;
+
   return fcode == BUILT_IN_INDEX
 	 || fcode == BUILT_IN_MEMCHR
 	 || fcode == BUILT_IN_MEMCMP
@@ -208,7 +222,8 @@ asan_intercepted_p (enum built_in_function fcode)
 static inline bool
 asan_sanitize_use_after_scope (void)
 {
-  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
+  return (flag_sanitize_address_use_after_scope
+	  && (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ()));
 }
 
 /* Return true if DECL should be guarded on the stack.  */
diff --git a/gcc/asan.c b/gcc/asan.c
index 2e79d39785467651c352169dae4551a47d7b3613..ca80088c546882571f4a8b580755764d58dab4a7 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dojump.h"
 #include "explow.h"
 #include "expr.h"
+#include "except.h"
 #include "output.h"
 #include "langhooks.h"
 #include "cfgloop.h"
@@ -579,15 +580,28 @@ get_last_alloca_addr ()
 static void
 handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_stack_p ()))
     return;
 
-  tree last_alloca = get_last_alloca_addr ();
   tree restored_stack = gimple_call_arg (call, 0);
-  tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
-  gimple *g = gimple_build_call (fn, 2, last_alloca, restored_stack);
-  gsi_insert_before (iter, g, GSI_SAME_STMT);
-  g = gimple_build_assign (last_alloca, restored_stack);
+
+  gimple *g;
+
+  if (hwasan_sanitize_stack_p ())
+    {
+      tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_HANDLE_LONGJMP);
+      g = gimple_build_call (fn, 1, restored_stack);
+    }
+  else
+    {
+      tree last_alloca = get_last_alloca_addr ();
+      tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
+      g = gimple_build_call (fn, 2, last_alloca, restored_stack);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      g = gimple_build_assign (last_alloca, restored_stack);
+    }
+
   gsi_insert_before (iter, g, GSI_SAME_STMT);
 }
 
@@ -617,14 +631,12 @@ handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 static void
 handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_stack_p ()))
     return;
 
   gassign *g;
   gcall *gg;
-  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
-
-  tree last_alloca = get_last_alloca_addr ();
   tree callee = gimple_call_fndecl (call);
   tree old_size = gimple_call_arg (call, 0);
   tree ptr_type = gimple_call_lhs (call) ? TREE_TYPE (gimple_call_lhs (call))
@@ -634,6 +646,85 @@ handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
     = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
       ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
 
+  if (hwasan_sanitize_stack_p ())
+    {
+      /*
+	 HWASAN needs a different expansion.
+
+	 addr = __builtin_alloca (size, align);
+
+	 should be replaced by
+
+	 new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment;
+	 untagged_addr = __builtin_alloca (new_size, align);
+	 tag = __hwasan_choose_alloca_tag ();
+	 addr = __hwasan_tag_pointer (untagged_addr, tag);
+	 __hwasan_tag_memory (addr, tag, new_size);
+	*/
+      /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start on
+	 a tag granule.  */
+      align = align > HWASAN_TAG_GRANULE_SIZE ? align : HWASAN_TAG_GRANULE_SIZE;
+
+      /* tree new_size = (old_size + 15) & ~15;  */
+      uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
+      tree old_size = gimple_call_arg (call, 0);
+      tree tree_mask = build_int_cst (size_type_node, tg_mask);
+      g = gimple_build_assign (make_ssa_name (size_type_node), PLUS_EXPR,
+			       old_size, tree_mask);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree oversize = gimple_assign_lhs (g);
+
+      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_NOT_EXPR,
+			       tree_mask);
+      tree mask = gimple_assign_lhs (g);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+
+      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_AND_EXPR,
+			       oversize, mask);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree new_size = gimple_assign_lhs (g);
+
+      /* emit the alloca call */
+      tree fn = builtin_decl_implicit (BUILT_IN_ALLOCA_WITH_ALIGN);
+      gg = gimple_build_call (fn, 2, new_size,
+			      build_int_cst (size_type_node, align));
+      tree untagged_addr = make_ssa_name (ptr_type, gg);
+      gimple_call_set_lhs (gg, untagged_addr);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code choosing the tag.
+	 Here we use an internal function so we can choose the tag at expand
+	 time.  We need the decision to be made after stack variables have been
+	 assigned their tag (i.e. once the tag_offset variable has been set to
+	 one after the last stack variables tag).  */
+
+      gg = gimple_build_call_internal (IFN_HWASAN_CHOOSE_TAG, 0);
+      tree tag = make_ssa_name (unsigned_char_type_node, gg);
+      gimple_call_set_lhs (gg, tag);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code adding tag to pointer.  */
+      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_PTR);
+      gg = gimple_build_call (fn, 2, untagged_addr, tag);
+      tree addr = make_ssa_name (ptr_type, gg);
+      gimple_call_set_lhs (gg, addr);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code tagging shadow memory.
+	 NOTE: require using `untagged_addr` here for libhwasan API.  */
+      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MEM);
+      gg = gimple_build_call (fn, 3, untagged_addr, tag, new_size);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Finally, replace old alloca ptr with NEW_ALLOCA.  */
+      replace_call_with_value (iter, addr);
+      return;
+    }
+
+  tree last_alloca = get_last_alloca_addr ();
+  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
+
+
   /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
      bytes of allocated space.  Otherwise, align alloca to ASAN_RED_ZONE_SIZE
      manually.  */
@@ -786,6 +877,31 @@ get_mem_refs_of_builtin_call (gcall *call,
       break;
 
     case BUILT_IN_STRLEN:
+      /* Special case strlen here since its length is taken from its return
+	 value.
+
+	 The approach taken by the sanitizers is to check a memory access
+	 before it's taken.  For ASAN strlen is intercepted by libasan, so no
+	 check is inserted by the compiler.
+
+	 This function still returns `true` and provides a length to the rest
+	 of the ASAN pass in order to record what areas have been checked,
+	 avoiding superfluous checks later on.
+
+	 HWASAN does not intercept any of these internal functions.
+	 This means that checks for memory accesses must be inserted by the
+	 compiler.
+	 strlen is a special case, because we can tell the length from the
+	 return of the function, but that is not known until after the function
+	 has returned.
+
+	 Hence we can't check the memory access before it happens.
+	 We could check the memory access after it has already happened, but
+	 for now I'm choosing to just ignore `strlen` calls.
+	 This decision was simply made because that means the special case is
+	 limited to this one case of this one function.  */
+      if (memory_tagging_p ())
+	return false;
       source0 = gimple_call_arg (call, 0);
       len = gimple_call_lhs (call);
       break;
@@ -1355,6 +1471,150 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+
+/* HWAddressSanitizer (hwasan) is a probabilistic method for detecting
+   out-of-bounds and use-after-free bugs.
+   Read more:
+   http://code.google.com/p/address-sanitizer/
+
+   Similar to AddressSanitizer (asan) it consists of two parts: the
+   instrumentation module in this file, and a run-time library.
+
+   The instrumentation module adds a run-time check before every memory insn in
+   the same manner as asan (see the block comment for AddressSanitizer above).
+   Currently, hwasan only adds out-of-line instrumentation, where each check is
+   implemented as a function call to the run-time library.  Hence a check for a
+   load of N bytes from address X would be implemented with a function call to
+   __hwasan_loadN(X), and checking a store of N bytes from address X would be
+   implemented with a function call to __hwasan_storeN(X).
+
+   The main difference between hwasan and asan is in the information stored to
+   help this checking.  Both sanitizers use a shadow memory area which stores
+   data recording the state of main memory at a corresponding address.
+
+   For hwasan, each 16 byte granule in main memory has a corresponding address
+   in shadow memory.  This shadow address can be calculated with equation:
+     (addr >> HWASAN_TAG_SHIFT_SIZE) + __hwasan_shadow_memory_dynamic_address;
+   The conversion between real and shadow memory for asan is given in the block
+   comment at the top of this file.
+   The description of how this shadow memory is laid out for asan is in the
+   block comment at the top of this file, here we describe how this shadow
+   memory is used for hwasan.
+
+   For hwasan, each variable is assigned a byte-sized 'tag'.  The extent of
+   that variable in the shadow memory is filled with the assigned tag, and
+   every pointer referencing that variable has its top byte set to the same
+   tag.  The run-time library redefines malloc so that every allocation returns
+   a tagged pointer and tags the corresponding shadow memory with the same tag.
+
+   On each pointer dereference the tag found in the pointer is compared to the
+   tag found in the shadow memory corresponding to the accessed memory address.
+   If these tags are found to differ then this memory access is judged to be
+   invalid and a report is generated.
+
+   This method of bug detection is not perfect -- it can not catch every bad
+   access, but catches them probabilistically instead.  There is always the
+   possibility that an invalid memory access will happen to access memory
+   tagged with the same tag as the pointer that this access used.
+   The chances of this are approx. 0.4% for any two uncorrelated objects.
+
+   If this does happen, random tag generation can mitigate the problem by
+   decreasing the probability that it will *always* happen.  i.e. if two
+   objects are tagged the same in one run of the binary they are unlikely to be
+   tagged the same in the next run.
+   Heap allocated objects always have randomly generated tags, while objects on
+   the stack can be given random tags determined by command line flags.
+
+   [16 byte granule implications]
+    Since the shadow memory only has a resolution on real memory of 16 bytes,
+    invalid accesses that are still within the same 16 byte granule as valid
+    memory will not be caught.
+
+    There is a "short-granule" feature in the runtime library which does catch
+    such accesses, but this feature is not implemented for stack objects (since
+    stack objects are allocated and tagged by compiler instrumentation, and
+    this feature has not yet been implemented in GCC instrumentation).
+
+    Another outcome of this 16 byte resolution is that each tagged object must
+    be 16 byte aligned.  If two objects were to share any 16 byte granule in
+    memory, then they both would have to be given the same tag, and invalid
+    accesses to one using a pointer to the other would be undetectable.
+
+   [Compiler instrumentation]
+    Compiler instrumentation ensures that two adjacent buffers on the stack are
+    given different tags, this means an access to one buffer using a pointer
+    generated from the other (e.g. through buffer overrun) will have mismatched
+    tags and be caught by hwasan.
+
+    We never randomly tag every object on the stack, since that would require
+    keeping many registers to record each tag.  Instead we randomly generate a
+    tag for each function frame, and each new stack object uses a tag offset
+    from that frame tag.
+    i.e. each object is tagged as RFT + offset, where RFT is the "random frame
+    tag" generated for this frame.
+
+    As a demonstration, using the same example program as in the asan block
+    comment above:
+
+     int
+     foo ()
+     {
+       char a[23] = {0};
+       int b[2] = {0};
+
+       a[5] = 1;
+       b[1] = 2;
+
+       return a[5] + b[1];
+     }
+
+    On AArch64 the stack will be ordered as follows for the above function:
+
+    Slot 1/ [24 bytes for variable 'a']
+    Slot 2/ [8 bytes padding for alignment]
+    Slot 3/ [8 bytes for variable 'b']
+    Slot 4/ [8 bytes padding for alignment]
+
+    (The padding is there to ensure 16 byte alignment as described in the 16
+     byte granule implications).
+
+    While the shadow memory will be ordered as follows:
+
+    - 2 bytes (representing 32 bytes in real memory) tagged with RFT + 1.
+    - 1 byte (representing 16 bytes in real memory) tagged with RFT + 2.
+
+    And any pointer to "a" will have the tag RFT + 1, and any pointer to "b"
+    will have the tag RFT + 2.
+
+   [Top Byte Ignore requirements]
+    Hwasan requires the ability to store an 8 bit tag in every pointer.  There
+    is no instrumentation done to remove this tag from pointers before
+    dereferencing, which means the hardware must ignore this tag during memory
+    accesses.
+
+    One architecture that provides this feature is the AArch64 architecture.
+    This is the only architecture that hwasan is implemented for at the moment.
+
+   [Stack requires cleanup on unwinding]
+    During normal operation of a hwasan sanitized program, as the stack grows
+    more space in the shadow memory becomes tagged.  As the stack shrinks this
+    shadow memory space must become untagged.  If it is not untagged then when
+    the stack grows again (during other function calls later on in the program)
+    objects on the stack that are usually not tagged (e.g. parameters passed on
+    the stack) can be placed in memory whose shadow space is tagged with
+    something else, and accesses can cause false positive reports.
+
+    Hence we place untagging code on every epilogue of functions which tag some
+    stack objects.
+
+    Moreover, the run-time library intercepts longjmp & setjmp to uncolour
+    when the stack is unwound this way.
+
+    C++ exceptions are not yet handled, which means this sanitizer can not
+    handle C++ code that throws exceptions -- it will give false positives
+    after an exception has been thrown.  */
+
+
 /* Returns whether we are tagging pointers and checking those tags on memory
    access.  This is true when checking with either in software or hardware.  */
 bool
@@ -1848,6 +2108,8 @@ static tree
 report_error_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
 		   int *nargs)
 {
+  gcc_assert (!memory_tagging_p ());
+
   static enum built_in_function report[2][2][6]
     = { { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
 	    BUILT_IN_ASAN_REPORT_LOAD4, BUILT_IN_ASAN_REPORT_LOAD8,
@@ -2184,7 +2446,13 @@ build_check_stmt (location_t loc, tree base, tree len,
   if (is_scalar_access)
     flags |= ASAN_CHECK_SCALAR_ACCESS;
 
-  g = gimple_build_call_internal (IFN_ASAN_CHECK, 4,
+  enum internal_fn fn;
+  if (memory_tagging_p ())
+    fn = IFN_HWASAN_CHECK;
+  else
+    fn = IFN_ASAN_CHECK;
+
+  g = gimple_build_call_internal (fn, 4,
 				  build_int_cst (integer_type_node, flags),
 				  base, len,
 				  build_int_cst (integer_type_node,
@@ -2208,10 +2476,13 @@ static void
 instrument_derefs (gimple_stmt_iterator *iter, tree t,
 		   location_t location, bool is_store)
 {
-  if (is_store && !ASAN_INSTRUMENT_WRITES)
-    return;
-  if (!is_store && !ASAN_INSTRUMENT_READS)
-    return;
+  if (! memory_tagging_p ())
+    {
+    if (is_store && !ASAN_INSTRUMENT_WRITES)
+      return;
+    if (!is_store && !ASAN_INSTRUMENT_READS)
+      return;
+    }
 
   tree type, base;
   HOST_WIDE_INT size_in_bytes;
@@ -2271,7 +2542,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
     {
       if (DECL_THREAD_LOCAL_P (inner))
 	return;
-      if (!ASAN_GLOBALS && is_global_var (inner))
+      if ((memory_tagging_p () || !ASAN_GLOBALS)
+	  && is_global_var (inner))
         return;
       if (!TREE_STATIC (inner))
 	{
@@ -2500,10 +2772,13 @@ maybe_instrument_call (gimple_stmt_iterator *iter)
 	      break;
 	    }
 	}
-      tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
-      gimple *g = gimple_build_call (decl, 0);
-      gimple_set_location (g, gimple_location (stmt));
-      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      if (! memory_tagging_p ())
+	{
+	  tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
+	  gimple *g = gimple_build_call (decl, 0);
+	  gimple_set_location (g, gimple_location (stmt));
+	  gsi_insert_before (iter, g, GSI_SAME_STMT);
+	}
     }
 
   bool instrumented = false;
@@ -2902,6 +3177,9 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_PTR_CONST_PTR_UINT8
+    = build_function_type_list (ptr_type_node, const_ptr_type_node,
+				unsigned_char_type_node, NULL_TREE);
   tree BT_FN_VOID_PTR_UINT8_SIZE
     = build_function_type_list (void_type_node, ptr_type_node,
 				unsigned_char_type_node, size_type_node,
@@ -3237,6 +3515,23 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
   unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
   gcc_assert (size_in_bytes);
 
+  if (memory_tagging_p ())
+    {
+      gcc_assert (HWASAN_STACK);
+      /* Here we swap ASAN_MARK calls for HWASAN_MARK.
+	 This is because we are using the approach of using ASAN_MARK as a
+	 synonym until here.
+	 That approach means we don't yet have to duplicate all the special
+	 cases for ASAN_MARK and ASAN_POISON with the exact same handling but
+	 called HWASAN_MARK etc.  */
+      gimple *hw_poison_call
+	= gimple_build_call_internal (IFN_HWASAN_MARK, 3,
+				      gimple_call_arg (g, 0),
+				      base, len);
+      gsi_replace (iter, hw_poison_call, false);
+      return false;
+    }
+
   g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
 			   NOP_EXPR, base);
   gimple_set_location (g, loc);
@@ -3298,6 +3593,7 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 bool
 asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
 {
+  gcc_assert (!memory_tagging_p ());
   gimple *g = gsi_stmt (*iter);
   location_t loc = gimple_location (g);
   bool recover_p;
@@ -3571,11 +3867,37 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
 
       int nargs;
       bool store_p = gimple_call_internal_p (use, IFN_ASAN_POISON_USE);
-      tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
-				    &nargs);
-
-      gcall *call = gimple_build_call (fun, 1,
-				       build_fold_addr_expr (shadow_var));
+      gcall *call;
+      if (memory_tagging_p ())
+	{
+	  tree fun = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MISMATCH4);
+	  /* NOTE: hwasan has no __hwasan_report_* functions like asan does.
+		We use __hwasan_tag_mismatch4 with arguments that tell it the
+		size of access and load to report all tag mismatches.  */
+	  unsigned HOST_WIDE_INT size_in_bytes = tree_to_uhwi (size);
+	  unsigned size_indicator = (size_in_bytes > 16)
+	    ? 0xf
+	    : exact_log2 (size_in_bytes);
+	  unsigned access_info = (0x20 * recover_p)
+	    + (0x10 * store_p)
+	    + (size_indicator);
+	  tree long_pointer_type
+	    = build_pointer_type (long_long_unsigned_type_node);
+	  call = gimple_build_call (fun, 3,
+				    build_fold_addr_expr (shadow_var),
+				    build_int_cst (long_long_unsigned_type_node,
+						   access_info),
+				    build_int_cst (long_pointer_type,
+						   0),
+				    size);
+	}
+      else
+	{
+	  tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
+					&nargs);
+	  call = gimple_build_call (fun, 1,
+				    build_fold_addr_expr (shadow_var));
+	}
       gimple_set_location (call, gimple_location (use));
       gimple *call_to_insert = call;
 
@@ -3709,6 +4031,16 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/*  HWASAN  */
+static unsigned int
+hwasan_instrument (void)
+{
+  transform_statements ();
+  last_alloca_addr = NULL_TREE;
+
+  return 0;
+}
+
 void
 hwasan_record_base (rtx base)
 {
@@ -3727,6 +4059,15 @@ hwasan_current_tag ()
   return tag_offset;
 }
 
+rtx
+hwasan_base ()
+{
+  if (! hwasan_base_ptr)
+    hwasan_record_base (gen_reg_rtx (Pmode));
+
+  return hwasan_base_ptr;
+}
+
 void
 hwasan_increment_tag ()
 {
@@ -3858,6 +4199,12 @@ hwasan_emit_untag_frame (rtx dynamic, rtx vars, rtx_insn *before)
   do_pending_stack_adjust ();
   rtx_insn *insns = get_insns ();
   end_sequence ();
+
+  /* Clear the hash_map recording which variables are handled by HWASAN_MARK.
+     The only use in HWASAN is to decide which variables need to be tagged in
+     the prologue and which don't.  */
+  delete asan_handled_variables;
+  asan_handled_variables = NULL;
   return insns;
 }
 
@@ -3894,4 +4241,214 @@ hwasan_finish_file (void)
   flag_sanitize |= SANITIZE_HWADDRESS;
 }
 
+/* Construct a function tree for __hwasan_{load,store}{1,2,4,8,16,_n}.
+   IS_STORE is either 1 (for a store) or 0 (for a load).  */
+
+static tree
+hwasan_check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
+		   int *nargs)
+{
+  static enum built_in_function check[2][2][6]
+    = { { { BUILT_IN_HWASAN_LOAD1, BUILT_IN_HWASAN_LOAD2,
+	    BUILT_IN_HWASAN_LOAD4, BUILT_IN_HWASAN_LOAD8,
+	    BUILT_IN_HWASAN_LOAD16, BUILT_IN_HWASAN_LOADN },
+	  { BUILT_IN_HWASAN_STORE1, BUILT_IN_HWASAN_STORE2,
+	    BUILT_IN_HWASAN_STORE4, BUILT_IN_HWASAN_STORE8,
+	    BUILT_IN_HWASAN_STORE16, BUILT_IN_HWASAN_STOREN } },
+	{ { BUILT_IN_HWASAN_LOAD1_NOABORT,
+	    BUILT_IN_HWASAN_LOAD2_NOABORT,
+	    BUILT_IN_HWASAN_LOAD4_NOABORT,
+	    BUILT_IN_HWASAN_LOAD8_NOABORT,
+	    BUILT_IN_HWASAN_LOAD16_NOABORT,
+	    BUILT_IN_HWASAN_LOADN_NOABORT },
+	  { BUILT_IN_HWASAN_STORE1_NOABORT,
+	    BUILT_IN_HWASAN_STORE2_NOABORT,
+	    BUILT_IN_HWASAN_STORE4_NOABORT,
+	    BUILT_IN_HWASAN_STORE8_NOABORT,
+	    BUILT_IN_HWASAN_STORE16_NOABORT,
+	    BUILT_IN_HWASAN_STOREN_NOABORT } } };
+  if (size_in_bytes == -1)
+    {
+      *nargs = 2;
+      return builtin_decl_implicit (check[recover_p][is_store][5]);
+    }
+  *nargs = 1;
+  int size_log2 = exact_log2 (size_in_bytes);
+  return builtin_decl_implicit (check[recover_p][is_store][size_log2]);
+}
+
+
+bool
+hwasan_expand_check_ifn (gimple_stmt_iterator *iter, bool)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  bool recover_p;
+  if (flag_sanitize & SANITIZE_USER_HWADDRESS)
+    recover_p = (flag_sanitize_recover & SANITIZE_USER_HWADDRESS) != 0;
+  else
+    recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_HWADDRESS) != 0;
+
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_CHECK_LAST);
+  bool is_scalar_access = (flags & ASAN_CHECK_SCALAR_ACCESS) != 0;
+  bool is_store = (flags & ASAN_CHECK_STORE) != 0;
+  bool is_non_zero_len = (flags & ASAN_CHECK_NON_ZERO_LEN) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  tree len = gimple_call_arg (g, 2);
+
+  /* `align` is unused for HWASAN_CHECK, but I pass the argument anyway
+     since that way the arguments match ASAN_CHECK.  */
+  /* HOST_WIDE_INT align = tree_to_shwi (gimple_call_arg (g, 3));  */
+
+  unsigned HOST_WIDE_INT size_in_bytes
+    = is_scalar_access && tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
+
+  gimple_stmt_iterator gsi = *iter;
+
+  if (!is_non_zero_len)
+    {
+      /* So, the length of the memory area to hwasan-protect is
+	 non-constant.  Let's guard the generated instrumentation code
+	 like:
+
+	 if (len != 0)
+	   {
+	     // hwasan instrumentation code goes here.
+	   }
+	 // falltrough instructions, starting with *ITER.  */
+
+      g = gimple_build_cond (NE_EXPR,
+			    len,
+			    build_int_cst (TREE_TYPE (len), 0),
+			    NULL_TREE, NULL_TREE);
+      gimple_set_location (g, loc);
+
+      basic_block then_bb, fallthrough_bb;
+      insert_if_then_before_iter (as_a <gcond *> (g), iter,
+				  /*then_more_likely_p=*/true,
+				  &then_bb, &fallthrough_bb);
+      /* Note that fallthrough_bb starts with the statement that was
+	pointed to by ITER.  */
+
+      /* The 'then block' of the 'if (len != 0) condition is where
+	we'll generate the hwasan instrumentation code now.  */
+      gsi = gsi_last_bb (then_bb);
+    }
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			   NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+  tree base_addr = gimple_assign_lhs (g);
+
+  int nargs = 0;
+  tree fun = hwasan_check_func (is_store, recover_p, size_in_bytes, &nargs);
+  if (nargs == 1)
+    g = gimple_build_call (fun, 1, base_addr);
+  else
+    {
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			       NOP_EXPR, len);
+      gimple_set_location (g, loc);
+      gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+      g = gimple_build_call (fun, nargs, base_addr, sz_arg);
+    }
+
+  gimple_set_location (g, loc);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+  gsi_remove (iter, true);
+  *iter = gsi;
+  return false;
+}
+
+bool
+hwasan_expand_mark_ifn (gimple_stmt_iterator *)
+{
+  /* HWASAN_MARK should only ever be available after the sanopt pass.  */
+  gcc_unreachable ();
+}
+
+bool
+gate_hwasan ()
+{
+  return memory_tagging_p ();
+}
+
+namespace {
+
+const pass_data pass_data_hwasan =
+{
+  GIMPLE_PASS, /* type */
+  "hwasan", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_hwasan : public gimple_opt_pass
+{
+public:
+  pass_hwasan (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_hwasan, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_hwasan (m_ctxt); }
+  virtual bool gate (function *) { return gate_hwasan (); }
+  virtual unsigned int execute (function *) { return hwasan_instrument (); }
+
+}; /* class pass_asan  */
+
+} /* anon namespace  */
+
+gimple_opt_pass *
+make_pass_hwasan (gcc::context *ctxt)
+{
+  return new pass_hwasan (ctxt);
+}
+
+namespace {
+
+const pass_data pass_data_hwasan_O0 =
+{
+  GIMPLE_PASS, /* type */
+  "hwasan_O0", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_hwasan_O0 : public gimple_opt_pass
+{
+public:
+  pass_hwasan_O0 (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_hwasan_O0, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_hwasan_O0 (m_ctxt); }
+  virtual bool gate (function *) { return !optimize && gate_hwasan (); }
+  virtual unsigned int execute (function *) { return hwasan_instrument (); }
+
+}; // class pass_asan
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_hwasan_O0 (gcc::context *ctxt)
+{
+  return new pass_hwasan_O0 (ctxt);
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index d05f597b6434f39fe95d4f28dd2ef3ed463dd925..00592b1eea76164471e281c8922893937cf9bb2e 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -493,6 +493,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_INT_FEXCEPT_T_PTR_INT, BT_INT, BT_FEXCEPT_T_PTR,
 		     BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_FEXCEPT_T_PTR_INT, BT_INT,
 		     BT_CONST_FEXCEPT_T_PTR, BT_INT)
+DEF_FUNCTION_TYPE_2 (BT_FN_PTR_CONST_PTR_UINT8, BT_PTR, BT_CONST_PTR, BT_UINT8)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR)
 
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 2d5ece0680538389118b909951863be50d570326..dba1d58b83a1caf657cbfdd309a3576442fd59d0 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -750,6 +750,7 @@ dump_gimple_call_args (pretty_printer *buffer, gcall *gs, dump_flags_t flags)
 	  limit = ARRAY_SIZE (reduction_args);
 	  break;
 
+	case IFN_HWASAN_MARK:
 	case IFN_ASAN_MARK:
 #define DEF(X) #X
 	  static const char *const asan_mark_args[] = {IFN_ASAN_MARK_FLAGS};
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 12ed3f8eb2129f2a68a054347d23e6f3eae2197a..fa886c8f088c4109fee3c97859bad3ed9fc7e073 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1232,8 +1232,11 @@ asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
 
   /* It's necessary to have all stack variables aligned to ASAN granularity
      bytes.  */
-  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
-    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+  gcc_assert (!memory_tagging_p () || hwasan_sanitize_stack_p ());
+  unsigned shadow_granularity
+    = memory_tagging_p () ? HWASAN_TAG_GRANULE_SIZE : ASAN_SHADOW_GRANULARITY;
+  if (DECL_ALIGN_UNIT (decl) <= shadow_granularity)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * shadow_granularity);
 
   HOST_WIDE_INT flags = poison ? ASAN_MARK_POISON : ASAN_MARK_UNPOISON;
 
@@ -14673,7 +14676,7 @@ gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
-  if (asan_sanitize_use_after_scope () && sanitize_flags_p (SANITIZE_ADDRESS))
+  if (asan_sanitize_use_after_scope ())
     asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
   if (asan_poisoned_variables)
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 549d6f1153b6e568d1ab8b0e5b8fa8ccc2161b61..19b37629ab8500bb870cfcd135cefb0831a7e856 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -457,6 +457,74 @@ expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
+expand_HWASAN_CHECK (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
+
+static void
+expand_HWASAN_CHOOSE_TAG (internal_fn, gcall *gc)
+{
+  tree tag = gimple_call_lhs (gc);
+  rtx target = expand_expr (tag, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  machine_mode mode = GET_MODE (target);
+  gcc_assert (mode == QImode);
+
+  rtx base_tag = hwasan_extract_tag (hwasan_base ());
+  gcc_assert (base_tag);
+  rtx tag_offset = GEN_INT (hwasan_current_tag ());
+  rtx chosen_tag = expand_simple_binop (QImode, PLUS, base_tag, tag_offset,
+					target, /* unsignedp = */1,
+					OPTAB_WIDEN);
+
+  gcc_assert (chosen_tag);
+  /* Really need to put the tag into the `target` RTX.  */
+  if (chosen_tag != target)
+    {
+      rtx temp = chosen_tag;
+      machine_mode ret_mode = GET_MODE (chosen_tag);
+      if (ret_mode != mode)
+	temp = simplify_gen_unary (TRUNCATE, mode, chosen_tag, ret_mode);
+
+      emit_move_insn (target, temp);
+    }
+
+  hwasan_increment_tag ();
+}
+
+static void
+expand_HWASAN_MARK (internal_fn, gcall *gc)
+{
+  HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
+  bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
+
+  tree base = gimple_call_arg (gc, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  rtx base_rtx = expand_normal (base);
+
+  rtx tag = is_poison ? const0_rtx : hwasan_extract_tag (base_rtx);
+  rtx address = hwasan_create_untagged_base (base_rtx);
+
+  tree len = gimple_call_arg (gc, 2);
+  gcc_assert (tree_fits_shwi_p (len));
+  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
+  gcc_assert (size_in_bytes);
+  size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
+  rtx size = gen_int_mode (size_in_bytes, Pmode);
+
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (func,
+      LCT_NORMAL,
+      VOIDmode,
+      address, ptr_mode,
+      tag, QImode,
+      size, ptr_mode);
+}
+
+/* This should get expanded in the sanopt pass.  */
+
+static void
 expand_ASAN_CHECK (internal_fn, gcall *)
 {
   gcc_unreachable ();
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index a9459449fee15b17fe9c8ff1113b0305ed936bff..045747c35d012ae583e5e2c9c17e98a5d5cb5363 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -303,6 +303,9 @@ DEF_INTERNAL_FN (UBSAN_PTR, ECF_LEAF | ECF_NOTHROW, ".R.")
 DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
 DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (HWASAN_CHOOSE_TAG, ECF_LEAF | ECF_NOTHROW, ".")
+DEF_INTERNAL_FN (HWASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, "..R..")
+DEF_INTERNAL_FN (HWASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, "..R..")
 DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ASAN_POISON, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
diff --git a/gcc/passes.def b/gcc/passes.def
index 798a391bd351110d05b89fc6ccc424fd871198a4..e4c395f6b554be8f3c2e06aa4cfcda5d96130f99 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -246,6 +246,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_sink_code);
       NEXT_PASS (pass_sancov);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_hwasan);
       NEXT_PASS (pass_tsan);
       NEXT_PASS (pass_dce);
       /* Pass group that runs when 1) enabled, 2) there are loops
@@ -364,6 +365,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_dce);
       NEXT_PASS (pass_sancov);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_hwasan);
       NEXT_PASS (pass_tsan);
       /* ???  We do want some kind of loop invariant motion, but we possibly
          need to adjust LIM to be more friendly towards preserving accurate
@@ -389,6 +391,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_sancov_O0);
   NEXT_PASS (pass_lower_switch_O0);
   NEXT_PASS (pass_asan_O0);
+  NEXT_PASS (pass_hwasan_O0);
   NEXT_PASS (pass_tsan_O0);
   NEXT_PASS (pass_sanopt);
   NEXT_PASS (pass_cleanup_eh);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 7bd50715f24a2cb154b578e2abdea4e8fcdb2107..6cfb7951e21d4ee2f31921097796650afb02b7b0 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -183,6 +183,60 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 /* Hardware Address Sanitizer.  */
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1, "__hwasan_load1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2, "__hwasan_load2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4, "__hwasan_load4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8, "__hwasan_load8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16, "__hwasan_load16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN, "__hwasan_loadN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1, "__hwasan_store1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2, "__hwasan_store2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4, "__hwasan_store4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8, "__hwasan_store8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16, "__hwasan_store16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN, "__hwasan_storeN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1_NOABORT, "__hwasan_load1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2_NOABORT, "__hwasan_load2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4_NOABORT, "__hwasan_load4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8_NOABORT, "__hwasan_load8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16_NOABORT, "__hwasan_load16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN_NOABORT, "__hwasan_loadN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1_NOABORT, "__hwasan_store1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2_NOABORT, "__hwasan_store2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4_NOABORT, "__hwasan_store4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8_NOABORT, "__hwasan_store8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16_NOABORT, "__hwasan_store16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN_NOABORT, "__hwasan_storeN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MISMATCH4, "__hwasan_tag_mismatch4",
+		      BT_FN_VOID_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_HANDLE_LONGJMP, "__hwasan_handle_longjmp",
+		      BT_FN_VOID_CONST_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_PTR, "__hwasan_tag_pointer",
+		      BT_FN_PTR_CONST_PTR_UINT8, ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
 		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 00ade87283223798038e58c37e9138fe483de5a1..756e9ede899f8c87fdfc61277bdb79512107655c 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -773,7 +773,8 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
   basic_block son;
   gimple_stmt_iterator gsi;
   sanopt_info *info = (sanopt_info *) bb->aux;
-  bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0;
+  bool asan_check_optimize
+    = ((flag_sanitize & SANITIZE_ADDRESS) != 0) || memory_tagging_p ();
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
@@ -803,6 +804,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
       if (asan_check_optimize
 	  && gimple_call_builtin_p (stmt, BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT))
 	{
+	  gcc_assert (!memory_tagging_p ());
 	  use_operand_p use;
 	  gimple *use_stmt;
 	  if (single_imm_use (gimple_vdef (stmt), &use, &use_stmt))
@@ -831,6 +833,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
 	  case IFN_UBSAN_PTR:
 	    remove = maybe_optimize_ubsan_ptr_ifn (ctx, stmt);
 	    break;
+	  case IFN_HWASAN_CHECK:
 	  case IFN_ASAN_CHECK:
 	    if (asan_check_optimize)
 	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
@@ -1256,6 +1259,10 @@ sanitize_rewrite_addressable_params (function *fun)
 unsigned int
 pass_sanopt::execute (function *fun)
 {
+  /* n.b. ASAN_MARK is used for both HWASAN and ASAN.
+     asan_num_accesses is hence used to count either HWASAN_CHECK or ASAN_CHECK
+     stuff.  This is fine because you can only have one of these active at a
+     time.  */
   basic_block bb;
   int asan_num_accesses = 0;
   bool contains_asan_mark = false;
@@ -1263,10 +1270,10 @@ pass_sanopt::execute (function *fun)
   /* Try to remove redundant checks.  */
   if (optimize
       && (flag_sanitize
-	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT
+	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_HWADDRESS
 	     | SANITIZE_ADDRESS | SANITIZE_VPTR | SANITIZE_POINTER_OVERFLOW)))
     asan_num_accesses = sanopt_optimize (fun, &contains_asan_mark);
-  else if (flag_sanitize & SANITIZE_ADDRESS)
+  else if (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS))
     {
       gimple_stmt_iterator gsi;
       FOR_EACH_BB_FN (bb, fun)
@@ -1286,7 +1293,7 @@ pass_sanopt::execute (function *fun)
       sanitize_asan_mark_poison ();
     }
 
-  if (asan_sanitize_stack_p ())
+  if (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ())
     sanitize_rewrite_addressable_params (fun);
 
   bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
@@ -1328,6 +1335,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_UBSAN_VPTR:
 		  no_next = ubsan_expand_vptr_ifn (&gsi);
 		  break;
+		case IFN_HWASAN_CHECK:
+		  no_next = hwasan_expand_check_ifn (&gsi, use_calls);
+		  break;
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
@@ -1339,6 +1349,9 @@ pass_sanopt::execute (function *fun)
 						    &need_commit_edge_insert,
 						    shadow_vars_mapping);
 		  break;
+		case IFN_HWASAN_MARK:
+		  no_next = hwasan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 7bd75548d2aebb3415ac85ec40ad25e5ca794094..8fc4e9c9232e7ee4bc31558ef01bb2b1b9edfa27 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -508,7 +508,7 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
-      if (flag_sanitize & SANITIZE_HWADDRESS)
+      if (gate_hwasan ())
 	hwasan_finish_file ();
 
       omp_finish_file ();
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a987661530eafabc5f5eb5a4fae27dab8b2e54fb..bfcad7112d75c8460bc7bcdea545cad347928e30 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -341,6 +341,8 @@ extern void register_pass (opt_pass* pass, pass_positioning_ops pos,
 
 extern gimple_opt_pass *make_pass_asan (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_asan_O0 (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_hwasan (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_hwasan_O0 (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tsan (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tsan_O0 (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_sancov (gcc::context *ctxt);


[-- Attachment #2: hwasan-patch5.patch.gz --]
[-- Type: application/gzip, Size: 12947 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (23 preceding siblings ...)
  2019-11-07 18:37   ` [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
@ 2019-11-07 18:37   ` Matthew Malcomson
  2019-11-20 14:06     ` Martin Liška
  2019-11-07 18:37   ` [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes Matthew Malcomson
                     ` (4 subsequent siblings)
  29 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-07 18:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 40614 bytes --]

Handling stack variables has three features.

1) Ensure HWASAN required alignment for stack variables

When tagging shadow memory, we need to ensure that each tag granule is
only used by one variable at a time.

This is done by ensuring that each tagged variable is aligned to the tag
granule representation size and also ensure that the end of each
variable as an alignment boundary between the end and the start of any
other data stored on the stack.

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

2) Put tags into each stack variable pointer

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

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

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

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

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

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

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

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

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

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

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

gcc/ChangeLog:

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

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



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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -1,7 +1,11 @@
 # This option enables -fsanitize=hwaddress for stage2 and stage3.
+# We need to disable random frame tags for bootstrap since the autoconf check
+# for which direction the stack is growing has UB that a random frame tag
+# breaks.  Running with a random frame tag gives approx. 50% chance of
+# bootstrap comparison diff in libiberty/alloca.c.
 
-STAGE2_CFLAGS += -fsanitize=hwaddress
-STAGE3_CFLAGS += -fsanitize=hwaddress
+STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
 POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
diff --git a/gcc/asan.h b/gcc/asan.h
index 7675f18a84ee3f187ba4cb40db0ce232f3958762..467231f8dad031a6176aeaddb9414f768b2af3fc 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -23,6 +23,18 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void asan_function_start (void);
 extern void asan_finish_file (void);
+extern void hwasan_finish_file (void);
+extern void hwasan_record_base (rtx);
+extern uint8_t hwasan_current_tag ();
+extern void hwasan_increment_tag ();
+extern rtx hwasan_with_tag (rtx, poly_int64);
+extern void hwasan_tag_init ();
+extern rtx hwasan_create_untagged_base (rtx);
+extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
+extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx, rtx_insn *);
+extern bool memory_tagging_p (void);
+extern bool hwasan_sanitize_stack_p (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -75,6 +87,31 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The values below define an ABI and are hard-coded to these values in
+   libhwasan, hence they can't be changed independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).  */
+#define HWASAN_TAG_SIZE 8
+/* Tag Granule of HWASAN shadow stack.
+   This is the size in real memory that each byte in the shadow memory refers
+   to.  I.e. if a variable is X bytes long in memory then it's tag in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   Most variables will need to be aligned to this amount since two variables
+   that are neighbours in memory and share a tag granule would need to share
+   the same tag (the shared tag granule can only store one tag).  */
+#define HWASAN_TAG_SHIFT_SIZE 4
+#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SHIFT_SIZE)
+/* Define the tag for the stack background.
+   This defines what tag the stack pointer will be and hence what tag all
+   variables that are not given special tags are (e.g. spilled registers,
+   and parameters passed on the stack).  */
+#define HWASAN_STACK_BACKGROUND 0
+/* How many bits to shift in order to access the tag bits.
+   The tag is stored in the top 8 bits of a pointer hence shifting 56 bits will
+   leave just the tag.  */
+#define HWASAN_SHIFT 56
+#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/asan.c b/gcc/asan.c
index a731bd490b4e78e916ae20fc9a0249c1fc04daa5..2e79d39785467651c352169dae4551a47d7b3613 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -257,6 +257,9 @@ hash_set<tree> *asan_handled_variables = NULL;
 
 hash_set <tree> *asan_used_labels = NULL;
 
+static uint8_t tag_offset = 0;
+static rtx hwasan_base_ptr = NULL_RTX;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -1352,6 +1355,21 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+/* Returns whether we are tagging pointers and checking those tags on memory
+   access.  This is true when checking with either in software or hardware.  */
+bool
+memory_tagging_p ()
+{
+    return sanitize_flags_p (SANITIZE_HWADDRESS);
+}
+
+/* Are we tagging the stack?  */
+bool
+hwasan_sanitize_stack_p ()
+{
+  return (memory_tagging_p () && HWASAN_STACK);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -2884,6 +2902,11 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_VOID_PTR_UINT8_SIZE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				unsigned_char_type_node, size_type_node,
+				NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
@@ -2934,6 +2957,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NOTHROW_LIST
+#define ATTR_NOTHROW_LIST ECF_NOTHROW
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
@@ -3684,4 +3709,189 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+void
+hwasan_record_base (rtx base)
+{
+  /* Initialise tag of the base register.
+     This has to be done as soon as the stack is getting expanded to ensure
+     anything emitted with `get_dynamic_stack_base` will use the value set here
+     instead of using a register without a value.
+     Especially note that RTL expansion of large aligned values does that.  */
+  targetm.memtag.gentag (base, virtual_stack_vars_rtx);
+  hwasan_base_ptr = base;
+}
+
+uint8_t
+hwasan_current_tag ()
+{
+  return tag_offset;
+}
+
+void
+hwasan_increment_tag ()
+{
+  uint8_t tag_bits = HWASAN_TAG_SIZE;
+  tag_offset = (tag_offset + 1) % (1 << tag_bits);
+}
+
+rtx
+hwasan_with_tag (rtx base, poly_int64 offset)
+{
+  gcc_assert (tag_offset < (1 << HWASAN_TAG_SIZE));
+  return targetm.memtag.addtag (base, offset, tag_offset);
+}
+
+/* Clear internal state for the next function.
+   This function is called before variables on the stack get expanded, in
+   `init_vars_expansion`.  */
+void
+hwasan_tag_init ()
+{
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
+  hwasan_base_ptr = NULL_RTX;
+  tag_offset = HWASAN_STACK_BACKGROUND + 1;
+}
+
+rtx
+hwasan_extract_tag (rtx tagged_pointer)
+{
+  rtx tag = expand_simple_binop (Pmode,
+				 LSHIFTRT,
+				 tagged_pointer,
+				 HWASAN_SHIFT_RTX,
+				 NULL_RTX,
+				 /* unsignedp = */0,
+				 OPTAB_DIRECT);
+  return gen_lowpart (QImode, tag);
+}
+
+void
+hwasan_emit_prologue (rtx *bases,
+		      rtx *untagged_bases,
+		      poly_int64 *offsets,
+		      uint8_t *tags,
+		      size_t length)
+{
+  /* We need untagged base pointers since libhwasan only accepts untagged
+    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
+    the base tag for an offset.  */
+  for (size_t i = 0; (i * 2) + 1 < length; i++)
+    {
+      poly_int64 start = offsets[i * 2];
+      poly_int64 end = offsets[(i * 2) + 1];
+
+      poly_int64 bot, top;
+      if (known_ge (start, end))
+	{
+	  top = start;
+	  bot = end;
+	}
+      else
+	{
+	  top = end;
+	  bot = start;
+	}
+      poly_int64 size = (top - bot);
+
+      /* Can't check that all poly_int64's are aligned, but still nice
+	 to check those that are compile-time constants.  */
+      HOST_WIDE_INT tmp;
+      if (top.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (bot.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (size.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      rtx base_tag = hwasan_extract_tag (bases[i]);
+      /* In the case of tag overflow we would want modulo wrapping -- which
+	 should be given from the `plus_constant` in QImode.  */
+      rtx tag = plus_constant (QImode, base_tag, tags[i]);
+      emit_library_call (ret,
+			 LCT_NORMAL,
+			 VOIDmode,
+			 plus_constant (ptr_mode, untagged_bases[i], bot),
+			 ptr_mode,
+			 tag,
+			 QImode,
+			 gen_int_mode (size, ptr_mode),
+			 ptr_mode);
+    }
+}
+
+rtx_insn *
+hwasan_emit_untag_frame (rtx dynamic, rtx vars, rtx_insn *before)
+{
+  if (before)
+    push_to_sequence (before);
+  else
+    start_sequence ();
+
+  dynamic = convert_memory_address (ptr_mode, dynamic);
+  vars = convert_memory_address (ptr_mode, vars);
+
+  rtx top_rtx;
+  rtx bot_rtx;
+  if (STACK_GROWS_DOWNWARD)
+    {
+      top_rtx = vars;
+      bot_rtx = dynamic;
+    }
+  else
+    {
+      top_rtx = dynamic;
+      bot_rtx = vars;
+    }
+
+  rtx size_rtx = expand_simple_binop (Pmode, MINUS, top_rtx, bot_rtx,
+				  NULL_RTX, /* unsignedp = */0, OPTAB_DIRECT);
+
+  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (ret, LCT_NORMAL, VOIDmode,
+      bot_rtx, ptr_mode,
+      const0_rtx, QImode,
+      size_rtx, ptr_mode);
+
+  do_pending_stack_adjust ();
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+rtx
+hwasan_create_untagged_base (rtx orig_base)
+{
+  rtx untagged_base = gen_reg_rtx (Pmode);
+  rtx tag_mask = gen_int_mode ((1ULL << HWASAN_SHIFT) - 1, Pmode);
+  untagged_base = expand_binop (Pmode, and_optab,
+				orig_base, tag_mask,
+				untagged_base, true, OPTAB_DIRECT);
+  gcc_assert (untagged_base);
+  return untagged_base;
+}
+
+/* Needs to be GTY(()), because cgraph_build_static_cdtor may
+   invoke ggc_collect.  */
+static GTY(()) tree hwasan_ctor_statements;
+
+void
+hwasan_finish_file (void)
+{
+  /* Do not emit constructor initialisation for the kernel.
+     (the kernel has its own initialisation already).  */
+  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+    return;
+
+  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
+  flag_sanitize &= ~SANITIZE_HWADDRESS;
+  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
+  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
+  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
+  flag_sanitize |= SANITIZE_HWADDRESS;
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index e5c9e063c480d1392b6c2b395ec9d029b6d94209..d05f597b6434f39fe95d4f28dd2ef3ed463dd925 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -625,6 +625,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_UINT32_UINT32_PTR,
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
 		     BT_PTR)
 DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_SIZE, BT_VOID, BT_PTR, BT_UINT8,
+		     BT_SIZE)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index d8233f5f760f8426e8ff85473ecda02aa6c2655b..3f621ffdbda0acf5949348882a7c1f3504634666 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -244,6 +244,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
+				| SANITIZE_HWADDRESS \
 				| SANITIZE_UNDEFINED \
 				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index c34a53b526b50d49cd73ab5a5c383efc6da5a23e..8e1ea21621e350b3d8779b79b0d7f69d571caa08 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -378,7 +378,14 @@ align_local_variable (tree decl, bool really_expand)
       if (really_expand)
 	SET_DECL_ALIGN (decl, align);
     }
-  return align / BITS_PER_UNIT;
+
+  unsigned int ret_align = align / BITS_PER_UNIT;
+
+  if (hwasan_sanitize_stack_p ())
+    ret_align = ret_align > HWASAN_TAG_GRANULE_SIZE
+      ? ret_align
+      : HWASAN_TAG_GRANULE_SIZE;
+  return ret_align;
 }
 
 /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
@@ -986,7 +993,7 @@ dump_stack_var_partition (void)
 
 static void
 expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
-			 poly_int64 offset)
+			 poly_int64 offset, rtx stack_base)
 {
   unsigned align;
   rtx x;
@@ -994,7 +1001,11 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   /* If this fails, we've overflowed the stack frame.  Error nicely?  */
   gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
 
-  x = plus_constant (Pmode, base, offset);
+  if (hwasan_sanitize_stack_p ())
+    x = hwasan_with_tag (base, offset);
+  else
+    x = plus_constant (Pmode, base, offset);
+
   x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
 		   ? TYPE_MODE (TREE_TYPE (decl))
 		   : DECL_MODE (SSAVAR (decl)), x);
@@ -1004,7 +1015,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
       /* Set alignment we actually gave this decl if it isn't an SSA name.
          If it is we generate stack slots only accidentally so it isn't as
 	 important, we'll simply use the alignment that is already set.  */
-      if (base == virtual_stack_vars_rtx)
+      if (base == stack_base)
 	offset -= frame_phase;
       align = known_alignment (offset);
       align *= BITS_PER_UNIT;
@@ -1030,9 +1041,19 @@ public:
      The vector is in reversed, highest offset pairs come first.  */
   auto_vec<HOST_WIDE_INT> asan_vec;
 
+  /* HWASAN records the poly_int64 so it can handle any stack variable.  */
+  auto_vec<poly_int64> hwasan_vec;
+  auto_vec<rtx> hwasan_untagged_base_vec;
+  auto_vec<rtx> hwasan_base_vec;
+
   /* Vector of partition representative decls in between the paddings.  */
   auto_vec<tree> asan_decl_vec;
 
+  /* Vector of tag offsets representing the tag for each stack variable.
+     Each offset determines the difference between the randomly generated
+     tag for the current frame and the tag for this stack variable.  */
+  auto_vec<uint8_t> hwasan_tag_vec;
+
   /* Base pseudo register for Address Sanitizer protected automatic vars.  */
   rtx asan_base;
 
@@ -1050,6 +1071,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
   size_t si, i, j, n = stack_vars_num;
   poly_uint64 large_size = 0, large_alloc = 0;
   rtx large_base = NULL;
+  rtx large_untagged_base = NULL;
   unsigned large_align = 0;
   bool large_allocation_done = false;
   tree decl;
@@ -1096,11 +1118,17 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	}
     }
 
+  if (hwasan_sanitize_stack_p () && data->asan_base == NULL)
+    {
+      data->asan_base = gen_reg_rtx (Pmode);
+      hwasan_record_base (data->asan_base);
+    }
+
   for (si = 0; si < n; ++si)
     {
       rtx base;
       unsigned base_align, alignb;
-      poly_int64 offset;
+      poly_int64 offset = 0;
 
       i = stack_vars_sorted[si];
 
@@ -1121,10 +1149,36 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       if (pred && !pred (i))
 	continue;
 
+      base = hwasan_sanitize_stack_p ()
+	? data->asan_base
+	: virtual_stack_vars_rtx;
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  base = virtual_stack_vars_rtx;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Allocate zero bytes to take advantage of the
+		 alloc_stack_frame_space logic of ensuring the stack is aligned
+		 despite having poly_int64's to deal with.
+
+		 There must be no tag granule "shared" between different
+		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
+		 chunk can have more than one object in it.
+
+		 We ensure this by forcing the end of the last bit of data to
+		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
+		 the start of each variable to be aligned to
+		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
+
+		 We can't align just one of the start or end, since there are
+		 untagged things stored on the stack that we have no control on
+		 the alignment and these can't share a tag granule with a
+		 tagged variable.  */
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	      offset = alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (offset);
+	      data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
+	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
 	  HOST_WIDE_INT prev_offset;
@@ -1204,6 +1258,9 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
 	      base_align = crtl->max_used_stack_slot_alignment;
 	    }
+
+	  if (hwasan_sanitize_stack_p ())
+	    data->hwasan_vec.safe_push (offset);
 	}
       else
 	{
@@ -1223,14 +1280,31 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      loffset = alloc_stack_frame_space
 		(rtx_to_poly_int64 (large_allocsize),
 		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
-	      large_base = get_dynamic_stack_base (loffset, large_align);
+	      large_base = get_dynamic_stack_base (loffset, large_align, base);
 	      large_allocation_done = true;
 	    }
-	  gcc_assert (large_base != NULL);
 
+	  gcc_assert (large_base != NULL);
 	  large_alloc = aligned_upper_bound (large_alloc, alignb);
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* An object with a large alignment requirement means that the
+		 alignment requirement is greater than the required alignment
+		 for tags.  */
+	      if (!large_untagged_base)
+		large_untagged_base = hwasan_create_untagged_base (large_base);
+	      data->hwasan_vec.safe_push (large_alloc);
+	      data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
+	    }
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Ensure the end of the variable is also aligned correctly.  */
+	      poly_int64 align_again =
+		aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (align_again);
+	    }
 
 	  base = large_base;
 	  base_align = large_align;
@@ -1242,7 +1316,21 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	{
 	  expand_one_stack_var_at (stack_vars[j].decl,
 				   base, base_align,
-				   offset);
+				   offset,
+				   hwasan_sanitize_stack_p ()
+				   ? data->asan_base
+				   : virtual_stack_vars_rtx);
+	}
+
+      if (hwasan_sanitize_stack_p ())
+	{
+	  /* Record the tag for this object in `data` so the prologue knows
+	     what tag to put in the shadow memory during cfgexpand.c.
+	     Then increment the tag so that the next object has a different
+	     tag to this object.  */
+	  data->hwasan_base_vec.safe_push (base);
+	  data->hwasan_tag_vec.safe_push (hwasan_current_tag ());
+	  hwasan_increment_tag ();
 	}
     }
 
@@ -1339,7 +1427,8 @@ expand_one_stack_var_1 (tree var)
   offset = alloc_stack_frame_space (size, byte_align);
 
   expand_one_stack_var_at (var, virtual_stack_vars_rtx,
-			   crtl->max_used_stack_slot_alignment, offset);
+			   crtl->max_used_stack_slot_alignment, offset,
+			   virtual_stack_vars_rtx);
 }
 
 /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
@@ -1552,8 +1641,13 @@ defer_stack_allocation (tree var, bool toplevel)
 
   /* If stack protection is enabled, *all* stack variables must be deferred,
      so that we can re-order the strings to the top of the frame.
-     Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || asan_sanitize_stack_p ())
+     Similarly for Address Sanitizer.
+     When tagging memory we defer all stack variables so we can handle them in
+     one place (handle here meaning ensure they are aligned and record
+     information on each variables position in the stack).  */
+  if (flag_stack_protect
+      || asan_sanitize_stack_p ()
+      || hwasan_sanitize_stack_p ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME
@@ -1938,6 +2032,8 @@ init_vars_expansion (void)
   /* Initialize local stack smashing state.  */
   has_protected_decls = false;
   has_short_buffer = false;
+  if (hwasan_sanitize_stack_p ())
+    hwasan_tag_init ();
 }
 
 /* Free up stack variable graph data.  */
@@ -2297,12 +2393,27 @@ expand_used_vars (void)
 	}
 
       expand_stack_vars (NULL, &data);
+
+      if (hwasan_sanitize_stack_p ())
+	hwasan_emit_prologue (data.hwasan_base_vec.address (),
+			      data.hwasan_untagged_base_vec.address (),
+			      data.hwasan_vec.address (),
+			      data.hwasan_tag_vec.address (),
+			      data.hwasan_vec.length ());
     }
 
   if (asan_sanitize_allocas_p () && cfun->calls_alloca)
     var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
 					      virtual_stack_vars_rtx,
 					      var_end_seq);
+  /* Here we clear tags fro the entire frame of this function.
+     We need to clear tags of *something* if we have tagged either local
+     variables or alloca objects.  */
+  else if (hwasan_sanitize_stack_p ()
+	   && (cfun->calls_alloca || stack_vars_num > 0))
+    var_end_seq = hwasan_emit_untag_frame (virtual_stack_dynamic_rtx,
+					   virtual_stack_vars_rtx,
+					   var_end_seq);
 
   fini_vars_expansion ();
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index e842b734c9c20253986880ba3622a8f692d3ca88..718d2e8aac56553bdc30c592fe70fa10aa911736 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2982,6 +2982,31 @@ A target hook which lets a backend compute the set of pressure classes to  be us
 True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that -fsanitize=hwaddress can work.
 @end deftypefn
 
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
+Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by TAG_OFFSET.
+The resulting RTX must either be a valid memory address or be able to get
+put into an operand with force_operand.  If overridden the more common case
+is that we force this into an operand using the backend hook
+"addtag_force_operand" that is called in force_operand.
+
+It is expected that that "addtag_force_operand" recognises the RTX
+generated by "addtag" and emits code to force that RTX into an operand.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG_FORCE_OPERAND (rtx @var{oper}, rtx @var{target})
+If the RTL expression OPER is of the form generated by targetm.memtag.addtag,
+then emit instructions to move the value into an operand (i.e. for
+force_operand).
+TARGET is an RTX suggestion of where to generate the value.
+This hook is most often implemented by emitting instructions to put the
+expression into a pseudo register, then returning that pseudo register.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_MEMTAG_GENTAG (rtx @var{base}, rtx @var{untagged})
+Set the BASE argument to UNTAGGED with some random tag.
+This function is used to generate a tagged base for the current stack frame.
+@end deftypefn
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 6136ac1a5fe0c0980d5b5123b67b102a1b1e0bcc..3d3761dbc097e6f73f4f4f937aa713b93872a4a2 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2379,6 +2379,12 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_ADDTAG
+
+@hook TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
+
+@hook TARGET_MEMTAG_GENTAG
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/explow.h b/gcc/explow.h
index 5110ad82d6a024fda1d3a3eaf80de40c5e6ad3b6..333948e0c69a1b1132e9a1d06707dc63f1226262 100644
--- a/gcc/explow.h
+++ b/gcc/explow.h
@@ -102,7 +102,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
 extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
 
 /* Returns the address of the dynamic stack space without allocating it.  */
-extern rtx get_dynamic_stack_base (poly_int64, unsigned);
+extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
 
 /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
 extern rtx align_dynamic_address (rtx, unsigned);
diff --git a/gcc/explow.c b/gcc/explow.c
index 7eb854bca4a6dcc5b15e5c42df1a5e88a19f2464..2728f7a4b1ee8ed0d20287b716a3d0ad5a97d84b 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1577,10 +1577,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
    OFFSET is the offset of the area into the virtual stack vars area.
 
    REQUIRED_ALIGN is the alignment (in bits) required for the region
-   of memory.  */
+   of memory.
+
+   BASE is the rtx of the base of this virtual stack vars area.
+   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+   on the stack.  */
 
 rtx
-get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
 {
   rtx target;
 
@@ -1588,7 +1592,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
     crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
 
   target = gen_reg_rtx (Pmode);
-  emit_move_insn (target, virtual_stack_vars_rtx);
+  emit_move_insn (target, base);
   target = expand_binop (Pmode, add_optab, target,
 			 gen_int_mode (offset, Pmode),
 			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
diff --git a/gcc/expr.c b/gcc/expr.c
index 476c6865f20828fc68f455e70d4874eaabd9d08d..24d011e698af0dbf3635ba5f9d8275376a124bf4 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -7500,6 +7500,13 @@ force_operand (rtx value, rtx target)
       return subtarget;
     }
 
+  if (targetm.memtag.addtag_force_operand)
+    {
+      rtx ret = targetm.memtag.addtag_force_operand (value, target);
+      if (ret)
+	return ret;
+    }
+
   if (ARITHMETIC_P (value))
     {
       op2 = XEXP (value, 1);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 374d15007d868363d9b4fbf467e1e462abbca61a..7bd50715f24a2cb154b578e2abdea4e8fcdb2107 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
+/* Hardware Address Sanitizer.  */
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
+		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
+
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/target.def b/gcc/target.def
index ad16a151b6af9b1ee13918c8f2980280d75b1d90..3c533acbe3965cdb0870621e364a009353f72c2e 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6751,6 +6751,37 @@ DEFHOOK
  pointers.  This feature means that -fsanitize=hwaddress can work.",
  bool, (), default_memtag_can_tag_addresses)
 
+DEFHOOK
+(addtag,
+ "Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by\
+ TAG_OFFSET.\n\
+The resulting RTX must either be a valid memory address or be able to get\n\
+put into an operand with force_operand.  If overridden the more common case\n\
+is that we force this into an operand using the backend hook\n\
+\"addtag_force_operand\" that is called in force_operand.\n\
+\n\
+It is expected that that \"addtag_force_operand\" recognises the RTX\n\
+generated by \"addtag\" and emits code to force that RTX into an operand.",
+rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
+default_memtag_addtag)
+
+DEFHOOK
+(addtag_force_operand,
+ "If the RTL expression OPER is of the form generated by targetm.memtag.addtag,\n\
+then emit instructions to move the value into an operand (i.e. for\n\
+force_operand).\n\
+TARGET is an RTX suggestion of where to generate the value.\n\
+This hook is most often implemented by emitting instructions to put the\n\
+expression into a pseudo register, then returning that pseudo register.",
+rtx, (rtx oper, rtx target), NULL)
+
+DEFHOOK
+(gentag,
+ "Set the BASE argument to UNTAGGED with some random tag.\n\
+This function is used to generate a tagged base for the current stack frame.",
+  void, (rtx base, rtx untagged),
+  default_memtag_gentag)
+
 HOOK_VECTOR_END (memtag)
 #undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 94e865259f35e46e26f6b4763c5e2f9dc9ed1b83..b0e32102acacdf7a64f1e3d314a966d1d3f062c7 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -283,4 +283,6 @@ extern bool speculation_safe_value_not_needed (bool);
 extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 
 extern bool default_memtag_can_tag_addresses ();
+extern void default_memtag_gentag (rtx, rtx);
+extern rtx default_memtag_addtag (rtx, poly_int64, uint8_t);
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 6e9e877c32e2c8705056bdf0ce2b1b6f125d93c3..cd9f98fc800d7232ead50b03f951364f76c01adc 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "flags.h"
 #include "explow.h"
+#include "expmed.h"
 #include "calls.h"
 #include "expr.h"
 #include "output.h"
@@ -84,6 +85,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "sbitmap.h"
 #include "function-abi.h"
+#include "attribs.h"
+#include "asan.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -2371,4 +2374,78 @@ default_memtag_can_tag_addresses ()
   return false;
 }
 
+void
+default_memtag_gentag (rtx base, rtx untagged)
+{
+  gcc_assert (HWASAN_STACK);
+  if (HWASAN_RANDOM_FRAME_TAG)
+    {
+    rtx temp = gen_reg_rtx (QImode);
+    rtx ret = init_one_libfunc ("__hwasan_generate_tag");
+    rtx new_tag = emit_library_call_value (ret, temp, LCT_NORMAL, QImode);
+    emit_move_insn (base, untagged);
+    /* We know that `base` is not the stack pointer, since we never want to put
+      a randomly generated tag into the stack pointer.  Hence we can use
+      `store_bit_field` which on aarch64 generates a `bfi` which can not act on
+      the stack pointer.  */
+    store_bit_field (base, 8, 56, 0, 0, QImode, new_tag, false);
+    }
+  else
+    {
+      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
+	 In the future we may add the option emit random tags with inline
+	 instrumentation instead of function calls.  This would be the same
+	 between the kernel and userland.  */
+      emit_move_insn (base, untagged);
+    }
+}
+
+rtx
+default_memtag_addtag (rtx base, poly_int64 offset, uint8_t tag_offset)
+{
+  /* Need to look into what the most efficient code sequence is.
+     This is a code sequence that would be emitted *many* times, so we
+     want it as small as possible.
+
+     If the tag offset is greater that (1 << 7) then the most efficient
+     sequence here would give UB from signed integer overflow in the
+     poly_int64.  Hence in that case we emit the slightly less efficient
+     sequence.
+
+     There are two places where tag overflow is a question:
+       - Tagging the shadow stack.
+	  (both tagging and untagging).
+       - Tagging addressable pointers.
+
+     We need to ensure both behaviours are the same (i.e. that the tag that
+     ends up in a pointer after "overflowing" the tag bits with a tag addition
+     is the same that ends up in the shadow space).
+
+     The aim is that the behaviour of tag addition should follow modulo
+     wrapping in both instances.
+
+     The libhwasan code doesn't have any path that increments a pointers tag,
+     which means it has no opinion on what happens when a tag increment
+     overflows (and hence we can choose our own behaviour).  */
+
+  if (tag_offset < (1 << 7))
+    {
+      offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
+      return plus_constant (Pmode, base, offset);
+    }
+  else
+    {
+      /* This is the fallback, it would be nice if it had less instructions,
+	 but we can look for cleverer ways later.  */
+      uint64_t tag_mask = ~(0xFFUL << HWASAN_SHIFT);
+      rtx untagged_base = gen_rtx_AND (Pmode, GEN_INT (tag_mask), base);
+      rtx new_addr = plus_constant (Pmode, untagged_base, offset);
+
+      rtx original_tag_value = gen_rtx_LSHIFTRT (Pmode, base, GEN_INT (HWASAN_SHIFT));
+      rtx new_tag_value = plus_constant (Pmode, original_tag_value, tag_offset);
+      rtx new_tag = gen_rtx_ASHIFT (Pmode, new_tag_value, GEN_INT (HWASAN_SHIFT));
+      return gen_rtx_IOR (Pmode, new_addr, new_tag);
+    }
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index ab67384249a3437ac37f42f741ed516884677f9f..7bd75548d2aebb3415ac85ec40ad25e5ca794094 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -508,6 +508,9 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      if (flag_sanitize & SANITIZE_HWADDRESS)
+	hwasan_finish_file ();
+
       omp_finish_file ();
 
       hsa_output_brig ();


[-- Attachment #2: hwasan-patch4.patch --]
[-- Type: text/plain, Size: 34041 bytes --]

diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -1,7 +1,11 @@
 # This option enables -fsanitize=hwaddress for stage2 and stage3.
+# We need to disable random frame tags for bootstrap since the autoconf check
+# for which direction the stack is growing has UB that a random frame tag
+# breaks.  Running with a random frame tag gives approx. 50% chance of
+# bootstrap comparison diff in libiberty/alloca.c.
 
-STAGE2_CFLAGS += -fsanitize=hwaddress
-STAGE3_CFLAGS += -fsanitize=hwaddress
+STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
 POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
diff --git a/gcc/asan.h b/gcc/asan.h
index 7675f18a84ee3f187ba4cb40db0ce232f3958762..467231f8dad031a6176aeaddb9414f768b2af3fc 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -23,6 +23,18 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void asan_function_start (void);
 extern void asan_finish_file (void);
+extern void hwasan_finish_file (void);
+extern void hwasan_record_base (rtx);
+extern uint8_t hwasan_current_tag ();
+extern void hwasan_increment_tag ();
+extern rtx hwasan_with_tag (rtx, poly_int64);
+extern void hwasan_tag_init ();
+extern rtx hwasan_create_untagged_base (rtx);
+extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
+extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx, rtx_insn *);
+extern bool memory_tagging_p (void);
+extern bool hwasan_sanitize_stack_p (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -75,6 +87,31 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The values below define an ABI and are hard-coded to these values in
+   libhwasan, hence they can't be changed independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).  */
+#define HWASAN_TAG_SIZE 8
+/* Tag Granule of HWASAN shadow stack.
+   This is the size in real memory that each byte in the shadow memory refers
+   to.  I.e. if a variable is X bytes long in memory then it's tag in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   Most variables will need to be aligned to this amount since two variables
+   that are neighbours in memory and share a tag granule would need to share
+   the same tag (the shared tag granule can only store one tag).  */
+#define HWASAN_TAG_SHIFT_SIZE 4
+#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SHIFT_SIZE)
+/* Define the tag for the stack background.
+   This defines what tag the stack pointer will be and hence what tag all
+   variables that are not given special tags are (e.g. spilled registers,
+   and parameters passed on the stack).  */
+#define HWASAN_STACK_BACKGROUND 0
+/* How many bits to shift in order to access the tag bits.
+   The tag is stored in the top 8 bits of a pointer hence shifting 56 bits will
+   leave just the tag.  */
+#define HWASAN_SHIFT 56
+#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/asan.c b/gcc/asan.c
index a731bd490b4e78e916ae20fc9a0249c1fc04daa5..2e79d39785467651c352169dae4551a47d7b3613 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -257,6 +257,9 @@ hash_set<tree> *asan_handled_variables = NULL;
 
 hash_set <tree> *asan_used_labels = NULL;
 
+static uint8_t tag_offset = 0;
+static rtx hwasan_base_ptr = NULL_RTX;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -1352,6 +1355,21 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+/* Returns whether we are tagging pointers and checking those tags on memory
+   access.  This is true when checking with either in software or hardware.  */
+bool
+memory_tagging_p ()
+{
+    return sanitize_flags_p (SANITIZE_HWADDRESS);
+}
+
+/* Are we tagging the stack?  */
+bool
+hwasan_sanitize_stack_p ()
+{
+  return (memory_tagging_p () && HWASAN_STACK);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -2884,6 +2902,11 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_VOID_PTR_UINT8_SIZE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				unsigned_char_type_node, size_type_node,
+				NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
@@ -2934,6 +2957,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NOTHROW_LIST
+#define ATTR_NOTHROW_LIST ECF_NOTHROW
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
@@ -3684,4 +3709,189 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+void
+hwasan_record_base (rtx base)
+{
+  /* Initialise tag of the base register.
+     This has to be done as soon as the stack is getting expanded to ensure
+     anything emitted with `get_dynamic_stack_base` will use the value set here
+     instead of using a register without a value.
+     Especially note that RTL expansion of large aligned values does that.  */
+  targetm.memtag.gentag (base, virtual_stack_vars_rtx);
+  hwasan_base_ptr = base;
+}
+
+uint8_t
+hwasan_current_tag ()
+{
+  return tag_offset;
+}
+
+void
+hwasan_increment_tag ()
+{
+  uint8_t tag_bits = HWASAN_TAG_SIZE;
+  tag_offset = (tag_offset + 1) % (1 << tag_bits);
+}
+
+rtx
+hwasan_with_tag (rtx base, poly_int64 offset)
+{
+  gcc_assert (tag_offset < (1 << HWASAN_TAG_SIZE));
+  return targetm.memtag.addtag (base, offset, tag_offset);
+}
+
+/* Clear internal state for the next function.
+   This function is called before variables on the stack get expanded, in
+   `init_vars_expansion`.  */
+void
+hwasan_tag_init ()
+{
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
+  hwasan_base_ptr = NULL_RTX;
+  tag_offset = HWASAN_STACK_BACKGROUND + 1;
+}
+
+rtx
+hwasan_extract_tag (rtx tagged_pointer)
+{
+  rtx tag = expand_simple_binop (Pmode,
+				 LSHIFTRT,
+				 tagged_pointer,
+				 HWASAN_SHIFT_RTX,
+				 NULL_RTX,
+				 /* unsignedp = */0,
+				 OPTAB_DIRECT);
+  return gen_lowpart (QImode, tag);
+}
+
+void
+hwasan_emit_prologue (rtx *bases,
+		      rtx *untagged_bases,
+		      poly_int64 *offsets,
+		      uint8_t *tags,
+		      size_t length)
+{
+  /* We need untagged base pointers since libhwasan only accepts untagged
+    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
+    the base tag for an offset.  */
+  for (size_t i = 0; (i * 2) + 1 < length; i++)
+    {
+      poly_int64 start = offsets[i * 2];
+      poly_int64 end = offsets[(i * 2) + 1];
+
+      poly_int64 bot, top;
+      if (known_ge (start, end))
+	{
+	  top = start;
+	  bot = end;
+	}
+      else
+	{
+	  top = end;
+	  bot = start;
+	}
+      poly_int64 size = (top - bot);
+
+      /* Can't check that all poly_int64's are aligned, but still nice
+	 to check those that are compile-time constants.  */
+      HOST_WIDE_INT tmp;
+      if (top.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (bot.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (size.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      rtx base_tag = hwasan_extract_tag (bases[i]);
+      /* In the case of tag overflow we would want modulo wrapping -- which
+	 should be given from the `plus_constant` in QImode.  */
+      rtx tag = plus_constant (QImode, base_tag, tags[i]);
+      emit_library_call (ret,
+			 LCT_NORMAL,
+			 VOIDmode,
+			 plus_constant (ptr_mode, untagged_bases[i], bot),
+			 ptr_mode,
+			 tag,
+			 QImode,
+			 gen_int_mode (size, ptr_mode),
+			 ptr_mode);
+    }
+}
+
+rtx_insn *
+hwasan_emit_untag_frame (rtx dynamic, rtx vars, rtx_insn *before)
+{
+  if (before)
+    push_to_sequence (before);
+  else
+    start_sequence ();
+
+  dynamic = convert_memory_address (ptr_mode, dynamic);
+  vars = convert_memory_address (ptr_mode, vars);
+
+  rtx top_rtx;
+  rtx bot_rtx;
+  if (STACK_GROWS_DOWNWARD)
+    {
+      top_rtx = vars;
+      bot_rtx = dynamic;
+    }
+  else
+    {
+      top_rtx = dynamic;
+      bot_rtx = vars;
+    }
+
+  rtx size_rtx = expand_simple_binop (Pmode, MINUS, top_rtx, bot_rtx,
+				  NULL_RTX, /* unsignedp = */0, OPTAB_DIRECT);
+
+  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (ret, LCT_NORMAL, VOIDmode,
+      bot_rtx, ptr_mode,
+      const0_rtx, QImode,
+      size_rtx, ptr_mode);
+
+  do_pending_stack_adjust ();
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+rtx
+hwasan_create_untagged_base (rtx orig_base)
+{
+  rtx untagged_base = gen_reg_rtx (Pmode);
+  rtx tag_mask = gen_int_mode ((1ULL << HWASAN_SHIFT) - 1, Pmode);
+  untagged_base = expand_binop (Pmode, and_optab,
+				orig_base, tag_mask,
+				untagged_base, true, OPTAB_DIRECT);
+  gcc_assert (untagged_base);
+  return untagged_base;
+}
+
+/* Needs to be GTY(()), because cgraph_build_static_cdtor may
+   invoke ggc_collect.  */
+static GTY(()) tree hwasan_ctor_statements;
+
+void
+hwasan_finish_file (void)
+{
+  /* Do not emit constructor initialisation for the kernel.
+     (the kernel has its own initialisation already).  */
+  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+    return;
+
+  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
+  flag_sanitize &= ~SANITIZE_HWADDRESS;
+  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
+  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
+  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
+  flag_sanitize |= SANITIZE_HWADDRESS;
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index e5c9e063c480d1392b6c2b395ec9d029b6d94209..d05f597b6434f39fe95d4f28dd2ef3ed463dd925 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -625,6 +625,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_UINT32_UINT32_PTR,
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
 		     BT_PTR)
 DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_SIZE, BT_VOID, BT_PTR, BT_UINT8,
+		     BT_SIZE)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index d8233f5f760f8426e8ff85473ecda02aa6c2655b..3f621ffdbda0acf5949348882a7c1f3504634666 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -244,6 +244,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
+				| SANITIZE_HWADDRESS \
 				| SANITIZE_UNDEFINED \
 				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index c34a53b526b50d49cd73ab5a5c383efc6da5a23e..8e1ea21621e350b3d8779b79b0d7f69d571caa08 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -378,7 +378,14 @@ align_local_variable (tree decl, bool really_expand)
       if (really_expand)
 	SET_DECL_ALIGN (decl, align);
     }
-  return align / BITS_PER_UNIT;
+
+  unsigned int ret_align = align / BITS_PER_UNIT;
+
+  if (hwasan_sanitize_stack_p ())
+    ret_align = ret_align > HWASAN_TAG_GRANULE_SIZE
+      ? ret_align
+      : HWASAN_TAG_GRANULE_SIZE;
+  return ret_align;
 }
 
 /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
@@ -986,7 +993,7 @@ dump_stack_var_partition (void)
 
 static void
 expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
-			 poly_int64 offset)
+			 poly_int64 offset, rtx stack_base)
 {
   unsigned align;
   rtx x;
@@ -994,7 +1001,11 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   /* If this fails, we've overflowed the stack frame.  Error nicely?  */
   gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
 
-  x = plus_constant (Pmode, base, offset);
+  if (hwasan_sanitize_stack_p ())
+    x = hwasan_with_tag (base, offset);
+  else
+    x = plus_constant (Pmode, base, offset);
+
   x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
 		   ? TYPE_MODE (TREE_TYPE (decl))
 		   : DECL_MODE (SSAVAR (decl)), x);
@@ -1004,7 +1015,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
       /* Set alignment we actually gave this decl if it isn't an SSA name.
          If it is we generate stack slots only accidentally so it isn't as
 	 important, we'll simply use the alignment that is already set.  */
-      if (base == virtual_stack_vars_rtx)
+      if (base == stack_base)
 	offset -= frame_phase;
       align = known_alignment (offset);
       align *= BITS_PER_UNIT;
@@ -1030,9 +1041,19 @@ public:
      The vector is in reversed, highest offset pairs come first.  */
   auto_vec<HOST_WIDE_INT> asan_vec;
 
+  /* HWASAN records the poly_int64 so it can handle any stack variable.  */
+  auto_vec<poly_int64> hwasan_vec;
+  auto_vec<rtx> hwasan_untagged_base_vec;
+  auto_vec<rtx> hwasan_base_vec;
+
   /* Vector of partition representative decls in between the paddings.  */
   auto_vec<tree> asan_decl_vec;
 
+  /* Vector of tag offsets representing the tag for each stack variable.
+     Each offset determines the difference between the randomly generated
+     tag for the current frame and the tag for this stack variable.  */
+  auto_vec<uint8_t> hwasan_tag_vec;
+
   /* Base pseudo register for Address Sanitizer protected automatic vars.  */
   rtx asan_base;
 
@@ -1050,6 +1071,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
   size_t si, i, j, n = stack_vars_num;
   poly_uint64 large_size = 0, large_alloc = 0;
   rtx large_base = NULL;
+  rtx large_untagged_base = NULL;
   unsigned large_align = 0;
   bool large_allocation_done = false;
   tree decl;
@@ -1096,11 +1118,17 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	}
     }
 
+  if (hwasan_sanitize_stack_p () && data->asan_base == NULL)
+    {
+      data->asan_base = gen_reg_rtx (Pmode);
+      hwasan_record_base (data->asan_base);
+    }
+
   for (si = 0; si < n; ++si)
     {
       rtx base;
       unsigned base_align, alignb;
-      poly_int64 offset;
+      poly_int64 offset = 0;
 
       i = stack_vars_sorted[si];
 
@@ -1121,10 +1149,36 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       if (pred && !pred (i))
 	continue;
 
+      base = hwasan_sanitize_stack_p ()
+	? data->asan_base
+	: virtual_stack_vars_rtx;
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  base = virtual_stack_vars_rtx;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Allocate zero bytes to take advantage of the
+		 alloc_stack_frame_space logic of ensuring the stack is aligned
+		 despite having poly_int64's to deal with.
+
+		 There must be no tag granule "shared" between different
+		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
+		 chunk can have more than one object in it.
+
+		 We ensure this by forcing the end of the last bit of data to
+		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
+		 the start of each variable to be aligned to
+		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
+
+		 We can't align just one of the start or end, since there are
+		 untagged things stored on the stack that we have no control on
+		 the alignment and these can't share a tag granule with a
+		 tagged variable.  */
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	      offset = alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (offset);
+	      data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
+	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
 	  HOST_WIDE_INT prev_offset;
@@ -1204,6 +1258,9 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
 	      base_align = crtl->max_used_stack_slot_alignment;
 	    }
+
+	  if (hwasan_sanitize_stack_p ())
+	    data->hwasan_vec.safe_push (offset);
 	}
       else
 	{
@@ -1223,14 +1280,31 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      loffset = alloc_stack_frame_space
 		(rtx_to_poly_int64 (large_allocsize),
 		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
-	      large_base = get_dynamic_stack_base (loffset, large_align);
+	      large_base = get_dynamic_stack_base (loffset, large_align, base);
 	      large_allocation_done = true;
 	    }
-	  gcc_assert (large_base != NULL);
 
+	  gcc_assert (large_base != NULL);
 	  large_alloc = aligned_upper_bound (large_alloc, alignb);
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* An object with a large alignment requirement means that the
+		 alignment requirement is greater than the required alignment
+		 for tags.  */
+	      if (!large_untagged_base)
+		large_untagged_base = hwasan_create_untagged_base (large_base);
+	      data->hwasan_vec.safe_push (large_alloc);
+	      data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
+	    }
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Ensure the end of the variable is also aligned correctly.  */
+	      poly_int64 align_again =
+		aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (align_again);
+	    }
 
 	  base = large_base;
 	  base_align = large_align;
@@ -1242,7 +1316,21 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	{
 	  expand_one_stack_var_at (stack_vars[j].decl,
 				   base, base_align,
-				   offset);
+				   offset,
+				   hwasan_sanitize_stack_p ()
+				   ? data->asan_base
+				   : virtual_stack_vars_rtx);
+	}
+
+      if (hwasan_sanitize_stack_p ())
+	{
+	  /* Record the tag for this object in `data` so the prologue knows
+	     what tag to put in the shadow memory during cfgexpand.c.
+	     Then increment the tag so that the next object has a different
+	     tag to this object.  */
+	  data->hwasan_base_vec.safe_push (base);
+	  data->hwasan_tag_vec.safe_push (hwasan_current_tag ());
+	  hwasan_increment_tag ();
 	}
     }
 
@@ -1339,7 +1427,8 @@ expand_one_stack_var_1 (tree var)
   offset = alloc_stack_frame_space (size, byte_align);
 
   expand_one_stack_var_at (var, virtual_stack_vars_rtx,
-			   crtl->max_used_stack_slot_alignment, offset);
+			   crtl->max_used_stack_slot_alignment, offset,
+			   virtual_stack_vars_rtx);
 }
 
 /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
@@ -1552,8 +1641,13 @@ defer_stack_allocation (tree var, bool toplevel)
 
   /* If stack protection is enabled, *all* stack variables must be deferred,
      so that we can re-order the strings to the top of the frame.
-     Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || asan_sanitize_stack_p ())
+     Similarly for Address Sanitizer.
+     When tagging memory we defer all stack variables so we can handle them in
+     one place (handle here meaning ensure they are aligned and record
+     information on each variables position in the stack).  */
+  if (flag_stack_protect
+      || asan_sanitize_stack_p ()
+      || hwasan_sanitize_stack_p ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME
@@ -1938,6 +2032,8 @@ init_vars_expansion (void)
   /* Initialize local stack smashing state.  */
   has_protected_decls = false;
   has_short_buffer = false;
+  if (hwasan_sanitize_stack_p ())
+    hwasan_tag_init ();
 }
 
 /* Free up stack variable graph data.  */
@@ -2297,12 +2393,27 @@ expand_used_vars (void)
 	}
 
       expand_stack_vars (NULL, &data);
+
+      if (hwasan_sanitize_stack_p ())
+	hwasan_emit_prologue (data.hwasan_base_vec.address (),
+			      data.hwasan_untagged_base_vec.address (),
+			      data.hwasan_vec.address (),
+			      data.hwasan_tag_vec.address (),
+			      data.hwasan_vec.length ());
     }
 
   if (asan_sanitize_allocas_p () && cfun->calls_alloca)
     var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
 					      virtual_stack_vars_rtx,
 					      var_end_seq);
+  /* Here we clear tags fro the entire frame of this function.
+     We need to clear tags of *something* if we have tagged either local
+     variables or alloca objects.  */
+  else if (hwasan_sanitize_stack_p ()
+	   && (cfun->calls_alloca || stack_vars_num > 0))
+    var_end_seq = hwasan_emit_untag_frame (virtual_stack_dynamic_rtx,
+					   virtual_stack_vars_rtx,
+					   var_end_seq);
 
   fini_vars_expansion ();
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index e842b734c9c20253986880ba3622a8f692d3ca88..718d2e8aac56553bdc30c592fe70fa10aa911736 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2982,6 +2982,31 @@ A target hook which lets a backend compute the set of pressure classes to  be us
 True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that -fsanitize=hwaddress can work.
 @end deftypefn
 
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
+Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by TAG_OFFSET.
+The resulting RTX must either be a valid memory address or be able to get
+put into an operand with force_operand.  If overridden the more common case
+is that we force this into an operand using the backend hook
+"addtag_force_operand" that is called in force_operand.
+
+It is expected that that "addtag_force_operand" recognises the RTX
+generated by "addtag" and emits code to force that RTX into an operand.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG_FORCE_OPERAND (rtx @var{oper}, rtx @var{target})
+If the RTL expression OPER is of the form generated by targetm.memtag.addtag,
+then emit instructions to move the value into an operand (i.e. for
+force_operand).
+TARGET is an RTX suggestion of where to generate the value.
+This hook is most often implemented by emitting instructions to put the
+expression into a pseudo register, then returning that pseudo register.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_MEMTAG_GENTAG (rtx @var{base}, rtx @var{untagged})
+Set the BASE argument to UNTAGGED with some random tag.
+This function is used to generate a tagged base for the current stack frame.
+@end deftypefn
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 6136ac1a5fe0c0980d5b5123b67b102a1b1e0bcc..3d3761dbc097e6f73f4f4f937aa713b93872a4a2 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2379,6 +2379,12 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_ADDTAG
+
+@hook TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
+
+@hook TARGET_MEMTAG_GENTAG
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/explow.h b/gcc/explow.h
index 5110ad82d6a024fda1d3a3eaf80de40c5e6ad3b6..333948e0c69a1b1132e9a1d06707dc63f1226262 100644
--- a/gcc/explow.h
+++ b/gcc/explow.h
@@ -102,7 +102,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
 extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
 
 /* Returns the address of the dynamic stack space without allocating it.  */
-extern rtx get_dynamic_stack_base (poly_int64, unsigned);
+extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
 
 /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
 extern rtx align_dynamic_address (rtx, unsigned);
diff --git a/gcc/explow.c b/gcc/explow.c
index 7eb854bca4a6dcc5b15e5c42df1a5e88a19f2464..2728f7a4b1ee8ed0d20287b716a3d0ad5a97d84b 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1577,10 +1577,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
    OFFSET is the offset of the area into the virtual stack vars area.
 
    REQUIRED_ALIGN is the alignment (in bits) required for the region
-   of memory.  */
+   of memory.
+
+   BASE is the rtx of the base of this virtual stack vars area.
+   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+   on the stack.  */
 
 rtx
-get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
 {
   rtx target;
 
@@ -1588,7 +1592,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
     crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
 
   target = gen_reg_rtx (Pmode);
-  emit_move_insn (target, virtual_stack_vars_rtx);
+  emit_move_insn (target, base);
   target = expand_binop (Pmode, add_optab, target,
 			 gen_int_mode (offset, Pmode),
 			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
diff --git a/gcc/expr.c b/gcc/expr.c
index 476c6865f20828fc68f455e70d4874eaabd9d08d..24d011e698af0dbf3635ba5f9d8275376a124bf4 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -7500,6 +7500,13 @@ force_operand (rtx value, rtx target)
       return subtarget;
     }
 
+  if (targetm.memtag.addtag_force_operand)
+    {
+      rtx ret = targetm.memtag.addtag_force_operand (value, target);
+      if (ret)
+	return ret;
+    }
+
   if (ARITHMETIC_P (value))
     {
       op2 = XEXP (value, 1);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 374d15007d868363d9b4fbf467e1e462abbca61a..7bd50715f24a2cb154b578e2abdea4e8fcdb2107 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
+/* Hardware Address Sanitizer.  */
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
+		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
+
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/target.def b/gcc/target.def
index ad16a151b6af9b1ee13918c8f2980280d75b1d90..3c533acbe3965cdb0870621e364a009353f72c2e 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6751,6 +6751,37 @@ DEFHOOK
  pointers.  This feature means that -fsanitize=hwaddress can work.",
  bool, (), default_memtag_can_tag_addresses)
 
+DEFHOOK
+(addtag,
+ "Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by\
+ TAG_OFFSET.\n\
+The resulting RTX must either be a valid memory address or be able to get\n\
+put into an operand with force_operand.  If overridden the more common case\n\
+is that we force this into an operand using the backend hook\n\
+\"addtag_force_operand\" that is called in force_operand.\n\
+\n\
+It is expected that that \"addtag_force_operand\" recognises the RTX\n\
+generated by \"addtag\" and emits code to force that RTX into an operand.",
+rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
+default_memtag_addtag)
+
+DEFHOOK
+(addtag_force_operand,
+ "If the RTL expression OPER is of the form generated by targetm.memtag.addtag,\n\
+then emit instructions to move the value into an operand (i.e. for\n\
+force_operand).\n\
+TARGET is an RTX suggestion of where to generate the value.\n\
+This hook is most often implemented by emitting instructions to put the\n\
+expression into a pseudo register, then returning that pseudo register.",
+rtx, (rtx oper, rtx target), NULL)
+
+DEFHOOK
+(gentag,
+ "Set the BASE argument to UNTAGGED with some random tag.\n\
+This function is used to generate a tagged base for the current stack frame.",
+  void, (rtx base, rtx untagged),
+  default_memtag_gentag)
+
 HOOK_VECTOR_END (memtag)
 #undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 94e865259f35e46e26f6b4763c5e2f9dc9ed1b83..b0e32102acacdf7a64f1e3d314a966d1d3f062c7 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -283,4 +283,6 @@ extern bool speculation_safe_value_not_needed (bool);
 extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 
 extern bool default_memtag_can_tag_addresses ();
+extern void default_memtag_gentag (rtx, rtx);
+extern rtx default_memtag_addtag (rtx, poly_int64, uint8_t);
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 6e9e877c32e2c8705056bdf0ce2b1b6f125d93c3..cd9f98fc800d7232ead50b03f951364f76c01adc 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "flags.h"
 #include "explow.h"
+#include "expmed.h"
 #include "calls.h"
 #include "expr.h"
 #include "output.h"
@@ -84,6 +85,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "sbitmap.h"
 #include "function-abi.h"
+#include "attribs.h"
+#include "asan.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -2371,4 +2374,78 @@ default_memtag_can_tag_addresses ()
   return false;
 }
 
+void
+default_memtag_gentag (rtx base, rtx untagged)
+{
+  gcc_assert (HWASAN_STACK);
+  if (HWASAN_RANDOM_FRAME_TAG)
+    {
+    rtx temp = gen_reg_rtx (QImode);
+    rtx ret = init_one_libfunc ("__hwasan_generate_tag");
+    rtx new_tag = emit_library_call_value (ret, temp, LCT_NORMAL, QImode);
+    emit_move_insn (base, untagged);
+    /* We know that `base` is not the stack pointer, since we never want to put
+      a randomly generated tag into the stack pointer.  Hence we can use
+      `store_bit_field` which on aarch64 generates a `bfi` which can not act on
+      the stack pointer.  */
+    store_bit_field (base, 8, 56, 0, 0, QImode, new_tag, false);
+    }
+  else
+    {
+      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
+	 In the future we may add the option emit random tags with inline
+	 instrumentation instead of function calls.  This would be the same
+	 between the kernel and userland.  */
+      emit_move_insn (base, untagged);
+    }
+}
+
+rtx
+default_memtag_addtag (rtx base, poly_int64 offset, uint8_t tag_offset)
+{
+  /* Need to look into what the most efficient code sequence is.
+     This is a code sequence that would be emitted *many* times, so we
+     want it as small as possible.
+
+     If the tag offset is greater that (1 << 7) then the most efficient
+     sequence here would give UB from signed integer overflow in the
+     poly_int64.  Hence in that case we emit the slightly less efficient
+     sequence.
+
+     There are two places where tag overflow is a question:
+       - Tagging the shadow stack.
+	  (both tagging and untagging).
+       - Tagging addressable pointers.
+
+     We need to ensure both behaviours are the same (i.e. that the tag that
+     ends up in a pointer after "overflowing" the tag bits with a tag addition
+     is the same that ends up in the shadow space).
+
+     The aim is that the behaviour of tag addition should follow modulo
+     wrapping in both instances.
+
+     The libhwasan code doesn't have any path that increments a pointers tag,
+     which means it has no opinion on what happens when a tag increment
+     overflows (and hence we can choose our own behaviour).  */
+
+  if (tag_offset < (1 << 7))
+    {
+      offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
+      return plus_constant (Pmode, base, offset);
+    }
+  else
+    {
+      /* This is the fallback, it would be nice if it had less instructions,
+	 but we can look for cleverer ways later.  */
+      uint64_t tag_mask = ~(0xFFUL << HWASAN_SHIFT);
+      rtx untagged_base = gen_rtx_AND (Pmode, GEN_INT (tag_mask), base);
+      rtx new_addr = plus_constant (Pmode, untagged_base, offset);
+
+      rtx original_tag_value = gen_rtx_LSHIFTRT (Pmode, base, GEN_INT (HWASAN_SHIFT));
+      rtx new_tag_value = plus_constant (Pmode, original_tag_value, tag_offset);
+      rtx new_tag = gen_rtx_ASHIFT (Pmode, new_tag_value, GEN_INT (HWASAN_SHIFT));
+      return gen_rtx_IOR (Pmode, new_addr, new_tag);
+    }
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index ab67384249a3437ac37f42f741ed516884677f9f..7bd75548d2aebb3415ac85ec40ad25e5ca794094 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -508,6 +508,9 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      if (flag_sanitize & SANITIZE_HWADDRESS)
+	hwasan_finish_file ();
+
       omp_finish_file ();
 
       hsa_output_brig ();


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (22 preceding siblings ...)
  2019-11-07 18:37   ` [PATCH 4/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
@ 2019-11-07 18:37   ` Matthew Malcomson
  2019-11-11 14:30     ` Martin Liška
  2019-11-07 18:37   ` [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN Matthew Malcomson
                     ` (5 subsequent siblings)
  29 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-07 18:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 5908 bytes --]

This is an analogous option to --bootstrap-asan to configure.  It allows
bootstrapping GCC using HWASAN.

For the same reasons as for ASAN we have to avoid using the HWASAN
sanitizer when compiling libiberty and the lto-plugin.

Also add a function to query whether -fsanitize=hwaddress has been
passed.

ChangeLog:

2019-08-29  Matthew Malcomson  <matthew.malcomson@arm.com>

	* configure: Regenerate.
	* configure.ac: Add --bootstrap-hwasan option.

config/ChangeLog:

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

	* bootstrap-hwasan.mk: New file.

libiberty/ChangeLog:

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

	* configure: Regenerate.
	* configure.ac: Avoid using sanitizer.

lto-plugin/ChangeLog:

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

	* Makefile.am: Avoid using sanitizer.
	* Makefile.in: Regenerate.



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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index aec9186b2b0123d3088b69eb1ee541567654953e..6f71b111bd18ec053180beecf83dd4549e83c2b9 100755
--- a/configure
+++ b/configure
@@ -7270,7 +7270,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index b8ce2ad20b9d03e42731252a9ec2a8417c13e566..16bfdf164555dad94c789f17b6a63ba1a2e3e9f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2775,7 +2775,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index 6c9579bfaff955eb43875b404fb7db1a667bf522..427a2f4e56b37e165b72cc166e1acb0732449a8b 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2645,6 +2645,11 @@ Some examples of build configurations designed for developers of GCC are:
 Compiles GCC itself using Address Sanitization in order to catch invalid memory
 accesses within the GCC code.
 
+@item @samp{bootstrap-hwasan}
+Compiles GCC itself using HWAddress Sanitization in order to catch invalid
+memory accesses within the GCC code.  This option is only available on AArch64
+targets with a very recent linux kernel (5.4 or later).
+
 @section Building a cross compiler
 
 When building a cross compiler, it is not generally possible to do a
diff --git a/libiberty/configure b/libiberty/configure
index 7a34dabec32b0b383bd33f07811757335f4dd39c..cb2dd4ff5295598343cc18b3a79a86a778f2261d 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5261,6 +5261,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index f1ce76010c9acde79c5dc46686a78b2e2f19244e..043237628b79cbf37d07359b59c5ffe17a7a22ef 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index 28dc21014b2e86988fa88adabd63ce6092e18e02..34aa397d785e3cc9b6975de460d065900364c3ff 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 8dd6e40ac9dddab39fe1752f9a70e6834ab3c926..7acfc047eff6f86f8d38287e6ffb6533c4c13500 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -672,8 +672,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


[-- Attachment #2: hwasan-patch2.patch --]
[-- Type: text/plain, Size: 4805 bytes --]

diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index aec9186b2b0123d3088b69eb1ee541567654953e..6f71b111bd18ec053180beecf83dd4549e83c2b9 100755
--- a/configure
+++ b/configure
@@ -7270,7 +7270,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index b8ce2ad20b9d03e42731252a9ec2a8417c13e566..16bfdf164555dad94c789f17b6a63ba1a2e3e9f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2775,7 +2775,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index 6c9579bfaff955eb43875b404fb7db1a667bf522..427a2f4e56b37e165b72cc166e1acb0732449a8b 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2645,6 +2645,11 @@ Some examples of build configurations designed for developers of GCC are:
 Compiles GCC itself using Address Sanitization in order to catch invalid memory
 accesses within the GCC code.
 
+@item @samp{bootstrap-hwasan}
+Compiles GCC itself using HWAddress Sanitization in order to catch invalid
+memory accesses within the GCC code.  This option is only available on AArch64
+targets with a very recent linux kernel (5.4 or later).
+
 @section Building a cross compiler
 
 When building a cross compiler, it is not generally possible to do a
diff --git a/libiberty/configure b/libiberty/configure
index 7a34dabec32b0b383bd33f07811757335f4dd39c..cb2dd4ff5295598343cc18b3a79a86a778f2261d 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5261,6 +5261,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index f1ce76010c9acde79c5dc46686a78b2e2f19244e..043237628b79cbf37d07359b59c5ffe17a7a22ef 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index 28dc21014b2e86988fa88adabd63ce6092e18e02..34aa397d785e3cc9b6975de460d065900364c3ff 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 8dd6e40ac9dddab39fe1752f9a70e6834ab3c926..7acfc047eff6f86f8d38287e6ffb6533c4c13500 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -672,8 +672,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


^ permalink raw reply	[flat|nested] 150+ messages in thread

* v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-05 11:32 ` Matthew Malcomson
@ 2019-11-07 18:37 Matthew Malcomson
  2019-11-05 11:32 ` Matthew Malcomson
                   ` (2 more replies)
  29 siblings, 3 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-07 18:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov, Martin Liska,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

[-- Attachment #1: Type: text/plain, Size: 1122 bytes --]

I have rebased this series onto Martin Liska's patches that take the most
recent libhwasan from upstream LLVM.
https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00340.html

I've also cleared up some nomenclature (I had previously used the word 'colour'
a few times instead of the word 'tag' and that clashes with other descriptions)
and based the patch series off a more recent GCC revision (r277678).

There's an ongoing discussion on whether to have __SANITIZER_ADDRESS__, or
__SANITIZER_HWADDRESS__, but I'm keeping that discussion to the existing
thread.

Similarly there's still the question around C++ exceptions that I'm keeping to
the existing thread (on the first patch series).


NOTE:
  Unfortunately, there's a bug in the more recent version of GCC I rebased
  onto.
  Hwasan catches this when bootstrapping, which means bootstrapping with hwasan
  fails.
  I'm working on tracking the bug down now, but sending this series upstream
  for visibility while that happens.

  Bugzilla link:
  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92410

Entire patch series attached to cover letter.

[-- Attachment #2: all-patches.tar.gz --]
[-- Type: application/gzip, Size: 54817 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 4/X] [libsanitizer][options] Add hwasan flags and argument parsing
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (21 preceding siblings ...)
  2019-11-05 18:30   ` Joseph Myers
@ 2019-11-07 18:37   ` Matthew Malcomson
  2019-11-20 11:22     ` Martin Liška
  2019-11-07 18:37   ` [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
                     ` (6 subsequent siblings)
  29 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-07 18:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 22676 bytes --]

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

The -fsanitize=kernel-hwaddress option is for compiling targeting the
kernel.  This flag has defaults that allow compiling KASAN with tags as
it is currently implemented.
These defaults are that we do not sanitize variables on the stack and
always recover from a detected bug.
Stack tagging in the kernel is a future aim, stack instrumentation has
not yet been enabled for the kernel for clang either
(https://lists.infradead.org/pipermail/linux-arm-kernel/2019-October/687121.html).

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

gcc/ChangeLog:

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

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

gcc/c-family/ChangeLog:

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

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



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


diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 1c9f28587fbb2348cc30e302e889a5a22906901a..a5e68061ff956018957b6be137a7b2f2b7353647 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -54,6 +54,8 @@ static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_sanitize_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_sanitize_address_attribute (tree *, tree, tree,
 						  int, bool *);
+static tree handle_no_sanitize_hwaddress_attribute (tree *, tree, tree,
+						    int, bool *);
 static tree handle_no_sanitize_thread_attribute (tree *, tree, tree,
 						 int, bool *);
 static tree handle_no_address_safety_analysis_attribute (tree *, tree, tree,
@@ -412,6 +414,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_no_sanitize_attribute, NULL },
   { "no_sanitize_address",    0, 0, true, false, false, false,
 			      handle_no_sanitize_address_attribute, NULL },
+  { "no_sanitize_hwaddress",    0, 0, true, false, false, false,
+			      handle_no_sanitize_hwaddress_attribute, NULL },
   { "no_sanitize_thread",     0, 0, true, false, false, false,
 			      handle_no_sanitize_thread_attribute, NULL },
   { "no_sanitize_undefined",  0, 0, true, false, false, false,
@@ -941,6 +945,22 @@ handle_no_sanitize_address_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+/* Handle a "no_sanitize_hwaddress" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_no_sanitize_hwaddress_attribute (tree *node, tree name, tree, int,
+				      bool *no_add_attrs)
+{
+  *no_add_attrs = true;
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
+  else
+    add_no_sanitize_value (*node, SANITIZE_HWADDRESS);
+
+  return NULL_TREE;
+}
+
 /* Handle a "no_sanitize_thread" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/common.opt b/gcc/common.opt
index cc279f411d7967209a111d958437595ce5cacd3a..d44295530474f873a00bc97a7d46bdd26255db9c 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -215,7 +215,7 @@ unsigned int flag_sanitize
 
 ; What sanitizers should recover from errors
 Variable
-unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
+unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS | SANITIZE_KERNEL_HWADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
 
 ; What the coverage sanitizers should instrument
 Variable
@@ -3313,6 +3313,9 @@ Driver
 static-libasan
 Driver
 
+static-libhwasan
+Driver
+
 static-libtsan
 Driver
 
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 599d07a729e7438080f8b5240ee95037a49fb983..e0c7e2c6276d91bd0efe5e9bf9b58b09087c3cd7 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -21371,6 +21371,15 @@ aarch64_stack_protect_guard (void)
   return NULL_TREE;
 }
 
+/* Implement TARGET_MEMTAG_CAN_TAG_ADDRESSES.  Here we tell the rest of the
+   compiler that we automatically ignore the top byte of our pointers, which
+   allows using -fsanitize=hwaddress.  */
+bool
+aarch64_can_tag_addresses ()
+{
+  return true;
+}
+
 /* Implement TARGET_ASM_FILE_END for AArch64.  This adds the AArch64 GNU NOTE
    section at the end if needed.  */
 #define GNU_PROPERTY_AARCH64_FEATURE_1_AND	0xc0000000
@@ -21936,6 +21945,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_FNTYPE_ABI
 #define TARGET_FNTYPE_ABI aarch64_fntype_abi
 
+#undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
+#define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h
index 95a3c29f7cee86336f958bef1d7fe56b82e05e6c..90b1fa91742c6a7d76aa6c7e931f8014fc4fff0c 100644
--- a/gcc/config/gnu-user.h
+++ b/gcc/config/gnu-user.h
@@ -129,14 +129,18 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Link -lasan early on the command line.  For -static-libasan, don't link
    it for -shared link, the executable should be compiled with -static-libasan
    in that case, and for executable link with --{,no-}whole-archive around
-   it to force everything into the executable.  And similarly for -ltsan
-   and -llsan.  */
+   it to force everything into the executable.  And similarly for -ltsan,
+   -lhwasan, and -llsan.  */
 #if defined(HAVE_LD_STATIC_DYNAMIC)
 #undef LIBASAN_EARLY_SPEC
 #define LIBASAN_EARLY_SPEC "%{!shared:libasan_preinit%O%s} " \
   "%{static-libasan:%{!shared:" \
   LD_STATIC_OPTION " --whole-archive -lasan --no-whole-archive " \
   LD_DYNAMIC_OPTION "}}%{!static-libasan:-lasan}"
+#undef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC "%{static-libhwasan:%{!shared:" \
+  LD_STATIC_OPTION " --whole-archive -lhwasan --no-whole-archive " \
+  LD_DYNAMIC_OPTION "}}%{!static-libhwasan:-lhwasan}"
 #undef LIBTSAN_EARLY_SPEC
 #define LIBTSAN_EARLY_SPEC "%{!shared:libtsan_preinit%O%s} " \
   "%{static-libtsan:%{!shared:" \
diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c
index 60e5bedc3665a25fa51c2eca00547f12a9953778..e8d0bedfc2eb22d1e72e7e4875155202c8389a38 100644
--- a/gcc/cppbuiltin.c
+++ b/gcc/cppbuiltin.c
@@ -93,6 +93,9 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile)
   if (flag_sanitize & SANITIZE_ADDRESS)
     cpp_define (pfile, "__SANITIZE_ADDRESS__");
 
+  if (flag_sanitize & SANITIZE_HWADDRESS)
+    cpp_define (pfile, "__SANITIZE_HWADDRESS__");
+
   if (flag_sanitize & SANITIZE_THREAD)
     cpp_define (pfile, "__SANITIZE_THREAD__");
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d1eb317f43cd31156c80ef7dd910024c50f33deb..f710e29549f5d3d58ed74aca74f2c62b577cd155 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -12594,13 +12594,33 @@ more details.  The run-time behavior can be influenced using the
 the available options are shown at startup of the instrumented program.  See
 @url{https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags}
 for a list of supported options.
-The option cannot be combined with @option{-fsanitize=thread}.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=hwaddress}.
 
 @item -fsanitize=kernel-address
 @opindex fsanitize=kernel-address
 Enable AddressSanitizer for Linux kernel.
 See @uref{https://github.com/google/kasan/wiki} for more details.
 
+@item -fsanitize=hwaddress
+@opindex fsanitize=hwaddress
+Enable HardwareAddressSanitizer, a fast memory error detector.
+Memory access instructions are instrumented to detect out-of-bounds and
+use-after-free bugs.
+The option enables @option{-fsanitize-address-use-after-scope}.
+See @uref{https://github.com/google/sanitizers/} for
+more details.  The run-time behavior can be influenced using the
+@env{HWASAN_OPTIONS} environment variable.  When set to @code{help=1},
+the available options are shown at startup of the instrumented program.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=address}.
+
+@item -fsanitize=kernel-hwaddress
+@opindex fsanitize=kernel-hwaddress
+Enable HardwareAddressSanitizer for Linux kernel.
+Similar to @option{-fsanitize=kernel-address} but using the software tagging
+method of instrumentation.
+
 @item -fsanitize=pointer-compare
 @opindex fsanitize=pointer-compare
 Instrument comparison operation (<, <=, >, >=) with pointer operands.
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index cd9aed9874f4e6b2b0e2f8956ed6155975e643a8..e842b734c9c20253986880ba3622a8f692d3ca88 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2978,6 +2978,10 @@ This hook defines the machine mode to use for the boolean result of  conditional
 A target hook which lets a backend compute the set of pressure classes to  be used by those optimization passes which take register pressure into  account, as opposed to letting IRA compute them.  It returns the number of  register classes stored in the array @var{pressure_classes}.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_MEMTAG_CAN_TAG_ADDRESSES ()
+True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that -fsanitize=hwaddress can work.
+@end deftypefn
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 2739e9ceec5ad7253ff9135da8dbe3bf6010e8d7..6136ac1a5fe0c0980d5b5123b67b102a1b1e0bcc 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2377,6 +2377,8 @@ in the reload pass.
 
 @hook TARGET_COMPUTE_PRESSURE_CLASSES
 
+@hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index a2103282d469db31ad157a87572068d943061c8c..57d8ff9a1a010409d966230140df1017bc3584a8 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -256,6 +256,9 @@ enum sanitize_code {
   SANITIZE_BUILTIN = 1UL << 25,
   SANITIZE_POINTER_COMPARE = 1UL << 26,
   SANITIZE_POINTER_SUBTRACT = 1UL << 27,
+  SANITIZE_HWADDRESS = 1UL << 28,
+  SANITIZE_USER_HWADDRESS = 1UL << 29,
+  SANITIZE_KERNEL_HWADDRESS = 1UL << 30,
   SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
diff --git a/gcc/gcc.c b/gcc/gcc.c
index c45a1df656b2b8122f457edeb55db5f44c57b5bf..037f9d3a688d8778b067aade698d40709ee97b3d 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -708,6 +708,24 @@ proper position among the other output files.  */
 #define LIBASAN_EARLY_SPEC ""
 #endif
 
+#ifndef LIBHWASAN_SPEC
+#define STATIC_LIBHWASAN_LIBS \
+  " %{static-libhwasan|static:%:include(libsanitizer.spec)%(link_libhwasan)}"
+#ifdef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_SPEC STATIC_LIBHWASAN_LIBS
+#elif defined(HAVE_LD_STATIC_DYNAMIC)
+#define LIBHWASAN_SPEC "%{static-libhwasan:" LD_STATIC_OPTION \
+		     "} -lhwasan %{static-libhwasan:" LD_DYNAMIC_OPTION "}" \
+		     STATIC_LIBHWASAN_LIBS
+#else
+#define LIBHWASAN_SPEC "-lhwasan" STATIC_LIBHWASAN_LIBS
+#endif
+#endif
+
+#ifndef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC ""
+#endif
+
 #ifndef LIBTSAN_SPEC
 #define STATIC_LIBTSAN_LIBS \
   " %{static-libtsan|static:%:include(libsanitizer.spec)%(link_libtsan)}"
@@ -982,6 +1000,7 @@ proper position among the other output files.  */
 #ifndef SANITIZER_EARLY_SPEC
 #define SANITIZER_EARLY_SPEC "\
 %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_EARLY_SPEC "} \
+    %{%:sanitize(hwaddress):" LIBHWASAN_EARLY_SPEC "} \
     %{%:sanitize(thread):" LIBTSAN_EARLY_SPEC "} \
     %{%:sanitize(leak):" LIBLSAN_EARLY_SPEC "}}}}"
 #endif
@@ -991,6 +1010,8 @@ proper position among the other output files.  */
 #define SANITIZER_SPEC "\
 %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_SPEC "\
     %{static:%ecannot specify -static with -fsanitize=address}}\
+    %{%:sanitize(hwaddress):" LIBHWASAN_SPEC "\
+	%{static:%ecannot specify -static with -fsanitize=hwaddress}}\
     %{%:sanitize(thread):" LIBTSAN_SPEC "\
     %{static:%ecannot specify -static with -fsanitize=thread}}\
     %{%:sanitize(undefined):" LIBUBSAN_SPEC "}\
@@ -9439,8 +9460,12 @@ sanitize_spec_function (int argc, const char **argv)
 
   if (strcmp (argv[0], "address") == 0)
     return (flag_sanitize & SANITIZE_USER_ADDRESS) ? "" : NULL;
+  if (strcmp (argv[0], "hwaddress") == 0)
+    return (flag_sanitize & SANITIZE_USER_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "kernel-address") == 0)
     return (flag_sanitize & SANITIZE_KERNEL_ADDRESS) ? "" : NULL;
+  if (strcmp (argv[0], "kernel-hwaddress") == 0)
+    return (flag_sanitize & SANITIZE_KERNEL_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "thread") == 0)
     return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
   if (strcmp (argv[0], "undefined") == 0)
diff --git a/gcc/opts.c b/gcc/opts.c
index 10b9f108f8d0618501c1567bb4f8a7f9de2bdb98..b46c6d44164c04aa7e561fa3dc2513d66ce61e4d 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1119,6 +1119,13 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 		  "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>");
     }
 
+  /* Userspace and kernel HWasan conflict with each other.  */
+  if ((opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS))
+    error_at (loc,
+	      "%<-fsanitize=hwaddress%> is incompatible with "
+	      "%<-fsanitize=kernel-hwaddress%>");
+
   /* Userspace and kernel ASan conflict with each other.  */
   if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
       && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
@@ -1138,6 +1145,20 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
     error_at (loc,
 	      "%<-fsanitize=leak%> is incompatible with %<-fsanitize=thread%>");
 
+  /* HWASan and ASan conflict with each other.  */
+  if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_HWADDRESS))
+    error_at (loc,
+	      "%<-fsanitize=hwaddress%> is incompatible with both "
+	      "%<-fsanitize=address%> and %<-fsanitize=kernel-address%>");
+
+  /* HWASan conflicts with TSan.  */
+  if ((opts->x_flag_sanitize & SANITIZE_HWADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_THREAD))
+    error_at (loc,
+	      "%<-fsanitize=hwaddress%> is incompatible with "
+	      "%<-fsanitize=thread%>");
+
   /* Check error recovery for -fsanitize-recover option.  */
   for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
     if ((opts->x_flag_sanitize_recover & sanitizer_opts[i].flag)
@@ -1157,7 +1178,8 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 
   /* Enable -fsanitize-address-use-after-scope if address sanitizer is
      enabled.  */
-  if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+  if (((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+       || (opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS))
       && !opts_set->x_flag_sanitize_address_use_after_scope)
     opts->x_flag_sanitize_address_use_after_scope = true;
 
@@ -1786,8 +1808,13 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 #define SANITIZER_OPT(name, flags, recover) \
     { #name, flags, sizeof #name - 1, recover }
   SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true),
+  SANITIZER_OPT (hwaddress, (SANITIZE_HWADDRESS | SANITIZE_USER_HWADDRESS),
+		 true),
   SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
 		 true),
+  SANITIZER_OPT (kernel-hwaddress,
+		 (SANITIZE_HWADDRESS | SANITIZE_KERNEL_HWADDRESS),
+		 true),
   SANITIZER_OPT (pointer-compare, SANITIZE_POINTER_COMPARE, true),
   SANITIZER_OPT (pointer-subtract, SANITIZE_POINTER_SUBTRACT, true),
   SANITIZER_OPT (thread, SANITIZE_THREAD, false),
@@ -2322,6 +2349,14 @@ common_handle_option (struct gcc_options *opts,
 				 opts->x_param_values,
 				 opts_set->x_param_values);
 	}
+      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+	{
+	  maybe_set_param_value (PARAM_HWASAN_STACK, 0, opts->x_param_values,
+				 opts_set->x_param_values);
+	  maybe_set_param_value (PARAM_HWASAN_RANDOM_FRAME_TAG, 0,
+				 opts->x_param_values,
+				 opts_set->x_param_values);
+	}
       break;
 
     case OPT_fsanitize_recover_:
diff --git a/gcc/params.def b/gcc/params.def
index 942447d77e66a07736751ebf90bbce105b9dc267..33556f6fc18d8543a52a878cac4fbc1a2d85a73b 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1335,6 +1335,17 @@ DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
 	 "smaller or equal to this number.",
 	 256, 0, INT_MAX)
 
+/* HWAsan stands for HardwareAddressSanitizer: https://github.com/google/sanitizers.  */
+DEFPARAM (PARAM_HWASAN_RANDOM_FRAME_TAG,
+	  "hwasan-random-frame-tag",
+	  "Use random base tag for each frame, as opposed to base always zero.",
+	  1, 0, 1)
+
+DEFPARAM (PARAM_HWASAN_STACK,
+	  "hwasan-stack",
+	  "Enable hwasan stack protection.",
+	  1, 0, 1)
+
 DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
 	  "uninit-control-dep-attempts",
 	  "Maximum number of nested calls to search for control dependencies "
diff --git a/gcc/params.h b/gcc/params.h
index 1aaef6d6a00da79533eb0bba540dd8c731b1eaa8..102d79d42409590040ad9fe0beed0be73c8cc50a 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -250,5 +250,9 @@ extern void init_param_values (int *params);
   PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
 #define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
   ((unsigned) PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD))
+#define HWASAN_STACK \
+  PARAM_VALUE (PARAM_HWASAN_STACK)
+#define HWASAN_RANDOM_FRAME_TAG \
+  PARAM_VALUE (PARAM_HWASAN_RANDOM_FRAME_TAG)
 
 #endif /* ! GCC_PARAMS_H */
diff --git a/gcc/target.def b/gcc/target.def
index 8e83c2c7a7136511c07a5bc9e18876c91a38b955..ad16a151b6af9b1ee13918c8f2980280d75b1d90 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6742,6 +6742,17 @@ DEFHOOK
 HOOK_VECTOR_END (mode_switching)
 
 #undef HOOK_PREFIX
+#define HOOK_PREFIX "TARGET_MEMTAG_"
+HOOK_VECTOR (TARGET_MEMTAG_, memtag)
+
+DEFHOOK
+(can_tag_addresses,
+ "True if backend architecture naturally supports ignoring the top byte of\
+ pointers.  This feature means that -fsanitize=hwaddress can work.",
+ bool, (), default_memtag_can_tag_addresses)
+
+HOOK_VECTOR_END (memtag)
+#undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
 
 #define DEF_TARGET_INSN(NAME, PROTO) \
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index d4c3563e82587feea9523a15c2dcc490dad9919e..94e865259f35e46e26f6b4763c5e2f9dc9ed1b83 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -282,4 +282,5 @@ extern bool default_have_speculation_safe_value (bool);
 extern bool speculation_safe_value_not_needed (bool);
 extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 
+extern bool default_memtag_can_tag_addresses ();
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index fee4cc271cd86a2206e18da2c2db24c5e04b0f4f..6e9e877c32e2c8705056bdf0ce2b1b6f125d93c3 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2365,4 +2365,10 @@ default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
   return result;
 }
 
+bool
+default_memtag_can_tag_addresses ()
+{
+  return false;
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 00a5e83212601bef62ea9a254ddbeae35cde8e6e..ab67384249a3437ac37f42f741ed516884677f9f 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1753,6 +1753,16 @@ process_options (void)
       flag_sanitize &= ~SANITIZE_ADDRESS;
     }
 
+  /* HWAsan requires top byte ignore feature in the backend.  */
+  if (flag_sanitize & SANITIZE_HWADDRESS
+      && ! targetm.memtag.can_tag_addresses ())
+    {
+      warning_at (UNKNOWN_LOCATION, 0,
+		  "%<-fsanitize=hwaddress%> can not be implemented on "
+		  "a backend that does not ignore the top byte of a pointer");
+      flag_sanitize &= ~SANITIZE_HWADDRESS;
+    }
+
  /* Do not use IPA optimizations for register allocation if profiler is active
     or patchable function entries are inserted for run-time instrumentation
     or port does not emit prologue and epilogue as RTL.  */


[-- Attachment #2: hwasan-patch3.patch --]
[-- Type: text/plain, Size: 19559 bytes --]

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 1c9f28587fbb2348cc30e302e889a5a22906901a..a5e68061ff956018957b6be137a7b2f2b7353647 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -54,6 +54,8 @@ static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_sanitize_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_sanitize_address_attribute (tree *, tree, tree,
 						  int, bool *);
+static tree handle_no_sanitize_hwaddress_attribute (tree *, tree, tree,
+						    int, bool *);
 static tree handle_no_sanitize_thread_attribute (tree *, tree, tree,
 						 int, bool *);
 static tree handle_no_address_safety_analysis_attribute (tree *, tree, tree,
@@ -412,6 +414,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_no_sanitize_attribute, NULL },
   { "no_sanitize_address",    0, 0, true, false, false, false,
 			      handle_no_sanitize_address_attribute, NULL },
+  { "no_sanitize_hwaddress",    0, 0, true, false, false, false,
+			      handle_no_sanitize_hwaddress_attribute, NULL },
   { "no_sanitize_thread",     0, 0, true, false, false, false,
 			      handle_no_sanitize_thread_attribute, NULL },
   { "no_sanitize_undefined",  0, 0, true, false, false, false,
@@ -941,6 +945,22 @@ handle_no_sanitize_address_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+/* Handle a "no_sanitize_hwaddress" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_no_sanitize_hwaddress_attribute (tree *node, tree name, tree, int,
+				      bool *no_add_attrs)
+{
+  *no_add_attrs = true;
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
+  else
+    add_no_sanitize_value (*node, SANITIZE_HWADDRESS);
+
+  return NULL_TREE;
+}
+
 /* Handle a "no_sanitize_thread" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/common.opt b/gcc/common.opt
index cc279f411d7967209a111d958437595ce5cacd3a..d44295530474f873a00bc97a7d46bdd26255db9c 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -215,7 +215,7 @@ unsigned int flag_sanitize
 
 ; What sanitizers should recover from errors
 Variable
-unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
+unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS | SANITIZE_KERNEL_HWADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
 
 ; What the coverage sanitizers should instrument
 Variable
@@ -3313,6 +3313,9 @@ Driver
 static-libasan
 Driver
 
+static-libhwasan
+Driver
+
 static-libtsan
 Driver
 
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 599d07a729e7438080f8b5240ee95037a49fb983..e0c7e2c6276d91bd0efe5e9bf9b58b09087c3cd7 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -21371,6 +21371,15 @@ aarch64_stack_protect_guard (void)
   return NULL_TREE;
 }
 
+/* Implement TARGET_MEMTAG_CAN_TAG_ADDRESSES.  Here we tell the rest of the
+   compiler that we automatically ignore the top byte of our pointers, which
+   allows using -fsanitize=hwaddress.  */
+bool
+aarch64_can_tag_addresses ()
+{
+  return true;
+}
+
 /* Implement TARGET_ASM_FILE_END for AArch64.  This adds the AArch64 GNU NOTE
    section at the end if needed.  */
 #define GNU_PROPERTY_AARCH64_FEATURE_1_AND	0xc0000000
@@ -21936,6 +21945,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_FNTYPE_ABI
 #define TARGET_FNTYPE_ABI aarch64_fntype_abi
 
+#undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
+#define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h
index 95a3c29f7cee86336f958bef1d7fe56b82e05e6c..90b1fa91742c6a7d76aa6c7e931f8014fc4fff0c 100644
--- a/gcc/config/gnu-user.h
+++ b/gcc/config/gnu-user.h
@@ -129,14 +129,18 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Link -lasan early on the command line.  For -static-libasan, don't link
    it for -shared link, the executable should be compiled with -static-libasan
    in that case, and for executable link with --{,no-}whole-archive around
-   it to force everything into the executable.  And similarly for -ltsan
-   and -llsan.  */
+   it to force everything into the executable.  And similarly for -ltsan,
+   -lhwasan, and -llsan.  */
 #if defined(HAVE_LD_STATIC_DYNAMIC)
 #undef LIBASAN_EARLY_SPEC
 #define LIBASAN_EARLY_SPEC "%{!shared:libasan_preinit%O%s} " \
   "%{static-libasan:%{!shared:" \
   LD_STATIC_OPTION " --whole-archive -lasan --no-whole-archive " \
   LD_DYNAMIC_OPTION "}}%{!static-libasan:-lasan}"
+#undef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC "%{static-libhwasan:%{!shared:" \
+  LD_STATIC_OPTION " --whole-archive -lhwasan --no-whole-archive " \
+  LD_DYNAMIC_OPTION "}}%{!static-libhwasan:-lhwasan}"
 #undef LIBTSAN_EARLY_SPEC
 #define LIBTSAN_EARLY_SPEC "%{!shared:libtsan_preinit%O%s} " \
   "%{static-libtsan:%{!shared:" \
diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c
index 60e5bedc3665a25fa51c2eca00547f12a9953778..e8d0bedfc2eb22d1e72e7e4875155202c8389a38 100644
--- a/gcc/cppbuiltin.c
+++ b/gcc/cppbuiltin.c
@@ -93,6 +93,9 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile)
   if (flag_sanitize & SANITIZE_ADDRESS)
     cpp_define (pfile, "__SANITIZE_ADDRESS__");
 
+  if (flag_sanitize & SANITIZE_HWADDRESS)
+    cpp_define (pfile, "__SANITIZE_HWADDRESS__");
+
   if (flag_sanitize & SANITIZE_THREAD)
     cpp_define (pfile, "__SANITIZE_THREAD__");
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d1eb317f43cd31156c80ef7dd910024c50f33deb..f710e29549f5d3d58ed74aca74f2c62b577cd155 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -12594,13 +12594,33 @@ more details.  The run-time behavior can be influenced using the
 the available options are shown at startup of the instrumented program.  See
 @url{https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags}
 for a list of supported options.
-The option cannot be combined with @option{-fsanitize=thread}.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=hwaddress}.
 
 @item -fsanitize=kernel-address
 @opindex fsanitize=kernel-address
 Enable AddressSanitizer for Linux kernel.
 See @uref{https://github.com/google/kasan/wiki} for more details.
 
+@item -fsanitize=hwaddress
+@opindex fsanitize=hwaddress
+Enable HardwareAddressSanitizer, a fast memory error detector.
+Memory access instructions are instrumented to detect out-of-bounds and
+use-after-free bugs.
+The option enables @option{-fsanitize-address-use-after-scope}.
+See @uref{https://github.com/google/sanitizers/} for
+more details.  The run-time behavior can be influenced using the
+@env{HWASAN_OPTIONS} environment variable.  When set to @code{help=1},
+the available options are shown at startup of the instrumented program.
+The option cannot be combined with @option{-fsanitize=thread} or
+@option{-fsanitize=address}.
+
+@item -fsanitize=kernel-hwaddress
+@opindex fsanitize=kernel-hwaddress
+Enable HardwareAddressSanitizer for Linux kernel.
+Similar to @option{-fsanitize=kernel-address} but using the software tagging
+method of instrumentation.
+
 @item -fsanitize=pointer-compare
 @opindex fsanitize=pointer-compare
 Instrument comparison operation (<, <=, >, >=) with pointer operands.
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index cd9aed9874f4e6b2b0e2f8956ed6155975e643a8..e842b734c9c20253986880ba3622a8f692d3ca88 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2978,6 +2978,10 @@ This hook defines the machine mode to use for the boolean result of  conditional
 A target hook which lets a backend compute the set of pressure classes to  be used by those optimization passes which take register pressure into  account, as opposed to letting IRA compute them.  It returns the number of  register classes stored in the array @var{pressure_classes}.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_MEMTAG_CAN_TAG_ADDRESSES ()
+True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that -fsanitize=hwaddress can work.
+@end deftypefn
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 2739e9ceec5ad7253ff9135da8dbe3bf6010e8d7..6136ac1a5fe0c0980d5b5123b67b102a1b1e0bcc 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2377,6 +2377,8 @@ in the reload pass.
 
 @hook TARGET_COMPUTE_PRESSURE_CLASSES
 
+@hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index a2103282d469db31ad157a87572068d943061c8c..57d8ff9a1a010409d966230140df1017bc3584a8 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -256,6 +256,9 @@ enum sanitize_code {
   SANITIZE_BUILTIN = 1UL << 25,
   SANITIZE_POINTER_COMPARE = 1UL << 26,
   SANITIZE_POINTER_SUBTRACT = 1UL << 27,
+  SANITIZE_HWADDRESS = 1UL << 28,
+  SANITIZE_USER_HWADDRESS = 1UL << 29,
+  SANITIZE_KERNEL_HWADDRESS = 1UL << 30,
   SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
diff --git a/gcc/gcc.c b/gcc/gcc.c
index c45a1df656b2b8122f457edeb55db5f44c57b5bf..037f9d3a688d8778b067aade698d40709ee97b3d 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -708,6 +708,24 @@ proper position among the other output files.  */
 #define LIBASAN_EARLY_SPEC ""
 #endif
 
+#ifndef LIBHWASAN_SPEC
+#define STATIC_LIBHWASAN_LIBS \
+  " %{static-libhwasan|static:%:include(libsanitizer.spec)%(link_libhwasan)}"
+#ifdef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_SPEC STATIC_LIBHWASAN_LIBS
+#elif defined(HAVE_LD_STATIC_DYNAMIC)
+#define LIBHWASAN_SPEC "%{static-libhwasan:" LD_STATIC_OPTION \
+		     "} -lhwasan %{static-libhwasan:" LD_DYNAMIC_OPTION "}" \
+		     STATIC_LIBHWASAN_LIBS
+#else
+#define LIBHWASAN_SPEC "-lhwasan" STATIC_LIBHWASAN_LIBS
+#endif
+#endif
+
+#ifndef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC ""
+#endif
+
 #ifndef LIBTSAN_SPEC
 #define STATIC_LIBTSAN_LIBS \
   " %{static-libtsan|static:%:include(libsanitizer.spec)%(link_libtsan)}"
@@ -982,6 +1000,7 @@ proper position among the other output files.  */
 #ifndef SANITIZER_EARLY_SPEC
 #define SANITIZER_EARLY_SPEC "\
 %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_EARLY_SPEC "} \
+    %{%:sanitize(hwaddress):" LIBHWASAN_EARLY_SPEC "} \
     %{%:sanitize(thread):" LIBTSAN_EARLY_SPEC "} \
     %{%:sanitize(leak):" LIBLSAN_EARLY_SPEC "}}}}"
 #endif
@@ -991,6 +1010,8 @@ proper position among the other output files.  */
 #define SANITIZER_SPEC "\
 %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_SPEC "\
     %{static:%ecannot specify -static with -fsanitize=address}}\
+    %{%:sanitize(hwaddress):" LIBHWASAN_SPEC "\
+	%{static:%ecannot specify -static with -fsanitize=hwaddress}}\
     %{%:sanitize(thread):" LIBTSAN_SPEC "\
     %{static:%ecannot specify -static with -fsanitize=thread}}\
     %{%:sanitize(undefined):" LIBUBSAN_SPEC "}\
@@ -9439,8 +9460,12 @@ sanitize_spec_function (int argc, const char **argv)
 
   if (strcmp (argv[0], "address") == 0)
     return (flag_sanitize & SANITIZE_USER_ADDRESS) ? "" : NULL;
+  if (strcmp (argv[0], "hwaddress") == 0)
+    return (flag_sanitize & SANITIZE_USER_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "kernel-address") == 0)
     return (flag_sanitize & SANITIZE_KERNEL_ADDRESS) ? "" : NULL;
+  if (strcmp (argv[0], "kernel-hwaddress") == 0)
+    return (flag_sanitize & SANITIZE_KERNEL_HWADDRESS) ? "" : NULL;
   if (strcmp (argv[0], "thread") == 0)
     return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
   if (strcmp (argv[0], "undefined") == 0)
diff --git a/gcc/opts.c b/gcc/opts.c
index 10b9f108f8d0618501c1567bb4f8a7f9de2bdb98..b46c6d44164c04aa7e561fa3dc2513d66ce61e4d 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1119,6 +1119,13 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 		  "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>");
     }
 
+  /* Userspace and kernel HWasan conflict with each other.  */
+  if ((opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS))
+    error_at (loc,
+	      "%<-fsanitize=hwaddress%> is incompatible with "
+	      "%<-fsanitize=kernel-hwaddress%>");
+
   /* Userspace and kernel ASan conflict with each other.  */
   if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
       && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
@@ -1138,6 +1145,20 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
     error_at (loc,
 	      "%<-fsanitize=leak%> is incompatible with %<-fsanitize=thread%>");
 
+  /* HWASan and ASan conflict with each other.  */
+  if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_HWADDRESS))
+    error_at (loc,
+	      "%<-fsanitize=hwaddress%> is incompatible with both "
+	      "%<-fsanitize=address%> and %<-fsanitize=kernel-address%>");
+
+  /* HWASan conflicts with TSan.  */
+  if ((opts->x_flag_sanitize & SANITIZE_HWADDRESS)
+      && (opts->x_flag_sanitize & SANITIZE_THREAD))
+    error_at (loc,
+	      "%<-fsanitize=hwaddress%> is incompatible with "
+	      "%<-fsanitize=thread%>");
+
   /* Check error recovery for -fsanitize-recover option.  */
   for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
     if ((opts->x_flag_sanitize_recover & sanitizer_opts[i].flag)
@@ -1157,7 +1178,8 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 
   /* Enable -fsanitize-address-use-after-scope if address sanitizer is
      enabled.  */
-  if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+  if (((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+       || (opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS))
       && !opts_set->x_flag_sanitize_address_use_after_scope)
     opts->x_flag_sanitize_address_use_after_scope = true;
 
@@ -1786,8 +1808,13 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 #define SANITIZER_OPT(name, flags, recover) \
     { #name, flags, sizeof #name - 1, recover }
   SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true),
+  SANITIZER_OPT (hwaddress, (SANITIZE_HWADDRESS | SANITIZE_USER_HWADDRESS),
+		 true),
   SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
 		 true),
+  SANITIZER_OPT (kernel-hwaddress,
+		 (SANITIZE_HWADDRESS | SANITIZE_KERNEL_HWADDRESS),
+		 true),
   SANITIZER_OPT (pointer-compare, SANITIZE_POINTER_COMPARE, true),
   SANITIZER_OPT (pointer-subtract, SANITIZE_POINTER_SUBTRACT, true),
   SANITIZER_OPT (thread, SANITIZE_THREAD, false),
@@ -2322,6 +2349,14 @@ common_handle_option (struct gcc_options *opts,
 				 opts->x_param_values,
 				 opts_set->x_param_values);
 	}
+      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+	{
+	  maybe_set_param_value (PARAM_HWASAN_STACK, 0, opts->x_param_values,
+				 opts_set->x_param_values);
+	  maybe_set_param_value (PARAM_HWASAN_RANDOM_FRAME_TAG, 0,
+				 opts->x_param_values,
+				 opts_set->x_param_values);
+	}
       break;
 
     case OPT_fsanitize_recover_:
diff --git a/gcc/params.def b/gcc/params.def
index 942447d77e66a07736751ebf90bbce105b9dc267..33556f6fc18d8543a52a878cac4fbc1a2d85a73b 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1335,6 +1335,17 @@ DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
 	 "smaller or equal to this number.",
 	 256, 0, INT_MAX)
 
+/* HWAsan stands for HardwareAddressSanitizer: https://github.com/google/sanitizers.  */
+DEFPARAM (PARAM_HWASAN_RANDOM_FRAME_TAG,
+	  "hwasan-random-frame-tag",
+	  "Use random base tag for each frame, as opposed to base always zero.",
+	  1, 0, 1)
+
+DEFPARAM (PARAM_HWASAN_STACK,
+	  "hwasan-stack",
+	  "Enable hwasan stack protection.",
+	  1, 0, 1)
+
 DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
 	  "uninit-control-dep-attempts",
 	  "Maximum number of nested calls to search for control dependencies "
diff --git a/gcc/params.h b/gcc/params.h
index 1aaef6d6a00da79533eb0bba540dd8c731b1eaa8..102d79d42409590040ad9fe0beed0be73c8cc50a 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -250,5 +250,9 @@ extern void init_param_values (int *params);
   PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
 #define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
   ((unsigned) PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD))
+#define HWASAN_STACK \
+  PARAM_VALUE (PARAM_HWASAN_STACK)
+#define HWASAN_RANDOM_FRAME_TAG \
+  PARAM_VALUE (PARAM_HWASAN_RANDOM_FRAME_TAG)
 
 #endif /* ! GCC_PARAMS_H */
diff --git a/gcc/target.def b/gcc/target.def
index 8e83c2c7a7136511c07a5bc9e18876c91a38b955..ad16a151b6af9b1ee13918c8f2980280d75b1d90 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6742,6 +6742,17 @@ DEFHOOK
 HOOK_VECTOR_END (mode_switching)
 
 #undef HOOK_PREFIX
+#define HOOK_PREFIX "TARGET_MEMTAG_"
+HOOK_VECTOR (TARGET_MEMTAG_, memtag)
+
+DEFHOOK
+(can_tag_addresses,
+ "True if backend architecture naturally supports ignoring the top byte of\
+ pointers.  This feature means that -fsanitize=hwaddress can work.",
+ bool, (), default_memtag_can_tag_addresses)
+
+HOOK_VECTOR_END (memtag)
+#undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
 
 #define DEF_TARGET_INSN(NAME, PROTO) \
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index d4c3563e82587feea9523a15c2dcc490dad9919e..94e865259f35e46e26f6b4763c5e2f9dc9ed1b83 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -282,4 +282,5 @@ extern bool default_have_speculation_safe_value (bool);
 extern bool speculation_safe_value_not_needed (bool);
 extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 
+extern bool default_memtag_can_tag_addresses ();
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index fee4cc271cd86a2206e18da2c2db24c5e04b0f4f..6e9e877c32e2c8705056bdf0ce2b1b6f125d93c3 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2365,4 +2365,10 @@ default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
   return result;
 }
 
+bool
+default_memtag_can_tag_addresses ()
+{
+  return false;
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 00a5e83212601bef62ea9a254ddbeae35cde8e6e..ab67384249a3437ac37f42f741ed516884677f9f 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1753,6 +1753,16 @@ process_options (void)
       flag_sanitize &= ~SANITIZE_ADDRESS;
     }
 
+  /* HWAsan requires top byte ignore feature in the backend.  */
+  if (flag_sanitize & SANITIZE_HWADDRESS
+      && ! targetm.memtag.can_tag_addresses ())
+    {
+      warning_at (UNKNOWN_LOCATION, 0,
+		  "%<-fsanitize=hwaddress%> can not be implemented on "
+		  "a backend that does not ignore the top byte of a pointer");
+      flag_sanitize &= ~SANITIZE_HWADDRESS;
+    }
+
  /* Do not use IPA optimizations for register allocation if profiler is active
     or patchable function entries are inserted for run-time instrumentation
     or port does not emit prologue and epilogue as RTL.  */


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 7/X] [libsanitizer] Add tests
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (27 preceding siblings ...)
  2019-11-07 18:37   ` [PATCH 2/X] [libsanitizer] Only build libhwasan when targeting AArch64 Matthew Malcomson
@ 2019-11-07 18:38   ` Matthew Malcomson
  2019-11-20 14:16     ` Martin Liška
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
  29 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-07 18:38 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 55897 bytes --]

Adding hwasan tests.

Frankly, these could be tidied up a little.
I will be tidying them up while getting feedback on the hwasan introduction.


gcc/testsuite/ChangeLog:

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

	* c-c++-common/hwasan/arguments.c: New test.
	* c-c++-common/hwasan/halt_on_error-1.c: New test.
	* g++.dg/hwasan/rvo-handled.c: New test.
	* g++.dg/hwasan/try-catch-0.cpp: New test.
	* g++.dg/hwasan/try-catch-1.cpp: New test.
	* gcc.dg/hwasan/aligned-alloc.c: New test.
	* gcc.dg/hwasan/alloca-array-accessible.c: New test.
	* gcc.dg/hwasan/alloca-gets-different-tag.c: New test.
	* gcc.dg/hwasan/alloca-outside-caught.c: New test.
	* gcc.dg/hwasan/bitfield-1.c: New test.
	* gcc.dg/hwasan/bitfield-2.c: New test.
	* gcc.dg/hwasan/builtin-special-handling.c: New test.
	* gcc.dg/hwasan/check-interface.c: New test.
	* gcc.dg/hwasan/hwasan-poison-optimisation.c: New test.
	* gcc.dg/hwasan/hwasan-thread-access-parent.c: New test.
	* gcc.dg/hwasan/hwasan-thread-basic-failure.c: New test.
	* gcc.dg/hwasan/hwasan-thread-clears-stack.c: New test.
	* gcc.dg/hwasan/hwasan-thread-success.c: New test.
	* gcc.dg/hwasan/hwasan.exp: New file.
	* gcc.dg/hwasan/kernel-defaults.c: New test.
	* gcc.dg/hwasan/large-aligned-0.c: New test.
	* gcc.dg/hwasan/large-aligned-1.c: New test.
	* gcc.dg/hwasan/macro-definition.c: New test.
	* gcc.dg/hwasan/nested-functions-0.c: New test.
	* gcc.dg/hwasan/nested-functions-1.c: New test.
	* gcc.dg/hwasan/nested-functions-2.c: New test.
	* gcc.dg/hwasan/no-sanitize-attribute.c: New test.
	* gcc.dg/hwasan/random-frame-tag.c: New test.
	* gcc.dg/hwasan/setjmp-longjmp-0.c: New test.
	* gcc.dg/hwasan/setjmp-longjmp-1.c: New test.
	* gcc.dg/hwasan/stack-tagging-basic-0.c: New test.
	* gcc.dg/hwasan/stack-tagging-basic-1.c: New test.
	* gcc.dg/hwasan/stack-tagging-disable.c: New test.
	* gcc.dg/hwasan/vararray-outside-caught.c: New test.
	* gcc.dg/hwasan/very-large-objects.c: New test.
	* lib/hwasan-dg.exp: New file.



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


diff --git a/gcc/testsuite/c-c++-common/hwasan/arguments.c b/gcc/testsuite/c-c++-common/hwasan/arguments.c
new file mode 100644
index 0000000000000000000000000000000000000000..2d563eb8541694d501b021babd9452fd7fd502a3
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/arguments.c
@@ -0,0 +1,7 @@
+/*
+   TODO
+   Somehow test the conflict of arguments
+   -fsanitize=hwaddress -fsanitize=kernel-address
+   -fsanitize=hwaddress -fsanitize=address
+   -fsanitize=hwaddress -fsanitize=thread
+ */
diff --git a/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c b/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..118191e2e00bd07bd4839888d2fb29baec926c60
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c
@@ -0,0 +1,25 @@
+/* Test recovery mode.  */
+/* { dg-do run } */
+/* { dg-options "-fsanitize-recover=hwaddress" } */
+/* { dg-set-target-env-var HWASAN_OPTIONS "halt_on_error=false" } */
+/* { dg-shouldfail "hwasan" } */
+
+#include <string.h>
+
+volatile int ten = 16;
+
+int main() {
+  char x[10];
+  __builtin_memset(x, 0, ten + 1);
+  asm volatile ("" : : : "memory");
+  volatile int res = x[ten];
+  x[ten] = res + 3;
+  res = x[ten];
+  return 0;
+}
+
+/* { dg-output "WRITE of size 17 at 0x\[0-9a-f\]+.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+.*" } */
+/* { dg-output "WRITE of size 1 at 0x\[0-9a-f\]+.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+.*" } */
+
diff --git a/gcc/testsuite/g++.dg/hwasan/rvo-handled.c b/gcc/testsuite/g++.dg/hwasan/rvo-handled.c
new file mode 100644
index 0000000000000000000000000000000000000000..6e6934a0be1b0ce14c459555168f6a2590a8ec7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/hwasan/rvo-handled.c
@@ -0,0 +1,56 @@
+/* { dg-do run } */
+/* TODO Ensure this test has enough optimisation to get RVO. */
+
+#define assert(x) if (!(x)) __builtin_abort ()
+
+struct big_struct {
+    int left;
+    int right;
+    void *ptr;
+    int big_array[100];
+};
+
+/*
+   Tests for RVO (basically, checking -fsanitize=hwaddress has not broken RVO
+   in any way).
+
+   0) The value is accessible in both functions without a hwasan complaint.
+   1) RVO does happen.
+ */
+
+struct big_struct __attribute__ ((noinline))
+return_on_stack()
+{
+  struct big_struct x;
+  x.left = 100;
+  x.right = 20;
+  x.big_array[10] = 30;
+  x.ptr = &x;
+  return x;
+}
+
+struct big_struct __attribute__ ((noinline))
+unnamed_return_on_stack()
+{
+  return (struct big_struct){
+      .left = 100,
+      .right = 20,
+      .ptr = __builtin_frame_address (0),
+      .big_array = {0}
+  };
+}
+
+int main()
+{
+  struct big_struct x;
+  x = return_on_stack();
+  /* Check that RVO happens by checking the address that the callee saw.  */
+  assert (x.ptr == &x);
+  struct big_struct y;
+  y = unnamed_return_on_stack();
+  /* Know only running this on AArch64, which means stack grows downwards,
+     We're checking that the frame of the callee function is below the address
+     of this variable, which means that the callee function used RVO.  */
+  assert (y.ptr < (void *)&y);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/hwasan/try-catch-0.cpp b/gcc/testsuite/g++.dg/hwasan/try-catch-0.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d5bbc93564c7f32a029be1a1e071f44188c91da5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/hwasan/try-catch-0.cpp
@@ -0,0 +1,65 @@
+/* { dg-do run } */
+
+/* This version should work just fine.  */
+#include <stdexcept>
+
+char *intermediate_pointer = NULL;
+static void optimization_barrier(void* arg) {
+  asm volatile("" : : "r"(arg) : "memory");
+}
+
+__attribute__((noinline))
+void h() {
+  char x[1000];
+  intermediate_pointer = (void *)&x;
+  optimization_barrier(x);
+  throw std::runtime_error("hello");
+}
+
+__attribute__((noinline))
+void g() {
+  char x[1000];
+  optimization_barrier(x);
+  h();
+  optimization_barrier(x);
+}
+
+__attribute__((noinline))
+void hwasan_read(char *p, int size) {
+  char volatile sink;
+  for (int i = 0; i < size; ++i)
+    sink = p[i];
+}
+
+__attribute__((noinline, no_sanitize("hwaddress"))) void after_catch() {
+  char x[10000];
+  hwasan_read(&x[0], sizeof(x));
+}
+
+
+__attribute__((noinline))
+void f() {
+  char x[1000];
+  try {
+    // Put two tagged frames on the stack, throw an exception from the deepest one.
+    g();
+  } catch (const std::runtime_error &e) {
+    // Put an untagged frame on stack, check that it is indeed untagged.
+    // This relies on exception support zeroing out stack tags.
+    // BAD: tag-mismatch
+    after_catch();
+    // Check that an in-scope stack allocation is still tagged.
+    // This relies on exception support not zeroing too much.
+    hwasan_read(&x[0], sizeof(x));
+#ifdef CLEARED_ACCESS_CATCH
+    return (int)(intermediate_pointer[1]);
+#else
+    return 0;
+#endif
+  }
+  __builtin_abort ();
+}
+
+int main() {
+  f();
+}
diff --git a/gcc/testsuite/g++.dg/hwasan/try-catch-1.cpp b/gcc/testsuite/g++.dg/hwasan/try-catch-1.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b124d29d33613794f569646b932b42c958b52348
--- /dev/null
+++ b/gcc/testsuite/g++.dg/hwasan/try-catch-1.cpp
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+
+/* This version should catch the invalid access.  */
+#define CLEARED_ACCESS_CATCH
+#include "try-catch-0.cpp"
+#undef CLEARED_ACCESS_CATCH
diff --git a/gcc/testsuite/gcc.dg/hwasan/aligned-alloc.c b/gcc/testsuite/gcc.dg/hwasan/aligned-alloc.c
new file mode 100644
index 0000000000000000000000000000000000000000..49b317cce7516845944e4b74ece94af0a8570076
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/aligned-alloc.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/*
+   TODO
+    This alignment isn't handled by the sanitizer interceptor alloc.
+    At the moment this program fails at runtime in the libhwasan library.
+
+    LLVM catches this problem at compile-time.
+ */
+
+int
+main ()
+{
+  void *p = __builtin_aligned_alloc (17, 100);
+  if ((unsigned long long)p & 0x10 == 0)
+    return 0;
+  __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/gcc.dg/hwasan/alloca-array-accessible.c b/gcc/testsuite/gcc.dg/hwasan/alloca-array-accessible.c
new file mode 100644
index 0000000000000000000000000000000000000000..c1d7c3bb0d1de7b8e6aa2ba7cf114604583e3ba3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/alloca-array-accessible.c
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+
+#define alloca __builtin_alloca
+
+int __attribute__ ((noinline))
+using_alloca (int num)
+{
+  int retval = 0;
+  int *big_array = alloca (num * sizeof (int));
+  for (int i = 0; i < num; ++i) {
+      retval += big_array[i];
+  }
+  return retval;
+}
+
+int __attribute__ ((noinline))
+using_vararray (int num)
+{
+  int retval = 0;
+  int big_array[num];
+  for (int i = 0; i < num; ++i) {
+      retval += big_array[i];
+  }
+  return retval;
+}
+
+int main()
+{
+  using_alloca (16);
+  using_vararray (12);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/hwasan/alloca-gets-different-tag.c b/gcc/testsuite/gcc.dg/hwasan/alloca-gets-different-tag.c
new file mode 100644
index 0000000000000000000000000000000000000000..d6dcb6fdf4ec535fef475d51de964bcc13337b67
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/alloca-gets-different-tag.c
@@ -0,0 +1,64 @@
+/* { dg-do run } */
+
+/* Alloca is given a different tag to other variables.
+   vararray should behave in the same way.  */
+
+#define alloca __builtin_alloca
+#define assert(x) if (!(x)) __builtin_abort ()
+
+struct two_values {
+    int left;
+    int right;
+};
+
+/* Require default hwasan tag ABI.
+   Know we're using AArch64 since that's the only architecture we run hwasan
+   tests on.  */
+char tag_of (void * x) { return ((unsigned long long)x) >> 56; }
+
+int __attribute__ ((noinline))
+alloca_different_tag (int num)
+{
+  struct two_values tmp_object = {
+      .left = 100,
+      .right = num,
+  };
+  int *big_array = alloca (num * sizeof (int));
+  int other_array[100];
+  
+  char first_tag = tag_of (&tmp_object);
+  char second_tag = tag_of (big_array);
+  char other_tag = tag_of (other_array);
+  assert (first_tag != second_tag);
+  assert (second_tag != other_tag);
+  assert (first_tag != other_tag);
+  return 0;
+}
+
+int __attribute__ ((noinline))
+vararray_different_tag (int num)
+{
+  struct two_values tmp_object = {
+      .left = 100,
+      .right = num,
+  };
+  int big_array[num];
+  int other_array[100];
+  
+  char first_tag = tag_of (&tmp_object);
+  char second_tag = tag_of (big_array);
+  char other_tag = tag_of (other_array);
+  assert (first_tag != second_tag);
+  assert (second_tag != other_tag);
+  assert (first_tag != other_tag);
+  return 0;
+}
+
+int __attribute__ ((noinline))
+main ()
+{
+  alloca_different_tag (10);
+  vararray_different_tag (8);
+  return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/hwasan/alloca-outside-caught.c b/gcc/testsuite/gcc.dg/hwasan/alloca-outside-caught.c
new file mode 100644
index 0000000000000000000000000000000000000000..c794d7900a80ffd42d46ee492b9d53795ba914b5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/alloca-outside-caught.c
@@ -0,0 +1,24 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+#define alloca __builtin_alloca
+
+int __attribute__ ((noinline))
+check_alloca (int num)
+{
+  volatile int *allocd_array = alloca (num * sizeof(int));
+  int other_array[10];
+  return allocd_array[12];
+}
+
+int __attribute__ ((noinline))
+main ()
+{
+  check_alloca (3);
+  return 1;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/bitfield-1.c b/gcc/testsuite/gcc.dg/hwasan/bitfield-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..4cabb1c8a1578bcfb230be40fb54551e010c85b7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/bitfield-1.c
@@ -0,0 +1,30 @@
+/* { dg-do run } */
+
+struct bitmapped_struct {
+    unsigned one : 1;
+    unsigned two : 1;
+    unsigned three : 1;
+    unsigned four : 1;
+    unsigned five : 1;
+    unsigned six : 1;
+    unsigned seven : 1;
+    unsigned eight : 1;
+};
+
+/* Check that hwasan allows valid bitfield accesses. */
+int __attribute__ ((noinline))
+handle_unaligned_access (struct bitmapped_struct *foo)
+{
+  if (foo->three)
+    return foo->four;
+
+  foo->five = 1;
+  return 1;
+}
+
+int main()
+{
+  struct bitmapped_struct myvar = {0};
+  handle_unaligned_access (&myvar);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/hwasan/bitfield-2.c b/gcc/testsuite/gcc.dg/hwasan/bitfield-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..4bd53bf8c3e4337d4dccd7f966908f50a9ace08b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/bitfield-2.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+struct A
+{
+  /* Ensure the offset from the start of this struct to the bitfield we access
+     is large enough to be in a different tag.  */
+  char base[16];
+  int : 4;
+  long x : 7;
+};
+
+int __attribute__ ((noinline, noclone))
+f (void *p) {
+  return ((struct A *)p)->x;
+}
+
+int
+main ()
+{
+  char a = 0;
+  return f (&a);
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 2 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/builtin-special-handling.c b/gcc/testsuite/gcc.dg/hwasan/builtin-special-handling.c
new file mode 100644
index 0000000000000000000000000000000000000000..502bb620c29ba650429277e4a9b7b114038599aa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/builtin-special-handling.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-fdump-tree-hwasan" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+
+typedef __SIZE_TYPE__ size_t;
+/* Functions to observe that HWASAN instruments memory builtins in the expected
+   manner.  */
+void * __attribute__((noinline))
+memset_builtin (void *dest, int value, size_t len)
+{
+  return __builtin_memset (dest, value, len);
+}
+
+/* HWASAN avoids strlen because it doesn't know the size of the memory access
+   until *after* the function call.  */
+size_t __attribute__ ((noinline))
+strlen_builtin (char *element)
+{
+  return __builtin_strlen (element);
+}
+
+/* First test ensures that the HWASAN_CHECK was emitted before the
+   memset.  Second test ensures there was only HWASAN_CHECK (which demonstrates
+   that strlen was not instrumented).  */
+/* { dg-final { scan-tree-dump-times "HWASAN_CHECK.*memset" 1 "hwasan1" } } */
+/* { dg-final { scan-tree-dump-times "HWASAN_CHECK" 1 "hwasan1" } } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/check-interface.c b/gcc/testsuite/gcc.dg/hwasan/check-interface.c
new file mode 100644
index 0000000000000000000000000000000000000000..dedb9174726ba031f1727c2617e7c9fc6dc4487d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/check-interface.c
@@ -0,0 +1,22 @@
+/* { dg-do run } */
+/*
+   Test taken from LLVM
+    compiler-rt/test/hwasan/TestCases/check-interface.cpp
+ */
+// Utilizes all flavors of __hwasan_load/store interface functions to verify
+// that the instrumentation and the interface provided by HWASan do match.
+// In case of a discrepancy, this test fails to link.
+
+#include <sanitizer/hwasan_interface.h>
+
+#define F(T) void f_##T(T *a, T *b) { *a = *b; }
+
+F(uint8_t)
+F(uint16_t)
+F(uint32_t)
+F(uint64_t)
+
+typedef unsigned V32 __attribute__((__vector_size__(32)));
+F(V32)
+
+int main() {}
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-poison-optimisation.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-poison-optimisation.c
new file mode 100644
index 0000000000000000000000000000000000000000..49e8bb54361da2ee40704ded354b8ab0d73aadaf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-poison-optimisation.c
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+/* { dg-additional-options "-fdump-tree-hwasan1 -save-temps" } */
+
+/* Here to check that the ASAN_POISON stuff works just fine.
+   This mechanism isn't very often used, but I should at least go through the
+   code-path once in my testfile.  */
+int
+main ()
+{
+  int *ptr = 0;
+
+  {
+    int a;
+    ptr = &a;
+    *ptr = 12345;
+  }
+
+  return *ptr;
+}
+
+/* TODO This is a pain around LTO.
+   I want to have a test that works for everything (including -flto functions),
+   but can't use any directive that works for both with and without -flto.  */
+/* { dg-final { scan-tree-dump-times "ASAN_POISON" 1 "hwasan1" }  } */
+/* { dg-final { scan-assembler-times "bl\\s*__hwasan_tag_mismatch4" 1 } } */
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-access-parent.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-access-parent.c
new file mode 100644
index 0000000000000000000000000000000000000000..107430a90245758637e14cca98be4a1cfe4faa13
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-access-parent.c
@@ -0,0 +1,44 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "-lpthread" } */
+
+#include <pthread.h>
+
+extern int printf (const char *, ...);
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+/* Test that tags are checked across different threads.
+   i.e. if this thread tries to access a different threads memory with the
+   incorrect tag, then this thread fails.  */
+void *
+failing_thread_function (void *argument)
+{
+    void * other = (void *)((uint64_t)argument & 0xffffffffffffffULL);
+    int *num = argument;
+    printf ("(should succeed): first number = %d\n", num[0]);
+    printf ("(now should fail):\n");
+
+    int *othernum = other;
+    printf (" second number = %d\n", othernum[0]);
+    return (void *)1;
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, failing_thread_function, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    return (uintptr_t)retval;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: 00/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T1.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-basic-failure.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-basic-failure.c
new file mode 100644
index 0000000000000000000000000000000000000000..baf4e57884ded6261052c41c94ea5c829372db28
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-basic-failure.c
@@ -0,0 +1,41 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "-lpthread" } */
+
+/* Ensure the failure mode for hwasan under pthreads looks sane.
+   (Looks sane means that the same error message is printed out rather than an
+   opaque message due to mishandling).  */
+
+#include <pthread.h>
+
+extern int printf (const char *, ...);
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+void *
+failing_from_stack (void * argument)
+{
+    int internal_array[16] = {0};
+    printf ("(now should fail):");
+    printf (" problem number is %d\n", internal_array[17]);
+    return (void *)1;
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, failing_from_stack, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    return (uintptr_t)retval;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T1.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T1.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-clears-stack.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-clears-stack.c
new file mode 100644
index 0000000000000000000000000000000000000000..9d07447fae1866bfdf271a6fa2e61a1e3185a48f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-clears-stack.c
@@ -0,0 +1,47 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "-lpthread" } */
+
+/* This checks the interceptor ABI pthread hooks.  */
+
+#include <pthread.h>
+
+extern int printf (const char *, ...);
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+__attribute__ ((noinline))
+void * Ident (void * argument)
+{
+	return argument;
+}
+
+void *
+pthread_stack_is_cleared (void *argument)
+{
+   (void)argument;
+   int internal_array[16] = {0};
+   return Ident((void*)internal_array);
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, pthread_stack_is_cleared, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    printf ("(should fail): ");
+    printf ("value left in stack is: %d\n", ((int *)retval)[0]);
+
+    return (uintptr_t)retval;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "HWAddressSanitizer can not describe address in more detail\..*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-success.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-success.c
new file mode 100644
index 0000000000000000000000000000000000000000..cf8f73495d0da3e734521a488da064f3fe287fcb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-success.c
@@ -0,0 +1,34 @@
+/* { dg-do run } */
+/* { dg-additional-options "-lpthread" } */
+
+/* Just ensure that a basic threaded program works while running with hwasan.
+   */
+
+#include <pthread.h>
+
+extern int printf (const char *, ...);
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+void *
+successful_thread_function (void * argument)
+{
+    int *deref = (int *)argument;
+    if (deref[0] == 100)
+      deref[1] = 10;
+    return (void *)0;
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, successful_thread_function, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    return (uintptr_t)retval;
+}
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan.exp b/gcc/testsuite/gcc.dg/hwasan/hwasan.exp
new file mode 100644
index 0000000000000000000000000000000000000000..f90fd4a8e3e900a36935f6869f011051c1f40882
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan.exp
@@ -0,0 +1,42 @@
+# Copyright (C) 2012-2019 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# TODO If target is not AArch64, then exit.
+# TODO If target does not have special kernel then exit.
+# TODO Probably best if I just check that compiling some basic file with
+# ~-fsanitize=hwaddress~ works and doesn't crash when using a syscall with a
+# tagged pointer.
+
+# Load support procs.
+load_lib gcc-dg.exp
+load_lib hwasan-dg.exp
+
+# Initialize `dg'.
+dg-init
+hwasan_init
+
+# Main loop.
+if [check_effective_target_fsanitize_hwaddress] {
+  gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c $srcdir/c-c++-common/hwasan/*.c]] "" ""
+}
+
+# All done.
+hwasan_finish
+dg-finish
diff --git a/gcc/testsuite/gcc.dg/hwasan/kernel-defaults.c b/gcc/testsuite/gcc.dg/hwasan/kernel-defaults.c
new file mode 100644
index 0000000000000000000000000000000000000000..be021464b8cea548467ac7ea53c67a30b6b79943
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/kernel-defaults.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-fno-sanitize=hwaddress -fsanitize=kernel-hwaddress" } */
+
+
+/* Defaults to check for kernel-hwaddress.
+   1) No stack tagging => no calls to __hwasan_tag_memory.
+   2) No block scope tagging (same again).
+   3) Use sanitize-recover by default (everything ends in noabort). */
+int __attribute__ ((noinline))
+accessing_pointers (int *left, int *right)
+{
+  int x = right[2];
+  left[3] = right[1];
+  return right[1] + left[2];
+}
+
+int __attribute__ ((noinline))
+using_stack (int num)
+{
+  int big_array[10];
+  int other_array[20];
+  accessing_pointers(other_array, big_array);
+  return big_array[num];
+}
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  using_stack (ARG);
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "__hwasan_tag_memory" } } */
+/* { dg-final { scan-assembler-not "__hwasan_(load|store)\\d(?!_noabort)" } } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c b/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..aba79e0acf36dd9fa20755d3cfcab29f3cb5625e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+
+/* Handling large aligned variables.
+   Large aligned variables take a different code-path through expand_stack_vars
+   in cfgexpand.c.  This testcase is just to exercise that code-path.
+
+   The alternate code-path produces a second base-pointer through some
+   instructions emitted in the prologue.
+   
+   Test cases are:
+   0) Valid access works without complaint.
+   1) Invalid access is caught.  */
+int __attribute__ ((noinline))
+handle_large_alignment (int num)
+{
+  int other_array[10];
+  int big_array[100] __attribute__ ((aligned (32)));
+  return big_array[num] + other_array[num];
+}
+
+#ifndef ARG
+#define ARG 1
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  handle_large_alignment (ARG);
+  return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/hwasan/large-aligned-1.c b/gcc/testsuite/gcc.dg/hwasan/large-aligned-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..abea810bee9d0418493cca72c80d61dfd61a03d7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/large-aligned-1.c
@@ -0,0 +1,13 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+#define ARG 12
+#include "large-aligned-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* NOTE: This assumes the current tagging mechanism (one at a time from the
+   base and large aligned variables being handled first).  */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/macro-definition.c b/gcc/testsuite/gcc.dg/hwasan/macro-definition.c
new file mode 100644
index 0000000000000000000000000000000000000000..5f654f557821f2dbe060e9976fbca7e5770f274c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/macro-definition.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+extern void testfunc(int);
+int foo()
+{
+#ifndef __SANITIZE_HWADDRESS__
+  testfunc(1);
+#endif
+  return 1;
+}
+
+/* { dg-final { scan-assembler-not "testfunc" } } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..22bcef57c7965fadb5b41b741bc7089df9eb0efc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c
@@ -0,0 +1,52 @@
+/* { dg-do run } */
+
+/*
+   Tests of nested funtions are:
+    0) Accessing closed over variables works.
+    1) Accesses outside of variables is caught.
+    2) Accessing variable out of scope is caught.
+
+    Here we test that accessing closed over variables works.
+ */
+
+/* We need a second layer of indirection so that GCC doesn't notice we're
+   returning the address of a local variable and put 0 in it's place.  */
+__attribute__((noinline))
+int *Ident(void *x) {
+  return x;
+}
+
+int __attribute__ ((noinline))
+intermediate (void (*f) (int, char),
+	      char num)
+{
+  if (num == 1)
+    /* NOTE: We need to overrun by an amount greater than the "extra data" in a
+       nonlocal goto structure.  The entire structure is allocated on the stack
+       with a single tag, which means hwasan can't tell if a closed-over buffer
+       was overrun by an amount small enough that the access was still to some
+       data in that nonlocal goto structure.  */
+    f (100, 100);
+  else
+    f (3, 100);
+  /* Just return something ... */
+  return num % 3;
+}
+
+int* __attribute__ ((noinline))
+nested_function (char num)
+{
+  int big_array[16];
+  int other_array[16];
+  void store (int index, char value)
+    { big_array[index] = value; }
+  return Ident(&other_array[intermediate (store, num)]);
+}
+
+#ifndef MAIN
+int main ()
+{
+  nested_function (0);
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..7d5e80e8a9cb6a1e5a2fb87f78f140c37cd88841
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c
@@ -0,0 +1,26 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/*
+   Tests of nested funtions are:
+    0) Accessing closed over variables works.
+    1) Accesses outside of variables is caught.
+    2) Accessing variable out of scope is caught.
+
+    Here we test option 1.
+ */
+
+#define MAIN 0
+#include "nested-functions-0.c"
+#undef MAIN
+
+int main ()
+{
+  nested_function (1);
+  return 0;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "WRITE of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..be7942d4fb1bf0dec657396e0bd3ca2f43b78b36
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c
@@ -0,0 +1,27 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/*
+   Tests of nested funtions are:
+    0) Accessing closed over variables works.
+    1) Accesses outside of variables is caught.
+    2) Accessing variable out of scope is caught.
+
+    Here we test option 2.
+ */
+
+#define MAIN 0
+#include "nested-functions-0.c"
+#undef MAIN
+
+int main ()
+{
+  int *retval = nested_function (2);
+  *retval = 100;
+  return 0;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "WRITE of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/no-sanitize-attribute.c b/gcc/testsuite/gcc.dg/hwasan/no-sanitize-attribute.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5ff4aec06c8bc6c80fe512061ccc8908e321153
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/no-sanitize-attribute.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+
+__attribute__((no_sanitize_hwaddress)) int
+f1 (int *p, int *q)
+{
+  *p = 42;
+  return *q;
+}
+
+__attribute__((no_sanitize("hwaddress"))) int
+f2 (int *p, int *q)
+{
+  *p = 42;
+  return *q;
+}
+
+/* Only have one instance of __hwasan, it is __hwasan_init (the module
+ * constructor) there is no instrumentation in the functions.  */
+/* { dg-final { scan-assembler-times "__hwasan" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/random-frame-tag.c b/gcc/testsuite/gcc.dg/hwasan/random-frame-tag.c
new file mode 100644
index 0000000000000000000000000000000000000000..6dba71bf39558a945d10007b5437651fb7871626
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/random-frame-tag.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-options "--param hwasan-random-frame-tag=1" } */
+
+#include "stack-tagging-basic-0.c"
+
+/* Random frame tag => call to __hwasan_generate_tag.  */
+/* { dg-final { scan-assembler "__hwasan_generate_tag" } } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-0.c b/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..3c7e9c5b3fec8677440b81a2e029427957b359ba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-0.c
@@ -0,0 +1,53 @@
+/* { dg-do run } */
+
+#include <setjmp.h>
+#include <stdio.h>
+
+/*
+   Testing longjmp/setjmp should test.
+
+   0) Nothing special happens with the jmp_buf.
+   1) Accesses to scopes jmp'd over are caught.
+ */
+int __attribute__ ((noinline))
+uses_longjmp (int **other_array, int num, jmp_buf env)
+{
+  int internal_array[100] = {0};
+  *other_array = &internal_array[0];
+  if (num % 2)
+    longjmp (env, num);
+  else
+    return num % 8;
+}
+
+int __attribute__ ((noinline))
+uses_setjmp (int num)
+{ 
+  int big_array[100];
+  int *other_array = NULL;
+  sigjmp_buf cur_env;
+  int temp = 0;
+  if ((temp = sigsetjmp (cur_env, 1)) != 0)
+    { 
+      if (other_array != NULL)
+        printf ("Value pointed to in other_array[0]: %d\n",
+                other_array[0]);
+  
+      printf ("Longjmp returned %d.\n", temp);
+      return 10;
+    }
+  else
+    {
+      return uses_longjmp (&other_array, num, cur_env);
+    } 
+} 
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  uses_setjmp (ARG);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-1.c b/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..a32b5063e556f0e45e83d657aa312fb4de860442
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-1.c
@@ -0,0 +1,18 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/*
+   Testing longjmp/setjmp should test.
+
+   0) Nothing special happens with the jmp_buf.
+   1) Accesses to scopes jmp'd over are caught.
+ */
+
+#define ARG 1
+#include "setjmp-longjmp-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..050a3eda7aa421b6b955aedd900f6fbb24a74a61
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c
@@ -0,0 +1,33 @@
+/* { dg-do run } */
+
+/* Basic tests for stack tagging.
+
+   0) Valid accesses work.
+   1) Accesses outside of a variable crash.
+*/
+int __attribute__ ((noinline))
+accessing_pointers (int *left, int *right)
+{
+  int x = right[2];
+  left[3] = right[1];
+  return right[1] + left[2];
+}
+
+int __attribute__ ((noinline))
+using_stack (int num)
+{
+  int big_array[10];
+  int other_array[20];
+  accessing_pointers(other_array, big_array);
+  return big_array[num];
+}
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  using_stack (ARG);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-1.c b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..0db6bc4bd220e99bcd695ee93fcc9f4118d09457
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-1.c
@@ -0,0 +1,17 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/* Basic tests for stack tagging.
+
+   0) Accesses outside of a variable crash.
+   1) Valid accesses work.
+*/
+
+#define ARG 17
+#include "stack-tagging-basic-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/stack-tagging-disable.c b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-disable.c
new file mode 100644
index 0000000000000000000000000000000000000000..30f45062d070c482be34f9c824c57f4bd9cc3e10
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-disable.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "--param hwasan-stack=0" } */
+
+
+/* No stack tagging => no calls to __hwasan_tag_memory.  */
+int __attribute__ ((noinline))
+accessing_pointers (int *left, int *right)
+{
+  int x = right[2];
+  left[3] = right[1];
+  return right[1] + left[2];
+}
+
+int __attribute__ ((noinline))
+using_stack (int num)
+{
+  int big_array[10];
+  int other_array[20];
+  accessing_pointers(other_array, big_array);
+  return big_array[num];
+}
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  using_stack (ARG);
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "__hwasan_tag_memory" } } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/vararray-outside-caught.c b/gcc/testsuite/gcc.dg/hwasan/vararray-outside-caught.c
new file mode 100644
index 0000000000000000000000000000000000000000..858bfcd7e596a90c6bd1d68667259a735bb4dce7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/vararray-outside-caught.c
@@ -0,0 +1,21 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+int __attribute__ ((noinline))
+check_vararray (int num)
+{
+  int var_array[num];
+  int other_array[10];
+  return var_array[12];
+}
+
+int __attribute__ ((noinline))
+main ()
+{
+  return check_vararray (3);
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/very-large-objects.c b/gcc/testsuite/gcc.dg/hwasan/very-large-objects.c
new file mode 100644
index 0000000000000000000000000000000000000000..55265353369540872e8fba4da99d9be92a7ad99b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/very-large-objects.c
@@ -0,0 +1,68 @@
+/* { dg-do compile } */
+
+/* Ensure the sanitizer can handle very large offsets (i.e. that the hooks
+   handle offsets too large for the relevant instructions).
+   Just want to make sure this compiles without an ICE.  */
+#ifndef ASIZE
+# define ASIZE 0x10000000000UL
+#endif
+
+typedef __UINT64_TYPE__ uint64_t;
+
+#if __LONG_MAX__ < 8 * ASIZE
+# undef ASIZE
+# define ASIZE 4096
+#endif
+
+extern void abort (void);
+
+int __attribute__((noinline))
+foo (const char *s)
+{
+  if (!s)
+    return 1;
+  if (s[0] != 'a')
+    abort ();
+  s += ASIZE - 1;
+  if (s[0] != 'b')
+    abort ();
+  return 0;
+}
+
+int (*fn) (const char *) = foo;
+
+int __attribute__((noinline))
+bar (void)
+{
+  char s[ASIZE];
+  s[0] = 'a';
+  s[ASIZE - 1] = 'b';
+  foo (s);
+  foo (s);
+  return 0;
+}
+
+int __attribute__((noinline))
+baz (long i)
+{
+  if (i)
+    return fn (0);
+  else
+    {
+      char s[ASIZE];
+      s[0] = 'a';
+      s[ASIZE - 1] = 'b';
+      foo (s);
+      foo (s);
+      return fn (0);
+    }
+}
+
+int __attribute__((noinline))
+very_large_offset (int *p)
+{
+  char init_array[(uint64_t)0xfefefef];
+  char other_array[(uint64_t)0xfefefef];
+  return (int)init_array[p[1]] + (int)other_array[p[0]];
+}
+
diff --git a/gcc/testsuite/lib/hwasan-dg.exp b/gcc/testsuite/lib/hwasan-dg.exp
new file mode 100644
index 0000000000000000000000000000000000000000..13cb94b02943f3186cd18f5913bfbd24089af75a
--- /dev/null
+++ b/gcc/testsuite/lib/hwasan-dg.exp
@@ -0,0 +1,352 @@
+# Copyright (C) 2012-2019 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Return 1 if compilation with -fsanitize=hwaddress is error-free for trivial
+# code, 0 otherwise.
+
+proc check_effective_target_fsanitize_hwaddress {} {
+    if ![check_no_compiler_messages fsanitize_hwaddress executable {
+	int main (void) { return 0; }
+    }] {
+	return 0;
+    }
+
+    # hwasan doesn't work if there's a ulimit on virtual memory.
+    if ![is_remote target] {
+	if [catch {exec sh -c "ulimit -v"} ulimit_v] {
+	    # failed to get ulimit
+	} elseif [regexp {^[0-9]+$} $ulimit_v] {
+	    # ulimit -v gave a numeric limit
+	    warning "skipping hwasan tests due to ulimit -v"
+	    return 0;
+	}
+    }
+
+    return 1;
+}
+
+proc hwasan_include_flags {} {
+    global srcdir
+    global TESTING_IN_BUILD_TREE
+
+    set flags ""
+
+    if { [is_remote host] || ! [info exists TESTING_IN_BUILD_TREE] } {
+      return "${flags}"
+    }
+
+    set flags "-I$srcdir/../../libsanitizer/include"
+
+    return "$flags"
+}
+
+#
+# hwasan_link_flags -- compute library path and flags to find libhwasan.
+# (originally from g++.exp)
+#
+
+proc hwasan_link_flags { paths } {
+    global srcdir
+    global ld_library_path
+    global shlib_ext
+    global hwasan_saved_library_path
+
+    set gccpath ${paths}
+    set flags ""
+
+    set shlib_ext [get_shlib_extension]
+    set hwasan_saved_library_path $ld_library_path
+
+    if { $gccpath != "" } {
+      if { [file exists "${gccpath}/libsanitizer/hwasan/.libs/libhwasan.a"]
+	   || [file exists "${gccpath}/libsanitizer/hwasan/.libs/libhwasan.${shlib_ext}"] } {
+	  append flags " -B${gccpath}/libsanitizer/ "
+	  append flags " -B${gccpath}/libsanitizer/hwasan/ "
+	  append flags " -L${gccpath}/libsanitizer/hwasan/.libs "
+	  append ld_library_path ":${gccpath}/libsanitizer/hwasan/.libs"
+      }
+    } else {
+      global tool_root_dir
+
+      set libhwasan [lookfor_file ${tool_root_dir} libhwasan]
+      if { $libhwasan != "" } {
+	  append flags "-L${libhwasan} "
+	  append ld_library_path ":${libhwasan}"
+      }
+    }
+
+    set_ld_library_path_env_vars
+
+    return "$flags"
+}
+
+#
+# hwasan_init -- called at the start of each subdir of tests
+#
+
+proc hwasan_init { args } {
+    global TEST_ALWAYS_FLAGS
+    global ALWAYS_CXXFLAGS
+    global TOOL_OPTIONS
+    global hwasan_saved_TEST_ALWAYS_FLAGS
+    global hwasan_saved_ALWAYS_CXXFLAGS
+
+    set link_flags ""
+    if ![is_remote host] {
+	if [info exists TOOL_OPTIONS] {
+	    set link_flags "[hwasan_link_flags [get_multilibs ${TOOL_OPTIONS}]]"
+	} else {
+	    set link_flags "[hwasan_link_flags [get_multilibs]]"
+	}
+    }
+
+    set include_flags "[hwasan_include_flags]"
+
+    if [info exists TEST_ALWAYS_FLAGS] {
+	set hwasan_saved_TEST_ALWAYS_FLAGS $TEST_ALWAYS_FLAGS
+    }
+    if [info exists ALWAYS_CXXFLAGS] {
+	set hwasan_saved_ALWAYS_CXXFLAGS $ALWAYS_CXXFLAGS
+	set ALWAYS_CXXFLAGS [concat "{ldflags=$link_flags}" $ALWAYS_CXXFLAGS]
+	set ALWAYS_CXXFLAGS [concat "{additional_flags=-fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags}" $ALWAYS_CXXFLAGS]
+    } else {
+	if [info exists TEST_ALWAYS_FLAGS] {
+	    set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags $TEST_ALWAYS_FLAGS"
+	} else {
+	    set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags"
+	}
+    }
+}
+
+#
+# hwasan_finish -- called at the start of each subdir of tests
+#
+
+proc hwasan_finish { args } {
+    global TEST_ALWAYS_FLAGS
+    global hwasan_saved_TEST_ALWAYS_FLAGS
+    global hwasan_saved_ALWAYS_CXXFLAGS
+    global hwasan_saved_library_path
+    global ld_library_path
+
+    if [info exists hwasan_saved_ALWAYS_CXXFLAGS ] {
+	set ALWAYS_CXXFLAGS $hwasan_saved_ALWAYS_CXXFLAGS
+    } else {
+	if [info exists hwasan_saved_TEST_ALWAYS_FLAGS] {
+	    set TEST_ALWAYS_FLAGS $hwasan_saved_TEST_ALWAYS_FLAGS
+	} else {
+	    unset TEST_ALWAYS_FLAGS
+	}
+    }
+    set ld_library_path $hwasan_saved_library_path
+    set_ld_library_path_env_vars
+    clear_effective_target_cache
+}
+
+# Symbolize lines like
+#   #2 0xdeadbeef (/some/path/libsanitizer.so.0.0.0+0xbeef)
+# in $output using addr2line to
+#   #2 0xdeadbeef in foobar file:123
+proc hwasan_symbolize { output } {
+    set addresses [regexp -inline -all -line "^ *#\[0-9\]+ 0x\[0-9a-f\]+ \[(\](\[^)\]+)\[+\](0x\[0-9a-f\]+)\[)\]$" "$output"]
+    if { [llength $addresses] > 0 } {
+	set addr2line_name [find_binutils_prog addr2line]
+	set idx 1
+	while { $idx < [llength $addresses] } {
+	    set key [regsub -all "\[\]\[\]" [lindex $addresses $idx] "\\\\&"]
+	    set val [lindex $addresses [expr $idx + 1]]
+	    lappend arr($key) $val
+	    set idx [expr $idx + 3]
+	}
+	foreach key [array names arr] {
+	    set args "-f -e $key $arr($key)"
+	    set status [remote_exec host "$addr2line_name" "$args"]
+	    if { [lindex $status 0] > 0 } continue
+	    regsub -all "\r\n" [lindex $status 1] "\n" addr2line_output
+	    regsub -all "\[\n\r\]BFD: \[^\n\r\]*" $addr2line_output "" addr2line_output
+	    regsub -all "^BFD: \[^\n\r\]*\[\n\r\]" $addr2line_output "" addr2line_output
+	    set addr2line_output [regexp -inline -all -line "^\[^\n\r]*" $addr2line_output]
+	    set idx 0
+	    foreach val $arr($key) {
+		if { [expr $idx + 1] < [llength $addr2line_output] } {
+		    set fnname [lindex $addr2line_output $idx]
+		    set fileline [lindex $addr2line_output [expr $idx + 1]]
+		    if { "$fnname" != "??" } {
+			set newkey "$key+$val"
+			set repl($newkey) "$fnname $fileline"
+		    }
+		    set idx [expr $idx + 2]
+		}
+	    }
+	}
+	set idx 0
+	set new_output ""
+	while {[regexp -start $idx -indices " #\[0-9\]+ 0x\[0-9a-f\]+ \[(\](\[^)\]+\[+\]0x\[0-9a-f\]+)\[)\]" "$output" -> addr] > 0} {
+	    set low [lindex $addr 0]
+	    set high [lindex $addr 1]
+	    set val [string range "$output" $low $high]
+	    append new_output [string range "$output" $idx [expr $low - 2]]
+	    if [info exists repl($val)] {
+		append new_output "in $repl($val)"
+	    } else {
+		append new_output "($val)"
+	    }
+	    set idx [expr $high + 2]
+	}
+	append new_output [string range "$output" $idx [string length "$output"]]
+	return "$new_output"
+    }
+    return "$output"
+}
+
+# Return a list of gtest tests, printed in the form
+# DEJAGNU_GTEST_TEST AddressSanitizer_SimpleDeathTest
+# DEJAGNU_GTEST_TEST AddressSanitizer_VariousMallocsTest
+proc hwasan_get_gtest_test_list { output } {
+    set idx 0
+    set ret ""
+    while {[regexp -start $idx -indices "DEJAGNU_GTEST_TEST (\[^\n\r\]*)(\r\n|\n|\r)" "$output" -> testname] > 0} {
+	set low [lindex $testname 0]
+	set high [lindex $testname 1]
+	set val [string range "$output" $low $high]
+	lappend ret $val
+	set idx [expr $high + 1]
+    }
+    return $ret
+}
+
+# Return a list of gtest EXPECT_DEATH tests, printed in the form
+# DEJAGNU_GTEST_EXPECT_DEATH1 statement DEJAGNU_GTEST_EXPECT_DEATH1 regexp DEJAGNU_GTEST_EXPECT_DEATH1
+# DEJAGNU_GTEST_EXPECT_DEATH2 other statement DEJAGNU_GTEST_EXPECT_DEATH2 other regexp DEJAGNU_GTEST_EXPECT_DEATH2
+proc hwasan_get_gtest_expect_death_list { output } {
+    set idx 0
+    set ret ""
+    while {[regexp -start $idx -indices "DEJAGNU_GTEST_EXPECT_DEATH(\[0-9\]*)" "$output" -> id ] > 0} {
+	set low [lindex $id 0]
+	set high [lindex $id 1]
+	set val_id [string range "$output" $low $high]
+	if {[regexp -start $low -indices "$val_id (.*) DEJAGNU_GTEST_EXPECT_DEATH$val_id (.*) DEJAGNU_GTEST_EXPECT_DEATH$val_id\[\n\r\]" "$output" whole statement regexpr ] == 0} { break }
+	set low [lindex $statement 0]
+	set high [lindex $statement 1]
+	set val_statement [string range "$output" $low $high]
+	set low [lindex $regexpr 0]
+	set high [lindex $regexpr 1]
+	set val_regexpr [string range "$output" $low $high]
+	lappend ret [list "$val_id" "$val_statement" "$val_regexpr"]
+	set idx [lindex $whole 1]
+    }
+    return $ret
+}
+
+# Replace ${tool}_load with a wrapper so that we can symbolize the output.
+if { [info procs ${tool}_load] != [list] \
+      && [info procs saved_hwasan_${tool}_load] == [list] } {
+    rename ${tool}_load saved_hwasan_${tool}_load
+
+    proc ${tool}_load { program args } {
+	global tool
+	global hwasan_last_gtest_test_list
+	global hwasan_last_gtest_expect_death_list
+	set result [eval [list saved_hwasan_${tool}_load $program] $args]
+	set output [lindex $result 1]
+	set symbolized_output [hwasan_symbolize "$output"]
+	set hwasan_last_gtest_test_list [hwasan_get_gtest_test_list "$output"]
+	set hwasan_last_gtest_expect_death_list [hwasan_get_gtest_expect_death_list "$output"]
+	set result [list [lindex $result 0] $symbolized_output]
+	return $result
+    }
+}
+
+# Utility for running gtest hwasan emulation under dejagnu, invoked via dg-final.
+# Call pass if variable has the desired value, otherwise fail.
+#
+# Argument 0 handles expected failures and the like
+proc hwasan-gtest { args } {
+    global tool
+    global hwasan_last_gtest_test_list
+    global hwasan_last_gtest_expect_death_list
+
+    if { ![info exists hwasan_last_gtest_test_list] } { return }
+    if { [llength $hwasan_last_gtest_test_list] == 0 } { return }
+    if { ![isnative] || [is_remote target] } { return }
+
+    set gtest_test_list $hwasan_last_gtest_test_list
+    unset hwasan_last_gtest_test_list
+
+    if { [llength $args] >= 1 } {
+	switch [dg-process-target [lindex $args 0]] {
+	    "S" { }
+	    "N" { return }
+	    "F" { setup_xfail "*-*-*" }
+	    "P" { }
+	}
+    }
+
+    # This assumes that we are three frames down from dg-test, and that
+    # it still stores the filename of the testcase in a local variable "name".
+    # A cleaner solution would require a new DejaGnu release.
+    upvar 2 name testcase
+    upvar 2 prog prog
+
+    set output_file "[file rootname [file tail $prog]].exe"
+
+    foreach gtest $gtest_test_list {
+	set testname "$testcase $gtest"
+	set status -1
+
+	setenv DEJAGNU_GTEST_ARG "$gtest"
+	set result [${tool}_load ./$output_file $gtest]
+	unsetenv DEJAGNU_GTEST_ARG
+	set status [lindex $result 0]
+	set output [lindex $result 1]
+	if { "$status" == "pass" } {
+	    pass "$testname execution test"
+	    if { [info exists hwasan_last_gtest_expect_death_list] } {
+		set gtest_expect_death_list $hwasan_last_gtest_expect_death_list
+		foreach gtest_death $gtest_expect_death_list {
+		    set id [lindex $gtest_death 0]
+		    set testname "$testcase $gtest [lindex $gtest_death 1]"
+		    set regexpr [lindex $gtest_death 2]
+		    set status -1
+
+		    setenv DEJAGNU_GTEST_ARG "$gtest:$id"
+		    set result [${tool}_load ./$output_file "$gtest:$id"]
+		    unsetenv DEJAGNU_GTEST_ARG
+		    set status [lindex $result 0]
+		    set output [lindex $result 1]
+		    if { "$status" == "fail" } {
+			pass "$testname execution test"
+			if { ![regexp $regexpr ${output}] } {
+			    fail "$testname output pattern test"
+			    send_log "Output should match: $regexpr\n"
+			} else {
+			    pass "$testname output pattern test"
+			}
+		    } elseif { "$status" == "pass" } {
+			fail "$testname execution test"
+		    } else {
+			$status "$testname execution test"
+		    }
+		}
+	    }
+	} else {
+	    $status "$testname execution test"
+	}
+	unset hwasan_last_gtest_expect_death_list
+    }
+
+    return
+}


[-- Attachment #2: hwasan-patch6.patch.gz --]
[-- Type: application/gzip, Size: 12079 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 13/X] [libsanitizer][options] Add hwasan flags and argument parsing
  2019-11-07 18:01           ` Evgenii Stepanov via gcc-patches
@ 2019-11-08 13:56             ` Andrey Konovalov via gcc-patches
  0 siblings, 0 replies; 150+ messages in thread
From: Andrey Konovalov via gcc-patches @ 2019-11-08 13:56 UTC (permalink / raw)
  To: Evgenii Stepanov
  Cc: Matthew Malcomson, kcc, dvyukov, gcc-patches, nd, Martin Liska,
	Richard Earnshaw, Kyrylo Tkachov, dodji, jakub, kasan-dev

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?

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-11-07 18:37   ` [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
@ 2019-11-11 14:30     ` Martin Liška
  2019-11-11 16:13       ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2019-11-11 14:30 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, kcc, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 11/7/19 7:37 PM, Matthew Malcomson wrote:
> +@item @samp{bootstrap-hwasan}
> +Compiles GCC itself using HWAddress Sanitization in order to catch invalid
> +memory accesses within the GCC code.  This option is only available on AArch64
> +targets with a very recent linux kernel (5.4 or later).
> +

Apparently, I see this hunk being applied:

diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index 2cb8a342a2c..ed47796e052 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2671,6 +2671,11 @@ the build tree.
  
  @end table
  
+@item @samp{bootstrap-hwasan}
+Compiles GCC itself using HWAddress Sanitization in order to catch invalid
+memory accesses within the GCC code.  This option is only available on AArch64
+targets with a very recent linux kernel (5.4 or later).
+
  @section Building a cross compiler
  
  When building a cross compiler, it is not generally possible to do a

and then I see:

make[2]: Entering directory '/dev/shm/objdir/gcc'
if [ xinfo = xinfo ]; then \
	makeinfo --split-size=5000000 --split-size=5000000 --no-split -I /home/marxin/Programming/gcc/gcc/doc \
		-I /home/marxin/Programming/gcc/gcc/doc/include -o doc/gccinstall.info /home/marxin/Programming/gcc/gcc/doc/install.texi; \
fi
/home/marxin/Programming/gcc/gcc/doc/install.texi:2674: @item outside of table or list
make[2]: *** [Makefile:3279: doc/gccinstall.info] Error 1
make[2]: Leaving directory '/dev/shm/objdir/gcc'

Can you please check it?
Thanks,
Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-11-11 14:30     ` Martin Liška
@ 2019-11-11 16:13       ` Matthew Malcomson
  2019-11-12 12:10         ` Martin Liška
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-11 16:13 UTC (permalink / raw)
  To: Martin Liška, gcc-patches
  Cc: nd, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 2010 bytes --]

On 11/11/2019 14:30, Martin Liška wrote:
> On 11/7/19 7:37 PM, Matthew Malcomson wrote:
>> +@item @samp{bootstrap-hwasan}
>> +Compiles GCC itself using HWAddress Sanitization in order to catch 
>> invalid
>> +memory accesses within the GCC code.  This option is only available 
>> on AArch64
>> +targets with a very recent linux kernel (5.4 or later).
>> +
> 
> Apparently, I see this hunk being applied:
> 
> diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
> index 2cb8a342a2c..ed47796e052 100644
> --- a/gcc/doc/install.texi
> +++ b/gcc/doc/install.texi
> @@ -2671,6 +2671,11 @@ the build tree.
> 
>   @end table
> 
> +@item @samp{bootstrap-hwasan}
> +Compiles GCC itself using HWAddress Sanitization in order to catch invalid
> +memory accesses within the GCC code.  This option is only available on 
> AArch64
> +targets with a very recent linux kernel (5.4 or later).
> +
>   @section Building a cross compiler
> 
>   When building a cross compiler, it is not generally possible to do a
> 
> and then I see:
> 
> make[2]: Entering directory '/dev/shm/objdir/gcc'
> if [ xinfo = xinfo ]; then \
>      makeinfo --split-size=5000000 --split-size=5000000 --no-split -I 
> /home/marxin/Programming/gcc/gcc/doc \
>          -I /home/marxin/Programming/gcc/gcc/doc/include -o 
> doc/gccinstall.info /home/marxin/Programming/gcc/gcc/doc/install.texi; \
> fi
> /home/marxin/Programming/gcc/gcc/doc/install.texi:2674: @item outside of 
> table or list
> make[2]: *** [Makefile:3279: doc/gccinstall.info] Error 1
> make[2]: Leaving directory '/dev/shm/objdir/gcc'
> 
> Can you please check it?
> Thanks,
> Martin

Ah!
My apologies -- I sent up a series with a few documentation mistakes.
(the others were wording problems so less noticeable)

I'm attaching the entire updated patch series (with the other 
documentation fixes in it too) and the fixed patch for just this part in 
case you just want to compile and test right now.

Regards,
Matthew

[-- Attachment #2: all-patches.tar.gz --]
[-- Type: application/gzip, Size: 58520 bytes --]

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: patch-3.diff --]
[-- Type: text/x-patch; name="patch-3.diff", Size: 4932 bytes --]

diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index aec9186b2b0123d3088b69eb1ee541567654953e..6f71b111bd18ec053180beecf83dd4549e83c2b9 100755
--- a/configure
+++ b/configure
@@ -7270,7 +7270,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index b8ce2ad20b9d03e42731252a9ec2a8417c13e566..16bfdf164555dad94c789f17b6a63ba1a2e3e9f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2775,7 +2775,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index 6c9579bfaff955eb43875b404fb7db1a667bf522..da9a8809c3440827ac22ef6936e080820197f4e7 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2645,6 +2645,13 @@ Some examples of build configurations designed for developers of GCC are:
 Compiles GCC itself using Address Sanitization in order to catch invalid memory
 accesses within the GCC code.
 
+@item @samp{bootstrap-hwasan}
+Compiles GCC itself using HWAddress Sanitization in order to catch invalid
+memory accesses within the GCC code.  This option is only available on AArch64
+targets with a very recent linux kernel (5.4 or later).
+
+@end table
+
 @section Building a cross compiler
 
 When building a cross compiler, it is not generally possible to do a
diff --git a/libiberty/configure b/libiberty/configure
index 7a34dabec32b0b383bd33f07811757335f4dd39c..cb2dd4ff5295598343cc18b3a79a86a778f2261d 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5261,6 +5261,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index f1ce76010c9acde79c5dc46686a78b2e2f19244e..043237628b79cbf37d07359b59c5ffe17a7a22ef 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index 28dc21014b2e86988fa88adabd63ce6092e18e02..34aa397d785e3cc9b6975de460d065900364c3ff 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 8dd6e40ac9dddab39fe1752f9a70e6834ab3c926..7acfc047eff6f86f8d38287e6ffb6533c4c13500 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -672,8 +672,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-07 18:37 v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC Matthew Malcomson
  2019-11-05 11:32 ` Matthew Malcomson
@ 2019-11-11 16:16 ` Matthew Malcomson
  2019-11-11 16:21   ` Matthew Malcomson
  2019-11-20 14:33 ` Martin Liška
  2 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-11 16:16 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov, Martin Liska,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 07/11/2019 18:37, Matthew Malcomson wrote:
> I have rebased this series onto Martin Liska's patches that take the most
> recent libhwasan from upstream LLVM.
> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00340.html
> 
> I've also cleared up some nomenclature (I had previously used the word 'colour'
> a few times instead of the word 'tag' and that clashes with other descriptions)
> and based the patch series off a more recent GCC revision (r277678).
> 
> There's an ongoing discussion on whether to have __SANITIZER_ADDRESS__, or
> __SANITIZER_HWADDRESS__, but I'm keeping that discussion to the existing
> thread.
> 
> Similarly there's still the question around C++ exceptions that I'm keeping to
> the existing thread (on the first patch series).
> 
> 
> NOTE:
>    Unfortunately, there's a bug in the more recent version of GCC I rebased
>    onto.
>    Hwasan catches this when bootstrapping, which means bootstrapping with hwasan
>    fails.
>    I'm working on tracking the bug down now, but sending this series upstream
>    for visibility while that happens.
> 
>    Bugzilla link:
>    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92410
> 
> Entire patch series attached to cover letter.
> 

It seems that PR 92410 is a little more extensive than it originally 
looked (in fixing it I saw instances of the same basic problem elsewhere 
too).

Just in case anyone wants to take a look at this HWASAN series in the 
meantime there are two ways:

1) Bootstrap with hwasan not crashing on error.

2) Apply the last patch I posted on 
http://pdtlreviewboard.cambridge.arm.com/r/12149/


Number 1 requires the patch below and to run make with HWASAN_OPTIONS 
set to "halt_on_error=0 exitcode=0".

diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 91989f4..3672b91 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -4,8 +4,8 @@
  # breaks.  Running with a random frame tag gives approx. 50% chance of
  # bootstrap comparison diff in libiberty/alloca.c.

-STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
-STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE2_CFLAGS += -fsanitize-recover=hwaddress -fsanitize=hwaddress 
--param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize-recover=hwaddress -fsanitize=hwaddress 
--param hwasan-random-frame-tag=0
  POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
                       -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
                       -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \




Number 2 option avoids the current problem, but causes two extra 
testsuite failures (unrelated to hwasan) in gcc.dg/fold-eqandshift-1.c 
and gcc.c-torture/compile/pr32482.c.
These are existing bugs that a new `assert` catches.

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-11 16:16 ` v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC Matthew Malcomson
@ 2019-11-11 16:21   ` Matthew Malcomson
  0 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-11 16:21 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov, Martin Liska,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 11/11/2019 16:13, Matthew Malcomson wrote:
> On 07/11/2019 18:37, Matthew Malcomson wrote:
>> I have rebased this series onto Martin Liska's patches that take the most
>> recent libhwasan from upstream LLVM.
>> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00340.html
>>
>> I've also cleared up some nomenclature (I had previously used the word 'colour'
>> a few times instead of the word 'tag' and that clashes with other descriptions)
>> and based the patch series off a more recent GCC revision (r277678).
>>
>> There's an ongoing discussion on whether to have __SANITIZER_ADDRESS__, or
>> __SANITIZER_HWADDRESS__, but I'm keeping that discussion to the existing
>> thread.
>>
>> Similarly there's still the question around C++ exceptions that I'm keeping to
>> the existing thread (on the first patch series).
>>
>>
>> NOTE:
>>     Unfortunately, there's a bug in the more recent version of GCC I rebased
>>     onto.
>>     Hwasan catches this when bootstrapping, which means bootstrapping with hwasan
>>     fails.
>>     I'm working on tracking the bug down now, but sending this series upstream
>>     for visibility while that happens.
>>
>>     Bugzilla link:
>>     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92410
>>
>> Entire patch series attached to cover letter.
>>
> 
> It seems that PR 92410 is a little more extensive than it originally
> looked (in fixing it I saw instances of the same basic problem elsewhere
> too).
> 
> Just in case anyone wants to take a look at this HWASAN series in the
> meantime there are two ways:
> 
> 1) Bootstrap with hwasan not crashing on error.
> 
> 2) Apply the last patch I posted on
> http://pdtlreviewboard.cambridge.arm.com/r/12149/

Oops -- clipboard didn't have what I thought it did.
I meant https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92410 here.

> 
> 
> Number 1 requires the patch below and to run make with HWASAN_OPTIONS
> set to "halt_on_error=0 exitcode=0".
> 
> diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
> index 91989f4..3672b91 100644
> --- a/config/bootstrap-hwasan.mk
> +++ b/config/bootstrap-hwasan.mk
> @@ -4,8 +4,8 @@
>    # breaks.  Running with a random frame tag gives approx. 50% chance of
>    # bootstrap comparison diff in libiberty/alloca.c.
> 
> -STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
> -STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
> +STAGE2_CFLAGS += -fsanitize-recover=hwaddress -fsanitize=hwaddress
> --param hwasan-random-frame-tag=0
> +STAGE3_CFLAGS += -fsanitize-recover=hwaddress -fsanitize=hwaddress
> --param hwasan-random-frame-tag=0
>    POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
>                         -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
>                         -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
> 
> 
> 
> 
> Number 2 option avoids the current problem, but causes two extra
> testsuite failures (unrelated to hwasan) in gcc.dg/fold-eqandshift-1.c
> and gcc.c-torture/compile/pr32482.c.
> These are existing bugs that a new `assert` catches.
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-11-11 16:13       ` Matthew Malcomson
@ 2019-11-12 12:10         ` Martin Liška
  2019-11-13 15:25           ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2019-11-12 12:10 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 7407 bytes --]

On 11/11/19 5:03 PM, Matthew Malcomson wrote:
> Ah!
> My apologies -- I sent up a series with a few documentation mistakes.
> (the others were wording problems so less noticeable)

That's fine, I fixed that very easily.

Right now, I can confirm using a aarch64 KVM with the following linux kernel:
5.4.0-rc6-3.g7068448-default works. I haven't tried HWASAN bootstrap, but I can
run almost all hwasan.exp tests.

There are 2 exceptions:

FAIL: gcc.dg/hwasan/stack-tagging-basic-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  execution test
FAIL: gcc.dg/hwasan/large-aligned-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  execution test

These fail due to unused value of a function that returns int. The attached patch fixes that.
I'm planning to make a proper comments about the series starting next week.

For the meantime, I have some libsanitizer upstream suggestions
that you can may be discuss. It's mostly about
shadow memory dump differences in between ASAN and HWASAN:

Let's consider one example:

$ cat malloc.c

#include <stdlib.h>

int main(int argc, char **argv)
{
	char *ptr = malloc (argc);
	return ptr[1];
}

$ gcc malloc.c -fsanitize=address && ./a.out
=================================================================
==7319==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xffffaca007b1 at pc 0x0000004007a0 bp 0xfffff26df150 sp 0xfffff26df168
READ of size 1 at 0xffffaca007b1 thread T0
     #0 0x40079c in main (/home/marxin/Programming/gcc/a.out+0x40079c)
     #1 0xffffb0d3d3e8 in __libc_start_main (/lib64/libc.so.6+0x243e8)
     #2 0x400670  (/home/marxin/Programming/gcc/a.out+0x400670)

0xffffaca007b1 is located 0 bytes to the right of 1-byte region [0xffffaca007b0,0xffffaca007b1)
allocated by thread T0 here:
     #0 0xffffb0f2bdbc in __interceptor_malloc ../../../../libsanitizer/asan/asan_malloc_linux.cpp:145
     #1 0x400748 in main (/home/marxin/Programming/gcc/a.out+0x400748)
     #2 0xffffb0d3d3e8 in __libc_start_main (/lib64/libc.so.6+0x243e8)
     #3 0x400670  (/home/marxin/Programming/gcc/a.out+0x400670)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/marxin/Programming/gcc/a.out+0x40079c) in main
Shadow bytes around the buggy address:
   0x200ff59400a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
   0x200ff59400b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
   0x200ff59400c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
   0x200ff59400d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
   0x200ff59400e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x200ff59400f0: fa fa fa fa fa fa[01]fa fa fa fa fa fa fa fa fa
   0x200ff5940100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
   0x200ff5940110: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
   0x200ff5940120: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
   0x200ff5940130: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
   0x200ff5940140: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
   Addressable:           00
   Partially addressable: 01 02 03 04 05 06 07
   Heap left redzone:       fa
   Freed heap region:       fd
   Stack left redzone:      f1
   Stack mid redzone:       f2
   Stack right redzone:     f3
   Stack after return:      f5
   Stack use after scope:   f8
   Global redzone:          f9
   Global init order:       f6
   Poisoned by user:        f7
   Container overflow:      fc
   Array cookie:            ac
   Intra object redzone:    bb
   ASan internal:           fe
   Left alloca redzone:     ca
   Right alloca redzone:    cb
   Shadow gap:              cc
==7319==ABORTING

$ gcc malloc.c -fsanitize=hwaddress && ./a.out
==7329==ERROR: HWAddressSanitizer: tag-mismatch on address 0xefdeffffe001 at pc 0xffff804bbcd0
READ of size 1 at 0xefdeffffe001 tags: 03/01 (ptr/mem) in thread T0
     #0 0xffff804bbccc in SigTrap<0> ../../../../libsanitizer/hwasan/hwasan_checks.h:27
     #1 0xffff804bbccc in CheckAddress<(__hwasan::ErrorAction)0, (__hwasan::AccessType)0, 0> ../../../../libsanitizer/hwasan/hwasan_checks.h:88
     #2 0xffff804bbccc in __hwasan_load1 ../../../../libsanitizer/hwasan/hwasan.cpp:469
     #3 0x4007d4 in main (/home/marxin/Programming/gcc/a.out+0x4007d4)
     #4 0xffff8035e3e8 in __libc_start_main (/lib64/libc.so.6+0x243e8)
     #5 0x4006b0  (/home/marxin/Programming/gcc/a.out+0x4006b0)

[0xefdeffffe000,0xefdeffffe020) is a small allocated heap chunk; size: 32 offset: 1
0xefdeffffe001 is located 0 bytes to the right of 1-byte region [0xefdeffffe000,0xefdeffffe001)
allocated here:
     #0 0xffff804bd81c in __sanitizer_malloc ../../../../libsanitizer/hwasan/hwasan_interceptors.cpp:169
     #1 0x4007b8 in main (/home/marxin/Programming/gcc/a.out+0x4007b8)
     #2 0xffff8035e3e8 in __libc_start_main (/lib64/libc.so.6+0x243e8)
     #3 0x4006b0  (/home/marxin/Programming/gcc/a.out+0x4006b0)

Thread: T0 0xeffe00002000 stack: [0xffffd63c2000,0xffffd6bc2000) sz: 8388608 tls: [0xffff80e25020,0xffff80e25790)
Memory tags around the buggy address (one tag corresponds to 16 bytes):
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
=>[01] 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00 <=
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Tags for short granules around the buggy address (one tag corresponds to 16 bytes):
    ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
=>[03] ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  .. <=
    ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags
SUMMARY: HWAddressSanitizer: tag-mismatch ../../../../libsanitizer/hwasan/hwasan_checks.h:27 in SigTrap<0>

Improvements I see:
a) HWASAN uses less compact dump (2 spaces compared to one)
b) HWASAN is not using colors and it would be handy to know which color is used for "uninitialized" tags
    and I would mark the 2 compares tags in dumps (ptr/mem)
c) "Tags for short granules around the buggy address" dump is using a dot notation which seems a bit misleading
d) For HWASAN address offset is missing for each line in both shadow memory and the pointer

Thanks,
Martin

> 
> I'm attaching the entire updated patch series (with the other
> documentation fixes in it too) and the fixed patch for just this part in
> case you just want to compile and test right now.


[-- Attachment #2: fix-hwasan-tests.patch --]
[-- Type: text/x-patch, Size: 974 bytes --]

diff --git a/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c b/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c
index aba79e0acf3..03465fd20b9 100644
--- a/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c
+++ b/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c
@@ -21,10 +21,13 @@ handle_large_alignment (int num)
 #ifndef ARG
 #define ARG 1
 #endif
+
+int global;
+
 int __attribute__ ((noinline))
 main ()
 {
-  handle_large_alignment (ARG);
+  global += handle_large_alignment (ARG);
   return 0;
 }
 
diff --git a/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c
index 050a3eda7aa..3087bd596f9 100644
--- a/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c
+++ b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c
@@ -25,9 +25,12 @@ using_stack (int num)
 #ifndef ARG
 #define ARG 0
 #endif
+
+int global;
+
 int __attribute__ ((noinline))
 main ()
 {
-  using_stack (ARG);
+  global += using_stack (ARG);
   return 0;
 }

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-11-12 12:10         ` Martin Liška
@ 2019-11-13 15:25           ` Matthew Malcomson
  2019-11-20 14:38             ` Martin Liška
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-13 15:25 UTC (permalink / raw)
  To: Martin Liška, gcc-patches
  Cc: nd, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 12/11/2019 12:08, Martin Liška wrote:
> On 11/11/19 5:03 PM, Matthew Malcomson wrote:
>> Ah!
>> My apologies -- I sent up a series with a few documentation mistakes.
>> (the others were wording problems so less noticeable)
> 
> That's fine, I fixed that very easily.
> 
> Right now, I can confirm using a aarch64 KVM with the following linux 
> kernel:
> 5.4.0-rc6-3.g7068448-default works. I haven't tried HWASAN bootstrap, 
> but I can
> run almost all hwasan.exp tests.
> 
> There are 2 exceptions:
> 
> FAIL: gcc.dg/hwasan/stack-tagging-basic-1.c   -O2 -flto 
> -fuse-linker-plugin -fno-fat-lto-objects  execution test
> FAIL: gcc.dg/hwasan/large-aligned-1.c   -O2 -flto -fuse-linker-plugin 
> -fno-fat-lto-objects  execution test

Wow, I have no idea how I missed that but thanks for the catch!

> 
> These fail due to unused value of a function that returns int. The 
> attached patch fixes that.
> I'm planning to make a proper comments about the series starting next week.
> 
> For the meantime, I have some libsanitizer upstream suggestions
> that you can may be discuss. It's mostly about
> shadow memory dump differences in between ASAN and HWASAN:
> 

> 
> Improvements I see:
> a) HWASAN uses less compact dump (2 spaces compared to one)
> b) HWASAN is not using colors and it would be handy to know which color 
> is used for "uninitialized" tags
>     and I would mark the 2 compares tags in dumps (ptr/mem)
> c) "Tags for short granules around the buggy address" dump is using a 
> dot notation which seems a bit misleading
> d) For HWASAN address offset is missing for each line in both shadow 
> memory and the pointer

To mention my thoughts in turn:
a) The dump of HWASAN takes more space, but represents more application
    bytes for a given space on the console.
    Each byte of HWASAN shadow space represents 16 application bytes
    while each byte in ASAN shadow space represents 8 application bytes.
    I personally appreciate the extra spacing, and figure that the 8|16
    byte difference allows for this.

b) Marking 'ptr' and 'mem' in the dump sounds like a good idea to me.
    Exactly how I'm not sure -- maybe with a colourscheme?  Do you have a
    marking in mind?

    Uninitialised shadow space has the zero tag, however, there are a few
    extra details that help understanding these traces:

    On the stack, zero is both uninitialized and "the background" (i.e.
    the tag for anything not specially instrumented, like register spills
    and parameters passed on the stack).
    However, accessible tagged objects can be given a zero tag.
    We allow this to avoid runtime checks when incrementing the random
    frame tag to get the tag for a new local variable.
    We can easily avoid the zero tag at compile-time if we don't use a
    random tag for each frame.  I had this in development at one point
    and found it quite useful for verification.  I already have an option
    to disable random tags for each frame that this ability could go
    under.
    I don't believe (but am not 100% certain) this option is in LLVM.

    On the heap uninitialised is tag zero, but memory that has been
    `free`d is given a random tag, so non-zero in a dump does not mean a
    valid object.

c) Is there an alternate notation you have in mind?
    I would guess the "dots" are there to say "this granule has no
    short-tag information", and I'm not sure what would be a better
    way to demonstrate that.

d) I agree, an address offset annotation on each line of the shadow
    memory sounds useful.

Cheers,
MM

> 
> Thanks,
> Martin
> 
>>
>> I'm attaching the entire updated patch series (with the other
>> documentation fixes in it too) and the fixed patch for just this part in
>> case you just want to compile and test right now.
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 1/X] [libsanitizer] Tie the hwasan library into our build system
  2019-11-07 18:37   ` [PATCH 1/X] [libsanitizer] Tie the hwasan library into our build system Matthew Malcomson
@ 2019-11-20 11:13     ` Martin Liška
  0 siblings, 0 replies; 150+ messages in thread
From: Martin Liška @ 2019-11-20 11:13 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, kcc, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 11/7/19 7:37 PM, Matthew Malcomson wrote:
> +# This file is used to maintain libtool version info for libmudflap. See

Just a small nit here, the comment is a copy&paste leftover, please rename
it to libhwasan. I've just sent similar patch that fixes that in other
places:
https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01922.html

Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 4/X] [libsanitizer][options] Add hwasan flags and argument parsing
  2019-11-07 18:37   ` [PATCH 4/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
@ 2019-11-20 11:22     ` Martin Liška
  0 siblings, 0 replies; 150+ messages in thread
From: Martin Liška @ 2019-11-20 11:22 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, kcc, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 11/7/19 7:37 PM, Matthew Malcomson wrote:
> These flags can't be used at the same time as any of the other
> sanitizers.
> We add an equivalent flag to -static-libasan in -static-libhwasan to
> ensure static linking.
> 
> The -fsanitize=kernel-hwaddress option is for compiling targeting the
> kernel.  This flag has defaults that allow compiling KASAN with tags as
> it is currently implemented.
> These defaults are that we do not sanitize variables on the stack and
> always recover from a detected bug.
> Stack tagging in the kernel is a future aim, stack instrumentation has
> not yet been enabled for the kernel for clang either
> (https://lists.infradead.org/pipermail/linux-arm-kernel/2019-October/687121.html).
> 
> We introduce a backend hook `targetm.memtag.can_tag_addresses` that
> indicates to the mid-end whether a target has a feature like AArch64 TBI
> where the top byte of an address is ignored.
> Without this feature hwasan sanitization is not done.
> 
> gcc/ChangeLog:
> 
> 2019-11-07  Matthew Malcomson  <matthew.malcomson@arm.com>
> 
> 	* asan.c (memory_tagging_p): New.
> 	* asan.h (memory_tagging_p): New.
> 	* common.opt (flag_sanitize_recover): Default for kernel
> 	hwaddress.
> 	(static-libhwasan): New cli option.
> 	* config/aarch64/aarch64.c (aarch64_can_tag_addresses): New.
> 	(TARGET_MEMTAG_CAN_TAG_ADDRESSES): New.
> 	* config/gnu-user.h (LIBHWASAN_EARLY_SPEC): hwasan equivalent of
> 	asan command line flags.
> 	* cppbuiltin.c (define_builtin_macros_for_compilation_flags):
> 	Add hwasan equivalent of __SANITIZE_ADDRESS__.
> 	* doc/tm.texi: Document new hook.
> 	* doc/tm.texi.in: Document new hook.
> 	* flag-types.h (enum sanitize_code): New sanitizer values.
> 	* gcc.c (STATIC_LIBHWASAN_LIBS): New macro.
> 	(LIBHWASAN_SPEC): New macro.
> 	(LIBHWASAN_EARLY_SPEC): New macro.
> 	(SANITIZER_EARLY_SPEC): Update to include hwasan.
> 	(SANITIZER_SPEC): Update to include hwasan.
> 	(sanitize_spec_function): Use hwasan options.
> 	* opts.c (finish_options): Describe conflicts between address
> 	sanitizers.
> 	(sanitizer_opts): Introduce new sanitizer flags.
> 	(common_handle_option): Add defaults for kernel sanitizer.
> 	* params.def (PARAM_HWASAN_RANDOM_FRAME_TAG): New.
> 	(PARAM_HWASAN_STACK): New.
> 	* params.h (HWASAN_STACK): New.
> 	(HWASAN_RANDOM_FRAME_TAG): New.
> 	* target.def (HOOK_PREFIX): Add new hook.
> 	* targhooks.c (default_memtag_can_tag_addresses): New.
> 	* toplev.c (process_options): Ensure hwasan only on TBI
> 	architectures.
> 
> gcc/c-family/ChangeLog:
> 
> 2019-11-07  Matthew Malcomson  <matthew.malcomson@arm.com>
> 
> 	* c-attribs.c (handle_no_sanitize_hwaddress_attribute): New
> 	attribute.
> 
> 
> 
> ###############     Attachment also inlined for ease of reply    ###############
> 
> 
> diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
> index 1c9f28587fbb2348cc30e302e889a5a22906901a..a5e68061ff956018957b6be137a7b2f2b7353647 100644
> --- a/gcc/c-family/c-attribs.c
> +++ b/gcc/c-family/c-attribs.c
> @@ -54,6 +54,8 @@ static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
>   static tree handle_no_sanitize_attribute (tree *, tree, tree, int, bool *);
>   static tree handle_no_sanitize_address_attribute (tree *, tree, tree,
>   						  int, bool *);
> +static tree handle_no_sanitize_hwaddress_attribute (tree *, tree, tree,
> +						    int, bool *);
>   static tree handle_no_sanitize_thread_attribute (tree *, tree, tree,
>   						 int, bool *);
>   static tree handle_no_address_safety_analysis_attribute (tree *, tree, tree,
> @@ -412,6 +414,8 @@ const struct attribute_spec c_common_attribute_table[] =
>   			      handle_no_sanitize_attribute, NULL },
>     { "no_sanitize_address",    0, 0, true, false, false, false,
>   			      handle_no_sanitize_address_attribute, NULL },
> +  { "no_sanitize_hwaddress",    0, 0, true, false, false, false,
> +			      handle_no_sanitize_hwaddress_attribute, NULL },
>     { "no_sanitize_thread",     0, 0, true, false, false, false,
>   			      handle_no_sanitize_thread_attribute, NULL },
>     { "no_sanitize_undefined",  0, 0, true, false, false, false,
> @@ -941,6 +945,22 @@ handle_no_sanitize_address_attribute (tree *node, tree name, tree, int,
>     return NULL_TREE;
>   }
>   
> +/* Handle a "no_sanitize_hwaddress" attribute; arguments as in
> +   struct attribute_spec.handler.  */
> +
> +static tree
> +handle_no_sanitize_hwaddress_attribute (tree *node, tree name, tree, int,
> +				      bool *no_add_attrs)
> +{
> +  *no_add_attrs = true;
> +  if (TREE_CODE (*node) != FUNCTION_DECL)
> +    warning (OPT_Wattributes, "%qE attribute ignored", name);
> +  else
> +    add_no_sanitize_value (*node, SANITIZE_HWADDRESS);
> +
> +  return NULL_TREE;
> +}
> +
>   /* Handle a "no_sanitize_thread" attribute; arguments as in
>      struct attribute_spec.handler.  */
>   
> diff --git a/gcc/common.opt b/gcc/common.opt
> index cc279f411d7967209a111d958437595ce5cacd3a..d44295530474f873a00bc97a7d46bdd26255db9c 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -215,7 +215,7 @@ unsigned int flag_sanitize
>   
>   ; What sanitizers should recover from errors
>   Variable
> -unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
> +unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS | SANITIZE_KERNEL_HWADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
>   
>   ; What the coverage sanitizers should instrument
>   Variable
> @@ -3313,6 +3313,9 @@ Driver
>   static-libasan
>   Driver
>   
> +static-libhwasan
> +Driver
> +
>   static-libtsan
>   Driver
>   
> diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
> index 599d07a729e7438080f8b5240ee95037a49fb983..e0c7e2c6276d91bd0efe5e9bf9b58b09087c3cd7 100644
> --- a/gcc/config/aarch64/aarch64.c
> +++ b/gcc/config/aarch64/aarch64.c
> @@ -21371,6 +21371,15 @@ aarch64_stack_protect_guard (void)
>     return NULL_TREE;
>   }
>   
> +/* Implement TARGET_MEMTAG_CAN_TAG_ADDRESSES.  Here we tell the rest of the
> +   compiler that we automatically ignore the top byte of our pointers, which
> +   allows using -fsanitize=hwaddress.  */
> +bool
> +aarch64_can_tag_addresses ()
> +{
> +  return true;
> +}
> +
>   /* Implement TARGET_ASM_FILE_END for AArch64.  This adds the AArch64 GNU NOTE
>      section at the end if needed.  */
>   #define GNU_PROPERTY_AARCH64_FEATURE_1_AND	0xc0000000
> @@ -21936,6 +21945,9 @@ aarch64_libgcc_floating_mode_supported_p
>   #undef TARGET_FNTYPE_ABI
>   #define TARGET_FNTYPE_ABI aarch64_fntype_abi
>   
> +#undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
> +#define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
> +
>   #if CHECKING_P
>   #undef TARGET_RUN_TARGET_SELFTESTS
>   #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
> diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h
> index 95a3c29f7cee86336f958bef1d7fe56b82e05e6c..90b1fa91742c6a7d76aa6c7e931f8014fc4fff0c 100644
> --- a/gcc/config/gnu-user.h
> +++ b/gcc/config/gnu-user.h
> @@ -129,14 +129,18 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>   /* Link -lasan early on the command line.  For -static-libasan, don't link
>      it for -shared link, the executable should be compiled with -static-libasan
>      in that case, and for executable link with --{,no-}whole-archive around
> -   it to force everything into the executable.  And similarly for -ltsan
> -   and -llsan.  */
> +   it to force everything into the executable.  And similarly for -ltsan,
> +   -lhwasan, and -llsan.  */
>   #if defined(HAVE_LD_STATIC_DYNAMIC)
>   #undef LIBASAN_EARLY_SPEC
>   #define LIBASAN_EARLY_SPEC "%{!shared:libasan_preinit%O%s} " \
>     "%{static-libasan:%{!shared:" \
>     LD_STATIC_OPTION " --whole-archive -lasan --no-whole-archive " \
>     LD_DYNAMIC_OPTION "}}%{!static-libasan:-lasan}"
> +#undef LIBHWASAN_EARLY_SPEC
> +#define LIBHWASAN_EARLY_SPEC "%{static-libhwasan:%{!shared:" \
> +  LD_STATIC_OPTION " --whole-archive -lhwasan --no-whole-archive " \
> +  LD_DYNAMIC_OPTION "}}%{!static-libhwasan:-lhwasan}"
>   #undef LIBTSAN_EARLY_SPEC
>   #define LIBTSAN_EARLY_SPEC "%{!shared:libtsan_preinit%O%s} " \
>     "%{static-libtsan:%{!shared:" \
> diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c
> index 60e5bedc3665a25fa51c2eca00547f12a9953778..e8d0bedfc2eb22d1e72e7e4875155202c8389a38 100644
> --- a/gcc/cppbuiltin.c
> +++ b/gcc/cppbuiltin.c
> @@ -93,6 +93,9 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile)
>     if (flag_sanitize & SANITIZE_ADDRESS)
>       cpp_define (pfile, "__SANITIZE_ADDRESS__");
>   
> +  if (flag_sanitize & SANITIZE_HWADDRESS)
> +    cpp_define (pfile, "__SANITIZE_HWADDRESS__");
> +
>     if (flag_sanitize & SANITIZE_THREAD)
>       cpp_define (pfile, "__SANITIZE_THREAD__");
>   
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index d1eb317f43cd31156c80ef7dd910024c50f33deb..f710e29549f5d3d58ed74aca74f2c62b577cd155 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -12594,13 +12594,33 @@ more details.  The run-time behavior can be influenced using the
>   the available options are shown at startup of the instrumented program.  See
>   @url{https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags}
>   for a list of supported options.
> -The option cannot be combined with @option{-fsanitize=thread}.
> +The option cannot be combined with @option{-fsanitize=thread} or
> +@option{-fsanitize=hwaddress}.

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

>   
>   @item -fsanitize=kernel-address
>   @opindex fsanitize=kernel-address
>   Enable AddressSanitizer for Linux kernel.
>   See @uref{https://github.com/google/kasan/wiki} for more details.
>   
> +@item -fsanitize=hwaddress
> +@opindex fsanitize=hwaddress
> +Enable HardwareAddressSanitizer, a fast memory error detector.
> +Memory access instructions are instrumented to detect out-of-bounds and
> +use-after-free bugs.
> +The option enables @option{-fsanitize-address-use-after-scope}.
> +See @uref{https://github.com/google/sanitizers/} for

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

> +more details.  The run-time behavior can be influenced using the
> +@env{HWASAN_OPTIONS} environment variable.  When set to @code{help=1},
> +the available options are shown at startup of the instrumented program.
> +The option cannot be combined with @option{-fsanitize=thread} or
> +@option{-fsanitize=address}.
> +
> +@item -fsanitize=kernel-hwaddress
> +@opindex fsanitize=kernel-hwaddress
> +Enable HardwareAddressSanitizer for Linux kernel.
> +Similar to @option{-fsanitize=kernel-address} but using the software tagging
> +method of instrumentation.
> +
>   @item -fsanitize=pointer-compare
>   @opindex fsanitize=pointer-compare
>   Instrument comparison operation (<, <=, >, >=) with pointer operands.
> diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
> index cd9aed9874f4e6b2b0e2f8956ed6155975e643a8..e842b734c9c20253986880ba3622a8f692d3ca88 100644
> --- a/gcc/doc/tm.texi
> +++ b/gcc/doc/tm.texi
> @@ -2978,6 +2978,10 @@ This hook defines the machine mode to use for the boolean result of  conditional
>   A target hook which lets a backend compute the set of pressure classes to  be used by those optimization passes which take register pressure into  account, as opposed to letting IRA compute them.  It returns the number of  register classes stored in the array @var{pressure_classes}.
>   @end deftypefn
>   
> +@deftypefn {Target Hook} bool TARGET_MEMTAG_CAN_TAG_ADDRESSES ()
> +True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that -fsanitize=hwaddress can work.
> +@end deftypefn
> +
>   @node Stack and Calling
>   @section Stack Layout and Calling Conventions
>   @cindex calling conventions
> diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
> index 2739e9ceec5ad7253ff9135da8dbe3bf6010e8d7..6136ac1a5fe0c0980d5b5123b67b102a1b1e0bcc 100644
> --- a/gcc/doc/tm.texi.in
> +++ b/gcc/doc/tm.texi.in
> @@ -2377,6 +2377,8 @@ in the reload pass.
>   
>   @hook TARGET_COMPUTE_PRESSURE_CLASSES
>   
> +@hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
> +
>   @node Stack and Calling
>   @section Stack Layout and Calling Conventions
>   @cindex calling conventions
> diff --git a/gcc/flag-types.h b/gcc/flag-types.h
> index a2103282d469db31ad157a87572068d943061c8c..57d8ff9a1a010409d966230140df1017bc3584a8 100644
> --- a/gcc/flag-types.h
> +++ b/gcc/flag-types.h
> @@ -256,6 +256,9 @@ enum sanitize_code {
>     SANITIZE_BUILTIN = 1UL << 25,
>     SANITIZE_POINTER_COMPARE = 1UL << 26,
>     SANITIZE_POINTER_SUBTRACT = 1UL << 27,
> +  SANITIZE_HWADDRESS = 1UL << 28,
> +  SANITIZE_USER_HWADDRESS = 1UL << 29,
> +  SANITIZE_KERNEL_HWADDRESS = 1UL << 30,
>     SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT,
>     SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
>   		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
> diff --git a/gcc/gcc.c b/gcc/gcc.c
> index c45a1df656b2b8122f457edeb55db5f44c57b5bf..037f9d3a688d8778b067aade698d40709ee97b3d 100644
> --- a/gcc/gcc.c
> +++ b/gcc/gcc.c
> @@ -708,6 +708,24 @@ proper position among the other output files.  */
>   #define LIBASAN_EARLY_SPEC ""
>   #endif
>   
> +#ifndef LIBHWASAN_SPEC
> +#define STATIC_LIBHWASAN_LIBS \
> +  " %{static-libhwasan|static:%:include(libsanitizer.spec)%(link_libhwasan)}"
> +#ifdef LIBHWASAN_EARLY_SPEC
> +#define LIBHWASAN_SPEC STATIC_LIBHWASAN_LIBS
> +#elif defined(HAVE_LD_STATIC_DYNAMIC)
> +#define LIBHWASAN_SPEC "%{static-libhwasan:" LD_STATIC_OPTION \
> +		     "} -lhwasan %{static-libhwasan:" LD_DYNAMIC_OPTION "}" \
> +		     STATIC_LIBHWASAN_LIBS
> +#else
> +#define LIBHWASAN_SPEC "-lhwasan" STATIC_LIBHWASAN_LIBS
> +#endif
> +#endif
> +
> +#ifndef LIBHWASAN_EARLY_SPEC
> +#define LIBHWASAN_EARLY_SPEC ""
> +#endif
> +
>   #ifndef LIBTSAN_SPEC
>   #define STATIC_LIBTSAN_LIBS \
>     " %{static-libtsan|static:%:include(libsanitizer.spec)%(link_libtsan)}"
> @@ -982,6 +1000,7 @@ proper position among the other output files.  */
>   #ifndef SANITIZER_EARLY_SPEC
>   #define SANITIZER_EARLY_SPEC "\
>   %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_EARLY_SPEC "} \
> +    %{%:sanitize(hwaddress):" LIBHWASAN_EARLY_SPEC "} \
>       %{%:sanitize(thread):" LIBTSAN_EARLY_SPEC "} \
>       %{%:sanitize(leak):" LIBLSAN_EARLY_SPEC "}}}}"
>   #endif
> @@ -991,6 +1010,8 @@ proper position among the other output files.  */
>   #define SANITIZER_SPEC "\
>   %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_SPEC "\
>       %{static:%ecannot specify -static with -fsanitize=address}}\
> +    %{%:sanitize(hwaddress):" LIBHWASAN_SPEC "\
> +	%{static:%ecannot specify -static with -fsanitize=hwaddress}}\
>       %{%:sanitize(thread):" LIBTSAN_SPEC "\
>       %{static:%ecannot specify -static with -fsanitize=thread}}\
>       %{%:sanitize(undefined):" LIBUBSAN_SPEC "}\
> @@ -9439,8 +9460,12 @@ sanitize_spec_function (int argc, const char **argv)
>   
>     if (strcmp (argv[0], "address") == 0)
>       return (flag_sanitize & SANITIZE_USER_ADDRESS) ? "" : NULL;
> +  if (strcmp (argv[0], "hwaddress") == 0)
> +    return (flag_sanitize & SANITIZE_USER_HWADDRESS) ? "" : NULL;
>     if (strcmp (argv[0], "kernel-address") == 0)
>       return (flag_sanitize & SANITIZE_KERNEL_ADDRESS) ? "" : NULL;
> +  if (strcmp (argv[0], "kernel-hwaddress") == 0)
> +    return (flag_sanitize & SANITIZE_KERNEL_HWADDRESS) ? "" : NULL;
>     if (strcmp (argv[0], "thread") == 0)
>       return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
>     if (strcmp (argv[0], "undefined") == 0)
> diff --git a/gcc/opts.c b/gcc/opts.c
> index 10b9f108f8d0618501c1567bb4f8a7f9de2bdb98..b46c6d44164c04aa7e561fa3dc2513d66ce61e4d 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -1119,6 +1119,13 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
>   		  "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>");
>       }
>   
> +  /* Userspace and kernel HWasan conflict with each other.  */
> +  if ((opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS)
> +      && (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS))
> +    error_at (loc,
> +	      "%<-fsanitize=hwaddress%> is incompatible with "
> +	      "%<-fsanitize=kernel-hwaddress%>");
> +
>     /* Userspace and kernel ASan conflict with each other.  */
>     if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
>         && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
> @@ -1138,6 +1145,20 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
>       error_at (loc,
>   	      "%<-fsanitize=leak%> is incompatible with %<-fsanitize=thread%>");
>   
> +  /* HWASan and ASan conflict with each other.  */
> +  if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
> +      && (opts->x_flag_sanitize & SANITIZE_HWADDRESS))
> +    error_at (loc,
> +	      "%<-fsanitize=hwaddress%> is incompatible with both "
> +	      "%<-fsanitize=address%> and %<-fsanitize=kernel-address%>");
> +
> +  /* HWASan conflicts with TSan.  */
> +  if ((opts->x_flag_sanitize & SANITIZE_HWADDRESS)
> +      && (opts->x_flag_sanitize & SANITIZE_THREAD))
> +    error_at (loc,
> +	      "%<-fsanitize=hwaddress%> is incompatible with "
> +	      "%<-fsanitize=thread%>");
> +
>     /* Check error recovery for -fsanitize-recover option.  */
>     for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
>       if ((opts->x_flag_sanitize_recover & sanitizer_opts[i].flag)
> @@ -1157,7 +1178,8 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
>   
>     /* Enable -fsanitize-address-use-after-scope if address sanitizer is
>        enabled.  */
> -  if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
> +  if (((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
> +       || (opts->x_flag_sanitize & SANITIZE_USER_HWADDRESS))
>         && !opts_set->x_flag_sanitize_address_use_after_scope)
>       opts->x_flag_sanitize_address_use_after_scope = true;
>   
> @@ -1786,8 +1808,13 @@ const struct sanitizer_opts_s sanitizer_opts[] =
>   #define SANITIZER_OPT(name, flags, recover) \
>       { #name, flags, sizeof #name - 1, recover }
>     SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true),
> +  SANITIZER_OPT (hwaddress, (SANITIZE_HWADDRESS | SANITIZE_USER_HWADDRESS),
> +		 true),
>     SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
>   		 true),
> +  SANITIZER_OPT (kernel-hwaddress,
> +		 (SANITIZE_HWADDRESS | SANITIZE_KERNEL_HWADDRESS),
> +		 true),
>     SANITIZER_OPT (pointer-compare, SANITIZE_POINTER_COMPARE, true),
>     SANITIZER_OPT (pointer-subtract, SANITIZE_POINTER_SUBTRACT, true),
>     SANITIZER_OPT (thread, SANITIZE_THREAD, false),
> @@ -2322,6 +2349,14 @@ common_handle_option (struct gcc_options *opts,
>   				 opts->x_param_values,
>   				 opts_set->x_param_values);
>   	}
> +      if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
> +	{
> +	  maybe_set_param_value (PARAM_HWASAN_STACK, 0, opts->x_param_values,
> +				 opts_set->x_param_values);
> +	  maybe_set_param_value (PARAM_HWASAN_RANDOM_FRAME_TAG, 0,
> +				 opts->x_param_values,
> +				 opts_set->x_param_values);
> +	}

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

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

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

Martin

> +
>   DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
>   	  "uninit-control-dep-attempts",
>   	  "Maximum number of nested calls to search for control dependencies "
> diff --git a/gcc/params.h b/gcc/params.h
> index 1aaef6d6a00da79533eb0bba540dd8c731b1eaa8..102d79d42409590040ad9fe0beed0be73c8cc50a 100644
> --- a/gcc/params.h
> +++ b/gcc/params.h
> @@ -250,5 +250,9 @@ extern void init_param_values (int *params);
>     PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
>   #define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
>     ((unsigned) PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD))
> +#define HWASAN_STACK \
> +  PARAM_VALUE (PARAM_HWASAN_STACK)
> +#define HWASAN_RANDOM_FRAME_TAG \
> +  PARAM_VALUE (PARAM_HWASAN_RANDOM_FRAME_TAG)
>   
>   #endif /* ! GCC_PARAMS_H */
> diff --git a/gcc/target.def b/gcc/target.def
> index 8e83c2c7a7136511c07a5bc9e18876c91a38b955..ad16a151b6af9b1ee13918c8f2980280d75b1d90 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -6742,6 +6742,17 @@ DEFHOOK
>   HOOK_VECTOR_END (mode_switching)
>   
>   #undef HOOK_PREFIX
> +#define HOOK_PREFIX "TARGET_MEMTAG_"
> +HOOK_VECTOR (TARGET_MEMTAG_, memtag)
> +
> +DEFHOOK
> +(can_tag_addresses,
> + "True if backend architecture naturally supports ignoring the top byte of\
> + pointers.  This feature means that -fsanitize=hwaddress can work.",
> + bool, (), default_memtag_can_tag_addresses)
> +
> +HOOK_VECTOR_END (memtag)
> +#undef HOOK_PREFIX
>   #define HOOK_PREFIX "TARGET_"
>   
>   #define DEF_TARGET_INSN(NAME, PROTO) \
> diff --git a/gcc/targhooks.h b/gcc/targhooks.h
> index d4c3563e82587feea9523a15c2dcc490dad9919e..94e865259f35e46e26f6b4763c5e2f9dc9ed1b83 100644
> --- a/gcc/targhooks.h
> +++ b/gcc/targhooks.h
> @@ -282,4 +282,5 @@ extern bool default_have_speculation_safe_value (bool);
>   extern bool speculation_safe_value_not_needed (bool);
>   extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
>   
> +extern bool default_memtag_can_tag_addresses ();
>   #endif /* GCC_TARGHOOKS_H */
> diff --git a/gcc/targhooks.c b/gcc/targhooks.c
> index fee4cc271cd86a2206e18da2c2db24c5e04b0f4f..6e9e877c32e2c8705056bdf0ce2b1b6f125d93c3 100644
> --- a/gcc/targhooks.c
> +++ b/gcc/targhooks.c
> @@ -2365,4 +2365,10 @@ default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
>     return result;
>   }
>   
> +bool
> +default_memtag_can_tag_addresses ()
> +{
> +  return false;
> +}
> +
>   #include "gt-targhooks.h"
> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index 00a5e83212601bef62ea9a254ddbeae35cde8e6e..ab67384249a3437ac37f42f741ed516884677f9f 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -1753,6 +1753,16 @@ process_options (void)
>         flag_sanitize &= ~SANITIZE_ADDRESS;
>       }
>   
> +  /* HWAsan requires top byte ignore feature in the backend.  */
> +  if (flag_sanitize & SANITIZE_HWADDRESS
> +      && ! targetm.memtag.can_tag_addresses ())
> +    {
> +      warning_at (UNKNOWN_LOCATION, 0,
> +		  "%<-fsanitize=hwaddress%> can not be implemented on "
> +		  "a backend that does not ignore the top byte of a pointer");
> +      flag_sanitize &= ~SANITIZE_HWADDRESS;
> +    }
> +
>    /* Do not use IPA optimizations for register allocation if profiler is active
>       or patchable function entries are inserted for run-time instrumentation
>       or port does not emit prologue and epilogue as RTL.  */
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN
  2019-11-07 18:37   ` [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN Matthew Malcomson
@ 2019-11-20 14:06     ` Martin Liška
  2019-11-20 14:42       ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2019-11-20 14:06 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, kcc, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 11/7/19 7:37 PM, Matthew Malcomson wrote:
> Handling stack variables has three features.
> 
> 1) Ensure HWASAN required alignment for stack variables
> 
> When tagging shadow memory, we need to ensure that each tag granule is
> only used by one variable at a time.
> 
> This is done by ensuring that each tagged variable is aligned to the tag
> granule representation size and also ensure that the end of each
> variable as an alignment boundary between the end and the start of any
> other data stored on the stack.
> 
> This patch ensures that by adding alignment requirements in
> `align_local_variable` and forcing all stack variable allocation to be
> deferred so that `expand_stack_vars` can ensure the stack pointer is
> aligned before allocating any variable for the current frame.
> 
> 2) Put tags into each stack variable pointer
> 
> Make sure that every pointer to a stack variable includes a tag of some
> sort on it.
> 
> The way tagging works is:
>    1) For every new stack frame, a random tag is generated.
>    2) A base register is formed from the stack pointer value and this
>       random tag.
>    3) References to stack variables are now formed with RTL describing an
>       offset from this base in both tag and value.
> 
> The random tag generation is handled by a backend hook.  This hook
> decides whether to introduce a random tag or use the stack background
> based on the parameter hwasan-random-frame-tag.  Using the stack
> background is necessary for testing and bootstrap.  It is necessary
> during bootstrap to avoid breaking the `configure` test program for
> determining stack direction.
> 
> Using the stack background means that every stack frame has the initial
> tag of zero and variables are tagged with incrementing tags from 1,
> which also makes debugging a bit easier.
> 
> The tag&value offsets are also handled by a backend hook.
> 
> This patch also adds some macros defining how the HWASAN shadow memory
> is stored and how a tag is stored in a pointer.
> 
> 3) For each stack variable, tag and untag the shadow stack on function
>     prologue and epilogue.
> 
> On entry to each function we tag the relevant shadow stack region for
> each stack variable the tag to match the tag added to each pointer for
> that variable.
> 
> This is the first patch where we use the HWASAN shadow space, so we need
> to add in the libhwasan initialisation code that creates this shadow
> memory region into the binary we produce.  This instrumentation is done
> in `compile_file`.
> 
> When exiting a function we need to ensure the shadow stack for this
> function has no remaining tag.  Without clearing the shadow stack area
> for this stack frame, later function calls could get false positives
> when those later function calls check untagged areas (such as parameters
> passed on the stack) against a shadow stack area with left-over tag.
> 
> Hence we ensure that the entire stack frame is cleared on function exit.
> 
> gcc/ChangeLog:
> 
> 2019-11-07  Matthew Malcomson  <matthew.malcomson@arm.com>
> 
> 	* asan.c (hwasan_record_base): New function.
> 	(hwasan_emit_untag_frame): New.
> 	(hwasan_increment_tag): New function.
> 	(hwasan_with_tag): New function.
> 	(hwasan_tag_init): New function.
> 	(initialize_sanitizer_builtins): Define new builtins.
> 	(ATTR_NOTHROW_LIST): New macro.
> 	(hwasan_current_tag): New.
> 	(hwasan_emit_prologue): New.
> 	(hwasan_create_untagged_base): New.
> 	(hwasan_finish_file): New.
> 	(hwasan_sanitize_stack_p): New.
> 	(memory_tagging_p): New.
> 	* asan.h (hwasan_record_base): New declaration.
> 	(hwasan_emit_untag_frame): New.
> 	(hwasan_increment_tag): New declaration.
> 	(hwasan_with_tag): New declaration.
> 	(hwasan_sanitize_stack_p): New declaration.
> 	(hwasan_tag_init): New declaration.
> 	(memory_tagging_p): New declaration.
> 	(HWASAN_TAG_SIZE): New macro.
> 	(HWASAN_TAG_GRANULE_SIZE):New macro.
> 	(HWASAN_SHIFT):New macro.
> 	(HWASAN_SHIFT_RTX):New macro.
> 	(HWASAN_STACK_BACKGROUND):New macro.
> 	(hwasan_finish_file): New.
> 	(hwasan_current_tag): New.
> 	(hwasan_create_untagged_base): New.
> 	(hwasan_emit_prologue): New.
> 	* cfgexpand.c (struct stack_vars_data): Add information to
> 	record hwasan variable stack offsets.
> 	(expand_stack_vars): Ensure variables are offset from a tagged
> 	base. Record offsets for hwasan. Ensure alignment.
> 	(expand_used_vars): Call function to emit prologue, and get
> 	untagging instructions for function exit.
> 	(align_local_variable): Ensure alignment.
> 	(defer_stack_allocation): Ensure all variables are deferred so
> 	they can be handled by `expand_stack_vars`.
> 	(expand_one_stack_var_at): Account for tags in
> 	variables when using HWASAN.
> 	(expand_one_stack_var_1): Pass new argument to
> 	expand_one_stack_var_at.
> 	(init_vars_expansion): Initialise hwasan internal variables when
> 	starting variable expansion.
> 	* doc/tm.texi (TARGET_MEMTAG_GENTAG): Document.
> 	* doc/tm.texi.in (TARGET_MEMTAG_GENTAG): Document.
> 	* explow.c (get_dynamic_stack_base): Parametrise stack vars RTX
> 	base.
> 	* explow.h (get_dynamic_stack_base): New declaration.
> 	* expr.c (force_operand): Use new addtag_force_operand hook.
> 	* target.def (TARGET_MEMTAG_GENTAG, TARGET_MEMTAG_ADDTAG,
> 	TARGET_MEMTAG_ADDTAG_FORCE_OPERAND): Introduce new hooks.
> 	* targhooks.c (default_memtag_gentag, default_memtag_addtag):
> 	New default hooks.
> 	* targhooks.h (default_memtag_gentag, default_memtag_addtag):
> 	Declare new default hooks.
> 	* builtin-types.def (BT_FN_VOID_PTR_UINT8_SIZE): New.
> 	* builtins.def (DEF_SANITIZER_BUILTIN): Enable for HWASAN.
> 	* sanitizer.def (BUILT_IN_HWASAN_INIT): New.
> 	(BUILT_IN_HWASAN_TAG_MEM): New.
> 	* toplev.c (compile_file): Emit libhwasan initialisation.
> 
> 
> 
> ###############     Attachment also inlined for ease of reply    ###############
> 
> 
> diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
> index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
> --- a/config/bootstrap-hwasan.mk
> +++ b/config/bootstrap-hwasan.mk
> @@ -1,7 +1,11 @@
>   # This option enables -fsanitize=hwaddress for stage2 and stage3.
> +# We need to disable random frame tags for bootstrap since the autoconf check
> +# for which direction the stack is growing has UB that a random frame tag
> +# breaks.  Running with a random frame tag gives approx. 50% chance of
> +# bootstrap comparison diff in libiberty/alloca.c.

Here I would like to see what's exactly the problem. I would expect ASAN will
have exactly the same problem? Can you please isolate it and file a bug. I bet
a configure script should not expose an undefined behavior.

>   
> -STAGE2_CFLAGS += -fsanitize=hwaddress
> -STAGE3_CFLAGS += -fsanitize=hwaddress
> +STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
> +STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
>   POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
>   		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
>   		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
> diff --git a/gcc/asan.h b/gcc/asan.h
> index 7675f18a84ee3f187ba4cb40db0ce232f3958762..467231f8dad031a6176aeaddb9414f768b2af3fc 100644
> --- a/gcc/asan.h
> +++ b/gcc/asan.h
> @@ -23,6 +23,18 @@ along with GCC; see the file COPYING3.  If not see
>   
>   extern void asan_function_start (void);
>   extern void asan_finish_file (void);
> +extern void hwasan_finish_file (void);
> +extern void hwasan_record_base (rtx);
> +extern uint8_t hwasan_current_tag ();
> +extern void hwasan_increment_tag ();
> +extern rtx hwasan_with_tag (rtx, poly_int64);
> +extern void hwasan_tag_init ();
> +extern rtx hwasan_create_untagged_base (rtx);
> +extern rtx hwasan_extract_tag (rtx tagged_pointer);
> +extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
> +extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx, rtx_insn *);
> +extern bool memory_tagging_p (void);
> +extern bool hwasan_sanitize_stack_p (void);
>   extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
>   					     HOST_WIDE_INT *, tree *, int);
>   extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
> @@ -75,6 +87,31 @@ extern hash_set <tree> *asan_used_labels;
>   
>   #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
>   
> +/* NOTE: The values below define an ABI and are hard-coded to these values in
> +   libhwasan, hence they can't be changed independently here.  */
> +/* How many bits are used to store a tag in a pointer.
> +   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).  */
> +#define HWASAN_TAG_SIZE 8
> +/* Tag Granule of HWASAN shadow stack.
> +   This is the size in real memory that each byte in the shadow memory refers
> +   to.  I.e. if a variable is X bytes long in memory then it's tag in shadow
> +   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
> +   Most variables will need to be aligned to this amount since two variables
> +   that are neighbours in memory and share a tag granule would need to share
> +   the same tag (the shared tag granule can only store one tag).  */
> +#define HWASAN_TAG_SHIFT_SIZE 4
> +#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SHIFT_SIZE)
> +/* Define the tag for the stack background.
> +   This defines what tag the stack pointer will be and hence what tag all
> +   variables that are not given special tags are (e.g. spilled registers,
> +   and parameters passed on the stack).  */
> +#define HWASAN_STACK_BACKGROUND 0
> +/* How many bits to shift in order to access the tag bits.
> +   The tag is stored in the top 8 bits of a pointer hence shifting 56 bits will
> +   leave just the tag.  */
> +#define HWASAN_SHIFT 56
> +#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
> +
>   /* Various flags for Asan builtins.  */
>   enum asan_check_flags
>   {
> diff --git a/gcc/asan.c b/gcc/asan.c
> index a731bd490b4e78e916ae20fc9a0249c1fc04daa5..2e79d39785467651c352169dae4551a47d7b3613 100644
> --- a/gcc/asan.c
> +++ b/gcc/asan.c
> @@ -257,6 +257,9 @@ hash_set<tree> *asan_handled_variables = NULL;
>   
>   hash_set <tree> *asan_used_labels = NULL;
>   
> +static uint8_t tag_offset = 0;
> +static rtx hwasan_base_ptr = NULL_RTX;
> +
>   /* Sets shadow offset to value in string VAL.  */
>   
>   bool
> @@ -1352,6 +1355,21 @@ asan_redzone_buffer::flush_if_full (void)
>       flush_redzone_payload ();
>   }
>   
> +/* Returns whether we are tagging pointers and checking those tags on memory
> +   access.  This is true when checking with either in software or hardware.  */
> +bool
> +memory_tagging_p ()

This one is very commonly used function and I don't like the name much. I would
prefer something like hwasan_p or sanitize_hwasan_p. Something which will have
hwasan in its name ;)

> +{
> +    return sanitize_flags_p (SANITIZE_HWADDRESS);
> +}
> +
> +/* Are we tagging the stack?  */
> +bool
> +hwasan_sanitize_stack_p ()
> +{
> +  return (memory_tagging_p () && HWASAN_STACK);
> +}
> +
>   /* Insert code to protect stack vars.  The prologue sequence should be emitted
>      directly, epilogue sequence returned.  BASE is the register holding the
>      stack base, against which OFFSETS array offsets are relative to, OFFSETS
> @@ -2884,6 +2902,11 @@ initialize_sanitizer_builtins (void)
>       = build_function_type_list (void_type_node, uint64_type_node,
>   				ptr_type_node, NULL_TREE);
>   
> +  tree BT_FN_VOID_PTR_UINT8_SIZE
> +    = build_function_type_list (void_type_node, ptr_type_node,
> +				unsigned_char_type_node, size_type_node,
> +				NULL_TREE);
> +
>     tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
>     tree BT_FN_IX_CONST_VPTR_INT[5];
>     tree BT_FN_IX_VPTR_IX_INT[5];
> @@ -2934,6 +2957,8 @@ initialize_sanitizer_builtins (void)
>   #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
>   #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
>   #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
> +#undef ATTR_NOTHROW_LIST
> +#define ATTR_NOTHROW_LIST ECF_NOTHROW
>   #undef ATTR_NOTHROW_LEAF_LIST
>   #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
>   #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
> @@ -3684,4 +3709,189 @@ make_pass_asan_O0 (gcc::context *ctxt)
>     return new pass_asan_O0 (ctxt);
>   }

Here you miss a function comment. There are actually multiple function that miss
a comment.

>   
> +void
> +hwasan_record_base (rtx base)
> +{
> +  /* Initialise tag of the base register.
> +     This has to be done as soon as the stack is getting expanded to ensure
> +     anything emitted with `get_dynamic_stack_base` will use the value set here
> +     instead of using a register without a value.
> +     Especially note that RTL expansion of large aligned values does that.  */
> +  targetm.memtag.gentag (base, virtual_stack_vars_rtx);
> +  hwasan_base_ptr = base;
> +}
> +
> +uint8_t
> +hwasan_current_tag ()
> +{
> +  return tag_offset;
> +}
> +
> +void
> +hwasan_increment_tag ()
> +{
> +  uint8_t tag_bits = HWASAN_TAG_SIZE;
> +  tag_offset = (tag_offset + 1) % (1 << tag_bits);

I know HWASAN_TAG_SIZE is quite fixed value, but maybe you would
like to add a static check (STATIC_ASSERT) that
HWASAN_TAG_SIZE can fit in sizeof(tag_offset)?

> +}
> +
> +rtx
> +hwasan_with_tag (rtx base, poly_int64 offset)
> +{
> +  gcc_assert (tag_offset < (1 << HWASAN_TAG_SIZE));
> +  return targetm.memtag.addtag (base, offset, tag_offset);
> +}
> +
> +/* Clear internal state for the next function.
> +   This function is called before variables on the stack get expanded, in
> +   `init_vars_expansion`.  */
> +void
> +hwasan_tag_init ()
> +{
> +  delete asan_used_labels;
> +  asan_used_labels = NULL;
> +
> +  hwasan_base_ptr = NULL_RTX;
> +  tag_offset = HWASAN_STACK_BACKGROUND + 1;
> +}
> +
> +rtx
> +hwasan_extract_tag (rtx tagged_pointer)
> +{
> +  rtx tag = expand_simple_binop (Pmode,
> +				 LSHIFTRT,
> +				 tagged_pointer,
> +				 HWASAN_SHIFT_RTX,
> +				 NULL_RTX,
> +				 /* unsignedp = */0,
> +				 OPTAB_DIRECT);
> +  return gen_lowpart (QImode, tag);
> +}
> +
> +void
> +hwasan_emit_prologue (rtx *bases,
> +		      rtx *untagged_bases,
> +		      poly_int64 *offsets,
> +		      uint8_t *tags,
> +		      size_t length)
> +{
> +  /* We need untagged base pointers since libhwasan only accepts untagged
> +    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
> +    the base tag for an offset.  */
> +  for (size_t i = 0; (i * 2) + 1 < length; i++)
> +    {
> +      poly_int64 start = offsets[i * 2];
> +      poly_int64 end = offsets[(i * 2) + 1];
> +
> +      poly_int64 bot, top;
> +      if (known_ge (start, end))
> +	{
> +	  top = start;
> +	  bot = end;
> +	}
> +      else
> +	{
> +	  top = end;
> +	  bot = start;
> +	}
> +      poly_int64 size = (top - bot);
> +
> +      /* Can't check that all poly_int64's are aligned, but still nice
> +	 to check those that are compile-time constants.  */
> +      HOST_WIDE_INT tmp;
> +      if (top.is_constant (&tmp))
> +	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
> +      if (bot.is_constant (&tmp))
> +	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
> +      if (size.is_constant (&tmp))
> +	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
> +
> +      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
> +      rtx base_tag = hwasan_extract_tag (bases[i]);
> +      /* In the case of tag overflow we would want modulo wrapping -- which
> +	 should be given from the `plus_constant` in QImode.  */
> +      rtx tag = plus_constant (QImode, base_tag, tags[i]);
> +      emit_library_call (ret,
> +			 LCT_NORMAL,
> +			 VOIDmode,
> +			 plus_constant (ptr_mode, untagged_bases[i], bot),
> +			 ptr_mode,
> +			 tag,
> +			 QImode,
> +			 gen_int_mode (size, ptr_mode),
> +			 ptr_mode);
> +    }
> +}
> +
> +rtx_insn *
> +hwasan_emit_untag_frame (rtx dynamic, rtx vars, rtx_insn *before)
> +{
> +  if (before)
> +    push_to_sequence (before);
> +  else
> +    start_sequence ();
> +
> +  dynamic = convert_memory_address (ptr_mode, dynamic);
> +  vars = convert_memory_address (ptr_mode, vars);
> +
> +  rtx top_rtx;
> +  rtx bot_rtx;
> +  if (STACK_GROWS_DOWNWARD)
> +    {
> +      top_rtx = vars;
> +      bot_rtx = dynamic;
> +    }
> +  else
> +    {
> +      top_rtx = dynamic;
> +      bot_rtx = vars;
> +    }
> +
> +  rtx size_rtx = expand_simple_binop (Pmode, MINUS, top_rtx, bot_rtx,
> +				  NULL_RTX, /* unsignedp = */0, OPTAB_DIRECT);
> +
> +  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
> +  emit_library_call (ret, LCT_NORMAL, VOIDmode,
> +      bot_rtx, ptr_mode,
> +      const0_rtx, QImode,
> +      size_rtx, ptr_mode);
> +
> +  do_pending_stack_adjust ();
> +  rtx_insn *insns = get_insns ();
> +  end_sequence ();
> +  return insns;
> +}
> +
> +rtx
> +hwasan_create_untagged_base (rtx orig_base)
> +{
> +  rtx untagged_base = gen_reg_rtx (Pmode);
> +  rtx tag_mask = gen_int_mode ((1ULL << HWASAN_SHIFT) - 1, Pmode);
> +  untagged_base = expand_binop (Pmode, and_optab,
> +				orig_base, tag_mask,
> +				untagged_base, true, OPTAB_DIRECT);
> +  gcc_assert (untagged_base);
> +  return untagged_base;
> +}
> +
> +/* Needs to be GTY(()), because cgraph_build_static_cdtor may
> +   invoke ggc_collect.  */
> +static GTY(()) tree hwasan_ctor_statements;
> +
> +void
> +hwasan_finish_file (void)
> +{
> +  /* Do not emit constructor initialisation for the kernel.
> +     (the kernel has its own initialisation already).  */
> +  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
> +    return;
> +
> +  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
> +  flag_sanitize &= ~SANITIZE_HWADDRESS;
> +  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
> +  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
> +  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
> +  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
> +  flag_sanitize |= SANITIZE_HWADDRESS;
> +}
> +
>   #include "gt-asan.h"
> diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
> index e5c9e063c480d1392b6c2b395ec9d029b6d94209..d05f597b6434f39fe95d4f28dd2ef3ed463dd925 100644
> --- a/gcc/builtin-types.def
> +++ b/gcc/builtin-types.def
> @@ -625,6 +625,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_UINT32_UINT32_PTR,
>   DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
>   		     BT_PTR)
>   DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
> +DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_SIZE, BT_VOID, BT_PTR, BT_UINT8,
> +		     BT_SIZE)
>   
>   DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
>   		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
> diff --git a/gcc/builtins.def b/gcc/builtins.def
> index d8233f5f760f8426e8ff85473ecda02aa6c2655b..3f621ffdbda0acf5949348882a7c1f3504634666 100644
> --- a/gcc/builtins.def
> +++ b/gcc/builtins.def
> @@ -244,6 +244,7 @@ along with GCC; see the file COPYING3.  If not see
>     DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
>   	       true, true, true, ATTRS, true, \
>   	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
> +				| SANITIZE_HWADDRESS \
>   				| SANITIZE_UNDEFINED \
>   				| SANITIZE_UNDEFINED_NONDEFAULT) \
>   	       || flag_sanitize_coverage))
> diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
> index c34a53b526b50d49cd73ab5a5c383efc6da5a23e..8e1ea21621e350b3d8779b79b0d7f69d571caa08 100644
> --- a/gcc/cfgexpand.c
> +++ b/gcc/cfgexpand.c
> @@ -378,7 +378,14 @@ align_local_variable (tree decl, bool really_expand)
>         if (really_expand)
>   	SET_DECL_ALIGN (decl, align);
>       }
> -  return align / BITS_PER_UNIT;
> +
> +  unsigned int ret_align = align / BITS_PER_UNIT;
> +
> +  if (hwasan_sanitize_stack_p ())
> +    ret_align = ret_align > HWASAN_TAG_GRANULE_SIZE
> +      ? ret_align
> +      : HWASAN_TAG_GRANULE_SIZE;

This can be simplified into something like:
if (hwasan_sanitize_stack_p ())
   ret_align = MIN (HWASAN_TAG_GRANULE_SIZE, ret_align);

Martin

> +  return ret_align;
>   }
>   
>   /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
> @@ -986,7 +993,7 @@ dump_stack_var_partition (void)
>   
>   static void
>   expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
> -			 poly_int64 offset)
> +			 poly_int64 offset, rtx stack_base)
>   {
>     unsigned align;
>     rtx x;
> @@ -994,7 +1001,11 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
>     /* If this fails, we've overflowed the stack frame.  Error nicely?  */
>     gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
>   
> -  x = plus_constant (Pmode, base, offset);
> +  if (hwasan_sanitize_stack_p ())
> +    x = hwasan_with_tag (base, offset);
> +  else
> +    x = plus_constant (Pmode, base, offset);
> +
>     x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
>   		   ? TYPE_MODE (TREE_TYPE (decl))
>   		   : DECL_MODE (SSAVAR (decl)), x);
> @@ -1004,7 +1015,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
>         /* Set alignment we actually gave this decl if it isn't an SSA name.
>            If it is we generate stack slots only accidentally so it isn't as
>   	 important, we'll simply use the alignment that is already set.  */
> -      if (base == virtual_stack_vars_rtx)
> +      if (base == stack_base)
>   	offset -= frame_phase;
>         align = known_alignment (offset);
>         align *= BITS_PER_UNIT;
> @@ -1030,9 +1041,19 @@ public:
>        The vector is in reversed, highest offset pairs come first.  */
>     auto_vec<HOST_WIDE_INT> asan_vec;
>   
> +  /* HWASAN records the poly_int64 so it can handle any stack variable.  */
> +  auto_vec<poly_int64> hwasan_vec;
> +  auto_vec<rtx> hwasan_untagged_base_vec;
> +  auto_vec<rtx> hwasan_base_vec;
> +
>     /* Vector of partition representative decls in between the paddings.  */
>     auto_vec<tree> asan_decl_vec;
>   
> +  /* Vector of tag offsets representing the tag for each stack variable.
> +     Each offset determines the difference between the randomly generated
> +     tag for the current frame and the tag for this stack variable.  */
> +  auto_vec<uint8_t> hwasan_tag_vec;
> +
>     /* Base pseudo register for Address Sanitizer protected automatic vars.  */
>     rtx asan_base;
>   
> @@ -1050,6 +1071,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>     size_t si, i, j, n = stack_vars_num;
>     poly_uint64 large_size = 0, large_alloc = 0;
>     rtx large_base = NULL;
> +  rtx large_untagged_base = NULL;
>     unsigned large_align = 0;
>     bool large_allocation_done = false;
>     tree decl;
> @@ -1096,11 +1118,17 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>   	}
>       }
>   
> +  if (hwasan_sanitize_stack_p () && data->asan_base == NULL)
> +    {
> +      data->asan_base = gen_reg_rtx (Pmode);
> +      hwasan_record_base (data->asan_base);
> +    }
> +
>     for (si = 0; si < n; ++si)
>       {
>         rtx base;
>         unsigned base_align, alignb;
> -      poly_int64 offset;
> +      poly_int64 offset = 0;
>   
>         i = stack_vars_sorted[si];
>   
> @@ -1121,10 +1149,36 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>         if (pred && !pred (i))
>   	continue;
>   
> +      base = hwasan_sanitize_stack_p ()
> +	? data->asan_base
> +	: virtual_stack_vars_rtx;
>         alignb = stack_vars[i].alignb;
>         if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
>   	{
> -	  base = virtual_stack_vars_rtx;
> +	  if (hwasan_sanitize_stack_p ())
> +	    {
> +	      /* Allocate zero bytes to take advantage of the
> +		 alloc_stack_frame_space logic of ensuring the stack is aligned
> +		 despite having poly_int64's to deal with.
> +
> +		 There must be no tag granule "shared" between different
> +		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
> +		 chunk can have more than one object in it.
> +
> +		 We ensure this by forcing the end of the last bit of data to
> +		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
> +		 the start of each variable to be aligned to
> +		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
> +
> +		 We can't align just one of the start or end, since there are
> +		 untagged things stored on the stack that we have no control on
> +		 the alignment and these can't share a tag granule with a
> +		 tagged variable.  */
> +	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
> +	      offset = alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
> +	      data->hwasan_vec.safe_push (offset);
> +	      data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
> +	    }
>   	  /* ASAN description strings don't yet have a syntax for expressing
>   	     polynomial offsets.  */
>   	  HOST_WIDE_INT prev_offset;
> @@ -1204,6 +1258,9 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>   	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
>   	      base_align = crtl->max_used_stack_slot_alignment;
>   	    }
> +
> +	  if (hwasan_sanitize_stack_p ())
> +	    data->hwasan_vec.safe_push (offset);
>   	}
>         else
>   	{
> @@ -1223,14 +1280,31 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>   	      loffset = alloc_stack_frame_space
>   		(rtx_to_poly_int64 (large_allocsize),
>   		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
> -	      large_base = get_dynamic_stack_base (loffset, large_align);
> +	      large_base = get_dynamic_stack_base (loffset, large_align, base);
>   	      large_allocation_done = true;
>   	    }
> -	  gcc_assert (large_base != NULL);
>   
> +	  gcc_assert (large_base != NULL);
>   	  large_alloc = aligned_upper_bound (large_alloc, alignb);
> +	  if (hwasan_sanitize_stack_p ())
> +	    {
> +	      /* An object with a large alignment requirement means that the
> +		 alignment requirement is greater than the required alignment
> +		 for tags.  */
> +	      if (!large_untagged_base)
> +		large_untagged_base = hwasan_create_untagged_base (large_base);
> +	      data->hwasan_vec.safe_push (large_alloc);
> +	      data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
> +	    }
>   	  offset = large_alloc;
>   	  large_alloc += stack_vars[i].size;
> +	  if (hwasan_sanitize_stack_p ())
> +	    {
> +	      /* Ensure the end of the variable is also aligned correctly.  */
> +	      poly_int64 align_again =
> +		aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
> +	      data->hwasan_vec.safe_push (align_again);
> +	    }
>   
>   	  base = large_base;
>   	  base_align = large_align;
> @@ -1242,7 +1316,21 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>   	{
>   	  expand_one_stack_var_at (stack_vars[j].decl,
>   				   base, base_align,
> -				   offset);
> +				   offset,
> +				   hwasan_sanitize_stack_p ()
> +				   ? data->asan_base
> +				   : virtual_stack_vars_rtx);
> +	}
> +
> +      if (hwasan_sanitize_stack_p ())
> +	{
> +	  /* Record the tag for this object in `data` so the prologue knows
> +	     what tag to put in the shadow memory during cfgexpand.c.
> +	     Then increment the tag so that the next object has a different
> +	     tag to this object.  */
> +	  data->hwasan_base_vec.safe_push (base);
> +	  data->hwasan_tag_vec.safe_push (hwasan_current_tag ());
> +	  hwasan_increment_tag ();
>   	}
>       }
>   
> @@ -1339,7 +1427,8 @@ expand_one_stack_var_1 (tree var)
>     offset = alloc_stack_frame_space (size, byte_align);
>   
>     expand_one_stack_var_at (var, virtual_stack_vars_rtx,
> -			   crtl->max_used_stack_slot_alignment, offset);
> +			   crtl->max_used_stack_slot_alignment, offset,
> +			   virtual_stack_vars_rtx);
>   }
>   
>   /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
> @@ -1552,8 +1641,13 @@ defer_stack_allocation (tree var, bool toplevel)
>   
>     /* If stack protection is enabled, *all* stack variables must be deferred,
>        so that we can re-order the strings to the top of the frame.
> -     Similarly for Address Sanitizer.  */
> -  if (flag_stack_protect || asan_sanitize_stack_p ())
> +     Similarly for Address Sanitizer.
> +     When tagging memory we defer all stack variables so we can handle them in
> +     one place (handle here meaning ensure they are aligned and record
> +     information on each variables position in the stack).  */
> +  if (flag_stack_protect
> +      || asan_sanitize_stack_p ()
> +      || hwasan_sanitize_stack_p ())
>       return true;
>   
>     unsigned int align = TREE_CODE (var) == SSA_NAME
> @@ -1938,6 +2032,8 @@ init_vars_expansion (void)
>     /* Initialize local stack smashing state.  */
>     has_protected_decls = false;
>     has_short_buffer = false;
> +  if (hwasan_sanitize_stack_p ())
> +    hwasan_tag_init ();
>   }
>   
>   /* Free up stack variable graph data.  */
> @@ -2297,12 +2393,27 @@ expand_used_vars (void)
>   	}
>   
>         expand_stack_vars (NULL, &data);
> +
> +      if (hwasan_sanitize_stack_p ())
> +	hwasan_emit_prologue (data.hwasan_base_vec.address (),
> +			      data.hwasan_untagged_base_vec.address (),
> +			      data.hwasan_vec.address (),
> +			      data.hwasan_tag_vec.address (),
> +			      data.hwasan_vec.length ());
>       }
>   
>     if (asan_sanitize_allocas_p () && cfun->calls_alloca)
>       var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
>   					      virtual_stack_vars_rtx,
>   					      var_end_seq);
> +  /* Here we clear tags fro the entire frame of this function.
> +     We need to clear tags of *something* if we have tagged either local
> +     variables or alloca objects.  */
> +  else if (hwasan_sanitize_stack_p ()
> +	   && (cfun->calls_alloca || stack_vars_num > 0))
> +    var_end_seq = hwasan_emit_untag_frame (virtual_stack_dynamic_rtx,
> +					   virtual_stack_vars_rtx,
> +					   var_end_seq);
>   
>     fini_vars_expansion ();
>   
> diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
> index e842b734c9c20253986880ba3622a8f692d3ca88..718d2e8aac56553bdc30c592fe70fa10aa911736 100644
> --- a/gcc/doc/tm.texi
> +++ b/gcc/doc/tm.texi
> @@ -2982,6 +2982,31 @@ A target hook which lets a backend compute the set of pressure classes to  be us
>   True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that -fsanitize=hwaddress can work.
>   @end deftypefn
>   
> +@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
> +Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by TAG_OFFSET.
> +The resulting RTX must either be a valid memory address or be able to get
> +put into an operand with force_operand.  If overridden the more common case
> +is that we force this into an operand using the backend hook
> +"addtag_force_operand" that is called in force_operand.
> +
> +It is expected that that "addtag_force_operand" recognises the RTX
> +generated by "addtag" and emits code to force that RTX into an operand.
> +@end deftypefn
> +
> +@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG_FORCE_OPERAND (rtx @var{oper}, rtx @var{target})
> +If the RTL expression OPER is of the form generated by targetm.memtag.addtag,
> +then emit instructions to move the value into an operand (i.e. for
> +force_operand).
> +TARGET is an RTX suggestion of where to generate the value.
> +This hook is most often implemented by emitting instructions to put the
> +expression into a pseudo register, then returning that pseudo register.
> +@end deftypefn
> +
> +@deftypefn {Target Hook} void TARGET_MEMTAG_GENTAG (rtx @var{base}, rtx @var{untagged})
> +Set the BASE argument to UNTAGGED with some random tag.
> +This function is used to generate a tagged base for the current stack frame.
> +@end deftypefn
> +
>   @node Stack and Calling
>   @section Stack Layout and Calling Conventions
>   @cindex calling conventions
> diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
> index 6136ac1a5fe0c0980d5b5123b67b102a1b1e0bcc..3d3761dbc097e6f73f4f4f937aa713b93872a4a2 100644
> --- a/gcc/doc/tm.texi.in
> +++ b/gcc/doc/tm.texi.in
> @@ -2379,6 +2379,12 @@ in the reload pass.
>   
>   @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
>   
> +@hook TARGET_MEMTAG_ADDTAG
> +
> +@hook TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
> +
> +@hook TARGET_MEMTAG_GENTAG
> +
>   @node Stack and Calling
>   @section Stack Layout and Calling Conventions
>   @cindex calling conventions
> diff --git a/gcc/explow.h b/gcc/explow.h
> index 5110ad82d6a024fda1d3a3eaf80de40c5e6ad3b6..333948e0c69a1b1132e9a1d06707dc63f1226262 100644
> --- a/gcc/explow.h
> +++ b/gcc/explow.h
> @@ -102,7 +102,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
>   extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
>   
>   /* Returns the address of the dynamic stack space without allocating it.  */
> -extern rtx get_dynamic_stack_base (poly_int64, unsigned);
> +extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
>   
>   /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
>   extern rtx align_dynamic_address (rtx, unsigned);
> diff --git a/gcc/explow.c b/gcc/explow.c
> index 7eb854bca4a6dcc5b15e5c42df1a5e88a19f2464..2728f7a4b1ee8ed0d20287b716a3d0ad5a97d84b 100644
> --- a/gcc/explow.c
> +++ b/gcc/explow.c
> @@ -1577,10 +1577,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
>      OFFSET is the offset of the area into the virtual stack vars area.
>   
>      REQUIRED_ALIGN is the alignment (in bits) required for the region
> -   of memory.  */
> +   of memory.
> +
> +   BASE is the rtx of the base of this virtual stack vars area.
> +   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
> +   on the stack.  */
>   
>   rtx
> -get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
> +get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
>   {
>     rtx target;
>   
> @@ -1588,7 +1592,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
>       crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
>   
>     target = gen_reg_rtx (Pmode);
> -  emit_move_insn (target, virtual_stack_vars_rtx);
> +  emit_move_insn (target, base);
>     target = expand_binop (Pmode, add_optab, target,
>   			 gen_int_mode (offset, Pmode),
>   			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
> diff --git a/gcc/expr.c b/gcc/expr.c
> index 476c6865f20828fc68f455e70d4874eaabd9d08d..24d011e698af0dbf3635ba5f9d8275376a124bf4 100644
> --- a/gcc/expr.c
> +++ b/gcc/expr.c
> @@ -7500,6 +7500,13 @@ force_operand (rtx value, rtx target)
>         return subtarget;
>       }
>   
> +  if (targetm.memtag.addtag_force_operand)
> +    {
> +      rtx ret = targetm.memtag.addtag_force_operand (value, target);
> +      if (ret)
> +	return ret;
> +    }
> +
>     if (ARITHMETIC_P (value))
>       {
>         op2 = XEXP (value, 1);
> diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
> index 374d15007d868363d9b4fbf467e1e462abbca61a..7bd50715f24a2cb154b578e2abdea4e8fcdb2107 100644
> --- a/gcc/sanitizer.def
> +++ b/gcc/sanitizer.def
> @@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
>   DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
>   		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
>   
> +/* Hardware Address Sanitizer.  */
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
> +		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
> +		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
> +
>   /* Thread Sanitizer */
>   DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init",
>   		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
> diff --git a/gcc/target.def b/gcc/target.def
> index ad16a151b6af9b1ee13918c8f2980280d75b1d90..3c533acbe3965cdb0870621e364a009353f72c2e 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -6751,6 +6751,37 @@ DEFHOOK
>    pointers.  This feature means that -fsanitize=hwaddress can work.",
>    bool, (), default_memtag_can_tag_addresses)
>   
> +DEFHOOK
> +(addtag,
> + "Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by\
> + TAG_OFFSET.\n\
> +The resulting RTX must either be a valid memory address or be able to get\n\
> +put into an operand with force_operand.  If overridden the more common case\n\
> +is that we force this into an operand using the backend hook\n\
> +\"addtag_force_operand\" that is called in force_operand.\n\
> +\n\
> +It is expected that that \"addtag_force_operand\" recognises the RTX\n\
> +generated by \"addtag\" and emits code to force that RTX into an operand.",
> +rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
> +default_memtag_addtag)
> +
> +DEFHOOK
> +(addtag_force_operand,
> + "If the RTL expression OPER is of the form generated by targetm.memtag.addtag,\n\
> +then emit instructions to move the value into an operand (i.e. for\n\
> +force_operand).\n\
> +TARGET is an RTX suggestion of where to generate the value.\n\
> +This hook is most often implemented by emitting instructions to put the\n\
> +expression into a pseudo register, then returning that pseudo register.",
> +rtx, (rtx oper, rtx target), NULL)
> +
> +DEFHOOK
> +(gentag,
> + "Set the BASE argument to UNTAGGED with some random tag.\n\
> +This function is used to generate a tagged base for the current stack frame.",
> +  void, (rtx base, rtx untagged),
> +  default_memtag_gentag)
> +
>   HOOK_VECTOR_END (memtag)
>   #undef HOOK_PREFIX
>   #define HOOK_PREFIX "TARGET_"
> diff --git a/gcc/targhooks.h b/gcc/targhooks.h
> index 94e865259f35e46e26f6b4763c5e2f9dc9ed1b83..b0e32102acacdf7a64f1e3d314a966d1d3f062c7 100644
> --- a/gcc/targhooks.h
> +++ b/gcc/targhooks.h
> @@ -283,4 +283,6 @@ extern bool speculation_safe_value_not_needed (bool);
>   extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
>   
>   extern bool default_memtag_can_tag_addresses ();
> +extern void default_memtag_gentag (rtx, rtx);
> +extern rtx default_memtag_addtag (rtx, poly_int64, uint8_t);
>   #endif /* GCC_TARGHOOKS_H */
> diff --git a/gcc/targhooks.c b/gcc/targhooks.c
> index 6e9e877c32e2c8705056bdf0ce2b1b6f125d93c3..cd9f98fc800d7232ead50b03f951364f76c01adc 100644
> --- a/gcc/targhooks.c
> +++ b/gcc/targhooks.c
> @@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "varasm.h"
>   #include "flags.h"
>   #include "explow.h"
> +#include "expmed.h"
>   #include "calls.h"
>   #include "expr.h"
>   #include "output.h"
> @@ -84,6 +85,8 @@ along with GCC; see the file COPYING3.  If not see
>   #include "langhooks.h"
>   #include "sbitmap.h"
>   #include "function-abi.h"
> +#include "attribs.h"
> +#include "asan.h"
>   
>   bool
>   default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
> @@ -2371,4 +2374,78 @@ default_memtag_can_tag_addresses ()
>     return false;
>   }
>   
> +void
> +default_memtag_gentag (rtx base, rtx untagged)
> +{
> +  gcc_assert (HWASAN_STACK);

This can be STATIC_ASSERT.

> +  if (HWASAN_RANDOM_FRAME_TAG)
> +    {
> +    rtx temp = gen_reg_rtx (QImode);
> +    rtx ret = init_one_libfunc ("__hwasan_generate_tag");
> +    rtx new_tag = emit_library_call_value (ret, temp, LCT_NORMAL, QImode);
> +    emit_move_insn (base, untagged);
> +    /* We know that `base` is not the stack pointer, since we never want to put
> +      a randomly generated tag into the stack pointer.  Hence we can use
> +      `store_bit_field` which on aarch64 generates a `bfi` which can not act on
> +      the stack pointer.  */
> +    store_bit_field (base, 8, 56, 0, 0, QImode, new_tag, false);
> +    }
> +  else
> +    {
> +      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
> +	 In the future we may add the option emit random tags with inline
> +	 instrumentation instead of function calls.  This would be the same
> +	 between the kernel and userland.  */
> +      emit_move_insn (base, untagged);
> +    }
> +}
> +
> +rtx
> +default_memtag_addtag (rtx base, poly_int64 offset, uint8_t tag_offset)
> +{
> +  /* Need to look into what the most efficient code sequence is.
> +     This is a code sequence that would be emitted *many* times, so we
> +     want it as small as possible.
> +
> +     If the tag offset is greater that (1 << 7) then the most efficient
> +     sequence here would give UB from signed integer overflow in the
> +     poly_int64.  Hence in that case we emit the slightly less efficient
> +     sequence.
> +
> +     There are two places where tag overflow is a question:
> +       - Tagging the shadow stack.
> +	  (both tagging and untagging).
> +       - Tagging addressable pointers.
> +
> +     We need to ensure both behaviours are the same (i.e. that the tag that
> +     ends up in a pointer after "overflowing" the tag bits with a tag addition
> +     is the same that ends up in the shadow space).
> +
> +     The aim is that the behaviour of tag addition should follow modulo
> +     wrapping in both instances.
> +
> +     The libhwasan code doesn't have any path that increments a pointers tag,
> +     which means it has no opinion on what happens when a tag increment
> +     overflows (and hence we can choose our own behaviour).  */
> +
> +  if (tag_offset < (1 << 7))
> +    {
> +      offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
> +      return plus_constant (Pmode, base, offset);
> +    }
> +  else
> +    {
> +      /* This is the fallback, it would be nice if it had less instructions,
> +	 but we can look for cleverer ways later.  */
> +      uint64_t tag_mask = ~(0xFFUL << HWASAN_SHIFT);
> +      rtx untagged_base = gen_rtx_AND (Pmode, GEN_INT (tag_mask), base);
> +      rtx new_addr = plus_constant (Pmode, untagged_base, offset);
> +
> +      rtx original_tag_value = gen_rtx_LSHIFTRT (Pmode, base, GEN_INT (HWASAN_SHIFT));
> +      rtx new_tag_value = plus_constant (Pmode, original_tag_value, tag_offset);
> +      rtx new_tag = gen_rtx_ASHIFT (Pmode, new_tag_value, GEN_INT (HWASAN_SHIFT));
> +      return gen_rtx_IOR (Pmode, new_addr, new_tag);
> +    }
> +}
> +
>   #include "gt-targhooks.h"
> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index ab67384249a3437ac37f42f741ed516884677f9f..7bd75548d2aebb3415ac85ec40ad25e5ca794094 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -508,6 +508,9 @@ compile_file (void)
>         if (flag_sanitize & SANITIZE_THREAD)
>   	tsan_finish_file ();
>   
> +      if (flag_sanitize & SANITIZE_HWADDRESS)
> +	hwasan_finish_file ();
> +
>         omp_finish_file ();
>   
>         hsa_output_brig ();
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes
  2019-11-07 18:37   ` [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes Matthew Malcomson
@ 2019-11-20 14:11     ` Martin Liška
  0 siblings, 0 replies; 150+ messages in thread
From: Martin Liška @ 2019-11-20 14:11 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, kcc, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 11/7/19 7:37 PM, Matthew Malcomson wrote:
> There are four main features to this change:
> 
> 1) Check pointer tags match address tags.
> 
> In the new `hwasan` pass we put HWASAN_CHECK internal functions around
> all memory accesses, to check that tags in the pointer being used match
> the tag stored in shadow memory for the memory region being used.
> 
> These internal functions are expanded into actual checks in the sanopt
> pass that happens just before expansion into RTL.
> 
> We use the same mechanism that currently inserts ASAN_CHECK internal
> functions to insert the new HWASAN_CHECK functions.
> 
> 2) Instrument known builtin function calls.
> 
> Handle all builtin functions that we know use memory accesses.
> This commit uses the machinery added for ASAN to identify builtin
> functions that access memory.
> 
> The main differences between the approaches for HWASAN and ASAN are:
>   - libhwasan intercepts much less builtin functions.
>   - Alloca needs to be transformed differently (instead of adding
>     redzones it needs to tag shadow memory and return a tagged pointer).
>   - stack_restore needs to untag the shadow stack between the current
>     position and where it's going.
>   - `noreturn` functions can not be handled by simply unpoisoning the
>     entire shadow stack -- there is no "always valid" tag.
>     (exceptions and things such as longjmp need to be handled in a
>     different way).
> 
> For hardware implemented checking (such as AArch64's memory tagging
> extension) alloca and stack_restore will need to be handled by hooks in
> the backend rather than transformation at the gimple level.  This will
> allow architecture specific handling of such stack modifications.
> 
> 3) Introduce HWASAN block-scope poisoning
> 
> Here we use exactly the same mechanism as ASAN_MARK to poison/unpoison
> variables on entry/exit of a block.
> 
> In order to simply use the exact same machinery we're using the same
> internal functions until the SANOPT pass.  This means that all handling
> of ASAN_MARK is the same.
> This has the negative that the naming may be a little confusing, but a
> positive that handling of the internal function doesn't have to be
> duplicated for a function that behaves exactly the same but has a
> different name.
> 
> gcc/ChangeLog:
> 
> 2019-11-07  Matthew Malcomson  <matthew.malcomson@arm.com>
> 
> 	* asan.c (handle_builtin_stack_restore): Account for HWASAN.
> 	(handle_builtin_alloca): Account for HWASAN.
> 	(get_mem_refs_of_builtin_call): Special case strlen for HWASAN.
> 	(report_error_func): Assert not HWASAN.
> 	(build_check_stmt): Make HWASAN_CHECK instead of ASAN_CHECK.
> 	(instrument_derefs): HWASAN does not tag globals.
> 	(maybe_instrument_call): Don't instrument `noreturn` functions.
> 	(initialize_sanitizer_builtins): Add new type.
> 	(asan_expand_mark_ifn): Account for HWASAN.
> 	(asan_expand_check_ifn): Assert never called by HWASAN.
> 	(asan_expand_poison_ifn): Account for HWASAN.
> 	(hwasan_instrument): New.
> 	(hwasan_base): New.
> 	(hwasan_emit_untag_frame): Free block-scope-var hash map.
> 	(hwasan_check_func): New.
> 	(hwasan_expand_check_ifn): New.
> 	(hwasan_expand_mark_ifn): New.
> 	(gate_hwasan): New.
> 	(class pass_hwasan): New.
> 	(make_pass_hwasan): New.
> 	(class pass_hwasan_O0): New.
> 	(make_pass_hwasan_O0): New.
> 	* asan.h (hwasan_base): New decl.
> 	(hwasan_expand_check_ifn): New decl.
> 	(hwasan_expand_mark_ifn): New decl.
> 	(gate_hwasan): New decl.
> 	(enum hwasan_mark_flags): New.
> 	(asan_intercepted_p): Always false for hwasan.
> 	(asan_sanitize_use_after_scope): Account for HWASAN.
> 	* builtin-types.def (BT_FN_PTR_CONST_PTR_UINT8): New.
> 	* gimple-pretty-print.c (dump_gimple_call_args): Account for
> 	HWASAN.
> 	* gimplify.c (asan_poison_variable): Account for HWASAN.
> 	(gimplify_function_tree): Remove requirement of
> 	SANITIZE_ADDRESS, requiring asan or hwasan is accounted for in
> 	`asan_sanitize_use_after_scope`.
> 	* internal-fn.c (expand_HWASAN_CHECK): New.
> 	(expand_HWASAN_CHOOSE_TAG): New.
> 	(expand_HWASAN_MARK): New.
> 	* internal-fn.def (HWASAN_CHOOSE_TAG): New.
> 	(HWASAN_CHECK): New.
> 	(HWASAN_MARK): New.
> 	* passes.def: Add hwasan and hwasan_O0 passes.
> 	* sanitizer.def (BUILT_IN_HWASAN_LOAD1): New.
> 	(BUILT_IN_HWASAN_LOAD2): New.
> 	(BUILT_IN_HWASAN_LOAD4): New.
> 	(BUILT_IN_HWASAN_LOAD8): New.
> 	(BUILT_IN_HWASAN_LOAD16): New.
> 	(BUILT_IN_HWASAN_LOADN): New.
> 	(BUILT_IN_HWASAN_STORE1): New.
> 	(BUILT_IN_HWASAN_STORE2): New.
> 	(BUILT_IN_HWASAN_STORE4): New.
> 	(BUILT_IN_HWASAN_STORE8): New.
> 	(BUILT_IN_HWASAN_STORE16): New.
> 	(BUILT_IN_HWASAN_STOREN): New.
> 	(BUILT_IN_HWASAN_LOAD1_NOABORT): New.
> 	(BUILT_IN_HWASAN_LOAD2_NOABORT): New.
> 	(BUILT_IN_HWASAN_LOAD4_NOABORT): New.
> 	(BUILT_IN_HWASAN_LOAD8_NOABORT): New.
> 	(BUILT_IN_HWASAN_LOAD16_NOABORT): New.
> 	(BUILT_IN_HWASAN_LOADN_NOABORT): New.
> 	(BUILT_IN_HWASAN_STORE1_NOABORT): New.
> 	(BUILT_IN_HWASAN_STORE2_NOABORT): New.
> 	(BUILT_IN_HWASAN_STORE4_NOABORT): New.
> 	(BUILT_IN_HWASAN_STORE8_NOABORT): New.
> 	(BUILT_IN_HWASAN_STORE16_NOABORT): New.
> 	(BUILT_IN_HWASAN_STOREN_NOABORT): New.
> 	(BUILT_IN_HWASAN_TAG_MISMATCH4): New.
> 	(BUILT_IN_HWASAN_HANDLE_LONGJMP): New.
> 	(BUILT_IN_HWASAN_TAG_PTR): New.
> 	* sanopt.c (sanopt_optimize_walker): Act during hwasan.
> 	(pass_sanopt::execute): Act during hwasan.
> 	* toplev.c (compile_file): Use `gate_hwasan` function.
> 	* tree-pass.h (make_pass_hwasan): Declare passes.
> 	(make_pass_hwasan_O0): Declare passes.
> 
> 
> 
> ###############     Attachment also inlined for ease of reply    ###############
> 
> 
> diff --git a/gcc/asan.h b/gcc/asan.h
> index 467231f8dad031a6176aeaddb9414f768b2af3fc..179f0369b68cf1aceab00e66004332b889cd890c 100644
> --- a/gcc/asan.h
> +++ b/gcc/asan.h
> @@ -31,10 +31,14 @@ extern rtx hwasan_with_tag (rtx, poly_int64);
>   extern void hwasan_tag_init ();
>   extern rtx hwasan_create_untagged_base (rtx);
>   extern rtx hwasan_extract_tag (rtx tagged_pointer);
> +extern rtx hwasan_base ();
>   extern void hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
>   extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx, rtx_insn *);
> +extern bool hwasan_expand_check_ifn (gimple_stmt_iterator *, bool);
> +extern bool hwasan_expand_mark_ifn (gimple_stmt_iterator *);
>   extern bool memory_tagging_p (void);
>   extern bool hwasan_sanitize_stack_p (void);
> +extern bool gate_hwasan (void);
>   extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
>   					     HOST_WIDE_INT *, tree *, int);
>   extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
> @@ -131,6 +135,13 @@ enum asan_mark_flags
>   #undef DEF
>   };
>   
> +enum hwasan_mark_flags
> +{
> +#define DEF(X) HWASAN_MARK_##X
> +  IFN_ASAN_MARK_FLAGS
> +#undef DEF
> +};
> +
>   /* Return true if STMT is ASAN_MARK with FLAG as first argument.  */
>   extern bool asan_mark_p (gimple *stmt, enum asan_mark_flags flag);
>   
> @@ -180,6 +191,9 @@ extern hash_set<tree> *asan_handled_variables;
>   static inline bool
>   asan_intercepted_p (enum built_in_function fcode)
>   {
> +  if (memory_tagging_p ())
> +    return false;
> +
>     return fcode == BUILT_IN_INDEX
>   	 || fcode == BUILT_IN_MEMCHR
>   	 || fcode == BUILT_IN_MEMCMP
> @@ -208,7 +222,8 @@ asan_intercepted_p (enum built_in_function fcode)
>   static inline bool
>   asan_sanitize_use_after_scope (void)
>   {
> -  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
> +  return (flag_sanitize_address_use_after_scope
> +	  && (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ()));
>   }
>   
>   /* Return true if DECL should be guarded on the stack.  */
> diff --git a/gcc/asan.c b/gcc/asan.c
> index 2e79d39785467651c352169dae4551a47d7b3613..ca80088c546882571f4a8b580755764d58dab4a7 100644
> --- a/gcc/asan.c
> +++ b/gcc/asan.c
> @@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "dojump.h"
>   #include "explow.h"
>   #include "expr.h"
> +#include "except.h"
>   #include "output.h"
>   #include "langhooks.h"
>   #include "cfgloop.h"
> @@ -579,15 +580,28 @@ get_last_alloca_addr ()
>   static void
>   handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
>   {
> -  if (!iter || !asan_sanitize_allocas_p ())
> +  if (!iter
> +      || !(asan_sanitize_allocas_p () || hwasan_sanitize_stack_p ()))
>       return;
>   
> -  tree last_alloca = get_last_alloca_addr ();
>     tree restored_stack = gimple_call_arg (call, 0);
> -  tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
> -  gimple *g = gimple_build_call (fn, 2, last_alloca, restored_stack);
> -  gsi_insert_before (iter, g, GSI_SAME_STMT);
> -  g = gimple_build_assign (last_alloca, restored_stack);
> +
> +  gimple *g;
> +
> +  if (hwasan_sanitize_stack_p ())
> +    {
> +      tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_HANDLE_LONGJMP);
> +      g = gimple_build_call (fn, 1, restored_stack);
> +    }
> +  else
> +    {
> +      tree last_alloca = get_last_alloca_addr ();
> +      tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
> +      g = gimple_build_call (fn, 2, last_alloca, restored_stack);
> +      gsi_insert_before (iter, g, GSI_SAME_STMT);
> +      g = gimple_build_assign (last_alloca, restored_stack);
> +    }
> +
>     gsi_insert_before (iter, g, GSI_SAME_STMT);
>   }
>   
> @@ -617,14 +631,12 @@ handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
>   static void
>   handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
>   {
> -  if (!iter || !asan_sanitize_allocas_p ())
> +  if (!iter
> +      || !(asan_sanitize_allocas_p () || hwasan_sanitize_stack_p ()))
>       return;
>   
>     gassign *g;
>     gcall *gg;
> -  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
> -
> -  tree last_alloca = get_last_alloca_addr ();
>     tree callee = gimple_call_fndecl (call);
>     tree old_size = gimple_call_arg (call, 0);
>     tree ptr_type = gimple_call_lhs (call) ? TREE_TYPE (gimple_call_lhs (call))
> @@ -634,6 +646,85 @@ handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
>       = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
>         ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
>   
> +  if (hwasan_sanitize_stack_p ())
> +    {
> +      /*
> +	 HWASAN needs a different expansion.
> +
> +	 addr = __builtin_alloca (size, align);
> +
> +	 should be replaced by
> +
> +	 new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment;
> +	 untagged_addr = __builtin_alloca (new_size, align);
> +	 tag = __hwasan_choose_alloca_tag ();
> +	 addr = __hwasan_tag_pointer (untagged_addr, tag);
> +	 __hwasan_tag_memory (addr, tag, new_size);
> +	*/
> +      /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start on
> +	 a tag granule.  */
> +      align = align > HWASAN_TAG_GRANULE_SIZE ? align : HWASAN_TAG_GRANULE_SIZE;
> +
> +      /* tree new_size = (old_size + 15) & ~15;  */
> +      uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
> +      tree old_size = gimple_call_arg (call, 0);
> +      tree tree_mask = build_int_cst (size_type_node, tg_mask);
> +      g = gimple_build_assign (make_ssa_name (size_type_node), PLUS_EXPR,
> +			       old_size, tree_mask);
> +      gsi_insert_before (iter, g, GSI_SAME_STMT);
> +      tree oversize = gimple_assign_lhs (g);
> +
> +      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_NOT_EXPR,
> +			       tree_mask);
> +      tree mask = gimple_assign_lhs (g);
> +      gsi_insert_before (iter, g, GSI_SAME_STMT);
> +
> +      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_AND_EXPR,
> +			       oversize, mask);
> +      gsi_insert_before (iter, g, GSI_SAME_STMT);
> +      tree new_size = gimple_assign_lhs (g);
> +
> +      /* emit the alloca call */
> +      tree fn = builtin_decl_implicit (BUILT_IN_ALLOCA_WITH_ALIGN);
> +      gg = gimple_build_call (fn, 2, new_size,
> +			      build_int_cst (size_type_node, align));
> +      tree untagged_addr = make_ssa_name (ptr_type, gg);
> +      gimple_call_set_lhs (gg, untagged_addr);
> +      gsi_insert_before (iter, gg, GSI_SAME_STMT);
> +
> +      /* Insert code choosing the tag.
> +	 Here we use an internal function so we can choose the tag at expand
> +	 time.  We need the decision to be made after stack variables have been
> +	 assigned their tag (i.e. once the tag_offset variable has been set to
> +	 one after the last stack variables tag).  */
> +
> +      gg = gimple_build_call_internal (IFN_HWASAN_CHOOSE_TAG, 0);
> +      tree tag = make_ssa_name (unsigned_char_type_node, gg);
> +      gimple_call_set_lhs (gg, tag);
> +      gsi_insert_before (iter, gg, GSI_SAME_STMT);
> +
> +      /* Insert code adding tag to pointer.  */
> +      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_PTR);
> +      gg = gimple_build_call (fn, 2, untagged_addr, tag);
> +      tree addr = make_ssa_name (ptr_type, gg);
> +      gimple_call_set_lhs (gg, addr);
> +      gsi_insert_before (iter, gg, GSI_SAME_STMT);
> +
> +      /* Insert code tagging shadow memory.
> +	 NOTE: require using `untagged_addr` here for libhwasan API.  */
> +      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MEM);
> +      gg = gimple_build_call (fn, 3, untagged_addr, tag, new_size);
> +      gsi_insert_before (iter, gg, GSI_SAME_STMT);
> +
> +      /* Finally, replace old alloca ptr with NEW_ALLOCA.  */
> +      replace_call_with_value (iter, addr);
> +      return;
> +    }
> +
> +  tree last_alloca = get_last_alloca_addr ();
> +  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
> +
> +
>     /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
>        bytes of allocated space.  Otherwise, align alloca to ASAN_RED_ZONE_SIZE
>        manually.  */
> @@ -786,6 +877,31 @@ get_mem_refs_of_builtin_call (gcall *call,
>         break;
>   
>       case BUILT_IN_STRLEN:
> +      /* Special case strlen here since its length is taken from its return
> +	 value.
> +
> +	 The approach taken by the sanitizers is to check a memory access
> +	 before it's taken.  For ASAN strlen is intercepted by libasan, so no
> +	 check is inserted by the compiler.
> +
> +	 This function still returns `true` and provides a length to the rest
> +	 of the ASAN pass in order to record what areas have been checked,
> +	 avoiding superfluous checks later on.
> +
> +	 HWASAN does not intercept any of these internal functions.
> +	 This means that checks for memory accesses must be inserted by the
> +	 compiler.
> +	 strlen is a special case, because we can tell the length from the
> +	 return of the function, but that is not known until after the function
> +	 has returned.
> +
> +	 Hence we can't check the memory access before it happens.
> +	 We could check the memory access after it has already happened, but
> +	 for now I'm choosing to just ignore `strlen` calls.
> +	 This decision was simply made because that means the special case is
> +	 limited to this one case of this one function.  */
> +      if (memory_tagging_p ())
> +	return false;
>         source0 = gimple_call_arg (call, 0);
>         len = gimple_call_lhs (call);
>         break;
> @@ -1355,6 +1471,150 @@ asan_redzone_buffer::flush_if_full (void)
>       flush_redzone_payload ();
>   }
>   
> +
> +/* HWAddressSanitizer (hwasan) is a probabilistic method for detecting
> +   out-of-bounds and use-after-free bugs.
> +   Read more:
> +   http://code.google.com/p/address-sanitizer/
> +
> +   Similar to AddressSanitizer (asan) it consists of two parts: the
> +   instrumentation module in this file, and a run-time library.
> +
> +   The instrumentation module adds a run-time check before every memory insn in
> +   the same manner as asan (see the block comment for AddressSanitizer above).
> +   Currently, hwasan only adds out-of-line instrumentation, where each check is
> +   implemented as a function call to the run-time library.  Hence a check for a
> +   load of N bytes from address X would be implemented with a function call to
> +   __hwasan_loadN(X), and checking a store of N bytes from address X would be
> +   implemented with a function call to __hwasan_storeN(X).
> +
> +   The main difference between hwasan and asan is in the information stored to
> +   help this checking.  Both sanitizers use a shadow memory area which stores
> +   data recording the state of main memory at a corresponding address.
> +
> +   For hwasan, each 16 byte granule in main memory has a corresponding address
> +   in shadow memory.  This shadow address can be calculated with equation:
> +     (addr >> HWASAN_TAG_SHIFT_SIZE) + __hwasan_shadow_memory_dynamic_address;
> +   The conversion between real and shadow memory for asan is given in the block
> +   comment at the top of this file.
> +   The description of how this shadow memory is laid out for asan is in the
> +   block comment at the top of this file, here we describe how this shadow
> +   memory is used for hwasan.
> +
> +   For hwasan, each variable is assigned a byte-sized 'tag'.  The extent of
> +   that variable in the shadow memory is filled with the assigned tag, and
> +   every pointer referencing that variable has its top byte set to the same
> +   tag.  The run-time library redefines malloc so that every allocation returns
> +   a tagged pointer and tags the corresponding shadow memory with the same tag.
> +
> +   On each pointer dereference the tag found in the pointer is compared to the
> +   tag found in the shadow memory corresponding to the accessed memory address.
> +   If these tags are found to differ then this memory access is judged to be
> +   invalid and a report is generated.
> +
> +   This method of bug detection is not perfect -- it can not catch every bad
> +   access, but catches them probabilistically instead.  There is always the
> +   possibility that an invalid memory access will happen to access memory
> +   tagged with the same tag as the pointer that this access used.
> +   The chances of this are approx. 0.4% for any two uncorrelated objects.
> +
> +   If this does happen, random tag generation can mitigate the problem by
> +   decreasing the probability that it will *always* happen.  i.e. if two
> +   objects are tagged the same in one run of the binary they are unlikely to be
> +   tagged the same in the next run.
> +   Heap allocated objects always have randomly generated tags, while objects on
> +   the stack can be given random tags determined by command line flags.
> +
> +   [16 byte granule implications]
> +    Since the shadow memory only has a resolution on real memory of 16 bytes,
> +    invalid accesses that are still within the same 16 byte granule as valid
> +    memory will not be caught.
> +
> +    There is a "short-granule" feature in the runtime library which does catch
> +    such accesses, but this feature is not implemented for stack objects (since
> +    stack objects are allocated and tagged by compiler instrumentation, and
> +    this feature has not yet been implemented in GCC instrumentation).
> +
> +    Another outcome of this 16 byte resolution is that each tagged object must
> +    be 16 byte aligned.  If two objects were to share any 16 byte granule in
> +    memory, then they both would have to be given the same tag, and invalid
> +    accesses to one using a pointer to the other would be undetectable.
> +
> +   [Compiler instrumentation]
> +    Compiler instrumentation ensures that two adjacent buffers on the stack are
> +    given different tags, this means an access to one buffer using a pointer
> +    generated from the other (e.g. through buffer overrun) will have mismatched
> +    tags and be caught by hwasan.
> +
> +    We never randomly tag every object on the stack, since that would require
> +    keeping many registers to record each tag.  Instead we randomly generate a
> +    tag for each function frame, and each new stack object uses a tag offset
> +    from that frame tag.
> +    i.e. each object is tagged as RFT + offset, where RFT is the "random frame
> +    tag" generated for this frame.
> +
> +    As a demonstration, using the same example program as in the asan block
> +    comment above:
> +
> +     int
> +     foo ()
> +     {
> +       char a[23] = {0};
> +       int b[2] = {0};
> +
> +       a[5] = 1;
> +       b[1] = 2;
> +
> +       return a[5] + b[1];
> +     }
> +
> +    On AArch64 the stack will be ordered as follows for the above function:
> +
> +    Slot 1/ [24 bytes for variable 'a']
> +    Slot 2/ [8 bytes padding for alignment]
> +    Slot 3/ [8 bytes for variable 'b']
> +    Slot 4/ [8 bytes padding for alignment]
> +
> +    (The padding is there to ensure 16 byte alignment as described in the 16
> +     byte granule implications).
> +
> +    While the shadow memory will be ordered as follows:
> +
> +    - 2 bytes (representing 32 bytes in real memory) tagged with RFT + 1.
> +    - 1 byte (representing 16 bytes in real memory) tagged with RFT + 2.
> +
> +    And any pointer to "a" will have the tag RFT + 1, and any pointer to "b"
> +    will have the tag RFT + 2.
> +
> +   [Top Byte Ignore requirements]
> +    Hwasan requires the ability to store an 8 bit tag in every pointer.  There
> +    is no instrumentation done to remove this tag from pointers before
> +    dereferencing, which means the hardware must ignore this tag during memory
> +    accesses.
> +
> +    One architecture that provides this feature is the AArch64 architecture.
> +    This is the only architecture that hwasan is implemented for at the moment.
> +
> +   [Stack requires cleanup on unwinding]
> +    During normal operation of a hwasan sanitized program, as the stack grows
> +    more space in the shadow memory becomes tagged.  As the stack shrinks this
> +    shadow memory space must become untagged.  If it is not untagged then when
> +    the stack grows again (during other function calls later on in the program)
> +    objects on the stack that are usually not tagged (e.g. parameters passed on
> +    the stack) can be placed in memory whose shadow space is tagged with
> +    something else, and accesses can cause false positive reports.
> +
> +    Hence we place untagging code on every epilogue of functions which tag some
> +    stack objects.
> +
> +    Moreover, the run-time library intercepts longjmp & setjmp to uncolour
> +    when the stack is unwound this way.
> +
> +    C++ exceptions are not yet handled, which means this sanitizer can not
> +    handle C++ code that throws exceptions -- it will give false positives
> +    after an exception has been thrown.  */
> +
> +
>   /* Returns whether we are tagging pointers and checking those tags on memory
>      access.  This is true when checking with either in software or hardware.  */
>   bool
> @@ -1848,6 +2108,8 @@ static tree
>   report_error_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
>   		   int *nargs)
>   {
> +  gcc_assert (!memory_tagging_p ());
> +
>     static enum built_in_function report[2][2][6]
>       = { { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
>   	    BUILT_IN_ASAN_REPORT_LOAD4, BUILT_IN_ASAN_REPORT_LOAD8,
> @@ -2184,7 +2446,13 @@ build_check_stmt (location_t loc, tree base, tree len,
>     if (is_scalar_access)
>       flags |= ASAN_CHECK_SCALAR_ACCESS;
>   
> -  g = gimple_build_call_internal (IFN_ASAN_CHECK, 4,
> +  enum internal_fn fn;
> +  if (memory_tagging_p ())
> +    fn = IFN_HWASAN_CHECK;
> +  else
> +    fn = IFN_ASAN_CHECK;

Maybe I would use a ternary operator here?

> +
> +  g = gimple_build_call_internal (fn, 4,
>   				  build_int_cst (integer_type_node, flags),
>   				  base, len,
>   				  build_int_cst (integer_type_node,
> @@ -2208,10 +2476,13 @@ static void
>   instrument_derefs (gimple_stmt_iterator *iter, tree t,
>   		   location_t location, bool is_store)
>   {
> -  if (is_store && !ASAN_INSTRUMENT_WRITES)
> -    return;
> -  if (!is_store && !ASAN_INSTRUMENT_READS)
> -    return;
> +  if (! memory_tagging_p ())
> +    {
> +    if (is_store && !ASAN_INSTRUMENT_WRITES)
> +      return;
> +    if (!is_store && !ASAN_INSTRUMENT_READS)
> +      return;
> +    }
>   
>     tree type, base;
>     HOST_WIDE_INT size_in_bytes;
> @@ -2271,7 +2542,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
>       {
>         if (DECL_THREAD_LOCAL_P (inner))
>   	return;
> -      if (!ASAN_GLOBALS && is_global_var (inner))
> +      if ((memory_tagging_p () || !ASAN_GLOBALS)
> +	  && is_global_var (inner))
>           return;
>         if (!TREE_STATIC (inner))
>   	{
> @@ -2500,10 +2772,13 @@ maybe_instrument_call (gimple_stmt_iterator *iter)
>   	      break;
>   	    }
>   	}
> -      tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
> -      gimple *g = gimple_build_call (decl, 0);
> -      gimple_set_location (g, gimple_location (stmt));
> -      gsi_insert_before (iter, g, GSI_SAME_STMT);
> +      if (! memory_tagging_p ())
> +	{
> +	  tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
> +	  gimple *g = gimple_build_call (decl, 0);
> +	  gimple_set_location (g, gimple_location (stmt));
> +	  gsi_insert_before (iter, g, GSI_SAME_STMT);
> +	}
>       }
>   
>     bool instrumented = false;
> @@ -2902,6 +3177,9 @@ initialize_sanitizer_builtins (void)
>       = build_function_type_list (void_type_node, uint64_type_node,
>   				ptr_type_node, NULL_TREE);
>   
> +  tree BT_FN_PTR_CONST_PTR_UINT8
> +    = build_function_type_list (ptr_type_node, const_ptr_type_node,
> +				unsigned_char_type_node, NULL_TREE);
>     tree BT_FN_VOID_PTR_UINT8_SIZE
>       = build_function_type_list (void_type_node, ptr_type_node,
>   				unsigned_char_type_node, size_type_node,
> @@ -3237,6 +3515,23 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
>     unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
>     gcc_assert (size_in_bytes);
>   
> +  if (memory_tagging_p ())
> +    {
> +      gcc_assert (HWASAN_STACK);
> +      /* Here we swap ASAN_MARK calls for HWASAN_MARK.
> +	 This is because we are using the approach of using ASAN_MARK as a
> +	 synonym until here.
> +	 That approach means we don't yet have to duplicate all the special
> +	 cases for ASAN_MARK and ASAN_POISON with the exact same handling but
> +	 called HWASAN_MARK etc.  */
> +      gimple *hw_poison_call
> +	= gimple_build_call_internal (IFN_HWASAN_MARK, 3,
> +				      gimple_call_arg (g, 0),
> +				      base, len);
> +      gsi_replace (iter, hw_poison_call, false);
> +      return false;
> +    }
> +
>     g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
>   			   NOP_EXPR, base);
>     gimple_set_location (g, loc);
> @@ -3298,6 +3593,7 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
>   bool
>   asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
>   {
> +  gcc_assert (!memory_tagging_p ());
>     gimple *g = gsi_stmt (*iter);
>     location_t loc = gimple_location (g);
>     bool recover_p;
> @@ -3571,11 +3867,37 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
>   
>         int nargs;
>         bool store_p = gimple_call_internal_p (use, IFN_ASAN_POISON_USE);
> -      tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
> -				    &nargs);
> -
> -      gcall *call = gimple_build_call (fun, 1,
> -				       build_fold_addr_expr (shadow_var));
> +      gcall *call;
> +      if (memory_tagging_p ())
> +	{
> +	  tree fun = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MISMATCH4);
> +	  /* NOTE: hwasan has no __hwasan_report_* functions like asan does.
> +		We use __hwasan_tag_mismatch4 with arguments that tell it the
> +		size of access and load to report all tag mismatches.  */

Huh, this ABI is quite complicated. Can you please document arguments of the function?
Or at least point to libsanitizer definition?

Martin

> +	  unsigned HOST_WIDE_INT size_in_bytes = tree_to_uhwi (size);
> +	  unsigned size_indicator = (size_in_bytes > 16)
> +	    ? 0xf
> +	    : exact_log2 (size_in_bytes);
> +	  unsigned access_info = (0x20 * recover_p)
> +	    + (0x10 * store_p)
> +	    + (size_indicator);
> +	  tree long_pointer_type
> +	    = build_pointer_type (long_long_unsigned_type_node);
> +	  call = gimple_build_call (fun, 3,
> +				    build_fold_addr_expr (shadow_var),
> +				    build_int_cst (long_long_unsigned_type_node,
> +						   access_info),
> +				    build_int_cst (long_pointer_type,
> +						   0),
> +				    size);
> +	}
> +      else
> +	{
> +	  tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
> +					&nargs);
> +	  call = gimple_build_call (fun, 1,
> +				    build_fold_addr_expr (shadow_var));
> +	}
>         gimple_set_location (call, gimple_location (use));
>         gimple *call_to_insert = call;
>   
> @@ -3709,6 +4031,16 @@ make_pass_asan_O0 (gcc::context *ctxt)
>     return new pass_asan_O0 (ctxt);
>   }
>   
> +/*  HWASAN  */
> +static unsigned int
> +hwasan_instrument (void)
> +{
> +  transform_statements ();
> +  last_alloca_addr = NULL_TREE;
> +
> +  return 0;
> +}
> +
>   void
>   hwasan_record_base (rtx base)
>   {
> @@ -3727,6 +4059,15 @@ hwasan_current_tag ()
>     return tag_offset;
>   }
>   
> +rtx
> +hwasan_base ()
> +{
> +  if (! hwasan_base_ptr)
> +    hwasan_record_base (gen_reg_rtx (Pmode));
> +
> +  return hwasan_base_ptr;
> +}
> +
>   void
>   hwasan_increment_tag ()
>   {
> @@ -3858,6 +4199,12 @@ hwasan_emit_untag_frame (rtx dynamic, rtx vars, rtx_insn *before)
>     do_pending_stack_adjust ();
>     rtx_insn *insns = get_insns ();
>     end_sequence ();
> +
> +  /* Clear the hash_map recording which variables are handled by HWASAN_MARK.
> +     The only use in HWASAN is to decide which variables need to be tagged in
> +     the prologue and which don't.  */
> +  delete asan_handled_variables;
> +  asan_handled_variables = NULL;
>     return insns;
>   }
>   
> @@ -3894,4 +4241,214 @@ hwasan_finish_file (void)
>     flag_sanitize |= SANITIZE_HWADDRESS;
>   }
>   
> +/* Construct a function tree for __hwasan_{load,store}{1,2,4,8,16,_n}.
> +   IS_STORE is either 1 (for a store) or 0 (for a load).  */
> +
> +static tree
> +hwasan_check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
> +		   int *nargs)
> +{
> +  static enum built_in_function check[2][2][6]
> +    = { { { BUILT_IN_HWASAN_LOAD1, BUILT_IN_HWASAN_LOAD2,
> +	    BUILT_IN_HWASAN_LOAD4, BUILT_IN_HWASAN_LOAD8,
> +	    BUILT_IN_HWASAN_LOAD16, BUILT_IN_HWASAN_LOADN },
> +	  { BUILT_IN_HWASAN_STORE1, BUILT_IN_HWASAN_STORE2,
> +	    BUILT_IN_HWASAN_STORE4, BUILT_IN_HWASAN_STORE8,
> +	    BUILT_IN_HWASAN_STORE16, BUILT_IN_HWASAN_STOREN } },
> +	{ { BUILT_IN_HWASAN_LOAD1_NOABORT,
> +	    BUILT_IN_HWASAN_LOAD2_NOABORT,
> +	    BUILT_IN_HWASAN_LOAD4_NOABORT,
> +	    BUILT_IN_HWASAN_LOAD8_NOABORT,
> +	    BUILT_IN_HWASAN_LOAD16_NOABORT,
> +	    BUILT_IN_HWASAN_LOADN_NOABORT },
> +	  { BUILT_IN_HWASAN_STORE1_NOABORT,
> +	    BUILT_IN_HWASAN_STORE2_NOABORT,
> +	    BUILT_IN_HWASAN_STORE4_NOABORT,
> +	    BUILT_IN_HWASAN_STORE8_NOABORT,
> +	    BUILT_IN_HWASAN_STORE16_NOABORT,
> +	    BUILT_IN_HWASAN_STOREN_NOABORT } } };
> +  if (size_in_bytes == -1)
> +    {
> +      *nargs = 2;
> +      return builtin_decl_implicit (check[recover_p][is_store][5]);
> +    }
> +  *nargs = 1;
> +  int size_log2 = exact_log2 (size_in_bytes);
> +  return builtin_decl_implicit (check[recover_p][is_store][size_log2]);
> +}
> +
> +
> +bool
> +hwasan_expand_check_ifn (gimple_stmt_iterator *iter, bool)
> +{
> +  gimple *g = gsi_stmt (*iter);
> +  location_t loc = gimple_location (g);
> +  bool recover_p;
> +  if (flag_sanitize & SANITIZE_USER_HWADDRESS)
> +    recover_p = (flag_sanitize_recover & SANITIZE_USER_HWADDRESS) != 0;
> +  else
> +    recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_HWADDRESS) != 0;
> +
> +  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
> +  gcc_assert (flags < ASAN_CHECK_LAST);
> +  bool is_scalar_access = (flags & ASAN_CHECK_SCALAR_ACCESS) != 0;
> +  bool is_store = (flags & ASAN_CHECK_STORE) != 0;
> +  bool is_non_zero_len = (flags & ASAN_CHECK_NON_ZERO_LEN) != 0;
> +
> +  tree base = gimple_call_arg (g, 1);
> +  tree len = gimple_call_arg (g, 2);
> +
> +  /* `align` is unused for HWASAN_CHECK, but I pass the argument anyway
> +     since that way the arguments match ASAN_CHECK.  */
> +  /* HOST_WIDE_INT align = tree_to_shwi (gimple_call_arg (g, 3));  */
> +
> +  unsigned HOST_WIDE_INT size_in_bytes
> +    = is_scalar_access && tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
> +
> +  gimple_stmt_iterator gsi = *iter;
> +
> +  if (!is_non_zero_len)
> +    {
> +      /* So, the length of the memory area to hwasan-protect is
> +	 non-constant.  Let's guard the generated instrumentation code
> +	 like:
> +
> +	 if (len != 0)
> +	   {
> +	     // hwasan instrumentation code goes here.
> +	   }
> +	 // falltrough instructions, starting with *ITER.  */
> +
> +      g = gimple_build_cond (NE_EXPR,
> +			    len,
> +			    build_int_cst (TREE_TYPE (len), 0),
> +			    NULL_TREE, NULL_TREE);
> +      gimple_set_location (g, loc);
> +
> +      basic_block then_bb, fallthrough_bb;
> +      insert_if_then_before_iter (as_a <gcond *> (g), iter,
> +				  /*then_more_likely_p=*/true,
> +				  &then_bb, &fallthrough_bb);
> +      /* Note that fallthrough_bb starts with the statement that was
> +	pointed to by ITER.  */
> +
> +      /* The 'then block' of the 'if (len != 0) condition is where
> +	we'll generate the hwasan instrumentation code now.  */
> +      gsi = gsi_last_bb (then_bb);
> +    }
> +
> +  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
> +			   NOP_EXPR, base);
> +  gimple_set_location (g, loc);
> +  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
> +  tree base_addr = gimple_assign_lhs (g);
> +
> +  int nargs = 0;
> +  tree fun = hwasan_check_func (is_store, recover_p, size_in_bytes, &nargs);
> +  if (nargs == 1)
> +    g = gimple_build_call (fun, 1, base_addr);
> +  else
> +    {
> +      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
> +			       NOP_EXPR, len);
> +      gimple_set_location (g, loc);
> +      gsi_insert_after (&gsi, g, GSI_NEW_STMT);
> +      tree sz_arg = gimple_assign_lhs (g);
> +      g = gimple_build_call (fun, nargs, base_addr, sz_arg);
> +    }
> +
> +  gimple_set_location (g, loc);
> +  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
> +  gsi_remove (iter, true);
> +  *iter = gsi;
> +  return false;
> +}
> +
> +bool
> +hwasan_expand_mark_ifn (gimple_stmt_iterator *)
> +{
> +  /* HWASAN_MARK should only ever be available after the sanopt pass.  */
> +  gcc_unreachable ();
> +}
> +
> +bool
> +gate_hwasan ()
> +{
> +  return memory_tagging_p ();
> +}
> +
> +namespace {
> +
> +const pass_data pass_data_hwasan =
> +{
> +  GIMPLE_PASS, /* type */
> +  "hwasan", /* name */
> +  OPTGROUP_NONE, /* optinfo_flags */
> +  TV_NONE, /* tv_id */
> +  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
> +  0, /* properties_provided */
> +  0, /* properties_destroyed */
> +  0, /* todo_flags_start */
> +  TODO_update_ssa, /* todo_flags_finish */
> +};
> +
> +class pass_hwasan : public gimple_opt_pass
> +{
> +public:
> +  pass_hwasan (gcc::context *ctxt)
> +    : gimple_opt_pass (pass_data_hwasan, ctxt)
> +  {}
> +
> +  /* opt_pass methods: */
> +  opt_pass * clone () { return new pass_hwasan (m_ctxt); }
> +  virtual bool gate (function *) { return gate_hwasan (); }
> +  virtual unsigned int execute (function *) { return hwasan_instrument (); }
> +
> +}; /* class pass_asan  */
> +
> +} /* anon namespace  */
> +
> +gimple_opt_pass *
> +make_pass_hwasan (gcc::context *ctxt)
> +{
> +  return new pass_hwasan (ctxt);
> +}
> +
> +namespace {
> +
> +const pass_data pass_data_hwasan_O0 =
> +{
> +  GIMPLE_PASS, /* type */
> +  "hwasan_O0", /* name */
> +  OPTGROUP_NONE, /* optinfo_flags */
> +  TV_NONE, /* tv_id */
> +  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
> +  0, /* properties_provided */
> +  0, /* properties_destroyed */
> +  0, /* todo_flags_start */
> +  TODO_update_ssa, /* todo_flags_finish */
> +};
> +
> +class pass_hwasan_O0 : public gimple_opt_pass
> +{
> +public:
> +  pass_hwasan_O0 (gcc::context *ctxt)
> +    : gimple_opt_pass (pass_data_hwasan_O0, ctxt)
> +  {}
> +
> +  /* opt_pass methods: */
> +  opt_pass * clone () { return new pass_hwasan_O0 (m_ctxt); }
> +  virtual bool gate (function *) { return !optimize && gate_hwasan (); }
> +  virtual unsigned int execute (function *) { return hwasan_instrument (); }
> +
> +}; // class pass_asan
> +
> +} // anon namespace
> +
> +gimple_opt_pass *
> +make_pass_hwasan_O0 (gcc::context *ctxt)
> +{
> +  return new pass_hwasan_O0 (ctxt);
> +}
> +
>   #include "gt-asan.h"
> diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
> index d05f597b6434f39fe95d4f28dd2ef3ed463dd925..00592b1eea76164471e281c8922893937cf9bb2e 100644
> --- a/gcc/builtin-types.def
> +++ b/gcc/builtin-types.def
> @@ -493,6 +493,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_INT_FEXCEPT_T_PTR_INT, BT_INT, BT_FEXCEPT_T_PTR,
>   		     BT_INT)
>   DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_FEXCEPT_T_PTR_INT, BT_INT,
>   		     BT_CONST_FEXCEPT_T_PTR, BT_INT)
> +DEF_FUNCTION_TYPE_2 (BT_FN_PTR_CONST_PTR_UINT8, BT_PTR, BT_CONST_PTR, BT_UINT8)
>   
>   DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR)
>   
> diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
> index 2d5ece0680538389118b909951863be50d570326..dba1d58b83a1caf657cbfdd309a3576442fd59d0 100644
> --- a/gcc/gimple-pretty-print.c
> +++ b/gcc/gimple-pretty-print.c
> @@ -750,6 +750,7 @@ dump_gimple_call_args (pretty_printer *buffer, gcall *gs, dump_flags_t flags)
>   	  limit = ARRAY_SIZE (reduction_args);
>   	  break;
>   
> +	case IFN_HWASAN_MARK:
>   	case IFN_ASAN_MARK:
>   #define DEF(X) #X
>   	  static const char *const asan_mark_args[] = {IFN_ASAN_MARK_FLAGS};
> diff --git a/gcc/gimplify.c b/gcc/gimplify.c
> index 12ed3f8eb2129f2a68a054347d23e6f3eae2197a..fa886c8f088c4109fee3c97859bad3ed9fc7e073 100644
> --- a/gcc/gimplify.c
> +++ b/gcc/gimplify.c
> @@ -1232,8 +1232,11 @@ asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
>   
>     /* It's necessary to have all stack variables aligned to ASAN granularity
>        bytes.  */
> -  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
> -    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
> +  gcc_assert (!memory_tagging_p () || hwasan_sanitize_stack_p ());
> +  unsigned shadow_granularity
> +    = memory_tagging_p () ? HWASAN_TAG_GRANULE_SIZE : ASAN_SHADOW_GRANULARITY;
> +  if (DECL_ALIGN_UNIT (decl) <= shadow_granularity)
> +    SET_DECL_ALIGN (decl, BITS_PER_UNIT * shadow_granularity);
>   
>     HOST_WIDE_INT flags = poison ? ASAN_MARK_POISON : ASAN_MARK_UNPOISON;
>   
> @@ -14673,7 +14676,7 @@ gimplify_function_tree (tree fndecl)
>         && !needs_to_live_in_memory (ret))
>       DECL_GIMPLE_REG_P (ret) = 1;
>   
> -  if (asan_sanitize_use_after_scope () && sanitize_flags_p (SANITIZE_ADDRESS))
> +  if (asan_sanitize_use_after_scope ())
>       asan_poisoned_variables = new hash_set<tree> ();
>     bind = gimplify_body (fndecl, true);
>     if (asan_poisoned_variables)
> diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
> index 549d6f1153b6e568d1ab8b0e5b8fa8ccc2161b61..19b37629ab8500bb870cfcd135cefb0831a7e856 100644
> --- a/gcc/internal-fn.c
> +++ b/gcc/internal-fn.c
> @@ -457,6 +457,74 @@ expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
>   /* This should get expanded in the sanopt pass.  */
>   
>   static void
> +expand_HWASAN_CHECK (internal_fn, gcall *)
> +{
> +  gcc_unreachable ();
> +}
> +
> +static void
> +expand_HWASAN_CHOOSE_TAG (internal_fn, gcall *gc)
> +{
> +  tree tag = gimple_call_lhs (gc);
> +  rtx target = expand_expr (tag, NULL_RTX, VOIDmode, EXPAND_NORMAL);
> +  machine_mode mode = GET_MODE (target);
> +  gcc_assert (mode == QImode);
> +
> +  rtx base_tag = hwasan_extract_tag (hwasan_base ());
> +  gcc_assert (base_tag);
> +  rtx tag_offset = GEN_INT (hwasan_current_tag ());
> +  rtx chosen_tag = expand_simple_binop (QImode, PLUS, base_tag, tag_offset,
> +					target, /* unsignedp = */1,
> +					OPTAB_WIDEN);
> +
> +  gcc_assert (chosen_tag);
> +  /* Really need to put the tag into the `target` RTX.  */
> +  if (chosen_tag != target)
> +    {
> +      rtx temp = chosen_tag;
> +      machine_mode ret_mode = GET_MODE (chosen_tag);
> +      if (ret_mode != mode)
> +	temp = simplify_gen_unary (TRUNCATE, mode, chosen_tag, ret_mode);
> +
> +      emit_move_insn (target, temp);
> +    }
> +
> +  hwasan_increment_tag ();
> +}
> +
> +static void
> +expand_HWASAN_MARK (internal_fn, gcall *gc)
> +{
> +  HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
> +  bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
> +
> +  tree base = gimple_call_arg (gc, 1);
> +  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
> +  rtx base_rtx = expand_normal (base);
> +
> +  rtx tag = is_poison ? const0_rtx : hwasan_extract_tag (base_rtx);
> +  rtx address = hwasan_create_untagged_base (base_rtx);
> +
> +  tree len = gimple_call_arg (gc, 2);
> +  gcc_assert (tree_fits_shwi_p (len));
> +  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
> +  uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
> +  gcc_assert (size_in_bytes);
> +  size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
> +  rtx size = gen_int_mode (size_in_bytes, Pmode);
> +
> +  rtx func = init_one_libfunc ("__hwasan_tag_memory");
> +  emit_library_call (func,
> +      LCT_NORMAL,
> +      VOIDmode,
> +      address, ptr_mode,
> +      tag, QImode,
> +      size, ptr_mode);
> +}
> +
> +/* This should get expanded in the sanopt pass.  */
> +
> +static void
>   expand_ASAN_CHECK (internal_fn, gcall *)
>   {
>     gcc_unreachable ();
> diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
> index a9459449fee15b17fe9c8ff1113b0305ed936bff..045747c35d012ae583e5e2c9c17e98a5d5cb5363 100644
> --- a/gcc/internal-fn.def
> +++ b/gcc/internal-fn.def
> @@ -303,6 +303,9 @@ DEF_INTERNAL_FN (UBSAN_PTR, ECF_LEAF | ECF_NOTHROW, ".R.")
>   DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
>   DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
>   DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
> +DEF_INTERNAL_FN (HWASAN_CHOOSE_TAG, ECF_LEAF | ECF_NOTHROW, ".")
> +DEF_INTERNAL_FN (HWASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, "..R..")
> +DEF_INTERNAL_FN (HWASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
>   DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, "..R..")
>   DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
>   DEF_INTERNAL_FN (ASAN_POISON, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
> diff --git a/gcc/passes.def b/gcc/passes.def
> index 798a391bd351110d05b89fc6ccc424fd871198a4..e4c395f6b554be8f3c2e06aa4cfcda5d96130f99 100644
> --- a/gcc/passes.def
> +++ b/gcc/passes.def
> @@ -246,6 +246,7 @@ along with GCC; see the file COPYING3.  If not see
>         NEXT_PASS (pass_sink_code);
>         NEXT_PASS (pass_sancov);
>         NEXT_PASS (pass_asan);
> +      NEXT_PASS (pass_hwasan);
>         NEXT_PASS (pass_tsan);
>         NEXT_PASS (pass_dce);
>         /* Pass group that runs when 1) enabled, 2) there are loops
> @@ -364,6 +365,7 @@ along with GCC; see the file COPYING3.  If not see
>         NEXT_PASS (pass_dce);
>         NEXT_PASS (pass_sancov);
>         NEXT_PASS (pass_asan);
> +      NEXT_PASS (pass_hwasan);
>         NEXT_PASS (pass_tsan);
>         /* ???  We do want some kind of loop invariant motion, but we possibly
>            need to adjust LIM to be more friendly towards preserving accurate
> @@ -389,6 +391,7 @@ along with GCC; see the file COPYING3.  If not see
>     NEXT_PASS (pass_sancov_O0);
>     NEXT_PASS (pass_lower_switch_O0);
>     NEXT_PASS (pass_asan_O0);
> +  NEXT_PASS (pass_hwasan_O0);
>     NEXT_PASS (pass_tsan_O0);
>     NEXT_PASS (pass_sanopt);
>     NEXT_PASS (pass_cleanup_eh);
> diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
> index 7bd50715f24a2cb154b578e2abdea4e8fcdb2107..6cfb7951e21d4ee2f31921097796650afb02b7b0 100644
> --- a/gcc/sanitizer.def
> +++ b/gcc/sanitizer.def
> @@ -183,6 +183,60 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
>   /* Hardware Address Sanitizer.  */
>   DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
>   		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1, "__hwasan_load1",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2, "__hwasan_load2",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4, "__hwasan_load4",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8, "__hwasan_load8",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16, "__hwasan_load16",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN, "__hwasan_loadN",
> +		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1, "__hwasan_store1",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2, "__hwasan_store2",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4, "__hwasan_store4",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8, "__hwasan_store8",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16, "__hwasan_store16",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN, "__hwasan_storeN",
> +		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1_NOABORT, "__hwasan_load1_noabort",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2_NOABORT, "__hwasan_load2_noabort",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4_NOABORT, "__hwasan_load4_noabort",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8_NOABORT, "__hwasan_load8_noabort",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16_NOABORT, "__hwasan_load16_noabort",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN_NOABORT, "__hwasan_loadN_noabort",
> +		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1_NOABORT, "__hwasan_store1_noabort",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2_NOABORT, "__hwasan_store2_noabort",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4_NOABORT, "__hwasan_store4_noabort",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8_NOABORT, "__hwasan_store8_noabort",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16_NOABORT, "__hwasan_store16_noabort",
> +		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN_NOABORT, "__hwasan_storeN_noabort",
> +		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MISMATCH4, "__hwasan_tag_mismatch4",
> +		      BT_FN_VOID_PTR, ATTR_NOTHROW_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_HANDLE_LONGJMP, "__hwasan_handle_longjmp",
> +		      BT_FN_VOID_CONST_PTR, ATTR_NOTHROW_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_PTR, "__hwasan_tag_pointer",
> +		      BT_FN_PTR_CONST_PTR_UINT8, ATTR_TMPURE_NOTHROW_LEAF_LIST)
>   DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
>   		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
>   
> diff --git a/gcc/sanopt.c b/gcc/sanopt.c
> index 00ade87283223798038e58c37e9138fe483de5a1..756e9ede899f8c87fdfc61277bdb79512107655c 100644
> --- a/gcc/sanopt.c
> +++ b/gcc/sanopt.c
> @@ -773,7 +773,8 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
>     basic_block son;
>     gimple_stmt_iterator gsi;
>     sanopt_info *info = (sanopt_info *) bb->aux;
> -  bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0;
> +  bool asan_check_optimize
> +    = ((flag_sanitize & SANITIZE_ADDRESS) != 0) || memory_tagging_p ();
>   
>     for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
>       {
> @@ -803,6 +804,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
>         if (asan_check_optimize
>   	  && gimple_call_builtin_p (stmt, BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT))
>   	{
> +	  gcc_assert (!memory_tagging_p ());
>   	  use_operand_p use;
>   	  gimple *use_stmt;
>   	  if (single_imm_use (gimple_vdef (stmt), &use, &use_stmt))
> @@ -831,6 +833,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
>   	  case IFN_UBSAN_PTR:
>   	    remove = maybe_optimize_ubsan_ptr_ifn (ctx, stmt);
>   	    break;
> +	  case IFN_HWASAN_CHECK:
>   	  case IFN_ASAN_CHECK:
>   	    if (asan_check_optimize)
>   	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
> @@ -1256,6 +1259,10 @@ sanitize_rewrite_addressable_params (function *fun)
>   unsigned int
>   pass_sanopt::execute (function *fun)
>   {
> +  /* n.b. ASAN_MARK is used for both HWASAN and ASAN.
> +     asan_num_accesses is hence used to count either HWASAN_CHECK or ASAN_CHECK
> +     stuff.  This is fine because you can only have one of these active at a
> +     time.  */
>     basic_block bb;
>     int asan_num_accesses = 0;
>     bool contains_asan_mark = false;
> @@ -1263,10 +1270,10 @@ pass_sanopt::execute (function *fun)
>     /* Try to remove redundant checks.  */
>     if (optimize
>         && (flag_sanitize
> -	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT
> +	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_HWADDRESS
>   	     | SANITIZE_ADDRESS | SANITIZE_VPTR | SANITIZE_POINTER_OVERFLOW)))
>       asan_num_accesses = sanopt_optimize (fun, &contains_asan_mark);
> -  else if (flag_sanitize & SANITIZE_ADDRESS)
> +  else if (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS))
>       {
>         gimple_stmt_iterator gsi;
>         FOR_EACH_BB_FN (bb, fun)
> @@ -1286,7 +1293,7 @@ pass_sanopt::execute (function *fun)
>         sanitize_asan_mark_poison ();
>       }
>   
> -  if (asan_sanitize_stack_p ())
> +  if (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ())
>       sanitize_rewrite_addressable_params (fun);
>   
>     bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
> @@ -1328,6 +1335,9 @@ pass_sanopt::execute (function *fun)
>   		case IFN_UBSAN_VPTR:
>   		  no_next = ubsan_expand_vptr_ifn (&gsi);
>   		  break;
> +		case IFN_HWASAN_CHECK:
> +		  no_next = hwasan_expand_check_ifn (&gsi, use_calls);
> +		  break;
>   		case IFN_ASAN_CHECK:
>   		  no_next = asan_expand_check_ifn (&gsi, use_calls);
>   		  break;
> @@ -1339,6 +1349,9 @@ pass_sanopt::execute (function *fun)
>   						    &need_commit_edge_insert,
>   						    shadow_vars_mapping);
>   		  break;
> +		case IFN_HWASAN_MARK:
> +		  no_next = hwasan_expand_mark_ifn (&gsi);
> +		  break;
>   		default:
>   		  break;
>   		}
> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index 7bd75548d2aebb3415ac85ec40ad25e5ca794094..8fc4e9c9232e7ee4bc31558ef01bb2b1b9edfa27 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -508,7 +508,7 @@ compile_file (void)
>         if (flag_sanitize & SANITIZE_THREAD)
>   	tsan_finish_file ();
>   
> -      if (flag_sanitize & SANITIZE_HWADDRESS)
> +      if (gate_hwasan ())
>   	hwasan_finish_file ();
>   
>         omp_finish_file ();
> diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
> index a987661530eafabc5f5eb5a4fae27dab8b2e54fb..bfcad7112d75c8460bc7bcdea545cad347928e30 100644
> --- a/gcc/tree-pass.h
> +++ b/gcc/tree-pass.h
> @@ -341,6 +341,8 @@ extern void register_pass (opt_pass* pass, pass_positioning_ops pos,
>   
>   extern gimple_opt_pass *make_pass_asan (gcc::context *ctxt);
>   extern gimple_opt_pass *make_pass_asan_O0 (gcc::context *ctxt);
> +extern gimple_opt_pass *make_pass_hwasan (gcc::context *ctxt);
> +extern gimple_opt_pass *make_pass_hwasan_O0 (gcc::context *ctxt);
>   extern gimple_opt_pass *make_pass_tsan (gcc::context *ctxt);
>   extern gimple_opt_pass *make_pass_tsan_O0 (gcc::context *ctxt);
>   extern gimple_opt_pass *make_pass_sancov (gcc::context *ctxt);
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 7/X] [libsanitizer] Add tests
  2019-11-07 18:38   ` [PATCH 7/X] [libsanitizer] Add tests Matthew Malcomson
@ 2019-11-20 14:16     ` Martin Liška
  0 siblings, 0 replies; 150+ messages in thread
From: Martin Liška @ 2019-11-20 14:16 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, kcc, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 11/7/19 7:37 PM, Matthew Malcomson wrote:
> Adding hwasan tests.
> 
> Frankly, these could be tidied up a little.
> I will be tidying them up while getting feedback on the hwasan introduction.

Which is file, however I would consider adding some dynamic allocation tests.
One can easily inspire either in GCC for ASAN, or here:
https://github.com/llvm/llvm-project/tree/master/compiler-rt/test/hwasan

Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-07 18:37 v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC Matthew Malcomson
  2019-11-05 11:32 ` Matthew Malcomson
  2019-11-11 16:16 ` v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC Matthew Malcomson
@ 2019-11-20 14:33 ` Martin Liška
  2019-11-20 17:41   ` Matthew Malcomson
  2 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2019-11-20 14:33 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 11/7/19 7:37 PM, Matthew Malcomson wrote:
> I have rebased this series onto Martin Liska's patches that take the most
> recent libhwasan from upstream LLVM.
> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00340.html
> 
> I've also cleared up some nomenclature (I had previously used the word 'colour'
> a few times instead of the word 'tag' and that clashes with other descriptions)
> and based the patch series off a more recent GCC revision (r277678).
> 
> There's an ongoing discussion on whether to have __SANITIZER_ADDRESS__, or
> __SANITIZER_HWADDRESS__, but I'm keeping that discussion to the existing
> thread.
> 
> Similarly there's still the question around C++ exceptions that I'm keeping to
> the existing thread (on the first patch series).
> 
> 
> NOTE:
>    Unfortunately, there's a bug in the more recent version of GCC I rebased
>    onto.
>    Hwasan catches this when bootstrapping, which means bootstrapping with hwasan
>    fails.
>    I'm working on tracking the bug down now, but sending this series upstream
>    for visibility while that happens.
> 
>    Bugzilla link:
>    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92410
> 
> Entire patch series attached to cover letter.=
> 

Hello.

Thank you very much for the patch set, I've just wrote some inline replies
and I have also some questions/suggestions that I'll summarize here. One caveat,
I'm not maintainer in any of the ideas and I must confirm that I haven't made
a deep review of the part which relates to new RTL hooks and stack expansion.
I expect Jakub can help us here.

Questions/notes:
a) For ASAN we do have bunch of parameters:

gcc --help=param | grep asan
   asan-stack                  Enable asan stack protection.
   asan-instrument-allocas     Enable asan allocas/VLAs protection.
   asan-globals                Enable asan globals protection.
   asan-instrument-writes      Enable asan store operations protection.
   asan-instrument-reads       Enable asan load operations protection.
   asan-memintrin              Enable asan builtin functions protection.
   asan-use-after-return       Enable asan detection of use-after-return bugs.
   asan-instrumentation-with-call-threshold Use callbacks instead of inline code if number of accesses in function becomes greater or equal to this number.

We can probably use some of these in order to drive hwaddress in a similar way. Most of them makes sense for hwaddress as well

b) Apparently clang prints also value of all registers. Do we want to do the same?

$ clang /home/marxin/Programming/gcc/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-1.c -fsanitize=hwaddress  && ./a.out
...
Tags for short granules around the buggy address (one tag corresponds to 16 bytes):
    ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
=> ..  ..  ..  ..  ..  ..  ..  ..  e9  .. [..] ..  ..  ..  ..  .. <=
    ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags
Registers where the failure occurred (pc 0xaaaab2976d60):
     x0  0000000000000063  x1  0000fffff763249c  x2  e900fffff76324a4  x3  0000aaaab2976d94
     x4  0000000000000000  x5  165a043ab8eb3734  x6  0000000000000000  x7  0000aaaab299f99c
     x8  0000000000000011  x9  0200efff00000000  x10 0000000000000000  x11 0000000000000063
     x12 0200fffeff763241  x13 0000000000000011  x14 0000000000000008  x15 00000000c000e9e9
     x16 0000aaaab2959b88  x17 0000000000000007  x18 0000000000000040  x19 0000aaaab2977148
     x20 0000000000000000  x21 0000aaaab2950388  x22 0000000000000000  x23 0000000000000000
     x24 0000000000000000  x25 0000000000000000  x26 0000000000000000  x27 0000000000000000
     x28 0000000000000000  x29 0000fffff76324a0  x30 0000aaaab2976d60
SUMMARY: HWAddressSanitizer: tag-mismatch (/home/marxin/Programming/gcc/a.out+0x2cd5c) in using_stack

c) I'm a bit confused by the pointer tags, where clang does:

$ clang /home/marxin/Programming/gcc/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-1.c -fsanitize=hwaddress  && ./a.out
==30028==ERROR: HWAddressSanitizer: tag-mismatch on address 0xfffff76324a4 at pc 0xaaaab2976d60
READ of size 4 at 0xfffff76324a4 tags: e9/00 (ptr/mem) in thread T0
     #0 0xaaaab2976d5c in using_stack (/home/marxin/Programming/gcc/a.out+0x2cd5c)
     #1 0xaaaab2976e08 in main (/home/marxin/Programming/gcc/a.out+0x2ce08)
     #2 0xffff83ca73e8 in __libc_start_main (/lib64/libc.so.6+0x243e8)
     #3 0xaaaab29503b8 in _start /home/abuild/rpmbuild/BUILD/glibc-2.30/csu/../sysdeps/aarch64/start.S:92

Address 0xfffff76324a4 is located in stack of thread T0
Thread: T0 0xeffe00002000 stack: [0xfffff6e33000,0xfffff7633000) sz: 8388608 tls: [0xffff83f82d20,0xffff83f83470)
Previously allocated frames:
   record_addr:0xffff83c68748 record:0x324aaaaab2976c8c in using_stack (/home/marxin/Programming/gcc/a.out+0x2cc8c)
Memory tags around the buggy address (one tag corresponds to 16 bytes):
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
=> 00  69  69  69  69  69  e9  e9  08  00 [00] 00  00  00  00  00 <=
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Tags for short granules around the buggy address (one tag corresponds to 16 bytes):
    ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
=> ..  ..  ..  ..  ..  ..  ..  ..  e9  .. [..] ..  ..  ..  ..  .. <=
    ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..

where I can see 'e9' in the dump. But for the GCC there's no:

$ gcc /home/marxin/Programming/gcc/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-1.c -fsanitize=hwaddress  && ./a.out
==30037==ERROR: HWAddressSanitizer: tag-mismatch on address 0xfffffd2bf574 at pc 0xffff81e3ad70
READ of size 4 at 0xfffffd2bf574 tags: df/de (ptr/mem) in thread T0
     #0 0xffff81e3ad6c in SigTrap<2> ../../../../libsanitizer/hwasan/hwasan_checks.h:27
     #1 0xffff81e3ad6c in CheckAddress<(__hwasan::ErrorAction)0, (__hwasan::AccessType)0, 2> ../../../../libsanitizer/hwasan/hwasan_checks.h:88
     #2 0xffff81e3ad6c in __hwasan_load4 ../../../../libsanitizer/hwasan/hwasan.cpp:475
     #3 0x4008bc in using_stack (/home/marxin/Programming/gcc/a.out+0x4008bc)
     #4 0x400910 in main (/home/marxin/Programming/gcc/a.out+0x400910)
     #5 0xffff81cdd3e8 in __libc_start_main (/lib64/libc.so.6+0x243e8)
     #6 0x4006b0  (/home/marxin/Programming/gcc/a.out+0x4006b0)

0xfffffd2bf574 is located to the right of a global variable in ([stack]+0x20550)
Address 0xfffffd2bf574 is located in stack of thread T0
Thread: T0 0xeffe00002000 stack: [0xfffffcac0000,0xfffffd2c0000) sz: 8388608 tls: [0xffff827a4020,0xffff827a4790)
Previously allocated frames:
Memory tags around the buggy address (one tag corresponds to 16 bytes):
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
=> 00  00  00  df  df  df  de [de] de  de  de  00  00  00  00  00 <=
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Tags for short granules around the buggy address (one tag corresponds to 16 bytes):
    ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
=> ..  ..  ..  ..  ..  ..  .. [..] ..  ..  ..  ..  ..  ..  ..  .. <=
    ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..

It's bit confusing. May I please ask for explanation?

d) Are you planning to come up with inline stack variable tagging/untagging for GCC 10?
e) In hwasan_increment_tag, shouldn't you skip HWASAN_STACK_BACKGROUND color?
f) I would rename ASAN_MARK in tree dumps to HWASAN_MARK, similarly to what you did
    for HWASAN_CHECK?
g) I noticed quite some GNU coding style violations: 'if (! my_cond)', should be 'if (!my_cond)',
    but it's a nit.
h) I maybe found a bug for:
$ cat malloc.c
#include <stdlib.h>

int main(int argc, char **argv)
{
	char foo[3];
	char *ptr = &foo[2];
	__builtin_printf ("ptr: %p\n", ptr);
	return *(ptr+10);
}

$ gcc malloc.c -fsanitize=hwaddress && ./a.out
ptr: 0x3c00ffffc86ac092
$ gcc malloc.c -fsanitize=address && ./a.out
ptr: 0xffffc41e0c32
=================================================================
==30102==ERROR: AddressSanitizer: stack-buffer-overflow on address 0xffffc41e0c3c at pc 0x0000004009a4 bp 0xffffc41e0ba0 sp 0xffffc41e0bb8
...
$ clang malloc.c -fsanitize=hwaddress && ./a.out
==30107==ERROR: HWAddressSanitizer: tag-mismatch on address 0xfffff12f5e2c at pc 0xaaaaad97eaa4
READ of size 1 at 0xfffff12f5e2c tags: e4/03 (ptr/mem) in thread T0

i) As mentioned, we would appreciate a comment before each newly added function :)

As said, thanks for working on that.
Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-11-13 15:25           ` Matthew Malcomson
@ 2019-11-20 14:38             ` Martin Liška
  2019-11-20 15:46               ` Matthew Malcomson
  2019-11-26 10:15               ` Martin Liška
  0 siblings, 2 replies; 150+ messages in thread
From: Martin Liška @ 2019-11-20 14:38 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 11/13/19 4:24 PM, Matthew Malcomson wrote:
> On 12/11/2019 12:08, Martin Liška wrote:
>> On 11/11/19 5:03 PM, Matthew Malcomson wrote:
>>> Ah!
>>> My apologies -- I sent up a series with a few documentation mistakes.
>>> (the others were wording problems so less noticeable)
>>
>> That's fine, I fixed that very easily.
>>
>> Right now, I can confirm using a aarch64 KVM with the following linux
>> kernel:
>> 5.4.0-rc6-3.g7068448-default works. I haven't tried HWASAN bootstrap,
>> but I can
>> run almost all hwasan.exp tests.
>>
>> There are 2 exceptions:
>>
>> FAIL: gcc.dg/hwasan/stack-tagging-basic-1.c   -O2 -flto
>> -fuse-linker-plugin -fno-fat-lto-objects  execution test
>> FAIL: gcc.dg/hwasan/large-aligned-1.c   -O2 -flto -fuse-linker-plugin
>> -fno-fat-lto-objects  execution test
> 
> Wow, I have no idea how I missed that but thanks for the catch!
> 
>>
>> These fail due to unused value of a function that returns int. The
>> attached patch fixes that.
>> I'm planning to make a proper comments about the series starting next week.
>>
>> For the meantime, I have some libsanitizer upstream suggestions
>> that you can may be discuss. It's mostly about
>> shadow memory dump differences in between ASAN and HWASAN:
>>
> 
>>
>> Improvements I see:
>> a) HWASAN uses less compact dump (2 spaces compared to one)
>> b) HWASAN is not using colors and it would be handy to know which color
>> is used for "uninitialized" tags
>>      and I would mark the 2 compares tags in dumps (ptr/mem)
>> c) "Tags for short granules around the buggy address" dump is using a
>> dot notation which seems a bit misleading
>> d) For HWASAN address offset is missing for each line in both shadow
>> memory and the pointer

Hi.

> 
> To mention my thoughts in turn:
> a) The dump of HWASAN takes more space, but represents more application
>      bytes for a given space on the console.
>      Each byte of HWASAN shadow space represents 16 application bytes
>      while each byte in ASAN shadow space represents 8 application bytes.
>      I personally appreciate the extra spacing, and figure that the 8|16
>      byte difference allows for this.

Thanks for explanation, it works for me.

> 
> b) Marking 'ptr' and 'mem' in the dump sounds like a good idea to me.
>      Exactly how I'm not sure -- maybe with a colourscheme?  Do you have a
>      marking in mind?

Libsanitizer is capable of using colors for report printing.
I can help with that and come up with a patch for upstream.

> 
>      Uninitialised shadow space has the zero tag, however, there are a few
>      extra details that help understanding these traces:
> 
>      On the stack, zero is both uninitialized and "the background" (i.e.
>      the tag for anything not specially instrumented, like register spills
>      and parameters passed on the stack).
>      However, accessible tagged objects can be given a zero tag.

Question here would be if we should use non-zero tags here? Maybe related
to my comment about skipping of HWASAN_STACK_BACKGROUND tag?

>      We allow this to avoid runtime checks when incrementing the random
>      frame tag to get the tag for a new local variable.
>      We can easily avoid the zero tag at compile-time if we don't use a
>      random tag for each frame.  I had this in development at one point
>      and found it quite useful for verification.  I already have an option
>      to disable random tags for each frame that this ability could go
>      under.
>      I don't believe (but am not 100% certain) this option is in LLVM.
> 
>      On the heap uninitialised is tag zero, but memory that has been
>      `free`d is given a random tag, so non-zero in a dump does not mean a
>      valid object.
> 
> c) Is there an alternate notation you have in mind?
>      I would guess the "dots" are there to say "this granule has no
>      short-tag information", and I'm not sure what would be a better
>      way to demonstrate that.

Now I've got it here. Dot means that top-byte of a pointer equals to zero.
Right?

> 
> d) I agree, an address offset annotation on each line of the shadow
>      memory sounds useful.

I can come up with an upstream patch as well.

Thank you,
Martin

> 
> Cheers,
> MM
> 
>>
>> Thanks,
>> Martin
>>
>>>
>>> I'm attaching the entire updated patch series (with the other
>>> documentation fixes in it too) and the fixed patch for just this part in
>>> case you just want to compile and test right now.
>>
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN
  2019-11-20 14:06     ` Martin Liška
@ 2019-11-20 14:42       ` Matthew Malcomson
  2019-11-20 14:48         ` Martin Liška
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-20 14:42 UTC (permalink / raw)
  To: Martin Liška, gcc-patches
  Cc: nd, kcc, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

Hi Martin,

Thanks for the review,
I'll get working on your comments now, but since I really enjoyed 
finding this bug in ./configure when I hit it I thought I'd answer this 
right away.


On 20/11/2019 14:02, Martin Liška wrote:
> On 11/7/19 7:37 PM, Matthew Malcomson wrote:
>>
>> diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
>> index 
>> 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 
>> 100644
>> --- a/config/bootstrap-hwasan.mk
>> +++ b/config/bootstrap-hwasan.mk
>> @@ -1,7 +1,11 @@
>>   # This option enables -fsanitize=hwaddress for stage2 and stage3.
>> +# We need to disable random frame tags for bootstrap since the 
>> autoconf check
>> +# for which direction the stack is growing has UB that a random frame 
>> tag
>> +# breaks.  Running with a random frame tag gives approx. 50% chance of
>> +# bootstrap comparison diff in libiberty/alloca.c.
> 
> Here I would like to see what's exactly the problem. I would expect ASAN 
> will
> have exactly the same problem? Can you please isolate it and file a bug. 
> I bet
> a configure script should not expose an undefined behavior.
> 

The configure problem is this snippet below:


find_stack_direction ()
{
   static char *addr = 0;
   auto char dummy;
   if (addr == 0)
     {
       addr = &dummy;
       return find_stack_direction ();
     }
   else
     return (&dummy > addr) ? 1 : -1;
}
main ()
{
   exit (find_stack_direction() < 0);
}


configure uses this to determine the direction that the stack grows.

`find_stack_direction` compares the address of two different objects and 
uses that to make a decision.

With HWASAN random frame tags the answer to the comparison is mostly 
determined by what random tag was assigned to the object in each frame, 
rather than the memory layout of the stack -- which means this configure 
test program can end up getting different answers on different runs.

This is not a problem for ASAN since ASAN does not store tags in the 
pointers of variables.


You're right -- I should file a bug on that for configure.

For reference the UB clause in the standard is 6.5.8 #5 (relational 
operators) where there's a sentence at the end saying "In all other 
cases, the behaviour is undefined".  Essentially, this program is 
comparing the address of two different objects on the stack, and that's 
not allowed.

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN
  2019-11-20 14:42       ` Matthew Malcomson
@ 2019-11-20 14:48         ` Martin Liška
  2019-11-20 15:02           ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2019-11-20 14:48 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, kcc, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 11/20/19 3:37 PM, Matthew Malcomson wrote:
> Hi Martin,
> 
> Thanks for the review,

You're welcome.

> I'll get working on your comments now, but since I really enjoyed
> finding this bug in ./configure when I hit it I thought I'd answer this
> right away.

Heh :)

> 
> 
> On 20/11/2019 14:02, Martin Liška wrote:
>> On 11/7/19 7:37 PM, Matthew Malcomson wrote:
>>>
>>> diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
>>> index
>>> 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541
>>> 100644
>>> --- a/config/bootstrap-hwasan.mk
>>> +++ b/config/bootstrap-hwasan.mk
>>> @@ -1,7 +1,11 @@
>>>    # This option enables -fsanitize=hwaddress for stage2 and stage3.
>>> +# We need to disable random frame tags for bootstrap since the
>>> autoconf check
>>> +# for which direction the stack is growing has UB that a random frame
>>> tag
>>> +# breaks.  Running with a random frame tag gives approx. 50% chance of
>>> +# bootstrap comparison diff in libiberty/alloca.c.
>>
>> Here I would like to see what's exactly the problem. I would expect ASAN
>> will
>> have exactly the same problem? Can you please isolate it and file a bug.
>> I bet
>> a configure script should not expose an undefined behavior.
>>
> 
> The configure problem is this snippet below:
> 
> 
> find_stack_direction ()
> {
>     static char *addr = 0;
>     auto char dummy;
>     if (addr == 0)
>       {
>         addr = &dummy;
>         return find_stack_direction ();
>       }
>     else
>       return (&dummy > addr) ? 1 : -1;
> }
> main ()
> {
>     exit (find_stack_direction() < 0);
> }
> 
> 
> configure uses this to determine the direction that the stack grows.
> 
> `find_stack_direction` compares the address of two different objects and
> uses that to make a decision.
> 
> With HWASAN random frame tags the answer to the comparison is mostly
> determined by what random tag was assigned to the object in each frame,
> rather than the memory layout of the stack -- which means this configure
> test program can end up getting different answers on different runs.
> 
> This is not a problem for ASAN since ASAN does not store tags in the
> pointers of variables.
> 
> 
> You're right -- I should file a bug on that for configure.
> 
> For reference the UB clause in the standard is 6.5.8 #5 (relational
> operators) where there's a sentence at the end saying "In all other
> cases, the behaviour is undefined".  Essentially, this program is
> comparing the address of two different objects on the stack, and that's
> not allowed.

Well, to be honest, this is quite cute violation of the standard. I would
have written exactly the same code for the stack direction direction. I understand
that a top byte will (a.k.a. tag) make the randomness.

Do you have an idea how can we rewrite the check?
Thanks,
Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN
  2019-11-20 14:48         ` Martin Liška
@ 2019-11-20 15:02           ` Matthew Malcomson
  2019-11-20 18:13             ` Joseph Myers
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-20 15:02 UTC (permalink / raw)
  To: Martin Liška, gcc-patches
  Cc: nd, kcc, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 20/11/2019 14:46, Martin Liška wrote:
> On 11/20/19 3:37 PM, Matthew Malcomson wrote:
>> Hi Martin,
>>
>> Thanks for the review,
> 
> You're welcome.
> 
>> I'll get working on your comments now, but since I really enjoyed
>> finding this bug in ./configure when I hit it I thought I'd answer this
>> right away.
> 
> Heh :)
> 
>>
>>
>> On 20/11/2019 14:02, Martin Liška wrote:
>>> On 11/7/19 7:37 PM, Matthew Malcomson wrote:
>>>>
>>>> diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
>>>> index
>>>> 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 
>>>>
>>>> 100644
>>>> --- a/config/bootstrap-hwasan.mk
>>>> +++ b/config/bootstrap-hwasan.mk
>>>> @@ -1,7 +1,11 @@
>>>>    # This option enables -fsanitize=hwaddress for stage2 and stage3.
>>>> +# We need to disable random frame tags for bootstrap since the
>>>> autoconf check
>>>> +# for which direction the stack is growing has UB that a random frame
>>>> tag
>>>> +# breaks.  Running with a random frame tag gives approx. 50% chance of
>>>> +# bootstrap comparison diff in libiberty/alloca.c.
>>>
>>> Here I would like to see what's exactly the problem. I would expect ASAN
>>> will
>>> have exactly the same problem? Can you please isolate it and file a bug.
>>> I bet
>>> a configure script should not expose an undefined behavior.
>>>
>>
>> The configure problem is this snippet below:
>>
>>
>> find_stack_direction ()
>> {
>>     static char *addr = 0;
>>     auto char dummy;
>>     if (addr == 0)
>>       {
>>         addr = &dummy;
>>         return find_stack_direction ();
>>       }
>>     else
>>       return (&dummy > addr) ? 1 : -1;
>> }
>> main ()
>> {
>>     exit (find_stack_direction() < 0);
>> }
>>
>>
>> configure uses this to determine the direction that the stack grows.
>>
>> `find_stack_direction` compares the address of two different objects and
>> uses that to make a decision.
>>
>> With HWASAN random frame tags the answer to the comparison is mostly
>> determined by what random tag was assigned to the object in each frame,
>> rather than the memory layout of the stack -- which means this configure
>> test program can end up getting different answers on different runs.
>>
>> This is not a problem for ASAN since ASAN does not store tags in the
>> pointers of variables.
>>
>>
>> You're right -- I should file a bug on that for configure.
>>
>> For reference the UB clause in the standard is 6.5.8 #5 (relational
>> operators) where there's a sentence at the end saying "In all other
>> cases, the behaviour is undefined".  Essentially, this program is
>> comparing the address of two different objects on the stack, and that's
>> not allowed.
> 
> Well, to be honest, this is quite cute violation of the standard. I would
> have written exactly the same code for the stack direction direction. I 
> understand
> that a top byte will (a.k.a. tag) make the randomness.
> 
> Do you have an idea how can we rewrite the check?
> Thanks,
> Martin
> 

I don't have much of a plan.

The most promising lead I have is that libiberty/alloca.c has a similar 
functionality but with macros to account for a special case.

Instead of just using '&' it uses a macro `ADDRESS_FUNCTION`.
I can use that macro to ensure the libiberty/alloca.c function could 
handle tags, but I'm not sure that architecture specific conditions will 
neatly fit into autoconf.

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-11-20 14:38             ` Martin Liška
@ 2019-11-20 15:46               ` Matthew Malcomson
  2019-11-21 13:10                 ` Martin Liška
  2019-11-26 10:15               ` Martin Liška
  1 sibling, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-20 15:46 UTC (permalink / raw)
  To: Martin Liška, gcc-patches
  Cc: nd, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 20/11/2019 14:33, Martin Liška wrote:
> On 11/13/19 4:24 PM, Matthew Malcomson wrote:
>> On 12/11/2019 12:08, Martin Liška wrote:
>>> On 11/11/19 5:03 PM, Matthew Malcomson wrote:
>>>> Ah!
>>>> My apologies -- I sent up a series with a few documentation mistakes.
> 
>>
>> b) Marking 'ptr' and 'mem' in the dump sounds like a good idea to me.
>>      Exactly how I'm not sure -- maybe with a colourscheme?  Do you 
>> have a
>>      marking in mind?
> 
> Libsanitizer is capable of using colors for report printing.
> I can help with that and come up with a patch for upstream.
> 
>>
>>      Uninitialised shadow space has the zero tag, however, there are a 
>> few
>>      extra details that help understanding these traces:
>>
>>      On the stack, zero is both uninitialized and "the background" (i.e.
>>      the tag for anything not specially instrumented, like register 
>> spills
>>      and parameters passed on the stack).
>>      However, accessible tagged objects can be given a zero tag.
> 
> Question here would be if we should use non-zero tags here? Maybe related
> to my comment about skipping of HWASAN_STACK_BACKGROUND tag?

Unfortunately we can't skip non-zero tags at compile time when using a 
random frame tag.  This is because we don't know at compile time what 
the random frame tag will be.

On each entry to a frame a "base tag" is generated randomly at runtime.
Each local object in the frame has a compile-time offset that's what 
gets calculated in `hwasan_increment_tag` -- the offset from this random 
tag.
The tag assigned to a local object is the runtime random frame tag plus 
the compile-time constant offset.


I could avoid HWASAN_STACK_BACKGROUND as a tag when the parameter 
`hwasan-random-frame-tag` is false, since then there is no runtime 
random base tag (instead I start with zero).

I'll be happy to add that in if you'd like -- I decided against it since 
it would only matter when a function has 256 or more variables, but I 
flip-flopped on the decision a few times.

> 
>>      We allow this to avoid runtime checks when incrementing the random
>>      frame tag to get the tag for a new local variable.
>>      We can easily avoid the zero tag at compile-time if we don't use a
>>      random tag for each frame.  I had this in development at one point
>>      and found it quite useful for verification.  I already have an 
>> option
>>      to disable random tags for each frame that this ability could go
>>      under.
>>      I don't believe (but am not 100% certain) this option is in LLVM.
>>
>>      On the heap uninitialised is tag zero, but memory that has been
>>      `free`d is given a random tag, so non-zero in a dump does not mean a
>>      valid object.
>>
>> c) Is there an alternate notation you have in mind?
>>      I would guess the "dots" are there to say "this granule has no
>>      short-tag information", and I'm not sure what would be a better
>>      way to demonstrate that.
> 
> Now I've got it here. Dot means that top-byte of a pointer equals to zero.
> Right?


Ah!
I think I never described the "short-tag" functionality, and the fact 
it's in the debug output is getting confusing.

This will also be part of answering your question "c)", and question 
"h", in the other email 
https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01950.html .


----------

The main tagging behaviour as described has a natural limitation.
Invalid accesses that do not cross a 16 byte boundary are not caught, 
since each shadow-memory tag applies to a 16 byte chunk.

To account for this, HWASAN has a "short-tag" functionality.
This functionality was introduced in llvm-svn revision 365551.

Usually, a shadow-memory byte records the *tag* that is valid for access 
to the relevant 16 byte granule in normal memory.
When using short-tags, if an object fills only part of a 16 byte granule 
in normal memory, the corresponding shadow-memory byte stores the 
*length (in bytes) into this granule that is valid*.
The *tag* is then stored in the last byte of the 16 byte granule in 
normal memory.
(We know that last byte is unused, since this is a "short" granule tag).


Now, checking a memory access consists of two parts.
1) A normal tag comparison.
2) A fallback in the tag-mismatch case.
    This fallback checks if the accessing pointer is accessing less bytes
    into the granule than the length given in shadow-memory.
    Then if that's the case it also checks the pointers tag matches the
    last byte in this 16 byte granule.


That is a little difficult to explain clearly in text, so I apologise if 
the above doesn't make sense.


----------

The hwasan error-reporting output lists both memory tags *and* short-tags.
These are the two sections under the titles of "Memory tags ..." and 
"Tags for short granules ...".

The first printed section shows what is stored in shadow-memory.
This is usually the tag, but can be a length if using "short" tags.
The second section contains the "last byte of a granule" for every 
granule whose shadow-memory byte could be a length.

This is why the majority of the "Memory tags ..." section is zero 
(uninitialized).
The majority of the "Tags for short granules ..." section is dots to 
represent that this granule can't be a "short" granule.
Hwasan knows those granules can't be "short" granules since their 
corresponding byte in shadow memory is not a valid length for a 
short-granule interpretation.
Valid lengths are in the range 1 to 15.

It is up to the user to disambiguate the two possibilities in the output.

----------

I have not implemented setting up short-tags for the stack (and do not 
intend to for GCC 10).

This explains your question "h)" -- the testcase you found accesses a 
stack-allocated buffer.
The access is outside the buffer, but not outside the 16 byte granule 
that object is in.  Hence without short-tags this can not be detected by 
hwasan.

It also explains your question "c)" there are no granules that could be 
interpreted as "short" because GCC doesn't yet set any granules as "short".

----------

Note: You will likely see some stack error-reports that do include 
"short" tag information.  This is not because the compiler has generated 
short tag information, but it's because the tags that have been 
generated could be interpreted as valid short tags.
This could cause some rare false-passes, but hwasan is already a 
probabilistic sanitizer.

Note 2: Adding short-tags later is backwards-compatible -- especially 
since I have not added inline tests yet.
The compatibility story for adding short-tags is:
- If you generate short-tags you must have short-tag checking.
- Having short-tag checking without generating short-tags can add
   rare false-passes.

Note 3: short-tags is not a feature in MTE.

> 
>>
>> d) I agree, an address offset annotation on each line of the shadow
>>      memory sounds useful.
> 
> I can come up with an upstream patch as well.
> 
> Thank you,
> Martin
> 
>>
>> Cheers,
>> MM
>>
>>>
>>> Thanks,
>>> Martin
>>>
>>>>
>>>> I'm attaching the entire updated patch series (with the other
>>>> documentation fixes in it too) and the fixed patch for just this 
>>>> part in
>>>> case you just want to compile and test right now.
>>>
>>
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-20 14:33 ` Martin Liška
@ 2019-11-20 17:41   ` Matthew Malcomson
  2019-11-21 13:15     ` Martin Liška
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-20 17:41 UTC (permalink / raw)
  To: Martin Liška, gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 20/11/2019 14:29, Martin Liška wrote:
> On 11/7/19 7:37 PM, Matthew Malcomson wrote:
>> I have rebased this series onto Martin Liska's patches that take the most
>> recent libhwasan from upstream LLVM.
>> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00340.html
>>
>> I've also cleared up some nomenclature (I had previously used the word 
>> 'colour'
>> a few times instead of the word 'tag' and that clashes with other 
>> descriptions)
>> and based the patch series off a more recent GCC revision (r277678).
>>
>> There's an ongoing discussion on whether to have 
>> __SANITIZER_ADDRESS__, or
>> __SANITIZER_HWADDRESS__, but I'm keeping that discussion to the existing
>> thread.
>>
>> Similarly there's still the question around C++ exceptions that I'm 
>> keeping to
>> the existing thread (on the first patch series).
>>
>>
>> NOTE:
>>    Unfortunately, there's a bug in the more recent version of GCC I 
>> rebased
>>    onto.
>>    Hwasan catches this when bootstrapping, which means bootstrapping 
>> with hwasan
>>    fails.
>>    I'm working on tracking the bug down now, but sending this series 
>> upstream
>>    for visibility while that happens.
>>
>>    Bugzilla link:
>>    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92410
>>
>> Entire patch series attached to cover letter.=
>>
> 
> Hello.
> 
> Thank you very much for the patch set, I've just wrote some inline replies
> and I have also some questions/suggestions that I'll summarize here. One 
> caveat,
> I'm not maintainer in any of the ideas and I must confirm that I haven't 
> made
> a deep review of the part which relates to new RTL hooks and stack 
> expansion.
> I expect Jakub can help us here.
> 
> Questions/notes:
> a) For ASAN we do have bunch of parameters:
> 
> gcc --help=param | grep asan
>    asan-stack                  Enable asan stack protection.
>    asan-instrument-allocas     Enable asan allocas/VLAs protection.
>    asan-globals                Enable asan globals protection.
>    asan-instrument-writes      Enable asan store operations protection.
>    asan-instrument-reads       Enable asan load operations protection.
>    asan-memintrin              Enable asan builtin functions protection.
>    asan-use-after-return       Enable asan detection of use-after-return 
> bugs.
>    asan-instrumentation-with-call-threshold Use callbacks instead of 
> inline code if number of accesses in function becomes greater or equal 
> to this number.
> 
> We can probably use some of these in order to drive hwaddress in a 
> similar way. Most of them makes sense for hwaddress as well


I would prefer to keep the options different but would be happy to share 
them if others want.

I figure that there will be some parameters that make sense for hwasan 
but not for asan.
For example hwasan-random-frame-tag doesn't have any analogue for asan.
Re-using `asan-stack` for hwasan would mean we would then need to decide 
between having options with either `hwasan` or `asan` prefix being able 
to affect hwasan, or introducing a parameter `asan-random-frame-tag` 
that has no meaning on asan.


> 
> b) Apparently clang prints also value of all registers. Do we want to do 
> the same?
> 

I believe this only happens on clang for inline checking (try testing 
clang using the parameter `-mllvm --hwasan-instrument-with-calls` to see 
without).

This would be nice to have, but I'm not planning on looking at it any 
time soon.
This is currently implemented in clang on top of two not-yet implemented 
features: Inline instrumentation, and generated checking functions with 
special calling conventions.

It's certainly not something I'm aiming to get into GCC 10.


> 
> c) I'm a bit confused by the pointer tags, where clang does:
> 

Yeah -- I left out a description of short-tags.
Hopefully my explanation in the below email helped.
https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01968.html

> 
> d) Are you planning to come up with inline stack variable 
> tagging/untagging for GCC 10?

To make sure I understand the question correctly:
I think you're asking about writing tags into the shadow memory.

I wasn't planning on this.

What I've posted has all the functionality I'm aiming to get in.
My stretch goal at the moment is to handle exceptions (where I currently 
have a fundamental problem I'm trying to resolve).


> e) In hwasan_increment_tag, shouldn't you skip HWASAN_STACK_BACKGROUND 
> color?

Hopefully answered in 
https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01968.html

> f) I would rename ASAN_MARK in tree dumps to HWASAN_MARK, similarly to 
> what you did
>     for HWASAN_CHECK?

This is an artifact of a shortcut I made (and tried but probably failed 
to explain well in 
https://gcc.gnu.org/ml/gcc-patches/2019-09/msg00392.html).

For HWASAN_CHECK I introduced a new internal function that takes the 
same arguments as ASAN_CHECK, hence this new function is printed 
differently.


For HWASAN_MARK, I used a trick to avoid adding "almost duplicate" code 
everywhere in the mid-end that checked for ASAN_MARK.
Then, in the sanopt pass (i.e. later on in compilation) I turn that 
ASAN_MARK internal function into a HWASAN_MARK internal function.
This happens after all passes that check for ASAN_MARK so it should be a 
problem .


Otherwise, everywhere that a function handles the case of coming across 
ASAN_MARK, I would need to account for HWASAN_MARK by doing essentially 
the same thing and I thought that would be a bit of a pain.

I would be happy to use the more explicit way of having two seperate 
functions -- not certain which I'd prefer myself.


> g) I noticed quite some GNU coding style violations: 'if (! my_cond)', 
> should be 'if (!my_cond)',
>     but it's a nit.

Will take a look.

> h) I maybe found a bug for:

Hopefully answered in 
https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01968.html

> i) As mentioned, we would appreciate a comment before each newly added 
> function :)

Will do ;-)

> 
> As said, thanks for working on that.
> Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN
  2019-11-20 15:02           ` Matthew Malcomson
@ 2019-11-20 18:13             ` Joseph Myers
  0 siblings, 0 replies; 150+ messages in thread
From: Joseph Myers @ 2019-11-20 18:13 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: Martin Liška, gcc-patches, nd, kcc, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On Wed, 20 Nov 2019, Matthew Malcomson wrote:

> I don't have much of a plan.
> 
> The most promising lead I have is that libiberty/alloca.c has a similar 
> functionality but with macros to account for a special case.

The comment in libiberty/aclocal.m4 is:

# We always want a C version of alloca() compiled into libiberty,
# because native-compiler support for the real alloca is so !@#$%
# unreliable that GCC has decided to use it only when being compiled
# by GCC.  This is the part of AC_FUNC_ALLOCA that calculates the
# information alloca.c needs.

This is the sort of thing that was relevant when GCC was built on lots of 
proprietary Unix systems with their system C compilers.  Most of those 
proprietary Unix systems are long obsolete and are no longer supported by 
GCC.  On the limited remaining set of host systems supported by GCC, there 
are a limited number of C++ compilers used to build most of the host code 
in GCC that is C++, and presumably a limited number of accompanying C 
compilers used to build libiberty.  Now, libiberty is used by binutils 
more of which is written in C, but I doubt that expands the range of 
relevant host C compilers; the set of host OSes used nowadays is simply 
much smaller than it was when this code was written.

So I'd suggest either completely eliminating C alloca from libiberty, or 
at least not building it at all when building with a compiler that defines 
__GNUC__.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-11-20 15:46               ` Matthew Malcomson
@ 2019-11-21 13:10                 ` Martin Liška
  0 siblings, 0 replies; 150+ messages in thread
From: Martin Liška @ 2019-11-21 13:10 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 11/20/19 4:45 PM, Matthew Malcomson wrote:
> On 20/11/2019 14:33, Martin Liška wrote:
>> On 11/13/19 4:24 PM, Matthew Malcomson wrote:
>>> On 12/11/2019 12:08, Martin Liška wrote:
>>>> On 11/11/19 5:03 PM, Matthew Malcomson wrote:
>>>>> Ah!
>>>>> My apologies -- I sent up a series with a few documentation mistakes.
>>
>>>
>>> b) Marking 'ptr' and 'mem' in the dump sounds like a good idea to me.
>>>       Exactly how I'm not sure -- maybe with a colourscheme?  Do you
>>> have a
>>>       marking in mind?
>>
>> Libsanitizer is capable of using colors for report printing.
>> I can help with that and come up with a patch for upstream.
>>
>>>
>>>       Uninitialised shadow space has the zero tag, however, there are a
>>> few
>>>       extra details that help understanding these traces:
>>>
>>>       On the stack, zero is both uninitialized and "the background" (i.e.
>>>       the tag for anything not specially instrumented, like register
>>> spills
>>>       and parameters passed on the stack).
>>>       However, accessible tagged objects can be given a zero tag.
>>
>> Question here would be if we should use non-zero tags here? Maybe related
>> to my comment about skipping of HWASAN_STACK_BACKGROUND tag?
> 
> Unfortunately we can't skip non-zero tags at compile time when using a
> random frame tag.  This is because we don't know at compile time what
> the random frame tag will be.
> 
> On each entry to a frame a "base tag" is generated randomly at runtime.
> Each local object in the frame has a compile-time offset that's what
> gets calculated in `hwasan_increment_tag` -- the offset from this random
> tag.
> The tag assigned to a local object is the runtime random frame tag plus
> the compile-time constant offset.

Ah, ok, I see.

> 
> 
> I could avoid HWASAN_STACK_BACKGROUND as a tag when the parameter
> `hwasan-random-frame-tag` is false, since then there is no runtime
> random base tag (instead I start with zero).

Yes, I would recommend that approach.

> 
> I'll be happy to add that in if you'd like -- I decided against it since
> it would only matter when a function has 256 or more variables, but I
> flip-flopped on the decision a few times.
> 
>>
>>>       We allow this to avoid runtime checks when incrementing the random
>>>       frame tag to get the tag for a new local variable.
>>>       We can easily avoid the zero tag at compile-time if we don't use a
>>>       random tag for each frame.  I had this in development at one point
>>>       and found it quite useful for verification.  I already have an
>>> option
>>>       to disable random tags for each frame that this ability could go
>>>       under.
>>>       I don't believe (but am not 100% certain) this option is in LLVM.
>>>
>>>       On the heap uninitialised is tag zero, but memory that has been
>>>       `free`d is given a random tag, so non-zero in a dump does not mean a
>>>       valid object.
>>>
>>> c) Is there an alternate notation you have in mind?
>>>       I would guess the "dots" are there to say "this granule has no
>>>       short-tag information", and I'm not sure what would be a better
>>>       way to demonstrate that.
>>
>> Now I've got it here. Dot means that top-byte of a pointer equals to zero.
>> Right?
> 
> 
> Ah!
> I think I never described the "short-tag" functionality, and the fact
> it's in the debug output is getting confusing.
> 
> This will also be part of answering your question "c)", and question
> "h", in the other email
> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01950.html .
> 
> 
> ----------
> 
> The main tagging behaviour as described has a natural limitation.
> Invalid accesses that do not cross a 16 byte boundary are not caught,
> since each shadow-memory tag applies to a 16 byte chunk.
> 
> To account for this, HWASAN has a "short-tag" functionality.
> This functionality was introduced in llvm-svn revision 365551.
> 
> Usually, a shadow-memory byte records the *tag* that is valid for access
> to the relevant 16 byte granule in normal memory.
> When using short-tags, if an object fills only part of a 16 byte granule
> in normal memory, the corresponding shadow-memory byte stores the
> *length (in bytes) into this granule that is valid*.
> The *tag* is then stored in the last byte of the 16 byte granule in
> normal memory.
> (We know that last byte is unused, since this is a "short" granule tag).
> 
> 
> Now, checking a memory access consists of two parts.
> 1) A normal tag comparison.
> 2) A fallback in the tag-mismatch case.
>      This fallback checks if the accessing pointer is accessing less bytes
>      into the granule than the length given in shadow-memory.
>      Then if that's the case it also checks the pointers tag matches the
>      last byte in this 16 byte granule.
> 
> 
> That is a little difficult to explain clearly in text, so I apologise if
> the above doesn't make sense.

I've got the technique, it's quite nice trick I would say. However, it makes
the sanitizer even more complicated.

> 
> 
> ----------
> 
> The hwasan error-reporting output lists both memory tags *and* short-tags.
> These are the two sections under the titles of "Memory tags ..." and
> "Tags for short granules ...".
> 
> The first printed section shows what is stored in shadow-memory.
> This is usually the tag, but can be a length if using "short" tags.
> The second section contains the "last byte of a granule" for every
> granule whose shadow-memory byte could be a length.
> 
> This is why the majority of the "Memory tags ..." section is zero
> (uninitialized).
> The majority of the "Tags for short granules ..." section is dots to
> represent that this granule can't be a "short" granule.
> Hwasan knows those granules can't be "short" granules since their
> corresponding byte in shadow memory is not a valid length for a
> short-granule interpretation.
> Valid lengths are in the range 1 to 15.
> 
> It is up to the user to disambiguate the two possibilities in the output.

I've got it.

> 
> ----------
> 
> I have not implemented setting up short-tags for the stack (and do not
> intend to for GCC 10).

Which makes perfectly sense, it's an initial implementation.

> 
> This explains your question "h)" -- the testcase you found accesses a
> stack-allocated buffer.
> The access is outside the buffer, but not outside the 16 byte granule
> that object is in.  Hence without short-tags this can not be detected by
> hwasan.
> 
> It also explains your question "c)" there are no granules that could be
> interpreted as "short" because GCC doesn't yet set any granules as "short".
> 
> ----------
> 
> Note: You will likely see some stack error-reports that do include
> "short" tag information.  This is not because the compiler has generated
> short tag information, but it's because the tags that have been
> generated could be interpreted as valid short tags.
> This could cause some rare false-passes, but hwasan is already a
> probabilistic sanitizer.
> 
> Note 2: Adding short-tags later is backwards-compatible -- especially
> since I have not added inline tests yet.
> The compatibility story for adding short-tags is:
> - If you generate short-tags you must have short-tag checking.
> - Having short-tag checking without generating short-tags can add
>     rare false-passes.

Martin

> 
> Note 3: short-tags is not a feature in MTE.
> 
>>
>>>
>>> d) I agree, an address offset annotation on each line of the shadow
>>>       memory sounds useful.
>>
>> I can come up with an upstream patch as well.
>>
>> Thank you,
>> Martin
>>
>>>
>>> Cheers,
>>> MM
>>>
>>>>
>>>> Thanks,
>>>> Martin
>>>>
>>>>>
>>>>> I'm attaching the entire updated patch series (with the other
>>>>> documentation fixes in it too) and the fixed patch for just this
>>>>> part in
>>>>> case you just want to compile and test right now.
>>>>
>>>
>>
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-20 17:41   ` Matthew Malcomson
@ 2019-11-21 13:15     ` Martin Liška
  2019-11-21 15:03       ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2019-11-21 13:15 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 11/20/19 6:40 PM, Matthew Malcomson wrote:
> On 20/11/2019 14:29, Martin Liška wrote:
>> On 11/7/19 7:37 PM, Matthew Malcomson wrote:
>>> I have rebased this series onto Martin Liska's patches that take the most
>>> recent libhwasan from upstream LLVM.
>>> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00340.html
>>>
>>> I've also cleared up some nomenclature (I had previously used the word
>>> 'colour'
>>> a few times instead of the word 'tag' and that clashes with other
>>> descriptions)
>>> and based the patch series off a more recent GCC revision (r277678).
>>>
>>> There's an ongoing discussion on whether to have
>>> __SANITIZER_ADDRESS__, or
>>> __SANITIZER_HWADDRESS__, but I'm keeping that discussion to the existing
>>> thread.
>>>
>>> Similarly there's still the question around C++ exceptions that I'm
>>> keeping to
>>> the existing thread (on the first patch series).
>>>
>>>
>>> NOTE:
>>>     Unfortunately, there's a bug in the more recent version of GCC I
>>> rebased
>>>     onto.
>>>     Hwasan catches this when bootstrapping, which means bootstrapping
>>> with hwasan
>>>     fails.
>>>     I'm working on tracking the bug down now, but sending this series
>>> upstream
>>>     for visibility while that happens.
>>>
>>>     Bugzilla link:
>>>     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92410
>>>
>>> Entire patch series attached to cover letter.=
>>>
>>
>> Hello.
>>
>> Thank you very much for the patch set, I've just wrote some inline replies
>> and I have also some questions/suggestions that I'll summarize here. One
>> caveat,
>> I'm not maintainer in any of the ideas and I must confirm that I haven't
>> made
>> a deep review of the part which relates to new RTL hooks and stack
>> expansion.
>> I expect Jakub can help us here.
>>
>> Questions/notes:
>> a) For ASAN we do have bunch of parameters:
>>
>> gcc --help=param | grep asan
>>     asan-stack                  Enable asan stack protection.
>>     asan-instrument-allocas     Enable asan allocas/VLAs protection.
>>     asan-globals                Enable asan globals protection.
>>     asan-instrument-writes      Enable asan store operations protection.
>>     asan-instrument-reads       Enable asan load operations protection.
>>     asan-memintrin              Enable asan builtin functions protection.
>>     asan-use-after-return       Enable asan detection of use-after-return
>> bugs.
>>     asan-instrumentation-with-call-threshold Use callbacks instead of
>> inline code if number of accesses in function becomes greater or equal
>> to this number.
>>
>> We can probably use some of these in order to drive hwaddress in a
>> similar way. Most of them makes sense for hwaddress as well
> 
> 
> I would prefer to keep the options different but would be happy to share
> them if others want.

Works for me.

> 
> I figure that there will be some parameters that make sense for hwasan
> but not for asan.
> For example hwasan-random-frame-tag doesn't have any analogue for asan.
> Re-using `asan-stack` for hwasan would mean we would then need to decide
> between having options with either `hwasan` or `asan` prefix being able
> to affect hwasan, or introducing a parameter `asan-random-frame-tag`
> that has no meaning on asan.

Then, I would come up with additional hwasan-* parameters that can have
the same semantic as the for asan (if it makes sense).

> 
> 
>>
>> b) Apparently clang prints also value of all registers. Do we want to do
>> the same?
>>
> 
> I believe this only happens on clang for inline checking (try testing
> clang using the parameter `-mllvm --hwasan-instrument-with-calls` to see
> without).

Are we talking about usage of __hwasan_check_x0_18?

> 
> This would be nice to have, but I'm not planning on looking at it any
> time soon.
> This is currently implemented in clang on top of two not-yet implemented
> features: Inline instrumentation, and generated checking functions with
> special calling conventions.
> 
> It's certainly not something I'm aiming to get into GCC 10.

Which is fine.

> 
> 
>>
>> c) I'm a bit confused by the pointer tags, where clang does:
>>
> 
> Yeah -- I left out a description of short-tags.
> Hopefully my explanation in the below email helped.
> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01968.html
> 
>>
>> d) Are you planning to come up with inline stack variable
>> tagging/untagging for GCC 10?
> 
> To make sure I understand the question correctly:
> I think you're asking about writing tags into the shadow memory.

Yes, which will be very similar to current ASAN code in asan_emit_stack_protection.
I would expect a significant speed up from the inline shadow memory tag emission?

> 
> I wasn't planning on this.
> 
> What I've posted has all the functionality I'm aiming to get in.
> My stretch goal at the moment is to handle exceptions (where I currently
> have a fundamental problem I'm trying to resolve).
> 
> 
>> e) In hwasan_increment_tag, shouldn't you skip HWASAN_STACK_BACKGROUND
>> color?
> 
> Hopefully answered in
> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01968.html

Yes.

> 
>> f) I would rename ASAN_MARK in tree dumps to HWASAN_MARK, similarly to
>> what you did
>>      for HWASAN_CHECK?
> 
> This is an artifact of a shortcut I made (and tried but probably failed
> to explain well in
> https://gcc.gnu.org/ml/gcc-patches/2019-09/msg00392.html).
> 
> For HWASAN_CHECK I introduced a new internal function that takes the
> same arguments as ASAN_CHECK, hence this new function is printed
> differently.
> 
> 
> For HWASAN_MARK, I used a trick to avoid adding "almost duplicate" code
> everywhere in the mid-end that checked for ASAN_MARK.
> Then, in the sanopt pass (i.e. later on in compilation) I turn that
> ASAN_MARK internal function into a HWASAN_MARK internal function.
> This happens after all passes that check for ASAN_MARK so it should be a
> problem .

I know that you are using ASAN_MARK in -fsanitize=hwaddress. What I said
is maybe we want to print in dump files {HW,.}ASAN_MARK based on what
sanitizer is used (as one can't utilize both at the same time). But it's
a nit.

> 
> 
> Otherwise, everywhere that a function handles the case of coming across
> ASAN_MARK, I would need to account for HWASAN_MARK by doing essentially
> the same thing and I thought that would be a bit of a pain.
> 
> I would be happy to use the more explicit way of having two seperate
> functions -- not certain which I'd prefer myself.
> 
> 
>> g) I noticed quite some GNU coding style violations: 'if (! my_cond)',
>> should be 'if (!my_cond)',
>>      but it's a nit.
> 
> Will take a look.
> 
>> h) I maybe found a bug for:
> 
> Hopefully answered in
> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01968.html
> 
>> i) As mentioned, we would appreciate a comment before each newly added
>> function :)

Thanks,
Martin

> 
> Will do ;-)
> 
>>
>> As said, thanks for working on that.
>> Martin
> 

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-21 13:15     ` Martin Liška
@ 2019-11-21 15:03       ` Matthew Malcomson
  2019-11-25 12:23         ` Martin Liška
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-11-21 15:03 UTC (permalink / raw)
  To: Martin Liška, gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 21/11/2019 13:10, Martin Liška wrote:
> On 11/20/19 6:40 PM, Matthew Malcomson wrote:
>> On 20/11/2019 14:29, Martin Liška wrote:
>>> On 11/7/19 7:37 PM, Matthew Malcomson wrote:
>>>> I have rebased this series onto Martin Liska's patches that take the 
>>>> most
>>>> recent libhwasan from upstream LLVM.
>>>> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00340.html
>>> a) For ASAN we do have bunch of parameters:
>>>
>>> gcc --help=param | grep asan
>>>     asan-stack                  Enable asan stack protection.
>>>     asan-instrument-allocas     Enable asan allocas/VLAs protection.
>>>     asan-globals                Enable asan globals protection.
>>>     asan-instrument-writes      Enable asan store operations protection.
>>>     asan-instrument-reads       Enable asan load operations protection.
>>>     asan-memintrin              Enable asan builtin functions 
>>> protection.
>>>     asan-use-after-return       Enable asan detection of 
>>> use-after-return
>>> bugs.
>>>     asan-instrumentation-with-call-threshold Use callbacks instead of
>>> inline code if number of accesses in function becomes greater or equal
>>> to this number.
>>>
>>> We can probably use some of these in order to drive hwaddress in a
>>> similar way. Most of them makes sense for hwaddress as well
>>
>>
>> I would prefer to keep the options different but would be happy to share
>> them if others want.
> 
> Works for me.
> 
>>
>> I figure that there will be some parameters that make sense for hwasan
>> but not for asan.
>> For example hwasan-random-frame-tag doesn't have any analogue for asan.
>> Re-using `asan-stack` for hwasan would mean we would then need to decide
>> between having options with either `hwasan` or `asan` prefix being able
>> to affect hwasan, or introducing a parameter `asan-random-frame-tag`
>> that has no meaning on asan.
> 
> Then, I would come up with additional hwasan-* parameters that can have
> the same semantic as the for asan (if it makes sense).
> 

Do you mean that I should add the options and the ability to toggle 
these features?

The options that could eventually make sense for hwasan and that I 
haven't implemented are:
   hwasan-instrument-allocas
   hwasan-instrument-writes
   hwasan-instrument-reads
   hwasan-memintrin

hwasan-globals could eventually be done, but that's certainly not 
something I could implement quickly.

>>
>>
>>>
>>> b) Apparently clang prints also value of all registers. Do we want to do
>>> the same?
>>>
>>
>> I believe this only happens on clang for inline checking (try testing
>> clang using the parameter `-mllvm --hwasan-instrument-with-calls` to see
>> without).
> 
> Are we talking about usage of __hwasan_check_x0_18?
> 

Using `-mllvm --hwasan-instrument-with-calls` uses function calls like 
`__hwasan_store4` (similar to the asan instrumentation with calls.


The __hwasan_check_x* functions are the "generated checking functions 
with special calling conventions" I mentioned.

The special checking functions are generated to accept a calling 
convention generated based on what would be optimal for where a check is 
needed.  This custom calling convention can then also be used to keep 
track of what register values were around in the calling frame.

This was introduced in clang at revision:   llvm-svn: 351920

https://reviews.llvm.org/D56954

>>
>> This would be nice to have, but I'm not planning on looking at it any
>> time soon.
>> This is currently implemented in clang on top of two not-yet implemented
>> features: Inline instrumentation, and generated checking functions with
>> special calling conventions.
>>
>> It's certainly not something I'm aiming to get into GCC 10.
> 
> Which is fine.
> 
>>
>>
>>>
>>> c) I'm a bit confused by the pointer tags, where clang does:
>>>
>>
>> Yeah -- I left out a description of short-tags.
>> Hopefully my explanation in the below email helped.
>> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01968.html
>>
>>>
>>> d) Are you planning to come up with inline stack variable
>>> tagging/untagging for GCC 10?
>>
>> To make sure I understand the question correctly:
>> I think you're asking about writing tags into the shadow memory.
> 
> Yes, which will be very similar to current ASAN code in 
> asan_emit_stack_protection.
> I would expect a significant speed up from the inline shadow memory tag 
> emission?

Yes, I would also expect a significant speed up.

Honestly, I doubt I could get this done properly by the end of stage 3.
I'd be happy to give it a shot, but would not like to commit to it.

> 
>>
>>
>>> f) I would rename ASAN_MARK in tree dumps to HWASAN_MARK, similarly to
>>> what you did
>>>      for HWASAN_CHECK?
>>
>> This is an artifact of a shortcut I made (and tried but probably failed
>> to explain well in
>> https://gcc.gnu.org/ml/gcc-patches/2019-09/msg00392.html).
>>
>> For HWASAN_CHECK I introduced a new internal function that takes the
>> same arguments as ASAN_CHECK, hence this new function is printed
>> differently.
>>
>>
>> For HWASAN_MARK, I used a trick to avoid adding "almost duplicate" code
>> everywhere in the mid-end that checked for ASAN_MARK.
>> Then, in the sanopt pass (i.e. later on in compilation) I turn that
>> ASAN_MARK internal function into a HWASAN_MARK internal function.
>> This happens after all passes that check for ASAN_MARK so it should be a
>> problem .
> 
> I know that you are using ASAN_MARK in -fsanitize=hwaddress. What I said
> is maybe we want to print in dump files {HW,.}ASAN_MARK based on what
> sanitizer is used (as one can't utilize both at the same time). But it's
> a nit.
> 

Ah, I can certainly look into it.

>>
>>

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC
  2019-11-21 15:03       ` Matthew Malcomson
@ 2019-11-25 12:23         ` Martin Liška
  0 siblings, 0 replies; 150+ messages in thread
From: Martin Liška @ 2019-11-25 12:23 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, rguenther, James Greenhalgh, kcc, law, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub

On 11/21/19 3:59 PM, Matthew Malcomson wrote:
> On 21/11/2019 13:10, Martin Liška wrote:
>> On 11/20/19 6:40 PM, Matthew Malcomson wrote:
>>> On 20/11/2019 14:29, Martin Liška wrote:
>>>> On 11/7/19 7:37 PM, Matthew Malcomson wrote:
>>>>> I have rebased this series onto Martin Liska's patches that take the
>>>>> most
>>>>> recent libhwasan from upstream LLVM.
>>>>> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00340.html
>>>> a) For ASAN we do have bunch of parameters:
>>>>
>>>> gcc --help=param | grep asan
>>>>      asan-stack                  Enable asan stack protection.
>>>>      asan-instrument-allocas     Enable asan allocas/VLAs protection.
>>>>      asan-globals                Enable asan globals protection.
>>>>      asan-instrument-writes      Enable asan store operations protection.
>>>>      asan-instrument-reads       Enable asan load operations protection.
>>>>      asan-memintrin              Enable asan builtin functions
>>>> protection.
>>>>      asan-use-after-return       Enable asan detection of
>>>> use-after-return
>>>> bugs.
>>>>      asan-instrumentation-with-call-threshold Use callbacks instead of
>>>> inline code if number of accesses in function becomes greater or equal
>>>> to this number.
>>>>
>>>> We can probably use some of these in order to drive hwaddress in a
>>>> similar way. Most of them makes sense for hwaddress as well
>>>
>>>
>>> I would prefer to keep the options different but would be happy to share
>>> them if others want.
>>
>> Works for me.
>>
>>>
>>> I figure that there will be some parameters that make sense for hwasan
>>> but not for asan.
>>> For example hwasan-random-frame-tag doesn't have any analogue for asan.
>>> Re-using `asan-stack` for hwasan would mean we would then need to decide
>>> between having options with either `hwasan` or `asan` prefix being able
>>> to affect hwasan, or introducing a parameter `asan-random-frame-tag`
>>> that has no meaning on asan.
>>
>> Then, I would come up with additional hwasan-* parameters that can have
>> the same semantic as the for asan (if it makes sense).
>>
> 

Hello.

> Do you mean that I should add the options and the ability to toggle
> these features?

Yes please, but it can be certainly added incrementally.

> 
> The options that could eventually make sense for hwasan and that I
> haven't implemented are:
>     hwasan-instrument-allocas
>     hwasan-instrument-writes
>     hwasan-instrument-reads
>     hwasan-memintrin

Works for me.

> 
> hwasan-globals could eventually be done, but that's certainly not
> something I could implement quickly.
> 
>>>
>>>
>>>>
>>>> b) Apparently clang prints also value of all registers. Do we want to do
>>>> the same?
>>>>
>>>
>>> I believe this only happens on clang for inline checking (try testing
>>> clang using the parameter `-mllvm --hwasan-instrument-with-calls` to see
>>> without).
>>
>> Are we talking about usage of __hwasan_check_x0_18?
>>
> 
> Using `-mllvm --hwasan-instrument-with-calls` uses function calls like
> `__hwasan_store4` (similar to the asan instrumentation with calls.
> 
> 
> The __hwasan_check_x* functions are the "generated checking functions
> with special calling conventions" I mentioned.
> 
> The special checking functions are generated to accept a calling
> convention generated based on what would be optimal for where a check is
> needed.  This custom calling convention can then also be used to keep
> track of what register values were around in the calling frame.
> 
> This was introduced in clang at revision:   llvm-svn: 351920

Ah ok, I see. Apparently it saves a lot of code size and speed as well.
That's also a possible improvement.

> 
> https://reviews.llvm.org/D56954
> 
>>>
>>> This would be nice to have, but I'm not planning on looking at it any
>>> time soon.
>>> This is currently implemented in clang on top of two not-yet implemented
>>> features: Inline instrumentation, and generated checking functions with
>>> special calling conventions.
>>>
>>> It's certainly not something I'm aiming to get into GCC 10.
>>
>> Which is fine.
>>
>>>
>>>
>>>>
>>>> c) I'm a bit confused by the pointer tags, where clang does:
>>>>
>>>
>>> Yeah -- I left out a description of short-tags.
>>> Hopefully my explanation in the below email helped.
>>> https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01968.html
>>>
>>>>
>>>> d) Are you planning to come up with inline stack variable
>>>> tagging/untagging for GCC 10?
>>>
>>> To make sure I understand the question correctly:
>>> I think you're asking about writing tags into the shadow memory.
>>
>> Yes, which will be very similar to current ASAN code in
>> asan_emit_stack_protection.
>> I would expect a significant speed up from the inline shadow memory tag
>> emission?
> 
> Yes, I would also expect a significant speed up.
> 
> Honestly, I doubt I could get this done properly by the end of stage 3.
> I'd be happy to give it a shot, but would not like to commit to it.

Sure, we should start first with a working implementation and speed up
patches can be added later.

> 
>>
>>>
>>>
>>>> f) I would rename ASAN_MARK in tree dumps to HWASAN_MARK, similarly to
>>>> what you did
>>>>       for HWASAN_CHECK?
>>>
>>> This is an artifact of a shortcut I made (and tried but probably failed
>>> to explain well in
>>> https://gcc.gnu.org/ml/gcc-patches/2019-09/msg00392.html).
>>>
>>> For HWASAN_CHECK I introduced a new internal function that takes the
>>> same arguments as ASAN_CHECK, hence this new function is printed
>>> differently.
>>>
>>>
>>> For HWASAN_MARK, I used a trick to avoid adding "almost duplicate" code
>>> everywhere in the mid-end that checked for ASAN_MARK.
>>> Then, in the sanopt pass (i.e. later on in compilation) I turn that
>>> ASAN_MARK internal function into a HWASAN_MARK internal function.
>>> This happens after all passes that check for ASAN_MARK so it should be a
>>> problem .
>>
>> I know that you are using ASAN_MARK in -fsanitize=hwaddress. What I said
>> is maybe we want to print in dump files {HW,.}ASAN_MARK based on what
>> sanitizer is used (as one can't utilize both at the same time). But it's
>> a nit.
>>
> 
> Ah, I can certainly look into it.

It should be quite simple to fix that.

Thanks,
Martin

> 
>>>
>>>

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-11-20 14:38             ` Martin Liška
  2019-11-20 15:46               ` Matthew Malcomson
@ 2019-11-26 10:15               ` Martin Liška
  1 sibling, 0 replies; 150+ messages in thread
From: Martin Liška @ 2019-11-26 10:15 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 11/20/19 3:33 PM, Martin Liška wrote:
> I can come up with an upstream patch as well.

Hello.

I've just made an upstream review request for that:
https://reviews.llvm.org/D70707

Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 2/X] [libsanitizer] Only build libhwasan when targeting AArch64
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (3 preceding siblings ...)
  2019-12-12 15:19     ` [PATCH 7/X] [libsanitizer] Add tests Matthew Malcomson
@ 2019-12-12 15:19     ` Matthew Malcomson
  2019-12-12 15:19     ` [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN Matthew Malcomson
                       ` (13 subsequent siblings)
  18 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-12-12 15:19 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 9744 bytes --]

Though the library has limited support for x86, we don't have any
support for generating code targeting x86 so there is no point building
for that target.

libsanitizer/ChangeLog:

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

	* Makefile.am: Condition building hwasan directory.
	* Makefile.in: Regenerate.
	* configure: Regenerate.
	* configure.ac: Set HWASAN_SUPPORTED based on target
	architecture.
	* configure.tgt: Likewise.



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


diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 2a7e8e1debe838719db0f0fad218b2543cc3111b..065a65e78d49f7689a01ecb64db1f07ca83aa987 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,7 +14,7 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan hwasan
+SUBDIRS += lsan asan ubsan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
@@ -23,6 +23,9 @@ nodist_saninclude_HEADERS += \
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
+if HWASAN_SUPPORTED
+SUBDIRS += hwasan
+endif
 endif
 
 ## May be used by toolexeclibdir.
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 36aa952af7e04bc0e4fb94cdcd584d539193d781..75a99491cb1d4422fd5e2d93cae93eb883ae0963 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -97,6 +97,7 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
+@HWASAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_5 = hwasan
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
@@ -207,7 +208,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan hwasan tsan
+	ubsan tsan hwasan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -363,7 +364,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	$(am__append_4) $(am__append_5)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/configure b/libsanitizer/configure
index ff72af73e6f77aaf93bf39e6799f896851a377dd..4e95194fe3567b1227c4036c2f5bf6540f735975 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -659,6 +659,8 @@ link_libubsan
 link_libtsan
 link_libhwasan
 link_libasan
+HWASAN_SUPPORTED_FALSE
+HWASAN_SUPPORTED_TRUE
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
 TSAN_SUPPORTED_FALSE
@@ -12335,7 +12337,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12338 "configure"
+#line 12340 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12441,7 +12443,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12444 "configure"
+#line 12446 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15792,6 +15794,7 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
  if test "x$TSAN_SUPPORTED" = "xyes"; then
   TSAN_SUPPORTED_TRUE=
@@ -15809,6 +15812,14 @@ else
   LSAN_SUPPORTED_FALSE=
 fi
 
+ if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  HWASAN_SUPPORTED_TRUE=
+  HWASAN_SUPPORTED_FALSE='#'
+else
+  HWASAN_SUPPORTED_TRUE='#'
+  HWASAN_SUPPORTED_FALSE=
+fi
+
 
 # Check for functions needed.
 for ac_func in clock_getres clock_gettime clock_settime lstat readlink
@@ -16791,7 +16802,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -16799,6 +16810,11 @@ if test "x$TSAN_SUPPORTED" = "xyes"; then
 
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  ac_config_files="$ac_config_files hwasan/Makefile"
+
+fi
+
 
 
 
@@ -17059,6 +17075,10 @@ if test -z "${LSAN_SUPPORTED_TRUE}" && test -z "${LSAN_SUPPORTED_FALSE}"; then
   as_fn_error $? "conditional \"LSAN_SUPPORTED\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HWASAN_SUPPORTED_TRUE}" && test -z "${HWASAN_SUPPORTED_FALSE}"; then
+  as_fn_error $? "conditional \"HWASAN_SUPPORTED\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${USING_MAC_INTERPOSE_TRUE}" && test -z "${USING_MAC_INTERPOSE_FALSE}"; then
   as_fn_error $? "conditional \"USING_MAC_INTERPOSE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -18068,9 +18088,9 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
-    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
@@ -20004,7 +20024,7 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
    sed -f vpsed$$ $ac_file > tmp$$
@@ -20015,18 +20035,18 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
-   sed -f vpsed$$ $ac_file > tmp$$
-   mv tmp$$ $ac_file
-   rm vpsed$$
-   echo 'MULTISUBDIR =' >> $ac_file
-   ml_norecursion=yes
-   . ${multi_basedir}/config-ml.in
-   { ml_norecursion=; unset ml_norecursion;}
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
     sed -f vpsed$$ $ac_file > tmp$$
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index 02c0c235171e4c8249821c5db91414f8e4bfb3ee..ba56e911ad59fc4f3aa88fa01ea4201b9549cd44 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -87,9 +87,11 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
 AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
 AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"])
+AM_CONDITIONAL(HWASAN_SUPPORTED, [test "x$HWASAN_SUPPORTED" = "xyes"])
 
 # Check for functions needed.
 AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime lstat readlink)
@@ -343,7 +345,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
@@ -371,6 +373,21 @@ _EOF
 ])
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  AC_CONFIG_FILES(AC_FOREACH([DIR], [hwasan], [DIR/Makefile ]), 
+    [cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    AS_UNSET([ml_norecursion])
+])
+fi
+
 AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS])
 AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS])
 
diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt
index 714f2923605011329b54be7e9fcf7af44a0593fd..92f4e9ff507b2972d1993ee288ea9ccef0103ec3 100644
--- a/libsanitizer/configure.tgt
+++ b/libsanitizer/configure.tgt
@@ -59,6 +59,7 @@ case "${target}" in
 		LSAN_SUPPORTED=yes
 		TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
 	fi
+	HWASAN_SUPPORTED=yes
 	;;
   x86_64-*-darwin[1]* | i?86-*-darwin[1]*)
 	TSAN_SUPPORTED=no


[-- Attachment #2: hwasan-patch1.patch --]
[-- Type: text/plain, Size: 8946 bytes --]

diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 2a7e8e1debe838719db0f0fad218b2543cc3111b..065a65e78d49f7689a01ecb64db1f07ca83aa987 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,7 +14,7 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan hwasan
+SUBDIRS += lsan asan ubsan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
@@ -23,6 +23,9 @@ nodist_saninclude_HEADERS += \
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
+if HWASAN_SUPPORTED
+SUBDIRS += hwasan
+endif
 endif
 
 ## May be used by toolexeclibdir.
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 36aa952af7e04bc0e4fb94cdcd584d539193d781..75a99491cb1d4422fd5e2d93cae93eb883ae0963 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -97,6 +97,7 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
+@HWASAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_5 = hwasan
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
@@ -207,7 +208,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan hwasan tsan
+	ubsan tsan hwasan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -363,7 +364,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	$(am__append_4) $(am__append_5)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/configure b/libsanitizer/configure
index ff72af73e6f77aaf93bf39e6799f896851a377dd..4e95194fe3567b1227c4036c2f5bf6540f735975 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -659,6 +659,8 @@ link_libubsan
 link_libtsan
 link_libhwasan
 link_libasan
+HWASAN_SUPPORTED_FALSE
+HWASAN_SUPPORTED_TRUE
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
 TSAN_SUPPORTED_FALSE
@@ -12335,7 +12337,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12338 "configure"
+#line 12340 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12441,7 +12443,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12444 "configure"
+#line 12446 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15792,6 +15794,7 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
  if test "x$TSAN_SUPPORTED" = "xyes"; then
   TSAN_SUPPORTED_TRUE=
@@ -15809,6 +15812,14 @@ else
   LSAN_SUPPORTED_FALSE=
 fi
 
+ if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  HWASAN_SUPPORTED_TRUE=
+  HWASAN_SUPPORTED_FALSE='#'
+else
+  HWASAN_SUPPORTED_TRUE='#'
+  HWASAN_SUPPORTED_FALSE=
+fi
+
 
 # Check for functions needed.
 for ac_func in clock_getres clock_gettime clock_settime lstat readlink
@@ -16791,7 +16802,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -16799,6 +16810,11 @@ if test "x$TSAN_SUPPORTED" = "xyes"; then
 
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  ac_config_files="$ac_config_files hwasan/Makefile"
+
+fi
+
 
 
 
@@ -17059,6 +17075,10 @@ if test -z "${LSAN_SUPPORTED_TRUE}" && test -z "${LSAN_SUPPORTED_FALSE}"; then
   as_fn_error $? "conditional \"LSAN_SUPPORTED\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HWASAN_SUPPORTED_TRUE}" && test -z "${HWASAN_SUPPORTED_FALSE}"; then
+  as_fn_error $? "conditional \"HWASAN_SUPPORTED\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${USING_MAC_INTERPOSE_TRUE}" && test -z "${USING_MAC_INTERPOSE_FALSE}"; then
   as_fn_error $? "conditional \"USING_MAC_INTERPOSE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -18068,9 +18088,9 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
-    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
@@ -20004,7 +20024,7 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
    sed -f vpsed$$ $ac_file > tmp$$
@@ -20015,18 +20035,18 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
-   sed -f vpsed$$ $ac_file > tmp$$
-   mv tmp$$ $ac_file
-   rm vpsed$$
-   echo 'MULTISUBDIR =' >> $ac_file
-   ml_norecursion=yes
-   . ${multi_basedir}/config-ml.in
-   { ml_norecursion=; unset ml_norecursion;}
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
     sed -f vpsed$$ $ac_file > tmp$$
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index 02c0c235171e4c8249821c5db91414f8e4bfb3ee..ba56e911ad59fc4f3aa88fa01ea4201b9549cd44 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -87,9 +87,11 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
 AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
 AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"])
+AM_CONDITIONAL(HWASAN_SUPPORTED, [test "x$HWASAN_SUPPORTED" = "xyes"])
 
 # Check for functions needed.
 AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime lstat readlink)
@@ -343,7 +345,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
@@ -371,6 +373,21 @@ _EOF
 ])
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  AC_CONFIG_FILES(AC_FOREACH([DIR], [hwasan], [DIR/Makefile ]), 
+    [cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    AS_UNSET([ml_norecursion])
+])
+fi
+
 AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS])
 AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS])
 
diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt
index 714f2923605011329b54be7e9fcf7af44a0593fd..92f4e9ff507b2972d1993ee288ea9ccef0103ec3 100644
--- a/libsanitizer/configure.tgt
+++ b/libsanitizer/configure.tgt
@@ -59,6 +59,7 @@ case "${target}" in
 		LSAN_SUPPORTED=yes
 		TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
 	fi
+	HWASAN_SUPPORTED=yes
 	;;
   x86_64-*-darwin[1]* | i?86-*-darwin[1]*)
 	TSAN_SUPPORTED=no


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
  2019-12-12 15:19     ` [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
  2019-12-12 15:19     ` [PATCH 4/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
@ 2019-12-12 15:19     ` Matthew Malcomson
  2020-01-07 15:10       ` Martin Liška
  2019-12-12 15:19     ` [PATCH 7/X] [libsanitizer] Add tests Matthew Malcomson
                       ` (15 subsequent siblings)
  18 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-12-12 15:19 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 54948 bytes --]

There are four main features to this change:

1) Check pointer tags match address tags.

In the new `hwasan` pass we put HWASAN_CHECK internal functions around
all memory accesses, to check that tags in the pointer being used match
the tag stored in shadow memory for the memory region being used.

These internal functions are expanded into actual checks in the sanopt
pass that happens just before expansion into RTL.

We use the same mechanism that currently inserts ASAN_CHECK internal
functions to insert the new HWASAN_CHECK functions.

2) Instrument known builtin function calls.

Handle all builtin functions that we know use memory accesses.
This commit uses the machinery added for ASAN to identify builtin
functions that access memory.

The main differences between the approaches for HWASAN and ASAN are:
 - libhwasan intercepts much less builtin functions.
 - Alloca needs to be transformed differently (instead of adding
   redzones it needs to tag shadow memory and return a tagged pointer).
 - stack_restore needs to untag the shadow stack between the current
   position and where it's going.
 - `noreturn` functions can not be handled by simply unpoisoning the
   entire shadow stack -- there is no "always valid" tag.
   (exceptions and things such as longjmp need to be handled in a
   different way).

For hardware implemented checking (such as AArch64's memory tagging
extension) alloca and stack_restore will need to be handled by hooks in
the backend rather than transformation at the gimple level.  This will
allow architecture specific handling of such stack modifications.

3) Introduce HWASAN block-scope poisoning

Here we use exactly the same mechanism as ASAN_MARK to poison/unpoison
variables on entry/exit of a block.

In order to simply use the exact same machinery we're using the same
internal functions until the SANOPT pass.  This means that all handling
of ASAN_MARK is the same.
This has the negative that the naming may be a little confusing, but a
positive that handling of the internal function doesn't have to be
duplicated for a function that behaves exactly the same but has a
different name.

gcc/ChangeLog:

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

	* asan.c (handle_builtin_stack_restore): Account for HWASAN.
	(handle_builtin_alloca): Account for HWASAN.
	(get_mem_refs_of_builtin_call): Special case strlen for HWASAN.
	(report_error_func): Assert not HWASAN.
	(build_check_stmt): Make HWASAN_CHECK instead of ASAN_CHECK.
	(instrument_derefs): HWASAN does not tag globals.
	(maybe_instrument_call): Don't instrument `noreturn` functions.
	(initialize_sanitizer_builtins): Add new type.
	(asan_expand_mark_ifn): Account for HWASAN.
	(asan_expand_check_ifn): Assert never called by HWASAN.
	(asan_expand_poison_ifn): Account for HWASAN.
	(hwasan_instrument): New.
	(hwasan_base): New.
	(hwasan_emit_untag_frame): Free block-scope-var hash map.
	(hwasan_check_func): New.
	(hwasan_expand_check_ifn): New.
	(hwasan_expand_mark_ifn): New.
	(gate_hwasan): New.
	(class pass_hwasan): New.
	(make_pass_hwasan): New.
	(class pass_hwasan_O0): New.
	(make_pass_hwasan_O0): New.
	* asan.h (hwasan_base): New decl.
	(hwasan_expand_check_ifn): New decl.
	(hwasan_expand_mark_ifn): New decl.
	(gate_hwasan): New decl.
	(enum hwasan_mark_flags): New.
	(asan_intercepted_p): Always false for hwasan.
	(asan_sanitize_use_after_scope): Account for HWASAN.
	* builtin-types.def (BT_FN_PTR_CONST_PTR_UINT8): New.
	* gimple-pretty-print.c (dump_gimple_call_args): Account for
	HWASAN.
	* gimplify.c (asan_poison_variable): Account for HWASAN.
	(gimplify_function_tree): Remove requirement of
	SANITIZE_ADDRESS, requiring asan or hwasan is accounted for in
	`asan_sanitize_use_after_scope`.
	* internal-fn.c (expand_HWASAN_CHECK): New.
	(expand_HWASAN_CHOOSE_TAG): New.
	(expand_HWASAN_MARK): New.
	(expand_HWASAN_ALLOCA_UNPOISON): New.
	* internal-fn.def (HWASAN_CHOOSE_TAG): New.
	(HWASAN_CHECK): New.
	(HWASAN_MARK): New.
	(HWASAN_ALLOCA_UNPOISON): New.
	* passes.def: Add hwasan and hwasan_O0 passes.
	* sanitizer.def (BUILT_IN_HWASAN_LOAD1): New.
	(BUILT_IN_HWASAN_LOAD2): New.
	(BUILT_IN_HWASAN_LOAD4): New.
	(BUILT_IN_HWASAN_LOAD8): New.
	(BUILT_IN_HWASAN_LOAD16): New.
	(BUILT_IN_HWASAN_LOADN): New.
	(BUILT_IN_HWASAN_STORE1): New.
	(BUILT_IN_HWASAN_STORE2): New.
	(BUILT_IN_HWASAN_STORE4): New.
	(BUILT_IN_HWASAN_STORE8): New.
	(BUILT_IN_HWASAN_STORE16): New.
	(BUILT_IN_HWASAN_STOREN): New.
	(BUILT_IN_HWASAN_LOAD1_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD2_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD4_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD8_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD16_NOABORT): New.
	(BUILT_IN_HWASAN_LOADN_NOABORT): New.
	(BUILT_IN_HWASAN_STORE1_NOABORT): New.
	(BUILT_IN_HWASAN_STORE2_NOABORT): New.
	(BUILT_IN_HWASAN_STORE4_NOABORT): New.
	(BUILT_IN_HWASAN_STORE8_NOABORT): New.
	(BUILT_IN_HWASAN_STORE16_NOABORT): New.
	(BUILT_IN_HWASAN_STOREN_NOABORT): New.
	(BUILT_IN_HWASAN_TAG_MISMATCH4): New.
	(BUILT_IN_HWASAN_HANDLE_LONGJMP): New.
	(BUILT_IN_HWASAN_TAG_PTR): New.
	* sanopt.c (sanopt_optimize_walker): Act during hwasan.
	(pass_sanopt::execute): Act during hwasan.
	* toplev.c (compile_file): Use `gate_hwasan` function.
	* tree-pass.h (make_pass_hwasan): Declare passes.
	(make_pass_hwasan_O0): Declare passes.



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


diff --git a/gcc/asan.h b/gcc/asan.h
index d47540cce9db6e473a6d5aab632b5c6120cb27cf..ecd06835b254cc10a1ec5f7958b428ca80ece4e9 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -31,11 +31,15 @@ extern rtx hwasan_with_tag (rtx, poly_int64);
 extern void hwasan_tag_init ();
 extern rtx hwasan_create_untagged_base (rtx);
 extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern rtx hwasan_base ();
 extern rtx_insn *hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
 extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx);
+extern bool hwasan_expand_check_ifn (gimple_stmt_iterator *, bool);
+extern bool hwasan_expand_mark_ifn (gimple_stmt_iterator *);
 extern bool hwasan_sanitize_p (void);
 extern bool hwasan_sanitize_stack_p (void);
 extern bool hwasan_sanitize_allocas_p (void);
+extern bool gate_hwasan (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -132,6 +136,13 @@ enum asan_mark_flags
 #undef DEF
 };
 
+enum hwasan_mark_flags
+{
+#define DEF(X) HWASAN_MARK_##X
+  IFN_ASAN_MARK_FLAGS
+#undef DEF
+};
+
 /* Return true if STMT is ASAN_MARK with FLAG as first argument.  */
 extern bool asan_mark_p (gimple *stmt, enum asan_mark_flags flag);
 
@@ -181,6 +192,9 @@ extern hash_set<tree> *asan_handled_variables;
 static inline bool
 asan_intercepted_p (enum built_in_function fcode)
 {
+  if (hwasan_sanitize_p ())
+    return false;
+
   return fcode == BUILT_IN_INDEX
 	 || fcode == BUILT_IN_MEMCHR
 	 || fcode == BUILT_IN_MEMCMP
@@ -209,7 +223,8 @@ asan_intercepted_p (enum built_in_function fcode)
 static inline bool
 asan_sanitize_use_after_scope (void)
 {
-  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
+  return (flag_sanitize_address_use_after_scope
+	  && (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ()));
 }
 
 /* Return true if DECL should be guarded on the stack.  */
diff --git a/gcc/asan.c b/gcc/asan.c
index 7458fee4b939a6a577c289faf2c9f0e3fac0b716..52c1e488f621be759b65a2a5fe313634b4a36777 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dojump.h"
 #include "explow.h"
 #include "expr.h"
+#include "except.h"
 #include "output.h"
 #include "langhooks.h"
 #include "cfgloop.h"
@@ -579,20 +580,47 @@ get_last_alloca_addr ()
    To overcome the issue we use following trick: pass new_sp as a second
    parameter to __asan_allocas_unpoison and rewrite it during expansion with
    new_sp + (virtual_dynamic_stack_rtx - sp) later in
-   expand_asan_emit_allocas_unpoison function.  */
+   expand_asan_emit_allocas_unpoison function.
+
+   HWASAN needs to do very similar, the eventual pseudocode should be:
+      __hwasan_tag_memory (virtual_stack_dynamic_rtx,
+			   0,
+			   new_sp - sp);
+      __builtin_stack_restore (new_sp)
+
+   Need to use the same trick to handle STACK_DYNAMIC_OFFSET as described
+   above. */
 
 static void
 handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p ()))
     return;
 
-  tree last_alloca = get_last_alloca_addr ();
   tree restored_stack = gimple_call_arg (call, 0);
-  tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
-  gimple *g = gimple_build_call (fn, 2, last_alloca, restored_stack);
-  gsi_insert_before (iter, g, GSI_SAME_STMT);
-  g = gimple_build_assign (last_alloca, restored_stack);
+
+  gimple *g;
+
+  if (hwasan_sanitize_allocas_p ())
+    {
+      enum internal_fn fn = IFN_HWASAN_ALLOCA_UNPOISON;
+      /* There is only one piece of information `expand_HWASAN_ALLOCA_UNPOISON`
+	 needs to work.  This is the length of the area that we're
+	 deallocating.  Since the stack pointer is known at expand time, the
+	 position of the new stack pointer after deallocation is enough
+	 information to calculate this length.  */
+      g = gimple_build_call_internal (fn, 1, restored_stack);
+    }
+  else
+    {
+      tree last_alloca = get_last_alloca_addr ();
+      tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
+      g = gimple_build_call (fn, 2, last_alloca, restored_stack);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      g = gimple_build_assign (last_alloca, restored_stack);
+    }
+
   gsi_insert_before (iter, g, GSI_SAME_STMT);
 }
 
@@ -622,14 +650,12 @@ handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 static void
 handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p ()))
     return;
 
   gassign *g;
   gcall *gg;
-  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
-
-  tree last_alloca = get_last_alloca_addr ();
   tree callee = gimple_call_fndecl (call);
   tree old_size = gimple_call_arg (call, 0);
   tree ptr_type = gimple_call_lhs (call) ? TREE_TYPE (gimple_call_lhs (call))
@@ -639,6 +665,85 @@ handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
     = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
       ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
 
+  if (hwasan_sanitize_allocas_p ())
+    {
+      /*
+	 HWASAN needs a different expansion.
+
+	 addr = __builtin_alloca (size, align);
+
+	 should be replaced by
+
+	 new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment;
+	 untagged_addr = __builtin_alloca (new_size, align);
+	 tag = __hwasan_choose_alloca_tag ();
+	 addr = __hwasan_tag_pointer (untagged_addr, tag);
+	 __hwasan_tag_memory (addr, tag, new_size);
+	*/
+      /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start on
+	 a tag granule.  */
+      align = align > HWASAN_TAG_GRANULE_SIZE ? align : HWASAN_TAG_GRANULE_SIZE;
+
+      /* tree new_size = (old_size + 15) & ~15;  */
+      uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
+      tree old_size = gimple_call_arg (call, 0);
+      tree tree_mask = build_int_cst (size_type_node, tg_mask);
+      g = gimple_build_assign (make_ssa_name (size_type_node), PLUS_EXPR,
+			       old_size, tree_mask);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree oversize = gimple_assign_lhs (g);
+
+      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_NOT_EXPR,
+			       tree_mask);
+      tree mask = gimple_assign_lhs (g);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+
+      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_AND_EXPR,
+			       oversize, mask);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree new_size = gimple_assign_lhs (g);
+
+      /* emit the alloca call */
+      tree fn = builtin_decl_implicit (BUILT_IN_ALLOCA_WITH_ALIGN);
+      gg = gimple_build_call (fn, 2, new_size,
+			      build_int_cst (size_type_node, align));
+      tree untagged_addr = make_ssa_name (ptr_type, gg);
+      gimple_call_set_lhs (gg, untagged_addr);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code choosing the tag.
+	 Here we use an internal function so we can choose the tag at expand
+	 time.  We need the decision to be made after stack variables have been
+	 assigned their tag (i.e. once the tag_offset variable has been set to
+	 one after the last stack variables tag).  */
+
+      gg = gimple_build_call_internal (IFN_HWASAN_CHOOSE_TAG, 0);
+      tree tag = make_ssa_name (unsigned_char_type_node, gg);
+      gimple_call_set_lhs (gg, tag);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code adding tag to pointer.  */
+      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_PTR);
+      gg = gimple_build_call (fn, 2, untagged_addr, tag);
+      tree addr = make_ssa_name (ptr_type, gg);
+      gimple_call_set_lhs (gg, addr);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code tagging shadow memory.
+	 NOTE: require using `untagged_addr` here for libhwasan API.  */
+      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MEM);
+      gg = gimple_build_call (fn, 3, untagged_addr, tag, new_size);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Finally, replace old alloca ptr with NEW_ALLOCA.  */
+      replace_call_with_value (iter, addr);
+      return;
+    }
+
+  tree last_alloca = get_last_alloca_addr ();
+  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
+
+
   /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
      bytes of allocated space.  Otherwise, align alloca to ASAN_RED_ZONE_SIZE
      manually.  */
@@ -791,6 +896,31 @@ get_mem_refs_of_builtin_call (gcall *call,
       break;
 
     case BUILT_IN_STRLEN:
+      /* Special case strlen here since its length is taken from its return
+	 value.
+
+	 The approach taken by the sanitizers is to check a memory access
+	 before it's taken.  For ASAN strlen is intercepted by libasan, so no
+	 check is inserted by the compiler.
+
+	 This function still returns `true` and provides a length to the rest
+	 of the ASAN pass in order to record what areas have been checked,
+	 avoiding superfluous checks later on.
+
+	 HWASAN does not intercept any of these internal functions.
+	 This means that checks for memory accesses must be inserted by the
+	 compiler.
+	 strlen is a special case, because we can tell the length from the
+	 return of the function, but that is not known until after the function
+	 has returned.
+
+	 Hence we can't check the memory access before it happens.
+	 We could check the memory access after it has already happened, but
+	 for now I'm choosing to just ignore `strlen` calls.
+	 This decision was simply made because that means the special case is
+	 limited to this one case of this one function.  */
+      if (hwasan_sanitize_p ())
+	return false;
       source0 = gimple_call_arg (call, 0);
       len = gimple_call_lhs (call);
       break;
@@ -1360,6 +1490,149 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+
+/* HWAddressSanitizer (hwasan) is a probabilistic method for detecting
+   out-of-bounds and use-after-free bugs.
+   Read more:
+   http://code.google.com/p/address-sanitizer/
+
+   Similar to AddressSanitizer (asan) it consists of two parts: the
+   instrumentation module in this file, and a run-time library.
+
+   The instrumentation module adds a run-time check before every memory insn in
+   the same manner as asan (see the block comment for AddressSanitizer above).
+   Currently, hwasan only adds out-of-line instrumentation, where each check is
+   implemented as a function call to the run-time library.  Hence a check for a
+   load of N bytes from address X would be implemented with a function call to
+   __hwasan_loadN(X), and checking a store of N bytes from address X would be
+   implemented with a function call to __hwasan_storeN(X).
+
+   The main difference between hwasan and asan is in the information stored to
+   help this checking.  Both sanitizers use a shadow memory area which stores
+   data recording the state of main memory at a corresponding address.
+
+   For hwasan, each 16 byte granule in main memory has a corresponding 1 byte
+   in shadow memory.  This shadow address can be calculated with equation:
+     (addr >> HWASAN_TAG_SHIFT_SIZE) + __hwasan_shadow_memory_dynamic_address;
+   The conversion between real and shadow memory for asan is given in the block
+   comment at the top of this file.
+   The description of how this shadow memory is laid out for asan is in the
+   block comment at the top of this file, here we describe how this shadow
+   memory is used for hwasan.
+
+   For hwasan, each variable is assigned a byte-sized 'tag'.  The extent of
+   the shadow memory for that variable is filled with the assigned tag, and
+   every pointer referencing that variable has its top byte set to the same
+   tag.  The run-time library redefines malloc so that every allocation returns
+   a tagged pointer and tags the corresponding shadow memory with the same tag.
+
+   On each pointer dereference the tag found in the pointer is compared to the
+   tag found in the shadow memory corresponding to the accessed memory address.
+   If these tags are found to differ then this memory access is judged to be
+   invalid and a report is generated.
+
+   This method of bug detection is not perfect -- it can not catch every bad
+   access -- but catches them probabilistically instead.  There is always the
+   possibility that an invalid memory access will happen to access memory
+   tagged with the same tag as the pointer that this access used.
+   The chances of this are approx. 0.4% for any two uncorrelated objects.
+
+   Random tag generation can mitigate this problem by decreasing the
+   probability that an invalid access will be missed in the same manner over
+   multiple runs.  i.e. if two objects are tagged the same in one run of the
+   binary they are unlikely to be tagged the same in the next run.
+   Both heap and stack allocated objects have random tags by default.
+
+   [16 byte granule implications]
+    Since the shadow memory only has a resolution on real memory of 16 bytes,
+    invalid accesses that are within the same 16 byte granule as a valid
+    address will not be caught.
+
+    There is a "short-granule" feature in the runtime library which does catch
+    such accesses, but this feature is not implemented for stack objects (since
+    stack objects are allocated and tagged by compiler instrumentation, and
+    this feature has not yet been implemented in GCC instrumentation).
+
+    Another outcome of this 16 byte resolution is that each tagged object must
+    be 16 byte aligned.  If two objects were to share any 16 byte granule in
+    memory, then they both would have to be given the same tag, and invalid
+    accesses to one using a pointer to the other would be undetectable.
+
+   [Compiler instrumentation]
+    Compiler instrumentation ensures that two adjacent buffers on the stack are
+    given different tags, this means an access to one buffer using a pointer
+    generated from the other (e.g. through buffer overrun) will have mismatched
+    tags and be caught by hwasan.
+
+    We don't randomly tag every object on the stack, since that would require
+    keeping many registers to record each tag.  Instead we randomly generate a
+    tag for each function frame, and each new stack object uses a tag offset
+    from that frame tag.
+    i.e. each object is tagged as RFT + offset, where RFT is the "random frame
+    tag" generated for this frame.
+
+    As a demonstration, using the same example program as in the asan block
+    comment above:
+
+     int
+     foo ()
+     {
+       char a[23] = {0};
+       int b[2] = {0};
+
+       a[5] = 1;
+       b[1] = 2;
+
+       return a[5] + b[1];
+     }
+
+    On AArch64 the stack will be ordered as follows for the above function:
+
+    Slot 1/ [24 bytes for variable 'a']
+    Slot 2/ [8 bytes padding for alignment]
+    Slot 3/ [8 bytes for variable 'b']
+    Slot 4/ [8 bytes padding for alignment]
+
+    (The padding is there to ensure 16 byte alignment as described in the 16
+     byte granule implications).
+
+    While the shadow memory will be ordered as follows:
+
+    - 2 bytes (representing 32 bytes in real memory) tagged with RFT + 1.
+    - 1 byte (representing 16 bytes in real memory) tagged with RFT + 2.
+
+    And any pointer to "a" will have the tag RFT + 1, and any pointer to "b"
+    will have the tag RFT + 2.
+
+   [Top Byte Ignore requirements]
+    Hwasan requires the ability to store an 8 bit tag in every pointer.  There
+    is no instrumentation done to remove this tag from pointers before
+    dereferencing, which means the hardware must ignore this tag during memory
+    accesses.
+
+    One architecture that provides this feature is the AArch64 architecture.
+    This is the only architecture that hwasan is currently implemented for.
+
+   [Stack requires cleanup on unwinding]
+    During normal operation of a hwasan sanitized program more space in the
+    shadow memory becomes tagged as the stack grows.  As the stack shrinks this
+    shadow memory space must become untagged.  If it is not untagged then when
+    the stack grows again (during other function calls later on in the program)
+    objects on the stack that are usually not tagged (e.g. parameters passed on
+    the stack) can be placed in memory whose shadow space is tagged with
+    something else, and accesses can cause false positive reports.
+
+    Hence we place untagging code on every epilogue of functions which tag some
+    stack objects.
+
+    Moreover, the run-time library intercepts longjmp & setjmp to uncolour
+    when the stack is unwound this way.
+
+    C++ exceptions are not yet handled, which means this sanitizer can not
+    handle C++ code that throws exceptions -- it will give false positives
+    after an exception has been thrown.  */
+
+
 /* Returns whether we are tagging pointers and checking those tags on memory
    access.  This is true when checking with either in software or hardware.  */
 bool
@@ -1860,6 +2133,8 @@ static tree
 report_error_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
 		   int *nargs)
 {
+  gcc_assert (!hwasan_sanitize_p ());
+
   static enum built_in_function report[2][2][6]
     = { { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
 	    BUILT_IN_ASAN_REPORT_LOAD4, BUILT_IN_ASAN_REPORT_LOAD8,
@@ -2196,7 +2471,11 @@ build_check_stmt (location_t loc, tree base, tree len,
   if (is_scalar_access)
     flags |= ASAN_CHECK_SCALAR_ACCESS;
 
-  g = gimple_build_call_internal (IFN_ASAN_CHECK, 4,
+  enum internal_fn fn = hwasan_sanitize_p ()
+    ? IFN_HWASAN_CHECK
+    : IFN_ASAN_CHECK;
+
+  g = gimple_build_call_internal (fn, 4,
 				  build_int_cst (integer_type_node, flags),
 				  base, len,
 				  build_int_cst (integer_type_node,
@@ -2220,9 +2499,11 @@ static void
 instrument_derefs (gimple_stmt_iterator *iter, tree t,
 		   location_t location, bool is_store)
 {
-  if (is_store && !param_asan_instrument_writes)
+  if (is_store
+      && (!param_asan_instrument_writes || !param_hwasan_instrument_writes))
     return;
-  if (!is_store && !param_asan_instrument_reads)
+  if (!is_store
+      && (!param_asan_instrument_reads || !param_hwasan_instrument_reads))
     return;
 
   tree type, base;
@@ -2283,7 +2564,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
     {
       if (DECL_THREAD_LOCAL_P (inner))
 	return;
-      if (!param_asan_globals && is_global_var (inner))
+      if ((hwasan_sanitize_p () || !param_asan_globals)
+	  && is_global_var (inner))
         return;
       if (!TREE_STATIC (inner))
 	{
@@ -2376,7 +2658,7 @@ instrument_mem_region_access (tree base, tree len,
 static bool
 instrument_builtin_call (gimple_stmt_iterator *iter)
 {
-  if (!param_asan_memintrin)
+  if (!param_asan_memintrin || !param_hwasan_memintrin)
     return false;
 
   bool iter_advanced_p = false;
@@ -2512,10 +2794,13 @@ maybe_instrument_call (gimple_stmt_iterator *iter)
 	      break;
 	    }
 	}
-      tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
-      gimple *g = gimple_build_call (decl, 0);
-      gimple_set_location (g, gimple_location (stmt));
-      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      if (! hwasan_sanitize_p ())
+	{
+	  tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
+	  gimple *g = gimple_build_call (decl, 0);
+	  gimple_set_location (g, gimple_location (stmt));
+	  gsi_insert_before (iter, g, GSI_SAME_STMT);
+	}
     }
 
   bool instrumented = false;
@@ -2914,6 +3199,9 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_PTR_CONST_PTR_UINT8
+    = build_function_type_list (ptr_type_node, const_ptr_type_node,
+				unsigned_char_type_node, NULL_TREE);
   tree BT_FN_VOID_PTR_UINT8_SIZE
     = build_function_type_list (void_type_node, ptr_type_node,
 				unsigned_char_type_node, size_type_node,
@@ -3237,18 +3525,34 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 
   gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
 
+  tree len = gimple_call_arg (g, 2);
+  gcc_assert (tree_fits_shwi_p (len));
+  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  gcc_assert (size_in_bytes);
+
+  if (hwasan_sanitize_p ())
+    {
+      gcc_assert (param_hwasan_stack);
+      /* Here we swap ASAN_MARK calls for HWASAN_MARK.
+	 This is because we are using the approach of using ASAN_MARK as a
+	 synonym until here.
+	 That approach means we don't yet have to duplicate all the special
+	 cases for ASAN_MARK and ASAN_POISON with the exact same handling but
+	 called HWASAN_MARK etc.  */
+      gimple *hw_poison_call
+	= gimple_build_call_internal (IFN_HWASAN_MARK, 3,
+				      gimple_call_arg (g, 0),
+				      base, len);
+      gsi_replace (iter, hw_poison_call, false);
+      return false;
+    }
+
   if (is_poison)
     {
       if (asan_handled_variables == NULL)
 	asan_handled_variables = new hash_set<tree> (16);
       asan_handled_variables->add (decl);
     }
-  tree len = gimple_call_arg (g, 2);
-
-  gcc_assert (tree_fits_shwi_p (len));
-  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
-  gcc_assert (size_in_bytes);
-
   g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
 			   NOP_EXPR, base);
   gimple_set_location (g, loc);
@@ -3311,6 +3615,7 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 bool
 asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
 {
+  gcc_assert (!hwasan_sanitize_p ());
   gimple *g = gsi_stmt (*iter);
   location_t loc = gimple_location (g);
   bool recover_p;
@@ -3584,11 +3889,66 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
 
       int nargs;
       bool store_p = gimple_call_internal_p (use, IFN_ASAN_POISON_USE);
-      tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
-				    &nargs);
-
-      gcall *call = gimple_build_call (fun, 1,
-				       build_fold_addr_expr (shadow_var));
+      gcall *call;
+      if (hwasan_sanitize_p ())
+	{
+	  tree fun = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MISMATCH4);
+	  /* NOTE: hwasan has no __hwasan_report_* functions like asan does.
+		We use __hwasan_tag_mismatch4 with arguments that tell it the
+		size of access and load to report all tag mismatches.
+
+		The arguments to this function are:
+		  Address of invalid access.
+		  Bitfield containing information about the access
+		    (access_info)
+		  Pointer to a frame of registers
+		    (for use in printing the contents of registers in a dump)
+		    Not used yet -- to be used by inline instrumentation.
+		  Size of access (only if size is not representable in the
+		    access_info argument).
+
+		The access_info bitfield encodes the following pieces of
+		information:
+		  - Is this a store or load?
+		    access_info & 0x10  =>  store
+		  - Should the program continue after reporting the error?
+		    access_info & 0x20  =>  recover
+		  - What size access is this (if size is less than 16 bytes)
+		    if (access_info & 0xf == 0xf)
+		      size is taken from last argument.
+		    else
+		      size == 1 << (access_info & 0xf)
+
+		The last argument contains the size of the access iff the
+		access was of a size greater than or equal to 16 bytes.
+
+		See the function definition `__hwasan_tag_mismatch4` in
+		libsanitizer/hwasan for the full definition.
+		*/
+	  unsigned HOST_WIDE_INT size_in_bytes = tree_to_uhwi (size);
+	  unsigned size_indicator = (size_in_bytes > 16)
+	    ? 0xf
+	    : exact_log2 (size_in_bytes);
+	  unsigned access_info = (0x20 * recover_p)
+	    + (0x10 * store_p)
+	    + (size_indicator);
+	  tree long_pointer_type
+	    = build_pointer_type (long_long_unsigned_type_node);
+	  call = gimple_build_call (fun, 3,
+				    build_fold_addr_expr (shadow_var),
+				    build_int_cst (long_long_unsigned_type_node,
+						   access_info),
+				    build_int_cst (long_pointer_type,
+						   0),
+				    size);
+	}
+      else
+	{
+	  tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
+					&nargs);
+	  call = gimple_build_call (fun, 1,
+				    build_fold_addr_expr (shadow_var));
+	}
       gimple_set_location (call, gimple_location (use));
       gimple *call_to_insert = call;
 
@@ -3722,6 +4082,17 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/*  HWASAN  */
+
+/* Instrument the current function.  */
+static unsigned int
+hwasan_instrument (void)
+{
+  transform_statements ();
+  last_alloca_addr = NULL_TREE;
+  return 0;
+}
+
 /* For stack tagging:
    Initialise tag of the base register.
    This has to be done as soon as the stack is getting expanded to ensure
@@ -3745,12 +4116,24 @@ hwasan_current_tag ()
 }
 
 /* For stack tagging:
+     Return the 'base pointer' for this function.  This pointer is what gets
+     returned from `hwasan_record_base`.  */
+rtx
+hwasan_base ()
+{
+  if (! hwasan_base_ptr)
+    hwasan_record_base (gen_reg_rtx (Pmode));
+
+  return hwasan_base_ptr;
+}
+
+/* For stack tagging:
       Increment the tag offset modulo the size a tag can represent.  */
 void
 hwasan_increment_tag ()
 {
   uint8_t tag_bits = HWASAN_TAG_SIZE;
-  STATIC_ASSERT (tag_bits == sizeof (tag_offset) * CHAR_BIT);
+  STATIC_ASSERT (HWASAN_TAG_SIZE == sizeof (tag_offset) * CHAR_BIT);
   tag_offset = (tag_offset + 1) % (1 << tag_bits);
   /* The "background tag" of the stack is zero by definition.
      This is the tag that objects like parameters passed on the stack and
@@ -3994,4 +4377,214 @@ hwasan_finish_file (void)
   flag_sanitize |= SANITIZE_HWADDRESS;
 }
 
+/* Construct a function tree for __hwasan_{load,store}{1,2,4,8,16,_n}.
+   IS_STORE is either 1 (for a store) or 0 (for a load).  */
+static tree
+hwasan_check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
+		   int *nargs)
+{
+  static enum built_in_function check[2][2][6]
+    = { { { BUILT_IN_HWASAN_LOAD1, BUILT_IN_HWASAN_LOAD2,
+	    BUILT_IN_HWASAN_LOAD4, BUILT_IN_HWASAN_LOAD8,
+	    BUILT_IN_HWASAN_LOAD16, BUILT_IN_HWASAN_LOADN },
+	  { BUILT_IN_HWASAN_STORE1, BUILT_IN_HWASAN_STORE2,
+	    BUILT_IN_HWASAN_STORE4, BUILT_IN_HWASAN_STORE8,
+	    BUILT_IN_HWASAN_STORE16, BUILT_IN_HWASAN_STOREN } },
+	{ { BUILT_IN_HWASAN_LOAD1_NOABORT,
+	    BUILT_IN_HWASAN_LOAD2_NOABORT,
+	    BUILT_IN_HWASAN_LOAD4_NOABORT,
+	    BUILT_IN_HWASAN_LOAD8_NOABORT,
+	    BUILT_IN_HWASAN_LOAD16_NOABORT,
+	    BUILT_IN_HWASAN_LOADN_NOABORT },
+	  { BUILT_IN_HWASAN_STORE1_NOABORT,
+	    BUILT_IN_HWASAN_STORE2_NOABORT,
+	    BUILT_IN_HWASAN_STORE4_NOABORT,
+	    BUILT_IN_HWASAN_STORE8_NOABORT,
+	    BUILT_IN_HWASAN_STORE16_NOABORT,
+	    BUILT_IN_HWASAN_STOREN_NOABORT } } };
+  if (size_in_bytes == -1)
+    {
+      *nargs = 2;
+      return builtin_decl_implicit (check[recover_p][is_store][5]);
+    }
+  *nargs = 1;
+  int size_log2 = exact_log2 (size_in_bytes);
+  return builtin_decl_implicit (check[recover_p][is_store][size_log2]);
+}
+
+/* Expand the HWASAN_{LOAD,STORE} builtins.  */
+bool
+hwasan_expand_check_ifn (gimple_stmt_iterator *iter, bool)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  bool recover_p;
+  if (flag_sanitize & SANITIZE_USER_HWADDRESS)
+    recover_p = (flag_sanitize_recover & SANITIZE_USER_HWADDRESS) != 0;
+  else
+    recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_HWADDRESS) != 0;
+
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_CHECK_LAST);
+  bool is_scalar_access = (flags & ASAN_CHECK_SCALAR_ACCESS) != 0;
+  bool is_store = (flags & ASAN_CHECK_STORE) != 0;
+  bool is_non_zero_len = (flags & ASAN_CHECK_NON_ZERO_LEN) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  tree len = gimple_call_arg (g, 2);
+
+  /* `align` is unused for HWASAN_CHECK, but I pass the argument anyway
+     since that way the arguments match ASAN_CHECK.  */
+  /* HOST_WIDE_INT align = tree_to_shwi (gimple_call_arg (g, 3));  */
+
+  unsigned HOST_WIDE_INT size_in_bytes
+    = is_scalar_access && tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
+
+  gimple_stmt_iterator gsi = *iter;
+
+  if (!is_non_zero_len)
+    {
+      /* So, the length of the memory area to hwasan-protect is
+	 non-constant.  Let's guard the generated instrumentation code
+	 like:
+
+	 if (len != 0)
+	   {
+	     // hwasan instrumentation code goes here.
+	   }
+	 // falltrough instructions, starting with *ITER.  */
+
+      g = gimple_build_cond (NE_EXPR,
+			    len,
+			    build_int_cst (TREE_TYPE (len), 0),
+			    NULL_TREE, NULL_TREE);
+      gimple_set_location (g, loc);
+
+      basic_block then_bb, fallthrough_bb;
+      insert_if_then_before_iter (as_a <gcond *> (g), iter,
+				  /*then_more_likely_p=*/true,
+				  &then_bb, &fallthrough_bb);
+      /* Note that fallthrough_bb starts with the statement that was
+	pointed to by ITER.  */
+
+      /* The 'then block' of the 'if (len != 0) condition is where
+	we'll generate the hwasan instrumentation code now.  */
+      gsi = gsi_last_bb (then_bb);
+    }
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			   NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+  tree base_addr = gimple_assign_lhs (g);
+
+  int nargs = 0;
+  tree fun = hwasan_check_func (is_store, recover_p, size_in_bytes, &nargs);
+  if (nargs == 1)
+    g = gimple_build_call (fun, 1, base_addr);
+  else
+    {
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			       NOP_EXPR, len);
+      gimple_set_location (g, loc);
+      gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+      g = gimple_build_call (fun, nargs, base_addr, sz_arg);
+    }
+
+  gimple_set_location (g, loc);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+  gsi_remove (iter, true);
+  *iter = gsi;
+  return false;
+}
+
+/* Dummy: the HWASAN_MARK internal function should only ever be in the code
+   after the sanopt pass.  */
+bool
+hwasan_expand_mark_ifn (gimple_stmt_iterator *)
+{
+  gcc_unreachable ();
+}
+
+bool
+gate_hwasan ()
+{
+  return hwasan_sanitize_p ();
+}
+
+namespace {
+
+const pass_data pass_data_hwasan =
+{
+  GIMPLE_PASS, /* type */
+  "hwasan", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_hwasan : public gimple_opt_pass
+{
+public:
+  pass_hwasan (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_hwasan, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_hwasan (m_ctxt); }
+  virtual bool gate (function *) { return gate_hwasan (); }
+  virtual unsigned int execute (function *) { return hwasan_instrument (); }
+
+}; /* class pass_asan  */
+
+} /* anon namespace  */
+
+gimple_opt_pass *
+make_pass_hwasan (gcc::context *ctxt)
+{
+  return new pass_hwasan (ctxt);
+}
+
+namespace {
+
+const pass_data pass_data_hwasan_O0 =
+{
+  GIMPLE_PASS, /* type */
+  "hwasan_O0", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_hwasan_O0 : public gimple_opt_pass
+{
+public:
+  pass_hwasan_O0 (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_hwasan_O0, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_hwasan_O0 (m_ctxt); }
+  virtual bool gate (function *) { return !optimize && gate_hwasan (); }
+  virtual unsigned int execute (function *) { return hwasan_instrument (); }
+
+}; // class pass_asan
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_hwasan_O0 (gcc::context *ctxt)
+{
+  return new pass_hwasan_O0 (ctxt);
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index 48c53119731430a5e0762482f2dcf8f8837e2de7..5370dbe514d227161c3153b10ebb2914b7a3efbc 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -496,6 +496,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_INT_FEXCEPT_T_PTR_INT, BT_INT, BT_FEXCEPT_T_PTR,
 		     BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_FEXCEPT_T_PTR_INT, BT_INT,
 		     BT_CONST_FEXCEPT_T_PTR, BT_INT)
+DEF_FUNCTION_TYPE_2 (BT_FN_PTR_CONST_PTR_UINT8, BT_PTR, BT_CONST_PTR, BT_UINT8)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR)
 
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index f59cc2aa31880fbb5b8ab9abeae8b15cf4a0a145..362b3da52a17e8683f52ee80ad6236870fe7d7c0 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -750,6 +750,7 @@ dump_gimple_call_args (pretty_printer *buffer, gcall *gs, dump_flags_t flags)
 	  limit = ARRAY_SIZE (reduction_args);
 	  break;
 
+	case IFN_HWASAN_MARK:
 	case IFN_ASAN_MARK:
 #define DEF(X) #X
 	  static const char *const asan_mark_args[] = {IFN_ASAN_MARK_FLAGS};
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 9073680cb3179f196979a0ea10b8db0188462eb8..e99eed526d5590d37e5ef18affd2a6993ad1c0fd 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1233,8 +1233,11 @@ asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
 
   /* It's necessary to have all stack variables aligned to ASAN granularity
      bytes.  */
-  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
-    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+  gcc_assert (!hwasan_sanitize_p () || hwasan_sanitize_stack_p ());
+  unsigned shadow_granularity
+    = hwasan_sanitize_p () ? HWASAN_TAG_GRANULE_SIZE : ASAN_SHADOW_GRANULARITY;
+  if (DECL_ALIGN_UNIT (decl) <= shadow_granularity)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * shadow_granularity);
 
   HOST_WIDE_INT flags = poison ? ASAN_MARK_POISON : ASAN_MARK_UNPOISON;
 
@@ -14755,7 +14758,7 @@ gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
-  if (asan_sanitize_use_after_scope () && sanitize_flags_p (SANITIZE_ADDRESS))
+  if (asan_sanitize_use_after_scope ())
     asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
   if (asan_poisoned_variables)
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 6bbbc9e946e72a83c6defa38750cbf2b4ff3d4ac..9805b188eb44ffd1a8d1bb5ca0925bee2d47734b 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -458,6 +458,90 @@ expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
+expand_HWASAN_CHECK (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
+
+static void
+expand_HWASAN_ALLOCA_UNPOISON (internal_fn, gcall *gc)
+{
+  tree restored_position = gimple_call_arg (gc, 0);
+  rtx restored_rtx = expand_expr (restored_position, NULL_RTX, VOIDmode,
+				  EXPAND_NORMAL);
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  rtx off = expand_simple_binop (Pmode, MINUS, restored_rtx,
+				 stack_pointer_rtx, NULL_RTX, 0,
+				 OPTAB_LIB_WIDEN);
+  emit_library_call_value (func, NULL_RTX, LCT_NORMAL, VOIDmode,
+			   virtual_stack_dynamic_rtx, ptr_mode,
+			   const0_rtx, QImode,
+			   off, ptr_mode);
+}
+
+static void
+expand_HWASAN_CHOOSE_TAG (internal_fn, gcall *gc)
+{
+  tree tag = gimple_call_lhs (gc);
+  rtx target = expand_expr (tag, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  machine_mode mode = GET_MODE (target);
+  gcc_assert (mode == QImode);
+
+  rtx base_tag = hwasan_extract_tag (hwasan_base ());
+  gcc_assert (base_tag);
+  rtx tag_offset = GEN_INT (hwasan_current_tag ());
+  rtx chosen_tag = expand_simple_binop (QImode, PLUS, base_tag, tag_offset,
+					target, /* unsignedp = */1,
+					OPTAB_WIDEN);
+
+  gcc_assert (chosen_tag);
+  /* Really need to put the tag into the `target` RTX.  */
+  if (chosen_tag != target)
+    {
+      rtx temp = chosen_tag;
+      machine_mode ret_mode = GET_MODE (chosen_tag);
+      if (ret_mode != mode)
+	temp = simplify_gen_unary (TRUNCATE, mode, chosen_tag, ret_mode);
+
+      emit_move_insn (target, temp);
+    }
+
+  hwasan_increment_tag ();
+}
+
+static void
+expand_HWASAN_MARK (internal_fn, gcall *gc)
+{
+  HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
+  bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
+
+  tree base = gimple_call_arg (gc, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  rtx base_rtx = expand_normal (base);
+
+  rtx tag = is_poison ? const0_rtx : hwasan_extract_tag (base_rtx);
+  rtx address = hwasan_create_untagged_base (base_rtx);
+
+  tree len = gimple_call_arg (gc, 2);
+  gcc_assert (tree_fits_shwi_p (len));
+  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
+  gcc_assert (size_in_bytes);
+  size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
+  rtx size = gen_int_mode (size_in_bytes, Pmode);
+
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (func,
+      LCT_NORMAL,
+      VOIDmode,
+      address, ptr_mode,
+      tag, QImode,
+      size, ptr_mode);
+}
+
+/* This should get expanded in the sanopt pass.  */
+
+static void
 expand_ASAN_CHECK (internal_fn, gcall *)
 {
   gcc_unreachable ();
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 85f45d660b7dc496b2f4d478128ac05d0a91a25d..a8d0c10cb61e159b5a0b884807a1eb8d5c8fdeff 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -308,6 +308,10 @@ DEF_INTERNAL_FN (UBSAN_PTR, ECF_LEAF | ECF_NOTHROW, ".R.")
 DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
 DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (HWASAN_ALLOCA_UNPOISON, ECF_LEAF | ECF_NOTHROW, ".R")
+DEF_INTERNAL_FN (HWASAN_CHOOSE_TAG, ECF_LEAF | ECF_NOTHROW, ".")
+DEF_INTERNAL_FN (HWASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, "..R..")
+DEF_INTERNAL_FN (HWASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, "..R..")
 DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ASAN_POISON, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
diff --git a/gcc/passes.def b/gcc/passes.def
index 798a391bd351110d05b89fc6ccc424fd871198a4..e4c395f6b554be8f3c2e06aa4cfcda5d96130f99 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -246,6 +246,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_sink_code);
       NEXT_PASS (pass_sancov);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_hwasan);
       NEXT_PASS (pass_tsan);
       NEXT_PASS (pass_dce);
       /* Pass group that runs when 1) enabled, 2) there are loops
@@ -364,6 +365,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_dce);
       NEXT_PASS (pass_sancov);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_hwasan);
       NEXT_PASS (pass_tsan);
       /* ???  We do want some kind of loop invariant motion, but we possibly
          need to adjust LIM to be more friendly towards preserving accurate
@@ -389,6 +391,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_sancov_O0);
   NEXT_PASS (pass_lower_switch_O0);
   NEXT_PASS (pass_asan_O0);
+  NEXT_PASS (pass_hwasan_O0);
   NEXT_PASS (pass_tsan_O0);
   NEXT_PASS (pass_sanopt);
   NEXT_PASS (pass_cleanup_eh);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 7bd50715f24a2cb154b578e2abdea4e8fcdb2107..6cfb7951e21d4ee2f31921097796650afb02b7b0 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -183,6 +183,60 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 /* Hardware Address Sanitizer.  */
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1, "__hwasan_load1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2, "__hwasan_load2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4, "__hwasan_load4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8, "__hwasan_load8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16, "__hwasan_load16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN, "__hwasan_loadN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1, "__hwasan_store1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2, "__hwasan_store2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4, "__hwasan_store4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8, "__hwasan_store8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16, "__hwasan_store16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN, "__hwasan_storeN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1_NOABORT, "__hwasan_load1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2_NOABORT, "__hwasan_load2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4_NOABORT, "__hwasan_load4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8_NOABORT, "__hwasan_load8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16_NOABORT, "__hwasan_load16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN_NOABORT, "__hwasan_loadN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1_NOABORT, "__hwasan_store1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2_NOABORT, "__hwasan_store2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4_NOABORT, "__hwasan_store4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8_NOABORT, "__hwasan_store8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16_NOABORT, "__hwasan_store16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN_NOABORT, "__hwasan_storeN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MISMATCH4, "__hwasan_tag_mismatch4",
+		      BT_FN_VOID_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_HANDLE_LONGJMP, "__hwasan_handle_longjmp",
+		      BT_FN_VOID_CONST_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_PTR, "__hwasan_tag_pointer",
+		      BT_FN_PTR_CONST_PTR_UINT8, ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
 		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 7749c948c23fdd8346472c25e074bdce865f3b31..c74097c78f4f5cb5334ffbfdefeb6c1d688711de 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -772,7 +772,8 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
   basic_block son;
   gimple_stmt_iterator gsi;
   sanopt_info *info = (sanopt_info *) bb->aux;
-  bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0;
+  bool asan_check_optimize
+    = ((flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS)) != 0);
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
@@ -802,6 +803,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
       if (asan_check_optimize
 	  && gimple_call_builtin_p (stmt, BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT))
 	{
+	  gcc_assert (!hwasan_sanitize_p ());
 	  use_operand_p use;
 	  gimple *use_stmt;
 	  if (single_imm_use (gimple_vdef (stmt), &use, &use_stmt))
@@ -830,6 +832,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
 	  case IFN_UBSAN_PTR:
 	    remove = maybe_optimize_ubsan_ptr_ifn (ctx, stmt);
 	    break;
+	  case IFN_HWASAN_CHECK:
 	  case IFN_ASAN_CHECK:
 	    if (asan_check_optimize)
 	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
@@ -1255,6 +1258,10 @@ sanitize_rewrite_addressable_params (function *fun)
 unsigned int
 pass_sanopt::execute (function *fun)
 {
+  /* n.b. ASAN_MARK is used for both HWASAN and ASAN.
+     asan_num_accesses is hence used to count either HWASAN_CHECK or ASAN_CHECK
+     stuff.  This is fine because you can only have one of these active at a
+     time.  */
   basic_block bb;
   int asan_num_accesses = 0;
   bool contains_asan_mark = false;
@@ -1262,10 +1269,10 @@ pass_sanopt::execute (function *fun)
   /* Try to remove redundant checks.  */
   if (optimize
       && (flag_sanitize
-	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT
+	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_HWADDRESS
 	     | SANITIZE_ADDRESS | SANITIZE_VPTR | SANITIZE_POINTER_OVERFLOW)))
     asan_num_accesses = sanopt_optimize (fun, &contains_asan_mark);
-  else if (flag_sanitize & SANITIZE_ADDRESS)
+  else if (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS))
     {
       gimple_stmt_iterator gsi;
       FOR_EACH_BB_FN (bb, fun)
@@ -1285,7 +1292,7 @@ pass_sanopt::execute (function *fun)
       sanitize_asan_mark_poison ();
     }
 
-  if (asan_sanitize_stack_p ())
+  if (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ())
     sanitize_rewrite_addressable_params (fun);
 
   bool use_calls = param_asan_instrumentation_with_call_threshold < INT_MAX
@@ -1327,6 +1334,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_UBSAN_VPTR:
 		  no_next = ubsan_expand_vptr_ifn (&gsi);
 		  break;
+		case IFN_HWASAN_CHECK:
+		  no_next = hwasan_expand_check_ifn (&gsi, use_calls);
+		  break;
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
@@ -1338,6 +1348,9 @@ pass_sanopt::execute (function *fun)
 						    &need_commit_edge_insert,
 						    shadow_vars_mapping);
 		  break;
+		case IFN_HWASAN_MARK:
+		  no_next = hwasan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index d2706b412d9f34ef25acb21981a3c9e02e190ed5..adbf7930e0ec97dfd18b6e614392e4198438e6ec 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -510,7 +510,7 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
-      if (flag_sanitize & SANITIZE_HWADDRESS)
+      if (gate_hwasan ())
 	hwasan_finish_file ();
 
       omp_finish_file ();
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a987661530eafabc5f5eb5a4fae27dab8b2e54fb..bfcad7112d75c8460bc7bcdea545cad347928e30 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -341,6 +341,8 @@ extern void register_pass (opt_pass* pass, pass_positioning_ops pos,
 
 extern gimple_opt_pass *make_pass_asan (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_asan_O0 (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_hwasan (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_hwasan_O0 (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tsan (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tsan_O0 (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_sancov (gcc::context *ctxt);


[-- Attachment #2: hwasan-patch5.patch.gz --]
[-- Type: application/gzip, Size: 14021 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 7/X] [libsanitizer] Add tests
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (2 preceding siblings ...)
  2019-12-12 15:19     ` [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes Matthew Malcomson
@ 2019-12-12 15:19     ` Matthew Malcomson
  2019-12-12 15:19     ` [PATCH 2/X] [libsanitizer] Only build libhwasan when targeting AArch64 Matthew Malcomson
                       ` (14 subsequent siblings)
  18 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-12-12 15:19 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 80429 bytes --]

Adding hwasan tests.

Only interesting thing here is that we have to make sure the tagging mechanism
is deterministic to avoid flaky tests.

gcc/testsuite/ChangeLog:

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

	* c-c++-common/hwasan/aligned-alloc.c: New test.
	* c-c++-common/hwasan/alloca-array-accessible.c: New test.
	* c-c++-common/hwasan/alloca-gets-different-tag.c: New test.
	* c-c++-common/hwasan/alloca-outside-caught.c: New test.
	* c-c++-common/hwasan/arguments.c: New test.
	* c-c++-common/hwasan/arguments-1.c: New test.
	* c-c++-common/hwasan/arguments-2.c: New test.
	* c-c++-common/hwasan/arguments-3.c: New test.
	* c-c++-common/hwasan/asan-pr63316.c: New test.
	* c-c++-common/hwasan/asan-pr70541.c: New test.
	* c-c++-common/hwasan/asan-pr78106.c: New test.
	* c-c++-common/hwasan/asan-pr79944.c: New test.
	* c-c++-common/hwasan/asan-rlimit-mmap-test-1.c: New test.
	* c-c++-common/hwasan/bitfield-1.c: New test.
	* c-c++-common/hwasan/bitfield-2.c: New test.
	* c-c++-common/hwasan/builtin-special-handling.c: New test.
	* c-c++-common/hwasan/check-interface.c: New test.
	* c-c++-common/hwasan/halt_on_error-1.c: New test.
	* c-c++-common/hwasan/heap-overflow.c: New test.
	* c-c++-common/hwasan/hwasan-poison-optimisation.c: New test.
	* c-c++-common/hwasan/hwasan-thread-access-parent.c: New test.
	* c-c++-common/hwasan/hwasan-thread-basic-failure.c: New test.
	* c-c++-common/hwasan/hwasan-thread-clears-stack.c: New test.
	* c-c++-common/hwasan/hwasan-thread-success.c: New test.
	* c-c++-common/hwasan/kernel-defaults.c: New test.
	* c-c++-common/hwasan/large-aligned-0.c: New test.
	* c-c++-common/hwasan/large-aligned-1.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-0.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-1.c: New test.
	* c-c++-common/hwasan/macro-definition.c: New test.
	* c-c++-common/hwasan/no-sanitize-attribute.c: New test.
	* c-c++-common/hwasan/param-instrument-reads-and-writes.c: New test.
	* c-c++-common/hwasan/param-instrument-reads.c: New test.
	* c-c++-common/hwasan/param-instrument-writes.c: New test.
	* c-c++-common/hwasan/param-memintrin.c: New test.
	* c-c++-common/hwasan/random-frame-tag.c: New test.
	* c-c++-common/hwasan/sanity-check-pure-c.c: New test.
	* c-c++-common/hwasan/setjmp-longjmp-0.c: New test.
	* c-c++-common/hwasan/setjmp-longjmp-1.c: New test.
	* c-c++-common/hwasan/stack-tagging-basic-0.c: New test.
	* c-c++-common/hwasan/stack-tagging-basic-1.c: New test.
	* c-c++-common/hwasan/stack-tagging-disable.c: New test.
	* c-c++-common/hwasan/unprotected-allocas-0.c: New test.
	* c-c++-common/hwasan/unprotected-allocas-1.c: New test.
	* c-c++-common/hwasan/use-after-free.c: New test.
	* c-c++-common/hwasan/vararray-outside-caught.c: New test.
	* c-c++-common/hwasan/vararray-stack-restore-correct.c: New test.
	* c-c++-common/hwasan/very-large-objects.c: New test.
	* g++.dg/hwasan/hwasan.exp: New file.
	* g++.dg/hwasan/rvo-handled.C: New test.
	* g++.dg/hwasan/try-catch-0.cpp: New test.
	* g++.dg/hwasan/try-catch-1.cpp: New test.
	* gcc.dg/hwasan/hwasan.exp: New file.
	* gcc.dg/hwasan/nested-functions-0.c: New test.
	* gcc.dg/hwasan/nested-functions-1.c: New test.
	* gcc.dg/hwasan/nested-functions-2.c: New test.
	* lib/hwasan-dg.exp: New file.



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


diff --git a/gcc/testsuite/c-c++-common/hwasan/aligned-alloc.c b/gcc/testsuite/c-c++-common/hwasan/aligned-alloc.c
new file mode 100644
index 0000000000000000000000000000000000000000..e5837a9f0766fc4d91fabc1a3c2e0017f845dc46
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/aligned-alloc.c
@@ -0,0 +1,15 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* This program fails at runtime in the libhwasan library.
+   The allocator can't handle the requested invalid alignment.  */
+
+int
+main ()
+{
+  void *p = __builtin_aligned_alloc (17, 100);
+  if (((unsigned long long)p & 0x10) == 0)
+    return 0;
+  return 1;
+}
+
+/* { dg-output "HWAddressSanitizer: invalid alignment requested in aligned_alloc: 17" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/alloca-array-accessible.c b/gcc/testsuite/c-c++-common/hwasan/alloca-array-accessible.c
new file mode 100644
index 0000000000000000000000000000000000000000..c6b4d264b8c477b52b859a0411aabb0fc8dde352
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/alloca-array-accessible.c
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+
+#define alloca __builtin_alloca
+
+int __attribute__ ((noinline))
+using_alloca (int num)
+{
+  int retval = 0;
+  int *big_array = (int*)alloca (num * sizeof (int));
+  for (int i = 0; i < num; ++i) {
+      retval += big_array[i];
+  }
+  return retval;
+}
+
+int __attribute__ ((noinline))
+using_vararray (int num)
+{
+  int retval = 0;
+  int big_array[num];
+  for (int i = 0; i < num; ++i) {
+      retval += big_array[i];
+  }
+  return retval;
+}
+
+int main()
+{
+  using_alloca (16);
+  using_vararray (12);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/alloca-gets-different-tag.c b/gcc/testsuite/c-c++-common/hwasan/alloca-gets-different-tag.c
new file mode 100644
index 0000000000000000000000000000000000000000..b5b3aa6be0ea7f3ce09bd0ba03c050fa49450d17
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/alloca-gets-different-tag.c
@@ -0,0 +1,64 @@
+/* { dg-do run } */
+
+/* Alloca is given a different tag to other variables.
+   vararray should behave in the same way.  */
+
+#define alloca __builtin_alloca
+#define assert(x) if (!(x)) __builtin_abort ()
+
+struct two_values {
+    int left;
+    int right;
+};
+
+/* Require default hwasan tag ABI.
+   Know we're using AArch64 since that's the only architecture we run hwasan
+   tests on.  */
+char tag_of (void * x) { return ((unsigned long long)x) >> 56; }
+
+int __attribute__ ((noinline))
+alloca_different_tag (int num)
+{
+  struct two_values tmp_object = {
+      .left = 100,
+      .right = num,
+  };
+  int *big_array = (int *)alloca (num * sizeof (int));
+  int other_array[100];
+  
+  char first_tag = tag_of (&tmp_object);
+  char second_tag = tag_of (big_array);
+  char other_tag = tag_of (other_array);
+  assert (first_tag != second_tag);
+  assert (second_tag != other_tag);
+  assert (first_tag != other_tag);
+  return 0;
+}
+
+int __attribute__ ((noinline))
+vararray_different_tag (int num)
+{
+  struct two_values tmp_object = {
+      .left = 100,
+      .right = num,
+  };
+  int big_array[num];
+  int other_array[100];
+  
+  char first_tag = tag_of (&tmp_object);
+  char second_tag = tag_of (big_array);
+  char other_tag = tag_of (other_array);
+  assert (first_tag != second_tag);
+  assert (second_tag != other_tag);
+  assert (first_tag != other_tag);
+  return 0;
+}
+
+int __attribute__ ((noinline))
+main ()
+{
+  alloca_different_tag (10);
+  vararray_different_tag (8);
+  return 0;
+}
+
diff --git a/gcc/testsuite/c-c++-common/hwasan/alloca-outside-caught.c b/gcc/testsuite/c-c++-common/hwasan/alloca-outside-caught.c
new file mode 100644
index 0000000000000000000000000000000000000000..263d6df3839e2ae0a50d0c60583f5dbc77dca4bb
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/alloca-outside-caught.c
@@ -0,0 +1,24 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+#define alloca __builtin_alloca
+
+int __attribute__ ((noinline))
+check_alloca (int num)
+{
+  volatile int *allocd_array = (int*)alloca (num * sizeof(int));
+  int other_array[10];
+  return allocd_array[12];
+}
+
+int __attribute__ ((noinline))
+main ()
+{
+  check_alloca (3);
+  return 1;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/arguments-1.c b/gcc/testsuite/c-c++-common/hwasan/arguments-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..435dad3cf159f565e56406467c41d97506f45d8f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/arguments-1.c
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fsanitize=kernel-hwaddress" } */
+/* { dg-error ".*'-fsanitize=hwaddress' is incompatible with '-fsanitize=kernel-hwaddress'.*" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/arguments-2.c b/gcc/testsuite/c-c++-common/hwasan/arguments-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..90012e4cc49057de8b352fea488ba52636bfef32
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/arguments-2.c
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fsanitize=kernel-address" } */
+/* { dg-error ".*'-fsanitize=hwaddress' is incompatible with both '-fsanitize=address' and '-fsanitize=kernel-address'.*" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/arguments-3.c b/gcc/testsuite/c-c++-common/hwasan/arguments-3.c
new file mode 100644
index 0000000000000000000000000000000000000000..02fdb1ee26e37e59191ba8d4e0f769f2360f7a11
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/arguments-3.c
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fsanitize=thread" } */
+/* { dg-error ".*'-fsanitize=hwaddress' is incompatible with '-fsanitize=thread'.*" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/arguments.c b/gcc/testsuite/c-c++-common/hwasan/arguments.c
new file mode 100644
index 0000000000000000000000000000000000000000..2dedbf1d737c559c7046e5749b5e5b8459a9cbf9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/arguments.c
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fsanitize=address" } */
+/* { dg-error ".*'-fsanitize=hwaddress' is incompatible with both '-fsanitize=address' and '-fsanitize=kernel-address'.*" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/asan-pr63316.c b/gcc/testsuite/c-c++-common/hwasan/asan-pr63316.c
new file mode 100644
index 0000000000000000000000000000000000000000..05e851a238e2a527e4e9b569e924ff69e759dc6c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/asan-pr63316.c
@@ -0,0 +1,23 @@
+/* PR sanitizer/63316 */
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* }  { "*" } { "-O2" } } */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void *malloc (__SIZE_TYPE__);
+extern void free (void *);
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+  int *p = (int *) malloc (sizeof (int));
+  *p = 3;
+  asm volatile ("" : : "r" (p) : "memory");
+  free (p);
+  return 0;
+}
+
diff --git a/gcc/testsuite/c-c++-common/hwasan/asan-pr70541.c b/gcc/testsuite/c-c++-common/hwasan/asan-pr70541.c
new file mode 100644
index 0000000000000000000000000000000000000000..e3ad427b52cd8514198e82c721a4bf55f264081d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/asan-pr70541.c
@@ -0,0 +1,35 @@
+/* { dg-do run } */
+/* { dg-options "-fno-builtin-malloc -fno-builtin-free" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+/* { dg-shouldfail "hwasan" } */
+
+#include <stdio.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void *malloc (__SIZE_TYPE__);
+extern void free (void *);
+#ifdef __cplusplus
+}
+#endif
+
+struct Simple {
+  int value;
+};
+
+int f(struct Simple simple) {
+  return simple.value;
+}
+
+int main() {
+  struct Simple *psimple = (struct Simple *) malloc(sizeof(struct Simple));
+  psimple->value = 42;
+  free(psimple);
+  printf("%d\n", f(*psimple));
+  return 0;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "freed by thread T0 here:.*" } */
+/* { dg-output "previously allocated here:" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/asan-pr78106.c b/gcc/testsuite/c-c++-common/hwasan/asan-pr78106.c
new file mode 100644
index 0000000000000000000000000000000000000000..3f53ad17693cc4fbb246bc0d3f6a1d02fc3222d0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/asan-pr78106.c
@@ -0,0 +1,31 @@
+/* PR sanitizer/78106 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=hwaddress -fdump-tree-sanopt-details -ffat-lto-objects" } */
+
+int *variable;
+
+void __attribute__((used)) release()
+{
+  __builtin_free (variable);
+}
+
+int main2(int argc)
+{
+  *variable = 2;
+
+  if (argc <= 5)
+    asm volatile ("call release");
+
+  *variable = 2;
+  __builtin_abort ();
+
+  return 0;
+}
+
+int main(int argc, char **argv)
+{
+  variable = (int *)__builtin_malloc (sizeof(int));
+  return main2(argc);
+}
+
+/* { dg-final { scan-tree-dump-not "Optimizing out(\n|\r\n|\r)  HWASAN_CHECK \\(7, variable.*" "sanopt" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/asan-pr79944.c b/gcc/testsuite/c-c++-common/hwasan/asan-pr79944.c
new file mode 100644
index 0000000000000000000000000000000000000000..62c56981b201f342ac18ed589c7ae7e1123c717c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/asan-pr79944.c
@@ -0,0 +1,18 @@
+/* PR sanitizer/79944 */
+/* { dg-do run } */
+
+struct S { int i; char p[1024]; };
+
+int
+main ()
+{
+  struct S *p = (struct S *) __builtin_malloc (__builtin_offsetof (struct S, p) + 64);
+  p->i = 5;
+  asm volatile ("" : "+r" (p) : : "memory");
+  __atomic_fetch_add ((int *) p, 5, __ATOMIC_RELAXED);
+  asm volatile ("" : "+r" (p) : : "memory");
+  if (p->i != 10)
+    __builtin_abort ();
+  __builtin_free (p);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/asan-rlimit-mmap-test-1.c b/gcc/testsuite/c-c++-common/hwasan/asan-rlimit-mmap-test-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..b6da691ca49143e650d7d2b9fe58053c5396a67c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/asan-rlimit-mmap-test-1.c
@@ -0,0 +1,23 @@
+/* Check that we properly report mmap failure. */
+
+/* { dg-do run { target setrlimit } } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+/* { dg-require-effective-target hw } */
+/* { dg-shouldfail "hwasan" } */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+static volatile void *x;
+
+int main(int argc, char **argv) {
+  struct rlimit mmap_resource_limit = { 0, 0 };
+  if (setrlimit(RLIMIT_AS, &mmap_resource_limit)) return 1;
+  x = malloc(10000000);
+  return 0;
+}
+
+/* { dg-output "ERROR: Failed to mmap" } */
+
diff --git a/gcc/testsuite/c-c++-common/hwasan/bitfield-1.c b/gcc/testsuite/c-c++-common/hwasan/bitfield-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..4cabb1c8a1578bcfb230be40fb54551e010c85b7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/bitfield-1.c
@@ -0,0 +1,30 @@
+/* { dg-do run } */
+
+struct bitmapped_struct {
+    unsigned one : 1;
+    unsigned two : 1;
+    unsigned three : 1;
+    unsigned four : 1;
+    unsigned five : 1;
+    unsigned six : 1;
+    unsigned seven : 1;
+    unsigned eight : 1;
+};
+
+/* Check that hwasan allows valid bitfield accesses. */
+int __attribute__ ((noinline))
+handle_unaligned_access (struct bitmapped_struct *foo)
+{
+  if (foo->three)
+    return foo->four;
+
+  foo->five = 1;
+  return 1;
+}
+
+int main()
+{
+  struct bitmapped_struct myvar = {0};
+  handle_unaligned_access (&myvar);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/bitfield-2.c b/gcc/testsuite/c-c++-common/hwasan/bitfield-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..17fda1eed5ea28ec3bb4b8af6d58468c89c22831
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/bitfield-2.c
@@ -0,0 +1,29 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/* Ensure that hwasan instruments bitfield accesses.  */
+struct A
+{
+  /* Ensure the offset from the start of this struct to the bitfield we access
+     is large enough to be in a different tag.  */
+  char base[16];
+  int : 4;
+  long x : 7;
+};
+
+int __attribute__ ((noinline, noclone))
+f (void *p) {
+  return ((struct A *)p)->x;
+}
+
+int
+main ()
+{
+  char a = 0;
+  return f (&a);
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 2 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/builtin-special-handling.c b/gcc/testsuite/c-c++-common/hwasan/builtin-special-handling.c
new file mode 100644
index 0000000000000000000000000000000000000000..69f112d2d4ffb1c0b9af7df83cdbd1e789c8d12c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/builtin-special-handling.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-hwasan" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+/* Only skip the -flto tests without the -flto-partition=none.
+   With -flto-partition=none we still get a hwasan1 dump file, without that
+   parameter we only get the lto dump files (which means scan-tree-dump-times
+   doesn't work.  */
+/* { dg-skip-if "" { *-*-* }  { "-flto" } { "-flto-partition=none" } } */
+
+typedef __SIZE_TYPE__ size_t;
+/* Functions to observe that HWASAN instruments memory builtins in the expected
+   manner.  */
+void * __attribute__((noinline))
+memset_builtin (void *dest, int value, size_t len)
+{
+  return __builtin_memset (dest, value, len);
+}
+
+/* HWASAN avoids strlen because it doesn't know the size of the memory access
+   until *after* the function call.  */
+size_t __attribute__ ((noinline))
+strlen_builtin (char *element)
+{
+  return __builtin_strlen (element);
+}
+
+/* First test ensures that the HWASAN_CHECK was emitted before the
+   memset.  Second test ensures there was only HWASAN_CHECK (which demonstrates
+   that strlen was not instrumented).  */
+/* { dg-final { scan-tree-dump-times "HWASAN_CHECK.*memset" 1 "hwasan1" } } */
+/* { dg-final { scan-tree-dump-times "HWASAN_CHECK" 1 "hwasan1" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/check-interface.c b/gcc/testsuite/c-c++-common/hwasan/check-interface.c
new file mode 100644
index 0000000000000000000000000000000000000000..08dec71a90960f0d3172f18300bbae024689c5a7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/check-interface.c
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/*
+   Test taken from LLVM
+    compiler-rt/test/hwasan/TestCases/check-interface.cpp
+ */
+// Utilizes all flavors of __hwasan_load/store interface functions to verify
+// that the instrumentation and the interface provided by HWASan do match.
+// In case of a discrepancy, this test fails to link.
+
+typedef __UINT8_TYPE__ uint8_t;
+typedef __UINT16_TYPE__ uint16_t;
+typedef __UINT32_TYPE__ uint32_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+#define F(T) void f_##T(T *a, T *b) { *a = *b; }
+
+F(uint8_t)
+F(uint16_t)
+F(uint32_t)
+F(uint64_t)
+
+typedef unsigned V32 __attribute__((__vector_size__(32)));
+F(V32)
+
+int main() {}
diff --git a/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c b/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..f9736b9ca80bb60dc0e608530dd9f7bc6b367091
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c
@@ -0,0 +1,23 @@
+/* Test recovery mode.  */
+/* { dg-do run } */
+/* { dg-options "-fsanitize-recover=hwaddress" } */
+/* { dg-set-target-env-var HWASAN_OPTIONS "halt_on_error=false" } */
+/* { dg-shouldfail "hwasan" } */
+
+volatile int sixteen = 16;
+
+int main() {
+  char x[16];
+  __builtin_memset(x, 0, sixteen + 1);
+  asm volatile ("" : : : "memory");
+  volatile int res = x[sixteen];
+  x[sixteen] = res + 3;
+  res = x[sixteen];
+  return 0;
+}
+
+/* { dg-output "WRITE of size 17 at 0x\[0-9a-f\]+.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+.*" } */
+/* { dg-output "WRITE of size 1 at 0x\[0-9a-f\]+.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+.*" } */
+
diff --git a/gcc/testsuite/c-c++-common/hwasan/heap-overflow.c b/gcc/testsuite/c-c++-common/hwasan/heap-overflow.c
new file mode 100644
index 0000000000000000000000000000000000000000..d7065ebde24b33b34e855692673bb61d041ce1b3
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/heap-overflow.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-options "-fno-builtin-malloc -fno-builtin-free -fno-builtin-memset" } */
+/* { dg-shouldfail "hwasan" } */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void *memset (void *, int, __SIZE_TYPE__);
+void *malloc (__SIZE_TYPE__);
+void free (void *);
+#ifdef __cplusplus
+}
+#endif
+
+volatile int ten = 10;
+int main(int argc, char **argv) {
+  char *x = (char*)malloc(10);
+  memset(x, 0, 10);
+  int res = x[ten];  /* BOOOM */
+  free(x);
+  return res;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "located 0 bytes to the right of 10-byte region.*" } */
+/* { dg-output "allocated here:.*" } */
+/* { dg-output "#1 0x\[0-9a-f\]+ +in _*main \[^\n\r]*heap-overflow.c:17" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/hwasan-poison-optimisation.c b/gcc/testsuite/c-c++-common/hwasan/hwasan-poison-optimisation.c
new file mode 100644
index 0000000000000000000000000000000000000000..c9743a01c903d7d7f3b28998d5a4f8d761528bd1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/hwasan-poison-optimisation.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+/* { dg-additional-options "-fdump-tree-hwasan1 -save-temps" } */
+
+/* Here to check that the ASAN_POISON stuff works just fine.
+   This mechanism isn't very often used, but I should at least go through the
+   code-path once in my testfile.  */
+int
+main ()
+{
+  int *ptr = 0;
+
+  {
+    int a;
+    ptr = &a;
+    *ptr = 12345;
+  }
+
+  return *ptr;
+}
+
+/* { dg-final { scan-tree-dump-times "ASAN_POISON" 1 "hwasan1" }  } */
+/* { dg-final { scan-assembler-times "bl\\s*__hwasan_tag_mismatch4" 1 } } */
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-access-parent.c b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-access-parent.c
new file mode 100644
index 0000000000000000000000000000000000000000..92a553501f6aceff40b1200670969e57fc3ceea8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-access-parent.c
@@ -0,0 +1,50 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "-lpthread" } */
+
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int printf (char const *, ...);
+#ifdef __cplusplus
+}
+#endif
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+/* Test that tags are checked across different threads.
+   i.e. if this thread tries to access a different threads memory with the
+   incorrect tag, then this thread fails.  */
+void *
+failing_thread_function (void *argument)
+{
+    void * other = (void *)((uint64_t)argument & 0xffffffffffffffULL);
+    int *num = (int*)argument;
+    printf ("(should succeed): first number = %d\n", num[0]);
+    printf ("(now should fail):\n");
+
+    int *othernum = (int*)other;
+    printf (" second number = %d\n", othernum[0]);
+    return (void *)1;
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, failing_thread_function, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    return (uintptr_t)retval;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: 00/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T1.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-basic-failure.c b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-basic-failure.c
new file mode 100644
index 0000000000000000000000000000000000000000..41b31e35c318bbc6be9ddaf1c5adceed6556665c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-basic-failure.c
@@ -0,0 +1,47 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "-lpthread" } */
+
+/* Ensure the failure mode for hwasan under pthreads looks sane.
+   (Looks sane means that the same error message is printed out rather than an
+   opaque message due to mishandling).  */
+
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int printf (char const *, ...);
+#ifdef __cplusplus
+}
+#endif
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+void *
+failing_from_stack (void * argument)
+{
+    int internal_array[16] = {0};
+    printf ("(now should fail):");
+    printf (" problem number is %d\n", internal_array[17]);
+    return (void *)1;
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, failing_from_stack, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    return (uintptr_t)retval;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T1.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T1.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-clears-stack.c b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-clears-stack.c
new file mode 100644
index 0000000000000000000000000000000000000000..a572d1461ff97d7ecbc01622ac69c6f732b564b9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-clears-stack.c
@@ -0,0 +1,55 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "-lpthread" } */
+
+/* This checks the interceptor ABI pthread hooks.
+   The stack of the thread that is finishing must be cleared of shadow tags
+   when that thread exits.  */
+
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int printf (char const *, ...);
+#ifdef __cplusplus
+}
+#endif
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+__attribute__ ((noinline))
+void * Ident (void * argument)
+{
+	return argument;
+}
+
+void *
+pthread_stack_is_cleared (void *argument)
+{
+   (void)argument;
+   int internal_array[16] = {0};
+   return Ident((void*)internal_array);
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, pthread_stack_is_cleared, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    printf ("(should fail): ");
+    printf ("value left in stack is: %d\n", ((int *)retval)[0]);
+
+    return (uintptr_t)retval;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "HWAddressSanitizer can not describe address in more detail\..*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-success.c b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-success.c
new file mode 100644
index 0000000000000000000000000000000000000000..cf8f73495d0da3e734521a488da064f3fe287fcb
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-success.c
@@ -0,0 +1,34 @@
+/* { dg-do run } */
+/* { dg-additional-options "-lpthread" } */
+
+/* Just ensure that a basic threaded program works while running with hwasan.
+   */
+
+#include <pthread.h>
+
+extern int printf (const char *, ...);
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+void *
+successful_thread_function (void * argument)
+{
+    int *deref = (int *)argument;
+    if (deref[0] == 100)
+      deref[1] = 10;
+    return (void *)0;
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, successful_thread_function, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    return (uintptr_t)retval;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/kernel-defaults.c b/gcc/testsuite/c-c++-common/hwasan/kernel-defaults.c
new file mode 100644
index 0000000000000000000000000000000000000000..abfe735e6e1fab712ae8f0bd1517a14a4d5eb178
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/kernel-defaults.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fno-sanitize=hwaddress -fsanitize=kernel-hwaddress" } */
+
+
+/* Defaults to check for kernel-hwaddress.
+   1) No stack tagging => no calls to __hwasan_tag_memory.
+   2) No block scope tagging (same again).
+   3) Use sanitize-recover by default (everything ends in noabort). */
+int __attribute__ ((noinline))
+accessing_pointers (int *left, int *right)
+{
+  int x = right[2];
+  left[3] = right[1];
+  return right[1] + left[2];
+}
+
+int __attribute__ ((noinline))
+using_stack (int num)
+{
+  int big_array[10];
+  int other_array[20];
+  accessing_pointers(other_array, big_array);
+  return big_array[num];
+}
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  using_stack (ARG);
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "__hwasan_tag_memory" } } */
+/* { dg-final { scan-assembler-not "__hwasan_(load|store)\\d(?!_noabort)" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/large-aligned-0.c b/gcc/testsuite/c-c++-common/hwasan/large-aligned-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..4fb547e40c1552f4d17a083d740fadd775f7db8e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/large-aligned-0.c
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+
+/* Handling large aligned variables.
+   Large aligned variables take a different code-path through expand_stack_vars
+   in cfgexpand.c.  This testcase is just to exercise that code-path.
+
+   The alternate code-path produces a second base-pointer through some
+   instructions emitted in the prologue.
+   
+   Test cases are:
+   0) Valid access works without complaint.
+   1) Invalid access is caught.  */
+int __attribute__ ((noinline))
+handle_large_alignment (int num)
+{
+  int other_array[10];
+  int big_array[100] __attribute__ ((aligned (32)));
+  return big_array[num] + other_array[num];
+}
+
+#ifndef ARG
+#define ARG 1
+#endif
+
+int global;
+
+int __attribute__ ((noinline))
+main ()
+{
+  global += handle_large_alignment (ARG);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/large-aligned-1.c b/gcc/testsuite/c-c++-common/hwasan/large-aligned-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..abea810bee9d0418493cca72c80d61dfd61a03d7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/large-aligned-1.c
@@ -0,0 +1,13 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+#define ARG 12
+#include "large-aligned-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* NOTE: This assumes the current tagging mechanism (one at a time from the
+   base and large aligned variables being handled first).  */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/large-aligned-untagging-0.c b/gcc/testsuite/c-c++-common/hwasan/large-aligned-untagging-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..2e20f19f2286b8d93b7b3d46d012ace193413b6b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/large-aligned-untagging-0.c
@@ -0,0 +1,59 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* Don't really need this option since there are no vararray/alloca objects in
+   the interesting function, however it never hurts to make doubly sure and
+   make it explicit that we're checking the alternate approach to deallocation.
+   */
+/* { dg-additional-options "--param hwasan-instrument-allocas=0" } */
+
+/* Handling large aligned variables.
+   Large aligned variables take a different code-path through expand_stack_vars
+   in cfgexpand.c.  This testcase is just to exercise that code-path.
+
+   The alternate code-path produces a second base-pointer through some
+   instructions emitted in the prologue.
+
+   This eventually follows a different code path for untagging when not tagging
+   allocas. The untagging needs to work at the top of the frame, and this
+   should account for this different base when large aligned variables are
+   around.  */
+__attribute__ ((noinline))
+void * Ident (void * argument)
+{
+  return argument;
+}
+
+int* __attribute__ ((noinline))
+large_alignment_untagging (int num)
+{
+  int other_array[100];
+  int big_array[100] __attribute__ ((aligned (32)));
+  if (num % 1)
+    return (int*)Ident(big_array);
+  else
+    return (int*)Ident(other_array);
+}
+
+#ifndef ARG
+#define ARG 1
+#endif
+
+int global;
+
+int __attribute__ ((noinline))
+main ()
+{
+  int *array = large_alignment_untagging (ARG);
+  /* Want to test that both ends of the function are untagged.
+     That means both the start of the large aligned variable and the end of the
+     other variable.  */
+  global += array[ARG ? 99 : 0];
+  return 0;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* NOTE: This assumes the current tagging mechanism (one at a time from the
+   base and large aligned variables being handled first).  */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/large-aligned-untagging-1.c b/gcc/testsuite/c-c++-common/hwasan/large-aligned-untagging-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..952b47e871a88a9eb4c249c892a20382350b0d7b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/large-aligned-untagging-1.c
@@ -0,0 +1,14 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "--param hwasan-instrument-allocas=0" } */
+
+#define ARG 0
+#include "large-aligned-untagging-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* NOTE: This assumes the current tagging mechanism (one at a time from the
+   base and large aligned variables being handled first).  */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/macro-definition.c b/gcc/testsuite/c-c++-common/hwasan/macro-definition.c
new file mode 100644
index 0000000000000000000000000000000000000000..5f654f557821f2dbe060e9976fbca7e5770f274c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/macro-definition.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+extern void testfunc(int);
+int foo()
+{
+#ifndef __SANITIZE_HWADDRESS__
+  testfunc(1);
+#endif
+  return 1;
+}
+
+/* { dg-final { scan-assembler-not "testfunc" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/no-sanitize-attribute.c b/gcc/testsuite/c-c++-common/hwasan/no-sanitize-attribute.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5ff4aec06c8bc6c80fe512061ccc8908e321153
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/no-sanitize-attribute.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+
+__attribute__((no_sanitize_hwaddress)) int
+f1 (int *p, int *q)
+{
+  *p = 42;
+  return *q;
+}
+
+__attribute__((no_sanitize("hwaddress"))) int
+f2 (int *p, int *q)
+{
+  *p = 42;
+  return *q;
+}
+
+/* Only have one instance of __hwasan, it is __hwasan_init (the module
+ * constructor) there is no instrumentation in the functions.  */
+/* { dg-final { scan-assembler-times "__hwasan" 1 } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/param-instrument-reads-and-writes.c b/gcc/testsuite/c-c++-common/hwasan/param-instrument-reads-and-writes.c
new file mode 100644
index 0000000000000000000000000000000000000000..1d565a2f5d959507fc7ba2418637489f7e14e6f0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/param-instrument-reads-and-writes.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-additional-options "--param hwasan-instrument-writes=0" } */
+
+#include "param-instrument-reads.c"
+
+/* { dg-final { scan-assembler "__hwasan_load" } } */
+/* { dg-final { scan-assembler-not "__hwasan_store" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/param-instrument-reads.c b/gcc/testsuite/c-c++-common/hwasan/param-instrument-reads.c
new file mode 100644
index 0000000000000000000000000000000000000000..9b8049a3cb94b166a2b27bbc97f8c26ac7efc6ac
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/param-instrument-reads.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-additional-options "--param hwasan-instrument-reads=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+/* Particular code doesn't really matter, the requirement is that it has both
+   loads and stores in it.  */
+__attribute__ ((noinline))
+int reader (int *array, size_t num)
+{
+  return array[num];
+}
+
+int __attribute__ ((noinline))
+writer (int *array, size_t num, int value)
+{
+  array[num] = value;
+  return num + value;
+}
+
+/* { dg-final { scan-assembler-not "__hwasan_load" } } */
+/* { dg-final { scan-assembler "__hwasan_store" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/param-instrument-writes.c b/gcc/testsuite/c-c++-common/hwasan/param-instrument-writes.c
new file mode 100644
index 0000000000000000000000000000000000000000..0f04fad6496253f31d09e49b49b77decbefe0e60
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/param-instrument-writes.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-additional-options "--param hwasan-instrument-reads=0 --param hwasan-instrument-writes=0" } */
+
+#include "param-instrument-reads.c"
+
+/* { dg-final { scan-assembler-not "__hwasan_load" } } */
+/* { dg-final { scan-assembler-not "__hwasan_store" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/param-memintrin.c b/gcc/testsuite/c-c++-common/hwasan/param-memintrin.c
new file mode 100644
index 0000000000000000000000000000000000000000..e52d6fe7df43256fe4acfe2ecc71c5cdeb36bef4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/param-memintrin.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+/* { dg-additional-options "--param hwasan-memintrin=0" } */
+
+#include "builtin-special-handling.c"
+
+/* With this flag there should be no checking of builtins.
+   The above file only has builtins, and hence there should be no checking
+   after compilation.  */
+/* { dg-final { scan-assembler-not "__hwasan_(load|store)" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/random-frame-tag.c b/gcc/testsuite/c-c++-common/hwasan/random-frame-tag.c
new file mode 100644
index 0000000000000000000000000000000000000000..8e55b298470a49bf5563f26013569ee03487933c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/random-frame-tag.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-additional-options "--param hwasan-random-frame-tag=1" } */
+
+#include "stack-tagging-basic-0.c"
+
+/* Random frame tag => call to __hwasan_generate_tag.  */
+/* { dg-final { scan-assembler "__hwasan_generate_tag" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/sanity-check-pure-c.c b/gcc/testsuite/c-c++-common/hwasan/sanity-check-pure-c.c
new file mode 100644
index 0000000000000000000000000000000000000000..6bba8f8206aa26491d45f66aa51d276c80cfdf6c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/sanity-check-pure-c.c
@@ -0,0 +1,24 @@
+/* { dg-do run } */
+/* { dg-options "-fno-builtin-malloc -fno-builtin-free" } */
+/* { dg-shouldfail "asan" } */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void *malloc (__SIZE_TYPE__);
+void free (void *);
+#ifdef __cplusplus
+}
+#endif
+
+int main() {
+  char *x = (char*)malloc(10);
+  free(x);
+  return x[5];
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "located 5 bytes inside of 10-byte region.*" } */
+/* { dg-output "freed by thread T0 here:.*" } */
+/* { dg-output "previously allocated here:" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/setjmp-longjmp-0.c b/gcc/testsuite/c-c++-common/hwasan/setjmp-longjmp-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..3c7e9c5b3fec8677440b81a2e029427957b359ba
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/setjmp-longjmp-0.c
@@ -0,0 +1,53 @@
+/* { dg-do run } */
+
+#include <setjmp.h>
+#include <stdio.h>
+
+/*
+   Testing longjmp/setjmp should test.
+
+   0) Nothing special happens with the jmp_buf.
+   1) Accesses to scopes jmp'd over are caught.
+ */
+int __attribute__ ((noinline))
+uses_longjmp (int **other_array, int num, jmp_buf env)
+{
+  int internal_array[100] = {0};
+  *other_array = &internal_array[0];
+  if (num % 2)
+    longjmp (env, num);
+  else
+    return num % 8;
+}
+
+int __attribute__ ((noinline))
+uses_setjmp (int num)
+{ 
+  int big_array[100];
+  int *other_array = NULL;
+  sigjmp_buf cur_env;
+  int temp = 0;
+  if ((temp = sigsetjmp (cur_env, 1)) != 0)
+    { 
+      if (other_array != NULL)
+        printf ("Value pointed to in other_array[0]: %d\n",
+                other_array[0]);
+  
+      printf ("Longjmp returned %d.\n", temp);
+      return 10;
+    }
+  else
+    {
+      return uses_longjmp (&other_array, num, cur_env);
+    } 
+} 
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  uses_setjmp (ARG);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/setjmp-longjmp-1.c b/gcc/testsuite/c-c++-common/hwasan/setjmp-longjmp-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..a32b5063e556f0e45e83d657aa312fb4de860442
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/setjmp-longjmp-1.c
@@ -0,0 +1,18 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/*
+   Testing longjmp/setjmp should test.
+
+   0) Nothing special happens with the jmp_buf.
+   1) Accesses to scopes jmp'd over are caught.
+ */
+
+#define ARG 1
+#include "setjmp-longjmp-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/stack-tagging-basic-0.c b/gcc/testsuite/c-c++-common/hwasan/stack-tagging-basic-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..3087bd596f9e56d156686cc37a1a8374fd27c219
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/stack-tagging-basic-0.c
@@ -0,0 +1,36 @@
+/* { dg-do run } */
+
+/* Basic tests for stack tagging.
+
+   0) Valid accesses work.
+   1) Accesses outside of a variable crash.
+*/
+int __attribute__ ((noinline))
+accessing_pointers (int *left, int *right)
+{
+  int x = right[2];
+  left[3] = right[1];
+  return right[1] + left[2];
+}
+
+int __attribute__ ((noinline))
+using_stack (int num)
+{
+  int big_array[10];
+  int other_array[20];
+  accessing_pointers(other_array, big_array);
+  return big_array[num];
+}
+
+#ifndef ARG
+#define ARG 0
+#endif
+
+int global;
+
+int __attribute__ ((noinline))
+main ()
+{
+  global += using_stack (ARG);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/stack-tagging-basic-1.c b/gcc/testsuite/c-c++-common/hwasan/stack-tagging-basic-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..0db6bc4bd220e99bcd695ee93fcc9f4118d09457
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/stack-tagging-basic-1.c
@@ -0,0 +1,17 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/* Basic tests for stack tagging.
+
+   0) Accesses outside of a variable crash.
+   1) Valid accesses work.
+*/
+
+#define ARG 17
+#include "stack-tagging-basic-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/stack-tagging-disable.c b/gcc/testsuite/c-c++-common/hwasan/stack-tagging-disable.c
new file mode 100644
index 0000000000000000000000000000000000000000..c48048988b7ca97507f5305cc2c30af1e8421bee
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/stack-tagging-disable.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-additional-options "--param hwasan-stack=0" } */
+
+
+/* No stack tagging => no calls to __hwasan_tag_memory.  */
+int __attribute__ ((noinline))
+accessing_pointers (int *left, int *right)
+{
+  int x = right[2];
+  left[3] = right[1];
+  return right[1] + left[2];
+}
+
+int __attribute__ ((noinline))
+using_stack (int num)
+{
+  int big_array[10];
+  int other_array[20];
+  accessing_pointers(other_array, big_array);
+  return big_array[num];
+}
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  using_stack (ARG);
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "__hwasan_tag_memory" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/unprotected-allocas-0.c b/gcc/testsuite/c-c++-common/hwasan/unprotected-allocas-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..fc53c389bf64beee3576b119ae8a6144f975c185
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/unprotected-allocas-0.c
@@ -0,0 +1,45 @@
+/* { dg-do run } */
+/* { dg-additional-options "--param hwasan-instrument-allocas=0 -save-temps" } */
+/* Only run this test without optimisation.  When running with optimisation we
+   use the unprotected-allocas-1.c file that also checks there are no memory
+   tagging calls (since when optimised the only variable on the stack should be
+   the vararray/alloca).  */
+/* { dg-skip-if "" { *-*-* } { "-O1" "-O2" "-O3" } { "" } } */
+
+#define alloca __builtin_alloca
+#define assert(x) if (!(x)) __builtin_abort ()
+
+char tag_of (void * x) { return ((unsigned long long)x) >> 56; }
+
+int __attribute__ ((noinline))
+using_alloca (int num)
+{
+  int retval = 0;
+  int *big_array = (int*)alloca (num * sizeof (int));
+  char alloca_tag = tag_of (big_array);
+  assert (alloca_tag == 0);
+  for (int i = 0; i < num; ++i) {
+      retval += big_array[i];
+  }
+  return retval;
+}
+
+int __attribute__ ((noinline))
+using_vararray (int num)
+{
+  int retval = 0;
+  int big_array[num];
+  char vararray_tag = tag_of (big_array);
+  assert (vararray_tag == 0);
+  for (int i = 0; i < num; ++i) {
+      retval += big_array[i];
+  }
+  return retval;
+}
+
+int main()
+{
+  using_alloca (16);
+  using_vararray (12);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/unprotected-allocas-1.c b/gcc/testsuite/c-c++-common/hwasan/unprotected-allocas-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..420901bd6f342cb73f937aec8249f96a6ddf3185
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/unprotected-allocas-1.c
@@ -0,0 +1,15 @@
+/* { dg-do run } */
+/* { dg-additional-options "--param hwasan-instrument-allocas=0 -save-temps" } */
+/* Only test there's no tagging done when not at -O0.  Without optimisation
+   the compiler creates a bunch of other variables on the stack other than the
+   vararray/alloca object.
+   We also avoid checking when using -flto, since with LTO the compiler can
+   recognise the vararray is only used with one size and that size is known at
+   compile time -- when the compiler recognises that it instead creates a
+   static array, which gets tagged as is expected but not as the test expects.
+   */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-flto" } { "" } } */
+
+#include "unprotected-allocas-0.c"
+
+/* { dg-final { scan-assembler-not "__hwasan_tag_memory" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/use-after-free.c b/gcc/testsuite/c-c++-common/hwasan/use-after-free.c
new file mode 100644
index 0000000000000000000000000000000000000000..1a49ee7f6f648f158fcca52d42bdba264b0e7dad
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/use-after-free.c
@@ -0,0 +1,27 @@
+/* { dg-do run } */
+/* { dg-options "-fno-builtin-malloc -fno-builtin-free" } */
+/* { dg-shouldfail "hwasan" } */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void *malloc (__SIZE_TYPE__);
+void free (void *);
+#ifdef __cplusplus
+}
+#endif
+
+int main() {
+  char *x = (char*)malloc(10);
+  free(x);
+  return x[5];
+}
+
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "is located 5 bytes inside of 10-byte region.*" } */
+/* { dg-output "freed by thread T0 here:.*" } */
+/* { dg-output "#1\[^\n\r]*main\[^\n\r]*use-after-free.c:16.*" } */
+/* { dg-output "previously allocated here:.*" } */
+/* { dg-output "#1\[^\n\r]*main\[^\n\r]*use-after-free.c:15" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/vararray-outside-caught.c b/gcc/testsuite/c-c++-common/hwasan/vararray-outside-caught.c
new file mode 100644
index 0000000000000000000000000000000000000000..858bfcd7e596a90c6bd1d68667259a735bb4dce7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/vararray-outside-caught.c
@@ -0,0 +1,21 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+int __attribute__ ((noinline))
+check_vararray (int num)
+{
+  int var_array[num];
+  int other_array[10];
+  return var_array[12];
+}
+
+int __attribute__ ((noinline))
+main ()
+{
+  return check_vararray (3);
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/vararray-stack-restore-correct.c b/gcc/testsuite/c-c++-common/hwasan/vararray-stack-restore-correct.c
new file mode 100644
index 0000000000000000000000000000000000000000..45c7d1805955c190ddf07c3bc4dad9adf108f646
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/vararray-stack-restore-correct.c
@@ -0,0 +1,42 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+#include <stdio.h>
+
+/* Testing that a function with outgoing arguments correctly decrements the
+   stack pointer when a vararray goes out of scope.  */
+
+const char *
+other (int argc, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l)
+{
+  const char ** other;
+    {
+      const char * test_array[argc];
+      test_array[0] = "test string";
+      test_array[argc - 1] = "hello";
+      /* To prevent optimisation.  */
+      printf("While the value stored in our test_array is: %s\n",
+	     test_array[argc - 1]);
+      other = test_array;
+    }
+  /* With the below function call (the one with many arguments), some of the
+     arguments have to be put on the stack, which means we have to reserve some
+     space on the stack for these arguments and that the VLA is stored at a
+     position that is not the stack pointer. */
+  printf("Hello there!\nOur numbers today are: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",
+	 a, b, c, d, e, f, g, h, i, j, k, l);
+  /* This should fail due to a bad read access.  */
+  return other[0];
+}
+
+int
+main ()
+{
+  int a, b, c, d, e, f, g, h, i, j, k, l;
+  const char * retval = other (1, a, b, c, d, e, f, g, h, i, j, k, l);
+  /* Numbers don't matter here, just want to ensure the program is reading them
+     so we know they won't be optimised out.  */
+  if (retval)
+    return 1;
+  return 10;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/very-large-objects.c b/gcc/testsuite/c-c++-common/hwasan/very-large-objects.c
new file mode 100644
index 0000000000000000000000000000000000000000..55265353369540872e8fba4da99d9be92a7ad99b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/very-large-objects.c
@@ -0,0 +1,68 @@
+/* { dg-do compile } */
+
+/* Ensure the sanitizer can handle very large offsets (i.e. that the hooks
+   handle offsets too large for the relevant instructions).
+   Just want to make sure this compiles without an ICE.  */
+#ifndef ASIZE
+# define ASIZE 0x10000000000UL
+#endif
+
+typedef __UINT64_TYPE__ uint64_t;
+
+#if __LONG_MAX__ < 8 * ASIZE
+# undef ASIZE
+# define ASIZE 4096
+#endif
+
+extern void abort (void);
+
+int __attribute__((noinline))
+foo (const char *s)
+{
+  if (!s)
+    return 1;
+  if (s[0] != 'a')
+    abort ();
+  s += ASIZE - 1;
+  if (s[0] != 'b')
+    abort ();
+  return 0;
+}
+
+int (*fn) (const char *) = foo;
+
+int __attribute__((noinline))
+bar (void)
+{
+  char s[ASIZE];
+  s[0] = 'a';
+  s[ASIZE - 1] = 'b';
+  foo (s);
+  foo (s);
+  return 0;
+}
+
+int __attribute__((noinline))
+baz (long i)
+{
+  if (i)
+    return fn (0);
+  else
+    {
+      char s[ASIZE];
+      s[0] = 'a';
+      s[ASIZE - 1] = 'b';
+      foo (s);
+      foo (s);
+      return fn (0);
+    }
+}
+
+int __attribute__((noinline))
+very_large_offset (int *p)
+{
+  char init_array[(uint64_t)0xfefefef];
+  char other_array[(uint64_t)0xfefefef];
+  return (int)init_array[p[1]] + (int)other_array[p[0]];
+}
+
diff --git a/gcc/testsuite/g++.dg/hwasan/hwasan.exp b/gcc/testsuite/g++.dg/hwasan/hwasan.exp
new file mode 100644
index 0000000000000000000000000000000000000000..559cf066f18cc82601661a751bf702303c66c888
--- /dev/null
+++ b/gcc/testsuite/g++.dg/hwasan/hwasan.exp
@@ -0,0 +1,34 @@
+# Copyright (C) 2012-2019 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Load support procs.
+load_lib g++-dg.exp
+load_lib hwasan-dg.exp
+
+# Initialize `dg'.
+dg-init
+hwasan_init
+
+# Main loop.
+if [check_effective_target_fsanitize_hwaddress] {
+  gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C $srcdir/c-c++-common/hwasan/*.c]] "" ""
+}
+
+# All done.
+hwasan_finish
+dg-finish
diff --git a/gcc/testsuite/g++.dg/hwasan/rvo-handled.C b/gcc/testsuite/g++.dg/hwasan/rvo-handled.C
new file mode 100644
index 0000000000000000000000000000000000000000..78f127f953260982807fd477a3c6f61ecdd864ed
--- /dev/null
+++ b/gcc/testsuite/g++.dg/hwasan/rvo-handled.C
@@ -0,0 +1,45 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+
+#define assert(x) if (!(x)) __builtin_abort ()
+typedef __UINTPTR_TYPE__ uintptr_t;
+void *untagged (void *ptr)
+{
+  /* Untag by removing the top byte.  */
+  return (void*)((uintptr_t)ptr & 0xffffffffffffff);
+}
+
+struct big_struct {
+    int left;
+    int right;
+    void *ptr;
+    int big_array[100];
+};
+
+/*
+   Tests for RVO (basically, checking -fsanitize=hwaddress has not broken RVO
+   in any way).
+
+   0) The value is accessible in both functions without a hwasan complaint.
+   1) RVO does happen.
+ */
+
+struct big_struct __attribute__ ((noinline))
+return_on_stack()
+{
+  struct big_struct x;
+  x.left = 100;
+  x.right = 20;
+  x.big_array[10] = 30;
+  x.ptr = untagged(&x);
+  return x;
+}
+
+int main()
+{
+  struct big_struct x;
+  x = return_on_stack();
+  /* Check that RVO happens by checking the address that the callee saw.  */
+  assert (x.ptr == untagged(&x));
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/hwasan/try-catch-0.cpp b/gcc/testsuite/g++.dg/hwasan/try-catch-0.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d5bbc93564c7f32a029be1a1e071f44188c91da5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/hwasan/try-catch-0.cpp
@@ -0,0 +1,65 @@
+/* { dg-do run } */
+
+/* This version should work just fine.  */
+#include <stdexcept>
+
+char *intermediate_pointer = NULL;
+static void optimization_barrier(void* arg) {
+  asm volatile("" : : "r"(arg) : "memory");
+}
+
+__attribute__((noinline))
+void h() {
+  char x[1000];
+  intermediate_pointer = (void *)&x;
+  optimization_barrier(x);
+  throw std::runtime_error("hello");
+}
+
+__attribute__((noinline))
+void g() {
+  char x[1000];
+  optimization_barrier(x);
+  h();
+  optimization_barrier(x);
+}
+
+__attribute__((noinline))
+void hwasan_read(char *p, int size) {
+  char volatile sink;
+  for (int i = 0; i < size; ++i)
+    sink = p[i];
+}
+
+__attribute__((noinline, no_sanitize("hwaddress"))) void after_catch() {
+  char x[10000];
+  hwasan_read(&x[0], sizeof(x));
+}
+
+
+__attribute__((noinline))
+void f() {
+  char x[1000];
+  try {
+    // Put two tagged frames on the stack, throw an exception from the deepest one.
+    g();
+  } catch (const std::runtime_error &e) {
+    // Put an untagged frame on stack, check that it is indeed untagged.
+    // This relies on exception support zeroing out stack tags.
+    // BAD: tag-mismatch
+    after_catch();
+    // Check that an in-scope stack allocation is still tagged.
+    // This relies on exception support not zeroing too much.
+    hwasan_read(&x[0], sizeof(x));
+#ifdef CLEARED_ACCESS_CATCH
+    return (int)(intermediate_pointer[1]);
+#else
+    return 0;
+#endif
+  }
+  __builtin_abort ();
+}
+
+int main() {
+  f();
+}
diff --git a/gcc/testsuite/g++.dg/hwasan/try-catch-1.cpp b/gcc/testsuite/g++.dg/hwasan/try-catch-1.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b124d29d33613794f569646b932b42c958b52348
--- /dev/null
+++ b/gcc/testsuite/g++.dg/hwasan/try-catch-1.cpp
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+
+/* This version should catch the invalid access.  */
+#define CLEARED_ACCESS_CATCH
+#include "try-catch-0.cpp"
+#undef CLEARED_ACCESS_CATCH
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan.exp b/gcc/testsuite/gcc.dg/hwasan/hwasan.exp
new file mode 100644
index 0000000000000000000000000000000000000000..5c040aec24a222e4024a548e055c52cee4afe8f8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan.exp
@@ -0,0 +1,36 @@
+# Copyright (C) 2012-2019 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+load_lib hwasan-dg.exp
+
+# Initialize `dg'.
+dg-init
+hwasan_init
+
+# Main loop.
+if [check_effective_target_fsanitize_hwaddress] {
+  gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c $srcdir/c-c++-common/hwasan/*.c]] "" ""
+}
+
+# All done.
+hwasan_finish
+dg-finish
diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..22bcef57c7965fadb5b41b741bc7089df9eb0efc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c
@@ -0,0 +1,52 @@
+/* { dg-do run } */
+
+/*
+   Tests of nested funtions are:
+    0) Accessing closed over variables works.
+    1) Accesses outside of variables is caught.
+    2) Accessing variable out of scope is caught.
+
+    Here we test that accessing closed over variables works.
+ */
+
+/* We need a second layer of indirection so that GCC doesn't notice we're
+   returning the address of a local variable and put 0 in it's place.  */
+__attribute__((noinline))
+int *Ident(void *x) {
+  return x;
+}
+
+int __attribute__ ((noinline))
+intermediate (void (*f) (int, char),
+	      char num)
+{
+  if (num == 1)
+    /* NOTE: We need to overrun by an amount greater than the "extra data" in a
+       nonlocal goto structure.  The entire structure is allocated on the stack
+       with a single tag, which means hwasan can't tell if a closed-over buffer
+       was overrun by an amount small enough that the access was still to some
+       data in that nonlocal goto structure.  */
+    f (100, 100);
+  else
+    f (3, 100);
+  /* Just return something ... */
+  return num % 3;
+}
+
+int* __attribute__ ((noinline))
+nested_function (char num)
+{
+  int big_array[16];
+  int other_array[16];
+  void store (int index, char value)
+    { big_array[index] = value; }
+  return Ident(&other_array[intermediate (store, num)]);
+}
+
+#ifndef MAIN
+int main ()
+{
+  nested_function (0);
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..7d5e80e8a9cb6a1e5a2fb87f78f140c37cd88841
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c
@@ -0,0 +1,26 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/*
+   Tests of nested funtions are:
+    0) Accessing closed over variables works.
+    1) Accesses outside of variables is caught.
+    2) Accessing variable out of scope is caught.
+
+    Here we test option 1.
+ */
+
+#define MAIN 0
+#include "nested-functions-0.c"
+#undef MAIN
+
+int main ()
+{
+  nested_function (1);
+  return 0;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "WRITE of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..be7942d4fb1bf0dec657396e0bd3ca2f43b78b36
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c
@@ -0,0 +1,27 @@
+/* { dg-do run } */
+/* { dg-shouldfail "hwasan" } */
+
+/*
+   Tests of nested funtions are:
+    0) Accessing closed over variables works.
+    1) Accesses outside of variables is caught.
+    2) Accessing variable out of scope is caught.
+
+    Here we test option 2.
+ */
+
+#define MAIN 0
+#include "nested-functions-0.c"
+#undef MAIN
+
+int main ()
+{
+  int *retval = nested_function (2);
+  *retval = 100;
+  return 0;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "WRITE of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/lib/hwasan-dg.exp b/gcc/testsuite/lib/hwasan-dg.exp
new file mode 100644
index 0000000000000000000000000000000000000000..86d2753e4143444d61aa03d53bfafb0a0986d77e
--- /dev/null
+++ b/gcc/testsuite/lib/hwasan-dg.exp
@@ -0,0 +1,356 @@
+# Copyright (C) 2012-2019 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Return 1 if compilation with -fsanitize=hwaddress is error-free for trivial
+# code, 0 otherwise.
+
+proc check_effective_target_fsanitize_hwaddress {} {
+    if ![check_no_compiler_messages fsanitize_hwaddress executable {
+	int main (void) { return 0; }
+    }] {
+	return 0;
+    }
+
+    # hwasan doesn't work if there's a ulimit on virtual memory.
+    if ![is_remote target] {
+	if [catch {exec sh -c "ulimit -v"} ulimit_v] {
+	    # failed to get ulimit
+	} elseif [regexp {^[0-9]+$} $ulimit_v] {
+	    # ulimit -v gave a numeric limit
+	    warning "skipping hwasan tests due to ulimit -v"
+	    return 0;
+	}
+    }
+
+    return 1;
+}
+
+proc hwasan_include_flags {} {
+    global srcdir
+    global TESTING_IN_BUILD_TREE
+
+    set flags ""
+
+    if { [is_remote host] || ! [info exists TESTING_IN_BUILD_TREE] } {
+      return "${flags}"
+    }
+
+    set flags "-I$srcdir/../../libsanitizer/include"
+
+    return "$flags"
+}
+
+#
+# hwasan_link_flags -- compute library path and flags to find libhwasan.
+# (originally from g++.exp)
+#
+
+proc hwasan_link_flags { paths } {
+    global srcdir
+    global ld_library_path
+    global shlib_ext
+    global hwasan_saved_library_path
+
+    set gccpath ${paths}
+    set flags ""
+
+    set shlib_ext [get_shlib_extension]
+    set hwasan_saved_library_path $ld_library_path
+
+    if { $gccpath != "" } {
+      if { [file exists "${gccpath}/libsanitizer/hwasan/.libs/libhwasan.a"]
+	   || [file exists "${gccpath}/libsanitizer/hwasan/.libs/libhwasan.${shlib_ext}"] } {
+	  append flags " -B${gccpath}/libsanitizer/ "
+	  append flags " -B${gccpath}/libsanitizer/hwasan/ "
+	  append flags " -L${gccpath}/libsanitizer/hwasan/.libs "
+	  append ld_library_path ":${gccpath}/libsanitizer/hwasan/.libs"
+      }
+    } else {
+      global tool_root_dir
+
+      set libhwasan [lookfor_file ${tool_root_dir} libhwasan]
+      if { $libhwasan != "" } {
+	  append flags "-L${libhwasan} "
+	  append ld_library_path ":${libhwasan}"
+      }
+    }
+
+    set_ld_library_path_env_vars
+
+    return "$flags"
+}
+
+#
+# hwasan_init -- called at the start of each subdir of tests
+#
+
+proc hwasan_init { args } {
+    global TEST_ALWAYS_FLAGS
+    global ALWAYS_CXXFLAGS
+    global TOOL_OPTIONS
+    global hwasan_saved_TEST_ALWAYS_FLAGS
+    global hwasan_saved_ALWAYS_CXXFLAGS
+
+    setenv HWASAN_OPTIONS "random_tags=0"
+
+    set link_flags ""
+    if ![is_remote host] {
+	if [info exists TOOL_OPTIONS] {
+	    set link_flags "[hwasan_link_flags [get_multilibs ${TOOL_OPTIONS}]]"
+	} else {
+	    set link_flags "[hwasan_link_flags [get_multilibs]]"
+	}
+    }
+
+    set include_flags "[hwasan_include_flags]"
+
+    if [info exists TEST_ALWAYS_FLAGS] {
+	set hwasan_saved_TEST_ALWAYS_FLAGS $TEST_ALWAYS_FLAGS
+    }
+    if [info exists ALWAYS_CXXFLAGS] {
+	set hwasan_saved_ALWAYS_CXXFLAGS $ALWAYS_CXXFLAGS
+	set ALWAYS_CXXFLAGS [concat "{ldflags=$link_flags}" $ALWAYS_CXXFLAGS]
+	set ALWAYS_CXXFLAGS [concat "{additional_flags=-fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags}" $ALWAYS_CXXFLAGS]
+    } else {
+	if [info exists TEST_ALWAYS_FLAGS] {
+	    set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags $TEST_ALWAYS_FLAGS"
+	} else {
+	    set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags"
+	}
+    }
+}
+
+#
+# hwasan_finish -- called at the start of each subdir of tests
+#
+
+proc hwasan_finish { args } {
+    global TEST_ALWAYS_FLAGS
+    global hwasan_saved_TEST_ALWAYS_FLAGS
+    global hwasan_saved_ALWAYS_CXXFLAGS
+    global hwasan_saved_library_path
+    global ld_library_path
+
+    unsetenv HWASAN_OPTIONS
+
+    if [info exists hwasan_saved_ALWAYS_CXXFLAGS ] {
+	set ALWAYS_CXXFLAGS $hwasan_saved_ALWAYS_CXXFLAGS
+    } else {
+	if [info exists hwasan_saved_TEST_ALWAYS_FLAGS] {
+	    set TEST_ALWAYS_FLAGS $hwasan_saved_TEST_ALWAYS_FLAGS
+	} else {
+	    unset TEST_ALWAYS_FLAGS
+	}
+    }
+    set ld_library_path $hwasan_saved_library_path
+    set_ld_library_path_env_vars
+    clear_effective_target_cache
+}
+
+# Symbolize lines like
+#   #2 0xdeadbeef (/some/path/libsanitizer.so.0.0.0+0xbeef)
+# in $output using addr2line to
+#   #2 0xdeadbeef in foobar file:123
+proc hwasan_symbolize { output } {
+    set addresses [regexp -inline -all -line "^ *#\[0-9\]+ 0x\[0-9a-f\]+ \[(\](\[^)\]+)\[+\](0x\[0-9a-f\]+)\[)\]$" "$output"]
+    if { [llength $addresses] > 0 } {
+	set addr2line_name [find_binutils_prog addr2line]
+	set idx 1
+	while { $idx < [llength $addresses] } {
+	    set key [regsub -all "\[\]\[\]" [lindex $addresses $idx] "\\\\&"]
+	    set val [lindex $addresses [expr $idx + 1]]
+	    lappend arr($key) $val
+	    set idx [expr $idx + 3]
+	}
+	foreach key [array names arr] {
+	    set args "-f -e $key $arr($key)"
+	    set status [remote_exec host "$addr2line_name" "$args"]
+	    if { [lindex $status 0] > 0 } continue
+	    regsub -all "\r\n" [lindex $status 1] "\n" addr2line_output
+	    regsub -all "\[\n\r\]BFD: \[^\n\r\]*" $addr2line_output "" addr2line_output
+	    regsub -all "^BFD: \[^\n\r\]*\[\n\r\]" $addr2line_output "" addr2line_output
+	    set addr2line_output [regexp -inline -all -line "^\[^\n\r]*" $addr2line_output]
+	    set idx 0
+	    foreach val $arr($key) {
+		if { [expr $idx + 1] < [llength $addr2line_output] } {
+		    set fnname [lindex $addr2line_output $idx]
+		    set fileline [lindex $addr2line_output [expr $idx + 1]]
+		    if { "$fnname" != "??" } {
+			set newkey "$key+$val"
+			set repl($newkey) "$fnname $fileline"
+		    }
+		    set idx [expr $idx + 2]
+		}
+	    }
+	}
+	set idx 0
+	set new_output ""
+	while {[regexp -start $idx -indices " #\[0-9\]+ 0x\[0-9a-f\]+ \[(\](\[^)\]+\[+\]0x\[0-9a-f\]+)\[)\]" "$output" -> addr] > 0} {
+	    set low [lindex $addr 0]
+	    set high [lindex $addr 1]
+	    set val [string range "$output" $low $high]
+	    append new_output [string range "$output" $idx [expr $low - 2]]
+	    if [info exists repl($val)] {
+		append new_output "in $repl($val)"
+	    } else {
+		append new_output "($val)"
+	    }
+	    set idx [expr $high + 2]
+	}
+	append new_output [string range "$output" $idx [string length "$output"]]
+	return "$new_output"
+    }
+    return "$output"
+}
+
+# Return a list of gtest tests, printed in the form
+# DEJAGNU_GTEST_TEST AddressSanitizer_SimpleDeathTest
+# DEJAGNU_GTEST_TEST AddressSanitizer_VariousMallocsTest
+proc hwasan_get_gtest_test_list { output } {
+    set idx 0
+    set ret ""
+    while {[regexp -start $idx -indices "DEJAGNU_GTEST_TEST (\[^\n\r\]*)(\r\n|\n|\r)" "$output" -> testname] > 0} {
+	set low [lindex $testname 0]
+	set high [lindex $testname 1]
+	set val [string range "$output" $low $high]
+	lappend ret $val
+	set idx [expr $high + 1]
+    }
+    return $ret
+}
+
+# Return a list of gtest EXPECT_DEATH tests, printed in the form
+# DEJAGNU_GTEST_EXPECT_DEATH1 statement DEJAGNU_GTEST_EXPECT_DEATH1 regexp DEJAGNU_GTEST_EXPECT_DEATH1
+# DEJAGNU_GTEST_EXPECT_DEATH2 other statement DEJAGNU_GTEST_EXPECT_DEATH2 other regexp DEJAGNU_GTEST_EXPECT_DEATH2
+proc hwasan_get_gtest_expect_death_list { output } {
+    set idx 0
+    set ret ""
+    while {[regexp -start $idx -indices "DEJAGNU_GTEST_EXPECT_DEATH(\[0-9\]*)" "$output" -> id ] > 0} {
+	set low [lindex $id 0]
+	set high [lindex $id 1]
+	set val_id [string range "$output" $low $high]
+	if {[regexp -start $low -indices "$val_id (.*) DEJAGNU_GTEST_EXPECT_DEATH$val_id (.*) DEJAGNU_GTEST_EXPECT_DEATH$val_id\[\n\r\]" "$output" whole statement regexpr ] == 0} { break }
+	set low [lindex $statement 0]
+	set high [lindex $statement 1]
+	set val_statement [string range "$output" $low $high]
+	set low [lindex $regexpr 0]
+	set high [lindex $regexpr 1]
+	set val_regexpr [string range "$output" $low $high]
+	lappend ret [list "$val_id" "$val_statement" "$val_regexpr"]
+	set idx [lindex $whole 1]
+    }
+    return $ret
+}
+
+# Replace ${tool}_load with a wrapper so that we can symbolize the output.
+if { [info procs ${tool}_load] != [list] \
+      && [info procs saved_hwasan_${tool}_load] == [list] } {
+    rename ${tool}_load saved_hwasan_${tool}_load
+
+    proc ${tool}_load { program args } {
+	global tool
+	global hwasan_last_gtest_test_list
+	global hwasan_last_gtest_expect_death_list
+	set result [eval [list saved_hwasan_${tool}_load $program] $args]
+	set output [lindex $result 1]
+	set symbolized_output [hwasan_symbolize "$output"]
+	set hwasan_last_gtest_test_list [hwasan_get_gtest_test_list "$output"]
+	set hwasan_last_gtest_expect_death_list [hwasan_get_gtest_expect_death_list "$output"]
+	set result [list [lindex $result 0] $symbolized_output]
+	return $result
+    }
+}
+
+# Utility for running gtest hwasan emulation under dejagnu, invoked via dg-final.
+# Call pass if variable has the desired value, otherwise fail.
+#
+# Argument 0 handles expected failures and the like
+proc hwasan-gtest { args } {
+    global tool
+    global hwasan_last_gtest_test_list
+    global hwasan_last_gtest_expect_death_list
+
+    if { ![info exists hwasan_last_gtest_test_list] } { return }
+    if { [llength $hwasan_last_gtest_test_list] == 0 } { return }
+    if { ![isnative] || [is_remote target] } { return }
+
+    set gtest_test_list $hwasan_last_gtest_test_list
+    unset hwasan_last_gtest_test_list
+
+    if { [llength $args] >= 1 } {
+	switch [dg-process-target [lindex $args 0]] {
+	    "S" { }
+	    "N" { return }
+	    "F" { setup_xfail "*-*-*" }
+	    "P" { }
+	}
+    }
+
+    # This assumes that we are three frames down from dg-test, and that
+    # it still stores the filename of the testcase in a local variable "name".
+    # A cleaner solution would require a new DejaGnu release.
+    upvar 2 name testcase
+    upvar 2 prog prog
+
+    set output_file "[file rootname [file tail $prog]].exe"
+
+    foreach gtest $gtest_test_list {
+	set testname "$testcase $gtest"
+	set status -1
+
+	setenv DEJAGNU_GTEST_ARG "$gtest"
+	set result [${tool}_load ./$output_file $gtest]
+	unsetenv DEJAGNU_GTEST_ARG
+	set status [lindex $result 0]
+	set output [lindex $result 1]
+	if { "$status" == "pass" } {
+	    pass "$testname execution test"
+	    if { [info exists hwasan_last_gtest_expect_death_list] } {
+		set gtest_expect_death_list $hwasan_last_gtest_expect_death_list
+		foreach gtest_death $gtest_expect_death_list {
+		    set id [lindex $gtest_death 0]
+		    set testname "$testcase $gtest [lindex $gtest_death 1]"
+		    set regexpr [lindex $gtest_death 2]
+		    set status -1
+
+		    setenv DEJAGNU_GTEST_ARG "$gtest:$id"
+		    set result [${tool}_load ./$output_file "$gtest:$id"]
+		    unsetenv DEJAGNU_GTEST_ARG
+		    set status [lindex $result 0]
+		    set output [lindex $result 1]
+		    if { "$status" == "fail" } {
+			pass "$testname execution test"
+			if { ![regexp $regexpr ${output}] } {
+			    fail "$testname output pattern test"
+			    send_log "Output should match: $regexpr\n"
+			} else {
+			    pass "$testname output pattern test"
+			}
+		    } elseif { "$status" == "pass" } {
+			fail "$testname execution test"
+		    } else {
+			$status "$testname execution test"
+		    }
+		}
+	    }
+	} else {
+	    $status "$testname execution test"
+	}
+	unset hwasan_last_gtest_expect_death_list
+    }
+
+    return
+}


[-- Attachment #2: hwasan-patch6.patch.gz --]
[-- Type: application/gzip, Size: 15813 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 4/X] [libsanitizer][options] Add hwasan flags and argument parsing
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
  2019-12-12 15:19     ` [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
@ 2019-12-12 15:19     ` Matthew Malcomson
  2020-01-07 15:04       ` Martin Liška
  2019-12-12 15:19     ` [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes Matthew Malcomson
                       ` (16 subsequent siblings)
  18 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-12-12 15:19 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 22997 bytes --]

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

The -fsanitize=kernel-hwaddress option is for compiling targeting the
kernel.  This flag has defaults that allow compiling KASAN with tags as
it is currently implemented.
These defaults are that we do not sanitize variables on the stack and
always recover from a detected bug.
Stack tagging in the kernel is a future aim, stack instrumentation has
not yet been enabled for the kernel for clang either
(https://lists.infradead.org/pipermail/linux-arm-kernel/2019-October/687121.html).

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

gcc/ChangeLog:

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

	* 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.opt (hwasan-stack): New
	(hwasan-random-frame-tag): New
	(hwasan-instrument-allocas): New
	(hwasan-instrument-reads): New
	(hwasan-instrument-writes): New
	(hwasan-memintrin): 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-12-12  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 dc56e2ec62ffc7f494cc59ddf02452ac0cb406de..81f8dfc6dd3f54067d7cc0def3c0babc03bcd9c4 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 b4dc31c7490dbed706a7101358115d12fccde5a3..38c434e60a01d6883d02da125078f11b6f479f5b 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -212,7 +212,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
@@ -3317,6 +3317,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 a3b18b381e1748f8fe5e522bdec4f7c850821fe8..7d0a445fba45b0c83d561da3a0b32a26fe86672f 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -21653,6 +21653,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
@@ -22223,6 +22232,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_FNTYPE_ABI
 #define TARGET_FNTYPE_ABI aarch64_fntype_abi
 
+#undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
+#define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h
index 95a3c29f7cee86336f958bef1d7fe56b82e05e6c..90b1fa91742c6a7d76aa6c7e931f8014fc4fff0c 100644
--- a/gcc/config/gnu-user.h
+++ b/gcc/config/gnu-user.h
@@ -129,14 +129,18 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Link -lasan early on the command line.  For -static-libasan, don't link
    it for -shared link, the executable should be compiled with -static-libasan
    in that case, and for executable link with --{,no-}whole-archive around
-   it to force everything into the executable.  And similarly for -ltsan
-   and -llsan.  */
+   it to force everything into the executable.  And similarly for -ltsan,
+   -lhwasan, and -llsan.  */
 #if defined(HAVE_LD_STATIC_DYNAMIC)
 #undef LIBASAN_EARLY_SPEC
 #define LIBASAN_EARLY_SPEC "%{!shared:libasan_preinit%O%s} " \
   "%{static-libasan:%{!shared:" \
   LD_STATIC_OPTION " --whole-archive -lasan --no-whole-archive " \
   LD_DYNAMIC_OPTION "}}%{!static-libasan:-lasan}"
+#undef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC "%{static-libhwasan:%{!shared:" \
+  LD_STATIC_OPTION " --whole-archive -lhwasan --no-whole-archive " \
+  LD_DYNAMIC_OPTION "}}%{!static-libhwasan:-lhwasan}"
 #undef LIBTSAN_EARLY_SPEC
 #define LIBTSAN_EARLY_SPEC "%{!shared:libtsan_preinit%O%s} " \
   "%{static-libtsan:%{!shared:" \
diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c
index 60e5bedc3665a25fa51c2eca00547f12a9953778..e8d0bedfc2eb22d1e72e7e4875155202c8389a38 100644
--- a/gcc/cppbuiltin.c
+++ b/gcc/cppbuiltin.c
@@ -93,6 +93,9 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile)
   if (flag_sanitize & SANITIZE_ADDRESS)
     cpp_define (pfile, "__SANITIZE_ADDRESS__");
 
+  if (flag_sanitize & SANITIZE_HWADDRESS)
+    cpp_define (pfile, "__SANITIZE_HWADDRESS__");
+
   if (flag_sanitize & SANITIZE_THREAD)
     cpp_define (pfile, "__SANITIZE_THREAD__");
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index af3c7f2b9109d4997d6313ca9a90b6fec9f5ccd7..97b4869b139805b633ad877e80bb6968f59b9206 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -12659,13 +12659,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 5b8b68bd710fd591bfe7a07e2d113d39178bfad7..38261cd1cb3fdeb9a3ecf42a6b526487f412bd76 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2970,6 +2970,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 1b061d7012792ac8f7984118efd03ef21280a3b6..ac5d9b0879c1909709b85b2aaf25017a7b5e3a26 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2373,6 +2373,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 0c23aadefed3d41797586763d4732a46fba0418e..ba7016b909e33a687f52058be5d336503ca3e86c 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 4428d50c3906b912f7d7cbb0391754008076cf21..97bdf73dadb3aff13561c07a9eb217a5d8e1d2d3 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -707,6 +707,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)}"
@@ -981,6 +999,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
@@ -990,6 +1009,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 3ddad551e6fc4741c787c38eded502a60a1d51da..8a47ac09df0cf31d73e5718e889e7759236d9f75 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1094,6 +1094,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))
@@ -1113,6 +1120,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)
@@ -1130,9 +1151,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);
 
@@ -1711,8 +1733,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),
@@ -2237,6 +2264,12 @@ 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_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 d88ae0c468b57975f4ed348243bfc64b63912542..8f43e7ea93dfb2974501195b83d916d7c22b9e4a 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
 Enable asan detection of use-after-return bugs.
 
+-param=hwasan-stack=
+Common Joined UInteger Var(param_hwasan_stack) Init(1) IntegerRange(0, 1) Param
+Enable hwasan stack protection.
+
+-param=hwasan-random-frame-tag=
+Common Joined UInteger Var(param_hwasan_random_frame_tag) Init(1) IntegerRange(0, 1) Param
+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
+Enable hwasan allocas/VLAs protection.
+
+-param=hwasan-instrument-reads=
+Common Joined UInteger Var(param_hwasan_instrument_reads) Init(1) IntegerRange(0, 1) Param
+Enable hwasan load operations protection.
+
+-param=hwasan-instrument-writes=
+Common Joined UInteger Var(param_hwasan_instrument_writes) Init(1) IntegerRange(0, 1) Param
+Enable hwasan store operations protection.
+
+-param=hwasan-memintrin=
+Common Joined UInteger Var(param_hwasan_memintrin) Init(1) IntegerRange(0, 1) Param
+Enable hwasan builtin functions protection.
+
 -param=avg-loop-niter=
 Common Joined UInteger Var(param_avg_loop_niter) Init(10) IntegerRange(1, 65536) Param
 Average number of iterations of a loop.
diff --git a/gcc/target.def b/gcc/target.def
index e0e856979a988a5aee005611f97e35bf06ad6c69..313ad34f39d4c6b835c405e4c3051c82840cc588 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6787,6 +6787,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 34987bbcdb3fcac9818990fc67ebf0d8199d312d..bba7acc0dbe194cb880d4677e029d772e9191450 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -283,4 +283,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 2d3bcbf3e89a38e753b15592d5c66b130ab96049..7f2b1ed8493917573f383084abbb25962fa9a893 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2364,4 +2364,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 059046f40f38cf5b340a7d7be02a438e0a16ac00..9c6c51aa57b0579738374661dcdea4373daf653d 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1824,6 +1824,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.  */


[-- Attachment #2: hwasan-patch3.patch --]
[-- Type: text/plain, Size: 19905 bytes --]

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index dc56e2ec62ffc7f494cc59ddf02452ac0cb406de..81f8dfc6dd3f54067d7cc0def3c0babc03bcd9c4 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 b4dc31c7490dbed706a7101358115d12fccde5a3..38c434e60a01d6883d02da125078f11b6f479f5b 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -212,7 +212,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
@@ -3317,6 +3317,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 a3b18b381e1748f8fe5e522bdec4f7c850821fe8..7d0a445fba45b0c83d561da3a0b32a26fe86672f 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -21653,6 +21653,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
@@ -22223,6 +22232,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_FNTYPE_ABI
 #define TARGET_FNTYPE_ABI aarch64_fntype_abi
 
+#undef TARGET_MEMTAG_CAN_TAG_ADDRESSES
+#define TARGET_MEMTAG_CAN_TAG_ADDRESSES aarch64_can_tag_addresses
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h
index 95a3c29f7cee86336f958bef1d7fe56b82e05e6c..90b1fa91742c6a7d76aa6c7e931f8014fc4fff0c 100644
--- a/gcc/config/gnu-user.h
+++ b/gcc/config/gnu-user.h
@@ -129,14 +129,18 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Link -lasan early on the command line.  For -static-libasan, don't link
    it for -shared link, the executable should be compiled with -static-libasan
    in that case, and for executable link with --{,no-}whole-archive around
-   it to force everything into the executable.  And similarly for -ltsan
-   and -llsan.  */
+   it to force everything into the executable.  And similarly for -ltsan,
+   -lhwasan, and -llsan.  */
 #if defined(HAVE_LD_STATIC_DYNAMIC)
 #undef LIBASAN_EARLY_SPEC
 #define LIBASAN_EARLY_SPEC "%{!shared:libasan_preinit%O%s} " \
   "%{static-libasan:%{!shared:" \
   LD_STATIC_OPTION " --whole-archive -lasan --no-whole-archive " \
   LD_DYNAMIC_OPTION "}}%{!static-libasan:-lasan}"
+#undef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC "%{static-libhwasan:%{!shared:" \
+  LD_STATIC_OPTION " --whole-archive -lhwasan --no-whole-archive " \
+  LD_DYNAMIC_OPTION "}}%{!static-libhwasan:-lhwasan}"
 #undef LIBTSAN_EARLY_SPEC
 #define LIBTSAN_EARLY_SPEC "%{!shared:libtsan_preinit%O%s} " \
   "%{static-libtsan:%{!shared:" \
diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c
index 60e5bedc3665a25fa51c2eca00547f12a9953778..e8d0bedfc2eb22d1e72e7e4875155202c8389a38 100644
--- a/gcc/cppbuiltin.c
+++ b/gcc/cppbuiltin.c
@@ -93,6 +93,9 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile)
   if (flag_sanitize & SANITIZE_ADDRESS)
     cpp_define (pfile, "__SANITIZE_ADDRESS__");
 
+  if (flag_sanitize & SANITIZE_HWADDRESS)
+    cpp_define (pfile, "__SANITIZE_HWADDRESS__");
+
   if (flag_sanitize & SANITIZE_THREAD)
     cpp_define (pfile, "__SANITIZE_THREAD__");
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index af3c7f2b9109d4997d6313ca9a90b6fec9f5ccd7..97b4869b139805b633ad877e80bb6968f59b9206 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -12659,13 +12659,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 5b8b68bd710fd591bfe7a07e2d113d39178bfad7..38261cd1cb3fdeb9a3ecf42a6b526487f412bd76 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2970,6 +2970,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 1b061d7012792ac8f7984118efd03ef21280a3b6..ac5d9b0879c1909709b85b2aaf25017a7b5e3a26 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2373,6 +2373,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 0c23aadefed3d41797586763d4732a46fba0418e..ba7016b909e33a687f52058be5d336503ca3e86c 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 4428d50c3906b912f7d7cbb0391754008076cf21..97bdf73dadb3aff13561c07a9eb217a5d8e1d2d3 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -707,6 +707,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)}"
@@ -981,6 +999,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
@@ -990,6 +1009,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 3ddad551e6fc4741c787c38eded502a60a1d51da..8a47ac09df0cf31d73e5718e889e7759236d9f75 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1094,6 +1094,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))
@@ -1113,6 +1120,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)
@@ -1130,9 +1151,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);
 
@@ -1711,8 +1733,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),
@@ -2237,6 +2264,12 @@ 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_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 d88ae0c468b57975f4ed348243bfc64b63912542..8f43e7ea93dfb2974501195b83d916d7c22b9e4a 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
 Enable asan detection of use-after-return bugs.
 
+-param=hwasan-stack=
+Common Joined UInteger Var(param_hwasan_stack) Init(1) IntegerRange(0, 1) Param
+Enable hwasan stack protection.
+
+-param=hwasan-random-frame-tag=
+Common Joined UInteger Var(param_hwasan_random_frame_tag) Init(1) IntegerRange(0, 1) Param
+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
+Enable hwasan allocas/VLAs protection.
+
+-param=hwasan-instrument-reads=
+Common Joined UInteger Var(param_hwasan_instrument_reads) Init(1) IntegerRange(0, 1) Param
+Enable hwasan load operations protection.
+
+-param=hwasan-instrument-writes=
+Common Joined UInteger Var(param_hwasan_instrument_writes) Init(1) IntegerRange(0, 1) Param
+Enable hwasan store operations protection.
+
+-param=hwasan-memintrin=
+Common Joined UInteger Var(param_hwasan_memintrin) Init(1) IntegerRange(0, 1) Param
+Enable hwasan builtin functions protection.
+
 -param=avg-loop-niter=
 Common Joined UInteger Var(param_avg_loop_niter) Init(10) IntegerRange(1, 65536) Param
 Average number of iterations of a loop.
diff --git a/gcc/target.def b/gcc/target.def
index e0e856979a988a5aee005611f97e35bf06ad6c69..313ad34f39d4c6b835c405e4c3051c82840cc588 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6787,6 +6787,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 34987bbcdb3fcac9818990fc67ebf0d8199d312d..bba7acc0dbe194cb880d4677e029d772e9191450 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -283,4 +283,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 2d3bcbf3e89a38e753b15592d5c66b130ab96049..7f2b1ed8493917573f383084abbb25962fa9a893 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2364,4 +2364,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 059046f40f38cf5b340a7d7be02a438e0a16ac00..9c6c51aa57b0579738374661dcdea4373daf653d 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1824,6 +1824,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.  */


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (4 preceding siblings ...)
  2019-12-12 15:19     ` [PATCH 2/X] [libsanitizer] Only build libhwasan when targeting AArch64 Matthew Malcomson
@ 2019-12-12 15:19     ` Matthew Malcomson
  2019-12-12 15:19     ` [PATCH 1/X] [libsanitizer] Tie the hwasan library into our build system Matthew Malcomson
                       ` (12 subsequent siblings)
  18 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-12-12 15:19 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 45764 bytes --]

Handling stack variables has three features.

1) Ensure HWASAN required alignment for stack variables

When tagging shadow memory, we need to ensure that each tag granule is
only used by one variable at a time.

This is done by ensuring that each tagged variable is aligned to the tag
granule representation size and also ensure that the end of each
variable as an alignment boundary between the end and the start of any
other data stored on the stack.

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

2) Put tags into each stack variable pointer

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

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

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

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

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

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

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

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

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

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

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

gcc/ChangeLog:

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

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



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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -1,7 +1,11 @@
 # This option enables -fsanitize=hwaddress for stage2 and stage3.
+# We need to disable random frame tags for bootstrap since the autoconf check
+# for which direction the stack is growing has UB that a random frame tag
+# breaks.  Running with a random frame tag gives approx. 50% chance of
+# bootstrap comparison diff in libiberty/alloca.c.
 
-STAGE2_CFLAGS += -fsanitize=hwaddress
-STAGE3_CFLAGS += -fsanitize=hwaddress
+STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
 POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
diff --git a/gcc/asan.h b/gcc/asan.h
index 7675f18a84ee3f187ba4cb40db0ce232f3958762..d47540cce9db6e473a6d5aab632b5c6120cb27cf 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -23,6 +23,19 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void asan_function_start (void);
 extern void asan_finish_file (void);
+extern void hwasan_finish_file (void);
+extern void hwasan_record_base (rtx);
+extern uint8_t hwasan_current_tag ();
+extern void hwasan_increment_tag ();
+extern rtx hwasan_with_tag (rtx, poly_int64);
+extern void hwasan_tag_init ();
+extern rtx hwasan_create_untagged_base (rtx);
+extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern rtx_insn *hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
+extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx);
+extern bool hwasan_sanitize_p (void);
+extern bool hwasan_sanitize_stack_p (void);
+extern bool hwasan_sanitize_allocas_p (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -75,6 +88,31 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The values below define an ABI and are hard-coded to these values in
+   libhwasan, hence they can't be changed independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).  */
+#define HWASAN_TAG_SIZE 8
+/* Tag Granule of HWASAN shadow stack.
+   This is the size in real memory that each byte in the shadow memory refers
+   to.  I.e. if a variable is X bytes long in memory then it's tag in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   Most variables will need to be aligned to this amount since two variables
+   that are neighbours in memory and share a tag granule would need to share
+   the same tag (the shared tag granule can only store one tag).  */
+#define HWASAN_TAG_SHIFT_SIZE 4
+#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SHIFT_SIZE)
+/* Define the tag for the stack background.
+   This defines what tag the stack pointer will be and hence what tag all
+   variables that are not given special tags are (e.g. spilled registers,
+   and parameters passed on the stack).  */
+#define HWASAN_STACK_BACKGROUND 0
+/* How many bits to shift in order to access the tag bits.
+   The tag is stored in the top 8 bits of a pointer hence shifting 56 bits will
+   leave just the tag.  */
+#define HWASAN_SHIFT 56
+#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/asan.c b/gcc/asan.c
index a6d4cf999ddd91ff61134eaa0744936d5694f49b..7458fee4b939a6a577c289faf2c9f0e3fac0b716 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -256,6 +256,15 @@ hash_set<tree> *asan_handled_variables = NULL;
 
 hash_set <tree> *asan_used_labels = NULL;
 
+/* Global variables for HWASAN stack tagging.  */
+/* tag_offset records the offset from the frame base tag that the next object
+   should have. */
+static uint8_t tag_offset = 0;
+/* hwasan_base_ptr is a pointer with the same address as
+   `virtual_stack_vars_rtx` for the current frame, and with the frame base tag
+   stored in it.  */
+static rtx hwasan_base_ptr = NULL_RTX;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -1351,6 +1360,28 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+/* Returns whether we are tagging pointers and checking those tags on memory
+   access.  This is true when checking with either in software or hardware.  */
+bool
+hwasan_sanitize_p ()
+{
+    return sanitize_flags_p (SANITIZE_HWADDRESS);
+}
+
+/* Are we tagging the stack?  */
+bool
+hwasan_sanitize_stack_p ()
+{
+  return (hwasan_sanitize_p () && param_hwasan_stack);
+}
+
+/* Are we protecting alloca objects?  */
+bool
+hwasan_sanitize_allocas_p (void)
+{
+  return (hwasan_sanitize_stack_p () && param_hwasan_protect_allocas);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -2883,6 +2914,11 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_VOID_PTR_UINT8_SIZE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				unsigned_char_type_node, size_type_node,
+				NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
@@ -2933,6 +2969,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NOTHROW_LIST
+#define ATTR_NOTHROW_LIST ECF_NOTHROW
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
@@ -3684,4 +3722,276 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/* For stack tagging:
+   Initialise tag of the base register.
+   This has to be done as soon as the stack is getting expanded to ensure
+   anything emitted with `get_dynamic_stack_base` will use the value set here
+   instead of using a register without a value.
+   Especially note that RTL expansion of large aligned values does that.  */
+void
+hwasan_record_base (rtx base)
+{
+  targetm.memtag.gentag (base, virtual_stack_vars_rtx);
+  hwasan_base_ptr = base;
+}
+
+/* For stack tagging:
+   Return the offset from the frame base tag that the "next" expanded object
+   should have.  */
+uint8_t
+hwasan_current_tag ()
+{
+  return tag_offset;
+}
+
+/* For stack tagging:
+      Increment the tag offset modulo the size a tag can represent.  */
+void
+hwasan_increment_tag ()
+{
+  uint8_t tag_bits = HWASAN_TAG_SIZE;
+  STATIC_ASSERT (tag_bits == sizeof (tag_offset) * CHAR_BIT);
+  tag_offset = (tag_offset + 1) % (1 << tag_bits);
+  /* The "background tag" of the stack is zero by definition.
+     This is the tag that objects like parameters passed on the stack and
+     spilled registers are given.  It is handy to avoid this for objects we
+     decide the tags ourselves, partly to ensure that buffer overruns can't
+     affect these important variables (e.g. saved link register, saved stack
+     pointer etc) and partly to make debugging easier (everything with a tag of
+     zero is space allocated automatically by the compiler).
+
+     This is not feasible when using random frame tags (the default
+     configuration for hwasan) since the tag for the given frame is randomly
+     chosen at runtime.  In order to avoid any tags matching the stack
+     background we would need to decide tag offsets at runtime instead of
+     compile time (and pay the resulting performance cost).
+
+     When not using random base tags for each frame (i.e. when compiled with
+     `--param hwasan-random-frame-tag=0`) the base tag for each frame is zero.
+     This means the tag that each object gets is equal to the tag_offset used
+     in determining it.
+     When this is the case we *can* ensure no object gets the tag of zero by
+     simply ensuring no object has the tag_offset of zero.  */
+  if (tag_offset == HWASAN_STACK_BACKGROUND
+      && ! param_hwasan_random_frame_tag)
+    tag_offset += 1;
+}
+
+/* For stack tagging:
+       Return an RTX representing `base + offset` address
+       and `tag_of(base) + tag_offset` tag.  */
+rtx
+hwasan_with_tag (rtx base, poly_int64 offset)
+{
+  gcc_assert (tag_offset < (1 << HWASAN_TAG_SIZE));
+  return targetm.memtag.addtag (base, offset, tag_offset);
+}
+
+/* Clear internal state for the next function.
+   This function is called before variables on the stack get expanded, in
+   `init_vars_expansion`.  */
+void
+hwasan_tag_init ()
+{
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
+  hwasan_base_ptr = NULL_RTX;
+  /* When not using a random frame tag we can avoid the background stack
+     colour which gives the user a little better debug output upon a crash.
+     Meanwhile, when using a random frame tag it will be nice to avoid adding
+     tags for the first object since that is unnecessary extra work.
+     Hence set the initial tag_offset to be 0 if using a random frame tag and 1
+     otherwise.  */
+  tag_offset = param_hwasan_random_frame_tag
+    ? 0
+    : HWASAN_STACK_BACKGROUND + 1;
+}
+
+/* Return an RTX for the tag of TAGGED_POINTER.  */
+rtx
+hwasan_extract_tag (rtx tagged_pointer)
+{
+  rtx tag = expand_simple_binop (Pmode,
+				 LSHIFTRT,
+				 tagged_pointer,
+				 HWASAN_SHIFT_RTX,
+				 NULL_RTX,
+				 /* unsignedp = */0,
+				 OPTAB_DIRECT);
+  return gen_lowpart (QImode, tag);
+}
+
+/* For stack tagging:
+      Does HWASAN equivalent of `asan_emit_stack_protection`.
+
+   Prologue sequence should be emitted directly, while the epilogue sequence is
+   returned.  The epilogue sequence is what should be used if we're not
+   protecting alloca objects.
+
+   BASES is an array containing the tagged base registers for each object.
+   We map each object to a given base since large aligned objects have a
+   different base to others and we need to know which objects use which base.
+
+   UNTAGGED_BASES contains the same information as above except without tags.
+   This is needed since libhwasan only accepts untagged pointers in
+   __hwasan_tag_memory.
+
+   OFFSETS is an array with the start and end offsets for each object stored on
+   the stack in this frame.  This array is hence twice the length of the other
+   array arguments (given it has two entries for each stack object).
+
+   TAGS is an array containing the tag *offset* each object should have from
+   the tag in its base pointer.
+
+   LENGTH contains the length of the OFFSETS array.  */
+rtx_insn *
+hwasan_emit_prologue (rtx *bases,
+		      rtx *untagged_bases,
+		      poly_int64 *offsets,
+		      uint8_t *tags,
+		      size_t length)
+{
+  /* We need untagged base pointers since libhwasan only accepts untagged
+    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
+    the base tag for an offset.  */
+
+  if (length < 2)
+    return NULL;
+
+  poly_int64 bot = 0, top = 0;
+  size_t i = 0;
+  for (i = 0; (i * 2) + 1 < length; i++)
+    {
+      poly_int64 start = offsets[i * 2];
+      poly_int64 end = offsets[(i * 2) + 1];
+
+      if (known_ge (start, end))
+	{
+	  top = start;
+	  bot = end;
+	}
+      else
+	{
+	  top = end;
+	  bot = start;
+	}
+      poly_int64 size = (top - bot);
+
+      /* Can't check that all poly_int64's are aligned, but still nice
+	 to check those that are compile-time constants.  */
+      HOST_WIDE_INT tmp;
+      if (top.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (bot.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (size.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      rtx base_tag = hwasan_extract_tag (bases[i]);
+      /* In the case of tag overflow we would want modulo wrapping -- which
+	 should be given from the `plus_constant` in QImode.  */
+      rtx tag = plus_constant (QImode, base_tag, tags[i]);
+      emit_library_call (ret,
+			 LCT_NORMAL,
+			 VOIDmode,
+			 plus_constant (ptr_mode, untagged_bases[i], bot),
+			 ptr_mode,
+			 tag,
+			 QImode,
+			 gen_int_mode (size, ptr_mode),
+			 ptr_mode);
+    }
+  /* We know the last element in the arrays is the greatest offset from the
+     base of the frame due to how we create the arrays.  Hence we just need
+     to emit something going from the last element in the array to
+     virtual_stack_vars_rtx.
+     TODO Would really like to avoid hard-coding in this knowledge that the
+     last element is the furthest from the frame "start".  However, it is
+     already hard-coded in expand_stack_vars, so it's not too much of a deal.
+     */
+  rtx frame_extent = plus_constant (Pmode, untagged_bases[i - 1],
+				    STACK_GROWS_DOWNWARD ? bot : top,
+				    false);
+  return hwasan_emit_untag_frame (frame_extent, virtual_stack_vars_rtx);
+}
+
+/* For stack tagging:
+       Return RTL insns to clear the tags between DYNAMIC and VARS pointers
+       into the stack.  These instructions should be emitted at the end of
+       every function.  */
+rtx_insn *
+hwasan_emit_untag_frame (rtx dynamic, rtx vars)
+{
+  start_sequence ();
+
+  dynamic = convert_memory_address (ptr_mode, dynamic);
+  vars = convert_memory_address (ptr_mode, vars);
+
+  rtx top_rtx;
+  rtx bot_rtx;
+  if (STACK_GROWS_DOWNWARD)
+    {
+      top_rtx = vars;
+      bot_rtx = dynamic;
+    }
+  else
+    {
+      top_rtx = dynamic;
+      bot_rtx = vars;
+    }
+
+  rtx size_rtx = expand_simple_binop (Pmode, MINUS, top_rtx, bot_rtx,
+				  NULL_RTX, /* unsignedp = */0, OPTAB_DIRECT);
+
+  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (ret, LCT_NORMAL, VOIDmode,
+      bot_rtx, ptr_mode,
+      const0_rtx, QImode,
+      size_rtx, ptr_mode);
+
+  do_pending_stack_adjust ();
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+/* For stack tagging:
+       Return an RTX representing ORIG_BASE without a tag.  */
+rtx
+hwasan_create_untagged_base (rtx orig_base)
+{
+  rtx untagged_base = gen_reg_rtx (Pmode);
+  rtx tag_mask = gen_int_mode ((1ULL << HWASAN_SHIFT) - 1, Pmode);
+  untagged_base = expand_binop (Pmode, and_optab,
+				orig_base, tag_mask,
+				untagged_base, true, OPTAB_DIRECT);
+  gcc_assert (untagged_base);
+  return untagged_base;
+}
+
+/* Needs to be GTY(()), because cgraph_build_static_cdtor may
+   invoke ggc_collect.  */
+static GTY(()) tree hwasan_ctor_statements;
+
+/* Insert module initialisation into this TU.  This initialisation calls the
+   initialisation code for libhwasan.  */
+void
+hwasan_finish_file (void)
+{
+  /* Do not emit constructor initialisation for the kernel.
+     (the kernel has its own initialisation already).  */
+  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+    return;
+
+  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
+  flag_sanitize &= ~SANITIZE_HWADDRESS;
+  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
+  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
+  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
+  flag_sanitize |= SANITIZE_HWADDRESS;
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index 2611e88da605506fade8a633591ad8031fc0c80a..48c53119731430a5e0762482f2dcf8f8837e2de7 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -628,6 +628,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_UINT32_UINT32_PTR,
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
 		     BT_PTR)
 DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_SIZE, BT_VOID, BT_PTR, BT_UINT8,
+		     BT_SIZE)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index d8233f5f760f8426e8ff85473ecda02aa6c2655b..3f621ffdbda0acf5949348882a7c1f3504634666 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -244,6 +244,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
+				| SANITIZE_HWADDRESS \
 				| SANITIZE_UNDEFINED \
 				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 23ec2424f9daeda74a49eb2da69f3ff985dcd7eb..b89400306bb6bef3043296f3f7e2f9792c6f4890 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -377,7 +377,13 @@ align_local_variable (tree decl, bool really_expand)
       if (really_expand)
 	SET_DECL_ALIGN (decl, align);
     }
-  return align / BITS_PER_UNIT;
+
+  unsigned int ret_align = align / BITS_PER_UNIT;
+
+  if (hwasan_sanitize_stack_p ())
+    ret_align = MAX (ret_align, HWASAN_TAG_GRANULE_SIZE);
+
+  return ret_align;
 }
 
 /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
@@ -985,7 +991,7 @@ dump_stack_var_partition (void)
 
 static void
 expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
-			 poly_int64 offset)
+			 poly_int64 offset, rtx stack_base)
 {
   unsigned align;
   rtx x;
@@ -993,7 +999,11 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   /* If this fails, we've overflowed the stack frame.  Error nicely?  */
   gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
 
-  x = plus_constant (Pmode, base, offset);
+  if (hwasan_sanitize_stack_p ())
+    x = hwasan_with_tag (base, offset);
+  else
+    x = plus_constant (Pmode, base, offset);
+
   x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
 		   ? TYPE_MODE (TREE_TYPE (decl))
 		   : DECL_MODE (SSAVAR (decl)), x);
@@ -1003,7 +1013,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
       /* Set alignment we actually gave this decl if it isn't an SSA name.
          If it is we generate stack slots only accidentally so it isn't as
 	 important, we'll simply use the alignment that is already set.  */
-      if (base == virtual_stack_vars_rtx)
+      if (base == stack_base)
 	offset -= frame_phase;
       align = known_alignment (offset);
       align *= BITS_PER_UNIT;
@@ -1029,9 +1039,19 @@ public:
      The vector is in reversed, highest offset pairs come first.  */
   auto_vec<HOST_WIDE_INT> asan_vec;
 
+  /* HWASAN records the poly_int64 so it can handle any stack variable.  */
+  auto_vec<poly_int64> hwasan_vec;
+  auto_vec<rtx> hwasan_untagged_base_vec;
+  auto_vec<rtx> hwasan_base_vec;
+
   /* Vector of partition representative decls in between the paddings.  */
   auto_vec<tree> asan_decl_vec;
 
+  /* Vector of tag offsets representing the tag for each stack variable.
+     Each offset determines the difference between the randomly generated
+     tag for the current frame and the tag for this stack variable.  */
+  auto_vec<uint8_t> hwasan_tag_vec;
+
   /* Base pseudo register for Address Sanitizer protected automatic vars.  */
   rtx asan_base;
 
@@ -1049,6 +1069,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
   size_t si, i, j, n = stack_vars_num;
   poly_uint64 large_size = 0, large_alloc = 0;
   rtx large_base = NULL;
+  rtx large_untagged_base = NULL;
   unsigned large_align = 0;
   bool large_allocation_done = false;
   tree decl;
@@ -1095,11 +1116,17 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	}
     }
 
+  if (hwasan_sanitize_stack_p () && data->asan_base == NULL)
+    {
+      data->asan_base = gen_reg_rtx (Pmode);
+      hwasan_record_base (data->asan_base);
+    }
+
   for (si = 0; si < n; ++si)
     {
       rtx base;
       unsigned base_align, alignb;
-      poly_int64 offset;
+      poly_int64 offset = 0;
 
       i = stack_vars_sorted[si];
 
@@ -1120,10 +1147,36 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       if (pred && !pred (i))
 	continue;
 
+      base = hwasan_sanitize_stack_p ()
+	? data->asan_base
+	: virtual_stack_vars_rtx;
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  base = virtual_stack_vars_rtx;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Allocate zero bytes to take advantage of the
+		 alloc_stack_frame_space logic of ensuring the stack is aligned
+		 despite having poly_int64's to deal with.
+
+		 There must be no tag granule "shared" between different
+		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
+		 chunk can have more than one object in it.
+
+		 We ensure this by forcing the end of the last bit of data to
+		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
+		 the start of each variable to be aligned to
+		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
+
+		 We can't align just one of the start or end, since there are
+		 untagged things stored on the stack that we have no control on
+		 the alignment and these can't share a tag granule with a
+		 tagged variable.  */
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	      offset = alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (offset);
+	      data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
+	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
 	  HOST_WIDE_INT prev_offset;
@@ -1203,6 +1256,9 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
 	      base_align = crtl->max_used_stack_slot_alignment;
 	    }
+
+	  if (hwasan_sanitize_stack_p ())
+	    data->hwasan_vec.safe_push (offset);
 	}
       else
 	{
@@ -1222,14 +1278,31 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      loffset = alloc_stack_frame_space
 		(rtx_to_poly_int64 (large_allocsize),
 		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
-	      large_base = get_dynamic_stack_base (loffset, large_align);
+	      large_base = get_dynamic_stack_base (loffset, large_align, base);
 	      large_allocation_done = true;
 	    }
-	  gcc_assert (large_base != NULL);
 
+	  gcc_assert (large_base != NULL);
 	  large_alloc = aligned_upper_bound (large_alloc, alignb);
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* An object with a large alignment requirement means that the
+		 alignment requirement is greater than the required alignment
+		 for tags.  */
+	      if (!large_untagged_base)
+		large_untagged_base = hwasan_create_untagged_base (large_base);
+	      data->hwasan_vec.safe_push (large_alloc);
+	      data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
+	    }
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Ensure the end of the variable is also aligned correctly.  */
+	      poly_int64 align_again =
+		aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (align_again);
+	    }
 
 	  base = large_base;
 	  base_align = large_align;
@@ -1241,7 +1314,21 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	{
 	  expand_one_stack_var_at (stack_vars[j].decl,
 				   base, base_align,
-				   offset);
+				   offset,
+				   hwasan_sanitize_stack_p ()
+				   ? data->asan_base
+				   : virtual_stack_vars_rtx);
+	}
+
+      if (hwasan_sanitize_stack_p ())
+	{
+	  /* Record the tag for this object in `data` so the prologue knows
+	     what tag to put in the shadow memory during cfgexpand.c.
+	     Then increment the tag so that the next object has a different
+	     tag to this object.  */
+	  data->hwasan_base_vec.safe_push (base);
+	  data->hwasan_tag_vec.safe_push (hwasan_current_tag ());
+	  hwasan_increment_tag ();
 	}
     }
 
@@ -1338,7 +1425,8 @@ expand_one_stack_var_1 (tree var)
   offset = alloc_stack_frame_space (size, byte_align);
 
   expand_one_stack_var_at (var, virtual_stack_vars_rtx,
-			   crtl->max_used_stack_slot_alignment, offset);
+			   crtl->max_used_stack_slot_alignment, offset,
+			   virtual_stack_vars_rtx);
 }
 
 /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
@@ -1551,8 +1639,13 @@ defer_stack_allocation (tree var, bool toplevel)
 
   /* If stack protection is enabled, *all* stack variables must be deferred,
      so that we can re-order the strings to the top of the frame.
-     Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || asan_sanitize_stack_p ())
+     Similarly for Address Sanitizer.
+     When tagging memory we defer all stack variables so we can handle them in
+     one place (handle here meaning ensure they are aligned and record
+     information on each variables position in the stack).  */
+  if (flag_stack_protect
+      || asan_sanitize_stack_p ()
+      || hwasan_sanitize_stack_p ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME
@@ -1937,6 +2030,8 @@ init_vars_expansion (void)
   /* Initialize local stack smashing state.  */
   has_protected_decls = false;
   has_short_buffer = false;
+  if (hwasan_sanitize_stack_p ())
+    hwasan_tag_init ();
 }
 
 /* Free up stack variable graph data.  */
@@ -2296,12 +2391,27 @@ expand_used_vars (void)
 	}
 
       expand_stack_vars (NULL, &data);
+
+      if (hwasan_sanitize_stack_p ())
+	var_end_seq
+	  = hwasan_emit_prologue (data.hwasan_base_vec.address (),
+				  data.hwasan_untagged_base_vec.address (),
+				  data.hwasan_vec.address (),
+				  data.hwasan_tag_vec.address (),
+				  data.hwasan_vec.length ());
     }
 
   if (asan_sanitize_allocas_p () && cfun->calls_alloca)
     var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
 					      virtual_stack_vars_rtx,
 					      var_end_seq);
+  else if (hwasan_sanitize_allocas_p () && cfun->calls_alloca)
+    /* Here we replace `var_end_seq` while for asan we append to `var_end_seq`.
+       This is for the out-of-line case which uses only one function call in
+       either case (and hence we want to replace the previous function call if
+       we're changing things).  */
+    var_end_seq = hwasan_emit_untag_frame (virtual_stack_dynamic_rtx,
+					   virtual_stack_vars_rtx);
 
   fini_vars_expansion ();
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 38261cd1cb3fdeb9a3ecf42a6b526487f412bd76..b975554825e16f7f09770839cb33d1204b43dedf 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2974,6 +2974,31 @@ A target hook which lets a backend compute the set of pressure classes to  be us
 True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that @option{-fsanitize=hwaddress} can work.
 @end deftypefn
 
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
+Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by TAG_OFFSET.
+The resulting RTX must either be a valid memory address or be able to get
+put into an operand with force_operand.  If overridden the more common case
+is that we force this into an operand using the backend hook
+"addtag_force_operand" that is called in force_operand.
+
+It is expected that that "addtag_force_operand" recognises the RTX
+generated by "addtag" and emits code to force that RTX into an operand.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG_FORCE_OPERAND (rtx @var{oper}, rtx @var{target})
+If the RTL expression OPER is of the form generated by targetm.memtag.addtag,
+then emit instructions to move the value into an operand (i.e. for
+force_operand).
+TARGET is an RTX suggestion of where to generate the value.
+This hook is most often implemented by emitting instructions to put the
+expression into a pseudo register, then returning that pseudo register.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_MEMTAG_GENTAG (rtx @var{base}, rtx @var{untagged})
+Set the BASE argument to UNTAGGED with some random tag.
+This function is used to generate a tagged base for the current stack frame.
+@end deftypefn
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index ac5d9b0879c1909709b85b2aaf25017a7b5e3a26..f109d934ef59d3d77236ca7e79b8969373babfcb 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2375,6 +2375,12 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_ADDTAG
+
+@hook TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
+
+@hook TARGET_MEMTAG_GENTAG
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/explow.h b/gcc/explow.h
index 5110ad82d6a024fda1d3a3eaf80de40c5e6ad3b6..333948e0c69a1b1132e9a1d06707dc63f1226262 100644
--- a/gcc/explow.h
+++ b/gcc/explow.h
@@ -102,7 +102,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
 extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
 
 /* Returns the address of the dynamic stack space without allocating it.  */
-extern rtx get_dynamic_stack_base (poly_int64, unsigned);
+extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
 
 /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
 extern rtx align_dynamic_address (rtx, unsigned);
diff --git a/gcc/explow.c b/gcc/explow.c
index d174632c5fccdee3336e522b71c1f662ae967da7..5fadf56db395ac8560a7065d5f88d570ec31861d 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1577,10 +1577,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
    OFFSET is the offset of the area into the virtual stack vars area.
 
    REQUIRED_ALIGN is the alignment (in bits) required for the region
-   of memory.  */
+   of memory.
+
+   BASE is the rtx of the base of this virtual stack vars area.
+   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+   on the stack.  */
 
 rtx
-get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
 {
   rtx target;
 
@@ -1588,7 +1592,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
     crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
 
   target = gen_reg_rtx (Pmode);
-  emit_move_insn (target, virtual_stack_vars_rtx);
+  emit_move_insn (target, base);
   target = expand_binop (Pmode, add_optab, target,
 			 gen_int_mode (offset, Pmode),
 			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
diff --git a/gcc/expr.c b/gcc/expr.c
index 20aa939709cd3ee64dbc6c588ed8efee19c5b657..669d1e08d24aebae608c6bcaa07c6fb9732ba3d5 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -7529,6 +7529,13 @@ force_operand (rtx value, rtx target)
       return subtarget;
     }
 
+  if (targetm.memtag.addtag_force_operand)
+    {
+      rtx ret = targetm.memtag.addtag_force_operand (value, target);
+      if (ret)
+	return ret;
+    }
+
   if (ARITHMETIC_P (value))
     {
       op2 = XEXP (value, 1);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 374d15007d868363d9b4fbf467e1e462abbca61a..7bd50715f24a2cb154b578e2abdea4e8fcdb2107 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
+/* Hardware Address Sanitizer.  */
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
+		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
+
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/target.def b/gcc/target.def
index 313ad34f39d4c6b835c405e4c3051c82840cc588..273860c5faede0203dbc3930652f92804c7b0e8a 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6796,6 +6796,37 @@ DEFHOOK
  pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
  bool, (), default_memtag_can_tag_addresses)
 
+DEFHOOK
+(addtag,
+ "Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by\
+ TAG_OFFSET.\n\
+The resulting RTX must either be a valid memory address or be able to get\n\
+put into an operand with force_operand.  If overridden the more common case\n\
+is that we force this into an operand using the backend hook\n\
+\"addtag_force_operand\" that is called in force_operand.\n\
+\n\
+It is expected that that \"addtag_force_operand\" recognises the RTX\n\
+generated by \"addtag\" and emits code to force that RTX into an operand.",
+rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
+default_memtag_addtag)
+
+DEFHOOK
+(addtag_force_operand,
+ "If the RTL expression OPER is of the form generated by targetm.memtag.addtag,\n\
+then emit instructions to move the value into an operand (i.e. for\n\
+force_operand).\n\
+TARGET is an RTX suggestion of where to generate the value.\n\
+This hook is most often implemented by emitting instructions to put the\n\
+expression into a pseudo register, then returning that pseudo register.",
+rtx, (rtx oper, rtx target), NULL)
+
+DEFHOOK
+(gentag,
+ "Set the BASE argument to UNTAGGED with some random tag.\n\
+This function is used to generate a tagged base for the current stack frame.",
+  void, (rtx base, rtx untagged),
+  default_memtag_gentag)
+
 HOOK_VECTOR_END (memtag)
 #undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index bba7acc0dbe194cb880d4677e029d772e9191450..73432e9d41b88df3b6225bfe410b344129ea5e78 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -284,4 +284,6 @@ extern bool speculation_safe_value_not_needed (bool);
 extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 
 extern bool default_memtag_can_tag_addresses ();
+extern void default_memtag_gentag (rtx, rtx);
+extern rtx default_memtag_addtag (rtx, poly_int64, uint8_t);
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 7f2b1ed8493917573f383084abbb25962fa9a893..f6a0614feb304d000d180d18bbdfeccfeb302be6 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "flags.h"
 #include "explow.h"
+#include "expmed.h"
 #include "calls.h"
 #include "expr.h"
 #include "output.h"
@@ -83,6 +84,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "sbitmap.h"
 #include "function-abi.h"
+#include "attribs.h"
+#include "asan.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -2370,4 +2373,78 @@ default_memtag_can_tag_addresses ()
   return false;
 }
 
+void
+default_memtag_gentag (rtx base, rtx untagged)
+{
+  gcc_assert (param_hwasan_stack);
+  if (param_hwasan_random_frame_tag)
+    {
+    rtx temp = gen_reg_rtx (QImode);
+    rtx ret = init_one_libfunc ("__hwasan_generate_tag");
+    rtx new_tag = emit_library_call_value (ret, temp, LCT_NORMAL, QImode);
+    emit_move_insn (base, untagged);
+    /* We know that `base` is not the stack pointer, since we never want to put
+      a randomly generated tag into the stack pointer.  Hence we can use
+      `store_bit_field` which on aarch64 generates a `bfi` which can not act on
+      the stack pointer.  */
+    store_bit_field (base, 8, 56, 0, 0, QImode, new_tag, false);
+    }
+  else
+    {
+      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
+	 In the future we may add the option emit random tags with inline
+	 instrumentation instead of function calls.  This would be the same
+	 between the kernel and userland.  */
+      emit_move_insn (base, untagged);
+    }
+}
+
+rtx
+default_memtag_addtag (rtx base, poly_int64 offset, uint8_t tag_offset)
+{
+  /* Need to look into what the most efficient code sequence is.
+     This is a code sequence that would be emitted *many* times, so we
+     want it as small as possible.
+
+     If the tag offset is greater that (1 << 7) then the most efficient
+     sequence here would give UB from signed integer overflow in the
+     poly_int64.  Hence in that case we emit the slightly less efficient
+     sequence.
+
+     There are two places where tag overflow is a question:
+       - Tagging the shadow stack.
+	  (both tagging and untagging).
+       - Tagging addressable pointers.
+
+     We need to ensure both behaviours are the same (i.e. that the tag that
+     ends up in a pointer after "overflowing" the tag bits with a tag addition
+     is the same that ends up in the shadow space).
+
+     The aim is that the behaviour of tag addition should follow modulo
+     wrapping in both instances.
+
+     The libhwasan code doesn't have any path that increments a pointers tag,
+     which means it has no opinion on what happens when a tag increment
+     overflows (and hence we can choose our own behaviour).  */
+
+  if (tag_offset < (1 << 7))
+    {
+      offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
+      return plus_constant (Pmode, base, offset);
+    }
+  else
+    {
+      /* This is the fallback, it would be nice if it had less instructions,
+	 but we can look for cleverer ways later.  */
+      uint64_t tag_mask = ~(0xFFUL << HWASAN_SHIFT);
+      rtx untagged_base = gen_rtx_AND (Pmode, GEN_INT (tag_mask), base);
+      rtx new_addr = plus_constant (Pmode, untagged_base, offset);
+
+      rtx original_tag_value = gen_rtx_LSHIFTRT (Pmode, base, GEN_INT (HWASAN_SHIFT));
+      rtx new_tag_value = plus_constant (Pmode, original_tag_value, tag_offset);
+      rtx new_tag = gen_rtx_ASHIFT (Pmode, new_tag_value, GEN_INT (HWASAN_SHIFT));
+      return gen_rtx_IOR (Pmode, new_addr, new_tag);
+    }
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 9c6c51aa57b0579738374661dcdea4373daf653d..d2706b412d9f34ef25acb21981a3c9e02e190ed5 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -510,6 +510,9 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      if (flag_sanitize & SANITIZE_HWADDRESS)
+	hwasan_finish_file ();
+
       omp_finish_file ();
 
       hsa_output_brig ();


[-- Attachment #2: hwasan-patch4.patch --]
[-- Type: text/plain, Size: 39089 bytes --]

diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -1,7 +1,11 @@
 # This option enables -fsanitize=hwaddress for stage2 and stage3.
+# We need to disable random frame tags for bootstrap since the autoconf check
+# for which direction the stack is growing has UB that a random frame tag
+# breaks.  Running with a random frame tag gives approx. 50% chance of
+# bootstrap comparison diff in libiberty/alloca.c.
 
-STAGE2_CFLAGS += -fsanitize=hwaddress
-STAGE3_CFLAGS += -fsanitize=hwaddress
+STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
 POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
diff --git a/gcc/asan.h b/gcc/asan.h
index 7675f18a84ee3f187ba4cb40db0ce232f3958762..d47540cce9db6e473a6d5aab632b5c6120cb27cf 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -23,6 +23,19 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void asan_function_start (void);
 extern void asan_finish_file (void);
+extern void hwasan_finish_file (void);
+extern void hwasan_record_base (rtx);
+extern uint8_t hwasan_current_tag ();
+extern void hwasan_increment_tag ();
+extern rtx hwasan_with_tag (rtx, poly_int64);
+extern void hwasan_tag_init ();
+extern rtx hwasan_create_untagged_base (rtx);
+extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern rtx_insn *hwasan_emit_prologue (rtx *, rtx *, poly_int64 *, uint8_t *, size_t);
+extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx);
+extern bool hwasan_sanitize_p (void);
+extern bool hwasan_sanitize_stack_p (void);
+extern bool hwasan_sanitize_allocas_p (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -75,6 +88,31 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The values below define an ABI and are hard-coded to these values in
+   libhwasan, hence they can't be changed independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).  */
+#define HWASAN_TAG_SIZE 8
+/* Tag Granule of HWASAN shadow stack.
+   This is the size in real memory that each byte in the shadow memory refers
+   to.  I.e. if a variable is X bytes long in memory then it's tag in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   Most variables will need to be aligned to this amount since two variables
+   that are neighbours in memory and share a tag granule would need to share
+   the same tag (the shared tag granule can only store one tag).  */
+#define HWASAN_TAG_SHIFT_SIZE 4
+#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SHIFT_SIZE)
+/* Define the tag for the stack background.
+   This defines what tag the stack pointer will be and hence what tag all
+   variables that are not given special tags are (e.g. spilled registers,
+   and parameters passed on the stack).  */
+#define HWASAN_STACK_BACKGROUND 0
+/* How many bits to shift in order to access the tag bits.
+   The tag is stored in the top 8 bits of a pointer hence shifting 56 bits will
+   leave just the tag.  */
+#define HWASAN_SHIFT 56
+#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/asan.c b/gcc/asan.c
index a6d4cf999ddd91ff61134eaa0744936d5694f49b..7458fee4b939a6a577c289faf2c9f0e3fac0b716 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -256,6 +256,15 @@ hash_set<tree> *asan_handled_variables = NULL;
 
 hash_set <tree> *asan_used_labels = NULL;
 
+/* Global variables for HWASAN stack tagging.  */
+/* tag_offset records the offset from the frame base tag that the next object
+   should have. */
+static uint8_t tag_offset = 0;
+/* hwasan_base_ptr is a pointer with the same address as
+   `virtual_stack_vars_rtx` for the current frame, and with the frame base tag
+   stored in it.  */
+static rtx hwasan_base_ptr = NULL_RTX;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -1351,6 +1360,28 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+/* Returns whether we are tagging pointers and checking those tags on memory
+   access.  This is true when checking with either in software or hardware.  */
+bool
+hwasan_sanitize_p ()
+{
+    return sanitize_flags_p (SANITIZE_HWADDRESS);
+}
+
+/* Are we tagging the stack?  */
+bool
+hwasan_sanitize_stack_p ()
+{
+  return (hwasan_sanitize_p () && param_hwasan_stack);
+}
+
+/* Are we protecting alloca objects?  */
+bool
+hwasan_sanitize_allocas_p (void)
+{
+  return (hwasan_sanitize_stack_p () && param_hwasan_protect_allocas);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -2883,6 +2914,11 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_VOID_PTR_UINT8_SIZE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				unsigned_char_type_node, size_type_node,
+				NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
@@ -2933,6 +2969,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NOTHROW_LIST
+#define ATTR_NOTHROW_LIST ECF_NOTHROW
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
@@ -3684,4 +3722,276 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/* For stack tagging:
+   Initialise tag of the base register.
+   This has to be done as soon as the stack is getting expanded to ensure
+   anything emitted with `get_dynamic_stack_base` will use the value set here
+   instead of using a register without a value.
+   Especially note that RTL expansion of large aligned values does that.  */
+void
+hwasan_record_base (rtx base)
+{
+  targetm.memtag.gentag (base, virtual_stack_vars_rtx);
+  hwasan_base_ptr = base;
+}
+
+/* For stack tagging:
+   Return the offset from the frame base tag that the "next" expanded object
+   should have.  */
+uint8_t
+hwasan_current_tag ()
+{
+  return tag_offset;
+}
+
+/* For stack tagging:
+      Increment the tag offset modulo the size a tag can represent.  */
+void
+hwasan_increment_tag ()
+{
+  uint8_t tag_bits = HWASAN_TAG_SIZE;
+  STATIC_ASSERT (tag_bits == sizeof (tag_offset) * CHAR_BIT);
+  tag_offset = (tag_offset + 1) % (1 << tag_bits);
+  /* The "background tag" of the stack is zero by definition.
+     This is the tag that objects like parameters passed on the stack and
+     spilled registers are given.  It is handy to avoid this for objects we
+     decide the tags ourselves, partly to ensure that buffer overruns can't
+     affect these important variables (e.g. saved link register, saved stack
+     pointer etc) and partly to make debugging easier (everything with a tag of
+     zero is space allocated automatically by the compiler).
+
+     This is not feasible when using random frame tags (the default
+     configuration for hwasan) since the tag for the given frame is randomly
+     chosen at runtime.  In order to avoid any tags matching the stack
+     background we would need to decide tag offsets at runtime instead of
+     compile time (and pay the resulting performance cost).
+
+     When not using random base tags for each frame (i.e. when compiled with
+     `--param hwasan-random-frame-tag=0`) the base tag for each frame is zero.
+     This means the tag that each object gets is equal to the tag_offset used
+     in determining it.
+     When this is the case we *can* ensure no object gets the tag of zero by
+     simply ensuring no object has the tag_offset of zero.  */
+  if (tag_offset == HWASAN_STACK_BACKGROUND
+      && ! param_hwasan_random_frame_tag)
+    tag_offset += 1;
+}
+
+/* For stack tagging:
+       Return an RTX representing `base + offset` address
+       and `tag_of(base) + tag_offset` tag.  */
+rtx
+hwasan_with_tag (rtx base, poly_int64 offset)
+{
+  gcc_assert (tag_offset < (1 << HWASAN_TAG_SIZE));
+  return targetm.memtag.addtag (base, offset, tag_offset);
+}
+
+/* Clear internal state for the next function.
+   This function is called before variables on the stack get expanded, in
+   `init_vars_expansion`.  */
+void
+hwasan_tag_init ()
+{
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
+  hwasan_base_ptr = NULL_RTX;
+  /* When not using a random frame tag we can avoid the background stack
+     colour which gives the user a little better debug output upon a crash.
+     Meanwhile, when using a random frame tag it will be nice to avoid adding
+     tags for the first object since that is unnecessary extra work.
+     Hence set the initial tag_offset to be 0 if using a random frame tag and 1
+     otherwise.  */
+  tag_offset = param_hwasan_random_frame_tag
+    ? 0
+    : HWASAN_STACK_BACKGROUND + 1;
+}
+
+/* Return an RTX for the tag of TAGGED_POINTER.  */
+rtx
+hwasan_extract_tag (rtx tagged_pointer)
+{
+  rtx tag = expand_simple_binop (Pmode,
+				 LSHIFTRT,
+				 tagged_pointer,
+				 HWASAN_SHIFT_RTX,
+				 NULL_RTX,
+				 /* unsignedp = */0,
+				 OPTAB_DIRECT);
+  return gen_lowpart (QImode, tag);
+}
+
+/* For stack tagging:
+      Does HWASAN equivalent of `asan_emit_stack_protection`.
+
+   Prologue sequence should be emitted directly, while the epilogue sequence is
+   returned.  The epilogue sequence is what should be used if we're not
+   protecting alloca objects.
+
+   BASES is an array containing the tagged base registers for each object.
+   We map each object to a given base since large aligned objects have a
+   different base to others and we need to know which objects use which base.
+
+   UNTAGGED_BASES contains the same information as above except without tags.
+   This is needed since libhwasan only accepts untagged pointers in
+   __hwasan_tag_memory.
+
+   OFFSETS is an array with the start and end offsets for each object stored on
+   the stack in this frame.  This array is hence twice the length of the other
+   array arguments (given it has two entries for each stack object).
+
+   TAGS is an array containing the tag *offset* each object should have from
+   the tag in its base pointer.
+
+   LENGTH contains the length of the OFFSETS array.  */
+rtx_insn *
+hwasan_emit_prologue (rtx *bases,
+		      rtx *untagged_bases,
+		      poly_int64 *offsets,
+		      uint8_t *tags,
+		      size_t length)
+{
+  /* We need untagged base pointers since libhwasan only accepts untagged
+    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
+    the base tag for an offset.  */
+
+  if (length < 2)
+    return NULL;
+
+  poly_int64 bot = 0, top = 0;
+  size_t i = 0;
+  for (i = 0; (i * 2) + 1 < length; i++)
+    {
+      poly_int64 start = offsets[i * 2];
+      poly_int64 end = offsets[(i * 2) + 1];
+
+      if (known_ge (start, end))
+	{
+	  top = start;
+	  bot = end;
+	}
+      else
+	{
+	  top = end;
+	  bot = start;
+	}
+      poly_int64 size = (top - bot);
+
+      /* Can't check that all poly_int64's are aligned, but still nice
+	 to check those that are compile-time constants.  */
+      HOST_WIDE_INT tmp;
+      if (top.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (bot.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+      if (size.is_constant (&tmp))
+	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
+
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      rtx base_tag = hwasan_extract_tag (bases[i]);
+      /* In the case of tag overflow we would want modulo wrapping -- which
+	 should be given from the `plus_constant` in QImode.  */
+      rtx tag = plus_constant (QImode, base_tag, tags[i]);
+      emit_library_call (ret,
+			 LCT_NORMAL,
+			 VOIDmode,
+			 plus_constant (ptr_mode, untagged_bases[i], bot),
+			 ptr_mode,
+			 tag,
+			 QImode,
+			 gen_int_mode (size, ptr_mode),
+			 ptr_mode);
+    }
+  /* We know the last element in the arrays is the greatest offset from the
+     base of the frame due to how we create the arrays.  Hence we just need
+     to emit something going from the last element in the array to
+     virtual_stack_vars_rtx.
+     TODO Would really like to avoid hard-coding in this knowledge that the
+     last element is the furthest from the frame "start".  However, it is
+     already hard-coded in expand_stack_vars, so it's not too much of a deal.
+     */
+  rtx frame_extent = plus_constant (Pmode, untagged_bases[i - 1],
+				    STACK_GROWS_DOWNWARD ? bot : top,
+				    false);
+  return hwasan_emit_untag_frame (frame_extent, virtual_stack_vars_rtx);
+}
+
+/* For stack tagging:
+       Return RTL insns to clear the tags between DYNAMIC and VARS pointers
+       into the stack.  These instructions should be emitted at the end of
+       every function.  */
+rtx_insn *
+hwasan_emit_untag_frame (rtx dynamic, rtx vars)
+{
+  start_sequence ();
+
+  dynamic = convert_memory_address (ptr_mode, dynamic);
+  vars = convert_memory_address (ptr_mode, vars);
+
+  rtx top_rtx;
+  rtx bot_rtx;
+  if (STACK_GROWS_DOWNWARD)
+    {
+      top_rtx = vars;
+      bot_rtx = dynamic;
+    }
+  else
+    {
+      top_rtx = dynamic;
+      bot_rtx = vars;
+    }
+
+  rtx size_rtx = expand_simple_binop (Pmode, MINUS, top_rtx, bot_rtx,
+				  NULL_RTX, /* unsignedp = */0, OPTAB_DIRECT);
+
+  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (ret, LCT_NORMAL, VOIDmode,
+      bot_rtx, ptr_mode,
+      const0_rtx, QImode,
+      size_rtx, ptr_mode);
+
+  do_pending_stack_adjust ();
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+/* For stack tagging:
+       Return an RTX representing ORIG_BASE without a tag.  */
+rtx
+hwasan_create_untagged_base (rtx orig_base)
+{
+  rtx untagged_base = gen_reg_rtx (Pmode);
+  rtx tag_mask = gen_int_mode ((1ULL << HWASAN_SHIFT) - 1, Pmode);
+  untagged_base = expand_binop (Pmode, and_optab,
+				orig_base, tag_mask,
+				untagged_base, true, OPTAB_DIRECT);
+  gcc_assert (untagged_base);
+  return untagged_base;
+}
+
+/* Needs to be GTY(()), because cgraph_build_static_cdtor may
+   invoke ggc_collect.  */
+static GTY(()) tree hwasan_ctor_statements;
+
+/* Insert module initialisation into this TU.  This initialisation calls the
+   initialisation code for libhwasan.  */
+void
+hwasan_finish_file (void)
+{
+  /* Do not emit constructor initialisation for the kernel.
+     (the kernel has its own initialisation already).  */
+  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+    return;
+
+  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
+  flag_sanitize &= ~SANITIZE_HWADDRESS;
+  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
+  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
+  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
+  flag_sanitize |= SANITIZE_HWADDRESS;
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index 2611e88da605506fade8a633591ad8031fc0c80a..48c53119731430a5e0762482f2dcf8f8837e2de7 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -628,6 +628,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_UINT32_UINT32_PTR,
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
 		     BT_PTR)
 DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_SIZE, BT_VOID, BT_PTR, BT_UINT8,
+		     BT_SIZE)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index d8233f5f760f8426e8ff85473ecda02aa6c2655b..3f621ffdbda0acf5949348882a7c1f3504634666 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -244,6 +244,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
+				| SANITIZE_HWADDRESS \
 				| SANITIZE_UNDEFINED \
 				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 23ec2424f9daeda74a49eb2da69f3ff985dcd7eb..b89400306bb6bef3043296f3f7e2f9792c6f4890 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -377,7 +377,13 @@ align_local_variable (tree decl, bool really_expand)
       if (really_expand)
 	SET_DECL_ALIGN (decl, align);
     }
-  return align / BITS_PER_UNIT;
+
+  unsigned int ret_align = align / BITS_PER_UNIT;
+
+  if (hwasan_sanitize_stack_p ())
+    ret_align = MAX (ret_align, HWASAN_TAG_GRANULE_SIZE);
+
+  return ret_align;
 }
 
 /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
@@ -985,7 +991,7 @@ dump_stack_var_partition (void)
 
 static void
 expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
-			 poly_int64 offset)
+			 poly_int64 offset, rtx stack_base)
 {
   unsigned align;
   rtx x;
@@ -993,7 +999,11 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   /* If this fails, we've overflowed the stack frame.  Error nicely?  */
   gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
 
-  x = plus_constant (Pmode, base, offset);
+  if (hwasan_sanitize_stack_p ())
+    x = hwasan_with_tag (base, offset);
+  else
+    x = plus_constant (Pmode, base, offset);
+
   x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
 		   ? TYPE_MODE (TREE_TYPE (decl))
 		   : DECL_MODE (SSAVAR (decl)), x);
@@ -1003,7 +1013,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
       /* Set alignment we actually gave this decl if it isn't an SSA name.
          If it is we generate stack slots only accidentally so it isn't as
 	 important, we'll simply use the alignment that is already set.  */
-      if (base == virtual_stack_vars_rtx)
+      if (base == stack_base)
 	offset -= frame_phase;
       align = known_alignment (offset);
       align *= BITS_PER_UNIT;
@@ -1029,9 +1039,19 @@ public:
      The vector is in reversed, highest offset pairs come first.  */
   auto_vec<HOST_WIDE_INT> asan_vec;
 
+  /* HWASAN records the poly_int64 so it can handle any stack variable.  */
+  auto_vec<poly_int64> hwasan_vec;
+  auto_vec<rtx> hwasan_untagged_base_vec;
+  auto_vec<rtx> hwasan_base_vec;
+
   /* Vector of partition representative decls in between the paddings.  */
   auto_vec<tree> asan_decl_vec;
 
+  /* Vector of tag offsets representing the tag for each stack variable.
+     Each offset determines the difference between the randomly generated
+     tag for the current frame and the tag for this stack variable.  */
+  auto_vec<uint8_t> hwasan_tag_vec;
+
   /* Base pseudo register for Address Sanitizer protected automatic vars.  */
   rtx asan_base;
 
@@ -1049,6 +1069,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
   size_t si, i, j, n = stack_vars_num;
   poly_uint64 large_size = 0, large_alloc = 0;
   rtx large_base = NULL;
+  rtx large_untagged_base = NULL;
   unsigned large_align = 0;
   bool large_allocation_done = false;
   tree decl;
@@ -1095,11 +1116,17 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	}
     }
 
+  if (hwasan_sanitize_stack_p () && data->asan_base == NULL)
+    {
+      data->asan_base = gen_reg_rtx (Pmode);
+      hwasan_record_base (data->asan_base);
+    }
+
   for (si = 0; si < n; ++si)
     {
       rtx base;
       unsigned base_align, alignb;
-      poly_int64 offset;
+      poly_int64 offset = 0;
 
       i = stack_vars_sorted[si];
 
@@ -1120,10 +1147,36 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       if (pred && !pred (i))
 	continue;
 
+      base = hwasan_sanitize_stack_p ()
+	? data->asan_base
+	: virtual_stack_vars_rtx;
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  base = virtual_stack_vars_rtx;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Allocate zero bytes to take advantage of the
+		 alloc_stack_frame_space logic of ensuring the stack is aligned
+		 despite having poly_int64's to deal with.
+
+		 There must be no tag granule "shared" between different
+		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
+		 chunk can have more than one object in it.
+
+		 We ensure this by forcing the end of the last bit of data to
+		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
+		 the start of each variable to be aligned to
+		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
+
+		 We can't align just one of the start or end, since there are
+		 untagged things stored on the stack that we have no control on
+		 the alignment and these can't share a tag granule with a
+		 tagged variable.  */
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	      offset = alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (offset);
+	      data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
+	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
 	  HOST_WIDE_INT prev_offset;
@@ -1203,6 +1256,9 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
 	      base_align = crtl->max_used_stack_slot_alignment;
 	    }
+
+	  if (hwasan_sanitize_stack_p ())
+	    data->hwasan_vec.safe_push (offset);
 	}
       else
 	{
@@ -1222,14 +1278,31 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      loffset = alloc_stack_frame_space
 		(rtx_to_poly_int64 (large_allocsize),
 		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
-	      large_base = get_dynamic_stack_base (loffset, large_align);
+	      large_base = get_dynamic_stack_base (loffset, large_align, base);
 	      large_allocation_done = true;
 	    }
-	  gcc_assert (large_base != NULL);
 
+	  gcc_assert (large_base != NULL);
 	  large_alloc = aligned_upper_bound (large_alloc, alignb);
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* An object with a large alignment requirement means that the
+		 alignment requirement is greater than the required alignment
+		 for tags.  */
+	      if (!large_untagged_base)
+		large_untagged_base = hwasan_create_untagged_base (large_base);
+	      data->hwasan_vec.safe_push (large_alloc);
+	      data->hwasan_untagged_base_vec.safe_push (large_untagged_base);
+	    }
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* Ensure the end of the variable is also aligned correctly.  */
+	      poly_int64 align_again =
+		aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
+	      data->hwasan_vec.safe_push (align_again);
+	    }
 
 	  base = large_base;
 	  base_align = large_align;
@@ -1241,7 +1314,21 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	{
 	  expand_one_stack_var_at (stack_vars[j].decl,
 				   base, base_align,
-				   offset);
+				   offset,
+				   hwasan_sanitize_stack_p ()
+				   ? data->asan_base
+				   : virtual_stack_vars_rtx);
+	}
+
+      if (hwasan_sanitize_stack_p ())
+	{
+	  /* Record the tag for this object in `data` so the prologue knows
+	     what tag to put in the shadow memory during cfgexpand.c.
+	     Then increment the tag so that the next object has a different
+	     tag to this object.  */
+	  data->hwasan_base_vec.safe_push (base);
+	  data->hwasan_tag_vec.safe_push (hwasan_current_tag ());
+	  hwasan_increment_tag ();
 	}
     }
 
@@ -1338,7 +1425,8 @@ expand_one_stack_var_1 (tree var)
   offset = alloc_stack_frame_space (size, byte_align);
 
   expand_one_stack_var_at (var, virtual_stack_vars_rtx,
-			   crtl->max_used_stack_slot_alignment, offset);
+			   crtl->max_used_stack_slot_alignment, offset,
+			   virtual_stack_vars_rtx);
 }
 
 /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
@@ -1551,8 +1639,13 @@ defer_stack_allocation (tree var, bool toplevel)
 
   /* If stack protection is enabled, *all* stack variables must be deferred,
      so that we can re-order the strings to the top of the frame.
-     Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || asan_sanitize_stack_p ())
+     Similarly for Address Sanitizer.
+     When tagging memory we defer all stack variables so we can handle them in
+     one place (handle here meaning ensure they are aligned and record
+     information on each variables position in the stack).  */
+  if (flag_stack_protect
+      || asan_sanitize_stack_p ()
+      || hwasan_sanitize_stack_p ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME
@@ -1937,6 +2030,8 @@ init_vars_expansion (void)
   /* Initialize local stack smashing state.  */
   has_protected_decls = false;
   has_short_buffer = false;
+  if (hwasan_sanitize_stack_p ())
+    hwasan_tag_init ();
 }
 
 /* Free up stack variable graph data.  */
@@ -2296,12 +2391,27 @@ expand_used_vars (void)
 	}
 
       expand_stack_vars (NULL, &data);
+
+      if (hwasan_sanitize_stack_p ())
+	var_end_seq
+	  = hwasan_emit_prologue (data.hwasan_base_vec.address (),
+				  data.hwasan_untagged_base_vec.address (),
+				  data.hwasan_vec.address (),
+				  data.hwasan_tag_vec.address (),
+				  data.hwasan_vec.length ());
     }
 
   if (asan_sanitize_allocas_p () && cfun->calls_alloca)
     var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
 					      virtual_stack_vars_rtx,
 					      var_end_seq);
+  else if (hwasan_sanitize_allocas_p () && cfun->calls_alloca)
+    /* Here we replace `var_end_seq` while for asan we append to `var_end_seq`.
+       This is for the out-of-line case which uses only one function call in
+       either case (and hence we want to replace the previous function call if
+       we're changing things).  */
+    var_end_seq = hwasan_emit_untag_frame (virtual_stack_dynamic_rtx,
+					   virtual_stack_vars_rtx);
 
   fini_vars_expansion ();
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 38261cd1cb3fdeb9a3ecf42a6b526487f412bd76..b975554825e16f7f09770839cb33d1204b43dedf 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2974,6 +2974,31 @@ A target hook which lets a backend compute the set of pressure classes to  be us
 True if backend architecture naturally supports ignoring the top byte of pointers.  This feature means that @option{-fsanitize=hwaddress} can work.
 @end deftypefn
 
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
+Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by TAG_OFFSET.
+The resulting RTX must either be a valid memory address or be able to get
+put into an operand with force_operand.  If overridden the more common case
+is that we force this into an operand using the backend hook
+"addtag_force_operand" that is called in force_operand.
+
+It is expected that that "addtag_force_operand" recognises the RTX
+generated by "addtag" and emits code to force that RTX into an operand.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADDTAG_FORCE_OPERAND (rtx @var{oper}, rtx @var{target})
+If the RTL expression OPER is of the form generated by targetm.memtag.addtag,
+then emit instructions to move the value into an operand (i.e. for
+force_operand).
+TARGET is an RTX suggestion of where to generate the value.
+This hook is most often implemented by emitting instructions to put the
+expression into a pseudo register, then returning that pseudo register.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_MEMTAG_GENTAG (rtx @var{base}, rtx @var{untagged})
+Set the BASE argument to UNTAGGED with some random tag.
+This function is used to generate a tagged base for the current stack frame.
+@end deftypefn
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index ac5d9b0879c1909709b85b2aaf25017a7b5e3a26..f109d934ef59d3d77236ca7e79b8969373babfcb 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2375,6 +2375,12 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_ADDTAG
+
+@hook TARGET_MEMTAG_ADDTAG_FORCE_OPERAND
+
+@hook TARGET_MEMTAG_GENTAG
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/explow.h b/gcc/explow.h
index 5110ad82d6a024fda1d3a3eaf80de40c5e6ad3b6..333948e0c69a1b1132e9a1d06707dc63f1226262 100644
--- a/gcc/explow.h
+++ b/gcc/explow.h
@@ -102,7 +102,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
 extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
 
 /* Returns the address of the dynamic stack space without allocating it.  */
-extern rtx get_dynamic_stack_base (poly_int64, unsigned);
+extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
 
 /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
 extern rtx align_dynamic_address (rtx, unsigned);
diff --git a/gcc/explow.c b/gcc/explow.c
index d174632c5fccdee3336e522b71c1f662ae967da7..5fadf56db395ac8560a7065d5f88d570ec31861d 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1577,10 +1577,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
    OFFSET is the offset of the area into the virtual stack vars area.
 
    REQUIRED_ALIGN is the alignment (in bits) required for the region
-   of memory.  */
+   of memory.
+
+   BASE is the rtx of the base of this virtual stack vars area.
+   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+   on the stack.  */
 
 rtx
-get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
 {
   rtx target;
 
@@ -1588,7 +1592,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
     crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
 
   target = gen_reg_rtx (Pmode);
-  emit_move_insn (target, virtual_stack_vars_rtx);
+  emit_move_insn (target, base);
   target = expand_binop (Pmode, add_optab, target,
 			 gen_int_mode (offset, Pmode),
 			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
diff --git a/gcc/expr.c b/gcc/expr.c
index 20aa939709cd3ee64dbc6c588ed8efee19c5b657..669d1e08d24aebae608c6bcaa07c6fb9732ba3d5 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -7529,6 +7529,13 @@ force_operand (rtx value, rtx target)
       return subtarget;
     }
 
+  if (targetm.memtag.addtag_force_operand)
+    {
+      rtx ret = targetm.memtag.addtag_force_operand (value, target);
+      if (ret)
+	return ret;
+    }
+
   if (ARITHMETIC_P (value))
     {
       op2 = XEXP (value, 1);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 374d15007d868363d9b4fbf467e1e462abbca61a..7bd50715f24a2cb154b578e2abdea4e8fcdb2107 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
+/* Hardware Address Sanitizer.  */
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
+		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
+
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/target.def b/gcc/target.def
index 313ad34f39d4c6b835c405e4c3051c82840cc588..273860c5faede0203dbc3930652f92804c7b0e8a 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6796,6 +6796,37 @@ DEFHOOK
  pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
  bool, (), default_memtag_can_tag_addresses)
 
+DEFHOOK
+(addtag,
+ "Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by\
+ TAG_OFFSET.\n\
+The resulting RTX must either be a valid memory address or be able to get\n\
+put into an operand with force_operand.  If overridden the more common case\n\
+is that we force this into an operand using the backend hook\n\
+\"addtag_force_operand\" that is called in force_operand.\n\
+\n\
+It is expected that that \"addtag_force_operand\" recognises the RTX\n\
+generated by \"addtag\" and emits code to force that RTX into an operand.",
+rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
+default_memtag_addtag)
+
+DEFHOOK
+(addtag_force_operand,
+ "If the RTL expression OPER is of the form generated by targetm.memtag.addtag,\n\
+then emit instructions to move the value into an operand (i.e. for\n\
+force_operand).\n\
+TARGET is an RTX suggestion of where to generate the value.\n\
+This hook is most often implemented by emitting instructions to put the\n\
+expression into a pseudo register, then returning that pseudo register.",
+rtx, (rtx oper, rtx target), NULL)
+
+DEFHOOK
+(gentag,
+ "Set the BASE argument to UNTAGGED with some random tag.\n\
+This function is used to generate a tagged base for the current stack frame.",
+  void, (rtx base, rtx untagged),
+  default_memtag_gentag)
+
 HOOK_VECTOR_END (memtag)
 #undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index bba7acc0dbe194cb880d4677e029d772e9191450..73432e9d41b88df3b6225bfe410b344129ea5e78 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -284,4 +284,6 @@ extern bool speculation_safe_value_not_needed (bool);
 extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 
 extern bool default_memtag_can_tag_addresses ();
+extern void default_memtag_gentag (rtx, rtx);
+extern rtx default_memtag_addtag (rtx, poly_int64, uint8_t);
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 7f2b1ed8493917573f383084abbb25962fa9a893..f6a0614feb304d000d180d18bbdfeccfeb302be6 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "flags.h"
 #include "explow.h"
+#include "expmed.h"
 #include "calls.h"
 #include "expr.h"
 #include "output.h"
@@ -83,6 +84,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "sbitmap.h"
 #include "function-abi.h"
+#include "attribs.h"
+#include "asan.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -2370,4 +2373,78 @@ default_memtag_can_tag_addresses ()
   return false;
 }
 
+void
+default_memtag_gentag (rtx base, rtx untagged)
+{
+  gcc_assert (param_hwasan_stack);
+  if (param_hwasan_random_frame_tag)
+    {
+    rtx temp = gen_reg_rtx (QImode);
+    rtx ret = init_one_libfunc ("__hwasan_generate_tag");
+    rtx new_tag = emit_library_call_value (ret, temp, LCT_NORMAL, QImode);
+    emit_move_insn (base, untagged);
+    /* We know that `base` is not the stack pointer, since we never want to put
+      a randomly generated tag into the stack pointer.  Hence we can use
+      `store_bit_field` which on aarch64 generates a `bfi` which can not act on
+      the stack pointer.  */
+    store_bit_field (base, 8, 56, 0, 0, QImode, new_tag, false);
+    }
+  else
+    {
+      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
+	 In the future we may add the option emit random tags with inline
+	 instrumentation instead of function calls.  This would be the same
+	 between the kernel and userland.  */
+      emit_move_insn (base, untagged);
+    }
+}
+
+rtx
+default_memtag_addtag (rtx base, poly_int64 offset, uint8_t tag_offset)
+{
+  /* Need to look into what the most efficient code sequence is.
+     This is a code sequence that would be emitted *many* times, so we
+     want it as small as possible.
+
+     If the tag offset is greater that (1 << 7) then the most efficient
+     sequence here would give UB from signed integer overflow in the
+     poly_int64.  Hence in that case we emit the slightly less efficient
+     sequence.
+
+     There are two places where tag overflow is a question:
+       - Tagging the shadow stack.
+	  (both tagging and untagging).
+       - Tagging addressable pointers.
+
+     We need to ensure both behaviours are the same (i.e. that the tag that
+     ends up in a pointer after "overflowing" the tag bits with a tag addition
+     is the same that ends up in the shadow space).
+
+     The aim is that the behaviour of tag addition should follow modulo
+     wrapping in both instances.
+
+     The libhwasan code doesn't have any path that increments a pointers tag,
+     which means it has no opinion on what happens when a tag increment
+     overflows (and hence we can choose our own behaviour).  */
+
+  if (tag_offset < (1 << 7))
+    {
+      offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
+      return plus_constant (Pmode, base, offset);
+    }
+  else
+    {
+      /* This is the fallback, it would be nice if it had less instructions,
+	 but we can look for cleverer ways later.  */
+      uint64_t tag_mask = ~(0xFFUL << HWASAN_SHIFT);
+      rtx untagged_base = gen_rtx_AND (Pmode, GEN_INT (tag_mask), base);
+      rtx new_addr = plus_constant (Pmode, untagged_base, offset);
+
+      rtx original_tag_value = gen_rtx_LSHIFTRT (Pmode, base, GEN_INT (HWASAN_SHIFT));
+      rtx new_tag_value = plus_constant (Pmode, original_tag_value, tag_offset);
+      rtx new_tag = gen_rtx_ASHIFT (Pmode, new_tag_value, GEN_INT (HWASAN_SHIFT));
+      return gen_rtx_IOR (Pmode, new_addr, new_tag);
+    }
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 9c6c51aa57b0579738374661dcdea4373daf653d..d2706b412d9f34ef25acb21981a3c9e02e190ed5 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -510,6 +510,9 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      if (flag_sanitize & SANITIZE_HWADDRESS)
+	hwasan_finish_file ();
+
       omp_finish_file ();
 
       hsa_output_brig ();


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [Patch 0/X] HWASAN v3
  2019-11-05 11:32 ` Matthew Malcomson
                     ` (28 preceding siblings ...)
  2019-11-07 18:38   ` [PATCH 7/X] [libsanitizer] Add tests Matthew Malcomson
@ 2019-12-12 15:19   ` Matthew Malcomson
  2019-12-12 15:19     ` [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
                       ` (18 more replies)
  29 siblings, 19 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-12-12 15:19 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, rguenther, kcc, law, dvyukov, Martin Liska, Richard Earnshaw,
	Kyrylo Tkachov, Marcus Shawcroft, ian, dodji, joseph, jakub

[-- Attachment #1: Type: text/plain, Size: 2340 bytes --]

Hello,

I've gone through the suggestions Martin made and implemented  the ones I think
I can implement for GCC10.

The two functionality changes in this version are:
Added the --param's hwasan-instrument-reads, hwasan-instrument-writes,
hwasan-instrument-allocas, hwasan-memintrin, options.  I.e. Those that asan has
and that make sense for hwasan.

Avoided HWASAN_STACK_BACKGROUND in hwasan_increment_tag when using a
deterministic tagging approach.


There are a lot of extra comments and tests.


Bootstrapped and regtested on x86_64 and AArch64.
Bootstrapped with `--with-build-config=bootstrap-hwasan` on AArch64 and hwasan
features tested there.
Built the linux kernel using this feature and ran the test_kasan.ko testing to
check the this works for the kernel.
(NOTE: I actually did all the above testing before a search and replace of
`memory_tagging_p` for `hwasan_sanitize_p` and fixing a typo in the
`hwasan-instrument-allocas` parameter name, I will run all the tests again
before committing but figure I'll send this out now since I fully expect the
tests to still pass).


I noticed one extra testsuite failure from those mentioned in the previous
version emails: g++.dg/cpp2a/ucn2.C.
I believe this is HWASAN correctly catching a problem in the compiler.
I've logged the issue here https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92919 .


I haven't gotten ASAN_MARK to print as HWASAN_MARK when using memory tagging,
since I'm not sure the way I found to implement this would be acceptable.  The
inlined patch below works but it requires a special declaration instead of just
an ~#include~.


diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h
index a1bc081..d81eb12 100644
--- a/gcc/internal-fn.h
+++ b/gcc/internal-fn.h
@@ -101,10 +101,16 @@ extern void init_internal_fns ();
 
 extern const char *const internal_fn_name_array[];
 
+
+extern bool hwasan_sanitize_p (void);
 static inline const char *
 internal_fn_name (enum internal_fn fn)
 {
-  return internal_fn_name_array[(int) fn];
+  const char *ret = internal_fn_name_array[(int) fn];
+  if (! strcmp (ret, "ASAN_MARK")
+      && hwasan_sanitize_p ())
+    return "HWASAN_MARK";
+  return ret;
 }
 
 extern internal_fn lookup_internal_fn (const char *);


Entire patch series attached to cover letter.

[-- Attachment #2: all-patches.tar.gz --]
[-- Type: application/gzip, Size: 61477 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
@ 2019-12-12 15:19     ` Matthew Malcomson
  2019-12-12 15:47       ` Matthew Malcomson
  2019-12-12 16:58       ` Kyrill Tkachov
  2019-12-12 15:19     ` [PATCH 4/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
                       ` (17 subsequent siblings)
  18 siblings, 2 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-12-12 15:19 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 5924 bytes --]

This is an analogous option to --bootstrap-asan to configure.  It allows
bootstrapping GCC using HWASAN.

For the same reasons as for ASAN we have to avoid using the HWASAN
sanitizer when compiling libiberty and the lto-plugin.

Also add a function to query whether -fsanitize=hwaddress has been
passed.

ChangeLog:

2019-08-29  Matthew Malcomson  <matthew.malcomson@arm.com>

	* configure: Regenerate.
	* configure.ac: Add --bootstrap-hwasan option.

config/ChangeLog:

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

	* bootstrap-hwasan.mk: New file.

libiberty/ChangeLog:

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

	* configure: Regenerate.
	* configure.ac: Avoid using sanitizer.

lto-plugin/ChangeLog:

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

	* Makefile.am: Avoid using sanitizer.
	* Makefile.in: Regenerate.



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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index aec9186b2b0123d3088b69eb1ee541567654953e..6f71b111bd18ec053180beecf83dd4549e83c2b9 100755
--- a/configure
+++ b/configure
@@ -7270,7 +7270,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index b8ce2ad20b9d03e42731252a9ec2a8417c13e566..16bfdf164555dad94c789f17b6a63ba1a2e3e9f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2775,7 +2775,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index 6c9579bfaff955eb43875b404fb7db1a667bf522..da9a8809c3440827ac22ef6936e080820197f4e7 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2645,6 +2645,13 @@ Some examples of build configurations designed for developers of GCC are:
 Compiles GCC itself using Address Sanitization in order to catch invalid memory
 accesses within the GCC code.
 
+@item @samp{bootstrap-hwasan}
+Compiles GCC itself using HWAddress Sanitization in order to catch invalid
+memory accesses within the GCC code.  This option is only available on AArch64
+targets with a very recent linux kernel (5.4 or later).
+
+@end table
+
 @section Building a cross compiler
 
 When building a cross compiler, it is not generally possible to do a
diff --git a/libiberty/configure b/libiberty/configure
index 7a34dabec32b0b383bd33f07811757335f4dd39c..cb2dd4ff5295598343cc18b3a79a86a778f2261d 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5261,6 +5261,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index f1ce76010c9acde79c5dc46686a78b2e2f19244e..043237628b79cbf37d07359b59c5ffe17a7a22ef 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index 28dc21014b2e86988fa88adabd63ce6092e18e02..34aa397d785e3cc9b6975de460d065900364c3ff 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 8dd6e40ac9dddab39fe1752f9a70e6834ab3c926..7acfc047eff6f86f8d38287e6ffb6533c4c13500 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -672,8 +672,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


[-- Attachment #2: hwasan-patch2.patch --]
[-- Type: text/plain, Size: 4819 bytes --]

diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index aec9186b2b0123d3088b69eb1ee541567654953e..6f71b111bd18ec053180beecf83dd4549e83c2b9 100755
--- a/configure
+++ b/configure
@@ -7270,7 +7270,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index b8ce2ad20b9d03e42731252a9ec2a8417c13e566..16bfdf164555dad94c789f17b6a63ba1a2e3e9f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2775,7 +2775,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index 6c9579bfaff955eb43875b404fb7db1a667bf522..da9a8809c3440827ac22ef6936e080820197f4e7 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2645,6 +2645,13 @@ Some examples of build configurations designed for developers of GCC are:
 Compiles GCC itself using Address Sanitization in order to catch invalid memory
 accesses within the GCC code.
 
+@item @samp{bootstrap-hwasan}
+Compiles GCC itself using HWAddress Sanitization in order to catch invalid
+memory accesses within the GCC code.  This option is only available on AArch64
+targets with a very recent linux kernel (5.4 or later).
+
+@end table
+
 @section Building a cross compiler
 
 When building a cross compiler, it is not generally possible to do a
diff --git a/libiberty/configure b/libiberty/configure
index 7a34dabec32b0b383bd33f07811757335f4dd39c..cb2dd4ff5295598343cc18b3a79a86a778f2261d 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5261,6 +5261,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index f1ce76010c9acde79c5dc46686a78b2e2f19244e..043237628b79cbf37d07359b59c5ffe17a7a22ef 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index 28dc21014b2e86988fa88adabd63ce6092e18e02..34aa397d785e3cc9b6975de460d065900364c3ff 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 8dd6e40ac9dddab39fe1752f9a70e6834ab3c926..7acfc047eff6f86f8d38287e6ffb6533c4c13500 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -672,8 +672,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 1/X] [libsanitizer] Tie the hwasan library into our build system
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (5 preceding siblings ...)
  2019-12-12 15:19     ` [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN Matthew Malcomson
@ 2019-12-12 15:19     ` Matthew Malcomson
  2019-12-12 15:37     ` Document --with-build-config=bootstrap-asan option Matthew Malcomson
                       ` (11 subsequent siblings)
  18 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-12-12 15:19 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 45617 bytes --]

This patch tries to tie libhwasan into the GCC build system in the same way
that the other sanitizer runtime libraries are handled.

libsanitizer/ChangeLog:

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

	* Makefile.am:  Build libhwasan.
	* Makefile.in:  Build libhwasan.
	* asan/Makefile.in:  Build libhwasan.
	* configure:  Build libhwasan.
	* configure.ac:  Build libhwasan.
	* hwasan/Makefile.am: New file.
	* hwasan/Makefile.in: New file.
	* hwasan/libtool-version: New file.
	* interception/Makefile.in: Build libhwasan.
	* libbacktrace/Makefile.in: Build libhwasan.
	* libsanitizer.spec.in: Build libhwasan.
	* lsan/Makefile.in: Build libhwasan.
	* merge.sh: Build libhwasan.
	* sanitizer_common/Makefile.in: Build libhwasan.
	* tsan/Makefile.in: Build libhwasan.
	* ubsan/Makefile.in: Build libhwasan.



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


diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 65ed1e712378ef453f820f86c4d3221f9dee5f2c..2a7e8e1debe838719db0f0fad218b2543cc3111b 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,11 +14,12 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan
+SUBDIRS += lsan asan ubsan hwasan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
-  include/sanitizer/tsan_interface.h
+  include/sanitizer/tsan_interface.h \
+  include/sanitizer/hwasan_interface.h
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 0d789b3a59d21ea2e5a23057ca3afe15425feec4..36aa952af7e04bc0e4fb94cdcd584d539193d781 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -92,7 +92,8 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@am__append_1 = include/sanitizer/common_interface_defs.h \
 @SANITIZER_SUPPORTED_TRUE@	include/sanitizer/lsan_interface.h \
 @SANITIZER_SUPPORTED_TRUE@	include/sanitizer/asan_interface.h \
-@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/tsan_interface.h
+@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/tsan_interface.h \
+@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/hwasan_interface.h
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
@@ -206,7 +207,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan tsan
+	ubsan hwasan tsan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -328,6 +329,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
@@ -361,7 +363,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	$(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in
index 00b6082da5372efd679ddc230f588bbc58161ef6..76689c3b224b1fb04895ae48829eac4b6784cd84 100644
--- a/libsanitizer/asan/Makefile.in
+++ b/libsanitizer/asan/Makefile.in
@@ -382,6 +382,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/configure b/libsanitizer/configure
index 79b5c1eadb59018bca13a33f19f3494c170365ee..ff72af73e6f77aaf93bf39e6799f896851a377dd 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -657,6 +657,7 @@ USING_MAC_INTERPOSE_TRUE
 link_liblsan
 link_libubsan
 link_libtsan
+link_libhwasan
 link_libasan
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
@@ -12334,7 +12335,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12337 "configure"
+#line 12338 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12440,7 +12441,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12443 "configure"
+#line 12444 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15916,6 +15917,10 @@ fi
 link_libasan=$link_sanitizer_common
 
 
+# Set up the set of additional libraries that we need to link against for libhwasan.
+link_libhwasan=$link_sanitizer_common
+
+
 # Set up the set of additional libraries that we need to link against for libtsan.
 link_libtsan=$link_sanitizer_common
 
@@ -16786,7 +16791,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -18063,6 +18068,7 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
 
@@ -19998,6 +20004,17 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+   sed -f vpsed$$ $ac_file > tmp$$
+   mv tmp$$ $ac_file
+   rm vpsed$$
+   echo 'MULTISUBDIR =' >> $ac_file
+   ml_norecursion=yes
+   . ${multi_basedir}/config-ml.in
+   { ml_norecursion=; unset ml_norecursion;}
+ ;;
     "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index 8aac33cb5c980db77e31e8d799ab5a12f3e3a404..02c0c235171e4c8249821c5db91414f8e4bfb3ee 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -111,6 +111,10 @@ AC_CHECK_LIB(dl, dlsym,
 link_libasan=$link_sanitizer_common
 AC_SUBST(link_libasan)
 
+# Set up the set of additional libraries that we need to link against for libhwasan.
+link_libhwasan=$link_sanitizer_common
+AC_SUBST(link_libhwasan)
+
 # Set up the set of additional libraries that we need to link against for libtsan.
 link_libtsan=$link_sanitizer_common
 AC_SUBST(link_libtsan)
@@ -339,7 +343,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
diff --git a/libsanitizer/hwasan/Makefile.am b/libsanitizer/hwasan/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..a20705ae1277b49d2edc464e23b106c5e6086d35
--- /dev/null
+++ b/libsanitizer/hwasan/Makefile.am
@@ -0,0 +1,88 @@
+AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
+ 
+# May be used by toolexeclibdir.
+gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
+
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -fno-ipa-icf
+AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+AM_CXXFLAGS += -std=gnu++11
+AM_CXXFLAGS += $(EXTRA_CXXFLAGS)
+ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
+
+toolexeclib_LTLIBRARIES = libhwasan.la
+
+hwasan_files = \
+	       hwasan_allocator.cpp \
+	       hwasan.cpp \
+	       hwasan_dynamic_shadow.cpp \
+	       hwasan_exceptions.cpp \
+	       hwasan_flags.inc \
+	       hwasan_interceptors.cpp \
+	       hwasan_interceptors_vfork.S \
+	       hwasan_linux.cpp \
+	       hwasan_memintrinsics.cpp \
+	       hwasan_new_delete.cpp \
+	       hwasan_poisoning.cpp \
+	       hwasan_report.cpp \
+	       hwasan_setjmp.S \
+	       hwasan_tag_mismatch_aarch64.S \
+	       hwasan_thread.cpp \
+	       hwasan_thread_list.cpp \
+	       hwasan_type_test.cpp
+
+libhwasan_la_SOURCES = $(hwasan_files)
+libhwasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la
+if !USING_MAC_INTERPOSE
+libhwasan_la_LIBADD += $(top_builddir)/interception/libinterception.la
+endif
+if LIBBACKTRACE_SUPPORTED
+libhwasan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
+endif
+libhwasan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+
+libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan)
+
+# Work around what appears to be a GNU make bug handling MAKEFLAGS
+# values defined in terms of make variables, as is the case for CC and
+# friends when we are called from the top level Makefile.
+AM_MAKEFLAGS = \
+	"AR_FLAGS=$(AR_FLAGS)" \
+	"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
+	"CFLAGS=$(CFLAGS)" \
+	"CXXFLAGS=$(CXXFLAGS)" \
+	"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
+	"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
+	"INSTALL=$(INSTALL)" \
+	"INSTALL_DATA=$(INSTALL_DATA)" \
+	"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
+	"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
+	"JC1FLAGS=$(JC1FLAGS)" \
+	"LDFLAGS=$(LDFLAGS)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
+	"MAKE=$(MAKE)" \
+	"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
+	"PICFLAG=$(PICFLAG)" \
+	"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
+	"SHELL=$(SHELL)" \
+	"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
+	"exec_prefix=$(exec_prefix)" \
+	"infodir=$(infodir)" \
+	"libdir=$(libdir)" \
+	"prefix=$(prefix)" \
+	"includedir=$(includedir)" \
+	"AR=$(AR)" \
+	"AS=$(AS)" \
+	"LD=$(LD)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"NM=$(NM)" \
+	"PICFLAG=$(PICFLAG)" \
+	"RANLIB=$(RANLIB)" \
+	"DESTDIR=$(DESTDIR)"
+
+MAKEOVERRIDES=
+
+## ################################################################
+
+
diff --git a/libsanitizer/hwasan/Makefile.in b/libsanitizer/hwasan/Makefile.in
new file mode 100644
index 0000000000000000000000000000000000000000..56a0a9553d642ac6ef6c4ac7484eac2b08548e6a
--- /dev/null
+++ b/libsanitizer/hwasan/Makefile.in
@@ -0,0 +1,800 @@
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+@USING_MAC_INTERPOSE_FALSE@am__append_1 = $(top_builddir)/interception/libinterception.la
+@LIBBACKTRACE_SUPPORTED_TRUE@am__append_2 = $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
+subdir = hwasan
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
+	$(top_srcdir)/../config/depstand.m4 \
+	$(top_srcdir)/../config/lead-dot.m4 \
+	$(top_srcdir)/../config/libstdc++-raw-cxx.m4 \
+	$(top_srcdir)/../config/multi.m4 \
+	$(top_srcdir)/../config/override.m4 \
+	$(top_srcdir)/../config/stdint.m4 \
+	$(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \
+	$(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \
+	$(top_srcdir)/acinclude.m4 $(top_srcdir)/../libtool.m4 \
+	$(top_srcdir)/../config/enable.m4 \
+	$(top_srcdir)/../config/cet.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am
+mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(toolexeclibdir)"
+LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libhwasan_la_DEPENDENCIES =  \
+	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
+	$(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1)
+am__objects_1 = hwasan_allocator.lo hwasan.lo hwasan_dynamic_shadow.lo \
+	hwasan_exceptions.lo hwasan_interceptors.lo \
+	hwasan_interceptors_vfork.lo hwasan_linux.lo \
+	hwasan_memintrinsics.lo hwasan_new_delete.lo \
+	hwasan_poisoning.lo hwasan_report.lo hwasan_setjmp.lo \
+	hwasan_tag_mismatch_aarch64.lo hwasan_thread.lo \
+	hwasan_thread_list.lo hwasan_type_test.lo
+am_libhwasan_la_OBJECTS = $(am__objects_1)
+libhwasan_la_OBJECTS = $(am_libhwasan_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libhwasan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+	$(CXXFLAGS) $(libhwasan_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/../depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS)
+LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CCAS) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CCASFLAGS) $(CCASFLAGS)
+AM_V_CPPAS = $(am__v_CPPAS_@AM_V@)
+am__v_CPPAS_ = $(am__v_CPPAS_@AM_DEFAULT_V@)
+am__v_CPPAS_0 = @echo "  CPPAS   " $@;
+am__v_CPPAS_1 = 
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo "  CXX     " $@;
+am__v_CXX_1 = 
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+	$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo "  CXXLD   " $@;
+am__v_CXXLD_1 = 
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libhwasan_la_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+ACLOCAL = @ACLOCAL@
+ALLOC_FILE = @ALLOC_FILE@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BACKTRACE_SUPPORTED = @BACKTRACE_SUPPORTED@
+BACKTRACE_SUPPORTS_THREADS = @BACKTRACE_SUPPORTS_THREADS@
+BACKTRACE_USES_MALLOC = @BACKTRACE_USES_MALLOC@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXTRA_ASFLAGS = @EXTRA_ASFLAGS@
+EXTRA_CFLAGS = @EXTRA_CFLAGS@
+EXTRA_CXXFLAGS = @EXTRA_CXXFLAGS@
+FGREP = @FGREP@
+FORMAT_FILE = @FORMAT_FILE@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSTDCXX_RAW_CXX_CXXFLAGS = @LIBSTDCXX_RAW_CXX_CXXFLAGS@
+LIBSTDCXX_RAW_CXX_LDFLAGS = @LIBSTDCXX_RAW_CXX_LDFLAGS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
+SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS = @SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
+VERSION = @VERSION@
+VIEW_FILE = @VIEW_FILE@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+enable_shared = @enable_shared@
+enable_static = @enable_static@
+exec_prefix = @exec_prefix@
+get_gcc_base_ver = @get_gcc_base_ver@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
+link_liblsan = @link_liblsan@
+link_libtsan = @link_libtsan@
+link_libubsan = @link_libubsan@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+multi_basedir = @multi_basedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_noncanonical = @target_noncanonical@
+target_os = @target_os@
+target_vendor = @target_vendor@
+toolexecdir = @toolexecdir@
+toolexeclibdir = @toolexeclibdir@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
+
+# May be used by toolexeclibdir.
+gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
+	-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
+	-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
+	-Wno-variadic-macros -fno-ipa-icf \
+	$(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=gnu++11 $(EXTRA_CXXFLAGS)
+ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
+toolexeclib_LTLIBRARIES = libhwasan.la
+hwasan_files = \
+	       hwasan_allocator.cpp \
+	       hwasan.cpp \
+	       hwasan_dynamic_shadow.cpp \
+	       hwasan_exceptions.cpp \
+	       hwasan_flags.inc \
+	       hwasan_interceptors.cpp \
+	       hwasan_interceptors_vfork.S \
+	       hwasan_linux.cpp \
+	       hwasan_memintrinsics.cpp \
+	       hwasan_new_delete.cpp \
+	       hwasan_poisoning.cpp \
+	       hwasan_report.cpp \
+	       hwasan_setjmp.S \
+	       hwasan_tag_mismatch_aarch64.S \
+	       hwasan_thread.cpp \
+	       hwasan_thread_list.cpp \
+	       hwasan_type_test.cpp
+
+libhwasan_la_SOURCES = $(hwasan_files)
+libhwasan_la_LIBADD =  \
+	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
+	$(am__append_1) $(am__append_2) $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan)
+
+# Work around what appears to be a GNU make bug handling MAKEFLAGS
+# values defined in terms of make variables, as is the case for CC and
+# friends when we are called from the top level Makefile.
+AM_MAKEFLAGS = \
+	"AR_FLAGS=$(AR_FLAGS)" \
+	"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
+	"CFLAGS=$(CFLAGS)" \
+	"CXXFLAGS=$(CXXFLAGS)" \
+	"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
+	"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
+	"INSTALL=$(INSTALL)" \
+	"INSTALL_DATA=$(INSTALL_DATA)" \
+	"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
+	"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
+	"JC1FLAGS=$(JC1FLAGS)" \
+	"LDFLAGS=$(LDFLAGS)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
+	"MAKE=$(MAKE)" \
+	"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
+	"PICFLAG=$(PICFLAG)" \
+	"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
+	"SHELL=$(SHELL)" \
+	"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
+	"exec_prefix=$(exec_prefix)" \
+	"infodir=$(infodir)" \
+	"libdir=$(libdir)" \
+	"prefix=$(prefix)" \
+	"includedir=$(includedir)" \
+	"AR=$(AR)" \
+	"AS=$(AS)" \
+	"LD=$(LD)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"NM=$(NM)" \
+	"PICFLAG=$(PICFLAG)" \
+	"RANLIB=$(RANLIB)" \
+	"DESTDIR=$(DESTDIR)"
+
+MAKEOVERRIDES = 
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .S .cpp .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign hwasan/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign hwasan/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-toolexeclibLTLIBRARIES: $(toolexeclib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(toolexeclibdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(toolexeclibdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(toolexeclibdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(toolexeclibdir)"; \
+	}
+
+uninstall-toolexeclibLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(toolexeclibdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(toolexeclibdir)/$$f"; \
+	done
+
+clean-toolexeclibLTLIBRARIES:
+	-test -z "$(toolexeclib_LTLIBRARIES)" || rm -f $(toolexeclib_LTLIBRARIES)
+	@list='$(toolexeclib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libhwasan.la: $(libhwasan_la_OBJECTS) $(libhwasan_la_DEPENDENCIES) $(EXTRA_libhwasan_la_DEPENDENCIES) 
+	$(AM_V_CXXLD)$(libhwasan_la_LINK) -rpath $(toolexeclibdir) $(libhwasan_la_OBJECTS) $(libhwasan_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_allocator.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_dynamic_shadow.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_exceptions.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors_vfork.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_linux.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_memintrinsics.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_new_delete.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_poisoning.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_report.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_setjmp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_tag_mismatch_aarch64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread_list.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_type_test.Plo@am__quote@
+
+.S.o:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ $<
+
+.S.obj:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.S.lo:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(LTCPPASCOMPILE) -c -o $@ $<
+
+.cpp.o:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+	for dir in "$(DESTDIR)$(toolexeclibdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-toolexeclibLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-toolexeclibLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-toolexeclibLTLIBRARIES cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags dvi dvi-am html html-am info \
+	info-am install install-am install-data install-data-am \
+	install-dvi install-dvi-am install-exec install-exec-am \
+	install-html install-html-am install-info install-info-am \
+	install-man install-pdf install-pdf-am install-ps \
+	install-ps-am install-strip install-toolexeclibLTLIBRARIES \
+	installcheck installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am \
+	uninstall-toolexeclibLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libsanitizer/hwasan/libtool-version b/libsanitizer/hwasan/libtool-version
new file mode 100644
index 0000000000000000000000000000000000000000..d1f9aa1b950cc95efea87b7a70e96e44df0f9c17
--- /dev/null
+++ b/libsanitizer/hwasan/libtool-version
@@ -0,0 +1,6 @@
+# This file is used to maintain libtool version info for libhwasan.  See
+# the libtool manual to understand the meaning of the fields.  This is
+# a separate file so that version updates don't involve re-running
+# automake.
+# CURRENT:REVISION:AGE
+0:0:0
diff --git a/libsanitizer/interception/Makefile.in b/libsanitizer/interception/Makefile.in
index 358afe9d8844e411897eb24cd8f0960d0758e375..ff21143d2fc371d76554d0cb57f49d0def1b5a6f 100644
--- a/libsanitizer/interception/Makefile.in
+++ b/libsanitizer/interception/Makefile.in
@@ -300,6 +300,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/libbacktrace/Makefile.in b/libsanitizer/libbacktrace/Makefile.in
index f0e190a81d82ddc86a3e39a5dc41e864190582c0..f2614e272b8e22028ffdd4e3a06083bf42efced0 100644
--- a/libsanitizer/libbacktrace/Makefile.in
+++ b/libsanitizer/libbacktrace/Makefile.in
@@ -350,6 +350,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/libsanitizer.spec.in b/libsanitizer/libsanitizer.spec.in
index a4fa87d8dfc51549b0f932b31a610db1a2e86c29..70a33574d7b1d494c4875cfc41f29606bde2172e 100644
--- a/libsanitizer/libsanitizer.spec.in
+++ b/libsanitizer/libsanitizer.spec.in
@@ -3,6 +3,8 @@
 
 *link_libasan: @link_libasan@
 
+*link_libhwasan: @link_libhwasan@
+
 *link_libtsan: @link_libtsan@
 
 *link_libubsan: @link_libubsan@
diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in
index 3ebf4ba08f6ac0c323dcdd5e3c1b518fcbd7a67f..0e263e8f14c3af3f2e8b5b4a4468aed0db086fcd 100644
--- a/libsanitizer/lsan/Makefile.in
+++ b/libsanitizer/lsan/Makefile.in
@@ -345,6 +345,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/merge.sh b/libsanitizer/merge.sh
index 6b682f7034e2836916c3c1a4846051f1b3f69324..2831542200ccfd989381897e30f6644f172001d7 100755
--- a/libsanitizer/merge.sh
+++ b/libsanitizer/merge.sh
@@ -68,6 +68,7 @@ CUR_REV=$(get_current_rev)
 echo Current upstream revision: $CUR_REV
 merge include/sanitizer include/sanitizer
 merge lib/asan asan
+merge lib/hwasan hwasan
 merge lib/lsan lsan
 merge lib/tsan/rtl tsan
 merge lib/sanitizer_common sanitizer_common
diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in
index 0fcebc89bc97da31a62bd58e7802d7a8d20007ec..47206082ac1bd5a008b962427db80f8190fef3ec 100644
--- a/libsanitizer/sanitizer_common/Makefile.in
+++ b/libsanitizer/sanitizer_common/Makefile.in
@@ -336,6 +336,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in
index 3d1d9565e4774701eda051b8d8c73f425e0d80aa..36c3579c65d834c7dd6701b2ffb6dcdaadd10aa5 100644
--- a/libsanitizer/tsan/Makefile.in
+++ b/libsanitizer/tsan/Makefile.in
@@ -374,6 +374,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/ubsan/Makefile.in b/libsanitizer/ubsan/Makefile.in
index 71d2626be63d11d2700d0fa729b1fae7956ee7da..3f10c4545bd7ee6caedcd177b743498872571ad0 100644
--- a/libsanitizer/ubsan/Makefile.in
+++ b/libsanitizer/ubsan/Makefile.in
@@ -339,6 +339,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@


[-- Attachment #2: hwasan-patch0.patch.gz --]
[-- Type: application/gzip, Size: 10820 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Document --with-build-config=bootstrap-asan option.
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (6 preceding siblings ...)
  2019-12-12 15:19     ` [PATCH 1/X] [libsanitizer] Tie the hwasan library into our build system Matthew Malcomson
@ 2019-12-12 15:37     ` Matthew Malcomson
  2020-01-11  7:39       ` Gerald Pfeifer
  2019-12-16 11:47     ` [PATCH 7/X] [libsanitizer] Add tests Matthew Malcomson
                       ` (10 subsequent siblings)
  18 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-12-12 15:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, rguenther, kcc, law, dvyukov, Martin Liska, Richard Earnshaw,
	Kyrylo Tkachov, Marcus Shawcroft, ian, dodji, joseph, jakub

Document how to configure using asan (bootstrap-asan option).

Since I'm adding a bootstrap-hwasan option and documenting that, 
bootstrap-asan should also be documented.

(As Martin pointed out in the hwasan reviews).



(FYI I now notice that my hwasan patch 3/X needs this to apply cleanly).



gcc/ChangeLog:

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

	* doc/install.texi: Document bootstrap-asan configuration option.





###########


commit 6e0bbe33120ad3f92e4266ecfe4ecb8ce8958865
Author: Matthew Malcomson <matthew.malcomson@arm.com>
Date:   Wed Nov 6 12:48:08 2019 +0000

     Document bootstrap-asan compilation option

diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index 80b4781..c5937c9 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2670,6 +2670,14 @@ the build tree.

  @end table

+Some examples of build configurations designed for developers of GCC are:
+
+@table @asis
+@item @samp{bootstrap-asan}
+Compiles GCC itself using Address Sanitization in order to catch 
invalid memory
+accesses within the GCC code.
+@end table
+
  @section Building a cross compiler

  When building a cross compiler, it is not generally possible to do a

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-12-12 15:19     ` [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
@ 2019-12-12 15:47       ` Matthew Malcomson
  2019-12-12 16:58       ` Kyrill Tkachov
  1 sibling, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-12-12 15:47 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 6144 bytes --]

Updated to include documentation ChangeLog and apply cleanly on the
bootstrap-asan documentation I mentioned in a different email.



This is an analogous option to --bootstrap-asan to configure.  It allows
bootstrapping GCC using HWASAN.

For the same reasons as for ASAN we have to avoid using the HWASAN
sanitizer when compiling libiberty and the lto-plugin.

Also add a function to query whether -fsanitize=hwaddress has been
passed.

ChangeLog:

2019-08-29  Matthew Malcomson  <matthew.malcomson@arm.com>

	* configure: Regenerate.
	* configure.ac: Add --bootstrap-hwasan option.

config/ChangeLog:

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

	* bootstrap-hwasan.mk: New file.

gcc/ChangeLog:

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

	* doc/install.texi: Document new option.

libiberty/ChangeLog:

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

	* configure: Regenerate.
	* configure.ac: Avoid using sanitizer.

lto-plugin/ChangeLog:

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

	* Makefile.am: Avoid using sanitizer.
	* Makefile.in: Regenerate.



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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index f6027397cedd5c28cd821a0bc7629d6537393ecf..0b502f2784927d72eab248741633305296bf09e7 100755
--- a/configure
+++ b/configure
@@ -7270,7 +7270,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index 50e2fa135b10486170f68ffa162dcbeee2adff90..f793e14e0316e26d05ba72696e8c7c10641c7c96 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2775,7 +2775,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index c5937c94b7cebd3d0f1d7d551a7b593c128e7673..37cb8ed15671205567ee20286e21fb904b3e3c9f 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2676,6 +2676,12 @@ Some examples of build configurations designed for developers of GCC are:
 @item @samp{bootstrap-asan}
 Compiles GCC itself using Address Sanitization in order to catch invalid memory
 accesses within the GCC code.
+
+@item @samp{bootstrap-hwasan}
+Compiles GCC itself using HWAddress Sanitization in order to catch invalid
+memory accesses within the GCC code.  This option is only available on AArch64
+targets with a very recent linux kernel (5.4 or later).
+
 @end table
 
 @section Building a cross compiler
diff --git a/libiberty/configure b/libiberty/configure
index 7a34dabec32b0b383bd33f07811757335f4dd39c..cb2dd4ff5295598343cc18b3a79a86a778f2261d 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5261,6 +5261,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index f1ce76010c9acde79c5dc46686a78b2e2f19244e..043237628b79cbf37d07359b59c5ffe17a7a22ef 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index 28dc21014b2e86988fa88adabd63ce6092e18e02..34aa397d785e3cc9b6975de460d065900364c3ff 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 8dd6e40ac9dddab39fe1752f9a70e6834ab3c926..7acfc047eff6f86f8d38287e6ffb6533c4c13500 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -672,8 +672,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


[-- Attachment #2: documentation-update.patch --]
[-- Type: text/plain, Size: 4776 bytes --]

diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index f6027397cedd5c28cd821a0bc7629d6537393ecf..0b502f2784927d72eab248741633305296bf09e7 100755
--- a/configure
+++ b/configure
@@ -7270,7 +7270,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index 50e2fa135b10486170f68ffa162dcbeee2adff90..f793e14e0316e26d05ba72696e8c7c10641c7c96 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2775,7 +2775,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index c5937c94b7cebd3d0f1d7d551a7b593c128e7673..37cb8ed15671205567ee20286e21fb904b3e3c9f 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2676,6 +2676,12 @@ Some examples of build configurations designed for developers of GCC are:
 @item @samp{bootstrap-asan}
 Compiles GCC itself using Address Sanitization in order to catch invalid memory
 accesses within the GCC code.
+
+@item @samp{bootstrap-hwasan}
+Compiles GCC itself using HWAddress Sanitization in order to catch invalid
+memory accesses within the GCC code.  This option is only available on AArch64
+targets with a very recent linux kernel (5.4 or later).
+
 @end table
 
 @section Building a cross compiler
diff --git a/libiberty/configure b/libiberty/configure
index 7a34dabec32b0b383bd33f07811757335f4dd39c..cb2dd4ff5295598343cc18b3a79a86a778f2261d 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5261,6 +5261,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index f1ce76010c9acde79c5dc46686a78b2e2f19244e..043237628b79cbf37d07359b59c5ffe17a7a22ef 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index 28dc21014b2e86988fa88adabd63ce6092e18e02..34aa397d785e3cc9b6975de460d065900364c3ff 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 8dd6e40ac9dddab39fe1752f9a70e6834ab3c926..7acfc047eff6f86f8d38287e6ffb6533c4c13500 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -672,8 +672,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN
  2019-12-12 15:19     ` [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
  2019-12-12 15:47       ` Matthew Malcomson
@ 2019-12-12 16:58       ` Kyrill Tkachov
  1 sibling, 0 replies; 150+ messages in thread
From: Kyrill Tkachov @ 2019-12-12 16:58 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, dodji, jakub

Hi Matthew,

Martin is the authority on this but I have a small comment inline...

On 12/12/19 3:19 PM, Matthew Malcomson wrote:
> This is an analogous option to --bootstrap-asan to configure.  It allows
> bootstrapping GCC using HWASAN.
>
> For the same reasons as for ASAN we have to avoid using the HWASAN
> sanitizer when compiling libiberty and the lto-plugin.
>
> Also add a function to query whether -fsanitize=hwaddress has been
> passed.
>
> ChangeLog:
>
> 2019-08-29  Matthew Malcomson <matthew.malcomson@arm.com>
>
>         * configure: Regenerate.
>         * configure.ac: Add --bootstrap-hwasan option.
>
> config/ChangeLog:
>
> 2019-12-12  Matthew Malcomson <matthew.malcomson@arm.com>
>
>         * bootstrap-hwasan.mk: New file.
>
> libiberty/ChangeLog:
>
> 2019-12-12  Matthew Malcomson <matthew.malcomson@arm.com>
>
>         * configure: Regenerate.
>         * configure.ac: Avoid using sanitizer.
>
> lto-plugin/ChangeLog:
>
> 2019-12-12  Matthew Malcomson <matthew.malcomson@arm.com>
>
>         * Makefile.am: Avoid using sanitizer.
>         * Makefile.in: Regenerate.
>
>
>
> ###############     Attachment also inlined for ease of reply    
> ###############
>
>
> diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
> new file mode 100644
> index 
> 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
> --- /dev/null
> +++ b/config/bootstrap-hwasan.mk
> @@ -0,0 +1,8 @@
> +# This option enables -fsanitize=hwaddress for stage2 and stage3.
> +
> +STAGE2_CFLAGS += -fsanitize=hwaddress
> +STAGE3_CFLAGS += -fsanitize=hwaddress
> +POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
> + -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
> + -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
> + -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
> diff --git a/configure b/configure
> index 
> aec9186b2b0123d3088b69eb1ee541567654953e..6f71b111bd18ec053180beecf83dd4549e83c2b9 
> 100755
> --- a/configure
> +++ b/configure
> @@ -7270,7 +7270,7 @@ fi
>  # or bootstrap-ubsan, bootstrap it.
>  if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 
> 2>&1; then
>    case "$BUILD_CONFIG" in
> -    *bootstrap-asan* | *bootstrap-ubsan* )
> +    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
> bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
>        bootstrap_fixincludes=yes
>        ;;
> diff --git a/configure.ac b/configure.ac
> index 
> b8ce2ad20b9d03e42731252a9ec2a8417c13e566..16bfdf164555dad94c789f17b6a63ba1a2e3e9f4 
> 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -2775,7 +2775,7 @@ fi
>  # or bootstrap-ubsan, bootstrap it.
>  if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 
> 2>&1; then
>    case "$BUILD_CONFIG" in
> -    *bootstrap-asan* | *bootstrap-ubsan* )
> +    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
> bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
>        bootstrap_fixincludes=yes
>        ;;
> diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
> index 
> 6c9579bfaff955eb43875b404fb7db1a667bf522..da9a8809c3440827ac22ef6936e080820197f4e7 
> 100644
> --- a/gcc/doc/install.texi
> +++ b/gcc/doc/install.texi
> @@ -2645,6 +2645,13 @@ Some examples of build configurations designed 
> for developers of GCC are:
>  Compiles GCC itself using Address Sanitization in order to catch 
> invalid memory
>  accesses within the GCC code.
>
> +@item @samp{bootstrap-hwasan}
> +Compiles GCC itself using HWAddress Sanitization in order to catch 
> invalid
> +memory accesses within the GCC code.  This option is only available 
> on AArch64
> +targets with a very recent linux kernel (5.4 or later).



Using terms like "very recent" in documentation is discouraged. It won't 
be very recent in a couple of years time and I doubt any of us will 
remember to come update this snippet :)

I suggest something like "this option requires a Linux kernel support 
that supports the right ABI (<or whatever is the best description>) (5.4 
or later)".

Thanks,

Kyrill



> +
> +@end table
> +
>  @section Building a cross compiler
>
>  When building a cross compiler, it is not generally possible to do a
> diff --git a/libiberty/configure b/libiberty/configure
> index 
> 7a34dabec32b0b383bd33f07811757335f4dd39c..cb2dd4ff5295598343cc18b3a79a86a778f2261d 
> 100755
> --- a/libiberty/configure
> +++ b/libiberty/configure
> @@ -5261,6 +5261,7 @@ fi
>  NOASANFLAG=
>  case " ${CFLAGS} " in
>    *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
> +  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
>  esac
>
>
> diff --git a/libiberty/configure.ac b/libiberty/configure.ac
> index 
> f1ce76010c9acde79c5dc46686a78b2e2f19244e..043237628b79cbf37d07359b59c5ffe17a7a22ef 
> 100644
> --- a/libiberty/configure.ac
> +++ b/libiberty/configure.ac
> @@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
>  NOASANFLAG=
>  case " ${CFLAGS} " in
>    *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
> +  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
>  esac
>  AC_SUBST(NOASANFLAG)
>
> diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
> index 
> 28dc21014b2e86988fa88adabd63ce6092e18e02..34aa397d785e3cc9b6975de460d065900364c3ff 
> 100644
> --- a/lto-plugin/Makefile.am
> +++ b/lto-plugin/Makefile.am
> @@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
>  AM_CFLAGS = @ac_lto_plugin_warn_cflags@
>  AM_LDFLAGS = @ac_lto_plugin_ldflags@
>  AM_LIBTOOLFLAGS = --tag=disable-static
> -override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
> -override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
> +override CFLAGS := $(filter-out -fsanitize=address 
> -fsanitize=hwaddress,$(CFLAGS))
> +override LDFLAGS := $(filter-out -fsanitize=address 
> -fsanitize=hwaddress,$(LDFLAGS))
>
>  libexecsub_LTLIBRARIES = liblto_plugin.la
>  gcc_build_dir = @gcc_build_dir@
> diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
> index 
> 8dd6e40ac9dddab39fe1752f9a70e6834ab3c926..7acfc047eff6f86f8d38287e6ffb6533c4c13500 
> 100644
> --- a/lto-plugin/Makefile.in
> +++ b/lto-plugin/Makefile.in
> @@ -672,8 +672,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
>
>  .PRECIOUS: Makefile
>
> -override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
> -override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
> +override CFLAGS := $(filter-out -fsanitize=address 
> -fsanitize=hwaddress,$(CFLAGS))
> +override LDFLAGS := $(filter-out -fsanitize=address 
> -fsanitize=hwaddress,$(LDFLAGS))
>
>  all-local: $(in_gcc_libs)
>
>

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 7/X] [libsanitizer] Add tests
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (7 preceding siblings ...)
  2019-12-12 15:37     ` Document --with-build-config=bootstrap-asan option Matthew Malcomson
@ 2019-12-16 11:47     ` Matthew Malcomson
  2019-12-17 14:32     ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (9 subsequent siblings)
  18 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2019-12-16 11:47 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, kcc, dvyukov, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	dodji, jakub

[-- Attachment #1: Type: text/plain, Size: 80413 bytes --]

I just remembered that the run tests all fail on cross builds.
This patch includes the effective target check to determine whether a hwasan
binary can be run on a given target.

We then add that target requirement to all tests which need to run.



Adding hwasan tests.

Only interesting thing here is that we have to make sure the tagging mechanism
is deterministic to avoid flaky tests.

gcc/testsuite/ChangeLog:

2019-12-16  Matthew Malcomson  <matthew.malcomson@arm.com>

	* c-c++-common/hwasan/aligned-alloc.c: New test.
	* c-c++-common/hwasan/alloca-array-accessible.c: New test.
	* c-c++-common/hwasan/alloca-gets-different-tag.c: New test.
	* c-c++-common/hwasan/alloca-outside-caught.c: New test.
	* c-c++-common/hwasan/arguments.c: New test.
	* c-c++-common/hwasan/arguments-1.c: New test.
	* c-c++-common/hwasan/arguments-2.c: New test.
	* c-c++-common/hwasan/arguments-3.c: New test.
	* c-c++-common/hwasan/asan-pr63316.c: New test.
	* c-c++-common/hwasan/asan-pr70541.c: New test.
	* c-c++-common/hwasan/asan-pr78106.c: New test.
	* c-c++-common/hwasan/asan-pr79944.c: New test.
	* c-c++-common/hwasan/asan-rlimit-mmap-test-1.c: New test.
	* c-c++-common/hwasan/bitfield-1.c: New test.
	* c-c++-common/hwasan/bitfield-2.c: New test.
	* c-c++-common/hwasan/builtin-special-handling.c: New test.
	* c-c++-common/hwasan/check-interface.c: New test.
	* c-c++-common/hwasan/halt_on_error-1.c: New test.
	* c-c++-common/hwasan/heap-overflow.c: New test.
	* c-c++-common/hwasan/hwasan-poison-optimisation.c: New test.
	* c-c++-common/hwasan/hwasan-thread-access-parent.c: New test.
	* c-c++-common/hwasan/hwasan-thread-basic-failure.c: New test.
	* c-c++-common/hwasan/hwasan-thread-clears-stack.c: New test.
	* c-c++-common/hwasan/hwasan-thread-success.c: New test.
	* c-c++-common/hwasan/kernel-defaults.c: New test.
	* c-c++-common/hwasan/large-aligned-0.c: New test.
	* c-c++-common/hwasan/large-aligned-1.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-0.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-1.c: New test.
	* c-c++-common/hwasan/macro-definition.c: New test.
	* c-c++-common/hwasan/no-sanitize-attribute.c: New test.
	* c-c++-common/hwasan/param-instrument-reads-and-writes.c: New test.
	* c-c++-common/hwasan/param-instrument-reads.c: New test.
	* c-c++-common/hwasan/param-instrument-writes.c: New test.
	* c-c++-common/hwasan/param-memintrin.c: New test.
	* c-c++-common/hwasan/random-frame-tag.c: New test.
	* c-c++-common/hwasan/sanity-check-pure-c.c: New test.
	* c-c++-common/hwasan/setjmp-longjmp-0.c: New test.
	* c-c++-common/hwasan/setjmp-longjmp-1.c: New test.
	* c-c++-common/hwasan/stack-tagging-basic-0.c: New test.
	* c-c++-common/hwasan/stack-tagging-basic-1.c: New test.
	* c-c++-common/hwasan/stack-tagging-disable.c: New test.
	* c-c++-common/hwasan/unprotected-allocas-0.c: New test.
	* c-c++-common/hwasan/unprotected-allocas-1.c: New test.
	* c-c++-common/hwasan/use-after-free.c: New test.
	* c-c++-common/hwasan/vararray-outside-caught.c: New test.
	* c-c++-common/hwasan/vararray-stack-restore-correct.c: New test.
	* c-c++-common/hwasan/very-large-objects.c: New test.
	* g++.dg/hwasan/hwasan.exp: New file.
	* g++.dg/hwasan/rvo-handled.C: New test.
	* gcc.dg/hwasan/hwasan.exp: New file.
	* gcc.dg/hwasan/nested-functions-0.c: New test.
	* gcc.dg/hwasan/nested-functions-1.c: New test.
	* gcc.dg/hwasan/nested-functions-2.c: New test.
	* lib/hwasan-dg.exp: New file.



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


diff --git a/gcc/testsuite/c-c++-common/hwasan/aligned-alloc.c b/gcc/testsuite/c-c++-common/hwasan/aligned-alloc.c
new file mode 100644
index 0000000000000000000000000000000000000000..d38b1f3f62d97dc3f5c3882137c370700fd4f9a0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/aligned-alloc.c
@@ -0,0 +1,16 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+/* This program fails at runtime in the libhwasan library.
+   The allocator can't handle the requested invalid alignment.  */
+
+int
+main ()
+{
+  void *p = __builtin_aligned_alloc (17, 100);
+  if (((unsigned long long)p & 0x10) == 0)
+    return 0;
+  return 1;
+}
+
+/* { dg-output "HWAddressSanitizer: invalid alignment requested in aligned_alloc: 17" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/alloca-array-accessible.c b/gcc/testsuite/c-c++-common/hwasan/alloca-array-accessible.c
new file mode 100644
index 0000000000000000000000000000000000000000..5e4c168f77efac5c9335768fb5b76fb3752783e9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/alloca-array-accessible.c
@@ -0,0 +1,33 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+
+#define alloca __builtin_alloca
+
+int __attribute__ ((noinline))
+using_alloca (int num)
+{
+  int retval = 0;
+  int *big_array = (int*)alloca (num * sizeof (int));
+  for (int i = 0; i < num; ++i) {
+      retval += big_array[i];
+  }
+  return retval;
+}
+
+int __attribute__ ((noinline))
+using_vararray (int num)
+{
+  int retval = 0;
+  int big_array[num];
+  for (int i = 0; i < num; ++i) {
+      retval += big_array[i];
+  }
+  return retval;
+}
+
+int main()
+{
+  using_alloca (16);
+  using_vararray (12);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/alloca-gets-different-tag.c b/gcc/testsuite/c-c++-common/hwasan/alloca-gets-different-tag.c
new file mode 100644
index 0000000000000000000000000000000000000000..e83734f4b79c2d35a49039797cba6b2506febbe5
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/alloca-gets-different-tag.c
@@ -0,0 +1,65 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+
+/* Alloca is given a different tag to other variables.
+   vararray should behave in the same way.  */
+
+#define alloca __builtin_alloca
+#define assert(x) if (!(x)) __builtin_abort ()
+
+struct two_values {
+    int left;
+    int right;
+};
+
+/* Require default hwasan tag ABI.
+   Know we're using AArch64 since that's the only architecture we run hwasan
+   tests on.  */
+char tag_of (void * x) { return ((unsigned long long)x) >> 56; }
+
+int __attribute__ ((noinline))
+alloca_different_tag (int num)
+{
+  struct two_values tmp_object = {
+      .left = 100,
+      .right = num,
+  };
+  int *big_array = (int *)alloca (num * sizeof (int));
+  int other_array[100];
+  
+  char first_tag = tag_of (&tmp_object);
+  char second_tag = tag_of (big_array);
+  char other_tag = tag_of (other_array);
+  assert (first_tag != second_tag);
+  assert (second_tag != other_tag);
+  assert (first_tag != other_tag);
+  return 0;
+}
+
+int __attribute__ ((noinline))
+vararray_different_tag (int num)
+{
+  struct two_values tmp_object = {
+      .left = 100,
+      .right = num,
+  };
+  int big_array[num];
+  int other_array[100];
+  
+  char first_tag = tag_of (&tmp_object);
+  char second_tag = tag_of (big_array);
+  char other_tag = tag_of (other_array);
+  assert (first_tag != second_tag);
+  assert (second_tag != other_tag);
+  assert (first_tag != other_tag);
+  return 0;
+}
+
+int __attribute__ ((noinline))
+main ()
+{
+  alloca_different_tag (10);
+  vararray_different_tag (8);
+  return 0;
+}
+
diff --git a/gcc/testsuite/c-c++-common/hwasan/alloca-outside-caught.c b/gcc/testsuite/c-c++-common/hwasan/alloca-outside-caught.c
new file mode 100644
index 0000000000000000000000000000000000000000..60d7a9a874f90e2c5c461a33f144dcb431dea838
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/alloca-outside-caught.c
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+
+#define alloca __builtin_alloca
+
+int __attribute__ ((noinline))
+check_alloca (int num)
+{
+  volatile int *allocd_array = (int*)alloca (num * sizeof(int));
+  int other_array[10];
+  return allocd_array[12];
+}
+
+int __attribute__ ((noinline))
+main ()
+{
+  check_alloca (3);
+  return 1;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/arguments-1.c b/gcc/testsuite/c-c++-common/hwasan/arguments-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..435dad3cf159f565e56406467c41d97506f45d8f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/arguments-1.c
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fsanitize=kernel-hwaddress" } */
+/* { dg-error ".*'-fsanitize=hwaddress' is incompatible with '-fsanitize=kernel-hwaddress'.*" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/arguments-2.c b/gcc/testsuite/c-c++-common/hwasan/arguments-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..90012e4cc49057de8b352fea488ba52636bfef32
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/arguments-2.c
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fsanitize=kernel-address" } */
+/* { dg-error ".*'-fsanitize=hwaddress' is incompatible with both '-fsanitize=address' and '-fsanitize=kernel-address'.*" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/arguments-3.c b/gcc/testsuite/c-c++-common/hwasan/arguments-3.c
new file mode 100644
index 0000000000000000000000000000000000000000..02fdb1ee26e37e59191ba8d4e0f769f2360f7a11
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/arguments-3.c
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fsanitize=thread" } */
+/* { dg-error ".*'-fsanitize=hwaddress' is incompatible with '-fsanitize=thread'.*" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/arguments.c b/gcc/testsuite/c-c++-common/hwasan/arguments.c
new file mode 100644
index 0000000000000000000000000000000000000000..2dedbf1d737c559c7046e5749b5e5b8459a9cbf9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/arguments.c
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fsanitize=address" } */
+/* { dg-error ".*'-fsanitize=hwaddress' is incompatible with both '-fsanitize=address' and '-fsanitize=kernel-address'.*" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/asan-pr63316.c b/gcc/testsuite/c-c++-common/hwasan/asan-pr63316.c
new file mode 100644
index 0000000000000000000000000000000000000000..dd3b3dbf22505f12063f088821fecd4be44817d0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/asan-pr63316.c
@@ -0,0 +1,24 @@
+/* PR sanitizer/63316 */
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-skip-if "" { *-*-* }  { "*" } { "-O2" } } */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void *malloc (__SIZE_TYPE__);
+extern void free (void *);
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+  int *p = (int *) malloc (sizeof (int));
+  *p = 3;
+  asm volatile ("" : : "r" (p) : "memory");
+  free (p);
+  return 0;
+}
+
diff --git a/gcc/testsuite/c-c++-common/hwasan/asan-pr70541.c b/gcc/testsuite/c-c++-common/hwasan/asan-pr70541.c
new file mode 100644
index 0000000000000000000000000000000000000000..ba2ed496e6ee81b13270ce05831ff01eca98edde
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/asan-pr70541.c
@@ -0,0 +1,36 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-options "-fno-builtin-malloc -fno-builtin-free" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+/* { dg-shouldfail "hwasan" } */
+
+#include <stdio.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void *malloc (__SIZE_TYPE__);
+extern void free (void *);
+#ifdef __cplusplus
+}
+#endif
+
+struct Simple {
+  int value;
+};
+
+int f(struct Simple simple) {
+  return simple.value;
+}
+
+int main() {
+  struct Simple *psimple = (struct Simple *) malloc(sizeof(struct Simple));
+  psimple->value = 42;
+  free(psimple);
+  printf("%d\n", f(*psimple));
+  return 0;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "freed by thread T0 here:.*" } */
+/* { dg-output "previously allocated here:" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/asan-pr78106.c b/gcc/testsuite/c-c++-common/hwasan/asan-pr78106.c
new file mode 100644
index 0000000000000000000000000000000000000000..3f53ad17693cc4fbb246bc0d3f6a1d02fc3222d0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/asan-pr78106.c
@@ -0,0 +1,31 @@
+/* PR sanitizer/78106 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=hwaddress -fdump-tree-sanopt-details -ffat-lto-objects" } */
+
+int *variable;
+
+void __attribute__((used)) release()
+{
+  __builtin_free (variable);
+}
+
+int main2(int argc)
+{
+  *variable = 2;
+
+  if (argc <= 5)
+    asm volatile ("call release");
+
+  *variable = 2;
+  __builtin_abort ();
+
+  return 0;
+}
+
+int main(int argc, char **argv)
+{
+  variable = (int *)__builtin_malloc (sizeof(int));
+  return main2(argc);
+}
+
+/* { dg-final { scan-tree-dump-not "Optimizing out(\n|\r\n|\r)  HWASAN_CHECK \\(7, variable.*" "sanopt" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/asan-pr79944.c b/gcc/testsuite/c-c++-common/hwasan/asan-pr79944.c
new file mode 100644
index 0000000000000000000000000000000000000000..7d54f54701bac6b8337d62ec9f6fbbb69f921a37
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/asan-pr79944.c
@@ -0,0 +1,19 @@
+/* PR sanitizer/79944 */
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+
+struct S { int i; char p[1024]; };
+
+int
+main ()
+{
+  struct S *p = (struct S *) __builtin_malloc (__builtin_offsetof (struct S, p) + 64);
+  p->i = 5;
+  asm volatile ("" : "+r" (p) : : "memory");
+  __atomic_fetch_add ((int *) p, 5, __ATOMIC_RELAXED);
+  asm volatile ("" : "+r" (p) : : "memory");
+  if (p->i != 10)
+    __builtin_abort ();
+  __builtin_free (p);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/asan-rlimit-mmap-test-1.c b/gcc/testsuite/c-c++-common/hwasan/asan-rlimit-mmap-test-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..5426b8ab430df1813be4e1cbf0930cfa4d817c48
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/asan-rlimit-mmap-test-1.c
@@ -0,0 +1,24 @@
+/* Check that we properly report mmap failure. */
+
+/* { dg-do run { target setrlimit } } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+/* { dg-require-effective-target hw } */
+/* { dg-shouldfail "hwasan" } */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+static volatile void *x;
+
+int main(int argc, char **argv) {
+  struct rlimit mmap_resource_limit = { 0, 0 };
+  if (setrlimit(RLIMIT_AS, &mmap_resource_limit)) return 1;
+  x = malloc(10000000);
+  return 0;
+}
+
+/* { dg-output "ERROR: Failed to mmap" } */
+
diff --git a/gcc/testsuite/c-c++-common/hwasan/bitfield-1.c b/gcc/testsuite/c-c++-common/hwasan/bitfield-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..0c3479eacc477fa4d4aaa15846bb73a40a5fbb48
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/bitfield-1.c
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+
+struct bitmapped_struct {
+    unsigned one : 1;
+    unsigned two : 1;
+    unsigned three : 1;
+    unsigned four : 1;
+    unsigned five : 1;
+    unsigned six : 1;
+    unsigned seven : 1;
+    unsigned eight : 1;
+};
+
+/* Check that hwasan allows valid bitfield accesses. */
+int __attribute__ ((noinline))
+handle_unaligned_access (struct bitmapped_struct *foo)
+{
+  if (foo->three)
+    return foo->four;
+
+  foo->five = 1;
+  return 1;
+}
+
+int main()
+{
+  struct bitmapped_struct myvar = {0};
+  handle_unaligned_access (&myvar);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/bitfield-2.c b/gcc/testsuite/c-c++-common/hwasan/bitfield-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..0b3f3aaaf2d899eb3f946e8f230601e16d491d4c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/bitfield-2.c
@@ -0,0 +1,30 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+
+/* Ensure that hwasan instruments bitfield accesses.  */
+struct A
+{
+  /* Ensure the offset from the start of this struct to the bitfield we access
+     is large enough to be in a different tag.  */
+  char base[16];
+  int : 4;
+  long x : 7;
+};
+
+int __attribute__ ((noinline, noclone))
+f (void *p) {
+  return ((struct A *)p)->x;
+}
+
+int
+main ()
+{
+  char a = 0;
+  return f (&a);
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 2 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/builtin-special-handling.c b/gcc/testsuite/c-c++-common/hwasan/builtin-special-handling.c
new file mode 100644
index 0000000000000000000000000000000000000000..69f112d2d4ffb1c0b9af7df83cdbd1e789c8d12c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/builtin-special-handling.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-hwasan" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+/* Only skip the -flto tests without the -flto-partition=none.
+   With -flto-partition=none we still get a hwasan1 dump file, without that
+   parameter we only get the lto dump files (which means scan-tree-dump-times
+   doesn't work.  */
+/* { dg-skip-if "" { *-*-* }  { "-flto" } { "-flto-partition=none" } } */
+
+typedef __SIZE_TYPE__ size_t;
+/* Functions to observe that HWASAN instruments memory builtins in the expected
+   manner.  */
+void * __attribute__((noinline))
+memset_builtin (void *dest, int value, size_t len)
+{
+  return __builtin_memset (dest, value, len);
+}
+
+/* HWASAN avoids strlen because it doesn't know the size of the memory access
+   until *after* the function call.  */
+size_t __attribute__ ((noinline))
+strlen_builtin (char *element)
+{
+  return __builtin_strlen (element);
+}
+
+/* First test ensures that the HWASAN_CHECK was emitted before the
+   memset.  Second test ensures there was only HWASAN_CHECK (which demonstrates
+   that strlen was not instrumented).  */
+/* { dg-final { scan-tree-dump-times "HWASAN_CHECK.*memset" 1 "hwasan1" } } */
+/* { dg-final { scan-tree-dump-times "HWASAN_CHECK" 1 "hwasan1" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/check-interface.c b/gcc/testsuite/c-c++-common/hwasan/check-interface.c
new file mode 100644
index 0000000000000000000000000000000000000000..90f52cac884dd67b1dab18d4037fe64dfc19eefc
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/check-interface.c
@@ -0,0 +1,26 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/*
+   Test taken from LLVM
+    compiler-rt/test/hwasan/TestCases/check-interface.cpp
+ */
+// Utilizes all flavors of __hwasan_load/store interface functions to verify
+// that the instrumentation and the interface provided by HWASan do match.
+// In case of a discrepancy, this test fails to link.
+
+typedef __UINT8_TYPE__ uint8_t;
+typedef __UINT16_TYPE__ uint16_t;
+typedef __UINT32_TYPE__ uint32_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+#define F(T) void f_##T(T *a, T *b) { *a = *b; }
+
+F(uint8_t)
+F(uint16_t)
+F(uint32_t)
+F(uint64_t)
+
+typedef unsigned V32 __attribute__((__vector_size__(32)));
+F(V32)
+
+int main() {}
diff --git a/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c b/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..90ca856874dfbdd8266981a26503f1e5b88cf039
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c
@@ -0,0 +1,24 @@
+/* Test recovery mode.  */
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-options "-fsanitize-recover=hwaddress" } */
+/* { dg-set-target-env-var HWASAN_OPTIONS "halt_on_error=false" } */
+/* { dg-shouldfail "hwasan" } */
+
+volatile int sixteen = 16;
+
+int main() {
+  char x[16];
+  __builtin_memset(x, 0, sixteen + 1);
+  asm volatile ("" : : : "memory");
+  volatile int res = x[sixteen];
+  x[sixteen] = res + 3;
+  res = x[sixteen];
+  return 0;
+}
+
+/* { dg-output "WRITE of size 17 at 0x\[0-9a-f\]+.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+.*" } */
+/* { dg-output "WRITE of size 1 at 0x\[0-9a-f\]+.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+.*" } */
+
diff --git a/gcc/testsuite/c-c++-common/hwasan/heap-overflow.c b/gcc/testsuite/c-c++-common/hwasan/heap-overflow.c
new file mode 100644
index 0000000000000000000000000000000000000000..48ebb56bdf407bb147dd1e404897ce79662b2d05
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/heap-overflow.c
@@ -0,0 +1,29 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-options "-fno-builtin-malloc -fno-builtin-free -fno-builtin-memset" } */
+/* { dg-shouldfail "hwasan" } */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void *memset (void *, int, __SIZE_TYPE__);
+void *malloc (__SIZE_TYPE__);
+void free (void *);
+#ifdef __cplusplus
+}
+#endif
+
+volatile int ten = 10;
+int main(int argc, char **argv) {
+  char *x = (char*)malloc(10);
+  memset(x, 0, 10);
+  int res = x[ten];  /* BOOOM */
+  free(x);
+  return res;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "located 0 bytes to the right of 10-byte region.*" } */
+/* { dg-output "allocated here:.*" } */
+/* { dg-output "#1 0x\[0-9a-f\]+ +in _*main \[^\n\r]*heap-overflow.c:17" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/hwasan-poison-optimisation.c b/gcc/testsuite/c-c++-common/hwasan/hwasan-poison-optimisation.c
new file mode 100644
index 0000000000000000000000000000000000000000..8726102cb1c84931ecfe99ea4f255ef1a70ccd8a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/hwasan-poison-optimisation.c
@@ -0,0 +1,29 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+/* { dg-additional-options "-fdump-tree-hwasan1 -save-temps" } */
+
+/* Here to check that the ASAN_POISON stuff works just fine.
+   This mechanism isn't very often used, but I should at least go through the
+   code-path once in my testfile.  */
+int
+main ()
+{
+  int *ptr = 0;
+
+  {
+    int a;
+    ptr = &a;
+    *ptr = 12345;
+  }
+
+  return *ptr;
+}
+
+/* { dg-final { scan-tree-dump-times "ASAN_POISON" 1 "hwasan1" }  } */
+/* { dg-final { scan-assembler-times "bl\\s*__hwasan_tag_mismatch4" 1 } } */
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-access-parent.c b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-access-parent.c
new file mode 100644
index 0000000000000000000000000000000000000000..828909d3b3bc6ad98fdb243cb6a06e78bcff4280
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-access-parent.c
@@ -0,0 +1,51 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "-lpthread" } */
+
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int printf (char const *, ...);
+#ifdef __cplusplus
+}
+#endif
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+/* Test that tags are checked across different threads.
+   i.e. if this thread tries to access a different threads memory with the
+   incorrect tag, then this thread fails.  */
+void *
+failing_thread_function (void *argument)
+{
+    void * other = (void *)((uint64_t)argument & 0xffffffffffffffULL);
+    int *num = (int*)argument;
+    printf ("(should succeed): first number = %d\n", num[0]);
+    printf ("(now should fail):\n");
+
+    int *othernum = (int*)other;
+    printf (" second number = %d\n", othernum[0]);
+    return (void *)1;
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, failing_thread_function, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    return (uintptr_t)retval;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: 00/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T1.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-basic-failure.c b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-basic-failure.c
new file mode 100644
index 0000000000000000000000000000000000000000..6a07521e1155de5bdbc95fad00d1305b2f33548e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-basic-failure.c
@@ -0,0 +1,48 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "-lpthread" } */
+
+/* Ensure the failure mode for hwasan under pthreads looks sane.
+   (Looks sane means that the same error message is printed out rather than an
+   opaque message due to mishandling).  */
+
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int printf (char const *, ...);
+#ifdef __cplusplus
+}
+#endif
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+void *
+failing_from_stack (void * argument)
+{
+    int internal_array[16] = {0};
+    printf ("(now should fail):");
+    printf (" problem number is %d\n", internal_array[17]);
+    return (void *)1;
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, failing_from_stack, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    return (uintptr_t)retval;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T1.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T1.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-clears-stack.c b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-clears-stack.c
new file mode 100644
index 0000000000000000000000000000000000000000..09c72a56f0f50a8c301d89217aa8c7df70087e6c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-clears-stack.c
@@ -0,0 +1,56 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "-lpthread" } */
+
+/* This checks the interceptor ABI pthread hooks.
+   The stack of the thread that is finishing must be cleared of shadow tags
+   when that thread exits.  */
+
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int printf (char const *, ...);
+#ifdef __cplusplus
+}
+#endif
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+__attribute__ ((noinline))
+void * Ident (void * argument)
+{
+	return argument;
+}
+
+void *
+pthread_stack_is_cleared (void *argument)
+{
+   (void)argument;
+   int internal_array[16] = {0};
+   return Ident((void*)internal_array);
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, pthread_stack_is_cleared, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    printf ("(should fail): ");
+    printf ("value left in stack is: %d\n", ((int *)retval)[0]);
+
+    return (uintptr_t)retval;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "HWAddressSanitizer can not describe address in more detail\..*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-success.c b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-success.c
new file mode 100644
index 0000000000000000000000000000000000000000..b0281f7b389f141f17c08fdb76db8848d0e7c429
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/hwasan-thread-success.c
@@ -0,0 +1,35 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-additional-options "-lpthread" } */
+
+/* Just ensure that a basic threaded program works while running with hwasan.
+   */
+
+#include <pthread.h>
+
+extern int printf (const char *, ...);
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+void *
+successful_thread_function (void * argument)
+{
+    int *deref = (int *)argument;
+    if (deref[0] == 100)
+      deref[1] = 10;
+    return (void *)0;
+}
+
+int
+main (int argc, char **argv)
+{
+    int argument[100] = {0};
+    argument[1] = 10;
+    pthread_t thread_index;
+    pthread_create (&thread_index, NULL, successful_thread_function, (void*)argument);
+
+    void *retval;
+    pthread_join (thread_index, &retval);
+
+    return (uintptr_t)retval;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/kernel-defaults.c b/gcc/testsuite/c-c++-common/hwasan/kernel-defaults.c
new file mode 100644
index 0000000000000000000000000000000000000000..abfe735e6e1fab712ae8f0bd1517a14a4d5eb178
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/kernel-defaults.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fno-sanitize=hwaddress -fsanitize=kernel-hwaddress" } */
+
+
+/* Defaults to check for kernel-hwaddress.
+   1) No stack tagging => no calls to __hwasan_tag_memory.
+   2) No block scope tagging (same again).
+   3) Use sanitize-recover by default (everything ends in noabort). */
+int __attribute__ ((noinline))
+accessing_pointers (int *left, int *right)
+{
+  int x = right[2];
+  left[3] = right[1];
+  return right[1] + left[2];
+}
+
+int __attribute__ ((noinline))
+using_stack (int num)
+{
+  int big_array[10];
+  int other_array[20];
+  accessing_pointers(other_array, big_array);
+  return big_array[num];
+}
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  using_stack (ARG);
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "__hwasan_tag_memory" } } */
+/* { dg-final { scan-assembler-not "__hwasan_(load|store)\\d(?!_noabort)" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/large-aligned-0.c b/gcc/testsuite/c-c++-common/hwasan/large-aligned-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..5b0071af2beccc36246ddf9406c087dde4b52f80
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/large-aligned-0.c
@@ -0,0 +1,33 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+
+/* Handling large aligned variables.
+   Large aligned variables take a different code-path through expand_stack_vars
+   in cfgexpand.c.  This testcase is just to exercise that code-path.
+
+   The alternate code-path produces a second base-pointer through some
+   instructions emitted in the prologue.
+   
+   Test cases are:
+   0) Valid access works without complaint.
+   1) Invalid access is caught.  */
+int __attribute__ ((noinline))
+handle_large_alignment (int num)
+{
+  int other_array[10];
+  int big_array[100] __attribute__ ((aligned (32)));
+  return big_array[num] + other_array[num];
+}
+
+#ifndef ARG
+#define ARG 1
+#endif
+
+int global;
+
+int __attribute__ ((noinline))
+main ()
+{
+  global += handle_large_alignment (ARG);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/large-aligned-1.c b/gcc/testsuite/c-c++-common/hwasan/large-aligned-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..1aa130323960225c2201067d29f83231f1cec5d7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/large-aligned-1.c
@@ -0,0 +1,14 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+
+#define ARG 12
+#include "large-aligned-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* NOTE: This assumes the current tagging mechanism (one at a time from the
+   base and large aligned variables being handled first).  */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/large-aligned-untagging-0.c b/gcc/testsuite/c-c++-common/hwasan/large-aligned-untagging-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..99a4e162169d54c40b101641623017695a93f55f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/large-aligned-untagging-0.c
@@ -0,0 +1,60 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+/* Don't really need this option since there are no vararray/alloca objects in
+   the interesting function, however it never hurts to make doubly sure and
+   make it explicit that we're checking the alternate approach to deallocation.
+   */
+/* { dg-additional-options "--param hwasan-instrument-allocas=0" } */
+
+/* Handling large aligned variables.
+   Large aligned variables take a different code-path through expand_stack_vars
+   in cfgexpand.c.  This testcase is just to exercise that code-path.
+
+   The alternate code-path produces a second base-pointer through some
+   instructions emitted in the prologue.
+
+   This eventually follows a different code path for untagging when not tagging
+   allocas. The untagging needs to work at the top of the frame, and this
+   should account for this different base when large aligned variables are
+   around.  */
+__attribute__ ((noinline))
+void * Ident (void * argument)
+{
+  return argument;
+}
+
+int* __attribute__ ((noinline))
+large_alignment_untagging (int num)
+{
+  int other_array[100];
+  int big_array[100] __attribute__ ((aligned (32)));
+  if (num % 1)
+    return (int*)Ident(big_array);
+  else
+    return (int*)Ident(other_array);
+}
+
+#ifndef ARG
+#define ARG 1
+#endif
+
+int global;
+
+int __attribute__ ((noinline))
+main ()
+{
+  int *array = large_alignment_untagging (ARG);
+  /* Want to test that both ends of the function are untagged.
+     That means both the start of the large aligned variable and the end of the
+     other variable.  */
+  global += array[ARG ? 99 : 0];
+  return 0;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* NOTE: This assumes the current tagging mechanism (one at a time from the
+   base and large aligned variables being handled first).  */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/large-aligned-untagging-1.c b/gcc/testsuite/c-c++-common/hwasan/large-aligned-untagging-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..1c0cf11c4d59ed77692b3c1ac97327c6cc4e558f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/large-aligned-untagging-1.c
@@ -0,0 +1,15 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+/* { dg-additional-options "--param hwasan-instrument-allocas=0" } */
+
+#define ARG 0
+#include "large-aligned-untagging-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* NOTE: This assumes the current tagging mechanism (one at a time from the
+   base and large aligned variables being handled first).  */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/macro-definition.c b/gcc/testsuite/c-c++-common/hwasan/macro-definition.c
new file mode 100644
index 0000000000000000000000000000000000000000..5f654f557821f2dbe060e9976fbca7e5770f274c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/macro-definition.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+extern void testfunc(int);
+int foo()
+{
+#ifndef __SANITIZE_HWADDRESS__
+  testfunc(1);
+#endif
+  return 1;
+}
+
+/* { dg-final { scan-assembler-not "testfunc" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/no-sanitize-attribute.c b/gcc/testsuite/c-c++-common/hwasan/no-sanitize-attribute.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5ff4aec06c8bc6c80fe512061ccc8908e321153
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/no-sanitize-attribute.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+
+__attribute__((no_sanitize_hwaddress)) int
+f1 (int *p, int *q)
+{
+  *p = 42;
+  return *q;
+}
+
+__attribute__((no_sanitize("hwaddress"))) int
+f2 (int *p, int *q)
+{
+  *p = 42;
+  return *q;
+}
+
+/* Only have one instance of __hwasan, it is __hwasan_init (the module
+ * constructor) there is no instrumentation in the functions.  */
+/* { dg-final { scan-assembler-times "__hwasan" 1 } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/param-instrument-reads-and-writes.c b/gcc/testsuite/c-c++-common/hwasan/param-instrument-reads-and-writes.c
new file mode 100644
index 0000000000000000000000000000000000000000..1d565a2f5d959507fc7ba2418637489f7e14e6f0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/param-instrument-reads-and-writes.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-additional-options "--param hwasan-instrument-writes=0" } */
+
+#include "param-instrument-reads.c"
+
+/* { dg-final { scan-assembler "__hwasan_load" } } */
+/* { dg-final { scan-assembler-not "__hwasan_store" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/param-instrument-reads.c b/gcc/testsuite/c-c++-common/hwasan/param-instrument-reads.c
new file mode 100644
index 0000000000000000000000000000000000000000..9b8049a3cb94b166a2b27bbc97f8c26ac7efc6ac
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/param-instrument-reads.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-additional-options "--param hwasan-instrument-reads=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+/* Particular code doesn't really matter, the requirement is that it has both
+   loads and stores in it.  */
+__attribute__ ((noinline))
+int reader (int *array, size_t num)
+{
+  return array[num];
+}
+
+int __attribute__ ((noinline))
+writer (int *array, size_t num, int value)
+{
+  array[num] = value;
+  return num + value;
+}
+
+/* { dg-final { scan-assembler-not "__hwasan_load" } } */
+/* { dg-final { scan-assembler "__hwasan_store" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/param-instrument-writes.c b/gcc/testsuite/c-c++-common/hwasan/param-instrument-writes.c
new file mode 100644
index 0000000000000000000000000000000000000000..0f04fad6496253f31d09e49b49b77decbefe0e60
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/param-instrument-writes.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-additional-options "--param hwasan-instrument-reads=0 --param hwasan-instrument-writes=0" } */
+
+#include "param-instrument-reads.c"
+
+/* { dg-final { scan-assembler-not "__hwasan_load" } } */
+/* { dg-final { scan-assembler-not "__hwasan_store" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/param-memintrin.c b/gcc/testsuite/c-c++-common/hwasan/param-memintrin.c
new file mode 100644
index 0000000000000000000000000000000000000000..e52d6fe7df43256fe4acfe2ecc71c5cdeb36bef4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/param-memintrin.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+/* { dg-additional-options "--param hwasan-memintrin=0" } */
+
+#include "builtin-special-handling.c"
+
+/* With this flag there should be no checking of builtins.
+   The above file only has builtins, and hence there should be no checking
+   after compilation.  */
+/* { dg-final { scan-assembler-not "__hwasan_(load|store)" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/random-frame-tag.c b/gcc/testsuite/c-c++-common/hwasan/random-frame-tag.c
new file mode 100644
index 0000000000000000000000000000000000000000..8e55b298470a49bf5563f26013569ee03487933c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/random-frame-tag.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-additional-options "--param hwasan-random-frame-tag=1" } */
+
+#include "stack-tagging-basic-0.c"
+
+/* Random frame tag => call to __hwasan_generate_tag.  */
+/* { dg-final { scan-assembler "__hwasan_generate_tag" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/sanity-check-pure-c.c b/gcc/testsuite/c-c++-common/hwasan/sanity-check-pure-c.c
new file mode 100644
index 0000000000000000000000000000000000000000..a42921bb44cf835696e00731849b33b4ef069b72
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/sanity-check-pure-c.c
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-options "-fno-builtin-malloc -fno-builtin-free" } */
+/* { dg-shouldfail "asan" } */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void *malloc (__SIZE_TYPE__);
+void free (void *);
+#ifdef __cplusplus
+}
+#endif
+
+int main() {
+  char *x = (char*)malloc(10);
+  free(x);
+  return x[5];
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "located 5 bytes inside of 10-byte region.*" } */
+/* { dg-output "freed by thread T0 here:.*" } */
+/* { dg-output "previously allocated here:" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/setjmp-longjmp-0.c b/gcc/testsuite/c-c++-common/hwasan/setjmp-longjmp-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..019c4ea3f7467be61da52272b37a3fd5d1924df7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/setjmp-longjmp-0.c
@@ -0,0 +1,54 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+
+#include <setjmp.h>
+#include <stdio.h>
+
+/*
+   Testing longjmp/setjmp should test.
+
+   0) Nothing special happens with the jmp_buf.
+   1) Accesses to scopes jmp'd over are caught.
+ */
+int __attribute__ ((noinline))
+uses_longjmp (int **other_array, int num, jmp_buf env)
+{
+  int internal_array[100] = {0};
+  *other_array = &internal_array[0];
+  if (num % 2)
+    longjmp (env, num);
+  else
+    return num % 8;
+}
+
+int __attribute__ ((noinline))
+uses_setjmp (int num)
+{ 
+  int big_array[100];
+  int *other_array = NULL;
+  sigjmp_buf cur_env;
+  int temp = 0;
+  if ((temp = sigsetjmp (cur_env, 1)) != 0)
+    { 
+      if (other_array != NULL)
+        printf ("Value pointed to in other_array[0]: %d\n",
+                other_array[0]);
+  
+      printf ("Longjmp returned %d.\n", temp);
+      return 10;
+    }
+  else
+    {
+      return uses_longjmp (&other_array, num, cur_env);
+    } 
+} 
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  uses_setjmp (ARG);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/setjmp-longjmp-1.c b/gcc/testsuite/c-c++-common/hwasan/setjmp-longjmp-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..6a4fceeb37acb156b0b92556453f6fc1bf49b876
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/setjmp-longjmp-1.c
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+
+/*
+   Testing longjmp/setjmp should test.
+
+   0) Nothing special happens with the jmp_buf.
+   1) Accesses to scopes jmp'd over are caught.
+ */
+
+#define ARG 1
+#include "setjmp-longjmp-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/stack-tagging-basic-0.c b/gcc/testsuite/c-c++-common/hwasan/stack-tagging-basic-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..114114165d97ef5b5c6c2fa83352cc7f071d3695
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/stack-tagging-basic-0.c
@@ -0,0 +1,37 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+
+/* Basic tests for stack tagging.
+
+   0) Valid accesses work.
+   1) Accesses outside of a variable crash.
+*/
+int __attribute__ ((noinline))
+accessing_pointers (int *left, int *right)
+{
+  int x = right[2];
+  left[3] = right[1];
+  return right[1] + left[2];
+}
+
+int __attribute__ ((noinline))
+using_stack (int num)
+{
+  int big_array[10];
+  int other_array[20];
+  accessing_pointers(other_array, big_array);
+  return big_array[num];
+}
+
+#ifndef ARG
+#define ARG 0
+#endif
+
+int global;
+
+int __attribute__ ((noinline))
+main ()
+{
+  global += using_stack (ARG);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/stack-tagging-basic-1.c b/gcc/testsuite/c-c++-common/hwasan/stack-tagging-basic-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..90d5837254f8dddadfc2235fe5e7191c45e7887b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/stack-tagging-basic-1.c
@@ -0,0 +1,18 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+
+/* Basic tests for stack tagging.
+
+   0) Accesses outside of a variable crash.
+   1) Valid accesses work.
+*/
+
+#define ARG 17
+#include "stack-tagging-basic-0.c"
+#undef ARG
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/stack-tagging-disable.c b/gcc/testsuite/c-c++-common/hwasan/stack-tagging-disable.c
new file mode 100644
index 0000000000000000000000000000000000000000..c48048988b7ca97507f5305cc2c30af1e8421bee
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/stack-tagging-disable.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-additional-options "--param hwasan-stack=0" } */
+
+
+/* No stack tagging => no calls to __hwasan_tag_memory.  */
+int __attribute__ ((noinline))
+accessing_pointers (int *left, int *right)
+{
+  int x = right[2];
+  left[3] = right[1];
+  return right[1] + left[2];
+}
+
+int __attribute__ ((noinline))
+using_stack (int num)
+{
+  int big_array[10];
+  int other_array[20];
+  accessing_pointers(other_array, big_array);
+  return big_array[num];
+}
+
+#ifndef ARG
+#define ARG 0
+#endif
+int __attribute__ ((noinline))
+main ()
+{
+  using_stack (ARG);
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "__hwasan_tag_memory" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/unprotected-allocas-0.c b/gcc/testsuite/c-c++-common/hwasan/unprotected-allocas-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..88465155c6d0a72bfef1fdd31c59adaf4db549ce
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/unprotected-allocas-0.c
@@ -0,0 +1,46 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-additional-options "--param hwasan-instrument-allocas=0 -save-temps" } */
+/* Only run this test without optimisation.  When running with optimisation we
+   use the unprotected-allocas-1.c file that also checks there are no memory
+   tagging calls (since when optimised the only variable on the stack should be
+   the vararray/alloca).  */
+/* { dg-skip-if "" { *-*-* } { "-O1" "-O2" "-O3" } { "" } } */
+
+#define alloca __builtin_alloca
+#define assert(x) if (!(x)) __builtin_abort ()
+
+char tag_of (void * x) { return ((unsigned long long)x) >> 56; }
+
+int __attribute__ ((noinline))
+using_alloca (int num)
+{
+  int retval = 0;
+  int *big_array = (int*)alloca (num * sizeof (int));
+  char alloca_tag = tag_of (big_array);
+  assert (alloca_tag == 0);
+  for (int i = 0; i < num; ++i) {
+      retval += big_array[i];
+  }
+  return retval;
+}
+
+int __attribute__ ((noinline))
+using_vararray (int num)
+{
+  int retval = 0;
+  int big_array[num];
+  char vararray_tag = tag_of (big_array);
+  assert (vararray_tag == 0);
+  for (int i = 0; i < num; ++i) {
+      retval += big_array[i];
+  }
+  return retval;
+}
+
+int main()
+{
+  using_alloca (16);
+  using_vararray (12);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/unprotected-allocas-1.c b/gcc/testsuite/c-c++-common/hwasan/unprotected-allocas-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..752edc1d3ea366af771936d7680902bad7c1878c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/unprotected-allocas-1.c
@@ -0,0 +1,16 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-additional-options "--param hwasan-instrument-allocas=0 -save-temps" } */
+/* Only test there's no tagging done when not at -O0.  Without optimisation
+   the compiler creates a bunch of other variables on the stack other than the
+   vararray/alloca object.
+   We also avoid checking when using -flto, since with LTO the compiler can
+   recognise the vararray is only used with one size and that size is known at
+   compile time -- when the compiler recognises that it instead creates a
+   static array, which gets tagged as is expected but not as the test expects.
+   */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-flto" } { "" } } */
+
+#include "unprotected-allocas-0.c"
+
+/* { dg-final { scan-assembler-not "__hwasan_tag_memory" } } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/use-after-free.c b/gcc/testsuite/c-c++-common/hwasan/use-after-free.c
new file mode 100644
index 0000000000000000000000000000000000000000..22dea56c644c25d660c26d6753d0ec4c5b0797fc
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/use-after-free.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-options "-fno-builtin-malloc -fno-builtin-free" } */
+/* { dg-shouldfail "hwasan" } */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void *malloc (__SIZE_TYPE__);
+void free (void *);
+#ifdef __cplusplus
+}
+#endif
+
+int main() {
+  char *x = (char*)malloc(10);
+  free(x);
+  return x[5];
+}
+
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "is located 5 bytes inside of 10-byte region.*" } */
+/* { dg-output "freed by thread T0 here:.*" } */
+/* { dg-output "#1\[^\n\r]*main\[^\n\r]*use-after-free.c:16.*" } */
+/* { dg-output "previously allocated here:.*" } */
+/* { dg-output "#1\[^\n\r]*main\[^\n\r]*use-after-free.c:15" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/vararray-outside-caught.c b/gcc/testsuite/c-c++-common/hwasan/vararray-outside-caught.c
new file mode 100644
index 0000000000000000000000000000000000000000..35a344def424e41dc9e810fdf0e804294efa75df
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/vararray-outside-caught.c
@@ -0,0 +1,22 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+
+int __attribute__ ((noinline))
+check_vararray (int num)
+{
+  int var_array[num];
+  int other_array[10];
+  return var_array[12];
+}
+
+int __attribute__ ((noinline))
+main ()
+{
+  return check_vararray (3);
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/c-c++-common/hwasan/vararray-stack-restore-correct.c b/gcc/testsuite/c-c++-common/hwasan/vararray-stack-restore-correct.c
new file mode 100644
index 0000000000000000000000000000000000000000..f4e1f57bdd2e92c9adf1158e60096ab5a72f38e8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/vararray-stack-restore-correct.c
@@ -0,0 +1,43 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+
+#include <stdio.h>
+
+/* Testing that a function with outgoing arguments correctly decrements the
+   stack pointer when a vararray goes out of scope.  */
+
+const char *
+other (int argc, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l)
+{
+  const char ** other;
+    {
+      const char * test_array[argc];
+      test_array[0] = "test string";
+      test_array[argc - 1] = "hello";
+      /* To prevent optimisation.  */
+      printf("While the value stored in our test_array is: %s\n",
+	     test_array[argc - 1]);
+      other = test_array;
+    }
+  /* With the below function call (the one with many arguments), some of the
+     arguments have to be put on the stack, which means we have to reserve some
+     space on the stack for these arguments and that the VLA is stored at a
+     position that is not the stack pointer. */
+  printf("Hello there!\nOur numbers today are: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",
+	 a, b, c, d, e, f, g, h, i, j, k, l);
+  /* This should fail due to a bad read access.  */
+  return other[0];
+}
+
+int
+main ()
+{
+  int a, b, c, d, e, f, g, h, i, j, k, l;
+  const char * retval = other (1, a, b, c, d, e, f, g, h, i, j, k, l);
+  /* Numbers don't matter here, just want to ensure the program is reading them
+     so we know they won't be optimised out.  */
+  if (retval)
+    return 1;
+  return 10;
+}
diff --git a/gcc/testsuite/c-c++-common/hwasan/very-large-objects.c b/gcc/testsuite/c-c++-common/hwasan/very-large-objects.c
new file mode 100644
index 0000000000000000000000000000000000000000..55265353369540872e8fba4da99d9be92a7ad99b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/hwasan/very-large-objects.c
@@ -0,0 +1,68 @@
+/* { dg-do compile } */
+
+/* Ensure the sanitizer can handle very large offsets (i.e. that the hooks
+   handle offsets too large for the relevant instructions).
+   Just want to make sure this compiles without an ICE.  */
+#ifndef ASIZE
+# define ASIZE 0x10000000000UL
+#endif
+
+typedef __UINT64_TYPE__ uint64_t;
+
+#if __LONG_MAX__ < 8 * ASIZE
+# undef ASIZE
+# define ASIZE 4096
+#endif
+
+extern void abort (void);
+
+int __attribute__((noinline))
+foo (const char *s)
+{
+  if (!s)
+    return 1;
+  if (s[0] != 'a')
+    abort ();
+  s += ASIZE - 1;
+  if (s[0] != 'b')
+    abort ();
+  return 0;
+}
+
+int (*fn) (const char *) = foo;
+
+int __attribute__((noinline))
+bar (void)
+{
+  char s[ASIZE];
+  s[0] = 'a';
+  s[ASIZE - 1] = 'b';
+  foo (s);
+  foo (s);
+  return 0;
+}
+
+int __attribute__((noinline))
+baz (long i)
+{
+  if (i)
+    return fn (0);
+  else
+    {
+      char s[ASIZE];
+      s[0] = 'a';
+      s[ASIZE - 1] = 'b';
+      foo (s);
+      foo (s);
+      return fn (0);
+    }
+}
+
+int __attribute__((noinline))
+very_large_offset (int *p)
+{
+  char init_array[(uint64_t)0xfefefef];
+  char other_array[(uint64_t)0xfefefef];
+  return (int)init_array[p[1]] + (int)other_array[p[0]];
+}
+
diff --git a/gcc/testsuite/g++.dg/hwasan/hwasan.exp b/gcc/testsuite/g++.dg/hwasan/hwasan.exp
new file mode 100644
index 0000000000000000000000000000000000000000..559cf066f18cc82601661a751bf702303c66c888
--- /dev/null
+++ b/gcc/testsuite/g++.dg/hwasan/hwasan.exp
@@ -0,0 +1,34 @@
+# Copyright (C) 2012-2019 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Load support procs.
+load_lib g++-dg.exp
+load_lib hwasan-dg.exp
+
+# Initialize `dg'.
+dg-init
+hwasan_init
+
+# Main loop.
+if [check_effective_target_fsanitize_hwaddress] {
+  gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C $srcdir/c-c++-common/hwasan/*.c]] "" ""
+}
+
+# All done.
+hwasan_finish
+dg-finish
diff --git a/gcc/testsuite/g++.dg/hwasan/rvo-handled.C b/gcc/testsuite/g++.dg/hwasan/rvo-handled.C
new file mode 100644
index 0000000000000000000000000000000000000000..0e30ff054fc65610996b7cdf3e5dcd6dbeeffe6c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/hwasan/rvo-handled.C
@@ -0,0 +1,46 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+
+#define assert(x) if (!(x)) __builtin_abort ()
+typedef __UINTPTR_TYPE__ uintptr_t;
+void *untagged (void *ptr)
+{
+  /* Untag by removing the top byte.  */
+  return (void*)((uintptr_t)ptr & 0xffffffffffffff);
+}
+
+struct big_struct {
+    int left;
+    int right;
+    void *ptr;
+    int big_array[100];
+};
+
+/*
+   Tests for RVO (basically, checking -fsanitize=hwaddress has not broken RVO
+   in any way).
+
+   0) The value is accessible in both functions without a hwasan complaint.
+   1) RVO does happen.
+ */
+
+struct big_struct __attribute__ ((noinline))
+return_on_stack()
+{
+  struct big_struct x;
+  x.left = 100;
+  x.right = 20;
+  x.big_array[10] = 30;
+  x.ptr = untagged(&x);
+  return x;
+}
+
+int main()
+{
+  struct big_struct x;
+  x = return_on_stack();
+  /* Check that RVO happens by checking the address that the callee saw.  */
+  assert (x.ptr == untagged(&x));
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan.exp b/gcc/testsuite/gcc.dg/hwasan/hwasan.exp
new file mode 100644
index 0000000000000000000000000000000000000000..5c040aec24a222e4024a548e055c52cee4afe8f8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/hwasan.exp
@@ -0,0 +1,36 @@
+# Copyright (C) 2012-2019 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+load_lib hwasan-dg.exp
+
+# Initialize `dg'.
+dg-init
+hwasan_init
+
+# Main loop.
+if [check_effective_target_fsanitize_hwaddress] {
+  gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c $srcdir/c-c++-common/hwasan/*.c]] "" ""
+}
+
+# All done.
+hwasan_finish
+dg-finish
diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c
new file mode 100644
index 0000000000000000000000000000000000000000..0afcc100508cd2a5467698e901e32fa2292a32c9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c
@@ -0,0 +1,53 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+
+/*
+   Tests of nested funtions are:
+    0) Accessing closed over variables works.
+    1) Accesses outside of variables is caught.
+    2) Accessing variable out of scope is caught.
+
+    Here we test that accessing closed over variables works.
+ */
+
+/* We need a second layer of indirection so that GCC doesn't notice we're
+   returning the address of a local variable and put 0 in it's place.  */
+__attribute__((noinline))
+int *Ident(void *x) {
+  return x;
+}
+
+int __attribute__ ((noinline))
+intermediate (void (*f) (int, char),
+	      char num)
+{
+  if (num == 1)
+    /* NOTE: We need to overrun by an amount greater than the "extra data" in a
+       nonlocal goto structure.  The entire structure is allocated on the stack
+       with a single tag, which means hwasan can't tell if a closed-over buffer
+       was overrun by an amount small enough that the access was still to some
+       data in that nonlocal goto structure.  */
+    f (100, 100);
+  else
+    f (3, 100);
+  /* Just return something ... */
+  return num % 3;
+}
+
+int* __attribute__ ((noinline))
+nested_function (char num)
+{
+  int big_array[16];
+  int other_array[16];
+  void store (int index, char value)
+    { big_array[index] = value; }
+  return Ident(&other_array[intermediate (store, num)]);
+}
+
+#ifndef MAIN
+int main ()
+{
+  nested_function (0);
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..016128101e23c6405d943f4a2a4729f7c48ff95a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c
@@ -0,0 +1,27 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+
+/*
+   Tests of nested funtions are:
+    0) Accessing closed over variables works.
+    1) Accesses outside of variables is caught.
+    2) Accessing variable out of scope is caught.
+
+    Here we test option 1.
+ */
+
+#define MAIN 0
+#include "nested-functions-0.c"
+#undef MAIN
+
+int main ()
+{
+  nested_function (1);
+  return 0;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "WRITE of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..b1a033fdb365cf53882fc1d8294b32bbad16ea8a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-effective-target hwaddress_exec } */
+/* { dg-shouldfail "hwasan" } */
+
+/*
+   Tests of nested funtions are:
+    0) Accessing closed over variables works.
+    1) Accesses outside of variables is caught.
+    2) Accessing variable out of scope is caught.
+
+    Here we test option 2.
+ */
+
+#define MAIN 0
+#include "nested-functions-0.c"
+#undef MAIN
+
+int main ()
+{
+  int *retval = nested_function (2);
+  *retval = 100;
+  return 0;
+}
+
+/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */
+/* { dg-output "WRITE of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */
+/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */
+/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */
diff --git a/gcc/testsuite/lib/hwasan-dg.exp b/gcc/testsuite/lib/hwasan-dg.exp
new file mode 100644
index 0000000000000000000000000000000000000000..78942fbe0f2c4958ea8cd060f4d3f83400f50895
--- /dev/null
+++ b/gcc/testsuite/lib/hwasan-dg.exp
@@ -0,0 +1,364 @@
+# Copyright (C) 2012-2019 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Return 1 if compilation with -fsanitize=hwaddress is error-free for trivial
+# code, 0 otherwise.
+
+proc check_effective_target_fsanitize_hwaddress {} {
+    if ![check_no_compiler_messages fsanitize_hwaddress executable {
+	int main (void) { return 0; }
+    }] {
+	return 0;
+    }
+    return 1;
+}
+
+proc check_effective_target_hwaddress_exec {} {
+    if ![check_runtime hwaddress_exec {
+	int main (void) { return 0; }
+    }] {
+	return 0;
+    }
+    return 1;
+
+    # hwasan doesn't work if there's a ulimit on virtual memory.
+    if ![is_remote target] {
+	if [catch {exec sh -c "ulimit -v"} ulimit_v] {
+	    # failed to get ulimit
+	} elseif [regexp {^[0-9]+$} $ulimit_v] {
+	    # ulimit -v gave a numeric limit
+	    warning "skipping hwasan tests due to ulimit -v"
+	    return 0;
+	}
+    }
+}
+
+proc hwasan_include_flags {} {
+    global srcdir
+    global TESTING_IN_BUILD_TREE
+
+    set flags ""
+
+    if { [is_remote host] || ! [info exists TESTING_IN_BUILD_TREE] } {
+      return "${flags}"
+    }
+
+    set flags "-I$srcdir/../../libsanitizer/include"
+
+    return "$flags"
+}
+
+#
+# hwasan_link_flags -- compute library path and flags to find libhwasan.
+# (originally from g++.exp)
+#
+
+proc hwasan_link_flags { paths } {
+    global srcdir
+    global ld_library_path
+    global shlib_ext
+    global hwasan_saved_library_path
+
+    set gccpath ${paths}
+    set flags ""
+
+    set shlib_ext [get_shlib_extension]
+    set hwasan_saved_library_path $ld_library_path
+
+    if { $gccpath != "" } {
+      if { [file exists "${gccpath}/libsanitizer/hwasan/.libs/libhwasan.a"]
+	   || [file exists "${gccpath}/libsanitizer/hwasan/.libs/libhwasan.${shlib_ext}"] } {
+	  append flags " -B${gccpath}/libsanitizer/ "
+	  append flags " -B${gccpath}/libsanitizer/hwasan/ "
+	  append flags " -L${gccpath}/libsanitizer/hwasan/.libs "
+	  append ld_library_path ":${gccpath}/libsanitizer/hwasan/.libs"
+      }
+    } else {
+      global tool_root_dir
+
+      set libhwasan [lookfor_file ${tool_root_dir} libhwasan]
+      if { $libhwasan != "" } {
+	  append flags "-L${libhwasan} "
+	  append ld_library_path ":${libhwasan}"
+      }
+    }
+
+    set_ld_library_path_env_vars
+
+    return "$flags"
+}
+
+#
+# hwasan_init -- called at the start of each subdir of tests
+#
+
+proc hwasan_init { args } {
+    global TEST_ALWAYS_FLAGS
+    global ALWAYS_CXXFLAGS
+    global TOOL_OPTIONS
+    global hwasan_saved_TEST_ALWAYS_FLAGS
+    global hwasan_saved_ALWAYS_CXXFLAGS
+
+    setenv HWASAN_OPTIONS "random_tags=0"
+
+    set link_flags ""
+    if ![is_remote host] {
+	if [info exists TOOL_OPTIONS] {
+	    set link_flags "[hwasan_link_flags [get_multilibs ${TOOL_OPTIONS}]]"
+	} else {
+	    set link_flags "[hwasan_link_flags [get_multilibs]]"
+	}
+    }
+
+    set include_flags "[hwasan_include_flags]"
+
+    if [info exists TEST_ALWAYS_FLAGS] {
+	set hwasan_saved_TEST_ALWAYS_FLAGS $TEST_ALWAYS_FLAGS
+    }
+    if [info exists ALWAYS_CXXFLAGS] {
+	set hwasan_saved_ALWAYS_CXXFLAGS $ALWAYS_CXXFLAGS
+	set ALWAYS_CXXFLAGS [concat "{ldflags=$link_flags}" $ALWAYS_CXXFLAGS]
+	set ALWAYS_CXXFLAGS [concat "{additional_flags=-fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags}" $ALWAYS_CXXFLAGS]
+    } else {
+	if [info exists TEST_ALWAYS_FLAGS] {
+	    set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags $TEST_ALWAYS_FLAGS"
+	} else {
+	    set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags"
+	}
+    }
+}
+
+#
+# hwasan_finish -- called at the start of each subdir of tests
+#
+
+proc hwasan_finish { args } {
+    global TEST_ALWAYS_FLAGS
+    global hwasan_saved_TEST_ALWAYS_FLAGS
+    global hwasan_saved_ALWAYS_CXXFLAGS
+    global hwasan_saved_library_path
+    global ld_library_path
+
+    unsetenv HWASAN_OPTIONS
+
+    if [info exists hwasan_saved_ALWAYS_CXXFLAGS ] {
+	set ALWAYS_CXXFLAGS $hwasan_saved_ALWAYS_CXXFLAGS
+    } else {
+	if [info exists hwasan_saved_TEST_ALWAYS_FLAGS] {
+	    set TEST_ALWAYS_FLAGS $hwasan_saved_TEST_ALWAYS_FLAGS
+	} else {
+	    unset TEST_ALWAYS_FLAGS
+	}
+    }
+    set ld_library_path $hwasan_saved_library_path
+    set_ld_library_path_env_vars
+    clear_effective_target_cache
+}
+
+# Symbolize lines like
+#   #2 0xdeadbeef (/some/path/libsanitizer.so.0.0.0+0xbeef)
+# in $output using addr2line to
+#   #2 0xdeadbeef in foobar file:123
+proc hwasan_symbolize { output } {
+    set addresses [regexp -inline -all -line "^ *#\[0-9\]+ 0x\[0-9a-f\]+ \[(\](\[^)\]+)\[+\](0x\[0-9a-f\]+)\[)\]$" "$output"]
+    if { [llength $addresses] > 0 } {
+	set addr2line_name [find_binutils_prog addr2line]
+	set idx 1
+	while { $idx < [llength $addresses] } {
+	    set key [regsub -all "\[\]\[\]" [lindex $addresses $idx] "\\\\&"]
+	    set val [lindex $addresses [expr $idx + 1]]
+	    lappend arr($key) $val
+	    set idx [expr $idx + 3]
+	}
+	foreach key [array names arr] {
+	    set args "-f -e $key $arr($key)"
+	    set status [remote_exec host "$addr2line_name" "$args"]
+	    if { [lindex $status 0] > 0 } continue
+	    regsub -all "\r\n" [lindex $status 1] "\n" addr2line_output
+	    regsub -all "\[\n\r\]BFD: \[^\n\r\]*" $addr2line_output "" addr2line_output
+	    regsub -all "^BFD: \[^\n\r\]*\[\n\r\]" $addr2line_output "" addr2line_output
+	    set addr2line_output [regexp -inline -all -line "^\[^\n\r]*" $addr2line_output]
+	    set idx 0
+	    foreach val $arr($key) {
+		if { [expr $idx + 1] < [llength $addr2line_output] } {
+		    set fnname [lindex $addr2line_output $idx]
+		    set fileline [lindex $addr2line_output [expr $idx + 1]]
+		    if { "$fnname" != "??" } {
+			set newkey "$key+$val"
+			set repl($newkey) "$fnname $fileline"
+		    }
+		    set idx [expr $idx + 2]
+		}
+	    }
+	}
+	set idx 0
+	set new_output ""
+	while {[regexp -start $idx -indices " #\[0-9\]+ 0x\[0-9a-f\]+ \[(\](\[^)\]+\[+\]0x\[0-9a-f\]+)\[)\]" "$output" -> addr] > 0} {
+	    set low [lindex $addr 0]
+	    set high [lindex $addr 1]
+	    set val [string range "$output" $low $high]
+	    append new_output [string range "$output" $idx [expr $low - 2]]
+	    if [info exists repl($val)] {
+		append new_output "in $repl($val)"
+	    } else {
+		append new_output "($val)"
+	    }
+	    set idx [expr $high + 2]
+	}
+	append new_output [string range "$output" $idx [string length "$output"]]
+	return "$new_output"
+    }
+    return "$output"
+}
+
+# Return a list of gtest tests, printed in the form
+# DEJAGNU_GTEST_TEST AddressSanitizer_SimpleDeathTest
+# DEJAGNU_GTEST_TEST AddressSanitizer_VariousMallocsTest
+proc hwasan_get_gtest_test_list { output } {
+    set idx 0
+    set ret ""
+    while {[regexp -start $idx -indices "DEJAGNU_GTEST_TEST (\[^\n\r\]*)(\r\n|\n|\r)" "$output" -> testname] > 0} {
+	set low [lindex $testname 0]
+	set high [lindex $testname 1]
+	set val [string range "$output" $low $high]
+	lappend ret $val
+	set idx [expr $high + 1]
+    }
+    return $ret
+}
+
+# Return a list of gtest EXPECT_DEATH tests, printed in the form
+# DEJAGNU_GTEST_EXPECT_DEATH1 statement DEJAGNU_GTEST_EXPECT_DEATH1 regexp DEJAGNU_GTEST_EXPECT_DEATH1
+# DEJAGNU_GTEST_EXPECT_DEATH2 other statement DEJAGNU_GTEST_EXPECT_DEATH2 other regexp DEJAGNU_GTEST_EXPECT_DEATH2
+proc hwasan_get_gtest_expect_death_list { output } {
+    set idx 0
+    set ret ""
+    while {[regexp -start $idx -indices "DEJAGNU_GTEST_EXPECT_DEATH(\[0-9\]*)" "$output" -> id ] > 0} {
+	set low [lindex $id 0]
+	set high [lindex $id 1]
+	set val_id [string range "$output" $low $high]
+	if {[regexp -start $low -indices "$val_id (.*) DEJAGNU_GTEST_EXPECT_DEATH$val_id (.*) DEJAGNU_GTEST_EXPECT_DEATH$val_id\[\n\r\]" "$output" whole statement regexpr ] == 0} { break }
+	set low [lindex $statement 0]
+	set high [lindex $statement 1]
+	set val_statement [string range "$output" $low $high]
+	set low [lindex $regexpr 0]
+	set high [lindex $regexpr 1]
+	set val_regexpr [string range "$output" $low $high]
+	lappend ret [list "$val_id" "$val_statement" "$val_regexpr"]
+	set idx [lindex $whole 1]
+    }
+    return $ret
+}
+
+# Replace ${tool}_load with a wrapper so that we can symbolize the output.
+if { [info procs ${tool}_load] != [list] \
+      && [info procs saved_hwasan_${tool}_load] == [list] } {
+    rename ${tool}_load saved_hwasan_${tool}_load
+
+    proc ${tool}_load { program args } {
+	global tool
+	global hwasan_last_gtest_test_list
+	global hwasan_last_gtest_expect_death_list
+	set result [eval [list saved_hwasan_${tool}_load $program] $args]
+	set output [lindex $result 1]
+	set symbolized_output [hwasan_symbolize "$output"]
+	set hwasan_last_gtest_test_list [hwasan_get_gtest_test_list "$output"]
+	set hwasan_last_gtest_expect_death_list [hwasan_get_gtest_expect_death_list "$output"]
+	set result [list [lindex $result 0] $symbolized_output]
+	return $result
+    }
+}
+
+# Utility for running gtest hwasan emulation under dejagnu, invoked via dg-final.
+# Call pass if variable has the desired value, otherwise fail.
+#
+# Argument 0 handles expected failures and the like
+proc hwasan-gtest { args } {
+    global tool
+    global hwasan_last_gtest_test_list
+    global hwasan_last_gtest_expect_death_list
+
+    if { ![info exists hwasan_last_gtest_test_list] } { return }
+    if { [llength $hwasan_last_gtest_test_list] == 0 } { return }
+    if { ![isnative] || [is_remote target] } { return }
+
+    set gtest_test_list $hwasan_last_gtest_test_list
+    unset hwasan_last_gtest_test_list
+
+    if { [llength $args] >= 1 } {
+	switch [dg-process-target [lindex $args 0]] {
+	    "S" { }
+	    "N" { return }
+	    "F" { setup_xfail "*-*-*" }
+	    "P" { }
+	}
+    }
+
+    # This assumes that we are three frames down from dg-test, and that
+    # it still stores the filename of the testcase in a local variable "name".
+    # A cleaner solution would require a new DejaGnu release.
+    upvar 2 name testcase
+    upvar 2 prog prog
+
+    set output_file "[file rootname [file tail $prog]].exe"
+
+    foreach gtest $gtest_test_list {
+	set testname "$testcase $gtest"
+	set status -1
+
+	setenv DEJAGNU_GTEST_ARG "$gtest"
+	set result [${tool}_load ./$output_file $gtest]
+	unsetenv DEJAGNU_GTEST_ARG
+	set status [lindex $result 0]
+	set output [lindex $result 1]
+	if { "$status" == "pass" } {
+	    pass "$testname execution test"
+	    if { [info exists hwasan_last_gtest_expect_death_list] } {
+		set gtest_expect_death_list $hwasan_last_gtest_expect_death_list
+		foreach gtest_death $gtest_expect_death_list {
+		    set id [lindex $gtest_death 0]
+		    set testname "$testcase $gtest [lindex $gtest_death 1]"
+		    set regexpr [lindex $gtest_death 2]
+		    set status -1
+
+		    setenv DEJAGNU_GTEST_ARG "$gtest:$id"
+		    set result [${tool}_load ./$output_file "$gtest:$id"]
+		    unsetenv DEJAGNU_GTEST_ARG
+		    set status [lindex $result 0]
+		    set output [lindex $result 1]
+		    if { "$status" == "fail" } {
+			pass "$testname execution test"
+			if { ![regexp $regexpr ${output}] } {
+			    fail "$testname output pattern test"
+			    send_log "Output should match: $regexpr\n"
+			} else {
+			    pass "$testname output pattern test"
+			}
+		    } elseif { "$status" == "pass" } {
+			fail "$testname execution test"
+		    } else {
+			$status "$testname execution test"
+		    }
+		}
+	    }
+	} else {
+	    $status "$testname execution test"
+	}
+	unset hwasan_last_gtest_expect_death_list
+    }
+
+    return
+}


[-- Attachment #2: updated-tests.patch.gz --]
[-- Type: application/gzip, Size: 15266 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [Patch 0/X] HWASAN v3
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (8 preceding siblings ...)
  2019-12-16 11:47     ` [PATCH 7/X] [libsanitizer] Add tests Matthew Malcomson
@ 2019-12-17 14:32     ` Matthew Malcomson
  2020-01-06 15:26       ` [PING] " Matthew Malcomson
  2020-01-07 15:14     ` Martin Liška
                       ` (8 subsequent siblings)
  18 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2019-12-17 14:32 UTC (permalink / raw)
  To: gcc-patches
  Cc: nd, rguenther, kcc, law, dvyukov, Martin Liska, Richard Earnshaw,
	Kyrylo Tkachov, Marcus Shawcroft, ian, dodji, joseph, jakub

[-- Attachment #1: Type: text/plain, Size: 3118 bytes --]

I've noticed a few minor problems with this patch series after I sent it 
out (mostly testcase stuff, one documentation tidy-up, but also that one 
patch didn't bootstrap due to something fixed in a later patch).

I also rely on a documentation change that isn't part of the series.

I figure I should make this easy on anyone that wants to try the patch 
series out, so I'm attaching a compressed tarfile containing the entire 
patch series plus the additional documentation patch so it can all be 
applied at once with `git apply *`.

It's attached.

Matthew.



On 12/12/2019 15:18, Matthew Malcomson wrote:
> Hello,
> 
> I've gone through the suggestions Martin made and implemented  the ones I think
> I can implement for GCC10.
> 
> The two functionality changes in this version are:
> Added the --param's hwasan-instrument-reads, hwasan-instrument-writes,
> hwasan-instrument-allocas, hwasan-memintrin, options.  I.e. Those that asan has
> and that make sense for hwasan.
> 
> Avoided HWASAN_STACK_BACKGROUND in hwasan_increment_tag when using a
> deterministic tagging approach.
> 
> 
> There are a lot of extra comments and tests.
> 
> 
> Bootstrapped and regtested on x86_64 and AArch64.
> Bootstrapped with `--with-build-config=bootstrap-hwasan` on AArch64 and hwasan
> features tested there.
> Built the linux kernel using this feature and ran the test_kasan.ko testing to
> check the this works for the kernel.
> (NOTE: I actually did all the above testing before a search and replace of
> `memory_tagging_p` for `hwasan_sanitize_p` and fixing a typo in the
> `hwasan-instrument-allocas` parameter name, I will run all the tests again
> before committing but figure I'll send this out now since I fully expect the
> tests to still pass).
> 
> 
> I noticed one extra testsuite failure from those mentioned in the previous
> version emails: g++.dg/cpp2a/ucn2.C.
> I believe this is HWASAN correctly catching a problem in the compiler.
> I've logged the issue here https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92919 .
> 
> 
> I haven't gotten ASAN_MARK to print as HWASAN_MARK when using memory tagging,
> since I'm not sure the way I found to implement this would be acceptable.  The
> inlined patch below works but it requires a special declaration instead of just
> an ~#include~.
> 
> 
> diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h
> index a1bc081..d81eb12 100644
> --- a/gcc/internal-fn.h
> +++ b/gcc/internal-fn.h
> @@ -101,10 +101,16 @@ extern void init_internal_fns ();
>   
>   extern const char *const internal_fn_name_array[];
>   
> +
> +extern bool hwasan_sanitize_p (void);
>   static inline const char *
>   internal_fn_name (enum internal_fn fn)
>   {
> -  return internal_fn_name_array[(int) fn];
> +  const char *ret = internal_fn_name_array[(int) fn];
> +  if (! strcmp (ret, "ASAN_MARK")
> +      && hwasan_sanitize_p ())
> +    return "HWASAN_MARK";
> +  return ret;
>   }
>   
>   extern internal_fn lookup_internal_fn (const char *);
> 
> 
> Entire patch series attached to cover letter.
> 


[-- Attachment #2: all-patches.tar.gz --]
[-- Type: application/gzip, Size: 61363 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PING] Re: [Patch 0/X] HWASAN v3
  2019-12-17 14:32     ` [Patch 0/X] HWASAN v3 Matthew Malcomson
@ 2020-01-06 15:26       ` Matthew Malcomson
  0 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2020-01-06 15:26 UTC (permalink / raw)
  To: gcc-patches, Martin Liška
  Cc: nd, rguenther, kcc, law, dvyukov, Martin Liska, Richard Earnshaw,
	Kyrylo Tkachov, Marcus Shawcroft, ian, dodji, joseph, jakub

Ping


On 17/12/2019 14:11, Matthew Malcomson wrote:
> I've noticed a few minor problems with this patch series after I sent it
> out (mostly testcase stuff, one documentation tidy-up, but also that one
> patch didn't bootstrap due to something fixed in a later patch).
> 
> I also rely on a documentation change that isn't part of the series.
> 
> I figure I should make this easy on anyone that wants to try the patch
> series out, so I'm attaching a compressed tarfile containing the entire
> patch series plus the additional documentation patch so it can all be
> applied at once with `git apply *`.
> 
> It's attached.
> 
> Matthew.
> 
> 
> 
> On 12/12/2019 15:18, Matthew Malcomson wrote:
>> Hello,
>>
>> I've gone through the suggestions Martin made and implemented  the ones I think
>> I can implement for GCC10.
>>
>> The two functionality changes in this version are:
>> Added the --param's hwasan-instrument-reads, hwasan-instrument-writes,
>> hwasan-instrument-allocas, hwasan-memintrin, options.  I.e. Those that asan has
>> and that make sense for hwasan.
>>
>> Avoided HWASAN_STACK_BACKGROUND in hwasan_increment_tag when using a
>> deterministic tagging approach.
>>
>>
>> There are a lot of extra comments and tests.
>>
>>
>> Bootstrapped and regtested on x86_64 and AArch64.
>> Bootstrapped with `--with-build-config=bootstrap-hwasan` on AArch64 and hwasan
>> features tested there.
>> Built the linux kernel using this feature and ran the test_kasan.ko testing to
>> check the this works for the kernel.
>> (NOTE: I actually did all the above testing before a search and replace of
>> `memory_tagging_p` for `hwasan_sanitize_p` and fixing a typo in the
>> `hwasan-instrument-allocas` parameter name, I will run all the tests again
>> before committing but figure I'll send this out now since I fully expect the
>> tests to still pass).
>>
>>
>> I noticed one extra testsuite failure from those mentioned in the previous
>> version emails: g++.dg/cpp2a/ucn2.C.
>> I believe this is HWASAN correctly catching a problem in the compiler.
>> I've logged the issue here https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92919 .
>>
>>
>> I haven't gotten ASAN_MARK to print as HWASAN_MARK when using memory tagging,
>> since I'm not sure the way I found to implement this would be acceptable.  The
>> inlined patch below works but it requires a special declaration instead of just
>> an ~#include~.
>>
>>
>> diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h
>> index a1bc081..d81eb12 100644
>> --- a/gcc/internal-fn.h
>> +++ b/gcc/internal-fn.h
>> @@ -101,10 +101,16 @@ extern void init_internal_fns ();
>>    
>>    extern const char *const internal_fn_name_array[];
>>    
>> +
>> +extern bool hwasan_sanitize_p (void);
>>    static inline const char *
>>    internal_fn_name (enum internal_fn fn)
>>    {
>> -  return internal_fn_name_array[(int) fn];
>> +  const char *ret = internal_fn_name_array[(int) fn];
>> +  if (! strcmp (ret, "ASAN_MARK")
>> +      && hwasan_sanitize_p ())
>> +    return "HWASAN_MARK";
>> +  return ret;
>>    }
>>    
>>    extern internal_fn lookup_internal_fn (const char *);
>>
>>
>> Entire patch series attached to cover letter.
>>

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 4/X] [libsanitizer][options] Add hwasan flags and argument parsing
  2019-12-12 15:19     ` [PATCH 4/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
@ 2020-01-07 15:04       ` Martin Liška
  0 siblings, 0 replies; 150+ messages in thread
From: Martin Liška @ 2020-01-07 15:04 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, kcc, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 12/12/19 4:19 PM, Matthew Malcomson wrote:
> +-param=hwasan-stack=
> +Common Joined UInteger Var(param_hwasan_stack) Init(1) IntegerRange(0, 1) Param
> +Enable hwasan stack protection.
> +
> +-param=hwasan-random-frame-tag=
> +Common Joined UInteger Var(param_hwasan_random_frame_tag) Init(1) IntegerRange(0, 1) Param
> +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
> +Enable hwasan allocas/VLAs protection.
> +
> +-param=hwasan-instrument-reads=
> +Common Joined UInteger Var(param_hwasan_instrument_reads) Init(1) IntegerRange(0, 1) Param
> +Enable hwasan load operations protection.
> +
> +-param=hwasan-instrument-writes=
> +Common Joined UInteger Var(param_hwasan_instrument_writes) Init(1) IntegerRange(0, 1) Param
> +Enable hwasan store operations protection.
> +
> +-param=hwasan-memintrin=
> +Common Joined UInteger Var(param_hwasan_memintrin) Init(1) IntegerRange(0, 1) Param
> +Enable hwasan builtin functions protection.

Similarly to majority of ASAN parameters, all these should have Optimization keyword.
That means one can have different parameter values for each function (one can do that
with LTO right now).

Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes
  2019-12-12 15:19     ` [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes Matthew Malcomson
@ 2020-01-07 15:10       ` Martin Liška
  2020-01-13 12:20         ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2020-01-07 15:10 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, kcc, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub

On 12/12/19 4:19 PM, Matthew Malcomson wrote:
> - if (is_store && !param_asan_instrument_writes)
> + if (is_store
> + && (!param_asan_instrument_writes || !param_hwasan_instrument_writes))
> return;
> - if (!is_store && !param_asan_instrument_reads)
> + if (!is_store
> + && (!param_asan_instrument_reads || !param_hwasan_instrument_reads))
> return;

I know it's very unlikely, but one can use -fsanitize=address and
--param hwasan-instrument-reads=0 which will drop instrumentation of reads
for ASAN.

Similarly for other parameters.

Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [Patch 0/X] HWASAN v3
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (9 preceding siblings ...)
  2019-12-17 14:32     ` [Patch 0/X] HWASAN v3 Matthew Malcomson
@ 2020-01-07 15:14     ` Martin Liška
  2020-01-08 11:26       ` Matthew Malcomson
  2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
                       ` (7 subsequent siblings)
  18 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2020-01-07 15:14 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: nd, rguenther, kcc, law, dvyukov, Richard Earnshaw,
	Kyrylo Tkachov, Marcus Shawcroft, ian, dodji, joseph, jakub

On 12/12/19 4:18 PM, Matthew Malcomson wrote:

Hello.

I've just sent few comments that are related to the v3 of the patch set.
Based on the HWASAN (limited) knowledge the patch seems reasonable to me.
I haven't looked much at the newly introduced RTL-hooks.
But these seems to me isolated to the aarch64 port.

I can also verify that the patchset works on my aarch64 linux machine and
hwasan.exp and asan.exp tests succeed.

> I haven't gotten ASAN_MARK to print as HWASAN_MARK when using memory tagging,
> since I'm not sure the way I found to implement this would be acceptable.  The
> inlined patch below works but it requires a special declaration instead of just
> an ~#include~.

Knowing that, I would not bother with the printing of HWASAN_MARK.

Thanks for the series,
Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [Patch 0/X] HWASAN v3
  2020-01-07 15:14     ` Martin Liška
@ 2020-01-08 11:26       ` Matthew Malcomson
  2020-01-08 19:30         ` Kostya Serebryany via gcc-patches
  2020-01-10 16:17         ` Kyrill Tkachov
  0 siblings, 2 replies; 150+ messages in thread
From: Matthew Malcomson @ 2020-01-08 11:26 UTC (permalink / raw)
  To: gcc-patches
  Cc: Martin Liška, nd, rguenther, kcc, law, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub, Evgenii Stepanov, Peter Collingbourne

Hi everyone,

I'm writing this email to summarise & publicise the state of this patch 
series, especially the difficulties around approval for GCC 10 mentioned 
on IRC.


The main obstacle seems to be that no maintainer feels they have enough 
knowledge about hwasan and justification that it's worthwhile to approve 
the patch series.

Similarly, Martin has given a review of the parts of the code he can 
(thanks!), but doesn't feel he can do a deep review of the code related 
to the RTL hooks and stack expansion -- hence that part is as yet not 
reviewed in-depth.



The questions around justification raised on IRC are mainly that it 
seems like a proof-of-concept for MTE rather than a stand-alone useable 
sanitizer.  Especially since in the GNU world hwasan instrumented code 
is not really ready for production since we can only use the 
less-"interceptor ABI" rather than the "platform ABI".  This restriction 
is because there is no version of glibc with the required modifications 
to provide the "platform ABI".

(n.b. that since https://reviews.llvm.org/D69574 the code-generation for 
these ABI's is the same).


 From my perspective the reasons that make HWASAN useful in itself are:

1) Much less memory usage.

 From a back-of-the-envelope calculation based on the hwasan paper's 
table of memory overhead from over-alignment 
https://arxiv.org/pdf/1802.09517.pdf  I guess hwasan instrumented code 
has an overhead of about 1.1x (~4% from overalignment and ~6.25% from 
shadow memory), while asan seems to have an overhead somewhere in the 
range 1.5x - 3x.

Maybe there's some data out there comparing total overheads that I 
haven't found? (I'd appreciate a reference if anyone has that info).



2) Available on more architectures that MTE.

HWASAN only requires TBI, which is a feature of all AArch64 machines, 
while MTE will be an optional extension and only available on certain 
architectures.


3) This enables using hwasan in the kernel.

While instrumented user-space applications will be using the 
"interceptor ABI" and hence are likely not production-quality, the 
biggest aim of implementing hwasan in GCC is to allow building the Linux 
kernel with tag-based sanitization using GCC.

Instrumented kernel code uses hooks in the kernel itself, so this ABI 
distinction is no longer relevant, and this sanitizer should produce a 
production-quality kernel binary.




I'm hoping I can find a maintainer willing to review and ACK this patch 
series -- especially with stage3 coming to a close soon.  If there's 
anything else I could do to help get someone willing up-to-speed then 
please just ask.


Cheers,
Matthew



On 07/01/2020 15:14, Martin Liška wrote:
> On 12/12/19 4:18 PM, Matthew Malcomson wrote:
> 
> Hello.
> 
> I've just sent few comments that are related to the v3 of the patch set.
> Based on the HWASAN (limited) knowledge the patch seems reasonable to me.
> I haven't looked much at the newly introduced RTL-hooks.
> But these seems to me isolated to the aarch64 port.
> 
> I can also verify that the patchset works on my aarch64 linux machine and
> hwasan.exp and asan.exp tests succeed.
> 
>> I haven't gotten ASAN_MARK to print as HWASAN_MARK when using memory 
>> tagging,
>> since I'm not sure the way I found to implement this would be 
>> acceptable.  The
>> inlined patch below works but it requires a special declaration 
>> instead of just
>> an ~#include~.
> 
> Knowing that, I would not bother with the printing of HWASAN_MARK.
> 
> Thanks for the series,
> Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [Patch 0/X] HWASAN v3
  2020-01-08 11:26       ` Matthew Malcomson
@ 2020-01-08 19:30         ` Kostya Serebryany via gcc-patches
  2020-01-10 16:17         ` Kyrill Tkachov
  1 sibling, 0 replies; 150+ messages in thread
From: Kostya Serebryany via gcc-patches @ 2020-01-08 19:30 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, Martin Liška, nd, rguenther, law, dvyukov,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian, dodji,
	joseph, jakub, Evgenii Stepanov, Peter Collingbourne

[asan/hwasan co-author here, with clearly biased opinions]

On Android, HWASAN is already a fully usable testing tool.
We apply it to the kernel, user space system libraries, and select apps.
A phone with HWASAN-ified system is fully usable (I carry one as my
primary device since March 2019).
HWASAN has discovered over 120 bugs by now (heap-use-after-free,
heap/stack buffer overflows, stack-use-after-return, double free).
Many of the bugs were discovered during the everyday use (as opposed
to testing in the lab).
The overhead is low enough that on a top-tier CPU the user will rarely
notice any slowdown
(the increased battery drain *is* noticeable - compiler
instrumentation is not a substitute for hardware).
HWASAN has also helped discover 4 instances of future incompatibility
with MTE, all fixed.

The main benefit of HWASAN over ASAN is, as Matthew correctly
explains, the memory usage.
On embedded devices, this is often the difference between "can't
deploy" and "can deploy"
because, unlike in the server land, you can't install more RAM.

The other, more subtle benefit, is that HWASAN is more sensitive to
some types of bugs,
such as buffer-overflow-far-from-bounds or use-after-long-ago-free, etc.

MTE hardware is years away. Even once we have it in major new devices,
many smaller devices will still be running on Arm v8, for a decade or two.
As with ASAN/TSAN/UBSAN, having this sanitizer implemented in GCC will
vastly extend its user base and applicability and thus contribute to
the overall code quality and security.

Whether HWASAN should intercept libc functions or libc itself should
support HWASAN...
My strong opinion is that today the interception approach can only be
seen as a way to prototype.
ASAN, implemented in 2011, had to use interception because we needed
to get a new idea working fast.
However, over these 9 years, the interception caused an enormous
amount of complexity and user dissatisfaction.
The Android implementation of HWASAN (with hooks in the Bionic libc
and no interceptors) is
many times simpler, robust, and complete.
We need to do the same for other LIBCs, eventually, but we don't have
to do it immediately.

--kcc





On Wed, Jan 8, 2020 at 3:26 AM Matthew Malcomson
<Matthew.Malcomson@arm.com> wrote:
>
> Hi everyone,
>
> I'm writing this email to summarise & publicise the state of this patch
> series, especially the difficulties around approval for GCC 10 mentioned
> on IRC.
>
>
> The main obstacle seems to be that no maintainer feels they have enough
> knowledge about hwasan and justification that it's worthwhile to approve
> the patch series.
>
> Similarly, Martin has given a review of the parts of the code he can
> (thanks!), but doesn't feel he can do a deep review of the code related
> to the RTL hooks and stack expansion -- hence that part is as yet not
> reviewed in-depth.
>
>
>
> The questions around justification raised on IRC are mainly that it
> seems like a proof-of-concept for MTE rather than a stand-alone useable
> sanitizer.  Especially since in the GNU world hwasan instrumented code
> is not really ready for production since we can only use the
> less-"interceptor ABI" rather than the "platform ABI".  This restriction
> is because there is no version of glibc with the required modifications
> to provide the "platform ABI".
>
> (n.b. that since https://reviews.llvm.org/D69574 the code-generation for
> these ABI's is the same).
>
>
>  From my perspective the reasons that make HWASAN useful in itself are:
>
> 1) Much less memory usage.
>
>  From a back-of-the-envelope calculation based on the hwasan paper's
> table of memory overhead from over-alignment
> https://arxiv.org/pdf/1802.09517.pdf  I guess hwasan instrumented code
> has an overhead of about 1.1x (~4% from overalignment and ~6.25% from
> shadow memory), while asan seems to have an overhead somewhere in the
> range 1.5x - 3x.
>
> Maybe there's some data out there comparing total overheads that I
> haven't found? (I'd appreciate a reference if anyone has that info).
>
>
>
> 2) Available on more architectures that MTE.
>
> HWASAN only requires TBI, which is a feature of all AArch64 machines,
> while MTE will be an optional extension and only available on certain
> architectures.
>
>
> 3) This enables using hwasan in the kernel.
>
> While instrumented user-space applications will be using the
> "interceptor ABI" and hence are likely not production-quality, the
> biggest aim of implementing hwasan in GCC is to allow building the Linux
> kernel with tag-based sanitization using GCC.
>
> Instrumented kernel code uses hooks in the kernel itself, so this ABI
> distinction is no longer relevant, and this sanitizer should produce a
> production-quality kernel binary.
>
>
>
>
> I'm hoping I can find a maintainer willing to review and ACK this patch
> series -- especially with stage3 coming to a close soon.  If there's
> anything else I could do to help get someone willing up-to-speed then
> please just ask.
>
>
> Cheers,
> Matthew
>
>
>
> On 07/01/2020 15:14, Martin Liška wrote:
> > On 12/12/19 4:18 PM, Matthew Malcomson wrote:
> >
> > Hello.
> >
> > I've just sent few comments that are related to the v3 of the patch set.
> > Based on the HWASAN (limited) knowledge the patch seems reasonable to me.
> > I haven't looked much at the newly introduced RTL-hooks.
> > But these seems to me isolated to the aarch64 port.
> >
> > I can also verify that the patchset works on my aarch64 linux machine and
> > hwasan.exp and asan.exp tests succeed.
> >
> >> I haven't gotten ASAN_MARK to print as HWASAN_MARK when using memory
> >> tagging,
> >> since I'm not sure the way I found to implement this would be
> >> acceptable.  The
> >> inlined patch below works but it requires a special declaration
> >> instead of just
> >> an ~#include~.
> >
> > Knowing that, I would not bother with the printing of HWASAN_MARK.
> >
> > Thanks for the series,
> > Martin
>

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [Patch 0/X] HWASAN v3
  2020-01-08 11:26       ` Matthew Malcomson
  2020-01-08 19:30         ` Kostya Serebryany via gcc-patches
@ 2020-01-10 16:17         ` Kyrill Tkachov
  1 sibling, 0 replies; 150+ messages in thread
From: Kyrill Tkachov @ 2020-01-10 16:17 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: Martin Liška, nd, rguenther, kcc, law, dvyukov,
	Richard Earnshaw, Marcus Shawcroft, ian, dodji, joseph, jakub,
	Evgenii Stepanov, Peter Collingbourne


On 1/8/20 11:26 AM, Matthew Malcomson wrote:
> Hi everyone,
>
> I'm writing this email to summarise & publicise the state of this patch
> series, especially the difficulties around approval for GCC 10 mentioned
> on IRC.
>
>
> The main obstacle seems to be that no maintainer feels they have enough
> knowledge about hwasan and justification that it's worthwhile to approve
> the patch series.
>
> Similarly, Martin has given a review of the parts of the code he can
> (thanks!), but doesn't feel he can do a deep review of the code related
> to the RTL hooks and stack expansion -- hence that part is as yet not
> reviewed in-depth.
>
>
>
> The questions around justification raised on IRC are mainly that it
> seems like a proof-of-concept for MTE rather than a stand-alone useable
> sanitizer.  Especially since in the GNU world hwasan instrumented code
> is not really ready for production since we can only use the
> less-"interceptor ABI" rather than the "platform ABI".  This restriction
> is because there is no version of glibc with the required modifications
> to provide the "platform ABI".
>
> (n.b. that since https://reviews.llvm.org/D69574 the code-generation for
> these ABI's is the same).
>
>
>  From my perspective the reasons that make HWASAN useful in itself are:
>
> 1) Much less memory usage.
>
>  From a back-of-the-envelope calculation based on the hwasan paper's
> table of memory overhead from over-alignment
> https://arxiv.org/pdf/1802.09517.pdf I guess hwasan instrumented code
> has an overhead of about 1.1x (~4% from overalignment and ~6.25% from
> shadow memory), while asan seems to have an overhead somewhere in the
> range 1.5x - 3x.
>
> Maybe there's some data out there comparing total overheads that I
> haven't found? (I'd appreciate a reference if anyone has that info).
>
>
>
> 2) Available on more architectures that MTE.
>
> HWASAN only requires TBI, which is a feature of all AArch64 machines,
> while MTE will be an optional extension and only available on certain
> architectures.
>
>
> 3) This enables using hwasan in the kernel.
>
> While instrumented user-space applications will be using the
> "interceptor ABI" and hence are likely not production-quality, the
> biggest aim of implementing hwasan in GCC is to allow building the Linux
> kernel with tag-based sanitization using GCC.
>
> Instrumented kernel code uses hooks in the kernel itself, so this ABI
> distinction is no longer relevant, and this sanitizer should produce a
> production-quality kernel binary.
>
>
>
>
> I'm hoping I can find a maintainer willing to review and ACK this patch
> series -- especially with stage3 coming to a close soon.  If there's
> anything else I could do to help get someone willing up-to-speed then
> please just ask.
>

FWIW I've reviewed the aarch64 parts over the lifetime of the patch 
series and I am okay with them.

Given the reviews of the sanitiser, library and aarch64 backend 
components, and the data at

https://gcc.gnu.org/ml/gcc-patches/2020-01/msg00387.html

how can we move forward with commit approval ? Is this something a 
global reviewer can help with, Jeff ? :)

Thanks,

Kyrill



>
> Cheers,
> Matthew
>
>
>
> On 07/01/2020 15:14, Martin Liška wrote:
> > On 12/12/19 4:18 PM, Matthew Malcomson wrote:
> >
> > Hello.
> >
> > I've just sent few comments that are related to the v3 of the patch set.
> > Based on the HWASAN (limited) knowledge the patch seems reasonable 
> to me.
> > I haven't looked much at the newly introduced RTL-hooks.
> > But these seems to me isolated to the aarch64 port.
> >
> > I can also verify that the patchset works on my aarch64 linux 
> machine and
> > hwasan.exp and asan.exp tests succeed.
> >
> >> I haven't gotten ASAN_MARK to print as HWASAN_MARK when using memory
> >> tagging,
> >> since I'm not sure the way I found to implement this would be
> >> acceptable.  The
> >> inlined patch below works but it requires a special declaration
> >> instead of just
> >> an ~#include~.
> >
> > Knowing that, I would not bother with the printing of HWASAN_MARK.
> >
> > Thanks for the series,
> > Martin
>

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Document --with-build-config=bootstrap-asan option.
  2019-12-12 15:37     ` Document --with-build-config=bootstrap-asan option Matthew Malcomson
@ 2020-01-11  7:39       ` Gerald Pfeifer
  2020-01-13 10:52         ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Gerald Pfeifer @ 2020-01-11  7:39 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, nd, rguenther, kcc, Jeff Law, dvyukov, Martin Liska,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian,
	Dodji Seketeli, Joseph S. Myers, Jakub Jelinek

On Thu, 12 Dec 2019, Matthew Malcomson wrote:
> gcc/ChangeLog:
> 
> 2019-12-12  Matthew Malcomson  <matthew.malcomson@arm.com>
> 
> 	* doc/install.texi: Document bootstrap-asan configuration option.

I see this introduces a new table.

> +Some examples of build configurations designed for developers of GCC are:

@samp{bootstrap-time}, @samp{bootstrap-debug-ckovw} and others appear
to fall into the same camp, essentially expected to be used by maintainers 
only. 

Would it make sense to add your new option to the existing table, or
perhaps see which other options from the existing table to move into
your new one?  Thoughts?


The patch is okay modulo the question above.

Thanks,
Gerald

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Document --with-build-config=bootstrap-asan option.
  2020-01-11  7:39       ` Gerald Pfeifer
@ 2020-01-13 10:52         ` Matthew Malcomson
  2020-11-20 18:11           ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-01-13 10:52 UTC (permalink / raw)
  To: Gerald Pfeifer
  Cc: gcc-patches, nd, rguenther, kcc, Jeff Law, dvyukov, Martin Liska,
	Richard Earnshaw, Kyrylo Tkachov, Marcus Shawcroft, ian,
	Dodji Seketeli, Joseph S. Myers, Jakub Jelinek

On 11/01/2020 07:19, Gerald Pfeifer wrote:
> On Thu, 12 Dec 2019, Matthew Malcomson wrote:
>> gcc/ChangeLog:
>>
>> 2019-12-12  Matthew Malcomson  <matthew.malcomson@arm.com>
>>
>> 	* doc/install.texi: Document bootstrap-asan configuration option.
> 
> I see this introduces a new table.
> 
>> +Some examples of build configurations designed for developers of GCC are:
> 
> @samp{bootstrap-time}, @samp{bootstrap-debug-ckovw} and others appear
> to fall into the same camp, essentially expected to be used by maintainers
> only.
> 
> Would it make sense to add your new option to the existing table, or
> perhaps see which other options from the existing table to move into
> your new one?  Thoughts?

Sounds good me.


> 
> 
> The patch is okay modulo the question above.
> 
> Thanks,
> Gerald
> 

Patch with above suggestion.

#####################

diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index 
80b47812fe66a8ef50edf3aad9708ab3409ba7dc..0705759c69f64c6d06e91f7ae83bb8c1ad210f34 
100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2668,6 +2668,10 @@ Arranges for the run time of each program started 
by the GCC driver,
  built in any stage, to be logged to @file{time.log}, in the top level of
  the build tree.

+@item @samp{bootstrap-asan}
+Compiles GCC itself using Address Sanitization in order to catch 
invalid memory
+accesses within the GCC code.
+
  @end table

  @section Building a cross compiler


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes
  2020-01-07 15:10       ` Martin Liška
@ 2020-01-13 12:20         ` Matthew Malcomson
  0 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2020-01-13 12:20 UTC (permalink / raw)
  To: Martin Liska
  Cc: nd, kcc, dvyukov, Richard Earnshaw, Kyrylo Tkachov, dodji, jakub,
	gcc-patches

[-- Attachment #1: Type: text/plain, Size: 3414 bytes --]

> On 12/12/19 4:19 PM, Matthew Malcomson wrote:
>> - if (is_store && !param_asan_instrument_writes)
>> + if (is_store
>> + && (!param_asan_instrument_writes || !param_hwasan_instrument_writes))
>> return;
>> - if (!is_store && !param_asan_instrument_reads)
>> + if (!is_store
>> + && (!param_asan_instrument_reads || !param_hwasan_instrument_reads))
>> return;
> 
> I know it's very unlikely, but one can use -fsanitize=address and
> --param hwasan-instrument-reads=0 which will drop instrumentation of reads
> for ASAN.

Ah! Thanks for the catch.
Updated patch is attached and has been tested.

I've also attached the patch including the new `Optimization` keyword to the
hwasan parameters to this email -- (putting both on this email to avoid a bit of
email spam).

> 
> Similarly for other parameters.
> 
> Martin


Inlining the new bit that avoids the problem you pointed out above, since
the implementation of that is the only new part someone might object to.


#######################################################################

diff --git a/gcc/asan.c b/gcc/asan.c
index fe6841b4f084f75be534cc9653079ca0a5bdc94e..55723bf4d5d2a4111eb574d169f21332f6eb33ff 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -326,6 +326,25 @@ asan_sanitize_allocas_p (void)
   return (asan_sanitize_stack_p () && param_asan_protect_allocas);
 }
 
+bool
+asan_instrument_reads (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_instrument_reads);
+}
+
+bool
+asan_instrument_writes (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_instrument_writes);
+}
+
+bool
+asan_memintrin (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_memintrin);
+}
+
+
 /* Checks whether section SEC should be sanitized.  */
 
 static bool
@@ -1382,6 +1673,28 @@ hwasan_sanitize_allocas_p (void)
   return (hwasan_sanitize_stack_p () && param_hwasan_protect_allocas);
 }
 
+/* Should we instrument reads?  */
+bool
+hwasan_instrument_reads (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_reads);
+}
+
+/* Should we instrument writes?  */
+bool
+hwasan_instrument_writes (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_writes);
+}
+
+/* Should we instrument builtin calls?  */
+bool
+hwasan_memintrin (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_memintrin);
+}
+
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -2220,9 +2539,9 @@ static void
 instrument_derefs (gimple_stmt_iterator *iter, tree t,
 		   location_t location, bool is_store)
 {
-  if (is_store && !param_asan_instrument_writes)
+  if (is_store && !(asan_instrument_writes () || hwasan_instrument_writes ()))
     return;
-  if (!is_store && !param_asan_instrument_reads)
+  if (!is_store && !(asan_instrument_reads () || hwasan_instrument_reads ()))
     return;
 
   tree type, base;
@@ -2376,7 +2696,7 @@ instrument_mem_region_access (tree base, tree len,
 static bool
 instrument_builtin_call (gimple_stmt_iterator *iter)
 {
-  if (!param_asan_memintrin)
+  if (!(asan_memintrin () || hwasan_memintrin ()))
     return false;
 
   bool iter_advanced_p = false;


[-- Attachment #2: hwasan-patch3.patch --]
[-- Type: text/plain, Size: 20009 bytes --]

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 77649e839c968c38485ea30d332576b9b089effe..c5746e3c89283ef0652de85cf6b94e76263aba38 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 9fc921109caa7744ad3ccde9d930cf53cfdf0811..217db2b1f1aa98cb0e7bf7077304edf998374fe6 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -212,7 +212,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
@@ -3321,6 +3321,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 66e20becaf22469432a7f9d79ab4f4eb2706e1a6..c11f6f37317c945d9b5e9170803d6807e9501973 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -21723,6 +21723,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
@@ -22293,6 +22302,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 2b07c09def8b434227ddd4f705530403f1d4b352..eb4a4ba3a9fa5874b21f08ed337635ba6d0cb644 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -12710,13 +12710,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 0e90b31c537bbc5fe72866ebf76ab363d47f9926..64bcda42abf5f724fb4f1dbe418c870f7a810f29 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2970,6 +2970,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 58647f052339c08522f04e1c76df7dd178cb4d21..78d87af7fffe7dba45616e36f1454b6d37d76bff 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2373,6 +2373,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 effc384f3ef11a7e0e5a1e0469034b7d3ce1de7b..136229dacd78546dd3d750f1b2d16815ea47e4c4 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -707,6 +707,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)}"
@@ -981,6 +999,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
@@ -990,6 +1009,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 d5efadb4334640cab854fa8483ef29316a5a5b42..3998331aa55b856d83e8aa01eff642adfaf512ef 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1094,6 +1094,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))
@@ -1113,6 +1120,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)
@@ -1130,9 +1151,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);
 
@@ -1711,8 +1733,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),
@@ -2237,6 +2264,12 @@ 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_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 5d39244761a0af8935629a2a599dcd497be1781b..002bda7d31c9d10ab198a9213b831f21545c62c1 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-stack=
+Common Joined UInteger Var(param_hwasan_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-memintrin=
+Common Joined UInteger Var(param_hwasan_memintrin) 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 b11c7bf6d6558a2d6f335a66228bf312e7fc1b20..fa37f453889ede6d015d2b8f8970f189418a2da6 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6787,6 +6787,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 72f3064e8f8d173fac7058840ffa269e6eaff98b..68b109c5a0ac4a80a504c7cbff9b98233d0257be 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -283,4 +283,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 4819bb8058fa5920e19d6ab5f2f7cc4e66aeec11..d9f0e18a2c5fb86d4fb1682cd446cce32ac029e1 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2364,4 +2364,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 0d70f6c07a0398c3a43e7df8a840aacab16d24d8..1007e90b5d56f4783f5ef0afc805f0f4bd147894 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1826,6 +1826,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.  */


[-- Attachment #3: hwasan-patch5.patch.gz --]
[-- Type: application/gzip, Size: 14076 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [Patch 0/X] HWASAN v4
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (10 preceding siblings ...)
  2020-01-07 15:14     ` Martin Liška
@ 2020-08-17 14:12     ` Matthew Malcomson
  2020-10-16  9:03       ` Martin Liška
                         ` (9 more replies)
  2020-08-17 14:12     ` [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system Matthew Malcomson
                       ` (6 subsequent siblings)
  18 siblings, 10 replies; 150+ messages in thread
From: Matthew Malcomson @ 2020-08-17 14:12 UTC (permalink / raw)
  To: gcc-patches
  Cc: kcc, Richard Sandiford, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 5240 bytes --]

Hello,

This is v4 of the HWASAN patches which add the LLVM hardware address
sanitizer (HWASAN) to GCC.
The document describing HWASAN can be found here
http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html.

This address sanitizer only works for AArch64 at the moment.  It
requires the "top byte ignore" feature where the top byte of a pointer
does not affect dereferences.  This is checked for by a backend hook so
that if other architectures have this feature HWASAN can be used for
them.

We require a linux kernel with the relaxed ABI to allow tagged pointers
in system calls.  This is in the linux mainline, I have been testing
this feature on 5.8.0, but it has been in since at least 5.5.0.

HWASAN works by storing a tag in the top bits of every pointer and a tag in
a shadow memory region corresponding to each area of memory pointed at.
On every memory access through a pointer the tag in the pointer is
checked against the tag in shadow memory corresponding to the memory the
pointer is accessing.  If the pointer tag and memory tag do not match
then a fault is signalled.

The instrumentation required for this sanitizer has a large overlap with
the instrumentation required for implementing MTE (which has similar
functionality but checks are automatically done in the hardware and
instructions for tagging shadow memory and for managing tags are
provided by the architecture).
https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/arm-a-profile-architecture-2018-developments-armv85a

We hope to use the HWASAN framework to implement MTE for the stack, so I
have included a "dummy" patch showing how this might be done in the full
patch set attached to this cover letter.  (mte-approach.patch)



Mainly this is the same patch series as posted in December rebased onto
master, but I have also identified and fixed a few minor bugs.

  - A few bugs around the use of ptr_mode and Pmode
    (for AArch64 only observable with -mabi=ilp32).
  - A frame-extent calculation bug when every stack object in a frame is
    `large aligned` -- see the arguments that hwasan_emit_prologue
    passes to hwasan_emit_untag_frame.
  - A few changes of parameter names to match Clang.
    The names were originally chosen to match ASAN, but I found that
    most of these parameters can be mapped to a Clang configurable and
    that renaming them to match clang makes Makefiles (like
    scripts/Makefile.kasan in the linux kernel) easier to write.

Bootstrapped and regtested on x86_64 and AArch64.
Bootstrapped with `--with-build-config=bootstrap-hwasan` on AArch64 and
hwasan features tested there (all new regression failures accounted for
manually).
Built the linux kernel using this feature and ran the test_kasan.ko
testing to check the this works for the kernel.

(NOTE: Stack-tagging for the linux kernel has recently been added for
clang.  Testing GCC stack-tagging on the kernel showed two small bugs --
1) using __hwasan_tag_pointer for allocas which isn't available in the
kernel, 2) not avoiding zero tag due to the stack pointer having tag of
0xff. I'm working on fixing these but I'm sending up the patch series
as-is to get feedback earlier.  A small change to a kernel makefile, and
some headers are needed to build it with this sanitizer -- a patch with
the changes and a text file explaining how to build and test the kernel
with HWASAN is attached.
  linux-for-gcc-hwasan.patch and testing-kasan.txt )


Last time the patch set went up Martin Liska had provided a patch to
build upon that added libhwasan into the libsanitizer directory so the
patch set didn't contain anything to do that.

The "full" patchset attached to this email contains a patch that adds
this library so that anyone who wants to test this can do so, but there
is no corresponding email for the individual patch.
(introduce-libhwasan.patch)


NOTE:
  1) The target of having this sanitizer in GCC is for use on the Linux
  kernel.  The implementation should be good for the kernel, while the
  userspace story is not robust.

  The main use case of HWASAN is when it lies *underneath* the system
  libc.  In this approach the libc calls into libhwasan on important
  events (e.g. longjmp).
  This has been used very well in Android.
  At the moment there are no plans to do similar on distros with a
  modified glibc.

  A userspace story can be made by intercepting important functions.
  LLVM maintain this approach for *testing* only, and hence it is not of
  a production quality.
  Similarly we aim to use this "interception" model for testing, while
  maintaining the focus of the GCC port of use on the kernel.

  2) This sanitizer has no handling of C++ exceptions.
  If an exception is thrown the "shadow stack" is left in an
  inconsistent state and will likely eventually cause a false positive
  later on in the program.
  This is due to the fact that the handling of exceptions in LLVM relies
  on having the frame record appear after any locals(*).  This
  restriction is not satisfied by GCC due to its frame layout
  optimisation.
  
  (*) https://github.com/llvm-mirror/compiler-rt/blob/master/lib/hwasan/hwasan_exceptions.cpp#L52



Entire patch set attached to this cover letter.

[-- Attachment #2: all-patches.tar.gz --]
[-- Type: application/gzip, Size: 112098 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (11 preceding siblings ...)
  2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
@ 2020-08-17 14:12     ` Matthew Malcomson
  2020-10-13 15:57       ` Richard Sandiford
  2020-08-17 14:13     ` [PATCH 2/X] libsanitizer: Only build libhwasan when targeting AArch64 Matthew Malcomson
                       ` (5 subsequent siblings)
  18 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-08-17 14:12 UTC (permalink / raw)
  To: gcc-patches
  Cc: kcc, Richard Sandiford, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 43646 bytes --]

This patch tries to tie libhwasan into the GCC build system in the same way
that the other sanitizer runtime libraries are handled.

libsanitizer/ChangeLog:

	* Makefile.am:  Build libhwasan.
	* Makefile.in:  Build libhwasan.
	* asan/Makefile.in:  Build libhwasan.
	* configure:  Build libhwasan.
	* configure.ac:  Build libhwasan.
	* hwasan/Makefile.am: New file.
	* hwasan/Makefile.in: New file.
	* hwasan/libtool-version: New file.
	* interception/Makefile.in: Build libhwasan.
	* libbacktrace/Makefile.in: Build libhwasan.
	* libsanitizer.spec.in: Build libhwasan.
	* lsan/Makefile.in: Build libhwasan.
	* sanitizer_common/Makefile.in: Build libhwasan.
	* tsan/Makefile.in: Build libhwasan.
	* ubsan/Makefile.in: Build libhwasan.



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


diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 65ed1e712378ef453f820f86c4d3221f9dee5f2c..2a7e8e1debe838719db0f0fad218b2543cc3111b 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,11 +14,12 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan
+SUBDIRS += lsan asan ubsan hwasan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
-  include/sanitizer/tsan_interface.h
+  include/sanitizer/tsan_interface.h \
+  include/sanitizer/hwasan_interface.h
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 02c7f70ac6578a3e93a490ce8bd2c54fc0693c50..2c57d49cbffdb486645aeb5f2c0f85d6e0fad124 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -92,7 +92,8 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@am__append_1 = include/sanitizer/common_interface_defs.h \
 @SANITIZER_SUPPORTED_TRUE@	include/sanitizer/lsan_interface.h \
 @SANITIZER_SUPPORTED_TRUE@	include/sanitizer/asan_interface.h \
-@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/tsan_interface.h
+@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/tsan_interface.h \
+@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/hwasan_interface.h
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
@@ -207,7 +208,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan tsan
+	ubsan hwasan tsan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -329,6 +330,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
@@ -362,7 +364,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	$(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in
index 29622bf466a37f819c9fade30e31195adda51190..25c7fd7b7597d6e243005a1bb7de5b6243d2cfcf 100644
--- a/libsanitizer/asan/Makefile.in
+++ b/libsanitizer/asan/Makefile.in
@@ -383,6 +383,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/configure b/libsanitizer/configure
index 04eca04fbe5e59bae1ba00597de0cf1b7cf1b5fa..9ed9669a85d3cfc2f2f623e796e61a5f8f7e4ded 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -657,6 +657,7 @@ USING_MAC_INTERPOSE_TRUE
 link_liblsan
 link_libubsan
 link_libtsan
+link_libhwasan
 link_libasan
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
@@ -12361,7 +12362,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12364 "configure"
+#line 12365 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12467,7 +12468,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12470 "configure"
+#line 12471 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15943,6 +15944,10 @@ fi
 link_libasan=$link_sanitizer_common
 
 
+# Set up the set of additional libraries that we need to link against for libhwasan.
+link_libhwasan=$link_sanitizer_common
+
+
 # Set up the set of additional libraries that we need to link against for libtsan.
 link_libtsan=$link_sanitizer_common
 
@@ -16813,7 +16818,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -18094,6 +18099,7 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
 
@@ -20029,6 +20035,17 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+   sed -f vpsed$$ $ac_file > tmp$$
+   mv tmp$$ $ac_file
+   rm vpsed$$
+   echo 'MULTISUBDIR =' >> $ac_file
+   ml_norecursion=yes
+   . ${multi_basedir}/config-ml.in
+   { ml_norecursion=; unset ml_norecursion;}
+ ;;
     "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index a0950c29ebe2c0bf862ffe08f9a441c554da3220..f0d526c52967112905115c8fbe3540427a27ae03 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -120,6 +120,10 @@ AC_CHECK_LIB(dl, dlsym,
 link_libasan=$link_sanitizer_common
 AC_SUBST(link_libasan)
 
+# Set up the set of additional libraries that we need to link against for libhwasan.
+link_libhwasan=$link_sanitizer_common
+AC_SUBST(link_libhwasan)
+
 # Set up the set of additional libraries that we need to link against for libtsan.
 link_libtsan=$link_sanitizer_common
 AC_SUBST(link_libtsan)
@@ -348,7 +352,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
diff --git a/libsanitizer/hwasan/Makefile.am b/libsanitizer/hwasan/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..aaa39b4536a5c5f54910a951470814bbc8a20946
--- /dev/null
+++ b/libsanitizer/hwasan/Makefile.am
@@ -0,0 +1,88 @@
+AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
+
+# May be used by toolexeclibdir.
+gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
+
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -fno-ipa-icf
+AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+AM_CXXFLAGS += -std=gnu++14
+AM_CXXFLAGS += $(EXTRA_CXXFLAGS)
+ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
+
+toolexeclib_LTLIBRARIES = libhwasan.la
+
+hwasan_files = \
+	hwasan_allocator.cpp \
+	hwasan.cpp \
+	hwasan_dynamic_shadow.cpp \
+	hwasan_exceptions.cpp \
+	hwasan_flags.inc \
+	hwasan_interceptors.cpp \
+	hwasan_interceptors_vfork.S \
+	hwasan_linux.cpp \
+	hwasan_memintrinsics.cpp \
+	hwasan_new_delete.cpp \
+	hwasan_poisoning.cpp \
+	hwasan_report.cpp \
+	hwasan_setjmp.S \
+	hwasan_tag_mismatch_aarch64.S \
+	hwasan_thread.cpp \
+	hwasan_thread_list.cpp \
+	hwasan_type_test.cpp
+
+libhwasan_la_SOURCES = $(hwasan_files)
+libhwasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la
+if !USING_MAC_INTERPOSE
+libhwasan_la_LIBADD += $(top_builddir)/interception/libinterception.la
+endif
+if LIBBACKTRACE_SUPPORTED
+libhwasan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
+endif
+libhwasan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+
+libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan)
+
+# Work around what appears to be a GNU make bug handling MAKEFLAGS
+# values defined in terms of make variables, as is the case for CC and
+# friends when we are called from the top level Makefile.
+AM_MAKEFLAGS = \
+	"AR_FLAGS=$(AR_FLAGS)" \
+	"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
+	"CFLAGS=$(CFLAGS)" \
+	"CXXFLAGS=$(CXXFLAGS)" \
+	"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
+	"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
+	"INSTALL=$(INSTALL)" \
+	"INSTALL_DATA=$(INSTALL_DATA)" \
+	"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
+	"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
+	"JC1FLAGS=$(JC1FLAGS)" \
+	"LDFLAGS=$(LDFLAGS)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
+	"MAKE=$(MAKE)" \
+	"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
+	"PICFLAG=$(PICFLAG)" \
+	"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
+	"SHELL=$(SHELL)" \
+	"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
+	"exec_prefix=$(exec_prefix)" \
+	"infodir=$(infodir)" \
+	"libdir=$(libdir)" \
+	"prefix=$(prefix)" \
+	"includedir=$(includedir)" \
+	"AR=$(AR)" \
+	"AS=$(AS)" \
+	"LD=$(LD)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"NM=$(NM)" \
+	"PICFLAG=$(PICFLAG)" \
+	"RANLIB=$(RANLIB)" \
+	"DESTDIR=$(DESTDIR)"
+
+MAKEOVERRIDES=
+
+## ################################################################
+
+
diff --git a/libsanitizer/hwasan/Makefile.in b/libsanitizer/hwasan/Makefile.in
new file mode 100644
index 0000000000000000000000000000000000000000..b705a11a7a19272c2ecb33c0ce9651d37002d439
--- /dev/null
+++ b/libsanitizer/hwasan/Makefile.in
@@ -0,0 +1,801 @@
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+@USING_MAC_INTERPOSE_FALSE@am__append_1 = $(top_builddir)/interception/libinterception.la
+@LIBBACKTRACE_SUPPORTED_TRUE@am__append_2 = $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
+subdir = hwasan
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
+	$(top_srcdir)/../config/depstand.m4 \
+	$(top_srcdir)/../config/lead-dot.m4 \
+	$(top_srcdir)/../config/libstdc++-raw-cxx.m4 \
+	$(top_srcdir)/../config/multi.m4 \
+	$(top_srcdir)/../config/override.m4 \
+	$(top_srcdir)/../config/stdint.m4 \
+	$(top_srcdir)/../config/toolexeclibdir.m4 \
+	$(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \
+	$(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \
+	$(top_srcdir)/acinclude.m4 $(top_srcdir)/../libtool.m4 \
+	$(top_srcdir)/../config/enable.m4 \
+	$(top_srcdir)/../config/cet.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am
+mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(toolexeclibdir)"
+LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libhwasan_la_DEPENDENCIES =  \
+	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
+	$(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1)
+am__objects_1 = hwasan_allocator.lo hwasan.lo hwasan_dynamic_shadow.lo \
+	hwasan_exceptions.lo hwasan_interceptors.lo \
+	hwasan_interceptors_vfork.lo hwasan_linux.lo \
+	hwasan_memintrinsics.lo hwasan_new_delete.lo \
+	hwasan_poisoning.lo hwasan_report.lo hwasan_setjmp.lo \
+	hwasan_tag_mismatch_aarch64.lo hwasan_thread.lo \
+	hwasan_thread_list.lo hwasan_type_test.lo
+am_libhwasan_la_OBJECTS = $(am__objects_1)
+libhwasan_la_OBJECTS = $(am_libhwasan_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libhwasan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+	$(CXXFLAGS) $(libhwasan_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/../depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS)
+LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CCAS) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CCASFLAGS) $(CCASFLAGS)
+AM_V_CPPAS = $(am__v_CPPAS_@AM_V@)
+am__v_CPPAS_ = $(am__v_CPPAS_@AM_DEFAULT_V@)
+am__v_CPPAS_0 = @echo "  CPPAS   " $@;
+am__v_CPPAS_1 = 
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo "  CXX     " $@;
+am__v_CXX_1 = 
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+	$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo "  CXXLD   " $@;
+am__v_CXXLD_1 = 
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libhwasan_la_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+ACLOCAL = @ACLOCAL@
+ALLOC_FILE = @ALLOC_FILE@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BACKTRACE_SUPPORTED = @BACKTRACE_SUPPORTED@
+BACKTRACE_SUPPORTS_THREADS = @BACKTRACE_SUPPORTS_THREADS@
+BACKTRACE_USES_MALLOC = @BACKTRACE_USES_MALLOC@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXTRA_ASFLAGS = @EXTRA_ASFLAGS@
+EXTRA_CFLAGS = @EXTRA_CFLAGS@
+EXTRA_CXXFLAGS = @EXTRA_CXXFLAGS@
+FGREP = @FGREP@
+FORMAT_FILE = @FORMAT_FILE@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSTDCXX_RAW_CXX_CXXFLAGS = @LIBSTDCXX_RAW_CXX_CXXFLAGS@
+LIBSTDCXX_RAW_CXX_LDFLAGS = @LIBSTDCXX_RAW_CXX_LDFLAGS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
+SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS = @SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
+VERSION = @VERSION@
+VIEW_FILE = @VIEW_FILE@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+enable_shared = @enable_shared@
+enable_static = @enable_static@
+exec_prefix = @exec_prefix@
+get_gcc_base_ver = @get_gcc_base_ver@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
+link_liblsan = @link_liblsan@
+link_libtsan = @link_libtsan@
+link_libubsan = @link_libubsan@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+multi_basedir = @multi_basedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_noncanonical = @target_noncanonical@
+target_os = @target_os@
+target_vendor = @target_vendor@
+toolexecdir = @toolexecdir@
+toolexeclibdir = @toolexeclibdir@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
+
+# May be used by toolexeclibdir.
+gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
+	-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
+	-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
+	-Wno-variadic-macros -fno-ipa-icf \
+	$(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=gnu++14 $(EXTRA_CXXFLAGS)
+ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
+toolexeclib_LTLIBRARIES = libhwasan.la
+hwasan_files = \
+	hwasan_allocator.cpp \
+	hwasan.cpp \
+	hwasan_dynamic_shadow.cpp \
+	hwasan_exceptions.cpp \
+	hwasan_flags.inc \
+	hwasan_interceptors.cpp \
+	hwasan_interceptors_vfork.S \
+	hwasan_linux.cpp \
+	hwasan_memintrinsics.cpp \
+	hwasan_new_delete.cpp \
+	hwasan_poisoning.cpp \
+	hwasan_report.cpp \
+	hwasan_setjmp.S \
+	hwasan_tag_mismatch_aarch64.S \
+	hwasan_thread.cpp \
+	hwasan_thread_list.cpp \
+	hwasan_type_test.cpp
+
+libhwasan_la_SOURCES = $(hwasan_files)
+libhwasan_la_LIBADD =  \
+	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
+	$(am__append_1) $(am__append_2) $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan)
+
+# Work around what appears to be a GNU make bug handling MAKEFLAGS
+# values defined in terms of make variables, as is the case for CC and
+# friends when we are called from the top level Makefile.
+AM_MAKEFLAGS = \
+	"AR_FLAGS=$(AR_FLAGS)" \
+	"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
+	"CFLAGS=$(CFLAGS)" \
+	"CXXFLAGS=$(CXXFLAGS)" \
+	"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
+	"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
+	"INSTALL=$(INSTALL)" \
+	"INSTALL_DATA=$(INSTALL_DATA)" \
+	"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
+	"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
+	"JC1FLAGS=$(JC1FLAGS)" \
+	"LDFLAGS=$(LDFLAGS)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
+	"MAKE=$(MAKE)" \
+	"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
+	"PICFLAG=$(PICFLAG)" \
+	"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
+	"SHELL=$(SHELL)" \
+	"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
+	"exec_prefix=$(exec_prefix)" \
+	"infodir=$(infodir)" \
+	"libdir=$(libdir)" \
+	"prefix=$(prefix)" \
+	"includedir=$(includedir)" \
+	"AR=$(AR)" \
+	"AS=$(AS)" \
+	"LD=$(LD)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"NM=$(NM)" \
+	"PICFLAG=$(PICFLAG)" \
+	"RANLIB=$(RANLIB)" \
+	"DESTDIR=$(DESTDIR)"
+
+MAKEOVERRIDES = 
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .S .cpp .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign hwasan/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign hwasan/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-toolexeclibLTLIBRARIES: $(toolexeclib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(toolexeclibdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(toolexeclibdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(toolexeclibdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(toolexeclibdir)"; \
+	}
+
+uninstall-toolexeclibLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(toolexeclibdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(toolexeclibdir)/$$f"; \
+	done
+
+clean-toolexeclibLTLIBRARIES:
+	-test -z "$(toolexeclib_LTLIBRARIES)" || rm -f $(toolexeclib_LTLIBRARIES)
+	@list='$(toolexeclib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libhwasan.la: $(libhwasan_la_OBJECTS) $(libhwasan_la_DEPENDENCIES) $(EXTRA_libhwasan_la_DEPENDENCIES) 
+	$(AM_V_CXXLD)$(libhwasan_la_LINK) -rpath $(toolexeclibdir) $(libhwasan_la_OBJECTS) $(libhwasan_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_allocator.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_dynamic_shadow.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_exceptions.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors_vfork.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_linux.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_memintrinsics.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_new_delete.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_poisoning.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_report.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_setjmp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_tag_mismatch_aarch64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread_list.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_type_test.Plo@am__quote@
+
+.S.o:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ $<
+
+.S.obj:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.S.lo:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(LTCPPASCOMPILE) -c -o $@ $<
+
+.cpp.o:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+	for dir in "$(DESTDIR)$(toolexeclibdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-toolexeclibLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-toolexeclibLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-toolexeclibLTLIBRARIES cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags dvi dvi-am html html-am info \
+	info-am install install-am install-data install-data-am \
+	install-dvi install-dvi-am install-exec install-exec-am \
+	install-html install-html-am install-info install-info-am \
+	install-man install-pdf install-pdf-am install-ps \
+	install-ps-am install-strip install-toolexeclibLTLIBRARIES \
+	installcheck installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am \
+	uninstall-toolexeclibLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libsanitizer/hwasan/libtool-version b/libsanitizer/hwasan/libtool-version
new file mode 100644
index 0000000000000000000000000000000000000000..d1f9aa1b950cc95efea87b7a70e96e44df0f9c17
--- /dev/null
+++ b/libsanitizer/hwasan/libtool-version
@@ -0,0 +1,6 @@
+# This file is used to maintain libtool version info for libhwasan.  See
+# the libtool manual to understand the meaning of the fields.  This is
+# a separate file so that version updates don't involve re-running
+# automake.
+# CURRENT:REVISION:AGE
+0:0:0
diff --git a/libsanitizer/interception/Makefile.in b/libsanitizer/interception/Makefile.in
index a20f52ea4b825e733b2c0ca24780e766ad69367d..4a872cb49690e7b739ad07d73a517849be2ba1ea 100644
--- a/libsanitizer/interception/Makefile.in
+++ b/libsanitizer/interception/Makefile.in
@@ -301,6 +301,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/libbacktrace/Makefile.in b/libsanitizer/libbacktrace/Makefile.in
index c843bcee0e4d39d788470d2d03b53d2f817aa231..3f05cdf3d621be74ee6346852c80b71f885e1203 100644
--- a/libsanitizer/libbacktrace/Makefile.in
+++ b/libsanitizer/libbacktrace/Makefile.in
@@ -351,6 +351,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/libsanitizer.spec.in b/libsanitizer/libsanitizer.spec.in
index a4fa87d8dfc51549b0f932b31a610db1a2e86c29..70a33574d7b1d494c4875cfc41f29606bde2172e 100644
--- a/libsanitizer/libsanitizer.spec.in
+++ b/libsanitizer/libsanitizer.spec.in
@@ -3,6 +3,8 @@
 
 *link_libasan: @link_libasan@
 
+*link_libhwasan: @link_libhwasan@
+
 *link_libtsan: @link_libtsan@
 
 *link_libubsan: @link_libubsan@
diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in
index 23a6fa69ec6941ec8a3f02f6a283b8015a8f706f..fbe87cd24961f0ca2d338b43c9d81af709a02802 100644
--- a/libsanitizer/lsan/Makefile.in
+++ b/libsanitizer/lsan/Makefile.in
@@ -346,6 +346,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in
index 299de50b34a6c1cbb33d6f3536d8df1a132d2f59..7e5555cc9df53c5b081313d22ffc532adbe967a5 100644
--- a/libsanitizer/sanitizer_common/Makefile.in
+++ b/libsanitizer/sanitizer_common/Makefile.in
@@ -338,6 +338,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in
index bfbdfe0d89c635c7ca1fe246722ec04949002f1b..ce11d2497b75a306f80f1701110b5eefc5330318 100644
--- a/libsanitizer/tsan/Makefile.in
+++ b/libsanitizer/tsan/Makefile.in
@@ -375,6 +375,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/ubsan/Makefile.in b/libsanitizer/ubsan/Makefile.in
index 312d6a5e5137ab64faef9a3f0f44ae3284c23e1b..2775c1422137d46d0d68a0a516826e6d62daf137 100644
--- a/libsanitizer/ubsan/Makefile.in
+++ b/libsanitizer/ubsan/Makefile.in
@@ -340,6 +340,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@


[-- Attachment #2: hwasan-diff0.patch.gz --]
[-- Type: application/gzip, Size: 10604 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 2/X] libsanitizer:  Only build libhwasan when targeting AArch64
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (12 preceding siblings ...)
  2020-08-17 14:12     ` [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system Matthew Malcomson
@ 2020-08-17 14:13     ` Matthew Malcomson
  2020-10-13 16:04       ` Richard Sandiford
  2020-08-17 14:13     ` [PATCH 3/X] libsanitizer: Add option to bootstrap using HWASAN Matthew Malcomson
                       ` (4 subsequent siblings)
  18 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-08-17 14:13 UTC (permalink / raw)
  To: gcc-patches
  Cc: kcc, Richard Sandiford, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 9423 bytes --]

Though the library has limited support for x86, we don't have any
support for generating code targeting x86 so there is no point building
for that target.

libsanitizer/ChangeLog:

	* Makefile.am: Condition building hwasan directory.
	* Makefile.in: Regenerate.
	* configure: Regenerate.
	* configure.ac: Set HWASAN_SUPPORTED based on target
	architecture.
	* configure.tgt: Likewise.



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


diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 2a7e8e1debe838719db0f0fad218b2543cc3111b..065a65e78d49f7689a01ecb64db1f07ca83aa987 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,7 +14,7 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan hwasan
+SUBDIRS += lsan asan ubsan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
@@ -23,6 +23,9 @@ nodist_saninclude_HEADERS += \
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
+if HWASAN_SUPPORTED
+SUBDIRS += hwasan
+endif
 endif
 
 ## May be used by toolexeclibdir.
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 2c57d49cbffdb486645aeb5f2c0f85d6e0fad124..3873ea4d7050f04a3f7bbd0dd3f2a71e9b65d287 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -97,6 +97,7 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
+@HWASAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_5 = hwasan
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
@@ -208,7 +209,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan hwasan tsan
+	ubsan tsan hwasan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -364,7 +365,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	$(am__append_4) $(am__append_5)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/configure b/libsanitizer/configure
index 9ed9669a85d3cfc2f2f623e796e61a5f8f7e4ded..cc5c229f4aebcdd454e9e2e415a8e16046dc1b1a 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -659,6 +659,8 @@ link_libubsan
 link_libtsan
 link_libhwasan
 link_libasan
+HWASAN_SUPPORTED_FALSE
+HWASAN_SUPPORTED_TRUE
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
 TSAN_SUPPORTED_FALSE
@@ -12362,7 +12364,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12365 "configure"
+#line 12367 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12468,7 +12470,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12471 "configure"
+#line 12473 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15819,6 +15821,7 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
  if test "x$TSAN_SUPPORTED" = "xyes"; then
   TSAN_SUPPORTED_TRUE=
@@ -15836,6 +15839,14 @@ else
   LSAN_SUPPORTED_FALSE=
 fi
 
+ if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  HWASAN_SUPPORTED_TRUE=
+  HWASAN_SUPPORTED_FALSE='#'
+else
+  HWASAN_SUPPORTED_TRUE='#'
+  HWASAN_SUPPORTED_FALSE=
+fi
+
 
 # Check for functions needed.
 for ac_func in clock_getres clock_gettime clock_settime lstat readlink
@@ -16818,7 +16829,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -16826,6 +16837,11 @@ if test "x$TSAN_SUPPORTED" = "xyes"; then
 
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  ac_config_files="$ac_config_files hwasan/Makefile"
+
+fi
+
 
 
 
@@ -17090,6 +17106,10 @@ if test -z "${LSAN_SUPPORTED_TRUE}" && test -z "${LSAN_SUPPORTED_FALSE}"; then
   as_fn_error $? "conditional \"LSAN_SUPPORTED\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HWASAN_SUPPORTED_TRUE}" && test -z "${HWASAN_SUPPORTED_FALSE}"; then
+  as_fn_error $? "conditional \"HWASAN_SUPPORTED\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${USING_MAC_INTERPOSE_TRUE}" && test -z "${USING_MAC_INTERPOSE_FALSE}"; then
   as_fn_error $? "conditional \"USING_MAC_INTERPOSE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -18099,9 +18119,9 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
-    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
@@ -20035,7 +20055,7 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
    sed -f vpsed$$ $ac_file > tmp$$
@@ -20046,18 +20066,18 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
-   sed -f vpsed$$ $ac_file > tmp$$
-   mv tmp$$ $ac_file
-   rm vpsed$$
-   echo 'MULTISUBDIR =' >> $ac_file
-   ml_norecursion=yes
-   . ${multi_basedir}/config-ml.in
-   { ml_norecursion=; unset ml_norecursion;}
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
     sed -f vpsed$$ $ac_file > tmp$$
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index f0d526c52967112905115c8fbe3540427a27ae03..13cd302030db7d0107ac8de0863d42558dea5c71 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -96,9 +96,11 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
 AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
 AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"])
+AM_CONDITIONAL(HWASAN_SUPPORTED, [test "x$HWASAN_SUPPORTED" = "xyes"])
 
 # Check for functions needed.
 AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime lstat readlink)
@@ -352,7 +354,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
@@ -380,6 +382,21 @@ _EOF
 ])
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  AC_CONFIG_FILES(AC_FOREACH([DIR], [hwasan], [DIR/Makefile ]), 
+    [cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    AS_UNSET([ml_norecursion])
+])
+fi
+
 AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS])
 AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS])
 
diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt
index 52503f1a880ba08b515b8a429ac44a262873f74b..fb55ae9762e9ac6531087a258e1291b5635fcd3e 100644
--- a/libsanitizer/configure.tgt
+++ b/libsanitizer/configure.tgt
@@ -61,6 +61,7 @@ case "${target}" in
 		LSAN_SUPPORTED=yes
 		TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
 	fi
+	HWASAN_SUPPORTED=yes
 	;;
   x86_64-*-darwin1[2-9]* | i?86-*-darwin1[2-9]*)
 	TSAN_SUPPORTED=no


[-- Attachment #2: hwasan-diff1.patch --]
[-- Type: text/plain, Size: 8952 bytes --]

diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 2a7e8e1debe838719db0f0fad218b2543cc3111b..065a65e78d49f7689a01ecb64db1f07ca83aa987 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,7 +14,7 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan hwasan
+SUBDIRS += lsan asan ubsan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
@@ -23,6 +23,9 @@ nodist_saninclude_HEADERS += \
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
+if HWASAN_SUPPORTED
+SUBDIRS += hwasan
+endif
 endif
 
 ## May be used by toolexeclibdir.
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 2c57d49cbffdb486645aeb5f2c0f85d6e0fad124..3873ea4d7050f04a3f7bbd0dd3f2a71e9b65d287 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -97,6 +97,7 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
+@HWASAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_5 = hwasan
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
@@ -208,7 +209,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan hwasan tsan
+	ubsan tsan hwasan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -364,7 +365,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	$(am__append_4) $(am__append_5)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/configure b/libsanitizer/configure
index 9ed9669a85d3cfc2f2f623e796e61a5f8f7e4ded..cc5c229f4aebcdd454e9e2e415a8e16046dc1b1a 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -659,6 +659,8 @@ link_libubsan
 link_libtsan
 link_libhwasan
 link_libasan
+HWASAN_SUPPORTED_FALSE
+HWASAN_SUPPORTED_TRUE
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
 TSAN_SUPPORTED_FALSE
@@ -12362,7 +12364,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12365 "configure"
+#line 12367 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12468,7 +12470,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12471 "configure"
+#line 12473 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15819,6 +15821,7 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
  if test "x$TSAN_SUPPORTED" = "xyes"; then
   TSAN_SUPPORTED_TRUE=
@@ -15836,6 +15839,14 @@ else
   LSAN_SUPPORTED_FALSE=
 fi
 
+ if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  HWASAN_SUPPORTED_TRUE=
+  HWASAN_SUPPORTED_FALSE='#'
+else
+  HWASAN_SUPPORTED_TRUE='#'
+  HWASAN_SUPPORTED_FALSE=
+fi
+
 
 # Check for functions needed.
 for ac_func in clock_getres clock_gettime clock_settime lstat readlink
@@ -16818,7 +16829,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -16826,6 +16837,11 @@ if test "x$TSAN_SUPPORTED" = "xyes"; then
 
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  ac_config_files="$ac_config_files hwasan/Makefile"
+
+fi
+
 
 
 
@@ -17090,6 +17106,10 @@ if test -z "${LSAN_SUPPORTED_TRUE}" && test -z "${LSAN_SUPPORTED_FALSE}"; then
   as_fn_error $? "conditional \"LSAN_SUPPORTED\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HWASAN_SUPPORTED_TRUE}" && test -z "${HWASAN_SUPPORTED_FALSE}"; then
+  as_fn_error $? "conditional \"HWASAN_SUPPORTED\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${USING_MAC_INTERPOSE_TRUE}" && test -z "${USING_MAC_INTERPOSE_FALSE}"; then
   as_fn_error $? "conditional \"USING_MAC_INTERPOSE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -18099,9 +18119,9 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
-    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
@@ -20035,7 +20055,7 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
    sed -f vpsed$$ $ac_file > tmp$$
@@ -20046,18 +20066,18 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
-   sed -f vpsed$$ $ac_file > tmp$$
-   mv tmp$$ $ac_file
-   rm vpsed$$
-   echo 'MULTISUBDIR =' >> $ac_file
-   ml_norecursion=yes
-   . ${multi_basedir}/config-ml.in
-   { ml_norecursion=; unset ml_norecursion;}
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
     sed -f vpsed$$ $ac_file > tmp$$
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index f0d526c52967112905115c8fbe3540427a27ae03..13cd302030db7d0107ac8de0863d42558dea5c71 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -96,9 +96,11 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
 AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
 AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"])
+AM_CONDITIONAL(HWASAN_SUPPORTED, [test "x$HWASAN_SUPPORTED" = "xyes"])
 
 # Check for functions needed.
 AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime lstat readlink)
@@ -352,7 +354,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
@@ -380,6 +382,21 @@ _EOF
 ])
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  AC_CONFIG_FILES(AC_FOREACH([DIR], [hwasan], [DIR/Makefile ]), 
+    [cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    AS_UNSET([ml_norecursion])
+])
+fi
+
 AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS])
 AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS])
 
diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt
index 52503f1a880ba08b515b8a429ac44a262873f74b..fb55ae9762e9ac6531087a258e1291b5635fcd3e 100644
--- a/libsanitizer/configure.tgt
+++ b/libsanitizer/configure.tgt
@@ -61,6 +61,7 @@ case "${target}" in
 		LSAN_SUPPORTED=yes
 		TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
 	fi
+	HWASAN_SUPPORTED=yes
 	;;
   x86_64-*-darwin1[2-9]* | i?86-*-darwin1[2-9]*)
 	TSAN_SUPPORTED=no


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 3/X] libsanitizer:  Add option to bootstrap using HWASAN
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (13 preceding siblings ...)
  2020-08-17 14:13     ` [PATCH 2/X] libsanitizer: Only build libhwasan when targeting AArch64 Matthew Malcomson
@ 2020-08-17 14:13     ` Matthew Malcomson
  2020-10-14  7:52       ` Richard Sandiford
  2020-08-17 14:13     ` [PATCH 4/X] libsanitizer: options: Add hwasan flags and argument parsing Matthew Malcomson
                       ` (3 subsequent siblings)
  18 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-08-17 14:13 UTC (permalink / raw)
  To: gcc-patches
  Cc: kcc, Richard Sandiford, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 5499 bytes --]

This is an analogous option to --bootstrap-asan to configure.  It allows
bootstrapping GCC using HWASAN.

For the same reasons as for ASAN we have to avoid using the HWASAN
sanitizer when compiling libiberty and the lto-plugin.

Also add a function to query whether -fsanitize=hwaddress has been
passed.

ChangeLog:

	* configure: Regenerate.
	* configure.ac: Add --bootstrap-hwasan option.

config/ChangeLog:

	* bootstrap-hwasan.mk: New file.

gcc/ChangeLog:

	* doc/install.texi: Document new option.

libiberty/ChangeLog:

	* configure: Regenerate.
	* configure.ac: Avoid using sanitizer.

lto-plugin/ChangeLog:

	* Makefile.am: Avoid using sanitizer.
	* Makefile.in: Regenerate.



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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index a0c5aca9e8d5cae2782c8fe4625a501853dc226a..203319e3f899e8d24429950c3a5d22927fb5150f 100755
--- a/configure
+++ b/configure
@@ -8297,7 +8297,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index 1a53ed418e4d97606356b14a17b50186c79adcd3..9d5c187c31bfc01003e75058896b686807e47643 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2809,7 +2809,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index d581a34653f61a440b3c3b832836fe109e2fbd08..25d041fcbb1f7c16f7ac47b7b5d4ea8308c6f69c 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2767,6 +2767,11 @@ the build tree.
 Compiles GCC itself using Address Sanitization in order to catch invalid memory
 accesses within the GCC code.
 
+@item @samp{bootstrap-hwasan}
+Compiles GCC itself using HWAddress Sanitization in order to catch invalid
+memory accesses within the GCC code.  This option is only available on AArch64
+targets running a Linux kernel that supports the required ABI (5.4 or later).
+
 @end table
 
 @section Building a cross compiler
diff --git a/libiberty/configure b/libiberty/configure
index 1f8e23f0d235a6a5d5158bf6705023db95ac7023..59e0b73d5838bbd42a5548759084471e97ec254f 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5264,6 +5264,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index 4e2599c14a89bafcb8c7e523b9ce5b3d60b8c0f6..ad952963971a31968b5d109661b9cab0aa4b95fc 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index ba5882df7a7272f65219191c82ecd78ab4d3725e..50d6e09dac881d28d4ff70def47b09ed8c0ea66c 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@ $(CET_HOST_FLAGS)
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 7da7cd26dbff9c21e0757926162d5615ef48479c..87d55aff33167e2788a957941e5e09fa91f2899f 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -675,8 +675,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


[-- Attachment #2: hwasan-diff2.patch --]
[-- Type: text/plain, Size: 4729 bytes --]

diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index a0c5aca9e8d5cae2782c8fe4625a501853dc226a..203319e3f899e8d24429950c3a5d22927fb5150f 100755
--- a/configure
+++ b/configure
@@ -8297,7 +8297,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index 1a53ed418e4d97606356b14a17b50186c79adcd3..9d5c187c31bfc01003e75058896b686807e47643 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2809,7 +2809,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index d581a34653f61a440b3c3b832836fe109e2fbd08..25d041fcbb1f7c16f7ac47b7b5d4ea8308c6f69c 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2767,6 +2767,11 @@ the build tree.
 Compiles GCC itself using Address Sanitization in order to catch invalid memory
 accesses within the GCC code.
 
+@item @samp{bootstrap-hwasan}
+Compiles GCC itself using HWAddress Sanitization in order to catch invalid
+memory accesses within the GCC code.  This option is only available on AArch64
+targets running a Linux kernel that supports the required ABI (5.4 or later).
+
 @end table
 
 @section Building a cross compiler
diff --git a/libiberty/configure b/libiberty/configure
index 1f8e23f0d235a6a5d5158bf6705023db95ac7023..59e0b73d5838bbd42a5548759084471e97ec254f 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5264,6 +5264,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index 4e2599c14a89bafcb8c7e523b9ce5b3d60b8c0f6..ad952963971a31968b5d109661b9cab0aa4b95fc 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index ba5882df7a7272f65219191c82ecd78ab4d3725e..50d6e09dac881d28d4ff70def47b09ed8c0ea66c 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@ $(CET_HOST_FLAGS)
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 7da7cd26dbff9c21e0757926162d5615ef48479c..87d55aff33167e2788a957941e5e09fa91f2899f 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -675,8 +675,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 4/X] libsanitizer: options:  Add hwasan flags and argument parsing
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (14 preceding siblings ...)
  2020-08-17 14:13     ` [PATCH 3/X] libsanitizer: Add option to bootstrap using HWASAN Matthew Malcomson
@ 2020-08-17 14:13     ` Matthew Malcomson
  2020-10-14  9:42       ` Richard Sandiford
  2020-08-17 14:13     ` [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN Matthew Malcomson
                       ` (2 subsequent siblings)
  18 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-08-17 14:13 UTC (permalink / raw)
  To: gcc-patches
  Cc: kcc, Richard Sandiford, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 22692 bytes --]

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.  */


[-- Attachment #2: hwasan-diff3.patch --]
[-- Type: text/plain, Size: 20130 bytes --]

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.  */


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (15 preceding siblings ...)
  2020-08-17 14:13     ` [PATCH 4/X] libsanitizer: options: Add hwasan flags and argument parsing Matthew Malcomson
@ 2020-08-17 14:13     ` Matthew Malcomson
  2020-10-14 16:09       ` Richard Sandiford
  2020-08-17 14:13     ` [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes Matthew Malcomson
  2020-08-17 14:13     ` [PATCH 7/X] libsanitizer: Add tests Matthew Malcomson
  18 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-08-17 14:13 UTC (permalink / raw)
  To: gcc-patches
  Cc: kcc, Richard Sandiford, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 45776 bytes --]

Handling stack variables has three features.

1) Ensure HWASAN required alignment for stack variables

When tagging shadow memory, we need to ensure that each tag granule is
only used by one variable at a time.

This is done by ensuring that each tagged variable is aligned to the tag
granule representation size and also ensure that the end of each
object is aligned to ensure the start of any other data stored on the
stack is in a different granule.

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

2) Put tags into each stack variable pointer

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

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

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

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

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

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

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

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

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

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

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

config/ChangeLog:

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

gcc/ChangeLog:

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



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


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


[-- Attachment #2: hwasan-diff4.patch --]
[-- Type: text/plain, Size: 39915 bytes --]

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


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 6/X] libsanitizer:  Add hwasan pass and associated gimple changes
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (16 preceding siblings ...)
  2020-08-17 14:13     ` [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN Matthew Malcomson
@ 2020-08-17 14:13     ` Matthew Malcomson
  2020-10-14 18:37       ` Richard Sandiford
  2020-08-17 14:13     ` [PATCH 7/X] libsanitizer: Add tests Matthew Malcomson
  18 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-08-17 14:13 UTC (permalink / raw)
  To: gcc-patches
  Cc: kcc, Richard Sandiford, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 55268 bytes --]

There are four main features to this change:

1) Check pointer tags match address tags.

In the new `hwasan` pass we put HWASAN_CHECK internal functions before
all memory accesses to check that tags in the pointer being used match
the tag stored in shadow memory for the memory region being used.

These internal functions are expanded into actual checks in the sanopt
pass that happens just before expansion into RTL.

We use the same mechanism that currently inserts ASAN_CHECK internal
functions to insert the new HWASAN_CHECK functions.

2) Instrument known builtin function calls.

Handle all builtin functions that we know use memory accesses.
This commit uses the machinery added for ASAN to identify builtin
functions that access memory.

The main differences between the approaches for HWASAN and ASAN are:
 - libhwasan intercepts much less builtin functions.
 - Alloca needs to be transformed differently (instead of adding
   redzones it needs to tag shadow memory and return a tagged pointer).
 - stack_restore needs to untag the shadow stack between the current
   position and where it's going.
 - `noreturn` functions can not be handled by simply unpoisoning the
   entire shadow stack -- there is no "always valid" tag.
   (exceptions and things such as longjmp need to be handled in a
   different way).

For hardware implemented checking (such as AArch64's memory tagging
extension) alloca and stack_restore will need to be handled by hooks in
the backend rather than transformation at the gimple level.  This will
allow architecture specific handling of such stack modifications.

3) Introduce HWASAN block-scope poisoning

Here we use exactly the same mechanism as ASAN_MARK to poison/unpoison
variables on entry/exit of a block.

In order to simply use the exact same machinery we're using the same
internal functions until the SANOPT pass.  This means that all handling
of ASAN_MARK is the same.
This has the negative that the naming may be a little confusing, but a
positive that handling of the internal function doesn't have to be
duplicated for a function that behaves exactly the same but has a
different name.

gcc/ChangeLog:

	* asan.c (asan_instrument_reads): New.
	(asan_instrument_writes): New.
	(asan_memintrin): New.
	(handle_builtin_stack_restore): Account for HWASAN.
	(handle_builtin_alloca): Account for HWASAN.
	(get_mem_refs_of_builtin_call): Special case strlen for HWASAN.
	(report_error_func): Assert not HWASAN.
	(build_check_stmt): Make HWASAN_CHECK instead of ASAN_CHECK.
	(instrument_derefs): HWASAN does not tag globals.
	(instrument_builtin_call): Use new helper functions.
	(maybe_instrument_call): Don't instrument `noreturn` functions.
	(initialize_sanitizer_builtins): Add new type.
	(asan_expand_mark_ifn): Account for HWASAN.
	(asan_expand_check_ifn): Assert never called by HWASAN.
	(asan_expand_poison_ifn): Account for HWASAN.
	(hwasan_instrument_reads): New.
	(hwasan_instrument_writes): New.
	(hwasan_memintrin): New.
	(hwasan_instrument): New.
	(hwasan_base): New.
	(hwasan_check_func): New.
	(hwasan_expand_check_ifn): New.
	(hwasan_expand_mark_ifn): New.
	(gate_hwasan): New.
	(class pass_hwasan): New.
	(make_pass_hwasan): New.
	(class pass_hwasan_O0): New.
	(make_pass_hwasan_O0): New.
	* asan.h (hwasan_base): New decl.
	(hwasan_expand_check_ifn): New decl.
	(hwasan_expand_mark_ifn): New decl.
	(gate_hwasan): New decl.
	(enum hwasan_mark_flags): New.
	(asan_intercepted_p): Always false for hwasan.
	(asan_sanitize_use_after_scope): Account for HWASAN.
	* builtin-types.def (BT_FN_PTR_CONST_PTR_UINT8): New.
	* gimple-pretty-print.c (dump_gimple_call_args): Account for
	HWASAN.
	* gimplify.c (asan_poison_variable): Account for HWASAN.
	(gimplify_function_tree): Remove requirement of
	SANITIZE_ADDRESS, requiring asan or hwasan is accounted for in
	`asan_sanitize_use_after_scope`.
	* internal-fn.c (expand_HWASAN_CHECK): New.
	(expand_HWASAN_CHOOSE_TAG): New.
	(expand_HWASAN_MARK): New.
	(expand_HWASAN_ALLOCA_UNPOISON): New.
	* internal-fn.def (HWASAN_CHOOSE_TAG): New.
	(HWASAN_CHECK): New.
	(HWASAN_MARK): New.
	(HWASAN_ALLOCA_UNPOISON): New.
	* passes.def: Add hwasan and hwasan_O0 passes.
	* sanitizer.def (BUILT_IN_HWASAN_LOAD1): New.
	(BUILT_IN_HWASAN_LOAD2): New.
	(BUILT_IN_HWASAN_LOAD4): New.
	(BUILT_IN_HWASAN_LOAD8): New.
	(BUILT_IN_HWASAN_LOAD16): New.
	(BUILT_IN_HWASAN_LOADN): New.
	(BUILT_IN_HWASAN_STORE1): New.
	(BUILT_IN_HWASAN_STORE2): New.
	(BUILT_IN_HWASAN_STORE4): New.
	(BUILT_IN_HWASAN_STORE8): New.
	(BUILT_IN_HWASAN_STORE16): New.
	(BUILT_IN_HWASAN_STOREN): New.
	(BUILT_IN_HWASAN_LOAD1_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD2_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD4_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD8_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD16_NOABORT): New.
	(BUILT_IN_HWASAN_LOADN_NOABORT): New.
	(BUILT_IN_HWASAN_STORE1_NOABORT): New.
	(BUILT_IN_HWASAN_STORE2_NOABORT): New.
	(BUILT_IN_HWASAN_STORE4_NOABORT): New.
	(BUILT_IN_HWASAN_STORE8_NOABORT): New.
	(BUILT_IN_HWASAN_STORE16_NOABORT): New.
	(BUILT_IN_HWASAN_STOREN_NOABORT): New.
	(BUILT_IN_HWASAN_TAG_MISMATCH4): New.
	(BUILT_IN_HWASAN_HANDLE_LONGJMP): New.
	(BUILT_IN_HWASAN_TAG_PTR): New.
	* sanopt.c (sanopt_optimize_walker): Act during hwasan.
	(pass_sanopt::execute): Act during hwasan.
	* toplev.c (compile_file): Use `gate_hwasan` function.
	* tree-pass.h (make_pass_hwasan): Declare passes.
	(make_pass_hwasan_O0): Declare passes.



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


diff --git a/gcc/asan.h b/gcc/asan.h
index 9f010993944058b10153ff258fd1d08f5dff67d9..28bbd57549881323a6bf55a640a3f36e45fa7526 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -31,12 +31,16 @@ extern rtx hwasan_with_tag (rtx, poly_int64);
 extern void hwasan_tag_init ();
 extern rtx hwasan_create_untagged_base (rtx);
 extern rtx hwasan_extract_tag (rtx tagged_pointer);
+extern rtx hwasan_base ();
 extern rtx_insn *hwasan_emit_prologue (rtx *, rtx *, rtx, poly_int64 *,
 				       uint8_t *, size_t);
 extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx);
+extern bool hwasan_expand_check_ifn (gimple_stmt_iterator *, bool);
+extern bool hwasan_expand_mark_ifn (gimple_stmt_iterator *);
 extern bool hwasan_sanitize_p (void);
 extern bool hwasan_sanitize_stack_p (void);
 extern bool hwasan_sanitize_allocas_p (void);
+extern bool gate_hwasan (void);
 extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 					     HOST_WIDE_INT *, tree *, int);
 extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
@@ -133,6 +137,13 @@ enum asan_mark_flags
 #undef DEF
 };
 
+enum hwasan_mark_flags
+{
+#define DEF(X) HWASAN_MARK_##X
+  IFN_ASAN_MARK_FLAGS
+#undef DEF
+};
+
 /* Return true if STMT is ASAN_MARK with FLAG as first argument.  */
 extern bool asan_mark_p (gimple *stmt, enum asan_mark_flags flag);
 
@@ -182,6 +193,9 @@ extern hash_set<tree> *asan_handled_variables;
 static inline bool
 asan_intercepted_p (enum built_in_function fcode)
 {
+  if (hwasan_sanitize_p ())
+    return false;
+
   return fcode == BUILT_IN_INDEX
 	 || fcode == BUILT_IN_MEMCHR
 	 || fcode == BUILT_IN_MEMCMP
@@ -210,7 +224,8 @@ asan_intercepted_p (enum built_in_function fcode)
 static inline bool
 asan_sanitize_use_after_scope (void)
 {
-  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
+  return (flag_sanitize_address_use_after_scope
+	  && (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ()));
 }
 
 /* Return true if DECL should be guarded on the stack.  */
diff --git a/gcc/asan.c b/gcc/asan.c
index f755a3290f1091be14fbe4c51d9579389e5eb245..4ae678aa6146097ae9150074f676fef5eff90be6 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -327,6 +327,25 @@ asan_sanitize_allocas_p (void)
   return (asan_sanitize_stack_p () && param_asan_protect_allocas);
 }
 
+bool
+asan_instrument_reads (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_instrument_reads);
+}
+
+bool
+asan_instrument_writes (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_instrument_writes);
+}
+
+bool
+asan_memintrin (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_memintrin);
+}
+
+
 /* Checks whether section SEC should be sanitized.  */
 
 static bool
@@ -580,20 +599,47 @@ get_last_alloca_addr ()
    To overcome the issue we use following trick: pass new_sp as a second
    parameter to __asan_allocas_unpoison and rewrite it during expansion with
    new_sp + (virtual_dynamic_stack_rtx - sp) later in
-   expand_asan_emit_allocas_unpoison function.  */
+   expand_asan_emit_allocas_unpoison function.
+
+   HWASAN needs to do very similar, the eventual pseudocode should be:
+      __hwasan_tag_memory (virtual_stack_dynamic_rtx,
+			   0,
+			   new_sp - sp);
+      __builtin_stack_restore (new_sp)
+
+   Need to use the same trick to handle STACK_DYNAMIC_OFFSET as described
+   above.  */
 
 static void
 handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p ()))
     return;
 
-  tree last_alloca = get_last_alloca_addr ();
   tree restored_stack = gimple_call_arg (call, 0);
-  tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
-  gimple *g = gimple_build_call (fn, 2, last_alloca, restored_stack);
-  gsi_insert_before (iter, g, GSI_SAME_STMT);
-  g = gimple_build_assign (last_alloca, restored_stack);
+
+  gimple *g;
+
+  if (hwasan_sanitize_allocas_p ())
+    {
+      enum internal_fn fn = IFN_HWASAN_ALLOCA_UNPOISON;
+      /* There is only one piece of information `expand_HWASAN_ALLOCA_UNPOISON`
+	 needs to work.  This is the length of the area that we're
+	 deallocating.  Since the stack pointer is known at expand time, the
+	 position of the new stack pointer after deallocation is enough
+	 information to calculate this length.  */
+      g = gimple_build_call_internal (fn, 1, restored_stack);
+    }
+  else
+    {
+      tree last_alloca = get_last_alloca_addr ();
+      tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
+      g = gimple_build_call (fn, 2, last_alloca, restored_stack);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      g = gimple_build_assign (last_alloca, restored_stack);
+    }
+
   gsi_insert_before (iter, g, GSI_SAME_STMT);
 }
 
@@ -623,14 +669,12 @@ handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 static void
 handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p ()))
     return;
 
   gassign *g;
   gcall *gg;
-  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
-
-  tree last_alloca = get_last_alloca_addr ();
   tree callee = gimple_call_fndecl (call);
   tree old_size = gimple_call_arg (call, 0);
   tree ptr_type = gimple_call_lhs (call) ? TREE_TYPE (gimple_call_lhs (call))
@@ -640,6 +684,85 @@ handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
     = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
       ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
 
+  if (hwasan_sanitize_allocas_p ())
+    {
+      /*
+	 HWASAN needs a different expansion.
+
+	 addr = __builtin_alloca (size, align);
+
+	 should be replaced by
+
+	 new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment;
+	 untagged_addr = __builtin_alloca (new_size, align);
+	 tag = __hwasan_choose_alloca_tag ();
+	 addr = __hwasan_tag_pointer (untagged_addr, tag);
+	 __hwasan_tag_memory (untagged_addr, tag, new_size);
+	*/
+      /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start on
+	 a tag granule.  */
+      align = align > HWASAN_TAG_GRANULE_SIZE ? align : HWASAN_TAG_GRANULE_SIZE;
+
+      uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
+      /* tree new_size = (old_size + tg_mask) & ~tg_mask;  */
+      tree old_size = gimple_call_arg (call, 0);
+      tree tree_mask = build_int_cst (size_type_node, tg_mask);
+      g = gimple_build_assign (make_ssa_name (size_type_node), PLUS_EXPR,
+			       old_size, tree_mask);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree oversize = gimple_assign_lhs (g);
+
+      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_NOT_EXPR,
+			       tree_mask);
+      tree mask = gimple_assign_lhs (g);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+
+      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_AND_EXPR,
+			       oversize, mask);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree new_size = gimple_assign_lhs (g);
+
+      /* emit the alloca call */
+      tree fn = builtin_decl_implicit (BUILT_IN_ALLOCA_WITH_ALIGN);
+      gg = gimple_build_call (fn, 2, new_size,
+			      build_int_cst (size_type_node, align));
+      tree untagged_addr = make_ssa_name (ptr_type, gg);
+      gimple_call_set_lhs (gg, untagged_addr);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code choosing the tag.
+	 Here we use an internal function so we can choose the tag at expand
+	 time.  We need the decision to be made after stack variables have been
+	 assigned their tag (i.e. once the tag_offset variable has been set to
+	 one after the last stack variables tag).  */
+
+      gg = gimple_build_call_internal (IFN_HWASAN_CHOOSE_TAG, 0);
+      tree tag = make_ssa_name (unsigned_char_type_node, gg);
+      gimple_call_set_lhs (gg, tag);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code adding tag to pointer.  */
+      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_PTR);
+      gg = gimple_build_call (fn, 2, untagged_addr, tag);
+      tree addr = make_ssa_name (ptr_type, gg);
+      gimple_call_set_lhs (gg, addr);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Insert code tagging shadow memory.
+	 NOTE: require using `untagged_addr` here for libhwasan API.  */
+      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MEM);
+      gg = gimple_build_call (fn, 3, untagged_addr, tag, new_size);
+      gsi_insert_before (iter, gg, GSI_SAME_STMT);
+
+      /* Finally, replace old alloca ptr with NEW_ALLOCA.  */
+      replace_call_with_value (iter, addr);
+      return;
+    }
+
+  tree last_alloca = get_last_alloca_addr ();
+  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
+
+
   /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
      bytes of allocated space.  Otherwise, align alloca to ASAN_RED_ZONE_SIZE
      manually.  */
@@ -792,6 +915,31 @@ get_mem_refs_of_builtin_call (gcall *call,
       break;
 
     case BUILT_IN_STRLEN:
+      /* Special case strlen here since its length is taken from its return
+	 value.
+
+	 The approach taken by the sanitizers is to check a memory access
+	 before it's taken.  For ASAN strlen is intercepted by libasan, so no
+	 check is inserted by the compiler.
+
+	 This function still returns `true` and provides a length to the rest
+	 of the ASAN pass in order to record what areas have been checked,
+	 avoiding superfluous checks later on.
+
+	 HWASAN does not intercept any of these internal functions.
+	 This means that checks for memory accesses must be inserted by the
+	 compiler.
+	 strlen is a special case, because we can tell the length from the
+	 return of the function, but that is not known until after the function
+	 has returned.
+
+	 Hence we can't check the memory access before it happens.
+	 We could check the memory access after it has already happened, but
+	 for now I'm choosing to just ignore `strlen` calls.
+	 This decision was simply made because that means the special case is
+	 limited to this one case of this one function.  */
+      if (hwasan_sanitize_p ())
+	return false;
       source0 = gimple_call_arg (call, 0);
       len = gimple_call_lhs (call);
       break;
@@ -1361,6 +1509,149 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+
+/* HWAddressSanitizer (hwasan) is a probabilistic method for detecting
+   out-of-bounds and use-after-free bugs.
+   Read more:
+   http://code.google.com/p/address-sanitizer/
+
+   Similar to AddressSanitizer (asan) it consists of two parts: the
+   instrumentation module in this file, and a run-time library.
+
+   The instrumentation module adds a run-time check before every memory insn in
+   the same manner as asan (see the block comment for AddressSanitizer above).
+   Currently, hwasan only adds out-of-line instrumentation, where each check is
+   implemented as a function call to the run-time library.  Hence a check for a
+   load of N bytes from address X would be implemented with a function call to
+   __hwasan_loadN(X), and checking a store of N bytes from address X would be
+   implemented with a function call to __hwasan_storeN(X).
+
+   The main difference between hwasan and asan is in the information stored to
+   help this checking.  Both sanitizers use a shadow memory area which stores
+   data recording the state of main memory at a corresponding address.
+
+   For hwasan, each 16 byte granule in main memory has a corresponding 1 byte
+   in shadow memory.  This shadow address can be calculated with equation:
+     (addr >> HWASAN_TAG_SHIFT_SIZE) + __hwasan_shadow_memory_dynamic_address;
+   The conversion between real and shadow memory for asan is given in the block
+   comment at the top of this file.
+   The description of how this shadow memory is laid out for asan is in the
+   block comment at the top of this file, here we describe how this shadow
+   memory is used for hwasan.
+
+   For hwasan, each variable is assigned a byte-sized 'tag'.  The extent of
+   the shadow memory for that variable is filled with the assigned tag, and
+   every pointer referencing that variable has its top byte set to the same
+   tag.  The run-time library redefines malloc so that every allocation returns
+   a tagged pointer and tags the corresponding shadow memory with the same tag.
+
+   On each pointer dereference the tag found in the pointer is compared to the
+   tag found in the shadow memory corresponding to the accessed memory address.
+   If these tags are found to differ then this memory access is judged to be
+   invalid and a report is generated.
+
+   This method of bug detection is not perfect -- it can not catch every bad
+   access -- but catches them probabilistically instead.  There is always the
+   possibility that an invalid memory access will happen to access memory
+   tagged with the same tag as the pointer that this access used.
+   The chances of this are approx. 0.4% for any two uncorrelated objects.
+
+   Random tag generation can mitigate this problem by decreasing the
+   probability that an invalid access will be missed in the same manner over
+   multiple runs.  i.e. if two objects are tagged the same in one run of the
+   binary they are unlikely to be tagged the same in the next run.
+   Both heap and stack allocated objects have random tags by default.
+
+   [16 byte granule implications]
+    Since the shadow memory only has a resolution on real memory of 16 bytes,
+    invalid accesses that are within the same 16 byte granule as a valid
+    address will not be caught.
+
+    There is a "short-granule" feature in the runtime library which does catch
+    such accesses, but this feature is not implemented for stack objects (since
+    stack objects are allocated and tagged by compiler instrumentation, and
+    this feature has not yet been implemented in GCC instrumentation).
+
+    Another outcome of this 16 byte resolution is that each tagged object must
+    be 16 byte aligned.  If two objects were to share any 16 byte granule in
+    memory, then they both would have to be given the same tag, and invalid
+    accesses to one using a pointer to the other would be undetectable.
+
+   [Compiler instrumentation]
+    Compiler instrumentation ensures that two adjacent buffers on the stack are
+    given different tags, this means an access to one buffer using a pointer
+    generated from the other (e.g. through buffer overrun) will have mismatched
+    tags and be caught by hwasan.
+
+    We don't randomly tag every object on the stack, since that would require
+    keeping many registers to record each tag.  Instead we randomly generate a
+    tag for each function frame, and each new stack object uses a tag offset
+    from that frame tag.
+    i.e. each object is tagged as RFT + offset, where RFT is the "random frame
+    tag" generated for this frame.
+
+    As a demonstration, using the same example program as in the asan block
+    comment above:
+
+     int
+     foo ()
+     {
+       char a[23] = {0};
+       int b[2] = {0};
+
+       a[5] = 1;
+       b[1] = 2;
+
+       return a[5] + b[1];
+     }
+
+    On AArch64 the stack will be ordered as follows for the above function:
+
+    Slot 1/ [24 bytes for variable 'a']
+    Slot 2/ [8 bytes padding for alignment]
+    Slot 3/ [8 bytes for variable 'b']
+    Slot 4/ [8 bytes padding for alignment]
+
+    (The padding is there to ensure 16 byte alignment as described in the 16
+     byte granule implications).
+
+    While the shadow memory will be ordered as follows:
+
+    - 2 bytes (representing 32 bytes in real memory) tagged with RFT + 1.
+    - 1 byte (representing 16 bytes in real memory) tagged with RFT + 2.
+
+    And any pointer to "a" will have the tag RFT + 1, and any pointer to "b"
+    will have the tag RFT + 2.
+
+   [Top Byte Ignore requirements]
+    Hwasan requires the ability to store an 8 bit tag in every pointer.  There
+    is no instrumentation done to remove this tag from pointers before
+    dereferencing, which means the hardware must ignore this tag during memory
+    accesses.
+
+    One architecture that provides this feature is the AArch64 architecture.
+    This is the only architecture that hwasan is currently implemented for.
+
+   [Stack requires cleanup on unwinding]
+    During normal operation of a hwasan sanitized program more space in the
+    shadow memory becomes tagged as the stack grows.  As the stack shrinks this
+    shadow memory space must become untagged.  If it is not untagged then when
+    the stack grows again (during other function calls later on in the program)
+    objects on the stack that are usually not tagged (e.g. parameters passed on
+    the stack) can be placed in memory whose shadow space is tagged with
+    something else, and accesses can cause false positive reports.
+
+    Hence we place untagging code on every epilogue of functions which tag some
+    stack objects.
+
+    Moreover, the run-time library intercepts longjmp & setjmp to uncolour
+    when the stack is unwound this way.
+
+    C++ exceptions are not yet handled, which means this sanitizer can not
+    handle C++ code that throws exceptions -- it will give false positives
+    after an exception has been thrown.  */
+
+
 /* Returns whether we are tagging pointers and checking those tags on memory
    access.  */
 bool
@@ -1383,6 +1674,28 @@ hwasan_sanitize_allocas_p (void)
   return (hwasan_sanitize_stack_p () && param_hwasan_protect_allocas);
 }
 
+/* Should we instrument reads?  */
+bool
+hwasan_instrument_reads (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_reads);
+}
+
+/* Should we instrument writes?  */
+bool
+hwasan_instrument_writes (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_writes);
+}
+
+/* Should we instrument builtin calls?  */
+bool
+hwasan_memintrin (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_mem_intrinsics);
+}
+
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1878,6 +2191,8 @@ static tree
 report_error_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
 		   int *nargs)
 {
+  gcc_assert (!hwasan_sanitize_p ());
+
   static enum built_in_function report[2][2][6]
     = { { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
 	    BUILT_IN_ASAN_REPORT_LOAD4, BUILT_IN_ASAN_REPORT_LOAD8,
@@ -2214,7 +2529,11 @@ build_check_stmt (location_t loc, tree base, tree len,
   if (is_scalar_access)
     flags |= ASAN_CHECK_SCALAR_ACCESS;
 
-  g = gimple_build_call_internal (IFN_ASAN_CHECK, 4,
+  enum internal_fn fn = hwasan_sanitize_p ()
+    ? IFN_HWASAN_CHECK
+    : IFN_ASAN_CHECK;
+
+  g = gimple_build_call_internal (fn, 4,
 				  build_int_cst (integer_type_node, flags),
 				  base, len,
 				  build_int_cst (integer_type_node,
@@ -2238,9 +2557,9 @@ static void
 instrument_derefs (gimple_stmt_iterator *iter, tree t,
 		   location_t location, bool is_store)
 {
-  if (is_store && !param_asan_instrument_writes)
+  if (is_store && !(asan_instrument_writes () || hwasan_instrument_writes ()))
     return;
-  if (!is_store && !param_asan_instrument_reads)
+  if (!is_store && !(asan_instrument_reads () || hwasan_instrument_reads ()))
     return;
 
   tree type, base;
@@ -2301,7 +2620,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
     {
       if (DECL_THREAD_LOCAL_P (inner))
 	return;
-      if (!param_asan_globals && is_global_var (inner))
+      if ((hwasan_sanitize_p () || !param_asan_globals)
+	  && is_global_var (inner))
         return;
       if (!TREE_STATIC (inner))
 	{
@@ -2394,7 +2714,7 @@ instrument_mem_region_access (tree base, tree len,
 static bool
 instrument_builtin_call (gimple_stmt_iterator *iter)
 {
-  if (!param_asan_memintrin)
+  if (!(asan_memintrin () || hwasan_memintrin ()))
     return false;
 
   bool iter_advanced_p = false;
@@ -2530,10 +2850,13 @@ maybe_instrument_call (gimple_stmt_iterator *iter)
 	      break;
 	    }
 	}
-      tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
-      gimple *g = gimple_build_call (decl, 0);
-      gimple_set_location (g, gimple_location (stmt));
-      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      if (! hwasan_sanitize_p ())
+	{
+	  tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
+	  gimple *g = gimple_build_call (decl, 0);
+	  gimple_set_location (g, gimple_location (stmt));
+	  gsi_insert_before (iter, g, GSI_SAME_STMT);
+	}
     }
 
   bool instrumented = false;
@@ -2932,6 +3255,9 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_PTR_CONST_PTR_UINT8
+    = build_function_type_list (ptr_type_node, const_ptr_type_node,
+				unsigned_char_type_node, NULL_TREE);
   tree BT_FN_VOID_PTR_UINT8_SIZE
     = build_function_type_list (void_type_node, ptr_type_node,
 				unsigned_char_type_node, size_type_node,
@@ -3255,18 +3581,34 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 
   gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
 
+  tree len = gimple_call_arg (g, 2);
+  gcc_assert (tree_fits_shwi_p (len));
+  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  gcc_assert (size_in_bytes);
+
+  if (hwasan_sanitize_p ())
+    {
+      gcc_assert (param_hwasan_instrument_stack);
+      /* Here we swap ASAN_MARK calls for HWASAN_MARK.
+	 This is because we are using the approach of using ASAN_MARK as a
+	 synonym until here.
+	 That approach means we don't yet have to duplicate all the special
+	 cases for ASAN_MARK and ASAN_POISON with the exact same handling but
+	 called HWASAN_MARK etc.  */
+      gimple *hw_poison_call
+	= gimple_build_call_internal (IFN_HWASAN_MARK, 3,
+				      gimple_call_arg (g, 0),
+				      base, len);
+      gsi_replace (iter, hw_poison_call, false);
+      return false;
+    }
+
   if (is_poison)
     {
       if (asan_handled_variables == NULL)
 	asan_handled_variables = new hash_set<tree> (16);
       asan_handled_variables->add (decl);
     }
-  tree len = gimple_call_arg (g, 2);
-
-  gcc_assert (tree_fits_shwi_p (len));
-  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
-  gcc_assert (size_in_bytes);
-
   g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
 			   NOP_EXPR, base);
   gimple_set_location (g, loc);
@@ -3329,6 +3671,7 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 bool
 asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
 {
+  gcc_assert (!hwasan_sanitize_p ());
   gimple *g = gsi_stmt (*iter);
   location_t loc = gimple_location (g);
   bool recover_p;
@@ -3602,11 +3945,66 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
 
       int nargs;
       bool store_p = gimple_call_internal_p (use, IFN_ASAN_POISON_USE);
-      tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
-				    &nargs);
-
-      gcall *call = gimple_build_call (fun, 1,
-				       build_fold_addr_expr (shadow_var));
+      gcall *call;
+      if (hwasan_sanitize_p ())
+	{
+	  tree fun = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MISMATCH4);
+	  /* NOTE: hwasan has no __hwasan_report_* functions like asan does.
+		We use __hwasan_tag_mismatch4 with arguments that tell it the
+		size of access and load to report all tag mismatches.
+
+		The arguments to this function are:
+		  Address of invalid access.
+		  Bitfield containing information about the access
+		    (access_info)
+		  Pointer to a frame of registers
+		    (for use in printing the contents of registers in a dump)
+		    Not used yet -- to be used by inline instrumentation.
+		  Size of access (only if size is not representable in the
+		    access_info argument).
+
+		The access_info bitfield encodes the following pieces of
+		information:
+		  - Is this a store or load?
+		    access_info & 0x10  =>  store
+		  - Should the program continue after reporting the error?
+		    access_info & 0x20  =>  recover
+		  - What size access is this (if size is less than 16 bytes)
+		    if (access_info & 0xf == 0xf)
+		      size is taken from last argument.
+		    else
+		      size == 1 << (access_info & 0xf)
+
+		The last argument contains the size of the access iff the
+		access was of a size greater than or equal to 16 bytes.
+
+		See the function definition `__hwasan_tag_mismatch4` in
+		libsanitizer/hwasan for the full definition.
+		*/
+	  unsigned HOST_WIDE_INT size_in_bytes = tree_to_uhwi (size);
+	  unsigned size_indicator = (size_in_bytes > 16)
+	    ? 0xf
+	    : exact_log2 (size_in_bytes);
+	  unsigned access_info = (0x20 * recover_p)
+	    + (0x10 * store_p)
+	    + (size_indicator);
+	  tree long_pointer_type
+	    = build_pointer_type (long_unsigned_type_node);
+	  call = gimple_build_call (fun, 3,
+				    build_fold_addr_expr (shadow_var),
+				    build_int_cst (long_unsigned_type_node,
+						   access_info),
+				    build_int_cst (long_pointer_type,
+						   0),
+				    size);
+	}
+      else
+	{
+	  tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
+					&nargs);
+	  call = gimple_build_call (fun, 1,
+				    build_fold_addr_expr (shadow_var));
+	}
       gimple_set_location (call, gimple_location (use));
       gimple *call_to_insert = call;
 
@@ -3740,6 +4138,16 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/*  HWASAN  */
+
+/* Instrument the current function.  */
+static unsigned int
+hwasan_instrument (void)
+{
+  transform_statements ();
+  return 0;
+}
+
 /* For stack tagging:
      Initialise tag of the base register.
      This has to be done as soon as the stack is getting expanded to ensure
@@ -3763,6 +4171,18 @@ hwasan_current_tag ()
 }
 
 /* For stack tagging:
+     Return the 'base pointer' for this function.  This pointer is what gets
+     returned from `hwasan_record_base`.  */
+rtx
+hwasan_base ()
+{
+  if (! hwasan_base_ptr)
+    hwasan_record_base (gen_reg_rtx (Pmode));
+
+  return hwasan_base_ptr;
+}
+
+/* For stack tagging:
      Increment the tag offset modulo the size a tag can represent.  */
 void
 hwasan_increment_tag ()
@@ -4005,4 +4425,216 @@ hwasan_finish_file (void)
   flag_sanitize |= SANITIZE_HWADDRESS;
 }
 
+/* Construct a function tree for __hwasan_{load,store}{1,2,4,8,16,_n}.
+   IS_STORE is either 1 (for a store) or 0 (for a load).  */
+static tree
+hwasan_check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
+		   int *nargs)
+{
+  static enum built_in_function check[2][2][6]
+    = { { { BUILT_IN_HWASAN_LOAD1, BUILT_IN_HWASAN_LOAD2,
+	    BUILT_IN_HWASAN_LOAD4, BUILT_IN_HWASAN_LOAD8,
+	    BUILT_IN_HWASAN_LOAD16, BUILT_IN_HWASAN_LOADN },
+	  { BUILT_IN_HWASAN_STORE1, BUILT_IN_HWASAN_STORE2,
+	    BUILT_IN_HWASAN_STORE4, BUILT_IN_HWASAN_STORE8,
+	    BUILT_IN_HWASAN_STORE16, BUILT_IN_HWASAN_STOREN } },
+	{ { BUILT_IN_HWASAN_LOAD1_NOABORT,
+	    BUILT_IN_HWASAN_LOAD2_NOABORT,
+	    BUILT_IN_HWASAN_LOAD4_NOABORT,
+	    BUILT_IN_HWASAN_LOAD8_NOABORT,
+	    BUILT_IN_HWASAN_LOAD16_NOABORT,
+	    BUILT_IN_HWASAN_LOADN_NOABORT },
+	  { BUILT_IN_HWASAN_STORE1_NOABORT,
+	    BUILT_IN_HWASAN_STORE2_NOABORT,
+	    BUILT_IN_HWASAN_STORE4_NOABORT,
+	    BUILT_IN_HWASAN_STORE8_NOABORT,
+	    BUILT_IN_HWASAN_STORE16_NOABORT,
+	    BUILT_IN_HWASAN_STOREN_NOABORT } } };
+  if (size_in_bytes == -1)
+    {
+      *nargs = 2;
+      return builtin_decl_implicit (check[recover_p][is_store][5]);
+    }
+  *nargs = 1;
+  int size_log2 = exact_log2 (size_in_bytes);
+  return builtin_decl_implicit (check[recover_p][is_store][size_log2]);
+}
+
+/* Expand the HWASAN_{LOAD,STORE} builtins.  */
+bool
+hwasan_expand_check_ifn (gimple_stmt_iterator *iter, bool)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  bool recover_p;
+  if (flag_sanitize & SANITIZE_USER_HWADDRESS)
+    recover_p = (flag_sanitize_recover & SANITIZE_USER_HWADDRESS) != 0;
+  else
+    recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_HWADDRESS) != 0;
+
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_CHECK_LAST);
+  bool is_scalar_access = (flags & ASAN_CHECK_SCALAR_ACCESS) != 0;
+  bool is_store = (flags & ASAN_CHECK_STORE) != 0;
+  bool is_non_zero_len = (flags & ASAN_CHECK_NON_ZERO_LEN) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  tree len = gimple_call_arg (g, 2);
+
+  /* `align` is unused for HWASAN_CHECK, but I pass the argument anyway
+     since that way the arguments match ASAN_CHECK.  */
+  /* HOST_WIDE_INT align = tree_to_shwi (gimple_call_arg (g, 3));  */
+
+  unsigned HOST_WIDE_INT size_in_bytes
+    = is_scalar_access && tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
+
+  gimple_stmt_iterator gsi = *iter;
+
+  if (!is_non_zero_len)
+    {
+      /* So, the length of the memory area to hwasan-protect is
+	 non-constant.  Let's guard the generated instrumentation code
+	 like:
+
+	 if (len != 0)
+	   {
+	     // hwasan instrumentation code goes here.
+	   }
+	 // falltrough instructions, starting with *ITER.  */
+
+      g = gimple_build_cond (NE_EXPR,
+			    len,
+			    build_int_cst (TREE_TYPE (len), 0),
+			    NULL_TREE, NULL_TREE);
+      gimple_set_location (g, loc);
+
+      basic_block then_bb, fallthrough_bb;
+      insert_if_then_before_iter (as_a <gcond *> (g), iter,
+				  /*then_more_likely_p=*/true,
+				  &then_bb, &fallthrough_bb);
+      /* Note that fallthrough_bb starts with the statement that was
+	pointed to by ITER.  */
+
+      /* The 'then block' of the 'if (len != 0) condition is where
+	we'll generate the hwasan instrumentation code now.  */
+      gsi = gsi_last_bb (then_bb);
+    }
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			   NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+  tree base_addr = gimple_assign_lhs (g);
+
+  int nargs = 0;
+  tree fun = hwasan_check_func (is_store, recover_p, size_in_bytes, &nargs);
+  if (nargs == 1)
+    g = gimple_build_call (fun, 1, base_addr);
+  else
+    {
+      gcc_assert (nargs == 2);
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			       NOP_EXPR, len);
+      gimple_set_location (g, loc);
+      gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+      g = gimple_build_call (fun, nargs, base_addr, sz_arg);
+    }
+
+  gimple_set_location (g, loc);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+  gsi_remove (iter, true);
+  *iter = gsi;
+  return false;
+}
+
+/* For stack tagging:
+     Dummy: the HWASAN_MARK internal function should only ever be in the code
+     after the sanopt pass.  */
+bool
+hwasan_expand_mark_ifn (gimple_stmt_iterator *)
+{
+  gcc_unreachable ();
+}
+
+bool
+gate_hwasan ()
+{
+  return hwasan_sanitize_p ();
+}
+
+namespace {
+
+const pass_data pass_data_hwasan =
+{
+  GIMPLE_PASS, /* type */
+  "hwasan", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_hwasan : public gimple_opt_pass
+{
+public:
+  pass_hwasan (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_hwasan, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_hwasan (m_ctxt); }
+  virtual bool gate (function *) { return gate_hwasan (); }
+  virtual unsigned int execute (function *) { return hwasan_instrument (); }
+
+}; /* class pass_hwasan  */
+
+} /* anon namespace  */
+
+gimple_opt_pass *
+make_pass_hwasan (gcc::context *ctxt)
+{
+  return new pass_hwasan (ctxt);
+}
+
+namespace {
+
+const pass_data pass_data_hwasan_O0 =
+{
+  GIMPLE_PASS, /* type */
+  "hwasan_O0", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_hwasan_O0 : public gimple_opt_pass
+{
+public:
+  pass_hwasan_O0 (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_hwasan_O0, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_hwasan_O0 (m_ctxt); }
+  virtual bool gate (function *) { return !optimize && gate_hwasan (); }
+  virtual unsigned int execute (function *) { return hwasan_instrument (); }
+
+}; /* class pass_hwasan */
+
+} /* anon namespace */
+
+gimple_opt_pass *
+make_pass_hwasan_O0 (gcc::context *ctxt)
+{
+  return new pass_hwasan_O0 (ctxt);
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index b217f29b9f5ebd9924507490ecd3d1190fdb8a4b..64e40710eaf8884ae85bc0894d74b44f9b234fb7 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -503,6 +503,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_INT_FEXCEPT_T_PTR_INT, BT_INT, BT_FEXCEPT_T_PTR,
 		     BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_FEXCEPT_T_PTR_INT, BT_INT,
 		     BT_CONST_FEXCEPT_T_PTR, BT_INT)
+DEF_FUNCTION_TYPE_2 (BT_FN_PTR_CONST_PTR_UINT8, BT_PTR, BT_CONST_PTR, BT_UINT8)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR)
 
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index e05b770138e60f8a5090164c5906153fed49acfc..48d2c72b444bf5dfd0c24331aaf14fbbb1ef2d26 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -753,6 +753,7 @@ dump_gimple_call_args (pretty_printer *buffer, const gcall *gs,
 	  limit = ARRAY_SIZE (reduction_args);
 	  break;
 
+	case IFN_HWASAN_MARK:
 	case IFN_ASAN_MARK:
 #define DEF(X) #X
 	  static const char *const asan_mark_args[] = {IFN_ASAN_MARK_FLAGS};
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 15dfee903ab298f6a02e45d1affcc2260f3c911d..24ebedd490aea4ad634a92aa5742a83b1b0c0bb7 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1234,8 +1234,11 @@ asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
 
   /* It's necessary to have all stack variables aligned to ASAN granularity
      bytes.  */
-  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
-    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+  gcc_assert (!hwasan_sanitize_p () || hwasan_sanitize_stack_p ());
+  unsigned shadow_granularity
+    = hwasan_sanitize_p () ? HWASAN_TAG_GRANULE_SIZE : ASAN_SHADOW_GRANULARITY;
+  if (DECL_ALIGN_UNIT (decl) <= shadow_granularity)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * shadow_granularity);
 
   HOST_WIDE_INT flags = poison ? ASAN_MARK_POISON : ASAN_MARK_UNPOISON;
 
@@ -15023,7 +15026,7 @@ gimplify_function_tree (tree fndecl)
      if necessary.  */
   cfun->curr_properties |= PROP_gimple_lva;
 
-  if (asan_sanitize_use_after_scope () && sanitize_flags_p (SANITIZE_ADDRESS))
+  if (asan_sanitize_use_after_scope ())
     asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
   if (asan_poisoned_variables)
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 8efc77d986b927dc4a37e396e6c710ffeda663ff..3397fa355f373f3ead5a02a07838a467f08f03c4 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -467,6 +467,105 @@ expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
+expand_HWASAN_CHECK (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
+
+/* For hwasan stack tagging:
+     Clear tags on the dynamically allocated space.
+     For use after an object dynamically allocated on the stack goes out of
+     scope.  */
+static void
+expand_HWASAN_ALLOCA_UNPOISON (internal_fn, gcall *gc)
+{
+  tree restored_position = gimple_call_arg (gc, 0);
+  rtx restored_rtx = expand_expr (restored_position, NULL_RTX, VOIDmode,
+				  EXPAND_NORMAL);
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  rtx off = expand_simple_binop (ptr_mode, MINUS, restored_rtx,
+				 stack_pointer_rtx, NULL_RTX, 0,
+				 OPTAB_LIB_WIDEN);
+  rtx dynamic = convert_memory_address (ptr_mode, virtual_stack_dynamic_rtx);
+  emit_library_call_value (func, NULL_RTX, LCT_NORMAL, VOIDmode,
+			   dynamic, ptr_mode,
+			   const0_rtx, QImode,
+			   off, ptr_mode);
+}
+
+/* For hwasan stack tagging:
+     Return a tag to be used for a dynamic allocation.  */
+static void
+expand_HWASAN_CHOOSE_TAG (internal_fn, gcall *gc)
+{
+  tree tag = gimple_call_lhs (gc);
+  rtx target = expand_expr (tag, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  machine_mode mode = GET_MODE (target);
+  gcc_assert (mode == QImode);
+
+  rtx base_tag = hwasan_extract_tag (hwasan_base ());
+  gcc_assert (base_tag);
+  rtx tag_offset = GEN_INT (hwasan_current_tag ());
+  rtx chosen_tag = expand_simple_binop (QImode, PLUS, base_tag, tag_offset,
+					target, /* unsignedp = */1,
+					OPTAB_WIDEN);
+
+  gcc_assert (chosen_tag);
+  /* Really need to put the tag into the `target` RTX.  */
+  if (chosen_tag != target)
+    {
+      rtx temp = chosen_tag;
+      machine_mode ret_mode = GET_MODE (chosen_tag);
+      if (ret_mode != mode)
+	temp = simplify_gen_unary (TRUNCATE, mode, chosen_tag, ret_mode);
+
+      emit_move_insn (target, temp);
+    }
+
+  hwasan_increment_tag ();
+}
+
+/* For hwasan stack tagging:
+     Tag a region of space in the shadow stack according to the base pointer of
+     an object on the stack.  */
+static void
+expand_HWASAN_MARK (internal_fn, gcall *gc)
+{
+  HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
+  bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
+
+  tree base = gimple_call_arg (gc, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  /* base is a pointer argument, hence in ptr_mode.
+     We convert to Pmode for use in the hwasan_extract_tag and
+     hwasan_create_untagged_base functions.
+     We then convert the result to ptr_mode for the emit_library_call.  */
+  rtx base_rtx = convert_memory_address (Pmode, expand_normal (base));
+
+  rtx tag = is_poison ? const0_rtx : hwasan_extract_tag (base_rtx);
+  rtx address = hwasan_create_untagged_base (base_rtx);
+  address = convert_memory_address (ptr_mode, address);
+
+  tree len = gimple_call_arg (gc, 2);
+  gcc_assert (tree_fits_shwi_p (len));
+  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
+  gcc_assert (size_in_bytes);
+  size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
+  rtx size = gen_int_mode (size_in_bytes, ptr_mode);
+
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (func,
+      LCT_NORMAL,
+      VOIDmode,
+      address, ptr_mode,
+      tag, QImode,
+      size, ptr_mode);
+}
+
+/* This should get expanded in the sanopt pass.  */
+
+static void
 expand_ASAN_CHECK (internal_fn, gcall *)
 {
   gcc_unreachable ();
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 13e60828fcf5db6c5f15aae2bacd4cf04029e430..bcdf798e8ad53c51f6846860c10aa55b5f475e7f 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -319,6 +319,10 @@ DEF_INTERNAL_FN (UBSAN_PTR, ECF_LEAF | ECF_NOTHROW, ".R.")
 DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
 DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (HWASAN_ALLOCA_UNPOISON, ECF_LEAF | ECF_NOTHROW, ".R")
+DEF_INTERNAL_FN (HWASAN_CHOOSE_TAG, ECF_LEAF | ECF_NOTHROW, ".")
+DEF_INTERNAL_FN (HWASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, "..R..")
+DEF_INTERNAL_FN (HWASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, "..R..")
 DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ASAN_POISON, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
diff --git a/gcc/passes.def b/gcc/passes.def
index 2b1e09fdda3681258d6e6d59f1f9997c07fc00ff..5380dc07139335bcfbc7968b333927f3d5890c01 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -251,6 +251,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_sink_code);
       NEXT_PASS (pass_sancov);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_hwasan);
       NEXT_PASS (pass_tsan);
       NEXT_PASS (pass_dce);
       /* Pass group that runs when 1) enabled, 2) there are loops
@@ -369,6 +370,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_dce);
       NEXT_PASS (pass_sancov);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_hwasan);
       NEXT_PASS (pass_tsan);
       /* ???  We do want some kind of loop invariant motion, but we possibly
          need to adjust LIM to be more friendly towards preserving accurate
@@ -394,6 +396,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_sancov_O0);
   NEXT_PASS (pass_lower_switch_O0);
   NEXT_PASS (pass_asan_O0);
+  NEXT_PASS (pass_hwasan_O0);
   NEXT_PASS (pass_tsan_O0);
   NEXT_PASS (pass_sanopt);
   NEXT_PASS (pass_cleanup_eh);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 39b9b05a75ff9d80663a5e0d5edbb73216be84a8..4a38c930df95551af81d3c50819ea52a5ea0bff5 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -183,6 +183,61 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 /* Hardware Address Sanitizer.  */
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1, "__hwasan_load1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2, "__hwasan_load2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4, "__hwasan_load4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8, "__hwasan_load8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16, "__hwasan_load16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN, "__hwasan_loadN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1, "__hwasan_store1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2, "__hwasan_store2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4, "__hwasan_store4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8, "__hwasan_store8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16, "__hwasan_store16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN, "__hwasan_storeN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1_NOABORT, "__hwasan_load1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2_NOABORT, "__hwasan_load2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4_NOABORT, "__hwasan_load4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8_NOABORT, "__hwasan_load8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16_NOABORT, "__hwasan_load16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN_NOABORT, "__hwasan_loadN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1_NOABORT, "__hwasan_store1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2_NOABORT, "__hwasan_store2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4_NOABORT, "__hwasan_store4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8_NOABORT, "__hwasan_store8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16_NOABORT,
+		      "__hwasan_store16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN_NOABORT, "__hwasan_storeN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MISMATCH4, "__hwasan_tag_mismatch4",
+		      BT_FN_VOID_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_HANDLE_LONGJMP, "__hwasan_handle_longjmp",
+		      BT_FN_VOID_CONST_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_PTR, "__hwasan_tag_pointer",
+		      BT_FN_PTR_CONST_PTR_UINT8, ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
 		      BT_FN_VOID_PTR_UINT8_SIZE, ATTR_NOTHROW_LIST)
 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 6c3bce923785f8a2dcd324edc0ce7ceb5f633a0c..965ab367c299f9315cddeb968d1c75086c8762b4 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -776,7 +776,8 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
   basic_block son;
   gimple_stmt_iterator gsi;
   sanopt_info *info = (sanopt_info *) bb->aux;
-  bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0;
+  bool asan_check_optimize
+    = ((flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS)) != 0);
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
@@ -806,6 +807,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
       if (asan_check_optimize
 	  && gimple_call_builtin_p (stmt, BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT))
 	{
+	  gcc_assert (!hwasan_sanitize_p ());
 	  use_operand_p use;
 	  gimple *use_stmt;
 	  if (single_imm_use (gimple_vdef (stmt), &use, &use_stmt))
@@ -834,6 +836,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
 	  case IFN_UBSAN_PTR:
 	    remove = maybe_optimize_ubsan_ptr_ifn (ctx, stmt);
 	    break;
+	  case IFN_HWASAN_CHECK:
 	  case IFN_ASAN_CHECK:
 	    if (asan_check_optimize)
 	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
@@ -1262,6 +1265,10 @@ sanitize_rewrite_addressable_params (function *fun)
 unsigned int
 pass_sanopt::execute (function *fun)
 {
+  /* n.b. ASAN_MARK is used for both HWASAN and ASAN.
+     asan_num_accesses is hence used to count either HWASAN_CHECK or ASAN_CHECK
+     stuff.  This is fine because you can only have one of these active at a
+     time.  */
   basic_block bb;
   int asan_num_accesses = 0;
   bool contains_asan_mark = false;
@@ -1269,10 +1276,10 @@ pass_sanopt::execute (function *fun)
   /* Try to remove redundant checks.  */
   if (optimize
       && (flag_sanitize
-	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT
+	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_HWADDRESS
 	     | SANITIZE_ADDRESS | SANITIZE_VPTR | SANITIZE_POINTER_OVERFLOW)))
     asan_num_accesses = sanopt_optimize (fun, &contains_asan_mark);
-  else if (flag_sanitize & SANITIZE_ADDRESS)
+  else if (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS))
     {
       gimple_stmt_iterator gsi;
       FOR_EACH_BB_FN (bb, fun)
@@ -1292,7 +1299,7 @@ pass_sanopt::execute (function *fun)
       sanitize_asan_mark_poison ();
     }
 
-  if (asan_sanitize_stack_p ())
+  if (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ())
     sanitize_rewrite_addressable_params (fun);
 
   bool use_calls = param_asan_instrumentation_with_call_threshold < INT_MAX
@@ -1334,6 +1341,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_UBSAN_VPTR:
 		  no_next = ubsan_expand_vptr_ifn (&gsi);
 		  break;
+		case IFN_HWASAN_CHECK:
+		  no_next = hwasan_expand_check_ifn (&gsi, use_calls);
+		  break;
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
@@ -1345,6 +1355,9 @@ pass_sanopt::execute (function *fun)
 						    &need_commit_edge_insert,
 						    shadow_vars_mapping);
 		  break;
+		case IFN_HWASAN_MARK:
+		  no_next = hwasan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 42aa4bdad9779179b9b6270b5342c99b53e1ca39..0826caf36d5118de5be9bd8c534c91818488b301 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -510,7 +510,7 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
-      if (flag_sanitize & SANITIZE_HWADDRESS)
+      if (gate_hwasan ())
 	hwasan_finish_file ();
 
       omp_finish_file ();
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 215c8f2a337a9d5d1044b8a9d264ec86b1b03364..8f3cb0fe6048dee22e1b28b4c622df705170f6be 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -341,6 +341,8 @@ extern void register_pass (opt_pass* pass, pass_positioning_ops pos,
 
 extern gimple_opt_pass *make_pass_asan (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_asan_O0 (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_hwasan (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_hwasan_O0 (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tsan (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tsan_O0 (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_sancov (gcc::context *ctxt);


[-- Attachment #2: hwasan-diff5.patch.gz --]
[-- Type: application/gzip, Size: 14279 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 7/X] libsanitizer:  Add tests
  2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
                       ` (17 preceding siblings ...)
  2020-08-17 14:13     ` [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes Matthew Malcomson
@ 2020-08-17 14:13     ` Matthew Malcomson
  2020-11-20 19:14       ` Richard Sandiford
  18 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-08-17 14:13 UTC (permalink / raw)
  To: gcc-patches
  Cc: kcc, Richard Sandiford, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 3521 bytes --]

Adding hwasan tests.

Only interesting thing here is that we have to make sure the tagging mechanism
is deterministic to avoid flaky tests.

gcc/testsuite/ChangeLog:

	* c-c++-common/hwasan/aligned-alloc.c: New test.
	* c-c++-common/hwasan/alloca-array-accessible.c: New test.
	* c-c++-common/hwasan/alloca-gets-different-tag.c: New test.
	* c-c++-common/hwasan/alloca-outside-caught.c: New test.
	* c-c++-common/hwasan/arguments.c: New test.
	* c-c++-common/hwasan/arguments-1.c: New test.
	* c-c++-common/hwasan/arguments-2.c: New test.
	* c-c++-common/hwasan/arguments-3.c: New test.
	* c-c++-common/hwasan/asan-pr63316.c: New test.
	* c-c++-common/hwasan/asan-pr70541.c: New test.
	* c-c++-common/hwasan/asan-pr78106.c: New test.
	* c-c++-common/hwasan/asan-pr79944.c: New test.
	* c-c++-common/hwasan/asan-rlimit-mmap-test-1.c: New test.
	* c-c++-common/hwasan/bitfield-1.c: New test.
	* c-c++-common/hwasan/bitfield-2.c: New test.
	* c-c++-common/hwasan/builtin-special-handling.c: New test.
	* c-c++-common/hwasan/check-interface.c: New test.
	* c-c++-common/hwasan/halt_on_error-1.c: New test.
	* c-c++-common/hwasan/heap-overflow.c: New test.
	* c-c++-common/hwasan/hwasan-poison-optimisation.c: New test.
	* c-c++-common/hwasan/hwasan-thread-access-parent.c: New test.
	* c-c++-common/hwasan/hwasan-thread-basic-failure.c: New test.
	* c-c++-common/hwasan/hwasan-thread-clears-stack.c: New test.
	* c-c++-common/hwasan/hwasan-thread-success.c: New test.
	* c-c++-common/hwasan/kernel-defaults.c: New test.
	* c-c++-common/hwasan/large-aligned-0.c: New test.
	* c-c++-common/hwasan/large-aligned-1.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-0.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-1.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-2.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-3.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-4.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-5.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-6.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-7.c: New test.
	* c-c++-common/hwasan/macro-definition.c: New test.
	* c-c++-common/hwasan/no-sanitize-attribute.c: New test.
	* c-c++-common/hwasan/param-instrument-reads-and-writes.c: New test.
	* c-c++-common/hwasan/param-instrument-reads.c: New test.
	* c-c++-common/hwasan/param-instrument-writes.c: New test.
	* c-c++-common/hwasan/param-instrument-mem-intrinsics.c: New test.
	* c-c++-common/hwasan/random-frame-tag.c: New test.
	* c-c++-common/hwasan/sanity-check-pure-c.c: New test.
	* c-c++-common/hwasan/setjmp-longjmp-0.c: New test.
	* c-c++-common/hwasan/setjmp-longjmp-1.c: New test.
	* c-c++-common/hwasan/stack-tagging-basic-0.c: New test.
	* c-c++-common/hwasan/stack-tagging-basic-1.c: New test.
	* c-c++-common/hwasan/stack-tagging-disable.c: New test.
	* c-c++-common/hwasan/unprotected-allocas-0.c: New test.
	* c-c++-common/hwasan/unprotected-allocas-1.c: New test.
	* c-c++-common/hwasan/use-after-free.c: New test.
	* c-c++-common/hwasan/vararray-outside-caught.c: New test.
	* c-c++-common/hwasan/vararray-stack-restore-correct.c: New test.
	* c-c++-common/hwasan/very-large-objects.c: New test.
	* g++.dg/hwasan/hwasan.exp: New file.
	* g++.dg/hwasan/rvo-handled.C: New test.
	* gcc.dg/hwasan/hwasan.exp: New file.
	* gcc.dg/hwasan/nested-functions-0.c: New test.
	* gcc.dg/hwasan/nested-functions-1.c: New test.
	* gcc.dg/hwasan/nested-functions-2.c: New test.
	* lib/hwasan-dg.exp: New file.

[-- Attachment #2: hwasan-diff6.patch.gz --]
[-- Type: application/gzip, Size: 15806 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system
  2020-08-17 14:12     ` [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system Matthew Malcomson
@ 2020-10-13 15:57       ` Richard Sandiford
  2020-10-28 13:58         ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Richard Sandiford @ 2020-10-13 15:57 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, kcc, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

Sorry for the slow review.

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> This patch tries to tie libhwasan into the GCC build system in the same way
> that the other sanitizer runtime libraries are handled.
>
> libsanitizer/ChangeLog:
>
> 	* Makefile.am:  Build libhwasan.
> 	* Makefile.in:  Build libhwasan.
> 	* asan/Makefile.in:  Build libhwasan.
> 	* configure:  Build libhwasan.
> 	* configure.ac:  Build libhwasan.
> 	* hwasan/Makefile.am: New file.
> 	* hwasan/Makefile.in: New file.
> 	* hwasan/libtool-version: New file.
> 	* interception/Makefile.in: Build libhwasan.
> 	* libbacktrace/Makefile.in: Build libhwasan.
> 	* libsanitizer.spec.in: Build libhwasan.
> 	* lsan/Makefile.in: Build libhwasan.
> 	* sanitizer_common/Makefile.in: Build libhwasan.
> 	* tsan/Makefile.in: Build libhwasan.
> 	* ubsan/Makefile.in: Build libhwasan.

I think this should also update README.gcc and merge.sh.  Could you
try locally merging in a dummy change to the llvm sources with merge.sh,
to make sure it works correctly?

> new file mode 100644
> index 0000000000000000000000000000000000000000..aaa39b4536a5c5f54910a951470814bbc8a20946
> --- /dev/null
> +++ b/libsanitizer/hwasan/Makefile.am
> @@ -0,0 +1,88 @@
> +AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
> +
> +# May be used by toolexeclibdir.
> +gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
> +
> +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
> +AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -fno-ipa-icf

I realise this is just taken from the other Makefile.ams, but do
you know the reason behind -fomit-frame-pointer?  I think we should
avoid building aarch64 libraries with that flag unless there's a
specific reason.

Otherwise looks good to me, although I'm definitely not an expert
on this stuff.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 2/X] libsanitizer: Only build libhwasan when targeting AArch64
  2020-08-17 14:13     ` [PATCH 2/X] libsanitizer: Only build libhwasan when targeting AArch64 Matthew Malcomson
@ 2020-10-13 16:04       ` Richard Sandiford
  0 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-10-13 16:04 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, kcc, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt
> index 52503f1a880ba08b515b8a429ac44a262873f74b..fb55ae9762e9ac6531087a258e1291b5635fcd3e 100644
> --- a/libsanitizer/configure.tgt
> +++ b/libsanitizer/configure.tgt
> @@ -61,6 +61,7 @@ case "${target}" in
>  		LSAN_SUPPORTED=yes
>  		TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
>  	fi
> +	HWASAN_SUPPORTED=yes
>  	;;
>    x86_64-*-darwin1[2-9]* | i?86-*-darwin1[2-9]*)
>  	TSAN_SUPPORTED=no

It might be worth having a comment here to emphasise that hwasan is
supported for both ILP32 and LP64.

OK with or without that change in combination with the other patches.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 3/X] libsanitizer:  Add option to bootstrap using HWASAN
  2020-08-17 14:13     ` [PATCH 3/X] libsanitizer: Add option to bootstrap using HWASAN Matthew Malcomson
@ 2020-10-14  7:52       ` Richard Sandiford
  2020-10-14  8:05         ` Stott Graham
  0 siblings, 1 reply; 150+ messages in thread
From: Richard Sandiford @ 2020-10-14  7:52 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, kcc, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
> index d581a34653f61a440b3c3b832836fe109e2fbd08..25d041fcbb1f7c16f7ac47b7b5d4ea8308c6f69c 100644
> --- a/gcc/doc/install.texi
> +++ b/gcc/doc/install.texi
> @@ -2767,6 +2767,11 @@ the build tree.
>  Compiles GCC itself using Address Sanitization in order to catch invalid memory
>  accesses within the GCC code.
>  
> +@item @samp{bootstrap-hwasan}
> +Compiles GCC itself using HWAddress Sanitization in order to catch invalid
> +memory accesses within the GCC code.  This option is only available on AArch64
> +targets running a Linux kernel that supports the required ABI (5.4 or later).

I'd suggest rewording the last sentence, since it isn't clear whether 5.4
is an ABI version or a Linux kernel version.  Maybe something like:

  This option is only available on AArch64 systems that are running Linux
  kernel version 5.4 or later.

Not sure how good that is, suggestions for something better welcome.

Looks good otherwise.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 3/X] libsanitizer: Add option to bootstrap using HWASAN
  2020-10-14  7:52       ` Richard Sandiford
@ 2020-10-14  8:05         ` Stott Graham
  0 siblings, 0 replies; 150+ messages in thread
From: Stott Graham @ 2020-10-14  8:05 UTC (permalink / raw)
  To: Richard Sandiford, Matthew Malcomson, gcc-patches, kcc,
	Richard Earnshaw, jakub, joseph, dvyukov, Kyrylo Tkachov, dodji,
	Martin Liska

I'm fine,  thanks Sara

On Wed, 14 Oct 2020, 08:53 Richard Sandiford via Gcc-patches, <
gcc-patches@gcc.gnu.org> wrote:

> Matthew Malcomson <matthew.malcomson@arm.com> writes:
> > diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
> > index
> d581a34653f61a440b3c3b832836fe109e2fbd08..25d041fcbb1f7c16f7ac47b7b5d4ea8308c6f69c
> 100644
> > --- a/gcc/doc/install.texi
> > +++ b/gcc/doc/install.texi
> > @@ -2767,6 +2767,11 @@ the build tree.
> >  Compiles GCC itself using Address Sanitization in order to catch
> invalid memory
> >  accesses within the GCC code.
> >
> > +@item @samp{bootstrap-hwasan}
> > +Compiles GCC itself using HWAddress Sanitization in order to catch
> invalid
> > +memory accesses within the GCC code.  This option is only available on
> AArch64
> > +targets running a Linux kernel that supports the required ABI (5.4 or
> later).
>
> I'd suggest rewording the last sentence, since it isn't clear whether 5.4
> is an ABI version or a Linux kernel version.  Maybe something like:
>
>   This option is only available on AArch64 systems that are running Linux
>   kernel version 5.4 or later.
>
> Not sure how good that is, suggestions for something better welcome.
>
> Looks good otherwise.
>
> Thanks,
> Richard
>

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 4/X] libsanitizer: options: Add hwasan flags and argument parsing
  2020-08-17 14:13     ` [PATCH 4/X] libsanitizer: options: Add hwasan flags and argument parsing Matthew Malcomson
@ 2020-10-14  9:42       ` Richard Sandiford
  0 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-10-14  9:42 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, kcc, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

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

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN
  2020-08-17 14:13     ` [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN Matthew Malcomson
@ 2020-10-14 16:09       ` Richard Sandiford
  2020-11-03 13:00         ` Matthew Malcomson
  2020-11-19 12:57         ` Update: " Matthew Malcomson
  0 siblings, 2 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-10-14 16:09 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, kcc, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> @@ -75,6 +89,31 @@ extern hash_set <tree> *asan_used_labels;
>  
>  #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
>  
> +/* NOTE: The values below define an ABI and are hard-coded to these values in
> +   libhwasan, hence they can't be changed independently here.  */
> +/* How many bits are used to store a tag in a pointer.
> +   HWASAN uses the entire top byte of a pointer (i.e. 8 bits).  */
> +#define HWASAN_TAG_SIZE 8
> +/* Tag Granule of HWASAN shadow stack.
> +   This is the size in real memory that each byte in the shadow memory refers
> +   to.  I.e. if a variable is X bytes long in memory then it's tag in shadow

s/it's/its/

> +   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
> +   Most variables will need to be aligned to this amount since two variables
> +   that are neighbours in memory and share a tag granule would need to share

“neighbors” (alas)

> +   the same tag (the shared tag granule can only store one tag).  */
> +#define HWASAN_TAG_SHIFT_SIZE 4
> +#define HWASAN_TAG_GRANULE_SIZE (1ULL << HWASAN_TAG_SHIFT_SIZE)
> +/* Define the tag for the stack background.
> +   This defines what tag the stack pointer will be and hence what tag all
> +   variables that are not given special tags are (e.g. spilled registers,
> +   and parameters passed on the stack).  */
> +#define HWASAN_STACK_BACKGROUND 0
> +/* How many bits to shift in order to access the tag bits.
> +   The tag is stored in the top 8 bits of a pointer hence shifting 56 bits will
> +   leave just the tag.  */
> +#define HWASAN_SHIFT 56
> +#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
> +
>  /* Various flags for Asan builtins.  */
>  enum asan_check_flags
>  {
> diff --git a/gcc/asan.c b/gcc/asan.c
> index 9c9aa4cae35832c1534a2cffac1d3d13eed0e687..f755a3290f1091be14fbe4c51d9579389e5eb245 100644
> --- a/gcc/asan.c
> +++ b/gcc/asan.c
> @@ -257,6 +257,15 @@ hash_set<tree> *asan_handled_variables = NULL;
>  
>  hash_set <tree> *asan_used_labels = NULL;
>  
> +/* Global variables for HWASAN stack tagging.  */
> +/* tag_offset records the offset from the frame base tag that the next object
> +   should have.  */
> +static uint8_t tag_offset = 0;
> +/* hwasan_base_ptr is a pointer with the same address as
> +   `virtual_stack_vars_rtx` for the current frame, and with the frame base tag
> +   stored in it.  */
> +static rtx hwasan_base_ptr = NULL_RTX;

Was initially surprised that this didn't need to be GTY, but I guess
there are no ggc_collect calls during the relevant parts of cfgexpand.

It's normally easy to tell when GTY is needed if all the users of the
state are in the same file, but for cases like this it's a little harder.
Might be worth a comment.

Might also be worth having “stack” or “frame” in these names, and similarly
for the public functions.

> @@ -1352,6 +1361,28 @@ asan_redzone_buffer::flush_if_full (void)
>      flush_redzone_payload ();
>  }
>  
> +/* Returns whether we are tagging pointers and checking those tags on memory
> +   access.  */
> +bool
> +hwasan_sanitize_p ()
> +{
> +    return sanitize_flags_p (SANITIZE_HWADDRESS);

Nit: excess indentation.

> @@ -2901,6 +2932,11 @@ initialize_sanitizer_builtins (void)
>      = build_function_type_list (void_type_node, uint64_type_node,
>  				ptr_type_node, NULL_TREE);
>  
> +  tree BT_FN_VOID_PTR_UINT8_SIZE
> +    = build_function_type_list (void_type_node, ptr_type_node,
> +				unsigned_char_type_node, size_type_node,
> +				NULL_TREE);

The function prototype seems to be:

  void __hwasan_tag_memory(uptr p, u8 tag, uptr sz)

and size_t doesn't have the same precision as pointers on all targets.
Maybe pointer_sized_int_node would be more accurate.  (Despite its name,
it's unsigned rather than signed.)

> @@ -3702,4 +3740,269 @@ make_pass_asan_O0 (gcc::context *ctxt)
>    return new pass_asan_O0 (ctxt);
>  }
>  
> +/* For stack tagging:
> +     Initialise tag of the base register.

“Initialize”

Very minor, but I've not seen this style of comment elsewhere in GCC,
where the main comment is indented by two extra spaces.  Think a blank
line and no extra indentation is more usual.

> +     This has to be done as soon as the stack is getting expanded to ensure
> +     anything emitted with `get_dynamic_stack_base` will use the value set here
> +     instead of using a register without a tag.
> +     Especially note that RTL expansion of large aligned values does that.  */
> +void
> +hwasan_record_base (rtx base)
> +{
> +  targetm.memtag.gentag (base, virtual_stack_vars_rtx);
> +  hwasan_base_ptr = base;
> +}
> +
> +/* For stack tagging:
> +     Return the offset from the frame base tag that the "next" expanded object
> +     should have.  */
> +uint8_t
> +hwasan_current_tag ()
> +{
> +  return tag_offset;
> +}
> +
> +/* For stack tagging:
> +     Increment the tag offset modulo the size a tag can represent.  */
> +void
> +hwasan_increment_tag ()
> +{
> +  uint8_t tag_bits = HWASAN_TAG_SIZE;
> +  STATIC_ASSERT (HWASAN_TAG_SIZE == sizeof (tag_offset) * CHAR_BIT);
> +  tag_offset = (tag_offset + 1) % (1 << tag_bits);

Might be misunderstanding, but doesn't the static assert make the %
redundant?

> +  /* The "background tag" of the stack is zero by definition.
> +     This is the tag that objects like parameters passed on the stack and
> +     spilled registers are given.  It is handy to avoid this for objects we
> +     decide the tags ourselves, partly to ensure that buffer overruns can't

Maybe “for objects whose tags we decide ourselves”?

> +     affect these important variables (e.g. saved link register, saved stack
> +     pointer etc) and partly to make debugging easier (everything with a tag of
> +     zero is space allocated automatically by the compiler).
> +
> +     This is not feasible when using random frame tags (the default
> +     configuration for hwasan) since the tag for the given frame is randomly
> +     chosen at runtime.  In order to avoid any tags matching the stack
> +     background we would need to decide tag offsets at runtime instead of
> +     compile time (and pay the resulting performance cost).
> +
> +     When not using random base tags for each frame (i.e. when compiled with
> +     `--param hwasan-random-frame-tag=0`) the base tag for each frame is zero.
> +     This means the tag that each object gets is equal to the tag_offset used
> +     in determining it.
> +     When this is the case we *can* ensure no object gets the tag of zero by
> +     simply ensuring no object has the tag_offset of zero.  */

Nice comment, thanks for explaining this.

> +  if (tag_offset == HWASAN_STACK_BACKGROUND
> +      && ! param_hwasan_random_frame_tag)
> +    tag_offset += 1;
> +}
> +
> +/* For stack tagging:
> +     Return an RTX representing `base + offset` address
> +     and `tag_of(base) + tag_offset` tag.  */
> +rtx
> +hwasan_with_tag (rtx base, poly_int64 offset)
> +{
> +  gcc_assert (tag_offset < (1 << HWASAN_TAG_SIZE));
> +  return targetm.memtag.addtag (base, offset, tag_offset);
> +}
> +
> +/* Clear internal state for the next function.
> +   This function is called before variables on the stack get expanded, in
> +   `init_vars_expansion`.  */
> +void
> +hwasan_tag_init ()
> +{
> +  delete asan_used_labels;
> +  asan_used_labels = NULL;
> +
> +  hwasan_base_ptr = NULL_RTX;
> +  /* When not using a random frame tag we can avoid the background stack
> +     colour which gives the user a little better debug output upon a crash.

color

> +     Meanwhile, when using a random frame tag it will be nice to avoid adding
> +     tags for the first object since that is unnecessary extra work.
> +     Hence set the initial tag_offset to be 0 if using a random frame tag and 1
> +     otherwise.  */
> +  tag_offset = param_hwasan_random_frame_tag
> +    ? 0
> +    : HWASAN_STACK_BACKGROUND + 1;
> +}
> +
> +/* Return an RTX for the tag of TAGGED_POINTER.  */
> +rtx
> +hwasan_extract_tag (rtx tagged_pointer)
> +{
> +  rtx tag = expand_simple_binop (Pmode,
> +				 LSHIFTRT,
> +				 tagged_pointer,
> +				 HWASAN_SHIFT_RTX,
> +				 NULL_RTX,
> +				 /* unsignedp = */0,
> +				 OPTAB_DIRECT);

Think it's more usual to put multiple arguments on the same line.

> +  return gen_lowpart (QImode, tag);
> +}
> +
> +/* For stack tagging:
> +      Does HWASAN equivalent of `asan_emit_stack_protection`.
> +
> +   Prologue sequence should be emitted directly, while the epilogue sequence is
> +   returned.  The epilogue sequence is what should be used if we're not
> +   protecting alloca objects.
> +
> +   BASES is an array containing the tagged base registers for each object.
> +   We map each object to a given base since large aligned objects have a
> +   different base to others and we need to know which objects use which base.
> +
> +   UNTAGGED_BASES contains the same information as above except without tags.
> +   This is needed since libhwasan only accepts untagged pointers in
> +   __hwasan_tag_memory.
> +
> +   OFFSETS is an array with the start and end offsets for each object stored on
> +   the stack in this frame.  This array is hence twice the length of the other
> +   array arguments (given it has two entries for each stack object).
> +
> +   TAGS is an array containing the tag *offset* each object should have from
> +   the tag in its base pointer.
> +
> +   LENGTH contains the length of the OFFSETS array.  */
> +rtx_insn *
> +hwasan_emit_prologue (rtx *bases,
> +		      rtx *untagged_bases,
> +		      rtx frame_extent,
> +		      poly_int64 *offsets,
> +		      uint8_t *tags,
> +		      size_t length)
> +{
> +  /* We need untagged base pointers since libhwasan only accepts untagged
> +    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
> +    the base tag for an offset.  */
> +
> +  if (length < 2)
> +    return NULL;
> +
> +  poly_int64 bot = 0, top = 0;
> +  size_t i = 0;
> +  for (i = 0; (i * 2) + 1 < length; i++)
> +    {
> +      poly_int64 start = offsets[i * 2];
> +      poly_int64 end = offsets[(i * 2) + 1];
> +
> +      if (known_ge (start, end))
> +	{
> +	  top = start;
> +	  bot = end;
> +	}
> +      else
> +	{
> +	  /* Given how these values are calculated, one must be known greater
> +	     than the other.  */
> +	  gcc_assert (known_lt (start, end));

Think this should be known_le.  E.g.

  known_ge (-2X - 2, -2)
  known_lt (-2X - 2, -2)

are both false for an unknown X >= 0.

Alternatively, you could assert ordered_p (start, end) before the
known_ge check.

> +	  top = end;
> +	  bot = start;
> +	}
> +      poly_int64 size = (top - bot);
> +
> +      /* Can't check that all poly_int64's are aligned, but still nice
> +	 to check those that are compile-time constants.  */

Wouldn't using multiple_p work?  If an assert of that form triggers
for SVE offsets then it would be worth looking into why.

> +      HOST_WIDE_INT tmp;
> +      if (top.is_constant (&tmp))
> +	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
> +      if (bot.is_constant (&tmp))
> +	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
> +      if (size.is_constant (&tmp))
> +	gcc_assert (tmp % HWASAN_TAG_GRANULE_SIZE == 0);
> +
> +      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
> +      rtx base_tag = hwasan_extract_tag (bases[i]);
> +      /* In the case of tag overflow we would want modulo wrapping -- which
> +	 should be given from the `plus_constant` in QImode.  */
> +      rtx tag = plus_constant (QImode, base_tag, tags[i]);

Might be worth a gcc_assert that GET_MODE_PRECISION (QImode)
== HWASAN_TAG_SIZE.

> +      rtx bottom = convert_memory_address (ptr_mode,
> +					   plus_constant (Pmode,
> +							  untagged_bases[i],
> +							  bot));
> +      emit_library_call (ret, LCT_NORMAL, VOIDmode,
> +			 bottom, ptr_mode,
> +			 tag, QImode,
> +			 gen_int_mode (size, ptr_mode), ptr_mode);
> +    }
> +  return hwasan_emit_untag_frame (frame_extent, virtual_stack_vars_rtx);

This call doesn't seem to rely on the prologue processing in the same
way that the asan version does.  I think it would be clearer if this
function emitted only the prologue and if the caller called
hwasan_emit_untag_frame directly.  It would also avoid creating
garbage rtl when var_end_seq needs to be replaced.

> +}
> +
> +/* For stack tagging:
> +     Return RTL insns to clear the tags between DYNAMIC and VARS pointers
> +     into the stack.  These instructions should be emitted at the end of
> +     every function.  */
> +rtx_insn *
> +hwasan_emit_untag_frame (rtx dynamic, rtx vars)
> +{
> +  start_sequence ();
> +
> +  dynamic = convert_memory_address (ptr_mode, dynamic);
> +  vars = convert_memory_address (ptr_mode, vars);
> +
> +  rtx top_rtx;
> +  rtx bot_rtx;
> +  if (FRAME_GROWS_DOWNWARD)
> +    {
> +      top_rtx = vars;
> +      bot_rtx = dynamic;
> +    }
> +  else
> +    {
> +      top_rtx = dynamic;
> +      bot_rtx = vars;
> +    }
> +
> +  rtx size_rtx = expand_simple_binop (ptr_mode, MINUS, top_rtx, bot_rtx,
> +				      NULL_RTX, /* unsignedp = */0,
> +				      OPTAB_DIRECT);
> +
> +  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
> +  emit_library_call (ret, LCT_NORMAL, VOIDmode,
> +      bot_rtx, ptr_mode,
> +      const0_rtx, QImode,
> +      size_rtx, ptr_mode);

Nit: should be indented under “ret”.

> +
> +  do_pending_stack_adjust ();
> +  rtx_insn *insns = get_insns ();
> +  end_sequence ();
> +  return insns;
> +}
> +
> +/* For stack tagging:
> +     Return an RTX representing ORIG_BASE without a tag.  */
> +rtx
> +hwasan_create_untagged_base (rtx orig_base)
> +{
> +  rtx untagged_base = gen_reg_rtx (Pmode);
> +  rtx tag_mask = gen_int_mode ((1ULL << HWASAN_SHIFT) - 1, Pmode);

Not sure how much it matters these days, but: HOST_WIDE_INT_1U instead
of 1ULL.

> +  untagged_base = expand_binop (Pmode, and_optab,
> +				orig_base, tag_mask,
> +				untagged_base, true, OPTAB_DIRECT);

It's better to pass null as the “target” parameter instead of creating
a register up front.

> +  gcc_assert (untagged_base);
> +  return untagged_base;
> +}
> +
> +/* Needs to be GTY(()), because cgraph_build_static_cdtor may
> +   invoke ggc_collect.  */
> +static GTY(()) tree hwasan_ctor_statements;
> +
> +/* Insert module initialisation into this TU.  This initialisation calls the
> +   initialisation code for libhwasan.  */

“initialization”, here and in the body.

> diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
> index b270a4ddb9db469ba52e42f36a1bc2f02d8f03fc..13a7cb3877c27c4c45a445b7d2c068038d9c4568 100644
> --- a/gcc/cfgexpand.c
> +++ b/gcc/cfgexpand.c
> @@ -377,7 +377,13 @@ align_local_variable (tree decl, bool really_expand)
>        if (really_expand)
>  	SET_DECL_ALIGN (decl, align);
>      }
> -  return align / BITS_PER_UNIT;
> +
> +  unsigned int ret_align = align / BITS_PER_UNIT;
> +
> +  if (hwasan_sanitize_stack_p ())
> +    ret_align = MAX (ret_align, HWASAN_TAG_GRANULE_SIZE);

I think it'd be better to update SET_DECL_ALIGN too.

> +
> +  return ret_align;
>  }
>  
>  /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
> @@ -988,7 +994,7 @@ dump_stack_var_partition (void)
>  
>  static void
>  expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
> -			 poly_int64 offset)
> +			 poly_int64 offset, rtx stack_base)

The function comment should describe the new variable.
But I wonder if it would be “better” to export hwasan_(stack_?)base_ptr
from asan.c so that code can treat it similarly to virtual_stack_vars_rtx
where necessary.  E.g. we might eventually want to know this in
find_temp_slot_from_address, although I realise we don't use tagging
for temp slots as things stand.

> @@ -1032,9 +1042,23 @@ public:
>       The vector is in reversed, highest offset pairs come first.  */
>    auto_vec<HOST_WIDE_INT> asan_vec;
>  
> +  /* HWASAN records the poly_int64 so it can handle any stack variable.  */
> +  auto_vec<poly_int64> hwasan_vec;
> +  auto_vec<rtx> hwasan_untagged_base_vec;
> +  auto_vec<rtx> hwasan_base_vec;
> +
>    /* Vector of partition representative decls in between the paddings.  */
>    auto_vec<tree> asan_decl_vec;
>  
> +  /* Vector of tag offsets representing the tag for each stack variable.
> +     Each offset determines the difference between the randomly generated
> +     tag for the current frame and the tag for this stack variable.  */
> +  auto_vec<uint8_t> hwasan_tag_vec;

Did you consider grouping the information into a struct, rather than
having separate vecs for each bit of information?  I.e.:

  untagged base
  tagged base
  nearest offset
  furthest offset
  tag

(Or alternatively, use start offset and end offset, with expand_stack_vars
rather than hwasan_emit_prologue doing the swap.)

> @@ -1123,10 +1153,36 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>        if (pred && !pred (i))
>  	continue;
>  
> +      base = hwasan_sanitize_stack_p ()
> +	? data->asan_base
> +	: virtual_stack_vars_rtx;
>        alignb = stack_vars[i].alignb;
>        if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
>  	{
> -	  base = virtual_stack_vars_rtx;
> +	  if (hwasan_sanitize_stack_p ())
> +	    {
> +	      /* Allocate zero bytes to take advantage of the
> +		 alloc_stack_frame_space logic of ensuring the stack is aligned
> +		 despite having poly_int64's to deal with.

Was initially a bit confused by this.  I don't think poly_int64s are the
only (or even the main) reason we need to use alloc_stack_frame_space,
and e.g. the plain asan code uses a 0-byte allocation despite requiring
fixed-size objects.

If the comment is explaining the strangeness of “allocating” zero bytes
(which I agree is non-obvious) then I think it would be better to have a
trivial wrapper around alloc_stack_frame_space, e.g. align_frame_offset,
and use that for the existing call too.

> +
> +		 There must be no tag granule "shared" between different
> +		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
> +		 chunk can have more than one object in it.
> +
> +		 We ensure this by forcing the end of the last bit of data to
> +		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
> +		 the start of each variable to be aligned to
> +		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
> +
> +		 We can't align just one of the start or end, since there are
> +		 untagged things stored on the stack that we have no control on
> +		 the alignment and these can't share a tag granule with a
> +		 tagged variable.  */

FAOD, please keep the rest of the comment though. :-)

> +	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
> +	      offset = alloc_stack_frame_space (0, HWASAN_TAG_GRANULE_SIZE);
> +	      data->hwasan_vec.safe_push (offset);
> +	      data->hwasan_untagged_base_vec.safe_push (virtual_stack_vars_rtx);
> +	    }
>  	  /* ASAN description strings don't yet have a syntax for expressing
>  	     polynomial offsets.  */
>  	  HOST_WIDE_INT prev_offset;
> @@ -1206,6 +1262,14 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>  	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
>  	      base_align = crtl->max_used_stack_slot_alignment;
>  	    }
> +
> +	  if (hwasan_sanitize_stack_p ())
> +	    {
> +	      data->hwasan_vec.safe_push (offset);
> +	      data->hwasan_frame_extent = plus_constant (Pmode,
> +							 virtual_stack_vars_rtx,
> +							 offset);
> +	    }

Since only the final value of this variable is used, it would be
better to do only one plus_constant, to avoid creating unused rtl.

> index 43b491b027615523f0202fe1ded430cac0b4bef4..145a631ceb5239a9f099ef531668603cc2f7aea6 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -6839,6 +6839,37 @@ DEFHOOK
>   pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
>   bool, (), default_memtag_can_tag_addresses)
>  
> +DEFHOOK
> +(addtag,
> + "Emit an RTX representing BASE offset in value by ADDR_OFFSET and in tag by\

All lines of the documentation should end in “\n\”.

The documentation should use texinfo markup, e.g. @var{base} and
@samp{addtag_force_operand} etc.  

> + TAG_OFFSET.\n\

Very picky, sorry, but it took me a couple of attempts to parse this,
initally tripping over whether “offset” was a noun or not.  Also,
“emit” for rtl normally means “add to the instruction stream”.  How about
something like:

  Return an expression that represents the result of adding
  @var{addr_offset} to the address in pointer @var{base} and
  @var{tag_offset} to the tag in pointer @var{base}.

Equally picky, but I think it looks more natural to me to add
an underscore between all the words in the function name, i.e.
“add_tag” and “gen_tag”.

> +The resulting RTX must either be a valid memory address or be able to get\n\
> +put into an operand with force_operand.  If overridden the more common case\n\
> +is that we force this into an operand using the backend hook\n\
> +\"addtag_force_operand\" that is called in force_operand.\n\
> +\n\
> +It is expected that that \"addtag_force_operand\" recognises the RTX\n\
> +generated by \"addtag\" and emits code to force that RTX into an operand.",

> +rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
> +default_memtag_addtag)
> +
> +DEFHOOK
> +(addtag_force_operand,
> + "If the RTL expression OPER is of the form generated by\n\
> +targetm.memtag.addtag, then emit instructions to move the value into an\n\
> +operand (i.e. for force_operand).\n\
> +TARGET is an RTX suggestion of where to generate the value.\n\
> +This hook is most often implemented by emitting instructions to put the\n\
> +expression into a pseudo register, then returning that pseudo register.",
> +rtx, (rtx oper, rtx target), NULL)

Why's this needed?  What does the addtag result look like for AArch64?

> +DEFHOOK
> +(gentag,
> + "Set the BASE argument to UNTAGGED with some random tag.\n\
> +This function is used to generate a tagged base for the current stack frame.",
> +  void, (rtx base, rtx untagged),
> +  default_memtag_gentag)

Again being picky, but “gentag” sounds like an operation that generates
a tag rather than something that tags a value.  Would “add_random_tag”
or “add_runtime_tag” make sense?

> @@ -2385,4 +2388,89 @@ default_memtag_can_tag_addresses ()
>    return false;
>  }
>  
> +/* Takes a REG rtx as BASE and some value as UNTAGGED.
> +   Ensures the register in BASE is given the value of UNTAGGED with the
> +   (possibly random) base frame tag on it.  */

Just:

  /* The default implementation of TARGET_MEMTAG_GENTAG.  */

is enough here, and helps to ensure that the comment and documentation
don't get out of sync.

> +void
> +default_memtag_gentag (rtx base, rtx untagged)
> +{
> +  gcc_assert (param_hwasan_instrument_stack);
> +  if (param_hwasan_random_frame_tag)
> +    {
> +    rtx temp = gen_reg_rtx (QImode);
> +    rtx ret = init_one_libfunc ("__hwasan_generate_tag");
> +    rtx new_tag = emit_library_call_value (ret, temp, LCT_NORMAL, QImode);
> +    emit_move_insn (base, untagged);
> +    /* We know that `base` is not the stack pointer, since we never want to put
> +      a randomly generated tag into the stack pointer.  Hence we can use
> +      `store_bit_field` which on aarch64 generates a `bfi` which can not act on
> +      the stack pointer.  */
> +    store_bit_field (base, 8, 56, 0, 0, QImode, new_tag, false);

Is the comment addressing a correctness or an optimisation concern?

Perhaps the easiest thing would be to make the interface return
a fresh register, so that there's no question that store_bit_field
is appropriate.  The kernel case could then just return “untagged”.

> +rtx
> +default_memtag_addtag (rtx base, poly_int64 offset, uint8_t tag_offset)
> +{
> +  /* Need to look into what the most efficient code sequence is.
> +     This is a code sequence that would be emitted *many* times, so we
> +     want it as small as possible.
> +
> +     If the tag offset is greater that (1 << 7) then the most efficient

s/that/than/

> +     sequence here would give UB from signed integer overflow in the
> +     poly_int64.  Hence in that case we emit the slightly less efficient
> +     sequence.

Do you mean compile-time or run-time UB?  

If it's compile-time UB in poly_int64 arithmetic then we can just
use poly_uint64 to do the addition.  But it looks like that's what
the code already does.

> +
> +     There are two places where tag overflow is a question:
> +       - Tagging the shadow stack.
> +	  (both tagging and untagging).
> +       - Tagging addressable pointers.
> +
> +     We need to ensure both behaviours are the same (i.e. that the tag that
> +     ends up in a pointer after "overflowing" the tag bits with a tag addition
> +     is the same that ends up in the shadow space).
> +
> +     The aim is that the behaviour of tag addition should follow modulo

behavior :-)

> +     wrapping in both instances.
> +
> +     The libhwasan code doesn't have any path that increments a pointers tag,

pointer's

Thanks,
Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes
  2020-08-17 14:13     ` [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes Matthew Malcomson
@ 2020-10-14 18:37       ` Richard Sandiford
  0 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-10-14 18:37 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, kcc, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> @@ -133,6 +137,13 @@ enum asan_mark_flags
>  #undef DEF
>  };
>  
> +enum hwasan_mark_flags
> +{
> +#define DEF(X) HWASAN_MARK_##X
> +  IFN_ASAN_MARK_FLAGS
> +#undef DEF
> +};

Are these used anywhere?  It looks like expand_HWASAN_MARK uses the
plain asan versions.

> @@ -640,6 +684,85 @@ handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
>      = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
>        ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
>  
> +  if (hwasan_sanitize_allocas_p ())
> +    {
> +      /*
> +	 HWASAN needs a different expansion.
> +
> +	 addr = __builtin_alloca (size, align);
> +
> +	 should be replaced by
> +
> +	 new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment;
> +	 untagged_addr = __builtin_alloca (new_size, align);
> +	 tag = __hwasan_choose_alloca_tag ();
> +	 addr = __hwasan_tag_pointer (untagged_addr, tag);
> +	 __hwasan_tag_memory (untagged_addr, tag, new_size);
> +	*/
> +      /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start on
> +	 a tag granule.  */
> +      align = align > HWASAN_TAG_GRANULE_SIZE ? align : HWASAN_TAG_GRANULE_SIZE;
> +
> +      uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
> +      /* tree new_size = (old_size + tg_mask) & ~tg_mask;  */
> +      tree old_size = gimple_call_arg (call, 0);
> +      tree tree_mask = build_int_cst (size_type_node, tg_mask);
> +      g = gimple_build_assign (make_ssa_name (size_type_node), PLUS_EXPR,
> +			       old_size, tree_mask);
> +      gsi_insert_before (iter, g, GSI_SAME_STMT);
> +      tree oversize = gimple_assign_lhs (g);
> +
> +      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_NOT_EXPR,
> +			       tree_mask);
> +      tree mask = gimple_assign_lhs (g);
> +      gsi_insert_before (iter, g, GSI_SAME_STMT);

Seems simpler to use:

      tree mask = build_int_cst (size_type_node, -HWASAN_TAG_GRANULE_SIZE);

> +
> +      g = gimple_build_assign (make_ssa_name (size_type_node), BIT_AND_EXPR,
> +			       oversize, mask);
> +      gsi_insert_before (iter, g, GSI_SAME_STMT);
> +      tree new_size = gimple_assign_lhs (g);
> +
> +      /* emit the alloca call */
> +      tree fn = builtin_decl_implicit (BUILT_IN_ALLOCA_WITH_ALIGN);
> +      gg = gimple_build_call (fn, 2, new_size,
> +			      build_int_cst (size_type_node, align));
> +      tree untagged_addr = make_ssa_name (ptr_type, gg);
> +      gimple_call_set_lhs (gg, untagged_addr);
> +      gsi_insert_before (iter, gg, GSI_SAME_STMT);
> +
> +      /* Insert code choosing the tag.
> +	 Here we use an internal function so we can choose the tag at expand
> +	 time.  We need the decision to be made after stack variables have been
> +	 assigned their tag (i.e. once the tag_offset variable has been set to
> +	 one after the last stack variables tag).  */
> +
> +      gg = gimple_build_call_internal (IFN_HWASAN_CHOOSE_TAG, 0);
> +      tree tag = make_ssa_name (unsigned_char_type_node, gg);
> +      gimple_call_set_lhs (gg, tag);
> +      gsi_insert_before (iter, gg, GSI_SAME_STMT);
> +
> +      /* Insert code adding tag to pointer.  */
> +      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_PTR);
> +      gg = gimple_build_call (fn, 2, untagged_addr, tag);
> +      tree addr = make_ssa_name (ptr_type, gg);
> +      gimple_call_set_lhs (gg, addr);
> +      gsi_insert_before (iter, gg, GSI_SAME_STMT);
> +
> +      /* Insert code tagging shadow memory.
> +	 NOTE: require using `untagged_addr` here for libhwasan API.  */
> +      fn = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MEM);
> +      gg = gimple_build_call (fn, 3, untagged_addr, tag, new_size);
> +      gsi_insert_before (iter, gg, GSI_SAME_STMT);
> +
> +      /* Finally, replace old alloca ptr with NEW_ALLOCA.  */
> +      replace_call_with_value (iter, addr);
> +      return;
> +    }
> +
> +  tree last_alloca = get_last_alloca_addr ();
> +  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
> +
> +
>    /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
>       bytes of allocated space.  Otherwise, align alloca to ASAN_RED_ZONE_SIZE
>       manually.  */
> @@ -792,6 +915,31 @@ get_mem_refs_of_builtin_call (gcall *call,
>        break;
>  
>      case BUILT_IN_STRLEN:
> +      /* Special case strlen here since its length is taken from its return
> +	 value.
> +
> +	 The approach taken by the sanitizers is to check a memory access
> +	 before it's taken.  For ASAN strlen is intercepted by libasan, so no
> +	 check is inserted by the compiler.
> +
> +	 This function still returns `true` and provides a length to the rest
> +	 of the ASAN pass in order to record what areas have been checked,
> +	 avoiding superfluous checks later on.
> +
> +	 HWASAN does not intercept any of these internal functions.
> +	 This means that checks for memory accesses must be inserted by the
> +	 compiler.
> +	 strlen is a special case, because we can tell the length from the
> +	 return of the function, but that is not known until after the function
> +	 has returned.
> +
> +	 Hence we can't check the memory access before it happens.
> +	 We could check the memory access after it has already happened, but
> +	 for now I'm choosing to just ignore `strlen` calls.
> +	 This decision was simply made because that means the special case is
> +	 limited to this one case of this one function.  */
> +      if (hwasan_sanitize_p ())
> +	return false;

What does LLVM do here?

s/I'm choosing/we choose/

> @@ -1361,6 +1509,149 @@ asan_redzone_buffer::flush_if_full (void)
>      flush_redzone_payload ();
>  }
>  
> +
> +/* HWAddressSanitizer (hwasan) is a probabilistic method for detecting
> +   out-of-bounds and use-after-free bugs.
> +   Read more:
> +   http://code.google.com/p/address-sanitizer/
> +
> +   Similar to AddressSanitizer (asan) it consists of two parts: the
> +   instrumentation module in this file, and a run-time library.
> +
> +   The instrumentation module adds a run-time check before every memory insn in
> +   the same manner as asan (see the block comment for AddressSanitizer above).
> +   Currently, hwasan only adds out-of-line instrumentation, where each check is
> +   implemented as a function call to the run-time library.  Hence a check for a
> +   load of N bytes from address X would be implemented with a function call to
> +   __hwasan_loadN(X), and checking a store of N bytes from address X would be
> +   implemented with a function call to __hwasan_storeN(X).
> +
> +   The main difference between hwasan and asan is in the information stored to
> +   help this checking.  Both sanitizers use a shadow memory area which stores
> +   data recording the state of main memory at a corresponding address.
> +
> +   For hwasan, each 16 byte granule in main memory has a corresponding 1 byte
> +   in shadow memory.  This shadow address can be calculated with equation:
> +     (addr >> HWASAN_TAG_SHIFT_SIZE) + __hwasan_shadow_memory_dynamic_address;
> +   The conversion between real and shadow memory for asan is given in the block
> +   comment at the top of this file.
> +   The description of how this shadow memory is laid out for asan is in the

Maybe “is also in”?

> +   block comment at the top of this file, here we describe how this shadow
> +   memory is used for hwasan.
> +
> +   For hwasan, each variable is assigned a byte-sized 'tag'.  The extent of
> +   the shadow memory for that variable is filled with the assigned tag, and
> +   every pointer referencing that variable has its top byte set to the same
> +   tag.  The run-time library redefines malloc so that every allocation returns
> +   a tagged pointer and tags the corresponding shadow memory with the same tag.
> +
> +   On each pointer dereference the tag found in the pointer is compared to the
> +   tag found in the shadow memory corresponding to the accessed memory address.
> +   If these tags are found to differ then this memory access is judged to be
> +   invalid and a report is generated.
> +
> +   This method of bug detection is not perfect -- it can not catch every bad
> +   access -- but catches them probabilistically instead.  There is always the
> +   possibility that an invalid memory access will happen to access memory
> +   tagged with the same tag as the pointer that this access used.
> +   The chances of this are approx. 0.4% for any two uncorrelated objects.
> +
> +   Random tag generation can mitigate this problem by decreasing the
> +   probability that an invalid access will be missed in the same manner over
> +   multiple runs.  i.e. if two objects are tagged the same in one run of the
> +   binary they are unlikely to be tagged the same in the next run.
> +   Both heap and stack allocated objects have random tags by default.
> +
> +   [16 byte granule implications]
> +    Since the shadow memory only has a resolution on real memory of 16 bytes,
> +    invalid accesses that are within the same 16 byte granule as a valid
> +    address will not be caught.
> +
> +    There is a "short-granule" feature in the runtime library which does catch
> +    such accesses, but this feature is not implemented for stack objects (since
> +    stack objects are allocated and tagged by compiler instrumentation, and
> +    this feature has not yet been implemented in GCC instrumentation).
> +
> +    Another outcome of this 16 byte resolution is that each tagged object must
> +    be 16 byte aligned.  If two objects were to share any 16 byte granule in
> +    memory, then they both would have to be given the same tag, and invalid
> +    accesses to one using a pointer to the other would be undetectable.
> +
> +   [Compiler instrumentation]
> +    Compiler instrumentation ensures that two adjacent buffers on the stack are
> +    given different tags, this means an access to one buffer using a pointer
> +    generated from the other (e.g. through buffer overrun) will have mismatched
> +    tags and be caught by hwasan.
> +
> +    We don't randomly tag every object on the stack, since that would require
> +    keeping many registers to record each tag.  Instead we randomly generate a
> +    tag for each function frame, and each new stack object uses a tag offset
> +    from that frame tag.
> +    i.e. each object is tagged as RFT + offset, where RFT is the "random frame
> +    tag" generated for this frame.

So randomisation isn't effective at perturbing the pairs of mismatching
tags within a frame, but that is mitigated by objects with the same tag
within a frame being very far apart.  Is that right?

> +    As a demonstration, using the same example program as in the asan block
> +    comment above:
> +
> +     int
> +     foo ()
> +     {
> +       char a[23] = {0};
> +       int b[2] = {0};
> +
> +       a[5] = 1;
> +       b[1] = 2;
> +
> +       return a[5] + b[1];
> +     }
> +
> +    On AArch64 the stack will be ordered as follows for the above function:
> +
> +    Slot 1/ [24 bytes for variable 'a']
> +    Slot 2/ [8 bytes padding for alignment]
> +    Slot 3/ [8 bytes for variable 'b']
> +    Slot 4/ [8 bytes padding for alignment]
> +
> +    (The padding is there to ensure 16 byte alignment as described in the 16
> +     byte granule implications).
> +
> +    While the shadow memory will be ordered as follows:
> +
> +    - 2 bytes (representing 32 bytes in real memory) tagged with RFT + 1.
> +    - 1 byte (representing 16 bytes in real memory) tagged with RFT + 2.
> +
> +    And any pointer to "a" will have the tag RFT + 1, and any pointer to "b"
> +    will have the tag RFT + 2.
> +
> +   [Top Byte Ignore requirements]
> +    Hwasan requires the ability to store an 8 bit tag in every pointer.  There
> +    is no instrumentation done to remove this tag from pointers before
> +    dereferencing, which means the hardware must ignore this tag during memory
> +    accesses.
> +
> +    One architecture that provides this feature is the AArch64 architecture.
> +    This is the only architecture that hwasan is currently implemented for.

This could easily get out of date.  Maybe it would be better to refer to
the target hook instead.

> +
> +   [Stack requires cleanup on unwinding]
> +    During normal operation of a hwasan sanitized program more space in the
> +    shadow memory becomes tagged as the stack grows.  As the stack shrinks this
> +    shadow memory space must become untagged.  If it is not untagged then when
> +    the stack grows again (during other function calls later on in the program)
> +    objects on the stack that are usually not tagged (e.g. parameters passed on
> +    the stack) can be placed in memory whose shadow space is tagged with
> +    something else, and accesses can cause false positive reports.
> +
> +    Hence we place untagging code on every epilogue of functions which tag some
> +    stack objects.
> +
> +    Moreover, the run-time library intercepts longjmp & setjmp to uncolour

uncolor

> +    when the stack is unwound this way.
> +
> +    C++ exceptions are not yet handled, which means this sanitizer can not
> +    handle C++ code that throws exceptions -- it will give false positives
> +    after an exception has been thrown.  */

Is this a limitation in the library as well as the compiler side?
Might be worth clarifying in the comment.

But thanks for this, it's a really nice write-up.

> @@ -2301,7 +2620,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
>      {
>        if (DECL_THREAD_LOCAL_P (inner))
>  	return;
> -      if (!param_asan_globals && is_global_var (inner))
> +      if ((hwasan_sanitize_p () || !param_asan_globals)
> +	  && is_global_var (inner))
>          return;
>        if (!TREE_STATIC (inner))
>  	{

Could you expand on this?

> @@ -2530,10 +2850,13 @@ maybe_instrument_call (gimple_stmt_iterator *iter)
>  	      break;
>  	    }
>  	}
> -      tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
> -      gimple *g = gimple_build_call (decl, 0);
> -      gimple_set_location (g, gimple_location (stmt));
> -      gsi_insert_before (iter, g, GSI_SAME_STMT);
> +      if (! hwasan_sanitize_p ())
> +	{
> +	  tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
> +	  gimple *g = gimple_build_call (decl, 0);
> +	  gimple_set_location (g, gimple_location (stmt));
> +	  gsi_insert_before (iter, g, GSI_SAME_STMT);
> +	}

Why is this only needed for asan?  (Just curious.)

> @@ -3255,18 +3581,34 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
>  
>    gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
>  
> +  tree len = gimple_call_arg (g, 2);
> +  gcc_assert (tree_fits_shwi_p (len));
> +  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
> +  gcc_assert (size_in_bytes);
> +
> +  if (hwasan_sanitize_p ())
> +    {
> +      gcc_assert (param_hwasan_instrument_stack);
> +      /* Here we swap ASAN_MARK calls for HWASAN_MARK.
> +	 This is because we are using the approach of using ASAN_MARK as a
> +	 synonym until here.
> +	 That approach means we don't yet have to duplicate all the special
> +	 cases for ASAN_MARK and ASAN_POISON with the exact same handling but
> +	 called HWASAN_MARK etc.  */

Definitely in favour of that :-)

> +      gimple *hw_poison_call
> +	= gimple_build_call_internal (IFN_HWASAN_MARK, 3,
> +				      gimple_call_arg (g, 0),
> +				      base, len);
> +      gsi_replace (iter, hw_poison_call, false);
> +      return false;
> +    }
> +
>    if (is_poison)
>      {
>        if (asan_handled_variables == NULL)
>  	asan_handled_variables = new hash_set<tree> (16);
>        asan_handled_variables->add (decl);
>      }
> -  tree len = gimple_call_arg (g, 2);
> -
> -  gcc_assert (tree_fits_shwi_p (len));
> -  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
> -  gcc_assert (size_in_bytes);
> -
>    g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
>  			   NOP_EXPR, base);
>    gimple_set_location (g, loc);
> @@ -3329,6 +3671,7 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
>  bool
>  asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
>  {
> +  gcc_assert (!hwasan_sanitize_p ());
>    gimple *g = gsi_stmt (*iter);
>    location_t loc = gimple_location (g);
>    bool recover_p;
> @@ -3602,11 +3945,66 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
>  
>        int nargs;
>        bool store_p = gimple_call_internal_p (use, IFN_ASAN_POISON_USE);
> -      tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
> -				    &nargs);
> -
> -      gcall *call = gimple_build_call (fun, 1,
> -				       build_fold_addr_expr (shadow_var));
> +      gcall *call;
> +      if (hwasan_sanitize_p ())
> +	{
> +	  tree fun = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MISMATCH4);
> +	  /* NOTE: hwasan has no __hwasan_report_* functions like asan does.
> +		We use __hwasan_tag_mismatch4 with arguments that tell it the
> +		size of access and load to report all tag mismatches.
> +
> +		The arguments to this function are:
> +		  Address of invalid access.
> +		  Bitfield containing information about the access
> +		    (access_info)
> +		  Pointer to a frame of registers
> +		    (for use in printing the contents of registers in a dump)
> +		    Not used yet -- to be used by inline instrumentation.
> +		  Size of access (only if size is not representable in the
> +		    access_info argument).
> +
> +		The access_info bitfield encodes the following pieces of
> +		information:
> +		  - Is this a store or load?
> +		    access_info & 0x10  =>  store
> +		  - Should the program continue after reporting the error?
> +		    access_info & 0x20  =>  recover
> +		  - What size access is this (if size is less than 16 bytes)
> +		    if (access_info & 0xf == 0xf)
> +		      size is taken from last argument.
> +		    else
> +		      size == 1 << (access_info & 0xf)
> +
> +		The last argument contains the size of the access iff the
> +		access was of a size greater than or equal to 16 bytes.
> +
> +		See the function definition `__hwasan_tag_mismatch4` in
> +		libsanitizer/hwasan for the full definition.
> +		*/
> +	  unsigned HOST_WIDE_INT size_in_bytes = tree_to_uhwi (size);
> +	  unsigned size_indicator = (size_in_bytes > 16)
> +	    ? 0xf
> +	    : exact_log2 (size_in_bytes);

The parts about “16 bytes” don't seem to match the equation;
seems like it's a power of 2 in the range [1, 16384].

Is there any benefit to using the size field encoded in the access_info here?
AIUI it was retained for compatibility with pre-__hwasan_tag_mismatch4
versions of the library.  Seems like we could always use 0xf instead.

> +	  unsigned access_info = (0x20 * recover_p)
> +	    + (0x10 * store_p)
> +	    + (size_indicator);
> +	  tree long_pointer_type
> +	    = build_pointer_type (long_unsigned_type_node);

pointer_sized_int_type seems more appropriate, given that the second
argument and the target of the third argument are uptrs.

> +	  call = gimple_build_call (fun, 3,
> +				    build_fold_addr_expr (shadow_var),
> +				    build_int_cst (long_unsigned_type_node,
> +						   access_info),
> +				    build_int_cst (long_pointer_type,
> +						   0),
> +				    size);
> +	}
> +      else
> +	{
> +	  tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
> +					&nargs);
> +	  call = gimple_build_call (fun, 1,
> +				    build_fold_addr_expr (shadow_var));
> +	}
>        gimple_set_location (call, gimple_location (use));
>        gimple *call_to_insert = call;
>  
> @@ -4005,4 +4425,216 @@ hwasan_finish_file (void)
>    flag_sanitize |= SANITIZE_HWADDRESS;
>  }
>  
> +/* Construct a function tree for __hwasan_{load,store}{1,2,4,8,16,_n}.
> +   IS_STORE is either 1 (for a store) or 0 (for a load).  */
> +static tree
> +hwasan_check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
> +		   int *nargs)
> +{
> +  static enum built_in_function check[2][2][6]
> +    = { { { BUILT_IN_HWASAN_LOAD1, BUILT_IN_HWASAN_LOAD2,
> +	    BUILT_IN_HWASAN_LOAD4, BUILT_IN_HWASAN_LOAD8,
> +	    BUILT_IN_HWASAN_LOAD16, BUILT_IN_HWASAN_LOADN },
> +	  { BUILT_IN_HWASAN_STORE1, BUILT_IN_HWASAN_STORE2,
> +	    BUILT_IN_HWASAN_STORE4, BUILT_IN_HWASAN_STORE8,
> +	    BUILT_IN_HWASAN_STORE16, BUILT_IN_HWASAN_STOREN } },
> +	{ { BUILT_IN_HWASAN_LOAD1_NOABORT,
> +	    BUILT_IN_HWASAN_LOAD2_NOABORT,
> +	    BUILT_IN_HWASAN_LOAD4_NOABORT,
> +	    BUILT_IN_HWASAN_LOAD8_NOABORT,
> +	    BUILT_IN_HWASAN_LOAD16_NOABORT,
> +	    BUILT_IN_HWASAN_LOADN_NOABORT },
> +	  { BUILT_IN_HWASAN_STORE1_NOABORT,
> +	    BUILT_IN_HWASAN_STORE2_NOABORT,
> +	    BUILT_IN_HWASAN_STORE4_NOABORT,
> +	    BUILT_IN_HWASAN_STORE8_NOABORT,
> +	    BUILT_IN_HWASAN_STORE16_NOABORT,
> +	    BUILT_IN_HWASAN_STOREN_NOABORT } } };
> +  if (size_in_bytes == -1)
> +    {
> +      *nargs = 2;
> +      return builtin_decl_implicit (check[recover_p][is_store][5]);
> +    }
> +  *nargs = 1;
> +  int size_log2 = exact_log2 (size_in_bytes);
> +  return builtin_decl_implicit (check[recover_p][is_store][size_log2]);

I don't follow the logic here.  Why is it the case that we have to
handle size_in_bytes == -1, but are otherwise guaranteed that
size_in_bytes is in [1, 2, 4, 8, 16]?  I.e. why is the tree_fits_shwi_p
call needed here:

> +  unsigned HOST_WIDE_INT size_in_bytes
> +    = is_scalar_access && tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;

?  If is_scalar_access guarantees a valid value then I think we should
drop the tree_fits_shwi_p check above and instead assert that size_log2
is in range.  If is_scalar_access doesn't guarantee a valid value then
I think we should check size_log2 and set *nargs to 2 for out-of-range
values.

> +}
> +
> +/* Expand the HWASAN_{LOAD,STORE} builtins.  */
> +bool
> +hwasan_expand_check_ifn (gimple_stmt_iterator *iter, bool)
> +{
> +  gimple *g = gsi_stmt (*iter);
> +  location_t loc = gimple_location (g);
> +  bool recover_p;
> +  if (flag_sanitize & SANITIZE_USER_HWADDRESS)
> +    recover_p = (flag_sanitize_recover & SANITIZE_USER_HWADDRESS) != 0;
> +  else
> +    recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_HWADDRESS) != 0;
> +
> +  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
> +  gcc_assert (flags < ASAN_CHECK_LAST);
> +  bool is_scalar_access = (flags & ASAN_CHECK_SCALAR_ACCESS) != 0;
> +  bool is_store = (flags & ASAN_CHECK_STORE) != 0;
> +  bool is_non_zero_len = (flags & ASAN_CHECK_NON_ZERO_LEN) != 0;
> +
> +  tree base = gimple_call_arg (g, 1);
> +  tree len = gimple_call_arg (g, 2);
> +
> +  /* `align` is unused for HWASAN_CHECK, but I pass the argument anyway

s/I/we/.

> +     since that way the arguments match ASAN_CHECK.  */
> +  /* HOST_WIDE_INT align = tree_to_shwi (gimple_call_arg (g, 3));  */
> +
> +  unsigned HOST_WIDE_INT size_in_bytes
> +    = is_scalar_access && tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
> +
> +  gimple_stmt_iterator gsi = *iter;
> +
> +  if (!is_non_zero_len)
> +    {
> +      /* So, the length of the memory area to hwasan-protect is
> +	 non-constant.  Let's guard the generated instrumentation code
> +	 like:
> +
> +	 if (len != 0)
> +	   {
> +	     // hwasan instrumentation code goes here.
> +	   }
> +	 // falltrough instructions, starting with *ITER.  */
> +
> +      g = gimple_build_cond (NE_EXPR,
> +			    len,
> +			    build_int_cst (TREE_TYPE (len), 0),
> +			    NULL_TREE, NULL_TREE);
> +      gimple_set_location (g, loc);
> +
> +      basic_block then_bb, fallthrough_bb;
> +      insert_if_then_before_iter (as_a <gcond *> (g), iter,
> +				  /*then_more_likely_p=*/true,
> +				  &then_bb, &fallthrough_bb);
> +      /* Note that fallthrough_bb starts with the statement that was
> +	pointed to by ITER.  */
> +
> +      /* The 'then block' of the 'if (len != 0) condition is where
> +	we'll generate the hwasan instrumentation code now.  */
> +      gsi = gsi_last_bb (then_bb);
> +    }
> +
> +  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
> +			   NOP_EXPR, base);
> +  gimple_set_location (g, loc);
> +  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
> +  tree base_addr = gimple_assign_lhs (g);
> +
> +  int nargs = 0;
> +  tree fun = hwasan_check_func (is_store, recover_p, size_in_bytes, &nargs);
> +  if (nargs == 1)
> +    g = gimple_build_call (fun, 1, base_addr);
> +  else
> +    {
> +      gcc_assert (nargs == 2);
> +      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
> +			       NOP_EXPR, len);
> +      gimple_set_location (g, loc);
> +      gsi_insert_after (&gsi, g, GSI_NEW_STMT);
> +      tree sz_arg = gimple_assign_lhs (g);
> +      g = gimple_build_call (fun, nargs, base_addr, sz_arg);
> +    }
> +
> +  gimple_set_location (g, loc);
> +  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
> +  gsi_remove (iter, true);
> +  *iter = gsi;
> +  return false;
> +}
> +
> +/* For stack tagging:
> +     Dummy: the HWASAN_MARK internal function should only ever be in the code
> +     after the sanopt pass.  */

Same nit as the previous part about this style of comment formatting.

> +bool
> +hwasan_expand_mark_ifn (gimple_stmt_iterator *)
> +{
> +  gcc_unreachable ();
> +}
> +
> +bool
> +gate_hwasan ()
> +{
> +  return hwasan_sanitize_p ();
> +}
> +
> +namespace {
> +
> +const pass_data pass_data_hwasan =
> +{
> +  GIMPLE_PASS, /* type */
> +  "hwasan", /* name */
> +  OPTGROUP_NONE, /* optinfo_flags */
> +  TV_NONE, /* tv_id */
> +  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
> +  0, /* properties_provided */
> +  0, /* properties_destroyed */
> +  0, /* todo_flags_start */
> +  TODO_update_ssa, /* todo_flags_finish */
> +};
> +
> +class pass_hwasan : public gimple_opt_pass
> +{
> +public:
> +  pass_hwasan (gcc::context *ctxt)
> +    : gimple_opt_pass (pass_data_hwasan, ctxt)
> +  {}
> +
> +  /* opt_pass methods: */
> +  opt_pass * clone () { return new pass_hwasan (m_ctxt); }
> +  virtual bool gate (function *) { return gate_hwasan (); }
> +  virtual unsigned int execute (function *) { return hwasan_instrument (); }
> +
> +}; /* class pass_hwasan  */
> +
> +} /* anon namespace  */
> +
> +gimple_opt_pass *
> +make_pass_hwasan (gcc::context *ctxt)
> +{
> +  return new pass_hwasan (ctxt);
> +}
> +
> +namespace {
> +
> +const pass_data pass_data_hwasan_O0 =
> +{
> +  GIMPLE_PASS, /* type */
> +  "hwasan_O0", /* name */
> +  OPTGROUP_NONE, /* optinfo_flags */
> +  TV_NONE, /* tv_id */
> +  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
> +  0, /* properties_provided */
> +  0, /* properties_destroyed */
> +  0, /* todo_flags_start */
> +  TODO_update_ssa, /* todo_flags_finish */
> +};
> +
> +class pass_hwasan_O0 : public gimple_opt_pass
> +{
> +public:
> +  pass_hwasan_O0 (gcc::context *ctxt)
> +    : gimple_opt_pass (pass_data_hwasan_O0, ctxt)
> +  {}
> +
> +  /* opt_pass methods: */
> +  opt_pass * clone () { return new pass_hwasan_O0 (m_ctxt); }
> +  virtual bool gate (function *) { return !optimize && gate_hwasan (); }
> +  virtual unsigned int execute (function *) { return hwasan_instrument (); }
> +
> +}; /* class pass_hwasan */
> +
> +} /* anon namespace */
> +
> +gimple_opt_pass *
> +make_pass_hwasan_O0 (gcc::context *ctxt)
> +{
> +  return new pass_hwasan_O0 (ctxt);
> +}

Is it worth creating separate passes for this?  Seems like we could
just fork the implementation of the asan ones based on whether we're
using hwasan or not.

> diff --git a/gcc/gimplify.c b/gcc/gimplify.c
> index 15dfee903ab298f6a02e45d1affcc2260f3c911d..24ebedd490aea4ad634a92aa5742a83b1b0c0bb7 100644
> --- a/gcc/gimplify.c
> +++ b/gcc/gimplify.c
> @@ -1234,8 +1234,11 @@ asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
>  
>    /* It's necessary to have all stack variables aligned to ASAN granularity
>       bytes.  */
> -  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
> -    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
> +  gcc_assert (!hwasan_sanitize_p () || hwasan_sanitize_stack_p ());
> +  unsigned shadow_granularity
> +    = hwasan_sanitize_p () ? HWASAN_TAG_GRANULE_SIZE : ASAN_SHADOW_GRANULARITY;
> +  if (DECL_ALIGN_UNIT (decl) <= shadow_granularity)
> +    SET_DECL_ALIGN (decl, BITS_PER_UNIT * shadow_granularity);
>  
>    HOST_WIDE_INT flags = poison ? ASAN_MARK_POISON : ASAN_MARK_UNPOISON;
>  
> @@ -15023,7 +15026,7 @@ gimplify_function_tree (tree fndecl)
>       if necessary.  */
>    cfun->curr_properties |= PROP_gimple_lva;
>  
> -  if (asan_sanitize_use_after_scope () && sanitize_flags_p (SANITIZE_ADDRESS))
> +  if (asan_sanitize_use_after_scope ())
>      asan_poisoned_variables = new hash_set<tree> ();
>    bind = gimplify_body (fndecl, true);
>    if (asan_poisoned_variables)
> diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
> index 8efc77d986b927dc4a37e396e6c710ffeda663ff..3397fa355f373f3ead5a02a07838a467f08f03c4 100644
> --- a/gcc/internal-fn.c
> +++ b/gcc/internal-fn.c
> @@ -467,6 +467,105 @@ expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
>  /* This should get expanded in the sanopt pass.  */
>  
>  static void
> +expand_HWASAN_CHECK (internal_fn, gcall *)
> +{
> +  gcc_unreachable ();
> +}
> +
> +/* For hwasan stack tagging:
> +     Clear tags on the dynamically allocated space.
> +     For use after an object dynamically allocated on the stack goes out of
> +     scope.  */
> +static void
> +expand_HWASAN_ALLOCA_UNPOISON (internal_fn, gcall *gc)
> +{
> +  tree restored_position = gimple_call_arg (gc, 0);
> +  rtx restored_rtx = expand_expr (restored_position, NULL_RTX, VOIDmode,
> +				  EXPAND_NORMAL);
> +  rtx func = init_one_libfunc ("__hwasan_tag_memory");
> +  rtx off = expand_simple_binop (ptr_mode, MINUS, restored_rtx,
> +				 stack_pointer_rtx, NULL_RTX, 0,
> +				 OPTAB_LIB_WIDEN);
> +  rtx dynamic = convert_memory_address (ptr_mode, virtual_stack_dynamic_rtx);
> +  emit_library_call_value (func, NULL_RTX, LCT_NORMAL, VOIDmode,
> +			   dynamic, ptr_mode,
> +			   const0_rtx, QImode,
> +			   off, ptr_mode);
> +}
> +
> +/* For hwasan stack tagging:
> +     Return a tag to be used for a dynamic allocation.  */
> +static void
> +expand_HWASAN_CHOOSE_TAG (internal_fn, gcall *gc)
> +{
> +  tree tag = gimple_call_lhs (gc);
> +  rtx target = expand_expr (tag, NULL_RTX, VOIDmode, EXPAND_NORMAL);
> +  machine_mode mode = GET_MODE (target);
> +  gcc_assert (mode == QImode);
> +
> +  rtx base_tag = hwasan_extract_tag (hwasan_base ());
> +  gcc_assert (base_tag);
> +  rtx tag_offset = GEN_INT (hwasan_current_tag ());
> +  rtx chosen_tag = expand_simple_binop (QImode, PLUS, base_tag, tag_offset,
> +					target, /* unsignedp = */1,
> +					OPTAB_WIDEN);
> +
> +  gcc_assert (chosen_tag);
> +  /* Really need to put the tag into the `target` RTX.  */
> +  if (chosen_tag != target)
> +    {
> +      rtx temp = chosen_tag;
> +      machine_mode ret_mode = GET_MODE (chosen_tag);
> +      if (ret_mode != mode)
> +	temp = simplify_gen_unary (TRUNCATE, mode, chosen_tag, ret_mode);

Is this truncation necessary?  I'd have expected:

  expand_simple_binop (QImode, …)

to always return a QImode value, even for the widening path.

> +/* For hwasan stack tagging:
> +     Tag a region of space in the shadow stack according to the base pointer of
> +     an object on the stack.  */
> +static void
> +expand_HWASAN_MARK (internal_fn, gcall *gc)
> +{
> +  HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
> +  bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
> +
> +  tree base = gimple_call_arg (gc, 1);
> +  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
> +  /* base is a pointer argument, hence in ptr_mode.
> +     We convert to Pmode for use in the hwasan_extract_tag and
> +     hwasan_create_untagged_base functions.
> +     We then convert the result to ptr_mode for the emit_library_call.  */

How does this ptr_mode<->Pmode stuff work for ILP32?  Wouldn't the tag
get lost by the Pmode->ptr_mode conversion?  Or does something ensure
that the tag is moved from bits 24+ to bits 56+ and back?

> +  rtx base_rtx = convert_memory_address (Pmode, expand_normal (base));
> +
> +  rtx tag = is_poison ? const0_rtx : hwasan_extract_tag (base_rtx);
> +  rtx address = hwasan_create_untagged_base (base_rtx);
> +  address = convert_memory_address (ptr_mode, address);
> +
> +  tree len = gimple_call_arg (gc, 2);
> +  gcc_assert (tree_fits_shwi_p (len));

Why's this guaranteed to be a SHWI?  For example, I couldn't see
anything that explicitly stopped calls to build_asan_poison_call_expr
with poly_int- or variable-sized decls.

Could we expand this to an aligned size in gimple, when generating
the HWASAN_MARK call?  E.g. we could reuse the alloca code for
computing an aligned size.

It'd probably be worth using the gimple_build routines in
gimple-fold.h to build the calculation, instead of calling
gimple_build_assign directly.  The operations will then get
folded on the fly.

> +  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
> +  uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
> +  gcc_assert (size_in_bytes);
> +  size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
> +  rtx size = gen_int_mode (size_in_bytes, ptr_mode);
> +
> +  rtx func = init_one_libfunc ("__hwasan_tag_memory");
> +  emit_library_call (func,
> +      LCT_NORMAL,
> +      VOIDmode,
> +      address, ptr_mode,
> +      tag, QImode,
> +      size, ptr_mode);

Formatting nit: should be indented under “func”.  Seems at least
LCT_NORMAL and VOIDmode could go on the same line as “func”.

Generally looks really good though (this and previous patches).

Thanks,
Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [Patch 0/X] HWASAN v4
  2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
@ 2020-10-16  9:03       ` Martin Liška
  2020-11-13 16:33         ` Martin Liška
  2020-11-16 15:36       ` Hwasan v5 Matthew Malcomson
                         ` (8 subsequent siblings)
  9 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2020-10-16  9:03 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: kcc, Richard Sandiford, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji

[-- Attachment #1: Type: text/plain, Size: 106 bytes --]

Hello.

I've just merged libsanitizer and there's the corresponding part that includes
libhwasan.

Martin

[-- Attachment #2: 0001-libsanitizer-merge-hwasan.patch --]
[-- Type: text/x-patch, Size: 168940 bytes --]

From 3a5bd3e4d00440e61e780e0549c57a6504459114 Mon Sep 17 00:00:00 2001
From: Martin Liska <mliska@suse.cz>
Date: Fri, 16 Oct 2020 10:26:08 +0200
Subject: [PATCH] libsanitizer: merge hwasan.

---
 libsanitizer/hwasan/hwasan.cpp                | 522 ++++++++++++++
 libsanitizer/hwasan/hwasan.h                  | 165 +++++
 libsanitizer/hwasan/hwasan_allocator.cpp      | 406 +++++++++++
 libsanitizer/hwasan/hwasan_allocator.h        |  99 +++
 libsanitizer/hwasan/hwasan_checks.h           | 124 ++++
 libsanitizer/hwasan/hwasan_dynamic_shadow.cpp | 126 ++++
 libsanitizer/hwasan/hwasan_dynamic_shadow.h   |  27 +
 libsanitizer/hwasan/hwasan_exceptions.cpp     |  67 ++
 libsanitizer/hwasan/hwasan_flags.h            |  29 +
 libsanitizer/hwasan/hwasan_flags.inc          |  74 ++
 libsanitizer/hwasan/hwasan_globals.cpp        |  91 +++
 libsanitizer/hwasan/hwasan_globals.h          |  49 ++
 libsanitizer/hwasan/hwasan_interceptors.cpp   | 349 ++++++++++
 .../hwasan/hwasan_interceptors_vfork.S        |  11 +
 .../hwasan/hwasan_interface_internal.h        | 227 ++++++
 libsanitizer/hwasan/hwasan_linux.cpp          | 455 ++++++++++++
 libsanitizer/hwasan/hwasan_malloc_bisect.h    |  50 ++
 libsanitizer/hwasan/hwasan_mapping.h          |  66 ++
 libsanitizer/hwasan/hwasan_memintrinsics.cpp  |  44 ++
 libsanitizer/hwasan/hwasan_new_delete.cpp     |  66 ++
 libsanitizer/hwasan/hwasan_poisoning.cpp      |  52 ++
 libsanitizer/hwasan/hwasan_poisoning.h        |  24 +
 libsanitizer/hwasan/hwasan_report.cpp         | 651 ++++++++++++++++++
 libsanitizer/hwasan/hwasan_report.h           |  35 +
 libsanitizer/hwasan/hwasan_setjmp.S           | 100 +++
 .../hwasan/hwasan_tag_mismatch_aarch64.S      | 152 ++++
 libsanitizer/hwasan/hwasan_thread.cpp         | 133 ++++
 libsanitizer/hwasan/hwasan_thread.h           |  98 +++
 libsanitizer/hwasan/hwasan_thread_list.cpp    |  15 +
 libsanitizer/hwasan/hwasan_thread_list.h      | 215 ++++++
 libsanitizer/hwasan/hwasan_type_test.cpp      |  25 +
 libsanitizer/merge.sh                         |   1 +
 32 files changed, 4548 insertions(+)
 create mode 100644 libsanitizer/hwasan/hwasan.cpp
 create mode 100644 libsanitizer/hwasan/hwasan.h
 create mode 100644 libsanitizer/hwasan/hwasan_allocator.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_allocator.h
 create mode 100644 libsanitizer/hwasan/hwasan_checks.h
 create mode 100644 libsanitizer/hwasan/hwasan_dynamic_shadow.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_dynamic_shadow.h
 create mode 100644 libsanitizer/hwasan/hwasan_exceptions.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_flags.h
 create mode 100644 libsanitizer/hwasan/hwasan_flags.inc
 create mode 100644 libsanitizer/hwasan/hwasan_globals.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_globals.h
 create mode 100644 libsanitizer/hwasan/hwasan_interceptors.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_interceptors_vfork.S
 create mode 100644 libsanitizer/hwasan/hwasan_interface_internal.h
 create mode 100644 libsanitizer/hwasan/hwasan_linux.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_malloc_bisect.h
 create mode 100644 libsanitizer/hwasan/hwasan_mapping.h
 create mode 100644 libsanitizer/hwasan/hwasan_memintrinsics.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_new_delete.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_poisoning.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_poisoning.h
 create mode 100644 libsanitizer/hwasan/hwasan_report.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_report.h
 create mode 100644 libsanitizer/hwasan/hwasan_setjmp.S
 create mode 100644 libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S
 create mode 100644 libsanitizer/hwasan/hwasan_thread.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_thread.h
 create mode 100644 libsanitizer/hwasan/hwasan_thread_list.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_thread_list.h
 create mode 100644 libsanitizer/hwasan/hwasan_type_test.cpp

diff --git a/libsanitizer/hwasan/hwasan.cpp b/libsanitizer/hwasan/hwasan.cpp
new file mode 100644
index 00000000000..c5322110cb6
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan.cpp
@@ -0,0 +1,522 @@
+//===-- hwasan.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer runtime.
+//===----------------------------------------------------------------------===//
+
+#include "hwasan.h"
+
+#include "hwasan_checks.h"
+#include "hwasan_dynamic_shadow.h"
+#include "hwasan_globals.h"
+#include "hwasan_poisoning.h"
+#include "hwasan_report.h"
+#include "hwasan_thread.h"
+#include "hwasan_thread_list.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+#include "ubsan/ubsan_flags.h"
+#include "ubsan/ubsan_init.h"
+
+// ACHTUNG! No system header includes in this file.
+
+using namespace __sanitizer;
+
+namespace __hwasan {
+
+static Flags hwasan_flags;
+
+Flags *flags() {
+  return &hwasan_flags;
+}
+
+int hwasan_inited = 0;
+int hwasan_instrumentation_inited = 0;
+bool hwasan_init_is_running;
+
+int hwasan_report_count = 0;
+
+void Flags::SetDefaults() {
+#define HWASAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "hwasan_flags.inc"
+#undef HWASAN_FLAG
+}
+
+static void RegisterHwasanFlags(FlagParser *parser, Flags *f) {
+#define HWASAN_FLAG(Type, Name, DefaultValue, Description) \
+  RegisterFlag(parser, #Name, Description, &f->Name);
+#include "hwasan_flags.inc"
+#undef HWASAN_FLAG
+}
+
+static void InitializeFlags() {
+  SetCommonFlagsDefaults();
+  {
+    CommonFlags cf;
+    cf.CopyFrom(*common_flags());
+    cf.external_symbolizer_path = GetEnv("HWASAN_SYMBOLIZER_PATH");
+    cf.malloc_context_size = 20;
+    cf.handle_ioctl = true;
+    // FIXME: test and enable.
+    cf.check_printf = false;
+    cf.intercept_tls_get_addr = true;
+    cf.exitcode = 99;
+    // 8 shadow pages ~512kB, small enough to cover common stack sizes.
+    cf.clear_shadow_mmap_threshold = 4096 * (SANITIZER_ANDROID ? 2 : 8);
+    // Sigtrap is used in error reporting.
+    cf.handle_sigtrap = kHandleSignalExclusive;
+
+#if SANITIZER_ANDROID
+    // Let platform handle other signals. It is better at reporting them then we
+    // are.
+    cf.handle_segv = kHandleSignalNo;
+    cf.handle_sigbus = kHandleSignalNo;
+    cf.handle_abort = kHandleSignalNo;
+    cf.handle_sigill = kHandleSignalNo;
+    cf.handle_sigfpe = kHandleSignalNo;
+#endif
+    OverrideCommonFlags(cf);
+  }
+
+  Flags *f = flags();
+  f->SetDefaults();
+
+  FlagParser parser;
+  RegisterHwasanFlags(&parser, f);
+  RegisterCommonFlags(&parser);
+
+#if HWASAN_CONTAINS_UBSAN
+  __ubsan::Flags *uf = __ubsan::flags();
+  uf->SetDefaults();
+
+  FlagParser ubsan_parser;
+  __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
+  RegisterCommonFlags(&ubsan_parser);
+#endif
+
+  // Override from user-specified string.
+  if (__hwasan_default_options)
+    parser.ParseString(__hwasan_default_options());
+#if HWASAN_CONTAINS_UBSAN
+  const char *ubsan_default_options = __ubsan_default_options();
+  ubsan_parser.ParseString(ubsan_default_options);
+#endif
+
+  parser.ParseStringFromEnv("HWASAN_OPTIONS");
+#if HWASAN_CONTAINS_UBSAN
+  ubsan_parser.ParseStringFromEnv("UBSAN_OPTIONS");
+#endif
+
+  InitializeCommonFlags();
+
+  if (Verbosity()) ReportUnrecognizedFlags();
+
+  if (common_flags()->help) parser.PrintFlagDescriptions();
+}
+
+static void HWAsanCheckFailed(const char *file, int line, const char *cond,
+                              u64 v1, u64 v2) {
+  Report("HWAddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file,
+         line, cond, (uptr)v1, (uptr)v2);
+  PRINT_CURRENT_STACK_CHECK();
+  Die();
+}
+
+static constexpr uptr kMemoryUsageBufferSize = 4096;
+
+static void HwasanFormatMemoryUsage(InternalScopedString &s) {
+  HwasanThreadList &thread_list = hwasanThreadList();
+  auto thread_stats = thread_list.GetThreadStats();
+  auto *sds = StackDepotGetStats();
+  AllocatorStatCounters asc;
+  GetAllocatorStats(asc);
+  s.append(
+      "HWASAN pid: %d rss: %zd threads: %zd stacks: %zd"
+      " thr_aux: %zd stack_depot: %zd uniq_stacks: %zd"
+      " heap: %zd",
+      internal_getpid(), GetRSS(), thread_stats.n_live_threads,
+      thread_stats.total_stack_size,
+      thread_stats.n_live_threads * thread_list.MemoryUsedPerThread(),
+      sds->allocated, sds->n_uniq_ids, asc[AllocatorStatMapped]);
+}
+
+#if SANITIZER_ANDROID
+static char *memory_usage_buffer = nullptr;
+
+static void InitMemoryUsage() {
+  memory_usage_buffer =
+      (char *)MmapOrDie(kMemoryUsageBufferSize, "memory usage string");
+  CHECK(memory_usage_buffer);
+  memory_usage_buffer[0] = '\0';
+  DecorateMapping((uptr)memory_usage_buffer, kMemoryUsageBufferSize,
+                  memory_usage_buffer);
+}
+
+void UpdateMemoryUsage() {
+  if (!flags()->export_memory_stats)
+    return;
+  if (!memory_usage_buffer)
+    InitMemoryUsage();
+  InternalScopedString s(kMemoryUsageBufferSize);
+  HwasanFormatMemoryUsage(s);
+  internal_strncpy(memory_usage_buffer, s.data(), kMemoryUsageBufferSize - 1);
+  memory_usage_buffer[kMemoryUsageBufferSize - 1] = '\0';
+}
+#else
+void UpdateMemoryUsage() {}
+#endif
+
+} // namespace __hwasan
+
+using namespace __hwasan;
+
+void __sanitizer::BufferedStackTrace::UnwindImpl(
+    uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
+  Thread *t = GetCurrentThread();
+  if (!t) {
+    // The thread is still being created, or has already been destroyed.
+    size = 0;
+    return;
+  }
+  Unwind(max_depth, pc, bp, context, t->stack_top(), t->stack_bottom(),
+         request_fast);
+}
+
+static bool InitializeSingleGlobal(const hwasan_global &global) {
+  uptr full_granule_size = RoundDownTo(global.size(), 16);
+  TagMemoryAligned(global.addr(), full_granule_size, global.tag());
+  if (global.size() % 16)
+    TagMemoryAligned(global.addr() + full_granule_size, 16, global.size() % 16);
+  return false;
+}
+
+static void InitLoadedGlobals() {
+  dl_iterate_phdr(
+      [](dl_phdr_info *info, size_t /* size */, void * /* data */) -> int {
+        for (const hwasan_global &global : HwasanGlobalsFor(
+                 info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum))
+          InitializeSingleGlobal(global);
+        return 0;
+      },
+      nullptr);
+}
+
+// Prepare to run instrumented code on the main thread.
+static void InitInstrumentation() {
+  if (hwasan_instrumentation_inited) return;
+
+  InitPrctl();
+
+  if (!InitShadow()) {
+    Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
+    DumpProcessMap();
+    Die();
+  }
+
+  InitThreads();
+  hwasanThreadList().CreateCurrentThread();
+
+  hwasan_instrumentation_inited = 1;
+}
+
+// Interface.
+
+uptr __hwasan_shadow_memory_dynamic_address;  // Global interface symbol.
+
+// This function was used by the old frame descriptor mechanism. We keep it
+// around to avoid breaking ABI.
+void __hwasan_init_frames(uptr beg, uptr end) {}
+
+void __hwasan_init_static() {
+  InitShadowGOT();
+  InitInstrumentation();
+
+  // In the non-static code path we call dl_iterate_phdr here. But at this point
+  // libc might not have been initialized enough for dl_iterate_phdr to work.
+  // Fortunately, since this is a statically linked executable we can use the
+  // linker-defined symbol __ehdr_start to find the only relevant set of phdrs.
+  extern ElfW(Ehdr) __ehdr_start;
+  for (const hwasan_global &global : HwasanGlobalsFor(
+           /* base */ 0,
+           reinterpret_cast<const ElfW(Phdr) *>(
+               reinterpret_cast<const char *>(&__ehdr_start) +
+               __ehdr_start.e_phoff),
+           __ehdr_start.e_phnum))
+    InitializeSingleGlobal(global);
+}
+
+void __hwasan_init() {
+  CHECK(!hwasan_init_is_running);
+  if (hwasan_inited) return;
+  hwasan_init_is_running = 1;
+  SanitizerToolName = "HWAddressSanitizer";
+
+  InitTlsSize();
+
+  CacheBinaryName();
+  InitializeFlags();
+
+  // Install tool-specific callbacks in sanitizer_common.
+  SetCheckFailedCallback(HWAsanCheckFailed);
+
+  __sanitizer_set_report_path(common_flags()->log_path);
+
+  AndroidTestTlsSlot();
+
+  DisableCoreDumperIfNecessary();
+
+  InitInstrumentation();
+  InitLoadedGlobals();
+
+  // Needs to be called here because flags()->random_tags might not have been
+  // initialized when InitInstrumentation() was called.
+  GetCurrentThread()->InitRandomState();
+
+  SetPrintfAndReportCallback(AppendToErrorMessageBuffer);
+  // This may call libc -> needs initialized shadow.
+  AndroidLogInit();
+
+  InitializeInterceptors();
+  InstallDeadlySignalHandlers(HwasanOnDeadlySignal);
+  InstallAtExitHandler(); // Needs __cxa_atexit interceptor.
+
+  InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
+
+  HwasanTSDInit();
+  HwasanTSDThreadInit();
+
+  HwasanAllocatorInit();
+
+#if HWASAN_CONTAINS_UBSAN
+  __ubsan::InitAsPlugin();
+#endif
+
+  VPrintf(1, "HWAddressSanitizer init done\n");
+
+  hwasan_init_is_running = 0;
+  hwasan_inited = 1;
+}
+
+void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                             ElfW(Half) phnum) {
+  for (const hwasan_global &global : HwasanGlobalsFor(base, phdr, phnum))
+    InitializeSingleGlobal(global);
+}
+
+void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                               ElfW(Half) phnum) {
+  for (; phnum != 0; ++phdr, --phnum)
+    if (phdr->p_type == PT_LOAD)
+      TagMemory(base + phdr->p_vaddr, phdr->p_memsz, 0);
+}
+
+void __hwasan_print_shadow(const void *p, uptr sz) {
+  uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p));
+  uptr shadow_first = MemToShadow(ptr_raw);
+  uptr shadow_last = MemToShadow(ptr_raw + sz - 1);
+  Printf("HWASan shadow map for %zx .. %zx (pointer tag %x)\n", ptr_raw,
+         ptr_raw + sz, GetTagFromPointer((uptr)p));
+  for (uptr s = shadow_first; s <= shadow_last; ++s)
+    Printf("  %zx: %x\n", ShadowToMem(s), *(tag_t *)s);
+}
+
+sptr __hwasan_test_shadow(const void *p, uptr sz) {
+  if (sz == 0)
+    return -1;
+  tag_t ptr_tag = GetTagFromPointer((uptr)p);
+  uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p));
+  uptr shadow_first = MemToShadow(ptr_raw);
+  uptr shadow_last = MemToShadow(ptr_raw + sz - 1);
+  for (uptr s = shadow_first; s <= shadow_last; ++s)
+    if (*(tag_t *)s != ptr_tag) {
+      sptr offset = ShadowToMem(s) - ptr_raw;
+      return offset < 0 ? 0 : offset;
+    }
+  return -1;
+}
+
+u16 __sanitizer_unaligned_load16(const uu16 *p) {
+  return *p;
+}
+u32 __sanitizer_unaligned_load32(const uu32 *p) {
+  return *p;
+}
+u64 __sanitizer_unaligned_load64(const uu64 *p) {
+  return *p;
+}
+void __sanitizer_unaligned_store16(uu16 *p, u16 x) {
+  *p = x;
+}
+void __sanitizer_unaligned_store32(uu32 *p, u32 x) {
+  *p = x;
+}
+void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
+  *p = x;
+}
+
+void __hwasan_loadN(uptr p, uptr sz) {
+  CheckAddressSized<ErrorAction::Abort, AccessType::Load>(p, sz);
+}
+void __hwasan_load1(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Load, 0>(p);
+}
+void __hwasan_load2(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Load, 1>(p);
+}
+void __hwasan_load4(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Load, 2>(p);
+}
+void __hwasan_load8(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Load, 3>(p);
+}
+void __hwasan_load16(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Load, 4>(p);
+}
+
+void __hwasan_loadN_noabort(uptr p, uptr sz) {
+  CheckAddressSized<ErrorAction::Recover, AccessType::Load>(p, sz);
+}
+void __hwasan_load1_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Load, 0>(p);
+}
+void __hwasan_load2_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Load, 1>(p);
+}
+void __hwasan_load4_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Load, 2>(p);
+}
+void __hwasan_load8_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Load, 3>(p);
+}
+void __hwasan_load16_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Load, 4>(p);
+}
+
+void __hwasan_storeN(uptr p, uptr sz) {
+  CheckAddressSized<ErrorAction::Abort, AccessType::Store>(p, sz);
+}
+void __hwasan_store1(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Store, 0>(p);
+}
+void __hwasan_store2(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Store, 1>(p);
+}
+void __hwasan_store4(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Store, 2>(p);
+}
+void __hwasan_store8(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Store, 3>(p);
+}
+void __hwasan_store16(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Store, 4>(p);
+}
+
+void __hwasan_storeN_noabort(uptr p, uptr sz) {
+  CheckAddressSized<ErrorAction::Recover, AccessType::Store>(p, sz);
+}
+void __hwasan_store1_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Store, 0>(p);
+}
+void __hwasan_store2_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Store, 1>(p);
+}
+void __hwasan_store4_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Store, 2>(p);
+}
+void __hwasan_store8_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Store, 3>(p);
+}
+void __hwasan_store16_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Store, 4>(p);
+}
+
+void __hwasan_tag_memory(uptr p, u8 tag, uptr sz) {
+  TagMemoryAligned(p, sz, tag);
+}
+
+uptr __hwasan_tag_pointer(uptr p, u8 tag) {
+  return AddTagToPointer(p, tag);
+}
+
+void __hwasan_handle_longjmp(const void *sp_dst) {
+  uptr dst = (uptr)sp_dst;
+  // HWASan does not support tagged SP.
+  CHECK(GetTagFromPointer(dst) == 0);
+
+  uptr sp = (uptr)__builtin_frame_address(0);
+  static const uptr kMaxExpectedCleanupSize = 64 << 20;  // 64M
+  if (dst < sp || dst - sp > kMaxExpectedCleanupSize) {
+    Report(
+        "WARNING: HWASan is ignoring requested __hwasan_handle_longjmp: "
+        "stack top: %p; target %p; distance: %p (%zd)\n"
+        "False positive error reports may follow\n",
+        (void *)sp, (void *)dst, dst - sp);
+    return;
+  }
+  TagMemory(sp, dst - sp, 0);
+}
+
+void __hwasan_handle_vfork(const void *sp_dst) {
+  uptr sp = (uptr)sp_dst;
+  Thread *t = GetCurrentThread();
+  CHECK(t);
+  uptr top = t->stack_top();
+  uptr bottom = t->stack_bottom();
+  if (top == 0 || bottom == 0 || sp < bottom || sp >= top) {
+    Report(
+        "WARNING: HWASan is ignoring requested __hwasan_handle_vfork: "
+        "stack top: %zx; current %zx; bottom: %zx \n"
+        "False positive error reports may follow\n",
+        top, sp, bottom);
+    return;
+  }
+  TagMemory(bottom, sp - bottom, 0);
+}
+
+extern "C" void *__hwasan_extra_spill_area() {
+  Thread *t = GetCurrentThread();
+  return &t->vfork_spill();
+}
+
+void __hwasan_print_memory_usage() {
+  InternalScopedString s(kMemoryUsageBufferSize);
+  HwasanFormatMemoryUsage(s);
+  Printf("%s\n", s.data());
+}
+
+static const u8 kFallbackTag = 0xBB;
+
+u8 __hwasan_generate_tag() {
+  Thread *t = GetCurrentThread();
+  if (!t) return kFallbackTag;
+  return t->GenerateRandomTag();
+}
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+const char* __hwasan_default_options() { return ""; }
+}  // extern "C"
+#endif
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_stack_trace() {
+  GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME());
+  stack.Print();
+}
+} // extern "C"
diff --git a/libsanitizer/hwasan/hwasan.h b/libsanitizer/hwasan/hwasan.h
new file mode 100644
index 00000000000..b8b7a1865e8
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan.h
@@ -0,0 +1,165 @@
+//===-- hwasan.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Private Hwasan header.
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_H
+#define HWASAN_H
+
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "hwasan_interface_internal.h"
+#include "hwasan_flags.h"
+#include "ubsan/ubsan_platform.h"
+
+#ifndef HWASAN_REPLACE_OPERATORS_NEW_AND_DELETE
+# define HWASAN_REPLACE_OPERATORS_NEW_AND_DELETE 1
+#endif
+
+#ifndef HWASAN_CONTAINS_UBSAN
+# define HWASAN_CONTAINS_UBSAN CAN_SANITIZE_UB
+#endif
+
+#ifndef HWASAN_WITH_INTERCEPTORS
+#define HWASAN_WITH_INTERCEPTORS 0
+#endif
+
+typedef u8 tag_t;
+
+// TBI (Top Byte Ignore) feature of AArch64: bits [63:56] are ignored in address
+// translation and can be used to store a tag.
+const unsigned kAddressTagShift = 56;
+const uptr kAddressTagMask = 0xFFUL << kAddressTagShift;
+
+// Minimal alignment of the shadow base address. Determines the space available
+// for threads and stack histories. This is an ABI constant.
+const unsigned kShadowBaseAlignment = 32;
+
+const unsigned kRecordAddrBaseTagShift = 3;
+const unsigned kRecordFPShift = 48;
+const unsigned kRecordFPLShift = 4;
+const unsigned kRecordFPModulus = 1 << (64 - kRecordFPShift + kRecordFPLShift);
+
+static inline tag_t GetTagFromPointer(uptr p) {
+  return p >> kAddressTagShift;
+}
+
+static inline uptr UntagAddr(uptr tagged_addr) {
+  return tagged_addr & ~kAddressTagMask;
+}
+
+static inline void *UntagPtr(const void *tagged_ptr) {
+  return reinterpret_cast<void *>(
+      UntagAddr(reinterpret_cast<uptr>(tagged_ptr)));
+}
+
+static inline uptr AddTagToPointer(uptr p, tag_t tag) {
+  return (p & ~kAddressTagMask) | ((uptr)tag << kAddressTagShift);
+}
+
+namespace __hwasan {
+
+extern int hwasan_inited;
+extern bool hwasan_init_is_running;
+extern int hwasan_report_count;
+
+bool InitShadow();
+void InitPrctl();
+void InitThreads();
+void InitializeInterceptors();
+
+void HwasanAllocatorInit();
+
+void *hwasan_malloc(uptr size, StackTrace *stack);
+void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack);
+void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack);
+void *hwasan_reallocarray(void *ptr, uptr nmemb, uptr size, StackTrace *stack);
+void *hwasan_valloc(uptr size, StackTrace *stack);
+void *hwasan_pvalloc(uptr size, StackTrace *stack);
+void *hwasan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack);
+void *hwasan_memalign(uptr alignment, uptr size, StackTrace *stack);
+int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
+                        StackTrace *stack);
+void hwasan_free(void *ptr, StackTrace *stack);
+
+void InstallAtExitHandler();
+
+#define GET_MALLOC_STACK_TRACE                                            \
+  BufferedStackTrace stack;                                               \
+  if (hwasan_inited)                                                      \
+    stack.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(),         \
+                 nullptr, common_flags()->fast_unwind_on_malloc,          \
+                 common_flags()->malloc_context_size)
+
+#define GET_FATAL_STACK_TRACE_PC_BP(pc, bp)              \
+  BufferedStackTrace stack;                              \
+  if (hwasan_inited)                                     \
+    stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal)
+
+#define GET_FATAL_STACK_TRACE_HERE \
+  GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME())
+
+#define PRINT_CURRENT_STACK_CHECK() \
+  {                                 \
+    GET_FATAL_STACK_TRACE_HERE;     \
+    stack.Print();                  \
+  }
+
+void HwasanTSDInit();
+void HwasanTSDThreadInit();
+
+void HwasanOnDeadlySignal(int signo, void *info, void *context);
+
+void UpdateMemoryUsage();
+
+void AppendToErrorMessageBuffer(const char *buffer);
+
+void AndroidTestTlsSlot();
+
+}  // namespace __hwasan
+
+#define HWASAN_MALLOC_HOOK(ptr, size)       \
+  do {                                    \
+    if (&__sanitizer_malloc_hook) {       \
+      __sanitizer_malloc_hook(ptr, size); \
+    }                                     \
+    RunMallocHooks(ptr, size);            \
+  } while (false)
+#define HWASAN_FREE_HOOK(ptr)       \
+  do {                            \
+    if (&__sanitizer_free_hook) { \
+      __sanitizer_free_hook(ptr); \
+    }                             \
+    RunFreeHooks(ptr);            \
+  } while (false)
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+// For both bionic and glibc __sigset_t is an unsigned long.
+typedef unsigned long __hw_sigset_t;
+// Setjmp and longjmp implementations are platform specific, and hence the
+// interception code is platform specific too.  As yet we've only implemented
+// the interception for AArch64.
+typedef unsigned long long __hw_register_buf[22];
+struct __hw_jmp_buf_struct {
+  // NOTE: The machine-dependent definition of `__sigsetjmp'
+  // assume that a `__hw_jmp_buf' begins with a `__hw_register_buf' and that
+  // `__mask_was_saved' follows it.  Do not move these members or add others
+  // before it.
+  __hw_register_buf __jmpbuf; // Calling environment.
+  int __mask_was_saved;       // Saved the signal mask?
+  __hw_sigset_t __saved_mask; // Saved signal mask.
+};
+typedef struct __hw_jmp_buf_struct __hw_jmp_buf[1];
+typedef struct __hw_jmp_buf_struct __hw_sigjmp_buf[1];
+#endif // HWASAN_WITH_INTERCEPTORS && __aarch64__
+
+#endif  // HWASAN_H
diff --git a/libsanitizer/hwasan/hwasan_allocator.cpp b/libsanitizer/hwasan/hwasan_allocator.cpp
new file mode 100644
index 00000000000..1d82db0e394
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_allocator.cpp
@@ -0,0 +1,406 @@
+//===-- hwasan_allocator.cpp ------------------------ ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer allocator.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "hwasan.h"
+#include "hwasan_allocator.h"
+#include "hwasan_checks.h"
+#include "hwasan_mapping.h"
+#include "hwasan_malloc_bisect.h"
+#include "hwasan_thread.h"
+#include "hwasan_report.h"
+
+namespace __hwasan {
+
+static Allocator allocator;
+static AllocatorCache fallback_allocator_cache;
+static SpinMutex fallback_mutex;
+static atomic_uint8_t hwasan_allocator_tagging_enabled;
+
+static const tag_t kFallbackAllocTag = 0xBB;
+static const tag_t kFallbackFreeTag = 0xBC;
+
+enum RightAlignMode {
+  kRightAlignNever,
+  kRightAlignSometimes,
+  kRightAlignAlways
+};
+
+// Initialized in HwasanAllocatorInit, an never changed.
+static ALIGNED(16) u8 tail_magic[kShadowAlignment - 1];
+
+bool HwasanChunkView::IsAllocated() const {
+  return metadata_ && metadata_->alloc_context_id && metadata_->requested_size;
+}
+
+// Aligns the 'addr' right to the granule boundary.
+static uptr AlignRight(uptr addr, uptr requested_size) {
+  uptr tail_size = requested_size % kShadowAlignment;
+  if (!tail_size) return addr;
+  return addr + kShadowAlignment - tail_size;
+}
+
+uptr HwasanChunkView::Beg() const {
+  if (metadata_ && metadata_->right_aligned)
+    return AlignRight(block_, metadata_->requested_size);
+  return block_;
+}
+uptr HwasanChunkView::End() const {
+  return Beg() + UsedSize();
+}
+uptr HwasanChunkView::UsedSize() const {
+  return metadata_->requested_size;
+}
+u32 HwasanChunkView::GetAllocStackId() const {
+  return metadata_->alloc_context_id;
+}
+
+uptr HwasanChunkView::ActualSize() const {
+  return allocator.GetActuallyAllocatedSize(reinterpret_cast<void *>(block_));
+}
+
+bool HwasanChunkView::FromSmallHeap() const {
+  return allocator.FromPrimary(reinterpret_cast<void *>(block_));
+}
+
+void GetAllocatorStats(AllocatorStatCounters s) {
+  allocator.GetStats(s);
+}
+
+void HwasanAllocatorInit() {
+  atomic_store_relaxed(&hwasan_allocator_tagging_enabled,
+                       !flags()->disable_allocator_tagging);
+  SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
+  allocator.Init(common_flags()->allocator_release_to_os_interval_ms);
+  for (uptr i = 0; i < sizeof(tail_magic); i++)
+    tail_magic[i] = GetCurrentThread()->GenerateRandomTag();
+}
+
+void AllocatorSwallowThreadLocalCache(AllocatorCache *cache) {
+  allocator.SwallowCache(cache);
+}
+
+static uptr TaggedSize(uptr size) {
+  if (!size) size = 1;
+  uptr new_size = RoundUpTo(size, kShadowAlignment);
+  CHECK_GE(new_size, size);
+  return new_size;
+}
+
+static void *HwasanAllocate(StackTrace *stack, uptr orig_size, uptr alignment,
+                            bool zeroise) {
+  if (orig_size > kMaxAllowedMallocSize) {
+    if (AllocatorMayReturnNull()) {
+      Report("WARNING: HWAddressSanitizer failed to allocate 0x%zx bytes\n",
+             orig_size);
+      return nullptr;
+    }
+    ReportAllocationSizeTooBig(orig_size, kMaxAllowedMallocSize, stack);
+  }
+
+  alignment = Max(alignment, kShadowAlignment);
+  uptr size = TaggedSize(orig_size);
+  Thread *t = GetCurrentThread();
+  void *allocated;
+  if (t) {
+    allocated = allocator.Allocate(t->allocator_cache(), size, alignment);
+  } else {
+    SpinMutexLock l(&fallback_mutex);
+    AllocatorCache *cache = &fallback_allocator_cache;
+    allocated = allocator.Allocate(cache, size, alignment);
+  }
+  if (UNLIKELY(!allocated)) {
+    SetAllocatorOutOfMemory();
+    if (AllocatorMayReturnNull())
+      return nullptr;
+    ReportOutOfMemory(size, stack);
+  }
+  Metadata *meta =
+      reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated));
+  meta->requested_size = static_cast<u32>(orig_size);
+  meta->alloc_context_id = StackDepotPut(*stack);
+  meta->right_aligned = false;
+  if (zeroise) {
+    internal_memset(allocated, 0, size);
+  } else if (flags()->max_malloc_fill_size > 0) {
+    uptr fill_size = Min(size, (uptr)flags()->max_malloc_fill_size);
+    internal_memset(allocated, flags()->malloc_fill_byte, fill_size);
+  }
+  if (size != orig_size) {
+    internal_memcpy(reinterpret_cast<u8 *>(allocated) + orig_size, tail_magic,
+                    size - orig_size - 1);
+  }
+
+  void *user_ptr = allocated;
+  // Tagging can only be skipped when both tag_in_malloc and tag_in_free are
+  // false. When tag_in_malloc = false and tag_in_free = true malloc needs to
+  // retag to 0.
+  if ((flags()->tag_in_malloc || flags()->tag_in_free) &&
+      atomic_load_relaxed(&hwasan_allocator_tagging_enabled)) {
+    if (flags()->tag_in_malloc && malloc_bisect(stack, orig_size)) {
+      tag_t tag = t ? t->GenerateRandomTag() : kFallbackAllocTag;
+      uptr tag_size = orig_size ? orig_size : 1;
+      uptr full_granule_size = RoundDownTo(tag_size, kShadowAlignment);
+      user_ptr =
+          (void *)TagMemoryAligned((uptr)user_ptr, full_granule_size, tag);
+      if (full_granule_size != tag_size) {
+        u8 *short_granule =
+            reinterpret_cast<u8 *>(allocated) + full_granule_size;
+        TagMemoryAligned((uptr)short_granule, kShadowAlignment,
+                         tag_size % kShadowAlignment);
+        short_granule[kShadowAlignment - 1] = tag;
+      }
+    } else {
+      user_ptr = (void *)TagMemoryAligned((uptr)user_ptr, size, 0);
+    }
+  }
+
+  HWASAN_MALLOC_HOOK(user_ptr, size);
+  return user_ptr;
+}
+
+static bool PointerAndMemoryTagsMatch(void *tagged_ptr) {
+  CHECK(tagged_ptr);
+  uptr tagged_uptr = reinterpret_cast<uptr>(tagged_ptr);
+  tag_t mem_tag = *reinterpret_cast<tag_t *>(
+      MemToShadow(reinterpret_cast<uptr>(UntagPtr(tagged_ptr))));
+  return PossiblyShortTagMatches(mem_tag, tagged_uptr, 1);
+}
+
+static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) {
+  CHECK(tagged_ptr);
+  HWASAN_FREE_HOOK(tagged_ptr);
+
+  if (!PointerAndMemoryTagsMatch(tagged_ptr))
+    ReportInvalidFree(stack, reinterpret_cast<uptr>(tagged_ptr));
+
+  void *untagged_ptr = UntagPtr(tagged_ptr);
+  void *aligned_ptr = reinterpret_cast<void *>(
+      RoundDownTo(reinterpret_cast<uptr>(untagged_ptr), kShadowAlignment));
+  Metadata *meta =
+      reinterpret_cast<Metadata *>(allocator.GetMetaData(aligned_ptr));
+  uptr orig_size = meta->requested_size;
+  u32 free_context_id = StackDepotPut(*stack);
+  u32 alloc_context_id = meta->alloc_context_id;
+
+  // Check tail magic.
+  uptr tagged_size = TaggedSize(orig_size);
+  if (flags()->free_checks_tail_magic && orig_size &&
+      tagged_size != orig_size) {
+    uptr tail_size = tagged_size - orig_size - 1;
+    CHECK_LT(tail_size, kShadowAlignment);
+    void *tail_beg = reinterpret_cast<void *>(
+        reinterpret_cast<uptr>(aligned_ptr) + orig_size);
+    if (tail_size && internal_memcmp(tail_beg, tail_magic, tail_size))
+      ReportTailOverwritten(stack, reinterpret_cast<uptr>(tagged_ptr),
+                            orig_size, tail_magic);
+  }
+
+  meta->requested_size = 0;
+  meta->alloc_context_id = 0;
+  // This memory will not be reused by anyone else, so we are free to keep it
+  // poisoned.
+  Thread *t = GetCurrentThread();
+  if (flags()->max_free_fill_size > 0) {
+    uptr fill_size =
+        Min(TaggedSize(orig_size), (uptr)flags()->max_free_fill_size);
+    internal_memset(aligned_ptr, flags()->free_fill_byte, fill_size);
+  }
+  if (flags()->tag_in_free && malloc_bisect(stack, 0) &&
+      atomic_load_relaxed(&hwasan_allocator_tagging_enabled))
+    TagMemoryAligned(reinterpret_cast<uptr>(aligned_ptr), TaggedSize(orig_size),
+                     t ? t->GenerateRandomTag() : kFallbackFreeTag);
+  if (t) {
+    allocator.Deallocate(t->allocator_cache(), aligned_ptr);
+    if (auto *ha = t->heap_allocations())
+      ha->push({reinterpret_cast<uptr>(tagged_ptr), alloc_context_id,
+                free_context_id, static_cast<u32>(orig_size)});
+  } else {
+    SpinMutexLock l(&fallback_mutex);
+    AllocatorCache *cache = &fallback_allocator_cache;
+    allocator.Deallocate(cache, aligned_ptr);
+  }
+}
+
+static void *HwasanReallocate(StackTrace *stack, void *tagged_ptr_old,
+                              uptr new_size, uptr alignment) {
+  if (!PointerAndMemoryTagsMatch(tagged_ptr_old))
+    ReportInvalidFree(stack, reinterpret_cast<uptr>(tagged_ptr_old));
+
+  void *tagged_ptr_new =
+      HwasanAllocate(stack, new_size, alignment, false /*zeroise*/);
+  if (tagged_ptr_old && tagged_ptr_new) {
+    void *untagged_ptr_old =  UntagPtr(tagged_ptr_old);
+    Metadata *meta =
+        reinterpret_cast<Metadata *>(allocator.GetMetaData(untagged_ptr_old));
+    internal_memcpy(UntagPtr(tagged_ptr_new), untagged_ptr_old,
+                    Min(new_size, static_cast<uptr>(meta->requested_size)));
+    HwasanDeallocate(stack, tagged_ptr_old);
+  }
+  return tagged_ptr_new;
+}
+
+static void *HwasanCalloc(StackTrace *stack, uptr nmemb, uptr size) {
+  if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
+    if (AllocatorMayReturnNull())
+      return nullptr;
+    ReportCallocOverflow(nmemb, size, stack);
+  }
+  return HwasanAllocate(stack, nmemb * size, sizeof(u64), true);
+}
+
+HwasanChunkView FindHeapChunkByAddress(uptr address) {
+  void *block = allocator.GetBlockBegin(reinterpret_cast<void*>(address));
+  if (!block)
+    return HwasanChunkView();
+  Metadata *metadata =
+      reinterpret_cast<Metadata*>(allocator.GetMetaData(block));
+  return HwasanChunkView(reinterpret_cast<uptr>(block), metadata);
+}
+
+static uptr AllocationSize(const void *tagged_ptr) {
+  const void *untagged_ptr = UntagPtr(tagged_ptr);
+  if (!untagged_ptr) return 0;
+  const void *beg = allocator.GetBlockBegin(untagged_ptr);
+  Metadata *b = (Metadata *)allocator.GetMetaData(untagged_ptr);
+  if (b->right_aligned) {
+    if (beg != reinterpret_cast<void *>(RoundDownTo(
+                   reinterpret_cast<uptr>(untagged_ptr), kShadowAlignment)))
+      return 0;
+  } else {
+    if (beg != untagged_ptr) return 0;
+  }
+  return b->requested_size;
+}
+
+void *hwasan_malloc(uptr size, StackTrace *stack) {
+  return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false));
+}
+
+void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
+  return SetErrnoOnNull(HwasanCalloc(stack, nmemb, size));
+}
+
+void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack) {
+  if (!ptr)
+    return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false));
+  if (size == 0) {
+    HwasanDeallocate(stack, ptr);
+    return nullptr;
+  }
+  return SetErrnoOnNull(HwasanReallocate(stack, ptr, size, sizeof(u64)));
+}
+
+void *hwasan_reallocarray(void *ptr, uptr nmemb, uptr size, StackTrace *stack) {
+  if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
+    errno = errno_ENOMEM;
+    if (AllocatorMayReturnNull())
+      return nullptr;
+    ReportReallocArrayOverflow(nmemb, size, stack);
+  }
+  return hwasan_realloc(ptr, nmemb * size, stack);
+}
+
+void *hwasan_valloc(uptr size, StackTrace *stack) {
+  return SetErrnoOnNull(
+      HwasanAllocate(stack, size, GetPageSizeCached(), false));
+}
+
+void *hwasan_pvalloc(uptr size, StackTrace *stack) {
+  uptr PageSize = GetPageSizeCached();
+  if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
+    errno = errno_ENOMEM;
+    if (AllocatorMayReturnNull())
+      return nullptr;
+    ReportPvallocOverflow(size, stack);
+  }
+  // pvalloc(0) should allocate one page.
+  size = size ? RoundUpTo(size, PageSize) : PageSize;
+  return SetErrnoOnNull(HwasanAllocate(stack, size, PageSize, false));
+}
+
+void *hwasan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack) {
+  if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
+    errno = errno_EINVAL;
+    if (AllocatorMayReturnNull())
+      return nullptr;
+    ReportInvalidAlignedAllocAlignment(size, alignment, stack);
+  }
+  return SetErrnoOnNull(HwasanAllocate(stack, size, alignment, false));
+}
+
+void *hwasan_memalign(uptr alignment, uptr size, StackTrace *stack) {
+  if (UNLIKELY(!IsPowerOfTwo(alignment))) {
+    errno = errno_EINVAL;
+    if (AllocatorMayReturnNull())
+      return nullptr;
+    ReportInvalidAllocationAlignment(alignment, stack);
+  }
+  return SetErrnoOnNull(HwasanAllocate(stack, size, alignment, false));
+}
+
+int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
+                        StackTrace *stack) {
+  if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
+    if (AllocatorMayReturnNull())
+      return errno_EINVAL;
+    ReportInvalidPosixMemalignAlignment(alignment, stack);
+  }
+  void *ptr = HwasanAllocate(stack, size, alignment, false);
+  if (UNLIKELY(!ptr))
+    // OOM error is already taken care of by HwasanAllocate.
+    return errno_ENOMEM;
+  CHECK(IsAligned((uptr)ptr, alignment));
+  *(void **)UntagPtr(memptr) = ptr;
+  return 0;
+}
+
+void hwasan_free(void *ptr, StackTrace *stack) {
+  return HwasanDeallocate(stack, ptr);
+}
+
+}  // namespace __hwasan
+
+using namespace __hwasan;
+
+void __hwasan_enable_allocator_tagging() {
+  atomic_store_relaxed(&hwasan_allocator_tagging_enabled, 1);
+}
+
+void __hwasan_disable_allocator_tagging() {
+  atomic_store_relaxed(&hwasan_allocator_tagging_enabled, 0);
+}
+
+uptr __sanitizer_get_current_allocated_bytes() {
+  uptr stats[AllocatorStatCount];
+  allocator.GetStats(stats);
+  return stats[AllocatorStatAllocated];
+}
+
+uptr __sanitizer_get_heap_size() {
+  uptr stats[AllocatorStatCount];
+  allocator.GetStats(stats);
+  return stats[AllocatorStatMapped];
+}
+
+uptr __sanitizer_get_free_bytes() { return 1; }
+
+uptr __sanitizer_get_unmapped_bytes() { return 1; }
+
+uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
+
+int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; }
+
+uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); }
diff --git a/libsanitizer/hwasan/hwasan_allocator.h b/libsanitizer/hwasan/hwasan_allocator.h
new file mode 100644
index 00000000000..f62be269602
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_allocator.h
@@ -0,0 +1,99 @@
+//===-- hwasan_allocator.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_ALLOCATOR_H
+#define HWASAN_ALLOCATOR_H
+
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_checks.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_ring_buffer.h"
+#include "hwasan_poisoning.h"
+
+#if !defined(__aarch64__) && !defined(__x86_64__)
+#error Unsupported platform
+#endif
+
+namespace __hwasan {
+
+struct Metadata {
+  u32 requested_size : 31;  // sizes are < 2G.
+  u32 right_aligned  : 1;
+  u32 alloc_context_id;
+};
+
+struct HwasanMapUnmapCallback {
+  void OnMap(uptr p, uptr size) const { UpdateMemoryUsage(); }
+  void OnUnmap(uptr p, uptr size) const {
+    // We are about to unmap a chunk of user memory.
+    // It can return as user-requested mmap() or another thread stack.
+    // Make it accessible with zero-tagged pointer.
+    TagMemory(p, size, 0);
+  }
+};
+
+static const uptr kMaxAllowedMallocSize = 2UL << 30;  // 2G
+
+struct AP64 {
+  static const uptr kSpaceBeg = ~0ULL;
+  static const uptr kSpaceSize = 0x2000000000ULL;
+  static const uptr kMetadataSize = sizeof(Metadata);
+  typedef __sanitizer::VeryDenseSizeClassMap SizeClassMap;
+  using AddressSpaceView = LocalAddressSpaceView;
+  typedef HwasanMapUnmapCallback MapUnmapCallback;
+  static const uptr kFlags = 0;
+};
+typedef SizeClassAllocator64<AP64> PrimaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator> Allocator;
+typedef Allocator::AllocatorCache AllocatorCache;
+
+void AllocatorSwallowThreadLocalCache(AllocatorCache *cache);
+
+class HwasanChunkView {
+ public:
+  HwasanChunkView() : block_(0), metadata_(nullptr) {}
+  HwasanChunkView(uptr block, Metadata *metadata)
+      : block_(block), metadata_(metadata) {}
+  bool IsAllocated() const;    // Checks if the memory is currently allocated
+  uptr Beg() const;            // First byte of user memory
+  uptr End() const;            // Last byte of user memory
+  uptr UsedSize() const;       // Size requested by the user
+  uptr ActualSize() const;     // Size allocated by the allocator.
+  u32 GetAllocStackId() const;
+  bool FromSmallHeap() const;
+ private:
+  uptr block_;
+  Metadata *const metadata_;
+};
+
+HwasanChunkView FindHeapChunkByAddress(uptr address);
+
+// Information about one (de)allocation that happened in the past.
+// These are recorded in a thread-local ring buffer.
+// TODO: this is currently 24 bytes (20 bytes + alignment).
+// Compress it to 16 bytes or extend it to be more useful.
+struct HeapAllocationRecord {
+  uptr tagged_addr;
+  u32  alloc_context_id;
+  u32  free_context_id;
+  u32  requested_size;
+};
+
+typedef RingBuffer<HeapAllocationRecord> HeapAllocationsRingBuffer;
+
+void GetAllocatorStats(AllocatorStatCounters s);
+
+} // namespace __hwasan
+
+#endif // HWASAN_ALLOCATOR_H
diff --git a/libsanitizer/hwasan/hwasan_checks.h b/libsanitizer/hwasan/hwasan_checks.h
new file mode 100644
index 00000000000..a8de0fef20f
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_checks.h
@@ -0,0 +1,124 @@
+//===-- hwasan_checks.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_CHECKS_H
+#define HWASAN_CHECKS_H
+
+#include "hwasan_mapping.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+namespace __hwasan {
+template <unsigned X>
+__attribute__((always_inline)) static void SigTrap(uptr p) {
+#if defined(__aarch64__)
+  (void)p;
+  // 0x900 is added to do not interfere with the kernel use of lower values of
+  // brk immediate.
+  register uptr x0 asm("x0") = p;
+  asm("brk %1\n\t" ::"r"(x0), "n"(0x900 + X));
+#elif defined(__x86_64__)
+  // INT3 + NOP DWORD ptr [EAX + X] to pass X to our signal handler, 5 bytes
+  // total. The pointer is passed via rdi.
+  // 0x40 is added as a safeguard, to help distinguish our trap from others and
+  // to avoid 0 offsets in the command (otherwise it'll be reduced to a
+  // different nop command, the three bytes one).
+  asm volatile(
+      "int3\n"
+      "nopl %c0(%%rax)\n" ::"n"(0x40 + X),
+      "D"(p));
+#else
+  // FIXME: not always sigill.
+  __builtin_trap();
+#endif
+  // __builtin_unreachable();
+}
+
+// Version with access size which is not power of 2
+template <unsigned X>
+__attribute__((always_inline)) static void SigTrap(uptr p, uptr size) {
+#if defined(__aarch64__)
+  register uptr x0 asm("x0") = p;
+  register uptr x1 asm("x1") = size;
+  asm("brk %2\n\t" ::"r"(x0), "r"(x1), "n"(0x900 + X));
+#elif defined(__x86_64__)
+  // Size is stored in rsi.
+  asm volatile(
+      "int3\n"
+      "nopl %c0(%%rax)\n" ::"n"(0x40 + X),
+      "D"(p), "S"(size));
+#else
+  __builtin_trap();
+#endif
+  // __builtin_unreachable();
+}
+
+__attribute__((always_inline, nodebug)) static bool PossiblyShortTagMatches(
+    tag_t mem_tag, uptr ptr, uptr sz) {
+  tag_t ptr_tag = GetTagFromPointer(ptr);
+  if (ptr_tag == mem_tag)
+    return true;
+  if (mem_tag >= kShadowAlignment)
+    return false;
+  if ((ptr & (kShadowAlignment - 1)) + sz > mem_tag)
+    return false;
+#ifndef __aarch64__
+  ptr = UntagAddr(ptr);
+#endif
+  return *(u8 *)(ptr | (kShadowAlignment - 1)) == ptr_tag;
+}
+
+enum class ErrorAction { Abort, Recover };
+enum class AccessType { Load, Store };
+
+template <ErrorAction EA, AccessType AT, unsigned LogSize>
+__attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) {
+  uptr ptr_raw = p & ~kAddressTagMask;
+  tag_t mem_tag = *(tag_t *)MemToShadow(ptr_raw);
+  if (UNLIKELY(!PossiblyShortTagMatches(mem_tag, p, 1 << LogSize))) {
+    SigTrap<0x20 * (EA == ErrorAction::Recover) +
+            0x10 * (AT == AccessType::Store) + LogSize>(p);
+    if (EA == ErrorAction::Abort)
+      __builtin_unreachable();
+  }
+}
+
+template <ErrorAction EA, AccessType AT>
+__attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p,
+                                                                      uptr sz) {
+  if (sz == 0)
+    return;
+  tag_t ptr_tag = GetTagFromPointer(p);
+  uptr ptr_raw = p & ~kAddressTagMask;
+  tag_t *shadow_first = (tag_t *)MemToShadow(ptr_raw);
+  tag_t *shadow_last = (tag_t *)MemToShadow(ptr_raw + sz);
+  for (tag_t *t = shadow_first; t < shadow_last; ++t)
+    if (UNLIKELY(ptr_tag != *t)) {
+      SigTrap<0x20 * (EA == ErrorAction::Recover) +
+              0x10 * (AT == AccessType::Store) + 0xf>(p, sz);
+      if (EA == ErrorAction::Abort)
+        __builtin_unreachable();
+    }
+  uptr end = p + sz;
+  uptr tail_sz = end & 0xf;
+  if (UNLIKELY(tail_sz != 0 &&
+               !PossiblyShortTagMatches(
+                   *shadow_last, end & ~(kShadowAlignment - 1), tail_sz))) {
+    SigTrap<0x20 * (EA == ErrorAction::Recover) +
+            0x10 * (AT == AccessType::Store) + 0xf>(p, sz);
+    if (EA == ErrorAction::Abort)
+      __builtin_unreachable();
+  }
+}
+
+}  // end namespace __hwasan
+
+#endif  // HWASAN_CHECKS_H
diff --git a/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp b/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp
new file mode 100644
index 00000000000..12730b29bae
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp
@@ -0,0 +1,126 @@
+//===-- hwasan_dynamic_shadow.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer. It reserves dynamic shadow memory
+/// region and handles ifunc resolver case, when necessary.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hwasan.h"
+#include "hwasan_dynamic_shadow.h"
+#include "hwasan_mapping.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_posix.h"
+
+#include <elf.h>
+#include <link.h>
+
+// The code in this file needs to run in an unrelocated binary. It should not
+// access any external symbol, including its own non-hidden globals.
+
+#if SANITIZER_ANDROID
+extern "C" {
+
+INTERFACE_ATTRIBUTE void __hwasan_shadow();
+decltype(__hwasan_shadow)* __hwasan_premap_shadow();
+
+}  // extern "C"
+
+namespace __hwasan {
+
+// Conservative upper limit.
+static uptr PremapShadowSize() {
+  return RoundUpTo(GetMaxVirtualAddress() >> kShadowScale,
+                   GetMmapGranularity());
+}
+
+static uptr PremapShadow() {
+  return MapDynamicShadow(PremapShadowSize(), kShadowScale,
+                          kShadowBaseAlignment, kHighMemEnd);
+}
+
+static bool IsPremapShadowAvailable() {
+  const uptr shadow = reinterpret_cast<uptr>(&__hwasan_shadow);
+  const uptr resolver = reinterpret_cast<uptr>(&__hwasan_premap_shadow);
+  // shadow == resolver is how Android KitKat and older handles ifunc.
+  // shadow == 0 just in case.
+  return shadow != 0 && shadow != resolver;
+}
+
+static uptr FindPremappedShadowStart(uptr shadow_size_bytes) {
+  const uptr granularity = GetMmapGranularity();
+  const uptr shadow_start = reinterpret_cast<uptr>(&__hwasan_shadow);
+  const uptr premap_shadow_size = PremapShadowSize();
+  const uptr shadow_size = RoundUpTo(shadow_size_bytes, granularity);
+
+  // We may have mapped too much. Release extra memory.
+  UnmapFromTo(shadow_start + shadow_size, shadow_start + premap_shadow_size);
+  return shadow_start;
+}
+
+}  // namespace __hwasan
+
+extern "C" {
+
+decltype(__hwasan_shadow)* __hwasan_premap_shadow() {
+  // The resolver might be called multiple times. Map the shadow just once.
+  static __sanitizer::uptr shadow = 0;
+  if (!shadow)
+    shadow = __hwasan::PremapShadow();
+  return reinterpret_cast<decltype(__hwasan_shadow)*>(shadow);
+}
+
+// __hwasan_shadow is a "function" that has the same address as the first byte
+// of the shadow mapping.
+INTERFACE_ATTRIBUTE __attribute__((ifunc("__hwasan_premap_shadow")))
+void __hwasan_shadow();
+
+extern __attribute((weak, visibility("hidden"))) ElfW(Rela) __rela_iplt_start[],
+    __rela_iplt_end[];
+
+}  // extern "C"
+
+namespace __hwasan {
+
+void InitShadowGOT() {
+  // Call the ifunc resolver for __hwasan_shadow and fill in its GOT entry. This
+  // needs to be done before other ifunc resolvers (which are handled by libc)
+  // because a resolver might read __hwasan_shadow.
+  typedef ElfW(Addr) (*ifunc_resolver_t)(void);
+  for (ElfW(Rela) *r = __rela_iplt_start; r != __rela_iplt_end; ++r) {
+    ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset);
+    ElfW(Addr) resolver = r->r_addend;
+    if (resolver == reinterpret_cast<ElfW(Addr)>(&__hwasan_premap_shadow)) {
+      *offset = reinterpret_cast<ifunc_resolver_t>(resolver)();
+      break;
+    }
+  }
+}
+
+uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
+  if (IsPremapShadowAvailable())
+    return FindPremappedShadowStart(shadow_size_bytes);
+  return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment,
+                          kHighMemEnd);
+}
+
+}  // namespace __hwasan
+#else
+namespace __hwasan {
+
+void InitShadowGOT() {}
+
+uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
+  return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment,
+                          kHighMemEnd);
+}
+
+}  // namespace __hwasan
+
+#endif  // SANITIZER_ANDROID
diff --git a/libsanitizer/hwasan/hwasan_dynamic_shadow.h b/libsanitizer/hwasan/hwasan_dynamic_shadow.h
new file mode 100644
index 00000000000..3c2e7c716a3
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_dynamic_shadow.h
@@ -0,0 +1,27 @@
+//===-- hwasan_dynamic_shadow.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer. It reserves dynamic shadow memory
+/// region.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_PREMAP_SHADOW_H
+#define HWASAN_PREMAP_SHADOW_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+namespace __hwasan {
+
+uptr FindDynamicShadowStart(uptr shadow_size_bytes);
+void InitShadowGOT();
+
+}  // namespace __hwasan
+
+#endif  // HWASAN_PREMAP_SHADOW_H
diff --git a/libsanitizer/hwasan/hwasan_exceptions.cpp b/libsanitizer/hwasan/hwasan_exceptions.cpp
new file mode 100644
index 00000000000..169e7876cb5
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_exceptions.cpp
@@ -0,0 +1,67 @@
+//===-- hwasan_exceptions.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer runtime.
+//===----------------------------------------------------------------------===//
+
+#include "hwasan_poisoning.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+#include <unwind.h>
+
+using namespace __hwasan;
+using namespace __sanitizer;
+
+typedef _Unwind_Reason_Code PersonalityFn(int version, _Unwind_Action actions,
+                                          uint64_t exception_class,
+                                          _Unwind_Exception* unwind_exception,
+                                          _Unwind_Context* context);
+
+// Pointers to the _Unwind_GetGR and _Unwind_GetCFA functions are passed in
+// instead of being called directly. This is to handle cases where the unwinder
+// is statically linked and the sanitizer runtime and the program are linked
+// against different unwinders. The _Unwind_Context data structure is opaque so
+// it may be incompatible between unwinders.
+typedef _Unwind_Word GetGRFn(_Unwind_Context* context, int index);
+typedef _Unwind_Word GetCFAFn(_Unwind_Context* context);
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE _Unwind_Reason_Code
+__hwasan_personality_wrapper(int version, _Unwind_Action actions,
+                             uint64_t exception_class,
+                             _Unwind_Exception* unwind_exception,
+                             _Unwind_Context* context,
+                             PersonalityFn* real_personality, GetGRFn* get_gr,
+                             GetCFAFn* get_cfa) {
+  _Unwind_Reason_Code rc;
+  if (real_personality)
+    rc = real_personality(version, actions, exception_class, unwind_exception,
+                          context);
+  else
+    rc = _URC_CONTINUE_UNWIND;
+
+  // We only untag frames without a landing pad because landing pads are
+  // responsible for untagging the stack themselves if they resume.
+  //
+  // Here we assume that the frame record appears after any locals. This is not
+  // required by AAPCS but is a requirement for HWASAN instrumented functions.
+  if ((actions & _UA_CLEANUP_PHASE) && rc == _URC_CONTINUE_UNWIND) {
+#if defined(__x86_64__)
+    uptr fp = get_gr(context, 6); // rbp
+#elif defined(__aarch64__)
+    uptr fp = get_gr(context, 29); // x29
+#else
+#error Unsupported architecture
+#endif
+    uptr sp = get_cfa(context);
+    TagMemory(sp, fp - sp, 0);
+  }
+
+  return rc;
+}
diff --git a/libsanitizer/hwasan/hwasan_flags.h b/libsanitizer/hwasan/hwasan_flags.h
new file mode 100644
index 00000000000..0a6998f675d
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_flags.h
@@ -0,0 +1,29 @@
+//===-- hwasan_flags.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+#ifndef HWASAN_FLAGS_H
+#define HWASAN_FLAGS_H
+
+namespace __hwasan {
+
+struct Flags {
+#define HWASAN_FLAG(Type, Name, DefaultValue, Description) Type Name;
+#include "hwasan_flags.inc"
+#undef HWASAN_FLAG
+
+  void SetDefaults();
+};
+
+Flags *flags();
+
+}  // namespace __hwasan
+
+#endif  // HWASAN_FLAGS_H
diff --git a/libsanitizer/hwasan/hwasan_flags.inc b/libsanitizer/hwasan/hwasan_flags.inc
new file mode 100644
index 00000000000..8e431d9c4ff
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_flags.inc
@@ -0,0 +1,74 @@
+//===-- hwasan_flags.inc ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Hwasan runtime flags.
+//
+//===----------------------------------------------------------------------===//
+#ifndef HWASAN_FLAG
+# error "Define HWASAN_FLAG prior to including this file!"
+#endif
+
+// HWASAN_FLAG(Type, Name, DefaultValue, Description)
+// See COMMON_FLAG in sanitizer_flags.inc for more details.
+
+HWASAN_FLAG(bool, verbose_threads, false,
+            "inform on thread creation/destruction")
+HWASAN_FLAG(bool, tag_in_malloc, true, "")
+HWASAN_FLAG(bool, tag_in_free, true, "")
+HWASAN_FLAG(bool, print_stats, false, "")
+HWASAN_FLAG(bool, halt_on_error, true, "")
+HWASAN_FLAG(bool, atexit, false, "")
+
+// Test only flag to disable malloc/realloc/free memory tagging on startup.
+// Tagging can be reenabled with __hwasan_enable_allocator_tagging().
+HWASAN_FLAG(bool, disable_allocator_tagging, false, "")
+
+// If false, use simple increment of a thread local counter to generate new
+// tags.
+HWASAN_FLAG(bool, random_tags, true, "")
+
+HWASAN_FLAG(
+    int, max_malloc_fill_size, 0,
+    "HWASan allocator flag. max_malloc_fill_size is the maximal amount of "
+    "bytes that will be filled with malloc_fill_byte on malloc.")
+
+HWASAN_FLAG(bool, free_checks_tail_magic, 1,
+    "If set, free() will check the magic values "
+    "to the right of the allocated object "
+    "if the allocation size is not a divident of the granule size")
+HWASAN_FLAG(
+    int, max_free_fill_size, 0,
+    "HWASan allocator flag. max_free_fill_size is the maximal amount of "
+    "bytes that will be filled with free_fill_byte during free.")
+HWASAN_FLAG(int, malloc_fill_byte, 0xbe,
+          "Value used to fill the newly allocated memory.")
+HWASAN_FLAG(int, free_fill_byte, 0x55,
+          "Value used to fill deallocated memory.")
+HWASAN_FLAG(int, heap_history_size, 1023,
+          "The number of heap (de)allocations remembered per thread. "
+          "Affects the quality of heap-related reports, but not the ability "
+          "to find bugs.")
+HWASAN_FLAG(bool, export_memory_stats, true,
+            "Export up-to-date memory stats through /proc")
+HWASAN_FLAG(int, stack_history_size, 1024,
+            "The number of stack frames remembered per thread. "
+            "Affects the quality of stack-related reports, but not the ability "
+            "to find bugs.")
+
+// Malloc / free bisection. Only tag malloc and free calls when a hash of
+// allocation size and stack trace is between malloc_bisect_left and
+// malloc_bisect_right (both inclusive). [0, 0] range is special and disables
+// bisection (i.e. everything is tagged). Once the range is narrowed down
+// enough, use malloc_bisect_dump to see interesting allocations.
+HWASAN_FLAG(uptr, malloc_bisect_left, 0,
+            "Left bound of malloc bisection, inclusive.")
+HWASAN_FLAG(uptr, malloc_bisect_right, 0,
+            "Right bound of malloc bisection, inclusive.")
+HWASAN_FLAG(bool, malloc_bisect_dump, false,
+            "Print all allocations within [malloc_bisect_left, "
+            "malloc_bisect_right] range ")
diff --git a/libsanitizer/hwasan/hwasan_globals.cpp b/libsanitizer/hwasan/hwasan_globals.cpp
new file mode 100644
index 00000000000..d71bcd792e1
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_globals.cpp
@@ -0,0 +1,91 @@
+//===-- hwasan_globals.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer globals-specific runtime.
+//===----------------------------------------------------------------------===//
+
+#include "hwasan_globals.h"
+
+namespace __hwasan {
+
+enum { NT_LLVM_HWASAN_GLOBALS = 3 };
+struct hwasan_global_note {
+  s32 begin_relptr;
+  s32 end_relptr;
+};
+
+// Check that the given library meets the code model requirements for tagged
+// globals. These properties are not checked at link time so they need to be
+// checked at runtime.
+static void CheckCodeModel(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                           ElfW(Half) phnum) {
+  ElfW(Addr) min_addr = -1ull, max_addr = 0;
+  for (unsigned i = 0; i != phnum; ++i) {
+    if (phdr[i].p_type != PT_LOAD)
+      continue;
+    ElfW(Addr) lo = base + phdr[i].p_vaddr, hi = lo + phdr[i].p_memsz;
+    if (min_addr > lo)
+      min_addr = lo;
+    if (max_addr < hi)
+      max_addr = hi;
+  }
+
+  if (max_addr - min_addr > 1ull << 32) {
+    Report("FATAL: HWAddressSanitizer: library size exceeds 2^32\n");
+    Die();
+  }
+  if (max_addr > 1ull << 48) {
+    Report("FATAL: HWAddressSanitizer: library loaded above address 2^48\n");
+    Die();
+  }
+}
+
+ArrayRef<const hwasan_global> HwasanGlobalsFor(ElfW(Addr) base,
+                                               const ElfW(Phdr) * phdr,
+                                               ElfW(Half) phnum) {
+  // Read the phdrs from this DSO.
+  for (unsigned i = 0; i != phnum; ++i) {
+    if (phdr[i].p_type != PT_NOTE)
+      continue;
+
+    const char *note = reinterpret_cast<const char *>(base + phdr[i].p_vaddr);
+    const char *nend = note + phdr[i].p_memsz;
+
+    // Traverse all the notes until we find a HWASan note.
+    while (note < nend) {
+      auto *nhdr = reinterpret_cast<const ElfW(Nhdr) *>(note);
+      const char *name = note + sizeof(ElfW(Nhdr));
+      const char *desc = name + RoundUpTo(nhdr->n_namesz, 4);
+
+      // Discard non-HWASan-Globals notes.
+      if (nhdr->n_type != NT_LLVM_HWASAN_GLOBALS ||
+          internal_strcmp(name, "LLVM") != 0) {
+        note = desc + RoundUpTo(nhdr->n_descsz, 4);
+        continue;
+      }
+
+      // Only libraries with instrumented globals need to be checked against the
+      // code model since they use relocations that aren't checked at link time.
+      CheckCodeModel(base, phdr, phnum);
+
+      auto *global_note = reinterpret_cast<const hwasan_global_note *>(desc);
+      auto *globals_begin = reinterpret_cast<const hwasan_global *>(
+          note + global_note->begin_relptr);
+      auto *globals_end = reinterpret_cast<const hwasan_global *>(
+          note + global_note->end_relptr);
+
+      return {globals_begin, globals_end};
+    }
+  }
+
+  return {};
+}
+
+}  // namespace __hwasan
diff --git a/libsanitizer/hwasan/hwasan_globals.h b/libsanitizer/hwasan/hwasan_globals.h
new file mode 100644
index 00000000000..fd7adf7a058
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_globals.h
@@ -0,0 +1,49 @@
+//===-- hwasan_globals.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Private Hwasan header.
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_GLOBALS_H
+#define HWASAN_GLOBALS_H
+
+#include <link.h>
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+namespace __hwasan {
+// This object should only ever be casted over the global (i.e. not constructed)
+// in the ELF PT_NOTE in order for `addr()` to work correctly.
+struct hwasan_global {
+  // The size of this global variable. Note that the size in the descriptor is
+  // max 1 << 24. Larger globals have multiple descriptors.
+  uptr size() const { return info & 0xffffff; }
+  // The fully-relocated address of this global.
+  uptr addr() const { return reinterpret_cast<uintptr_t>(this) + gv_relptr; }
+  // The static tag of this global.
+  u8 tag() const { return info >> 24; };
+
+  // The relative address between the start of the descriptor for the HWASan
+  // global (in the PT_NOTE), and the fully relocated address of the global.
+  s32 gv_relptr;
+  u32 info;
+};
+
+// Walk through the specific DSO (as specified by the base, phdr, and phnum),
+// and return the range of the [beginning, end) of the HWASan globals descriptor
+// array.
+ArrayRef<const hwasan_global> HwasanGlobalsFor(ElfW(Addr) base,
+                                               const ElfW(Phdr) * phdr,
+                                               ElfW(Half) phnum);
+
+}  // namespace __hwasan
+
+#endif  // HWASAN_GLOBALS_H
diff --git a/libsanitizer/hwasan/hwasan_interceptors.cpp b/libsanitizer/hwasan/hwasan_interceptors.cpp
new file mode 100644
index 00000000000..44e569ee6d7
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_interceptors.cpp
@@ -0,0 +1,349 @@
+//===-- hwasan_interceptors.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Interceptors for standard library functions.
+//
+// FIXME: move as many interceptors as possible into
+// sanitizer_common/sanitizer_common_interceptors.h
+//===----------------------------------------------------------------------===//
+
+#include "interception/interception.h"
+#include "hwasan.h"
+#include "hwasan_allocator.h"
+#include "hwasan_mapping.h"
+#include "hwasan_thread.h"
+#include "hwasan_poisoning.h"
+#include "hwasan_report.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_allocator_internal.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_linux.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
+
+#include <stdarg.h>
+// ACHTUNG! No other system header includes in this file.
+// Ideally, we should get rid of stdarg.h as well.
+
+using namespace __hwasan;
+
+using __sanitizer::memory_order;
+using __sanitizer::atomic_load;
+using __sanitizer::atomic_store;
+using __sanitizer::atomic_uintptr_t;
+
+static uptr allocated_for_dlsym;
+static const uptr kDlsymAllocPoolSize = 1024;
+static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
+
+static bool IsInDlsymAllocPool(const void *ptr) {
+  uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
+  return off < sizeof(alloc_memory_for_dlsym);
+}
+
+static void *AllocateFromLocalPool(uptr size_in_bytes) {
+  uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
+  void *mem = (void *)&alloc_memory_for_dlsym[allocated_for_dlsym];
+  allocated_for_dlsym += size_in_words;
+  CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
+  return mem;
+}
+
+#define ENSURE_HWASAN_INITED() do { \
+  CHECK(!hwasan_init_is_running); \
+  if (!hwasan_inited) { \
+    __hwasan_init(); \
+  } \
+} while (0)
+
+
+int __sanitizer_posix_memalign(void **memptr, uptr alignment, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  CHECK_NE(memptr, 0);
+  int res = hwasan_posix_memalign(memptr, alignment, size, &stack);
+  return res;
+}
+
+void * __sanitizer_memalign(uptr alignment, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  return hwasan_memalign(alignment, size, &stack);
+}
+
+void * __sanitizer_aligned_alloc(uptr alignment, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  return hwasan_aligned_alloc(alignment, size, &stack);
+}
+
+void * __sanitizer___libc_memalign(uptr alignment, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  void *ptr = hwasan_memalign(alignment, size, &stack);
+  if (ptr)
+    DTLS_on_libc_memalign(ptr, size);
+  return ptr;
+}
+
+void * __sanitizer_valloc(uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  return hwasan_valloc(size, &stack);
+}
+
+void * __sanitizer_pvalloc(uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  return hwasan_pvalloc(size, &stack);
+}
+
+void __sanitizer_free(void *ptr) {
+  GET_MALLOC_STACK_TRACE;
+  if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
+  hwasan_free(ptr, &stack);
+}
+
+void __sanitizer_cfree(void *ptr) {
+  GET_MALLOC_STACK_TRACE;
+  if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
+  hwasan_free(ptr, &stack);
+}
+
+uptr __sanitizer_malloc_usable_size(const void *ptr) {
+  return __sanitizer_get_allocated_size(ptr);
+}
+
+struct __sanitizer_struct_mallinfo __sanitizer_mallinfo() {
+  __sanitizer_struct_mallinfo sret;
+  internal_memset(&sret, 0, sizeof(sret));
+  return sret;
+}
+
+int __sanitizer_mallopt(int cmd, int value) {
+  return 0;
+}
+
+void __sanitizer_malloc_stats(void) {
+  // FIXME: implement, but don't call REAL(malloc_stats)!
+}
+
+void * __sanitizer_calloc(uptr nmemb, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  if (UNLIKELY(!hwasan_inited))
+    // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
+    return AllocateFromLocalPool(nmemb * size);
+  return hwasan_calloc(nmemb, size, &stack);
+}
+
+void * __sanitizer_realloc(void *ptr, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
+    uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
+    uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
+    void *new_ptr;
+    if (UNLIKELY(!hwasan_inited)) {
+      new_ptr = AllocateFromLocalPool(copy_size);
+    } else {
+      copy_size = size;
+      new_ptr = hwasan_malloc(copy_size, &stack);
+    }
+    internal_memcpy(new_ptr, ptr, copy_size);
+    return new_ptr;
+  }
+  return hwasan_realloc(ptr, size, &stack);
+}
+
+void * __sanitizer_reallocarray(void *ptr, uptr nmemb, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  return hwasan_reallocarray(ptr, nmemb, size, &stack);
+}
+
+void * __sanitizer_malloc(uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  if (UNLIKELY(!hwasan_init_is_running))
+    ENSURE_HWASAN_INITED();
+  if (UNLIKELY(!hwasan_inited))
+    // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
+    return AllocateFromLocalPool(size);
+  return hwasan_malloc(size, &stack);
+}
+
+#if HWASAN_WITH_INTERCEPTORS
+#define INTERCEPTOR_ALIAS(RET, FN, ARGS...)                                  \
+  extern "C" SANITIZER_INTERFACE_ATTRIBUTE RET WRAP(FN)(ARGS)                \
+      ALIAS("__sanitizer_" #FN);                                             \
+  extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE RET FN(  \
+      ARGS) ALIAS("__sanitizer_" #FN)
+
+INTERCEPTOR_ALIAS(int, posix_memalign, void **memptr, SIZE_T alignment,
+                  SIZE_T size);
+INTERCEPTOR_ALIAS(void *, aligned_alloc, SIZE_T alignment, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, __libc_memalign, SIZE_T alignment, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, valloc, SIZE_T size);
+INTERCEPTOR_ALIAS(void, free, void *ptr);
+INTERCEPTOR_ALIAS(uptr, malloc_usable_size, const void *ptr);
+INTERCEPTOR_ALIAS(void *, calloc, SIZE_T nmemb, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, realloc, void *ptr, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, reallocarray, void *ptr, SIZE_T nmemb, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, malloc, SIZE_T size);
+
+#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
+INTERCEPTOR_ALIAS(void *, memalign, SIZE_T alignment, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, pvalloc, SIZE_T size);
+INTERCEPTOR_ALIAS(void, cfree, void *ptr);
+INTERCEPTOR_ALIAS(__sanitizer_struct_mallinfo, mallinfo);
+INTERCEPTOR_ALIAS(int, mallopt, int cmd, int value);
+INTERCEPTOR_ALIAS(void, malloc_stats, void);
+#endif
+
+struct ThreadStartArg {
+  thread_callback_t callback;
+  void *param;
+};
+
+static void *HwasanThreadStartFunc(void *arg) {
+  __hwasan_thread_enter();
+  ThreadStartArg A = *reinterpret_cast<ThreadStartArg*>(arg);
+  UnmapOrDie(arg, GetPageSizeCached());
+  return A.callback(A.param);
+}
+
+INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
+            void * param) {
+  ScopedTaggingDisabler disabler;
+  ThreadStartArg *A = reinterpret_cast<ThreadStartArg *> (MmapOrDie(
+      GetPageSizeCached(), "pthread_create"));
+  *A = {callback, param};
+  int res = REAL(pthread_create)(UntagPtr(th), UntagPtr(attr),
+                                 &HwasanThreadStartFunc, A);
+  return res;
+}
+
+DEFINE_REAL(int, vfork)
+DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
+#endif // HWASAN_WITH_INTERCEPTORS
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+// Get and/or change the set of blocked signals.
+extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set,
+                           __hw_sigset_t *__restrict __oset);
+#define SIG_BLOCK 0
+#define SIG_SETMASK 2
+extern "C" int __sigjmp_save(__hw_sigjmp_buf env, int savemask) {
+  env[0].__mask_was_saved =
+      (savemask && sigprocmask(SIG_BLOCK, (__hw_sigset_t *)0,
+                               &env[0].__saved_mask) == 0);
+  return 0;
+}
+
+static void __attribute__((always_inline))
+InternalLongjmp(__hw_register_buf env, int retval) {
+  // Clear all memory tags on the stack between here and where we're going.
+  unsigned long long stack_pointer = env[13];
+  // The stack pointer should never be tagged, so we don't need to clear the
+  // tag for this function call.
+  __hwasan_handle_longjmp((void *)stack_pointer);
+
+  // Run code for handling a longjmp.
+  // Need to use a register that isn't going to be loaded from the environment
+  // buffer -- hence why we need to specify the register to use.
+  // Must implement this ourselves, since we don't know the order of registers
+  // in different libc implementations and many implementations mangle the
+  // stack pointer so we can't use it without knowing the demangling scheme.
+  register long int retval_tmp asm("x1") = retval;
+  register void *env_address asm("x0") = &env[0];
+  asm volatile("ldp	x19, x20, [%0, #0<<3];"
+               "ldp	x21, x22, [%0, #2<<3];"
+               "ldp	x23, x24, [%0, #4<<3];"
+               "ldp	x25, x26, [%0, #6<<3];"
+               "ldp	x27, x28, [%0, #8<<3];"
+               "ldp	x29, x30, [%0, #10<<3];"
+               "ldp	 d8,  d9, [%0, #14<<3];"
+               "ldp	d10, d11, [%0, #16<<3];"
+               "ldp	d12, d13, [%0, #18<<3];"
+               "ldp	d14, d15, [%0, #20<<3];"
+               "ldr	x5, [%0, #13<<3];"
+               "mov	sp, x5;"
+               // Return the value requested to return through arguments.
+               // This should be in x1 given what we requested above.
+               "cmp	%1, #0;"
+               "mov	x0, #1;"
+               "csel	x0, %1, x0, ne;"
+               "br	x30;"
+               : "+r"(env_address)
+               : "r"(retval_tmp));
+}
+
+INTERCEPTOR(void, siglongjmp, __hw_sigjmp_buf env, int val) {
+  if (env[0].__mask_was_saved)
+    // Restore the saved signal mask.
+    (void)sigprocmask(SIG_SETMASK, &env[0].__saved_mask,
+                      (__hw_sigset_t *)0);
+  InternalLongjmp(env[0].__jmpbuf, val);
+}
+
+// Required since glibc libpthread calls __libc_longjmp on pthread_exit, and
+// _setjmp on start_thread.  Hence we have to intercept the longjmp on
+// pthread_exit so the __hw_jmp_buf order matches.
+INTERCEPTOR(void, __libc_longjmp, __hw_jmp_buf env, int val) {
+  InternalLongjmp(env[0].__jmpbuf, val);
+}
+
+INTERCEPTOR(void, longjmp, __hw_jmp_buf env, int val) {
+  InternalLongjmp(env[0].__jmpbuf, val);
+}
+#undef SIG_BLOCK
+#undef SIG_SETMASK
+
+#endif // HWASAN_WITH_INTERCEPTORS && __aarch64__
+
+static void BeforeFork() {
+  StackDepotLockAll();
+}
+
+static void AfterFork() {
+  StackDepotUnlockAll();
+}
+
+INTERCEPTOR(int, fork, void) {
+  ENSURE_HWASAN_INITED();
+  BeforeFork();
+  int pid = REAL(fork)();
+  AfterFork();
+  return pid;
+}
+
+namespace __hwasan {
+
+int OnExit() {
+  // FIXME: ask frontend whether we need to return failure.
+  return 0;
+}
+
+} // namespace __hwasan
+
+namespace __hwasan {
+
+void InitializeInterceptors() {
+  static int inited = 0;
+  CHECK_EQ(inited, 0);
+
+  INTERCEPT_FUNCTION(fork);
+
+#if HWASAN_WITH_INTERCEPTORS
+#if defined(__linux__)
+  INTERCEPT_FUNCTION(vfork);
+#endif  // __linux__
+  INTERCEPT_FUNCTION(pthread_create);
+#endif
+
+  inited = 1;
+}
+} // namespace __hwasan
diff --git a/libsanitizer/hwasan/hwasan_interceptors_vfork.S b/libsanitizer/hwasan/hwasan_interceptors_vfork.S
new file mode 100644
index 00000000000..23d565936d8
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_interceptors_vfork.S
@@ -0,0 +1,11 @@
+#include "sanitizer_common/sanitizer_asm.h"
+
+#if defined(__linux__) && HWASAN_WITH_INTERCEPTORS
+#define COMMON_INTERCEPTOR_SPILL_AREA __hwasan_extra_spill_area
+#define COMMON_INTERCEPTOR_HANDLE_VFORK __hwasan_handle_vfork
+#include "sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S"
+#include "sanitizer_common/sanitizer_common_interceptors_vfork_riscv64.inc.S"
+#include "sanitizer_common/sanitizer_common_interceptors_vfork_x86_64.inc.S"
+#endif
+
+NO_EXEC_STACK_DIRECTIVE
diff --git a/libsanitizer/hwasan/hwasan_interface_internal.h b/libsanitizer/hwasan/hwasan_interface_internal.h
new file mode 100644
index 00000000000..aedda317497
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_interface_internal.h
@@ -0,0 +1,227 @@
+//===-- hwasan_interface_internal.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Private Hwasan interface header.
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_INTERFACE_INTERNAL_H
+#define HWASAN_INTERFACE_INTERNAL_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include <link.h>
+
+extern "C" {
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_init_static();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_init();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                             ElfW(Half) phnum);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                               ElfW(Half) phnum);
+
+using __sanitizer::uptr;
+using __sanitizer::sptr;
+using __sanitizer::uu64;
+using __sanitizer::uu32;
+using __sanitizer::uu16;
+using __sanitizer::u64;
+using __sanitizer::u32;
+using __sanitizer::u16;
+using __sanitizer::u8;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_init_frames(uptr, uptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+extern uptr __hwasan_shadow_memory_dynamic_address;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_loadN(uptr, uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load1(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load2(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load4(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load8(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load16(uptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_loadN_noabort(uptr, uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load1_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load2_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load4_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load8_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load16_noabort(uptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_storeN(uptr, uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store1(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store2(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store4(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store8(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store16(uptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_storeN_noabort(uptr, uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store1_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store2_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store4_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store8_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store16_noabort(uptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_tag_memory(uptr p, u8 tag, uptr sz);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __hwasan_tag_pointer(uptr p, u8 tag);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_tag_mismatch(uptr addr, u8 ts);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_tag_mismatch4(uptr addr, uptr access_info, uptr *registers_frame,
+                            size_t outsize);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+u8 __hwasan_generate_tag();
+
+// Returns the offset of the first tag mismatch or -1 if the whole range is
+// good.
+SANITIZER_INTERFACE_ATTRIBUTE
+sptr __hwasan_test_shadow(const void *x, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+/* OPTIONAL */ const char* __hwasan_default_options();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_print_shadow(const void *x, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_handle_longjmp(const void *sp_dst);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_handle_vfork(const void *sp_dst);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+u16 __sanitizer_unaligned_load16(const uu16 *p);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+u32 __sanitizer_unaligned_load32(const uu32 *p);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+u64 __sanitizer_unaligned_load64(const uu64 *p);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store16(uu16 *p, u16 x);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store32(uu32 *p, u32 x);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store64(uu64 *p, u64 x);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_enable_allocator_tagging();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_disable_allocator_tagging();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_thread_enter();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_thread_exit();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_print_memory_usage();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_posix_memalign(void **memptr, uptr alignment, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_memalign(uptr alignment, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_aligned_alloc(uptr alignment, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer___libc_memalign(uptr alignment, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_valloc(uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_pvalloc(uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_free(void *ptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cfree(void *ptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_malloc_usable_size(const void *ptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+__hwasan::__sanitizer_struct_mallinfo __sanitizer_mallinfo();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_mallopt(int cmd, int value);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_malloc_stats(void);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_calloc(uptr nmemb, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_realloc(void *ptr, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_reallocarray(void *ptr, uptr nmemb, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_malloc(uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__hwasan_memcpy(void *dst, const void *src, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__hwasan_memset(void *s, int c, uptr n);
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__hwasan_memmove(void *dest, const void *src, uptr n);
+}  // extern "C"
+
+#endif  // HWASAN_INTERFACE_INTERNAL_H
diff --git a/libsanitizer/hwasan/hwasan_linux.cpp b/libsanitizer/hwasan/hwasan_linux.cpp
new file mode 100644
index 00000000000..e99926d355c
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_linux.cpp
@@ -0,0 +1,455 @@
+//===-- hwasan_linux.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer and contains Linux-, NetBSD- and
+/// FreeBSD-specific code.
+///
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
+
+#include "hwasan.h"
+#include "hwasan_dynamic_shadow.h"
+#include "hwasan_interface_internal.h"
+#include "hwasan_mapping.h"
+#include "hwasan_report.h"
+#include "hwasan_thread.h"
+#include "hwasan_thread_list.h"
+
+#include <dlfcn.h>
+#include <elf.h>
+#include <link.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <unwind.h>
+#include <sys/prctl.h>
+#include <errno.h>
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+
+// Configurations of HWASAN_WITH_INTERCEPTORS and SANITIZER_ANDROID.
+//
+// HWASAN_WITH_INTERCEPTORS=OFF, SANITIZER_ANDROID=OFF
+//   Not currently tested.
+// HWASAN_WITH_INTERCEPTORS=OFF, SANITIZER_ANDROID=ON
+//   Integration tests downstream exist.
+// HWASAN_WITH_INTERCEPTORS=ON, SANITIZER_ANDROID=OFF
+//    Tested with check-hwasan on x86_64-linux.
+// HWASAN_WITH_INTERCEPTORS=ON, SANITIZER_ANDROID=ON
+//    Tested with check-hwasan on aarch64-linux-android.
+#if !SANITIZER_ANDROID
+SANITIZER_INTERFACE_ATTRIBUTE
+THREADLOCAL uptr __hwasan_tls;
+#endif
+
+namespace __hwasan {
+
+// With the zero shadow base we can not actually map pages starting from 0.
+// This constant is somewhat arbitrary.
+constexpr uptr kZeroBaseShadowStart = 0;
+constexpr uptr kZeroBaseMaxShadowStart = 1 << 18;
+
+static void ProtectGap(uptr addr, uptr size) {
+  __sanitizer::ProtectGap(addr, size, kZeroBaseShadowStart,
+                          kZeroBaseMaxShadowStart);
+}
+
+uptr kLowMemStart;
+uptr kLowMemEnd;
+uptr kLowShadowEnd;
+uptr kLowShadowStart;
+uptr kHighShadowStart;
+uptr kHighShadowEnd;
+uptr kHighMemStart;
+uptr kHighMemEnd;
+
+static void PrintRange(uptr start, uptr end, const char *name) {
+  Printf("|| [%p, %p] || %.*s ||\n", (void *)start, (void *)end, 10, name);
+}
+
+static void PrintAddressSpaceLayout() {
+  PrintRange(kHighMemStart, kHighMemEnd, "HighMem");
+  if (kHighShadowEnd + 1 < kHighMemStart)
+    PrintRange(kHighShadowEnd + 1, kHighMemStart - 1, "ShadowGap");
+  else
+    CHECK_EQ(kHighShadowEnd + 1, kHighMemStart);
+  PrintRange(kHighShadowStart, kHighShadowEnd, "HighShadow");
+  if (kLowShadowEnd + 1 < kHighShadowStart)
+    PrintRange(kLowShadowEnd + 1, kHighShadowStart - 1, "ShadowGap");
+  else
+    CHECK_EQ(kLowMemEnd + 1, kHighShadowStart);
+  PrintRange(kLowShadowStart, kLowShadowEnd, "LowShadow");
+  if (kLowMemEnd + 1 < kLowShadowStart)
+    PrintRange(kLowMemEnd + 1, kLowShadowStart - 1, "ShadowGap");
+  else
+    CHECK_EQ(kLowMemEnd + 1, kLowShadowStart);
+  PrintRange(kLowMemStart, kLowMemEnd, "LowMem");
+  CHECK_EQ(0, kLowMemStart);
+}
+
+static uptr GetHighMemEnd() {
+  // HighMem covers the upper part of the address space.
+  uptr max_address = GetMaxUserVirtualAddress();
+  // Adjust max address to make sure that kHighMemEnd and kHighMemStart are
+  // properly aligned:
+  max_address |= (GetMmapGranularity() << kShadowScale) - 1;
+  return max_address;
+}
+
+static void InitializeShadowBaseAddress(uptr shadow_size_bytes) {
+  __hwasan_shadow_memory_dynamic_address =
+      FindDynamicShadowStart(shadow_size_bytes);
+}
+
+void InitPrctl() {
+#define PR_SET_TAGGED_ADDR_CTRL 55
+#define PR_GET_TAGGED_ADDR_CTRL 56
+#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
+  // Check we're running on a kernel that can use the tagged address ABI.
+  if (internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0) == (uptr)-1 &&
+      errno == EINVAL) {
+#if SANITIZER_ANDROID
+    // Some older Android kernels have the tagged pointer ABI on
+    // unconditionally, and hence don't have the tagged-addr prctl while still
+    // allow the ABI.
+    // If targeting Android and the prctl is not around we assume this is the
+    // case.
+    return;
+#else
+    Printf(
+        "FATAL: "
+        "HWAddressSanitizer requires a kernel with tagged address ABI.\n");
+    Die();
+#endif
+  }
+
+  // Turn on the tagged address ABI.
+  if (internal_prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) ==
+          (uptr)-1 ||
+      !internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0)) {
+    Printf(
+        "FATAL: HWAddressSanitizer failed to enable tagged address syscall "
+        "ABI.\nSuggest check `sysctl abi.tagged_addr_disabled` "
+        "configuration.\n");
+    Die();
+  }
+#undef PR_SET_TAGGED_ADDR_CTRL
+#undef PR_GET_TAGGED_ADDR_CTRL
+#undef PR_TAGGED_ADDR_ENABLE
+}
+
+bool InitShadow() {
+  // Define the entire memory range.
+  kHighMemEnd = GetHighMemEnd();
+
+  // Determine shadow memory base offset.
+  InitializeShadowBaseAddress(MemToShadowSize(kHighMemEnd));
+
+  // Place the low memory first.
+  kLowMemEnd = __hwasan_shadow_memory_dynamic_address - 1;
+  kLowMemStart = 0;
+
+  // Define the low shadow based on the already placed low memory.
+  kLowShadowEnd = MemToShadow(kLowMemEnd);
+  kLowShadowStart = __hwasan_shadow_memory_dynamic_address;
+
+  // High shadow takes whatever memory is left up there (making sure it is not
+  // interfering with low memory in the fixed case).
+  kHighShadowEnd = MemToShadow(kHighMemEnd);
+  kHighShadowStart = Max(kLowMemEnd, MemToShadow(kHighShadowEnd)) + 1;
+
+  // High memory starts where allocated shadow allows.
+  kHighMemStart = ShadowToMem(kHighShadowStart);
+
+  // Check the sanity of the defined memory ranges (there might be gaps).
+  CHECK_EQ(kHighMemStart % GetMmapGranularity(), 0);
+  CHECK_GT(kHighMemStart, kHighShadowEnd);
+  CHECK_GT(kHighShadowEnd, kHighShadowStart);
+  CHECK_GT(kHighShadowStart, kLowMemEnd);
+  CHECK_GT(kLowMemEnd, kLowMemStart);
+  CHECK_GT(kLowShadowEnd, kLowShadowStart);
+  CHECK_GT(kLowShadowStart, kLowMemEnd);
+
+  if (Verbosity())
+    PrintAddressSpaceLayout();
+
+  // Reserve shadow memory.
+  ReserveShadowMemoryRange(kLowShadowStart, kLowShadowEnd, "low shadow");
+  ReserveShadowMemoryRange(kHighShadowStart, kHighShadowEnd, "high shadow");
+
+  // Protect all the gaps.
+  ProtectGap(0, Min(kLowMemStart, kLowShadowStart));
+  if (kLowMemEnd + 1 < kLowShadowStart)
+    ProtectGap(kLowMemEnd + 1, kLowShadowStart - kLowMemEnd - 1);
+  if (kLowShadowEnd + 1 < kHighShadowStart)
+    ProtectGap(kLowShadowEnd + 1, kHighShadowStart - kLowShadowEnd - 1);
+  if (kHighShadowEnd + 1 < kHighMemStart)
+    ProtectGap(kHighShadowEnd + 1, kHighMemStart - kHighShadowEnd - 1);
+
+  return true;
+}
+
+void InitThreads() {
+  CHECK(__hwasan_shadow_memory_dynamic_address);
+  uptr guard_page_size = GetMmapGranularity();
+  uptr thread_space_start =
+      __hwasan_shadow_memory_dynamic_address - (1ULL << kShadowBaseAlignment);
+  uptr thread_space_end =
+      __hwasan_shadow_memory_dynamic_address - guard_page_size;
+  ReserveShadowMemoryRange(thread_space_start, thread_space_end - 1,
+                           "hwasan threads", /*madvise_shadow*/ false);
+  ProtectGap(thread_space_end,
+             __hwasan_shadow_memory_dynamic_address - thread_space_end);
+  InitThreadList(thread_space_start, thread_space_end - thread_space_start);
+}
+
+bool MemIsApp(uptr p) {
+  CHECK(GetTagFromPointer(p) == 0);
+  return p >= kHighMemStart || (p >= kLowMemStart && p <= kLowMemEnd);
+}
+
+static void HwasanAtExit(void) {
+  if (common_flags()->print_module_map)
+    DumpProcessMap();
+  if (flags()->print_stats && (flags()->atexit || hwasan_report_count > 0))
+    ReportStats();
+  if (hwasan_report_count > 0) {
+    // ReportAtExitStatistics();
+    if (common_flags()->exitcode)
+      internal__exit(common_flags()->exitcode);
+  }
+}
+
+void InstallAtExitHandler() {
+  atexit(HwasanAtExit);
+}
+
+// ---------------------- TSD ---------------- {{{1
+
+extern "C" void __hwasan_thread_enter() {
+  hwasanThreadList().CreateCurrentThread()->InitRandomState();
+}
+
+extern "C" void __hwasan_thread_exit() {
+  Thread *t = GetCurrentThread();
+  // Make sure that signal handler can not see a stale current thread pointer.
+  atomic_signal_fence(memory_order_seq_cst);
+  if (t)
+    hwasanThreadList().ReleaseThread(t);
+}
+
+#if HWASAN_WITH_INTERCEPTORS
+static pthread_key_t tsd_key;
+static bool tsd_key_inited = false;
+
+void HwasanTSDThreadInit() {
+  if (tsd_key_inited)
+    CHECK_EQ(0, pthread_setspecific(tsd_key,
+                                    (void *)GetPthreadDestructorIterations()));
+}
+
+void HwasanTSDDtor(void *tsd) {
+  uptr iterations = (uptr)tsd;
+  if (iterations > 1) {
+    CHECK_EQ(0, pthread_setspecific(tsd_key, (void *)(iterations - 1)));
+    return;
+  }
+  __hwasan_thread_exit();
+}
+
+void HwasanTSDInit() {
+  CHECK(!tsd_key_inited);
+  tsd_key_inited = true;
+  CHECK_EQ(0, pthread_key_create(&tsd_key, HwasanTSDDtor));
+}
+#else
+void HwasanTSDInit() {}
+void HwasanTSDThreadInit() {}
+#endif
+
+#if SANITIZER_ANDROID
+uptr *GetCurrentThreadLongPtr() {
+  return (uptr *)get_android_tls_ptr();
+}
+#else
+uptr *GetCurrentThreadLongPtr() {
+  return &__hwasan_tls;
+}
+#endif
+
+#if SANITIZER_ANDROID
+void AndroidTestTlsSlot() {
+  uptr kMagicValue = 0x010203040A0B0C0D;
+  uptr *tls_ptr = GetCurrentThreadLongPtr();
+  uptr old_value = *tls_ptr;
+  *tls_ptr = kMagicValue;
+  dlerror();
+  if (*(uptr *)get_android_tls_ptr() != kMagicValue) {
+    Printf(
+        "ERROR: Incompatible version of Android: TLS_SLOT_SANITIZER(6) is used "
+        "for dlerror().\n");
+    Die();
+  }
+  *tls_ptr = old_value;
+}
+#else
+void AndroidTestTlsSlot() {}
+#endif
+
+Thread *GetCurrentThread() {
+  uptr *ThreadLongPtr = GetCurrentThreadLongPtr();
+  if (UNLIKELY(*ThreadLongPtr == 0))
+    return nullptr;
+  auto *R = (StackAllocationsRingBuffer *)ThreadLongPtr;
+  return hwasanThreadList().GetThreadByBufferAddress((uptr)R->Next());
+}
+
+struct AccessInfo {
+  uptr addr;
+  uptr size;
+  bool is_store;
+  bool is_load;
+  bool recover;
+};
+
+static AccessInfo GetAccessInfo(siginfo_t *info, ucontext_t *uc) {
+  // Access type is passed in a platform dependent way (see below) and encoded
+  // as 0xXY, where X&1 is 1 for store, 0 for load, and X&2 is 1 if the error is
+  // recoverable. Valid values of Y are 0 to 4, which are interpreted as
+  // log2(access_size), and 0xF, which means that access size is passed via
+  // platform dependent register (see below).
+#if defined(__aarch64__)
+  // Access type is encoded in BRK immediate as 0x900 + 0xXY. For Y == 0xF,
+  // access size is stored in X1 register. Access address is always in X0
+  // register.
+  uptr pc = (uptr)info->si_addr;
+  const unsigned code = ((*(u32 *)pc) >> 5) & 0xffff;
+  if ((code & 0xff00) != 0x900)
+    return AccessInfo{}; // Not ours.
+
+  const bool is_store = code & 0x10;
+  const bool recover = code & 0x20;
+  const uptr addr = uc->uc_mcontext.regs[0];
+  const unsigned size_log = code & 0xf;
+  if (size_log > 4 && size_log != 0xf)
+    return AccessInfo{}; // Not ours.
+  const uptr size = size_log == 0xf ? uc->uc_mcontext.regs[1] : 1U << size_log;
+
+#elif defined(__x86_64__)
+  // Access type is encoded in the instruction following INT3 as
+  // NOP DWORD ptr [EAX + 0x40 + 0xXY]. For Y == 0xF, access size is stored in
+  // RSI register. Access address is always in RDI register.
+  uptr pc = (uptr)uc->uc_mcontext.gregs[REG_RIP];
+  uint8_t *nop = (uint8_t*)pc;
+  if (*nop != 0x0f || *(nop + 1) != 0x1f || *(nop + 2) != 0x40  ||
+      *(nop + 3) < 0x40)
+    return AccessInfo{}; // Not ours.
+  const unsigned code = *(nop + 3);
+
+  const bool is_store = code & 0x10;
+  const bool recover = code & 0x20;
+  const uptr addr = uc->uc_mcontext.gregs[REG_RDI];
+  const unsigned size_log = code & 0xf;
+  if (size_log > 4 && size_log != 0xf)
+    return AccessInfo{}; // Not ours.
+  const uptr size =
+      size_log == 0xf ? uc->uc_mcontext.gregs[REG_RSI] : 1U << size_log;
+
+#else
+# error Unsupported architecture
+#endif
+
+  return AccessInfo{addr, size, is_store, !is_store, recover};
+}
+
+static void HandleTagMismatch(AccessInfo ai, uptr pc, uptr frame,
+                              ucontext_t *uc, uptr *registers_frame = nullptr) {
+  InternalMmapVector<BufferedStackTrace> stack_buffer(1);
+  BufferedStackTrace *stack = stack_buffer.data();
+  stack->Reset();
+  stack->Unwind(pc, frame, uc, common_flags()->fast_unwind_on_fatal);
+
+  // The second stack frame contains the failure __hwasan_check function, as
+  // we have a stack frame for the registers saved in __hwasan_tag_mismatch that
+  // we wish to ignore. This (currently) only occurs on AArch64, as x64
+  // implementations use SIGTRAP to implement the failure, and thus do not go
+  // through the stack saver.
+  if (registers_frame && stack->trace && stack->size > 0) {
+    stack->trace++;
+    stack->size--;
+  }
+
+  bool fatal = flags()->halt_on_error || !ai.recover;
+  ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store, fatal,
+                    registers_frame);
+}
+
+static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) {
+  AccessInfo ai = GetAccessInfo(info, uc);
+  if (!ai.is_store && !ai.is_load)
+    return false;
+
+  SignalContext sig{info, uc};
+  HandleTagMismatch(ai, StackTrace::GetNextInstructionPc(sig.pc), sig.bp, uc);
+
+#if defined(__aarch64__)
+  uc->uc_mcontext.pc += 4;
+#elif defined(__x86_64__)
+#else
+# error Unsupported architecture
+#endif
+  return true;
+}
+
+static void OnStackUnwind(const SignalContext &sig, const void *,
+                          BufferedStackTrace *stack) {
+  stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context,
+                common_flags()->fast_unwind_on_fatal);
+}
+
+void HwasanOnDeadlySignal(int signo, void *info, void *context) {
+  // Probably a tag mismatch.
+  if (signo == SIGTRAP)
+    if (HwasanOnSIGTRAP(signo, (siginfo_t *)info, (ucontext_t*)context))
+      return;
+
+  HandleDeadlySignal(info, context, GetTid(), &OnStackUnwind, nullptr);
+}
+
+
+} // namespace __hwasan
+
+// Entry point for interoperability between __hwasan_tag_mismatch (ASM) and the
+// rest of the mismatch handling code (C++).
+void __hwasan_tag_mismatch4(uptr addr, uptr access_info, uptr *registers_frame,
+                            size_t outsize) {
+  __hwasan::AccessInfo ai;
+  ai.is_store = access_info & 0x10;
+  ai.is_load = !ai.is_store;
+  ai.recover = access_info & 0x20;
+  ai.addr = addr;
+  if ((access_info & 0xf) == 0xf)
+    ai.size = outsize;
+  else
+    ai.size = 1 << (access_info & 0xf);
+
+  __hwasan::HandleTagMismatch(ai, (uptr)__builtin_return_address(0),
+                              (uptr)__builtin_frame_address(0), nullptr,
+                              registers_frame);
+  __builtin_unreachable();
+}
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
diff --git a/libsanitizer/hwasan/hwasan_malloc_bisect.h b/libsanitizer/hwasan/hwasan_malloc_bisect.h
new file mode 100644
index 00000000000..7d134e8c4b7
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_malloc_bisect.h
@@ -0,0 +1,50 @@
+//===-- hwasan_malloc_bisect.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_hash.h"
+#include "hwasan.h"
+
+namespace __hwasan {
+
+static u32 malloc_hash(StackTrace *stack, uptr orig_size) {
+  uptr len = Min(stack->size, (unsigned)7);
+  MurMur2HashBuilder H(len);
+  H.add(orig_size);
+  // Start with frame #1 to skip __sanitizer_malloc frame, which is
+  // (a) almost always the same (well, could be operator new or new[])
+  // (b) can change hashes when compiler-rt is rebuilt, invalidating previous
+  // bisection results.
+  // Because of ASLR, use only offset inside the page.
+  for (uptr i = 1; i < len; ++i) H.add(((u32)stack->trace[i]) & 0xFFF);
+  return H.get();
+}
+
+static inline bool malloc_bisect(StackTrace *stack, uptr orig_size) {
+  uptr left = flags()->malloc_bisect_left;
+  uptr right = flags()->malloc_bisect_right;
+  if (LIKELY(left == 0 && right == 0))
+    return true;
+  if (!stack)
+    return true;
+  // Allow malloc_bisect_right > (u32)(-1) to avoid spelling the latter in
+  // decimal.
+  uptr h = (uptr)malloc_hash(stack, orig_size);
+  if (h < left || h > right)
+    return false;
+  if (flags()->malloc_bisect_dump) {
+    Printf("[alloc] %u %zu\n", h, orig_size);
+    stack->Print();
+  }
+  return true;
+}
+
+}  // namespace __hwasan
diff --git a/libsanitizer/hwasan/hwasan_mapping.h b/libsanitizer/hwasan/hwasan_mapping.h
new file mode 100644
index 00000000000..c149687bdfa
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_mapping.h
@@ -0,0 +1,66 @@
+//===-- hwasan_mapping.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer and defines memory mapping.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_MAPPING_H
+#define HWASAN_MAPPING_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "hwasan_interface_internal.h"
+
+// Typical mapping on Linux/x86_64:
+// with dynamic shadow mapped at [0x770d59f40000, 0x7f0d59f40000]:
+// || [0x7f0d59f40000, 0x7fffffffffff] || HighMem    ||
+// || [0x7efe2f934000, 0x7f0d59f3ffff] || HighShadow ||
+// || [0x7e7e2f934000, 0x7efe2f933fff] || ShadowGap  ||
+// || [0x770d59f40000, 0x7e7e2f933fff] || LowShadow  ||
+// || [0x000000000000, 0x770d59f3ffff] || LowMem     ||
+
+// Typical mapping on Android/AArch64
+// with dynamic shadow mapped: [0x007477480000, 0x007c77480000]:
+// || [0x007c77480000, 0x007fffffffff] || HighMem    ||
+// || [0x007c3ebc8000, 0x007c7747ffff] || HighShadow ||
+// || [0x007bbebc8000, 0x007c3ebc7fff] || ShadowGap  ||
+// || [0x007477480000, 0x007bbebc7fff] || LowShadow  ||
+// || [0x000000000000, 0x00747747ffff] || LowMem     ||
+
+// Reasonable values are 4 (for 1/16th shadow) and 6 (for 1/64th).
+constexpr uptr kShadowScale = 4;
+constexpr uptr kShadowAlignment = 1ULL << kShadowScale;
+
+namespace __hwasan {
+
+extern uptr kLowMemStart;
+extern uptr kLowMemEnd;
+extern uptr kLowShadowEnd;
+extern uptr kLowShadowStart;
+extern uptr kHighShadowStart;
+extern uptr kHighShadowEnd;
+extern uptr kHighMemStart;
+extern uptr kHighMemEnd;
+
+inline uptr MemToShadow(uptr untagged_addr) {
+  return (untagged_addr >> kShadowScale) +
+         __hwasan_shadow_memory_dynamic_address;
+}
+inline uptr ShadowToMem(uptr shadow_addr) {
+  return (shadow_addr - __hwasan_shadow_memory_dynamic_address) << kShadowScale;
+}
+inline uptr MemToShadowSize(uptr size) {
+  return size >> kShadowScale;
+}
+
+bool MemIsApp(uptr p);
+
+}  // namespace __hwasan
+
+#endif  // HWASAN_MAPPING_H
diff --git a/libsanitizer/hwasan/hwasan_memintrinsics.cpp b/libsanitizer/hwasan/hwasan_memintrinsics.cpp
new file mode 100644
index 00000000000..e82d77a1bc1
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_memintrinsics.cpp
@@ -0,0 +1,44 @@
+//===-- hwasan_memintrinsics.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer and contains HWASAN versions of
+/// memset, memcpy and memmove
+///
+//===----------------------------------------------------------------------===//
+
+#include <string.h>
+#include "hwasan.h"
+#include "hwasan_checks.h"
+#include "hwasan_flags.h"
+#include "hwasan_interface_internal.h"
+#include "sanitizer_common/sanitizer_libc.h"
+
+using namespace __hwasan;
+
+void *__hwasan_memset(void *block, int c, uptr size) {
+  CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
+      reinterpret_cast<uptr>(block), size);
+  return memset(UntagPtr(block), c, size);
+}
+
+void *__hwasan_memcpy(void *to, const void *from, uptr size) {
+  CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
+      reinterpret_cast<uptr>(to), size);
+  CheckAddressSized<ErrorAction::Recover, AccessType::Load>(
+      reinterpret_cast<uptr>(from), size);
+  return memcpy(UntagPtr(to), UntagPtr(from), size);
+}
+
+void *__hwasan_memmove(void *to, const void *from, uptr size) {
+  CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
+      reinterpret_cast<uptr>(to), size);
+  CheckAddressSized<ErrorAction::Recover, AccessType::Load>(
+      reinterpret_cast<uptr>(from), size);
+  return memmove(UntagPtr(to), UntagPtr(from), size);
+}
diff --git a/libsanitizer/hwasan/hwasan_new_delete.cpp b/libsanitizer/hwasan/hwasan_new_delete.cpp
new file mode 100644
index 00000000000..191c17e56a7
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_new_delete.cpp
@@ -0,0 +1,66 @@
+//===-- hwasan_new_delete.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Interceptors for operators new and delete.
+//===----------------------------------------------------------------------===//
+
+#include "hwasan.h"
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
+
+#if HWASAN_REPLACE_OPERATORS_NEW_AND_DELETE
+
+#include <stddef.h>
+
+using namespace __hwasan;
+
+// Fake std::nothrow_t to avoid including <new>.
+namespace std {
+  struct nothrow_t {};
+}  // namespace std
+
+
+// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
+#define OPERATOR_NEW_BODY(nothrow) \
+  GET_MALLOC_STACK_TRACE; \
+  void *res = hwasan_malloc(size, &stack);\
+  if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
+  return res
+
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void *operator new(size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void *operator new[](size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void *operator new(size_t size, std::nothrow_t const&) {
+  OPERATOR_NEW_BODY(true /*nothrow*/);
+}
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void *operator new[](size_t size, std::nothrow_t const&) {
+  OPERATOR_NEW_BODY(true /*nothrow*/);
+}
+
+#define OPERATOR_DELETE_BODY \
+  GET_MALLOC_STACK_TRACE; \
+  if (ptr) hwasan_free(ptr, &stack)
+
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const&) {
+  OPERATOR_DELETE_BODY;
+}
+
+#endif // HWASAN_REPLACE_OPERATORS_NEW_AND_DELETE
diff --git a/libsanitizer/hwasan/hwasan_poisoning.cpp b/libsanitizer/hwasan/hwasan_poisoning.cpp
new file mode 100644
index 00000000000..2a0816428e7
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_poisoning.cpp
@@ -0,0 +1,52 @@
+//===-- hwasan_poisoning.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "hwasan_poisoning.h"
+
+#include "hwasan_mapping.h"
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_linux.h"
+
+namespace __hwasan {
+
+uptr TagMemoryAligned(uptr p, uptr size, tag_t tag) {
+  CHECK(IsAligned(p, kShadowAlignment));
+  CHECK(IsAligned(size, kShadowAlignment));
+  uptr shadow_start = MemToShadow(p);
+  uptr shadow_size = MemToShadowSize(size);
+
+  uptr page_size = GetPageSizeCached();
+  uptr page_start = RoundUpTo(shadow_start, page_size);
+  uptr page_end = RoundDownTo(shadow_start + shadow_size, page_size);
+  uptr threshold = common_flags()->clear_shadow_mmap_threshold;
+  if (SANITIZER_LINUX &&
+      UNLIKELY(page_end >= page_start + threshold && tag == 0)) {
+    internal_memset((void *)shadow_start, tag, page_start - shadow_start);
+    internal_memset((void *)page_end, tag,
+                    shadow_start + shadow_size - page_end);
+    // For an anonymous private mapping MADV_DONTNEED will return a zero page on
+    // Linux.
+    ReleaseMemoryPagesToOSAndZeroFill(page_start, page_end);
+  } else {
+    internal_memset((void *)shadow_start, tag, shadow_size);
+  }
+  return AddTagToPointer(p, tag);
+}
+
+uptr TagMemory(uptr p, uptr size, tag_t tag) {
+  uptr start = RoundDownTo(p, kShadowAlignment);
+  uptr end = RoundUpTo(p + size, kShadowAlignment);
+  return TagMemoryAligned(start, end - start, tag);
+}
+
+}  // namespace __hwasan
diff --git a/libsanitizer/hwasan/hwasan_poisoning.h b/libsanitizer/hwasan/hwasan_poisoning.h
new file mode 100644
index 00000000000..61751f7d252
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_poisoning.h
@@ -0,0 +1,24 @@
+//===-- hwasan_poisoning.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_POISONING_H
+#define HWASAN_POISONING_H
+
+#include "hwasan.h"
+
+namespace __hwasan {
+uptr TagMemory(uptr p, uptr size, tag_t tag);
+uptr TagMemoryAligned(uptr p, uptr size, tag_t tag);
+
+}  // namespace __hwasan
+
+#endif  // HWASAN_POISONING_H
diff --git a/libsanitizer/hwasan/hwasan_report.cpp b/libsanitizer/hwasan/hwasan_report.cpp
new file mode 100644
index 00000000000..0be7deeaee1
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_report.cpp
@@ -0,0 +1,651 @@
+//===-- hwasan_report.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Error reporting.
+//===----------------------------------------------------------------------===//
+
+#include "hwasan_report.h"
+
+#include <dlfcn.h>
+
+#include "hwasan.h"
+#include "hwasan_allocator.h"
+#include "hwasan_globals.h"
+#include "hwasan_mapping.h"
+#include "hwasan_thread.h"
+#include "hwasan_thread_list.h"
+#include "sanitizer_common/sanitizer_allocator_internal.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_report_decorator.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace_printer.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+using namespace __sanitizer;
+
+namespace __hwasan {
+
+class ScopedReport {
+ public:
+  ScopedReport(bool fatal = false) : error_message_(1), fatal(fatal) {
+    BlockingMutexLock lock(&error_message_lock_);
+    error_message_ptr_ = fatal ? &error_message_ : nullptr;
+    ++hwasan_report_count;
+  }
+
+  ~ScopedReport() {
+    {
+      BlockingMutexLock lock(&error_message_lock_);
+      if (fatal)
+        SetAbortMessage(error_message_.data());
+      error_message_ptr_ = nullptr;
+    }
+    if (common_flags()->print_module_map >= 2 ||
+        (fatal && common_flags()->print_module_map))
+      DumpProcessMap();
+    if (fatal)
+      Die();
+  }
+
+  static void MaybeAppendToErrorMessage(const char *msg) {
+    BlockingMutexLock lock(&error_message_lock_);
+    if (!error_message_ptr_)
+      return;
+    uptr len = internal_strlen(msg);
+    uptr old_size = error_message_ptr_->size();
+    error_message_ptr_->resize(old_size + len);
+    // overwrite old trailing '\0', keep new trailing '\0' untouched.
+    internal_memcpy(&(*error_message_ptr_)[old_size - 1], msg, len);
+  }
+ private:
+  ScopedErrorReportLock error_report_lock_;
+  InternalMmapVector<char> error_message_;
+  bool fatal;
+
+  static InternalMmapVector<char> *error_message_ptr_;
+  static BlockingMutex error_message_lock_;
+};
+
+InternalMmapVector<char> *ScopedReport::error_message_ptr_;
+BlockingMutex ScopedReport::error_message_lock_;
+
+// If there is an active ScopedReport, append to its error message.
+void AppendToErrorMessageBuffer(const char *buffer) {
+  ScopedReport::MaybeAppendToErrorMessage(buffer);
+}
+
+static StackTrace GetStackTraceFromId(u32 id) {
+  CHECK(id);
+  StackTrace res = StackDepotGet(id);
+  CHECK(res.trace);
+  return res;
+}
+
+// A RAII object that holds a copy of the current thread stack ring buffer.
+// The actual stack buffer may change while we are iterating over it (for
+// example, Printf may call syslog() which can itself be built with hwasan).
+class SavedStackAllocations {
+ public:
+  SavedStackAllocations(StackAllocationsRingBuffer *rb) {
+    uptr size = rb->size() * sizeof(uptr);
+    void *storage =
+        MmapAlignedOrDieOnFatalError(size, size * 2, "saved stack allocations");
+    new (&rb_) StackAllocationsRingBuffer(*rb, storage);
+  }
+
+  ~SavedStackAllocations() {
+    StackAllocationsRingBuffer *rb = get();
+    UnmapOrDie(rb->StartOfStorage(), rb->size() * sizeof(uptr));
+  }
+
+  StackAllocationsRingBuffer *get() {
+    return (StackAllocationsRingBuffer *)&rb_;
+  }
+
+ private:
+  uptr rb_;
+};
+
+class Decorator: public __sanitizer::SanitizerCommonDecorator {
+ public:
+  Decorator() : SanitizerCommonDecorator() { }
+  const char *Access() { return Blue(); }
+  const char *Allocation() const { return Magenta(); }
+  const char *Origin() const { return Magenta(); }
+  const char *Name() const { return Green(); }
+  const char *Location() { return Green(); }
+  const char *Thread() { return Green(); }
+};
+
+static bool FindHeapAllocation(HeapAllocationsRingBuffer *rb, uptr tagged_addr,
+                               HeapAllocationRecord *har, uptr *ring_index,
+                               uptr *num_matching_addrs,
+                               uptr *num_matching_addrs_4b) {
+  if (!rb) return false;
+
+  *num_matching_addrs = 0;
+  *num_matching_addrs_4b = 0;
+  for (uptr i = 0, size = rb->size(); i < size; i++) {
+    auto h = (*rb)[i];
+    if (h.tagged_addr <= tagged_addr &&
+        h.tagged_addr + h.requested_size > tagged_addr) {
+      *har = h;
+      *ring_index = i;
+      return true;
+    }
+
+    // Measure the number of heap ring buffer entries that would have matched
+    // if we had only one entry per address (e.g. if the ring buffer data was
+    // stored at the address itself). This will help us tune the allocator
+    // implementation for MTE.
+    if (UntagAddr(h.tagged_addr) <= UntagAddr(tagged_addr) &&
+        UntagAddr(h.tagged_addr) + h.requested_size > UntagAddr(tagged_addr)) {
+      ++*num_matching_addrs;
+    }
+
+    // Measure the number of heap ring buffer entries that would have matched
+    // if we only had 4 tag bits, which is the case for MTE.
+    auto untag_4b = [](uptr p) {
+      return p & ((1ULL << 60) - 1);
+    };
+    if (untag_4b(h.tagged_addr) <= untag_4b(tagged_addr) &&
+        untag_4b(h.tagged_addr) + h.requested_size > untag_4b(tagged_addr)) {
+      ++*num_matching_addrs_4b;
+    }
+  }
+  return false;
+}
+
+static void PrintStackAllocations(StackAllocationsRingBuffer *sa,
+                                  tag_t addr_tag, uptr untagged_addr) {
+  uptr frames = Min((uptr)flags()->stack_history_size, sa->size());
+  bool found_local = false;
+  for (uptr i = 0; i < frames; i++) {
+    const uptr *record_addr = &(*sa)[i];
+    uptr record = *record_addr;
+    if (!record)
+      break;
+    tag_t base_tag =
+        reinterpret_cast<uptr>(record_addr) >> kRecordAddrBaseTagShift;
+    uptr fp = (record >> kRecordFPShift) << kRecordFPLShift;
+    uptr pc_mask = (1ULL << kRecordFPShift) - 1;
+    uptr pc = record & pc_mask;
+    FrameInfo frame;
+    if (Symbolizer::GetOrInit()->SymbolizeFrame(pc, &frame)) {
+      for (LocalInfo &local : frame.locals) {
+        if (!local.has_frame_offset || !local.has_size || !local.has_tag_offset)
+          continue;
+        tag_t obj_tag = base_tag ^ local.tag_offset;
+        if (obj_tag != addr_tag)
+          continue;
+        // Calculate the offset from the object address to the faulting
+        // address. Because we only store bits 4-19 of FP (bits 0-3 are
+        // guaranteed to be zero), the calculation is performed mod 2^20 and may
+        // harmlessly underflow if the address mod 2^20 is below the object
+        // address.
+        uptr obj_offset =
+            (untagged_addr - fp - local.frame_offset) & (kRecordFPModulus - 1);
+        if (obj_offset >= local.size)
+          continue;
+        if (!found_local) {
+          Printf("Potentially referenced stack objects:\n");
+          found_local = true;
+        }
+        Printf("  %s in %s %s:%d\n", local.name, local.function_name,
+               local.decl_file, local.decl_line);
+      }
+      frame.Clear();
+    }
+  }
+
+  if (found_local)
+    return;
+
+  // We didn't find any locals. Most likely we don't have symbols, so dump
+  // the information that we have for offline analysis.
+  InternalScopedString frame_desc(GetPageSizeCached() * 2);
+  Printf("Previously allocated frames:\n");
+  for (uptr i = 0; i < frames; i++) {
+    const uptr *record_addr = &(*sa)[i];
+    uptr record = *record_addr;
+    if (!record)
+      break;
+    uptr pc_mask = (1ULL << 48) - 1;
+    uptr pc = record & pc_mask;
+    frame_desc.append("  record_addr:0x%zx record:0x%zx",
+                      reinterpret_cast<uptr>(record_addr), record);
+    if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) {
+      RenderFrame(&frame_desc, " %F %L\n", 0, frame->info.address, &frame->info,
+                  common_flags()->symbolize_vs_style,
+                  common_flags()->strip_path_prefix);
+      frame->ClearAll();
+    }
+    Printf("%s", frame_desc.data());
+    frame_desc.clear();
+  }
+}
+
+// Returns true if tag == *tag_ptr, reading tags from short granules if
+// necessary. This may return a false positive if tags 1-15 are used as a
+// regular tag rather than a short granule marker.
+static bool TagsEqual(tag_t tag, tag_t *tag_ptr) {
+  if (tag == *tag_ptr)
+    return true;
+  if (*tag_ptr == 0 || *tag_ptr > kShadowAlignment - 1)
+    return false;
+  uptr mem = ShadowToMem(reinterpret_cast<uptr>(tag_ptr));
+  tag_t inline_tag = *reinterpret_cast<tag_t *>(mem + kShadowAlignment - 1);
+  return tag == inline_tag;
+}
+
+// HWASan globals store the size of the global in the descriptor. In cases where
+// we don't have a binary with symbols, we can't grab the size of the global
+// from the debug info - but we might be able to retrieve it from the
+// descriptor. Returns zero if the lookup failed.
+static uptr GetGlobalSizeFromDescriptor(uptr ptr) {
+  // Find the ELF object that this global resides in.
+  Dl_info info;
+  dladdr(reinterpret_cast<void *>(ptr), &info);
+  auto *ehdr = reinterpret_cast<const ElfW(Ehdr) *>(info.dli_fbase);
+  auto *phdr_begin = reinterpret_cast<const ElfW(Phdr) *>(
+      reinterpret_cast<const u8 *>(ehdr) + ehdr->e_phoff);
+
+  // Get the load bias. This is normally the same as the dli_fbase address on
+  // position-independent code, but can be different on non-PIE executables,
+  // binaries using LLD's partitioning feature, or binaries compiled with a
+  // linker script.
+  ElfW(Addr) load_bias = 0;
+  for (const auto &phdr :
+       ArrayRef<const ElfW(Phdr)>(phdr_begin, phdr_begin + ehdr->e_phnum)) {
+    if (phdr.p_type != PT_LOAD || phdr.p_offset != 0)
+      continue;
+    load_bias = reinterpret_cast<ElfW(Addr)>(ehdr) - phdr.p_vaddr;
+    break;
+  }
+
+  // Walk all globals in this ELF object, looking for the one we're interested
+  // in. Once we find it, we can stop iterating and return the size of the
+  // global we're interested in.
+  for (const hwasan_global &global :
+       HwasanGlobalsFor(load_bias, phdr_begin, ehdr->e_phnum))
+    if (global.addr() <= ptr && ptr < global.addr() + global.size())
+      return global.size();
+
+  return 0;
+}
+
+void PrintAddressDescription(
+    uptr tagged_addr, uptr access_size,
+    StackAllocationsRingBuffer *current_stack_allocations) {
+  Decorator d;
+  int num_descriptions_printed = 0;
+  uptr untagged_addr = UntagAddr(tagged_addr);
+
+  // Print some very basic information about the address, if it's a heap.
+  HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
+  if (uptr beg = chunk.Beg()) {
+    uptr size = chunk.ActualSize();
+    Printf("%s[%p,%p) is a %s %s heap chunk; "
+           "size: %zd offset: %zd\n%s",
+           d.Location(),
+           beg, beg + size,
+           chunk.FromSmallHeap() ? "small" : "large",
+           chunk.IsAllocated() ? "allocated" : "unallocated",
+           size, untagged_addr - beg,
+           d.Default());
+  }
+
+  // Check if this looks like a heap buffer overflow by scanning
+  // the shadow left and right and looking for the first adjacent
+  // object with a different memory tag. If that tag matches addr_tag,
+  // check the allocator if it has a live chunk there.
+  tag_t addr_tag = GetTagFromPointer(tagged_addr);
+  tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
+  tag_t *candidate = nullptr, *left = tag_ptr, *right = tag_ptr;
+  for (int i = 0; i < 1000; i++) {
+    if (TagsEqual(addr_tag, left)) {
+      candidate = left;
+      break;
+    }
+    --left;
+    if (TagsEqual(addr_tag, right)) {
+      candidate = right;
+      break;
+    }
+    ++right;
+  }
+
+  if (candidate) {
+    uptr mem = ShadowToMem(reinterpret_cast<uptr>(candidate));
+    HwasanChunkView chunk = FindHeapChunkByAddress(mem);
+    if (chunk.IsAllocated()) {
+      Printf("%s", d.Location());
+      Printf("%p is located %zd bytes to the %s of %zd-byte region [%p,%p)\n",
+             untagged_addr,
+             candidate == left ? untagged_addr - chunk.End()
+                               : chunk.Beg() - untagged_addr,
+             candidate == left ? "right" : "left", chunk.UsedSize(),
+             chunk.Beg(), chunk.End());
+      Printf("%s", d.Allocation());
+      Printf("allocated here:\n");
+      Printf("%s", d.Default());
+      GetStackTraceFromId(chunk.GetAllocStackId()).Print();
+      num_descriptions_printed++;
+    } else {
+      // Check whether the address points into a loaded library. If so, this is
+      // most likely a global variable.
+      const char *module_name;
+      uptr module_address;
+      Symbolizer *sym = Symbolizer::GetOrInit();
+      if (sym->GetModuleNameAndOffsetForPC(mem, &module_name,
+                                           &module_address)) {
+        DataInfo info;
+        if (sym->SymbolizeData(mem, &info) && info.start) {
+          Printf(
+              "%p is located %zd bytes to the %s of %zd-byte global variable "
+              "%s [%p,%p) in %s\n",
+              untagged_addr,
+              candidate == left ? untagged_addr - (info.start + info.size)
+                                : info.start - untagged_addr,
+              candidate == left ? "right" : "left", info.size, info.name,
+              info.start, info.start + info.size, module_name);
+        } else {
+          uptr size = GetGlobalSizeFromDescriptor(mem);
+          if (size == 0)
+            // We couldn't find the size of the global from the descriptors.
+            Printf(
+                "%p is located to the %s of a global variable in (%s+0x%x)\n",
+                untagged_addr, candidate == left ? "right" : "left",
+                module_name, module_address);
+          else
+            Printf(
+                "%p is located to the %s of a %zd-byte global variable in "
+                "(%s+0x%x)\n",
+                untagged_addr, candidate == left ? "right" : "left", size,
+                module_name, module_address);
+        }
+        num_descriptions_printed++;
+      }
+    }
+  }
+
+  hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
+    // Scan all threads' ring buffers to find if it's a heap-use-after-free.
+    HeapAllocationRecord har;
+    uptr ring_index, num_matching_addrs, num_matching_addrs_4b;
+    if (FindHeapAllocation(t->heap_allocations(), tagged_addr, &har,
+                           &ring_index, &num_matching_addrs,
+                           &num_matching_addrs_4b)) {
+      Printf("%s", d.Location());
+      Printf("%p is located %zd bytes inside of %zd-byte region [%p,%p)\n",
+             untagged_addr, untagged_addr - UntagAddr(har.tagged_addr),
+             har.requested_size, UntagAddr(har.tagged_addr),
+             UntagAddr(har.tagged_addr) + har.requested_size);
+      Printf("%s", d.Allocation());
+      Printf("freed by thread T%zd here:\n", t->unique_id());
+      Printf("%s", d.Default());
+      GetStackTraceFromId(har.free_context_id).Print();
+
+      Printf("%s", d.Allocation());
+      Printf("previously allocated here:\n", t);
+      Printf("%s", d.Default());
+      GetStackTraceFromId(har.alloc_context_id).Print();
+
+      // Print a developer note: the index of this heap object
+      // in the thread's deallocation ring buffer.
+      Printf("hwasan_dev_note_heap_rb_distance: %zd %zd\n", ring_index + 1,
+             flags()->heap_history_size);
+      Printf("hwasan_dev_note_num_matching_addrs: %zd\n", num_matching_addrs);
+      Printf("hwasan_dev_note_num_matching_addrs_4b: %zd\n",
+             num_matching_addrs_4b);
+
+      t->Announce();
+      num_descriptions_printed++;
+    }
+
+    // Very basic check for stack memory.
+    if (t->AddrIsInStack(untagged_addr)) {
+      Printf("%s", d.Location());
+      Printf("Address %p is located in stack of thread T%zd\n", untagged_addr,
+             t->unique_id());
+      Printf("%s", d.Default());
+      t->Announce();
+
+      auto *sa = (t == GetCurrentThread() && current_stack_allocations)
+                     ? current_stack_allocations
+                     : t->stack_allocations();
+      PrintStackAllocations(sa, addr_tag, untagged_addr);
+      num_descriptions_printed++;
+    }
+  });
+
+  // Print the remaining threads, as an extra information, 1 line per thread.
+  hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { t->Announce(); });
+
+  if (!num_descriptions_printed)
+    // We exhausted our possibilities. Bail out.
+    Printf("HWAddressSanitizer can not describe address in more detail.\n");
+}
+
+void ReportStats() {}
+
+static void PrintTagInfoAroundAddr(tag_t *tag_ptr, uptr num_rows,
+                                   void (*print_tag)(InternalScopedString &s,
+                                                     tag_t *tag)) {
+  const uptr row_len = 16;  // better be power of two.
+  tag_t *center_row_beg = reinterpret_cast<tag_t *>(
+      RoundDownTo(reinterpret_cast<uptr>(tag_ptr), row_len));
+  tag_t *beg_row = center_row_beg - row_len * (num_rows / 2);
+  tag_t *end_row = center_row_beg + row_len * ((num_rows + 1) / 2);
+  InternalScopedString s(GetPageSizeCached() * 8);
+  for (tag_t *row = beg_row; row < end_row; row += row_len) {
+    s.append("%s", row == center_row_beg ? "=>" : "  ");
+    s.append("%p:", row);
+    for (uptr i = 0; i < row_len; i++) {
+      s.append("%s", row + i == tag_ptr ? "[" : " ");
+      print_tag(s, &row[i]);
+      s.append("%s", row + i == tag_ptr ? "]" : " ");
+    }
+    s.append("\n");
+  }
+  Printf("%s", s.data());
+}
+
+static void PrintTagsAroundAddr(tag_t *tag_ptr) {
+  Printf(
+      "Memory tags around the buggy address (one tag corresponds to %zd "
+      "bytes):\n", kShadowAlignment);
+  PrintTagInfoAroundAddr(tag_ptr, 17, [](InternalScopedString &s, tag_t *tag) {
+    s.append("%02x", *tag);
+  });
+
+  Printf(
+      "Tags for short granules around the buggy address (one tag corresponds "
+      "to %zd bytes):\n",
+      kShadowAlignment);
+  PrintTagInfoAroundAddr(tag_ptr, 3, [](InternalScopedString &s, tag_t *tag) {
+    if (*tag >= 1 && *tag <= kShadowAlignment) {
+      uptr granule_addr = ShadowToMem(reinterpret_cast<uptr>(tag));
+      s.append("%02x",
+               *reinterpret_cast<u8 *>(granule_addr + kShadowAlignment - 1));
+    } else {
+      s.append("..");
+    }
+  });
+  Printf(
+      "See "
+      "https://clang.llvm.org/docs/"
+      "HardwareAssistedAddressSanitizerDesign.html#short-granules for a "
+      "description of short granule tags\n");
+}
+
+void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
+  ScopedReport R(flags()->halt_on_error);
+
+  uptr untagged_addr = UntagAddr(tagged_addr);
+  tag_t ptr_tag = GetTagFromPointer(tagged_addr);
+  tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
+  tag_t mem_tag = *tag_ptr;
+  Decorator d;
+  Printf("%s", d.Error());
+  uptr pc = stack->size ? stack->trace[0] : 0;
+  const char *bug_type = "invalid-free";
+  Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,
+         untagged_addr, pc);
+  Printf("%s", d.Access());
+  Printf("tags: %02x/%02x (ptr/mem)\n", ptr_tag, mem_tag);
+  Printf("%s", d.Default());
+
+  stack->Print();
+
+  PrintAddressDescription(tagged_addr, 0, nullptr);
+
+  PrintTagsAroundAddr(tag_ptr);
+
+  ReportErrorSummary(bug_type, stack);
+}
+
+void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
+                           const u8 *expected) {
+  uptr tail_size = kShadowAlignment - (orig_size % kShadowAlignment);
+  ScopedReport R(flags()->halt_on_error);
+  Decorator d;
+  uptr untagged_addr = UntagAddr(tagged_addr);
+  Printf("%s", d.Error());
+  const char *bug_type = "allocation-tail-overwritten";
+  Report("ERROR: %s: %s; heap object [%p,%p) of size %zd\n", SanitizerToolName,
+         bug_type, untagged_addr, untagged_addr + orig_size, orig_size);
+  Printf("\n%s", d.Default());
+  stack->Print();
+  HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
+  if (chunk.Beg()) {
+    Printf("%s", d.Allocation());
+    Printf("allocated here:\n");
+    Printf("%s", d.Default());
+    GetStackTraceFromId(chunk.GetAllocStackId()).Print();
+  }
+
+  InternalScopedString s(GetPageSizeCached() * 8);
+  CHECK_GT(tail_size, 0U);
+  CHECK_LT(tail_size, kShadowAlignment);
+  u8 *tail = reinterpret_cast<u8*>(untagged_addr + orig_size);
+  s.append("Tail contains: ");
+  for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
+    s.append(".. ");
+  for (uptr i = 0; i < tail_size; i++)
+    s.append("%02x ", tail[i]);
+  s.append("\n");
+  s.append("Expected:      ");
+  for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
+    s.append(".. ");
+  for (uptr i = 0; i < tail_size; i++)
+    s.append("%02x ", expected[i]);
+  s.append("\n");
+  s.append("               ");
+  for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
+    s.append("   ");
+  for (uptr i = 0; i < tail_size; i++)
+    s.append("%s ", expected[i] != tail[i] ? "^^" : "  ");
+
+  s.append("\nThis error occurs when a buffer overflow overwrites memory\n"
+    "to the right of a heap object, but within the %zd-byte granule, e.g.\n"
+    "   char *x = new char[20];\n"
+    "   x[25] = 42;\n"
+    "%s does not detect such bugs in uninstrumented code at the time of write,"
+    "\nbut can detect them at the time of free/delete.\n"
+    "To disable this feature set HWASAN_OPTIONS=free_checks_tail_magic=0\n",
+    kShadowAlignment, SanitizerToolName);
+  Printf("%s", s.data());
+  GetCurrentThread()->Announce();
+
+  tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
+  PrintTagsAroundAddr(tag_ptr);
+
+  ReportErrorSummary(bug_type, stack);
+}
+
+void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
+                       bool is_store, bool fatal, uptr *registers_frame) {
+  ScopedReport R(fatal);
+  SavedStackAllocations current_stack_allocations(
+      GetCurrentThread()->stack_allocations());
+
+  Decorator d;
+  Printf("%s", d.Error());
+  uptr untagged_addr = UntagAddr(tagged_addr);
+  // TODO: when possible, try to print heap-use-after-free, etc.
+  const char *bug_type = "tag-mismatch";
+  uptr pc = stack->size ? stack->trace[0] : 0;
+  Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,
+         untagged_addr, pc);
+
+  Thread *t = GetCurrentThread();
+
+  sptr offset =
+      __hwasan_test_shadow(reinterpret_cast<void *>(tagged_addr), access_size);
+  CHECK(offset >= 0 && offset < static_cast<sptr>(access_size));
+  tag_t ptr_tag = GetTagFromPointer(tagged_addr);
+  tag_t *tag_ptr =
+      reinterpret_cast<tag_t *>(MemToShadow(untagged_addr + offset));
+  tag_t mem_tag = *tag_ptr;
+
+  Printf("%s", d.Access());
+  Printf("%s of size %zu at %p tags: %02x/%02x (ptr/mem) in thread T%zd\n",
+         is_store ? "WRITE" : "READ", access_size, untagged_addr, ptr_tag,
+         mem_tag, t->unique_id());
+  if (offset != 0)
+    Printf("Invalid access starting at offset [%zu, %zu)\n", offset,
+           Min(access_size, static_cast<uptr>(offset) + (1 << kShadowScale)));
+  Printf("%s", d.Default());
+
+  stack->Print();
+
+  PrintAddressDescription(tagged_addr, access_size,
+                          current_stack_allocations.get());
+  t->Announce();
+
+  PrintTagsAroundAddr(tag_ptr);
+
+  if (registers_frame)
+    ReportRegisters(registers_frame, pc);
+
+  ReportErrorSummary(bug_type, stack);
+}
+
+// See the frame breakdown defined in __hwasan_tag_mismatch (from
+// hwasan_tag_mismatch_aarch64.S).
+void ReportRegisters(uptr *frame, uptr pc) {
+  Printf("Registers where the failure occurred (pc %p):\n", pc);
+
+  // We explicitly print a single line (4 registers/line) each iteration to
+  // reduce the amount of logcat error messages printed. Each Printf() will
+  // result in a new logcat line, irrespective of whether a newline is present,
+  // and so we wish to reduce the number of Printf() calls we have to make.
+  Printf("    x0  %016llx  x1  %016llx  x2  %016llx  x3  %016llx\n",
+       frame[0], frame[1], frame[2], frame[3]);
+  Printf("    x4  %016llx  x5  %016llx  x6  %016llx  x7  %016llx\n",
+       frame[4], frame[5], frame[6], frame[7]);
+  Printf("    x8  %016llx  x9  %016llx  x10 %016llx  x11 %016llx\n",
+       frame[8], frame[9], frame[10], frame[11]);
+  Printf("    x12 %016llx  x13 %016llx  x14 %016llx  x15 %016llx\n",
+       frame[12], frame[13], frame[14], frame[15]);
+  Printf("    x16 %016llx  x17 %016llx  x18 %016llx  x19 %016llx\n",
+       frame[16], frame[17], frame[18], frame[19]);
+  Printf("    x20 %016llx  x21 %016llx  x22 %016llx  x23 %016llx\n",
+       frame[20], frame[21], frame[22], frame[23]);
+  Printf("    x24 %016llx  x25 %016llx  x26 %016llx  x27 %016llx\n",
+       frame[24], frame[25], frame[26], frame[27]);
+  Printf("    x28 %016llx  x29 %016llx  x30 %016llx\n",
+       frame[28], frame[29], frame[30]);
+}
+
+}  // namespace __hwasan
diff --git a/libsanitizer/hwasan/hwasan_report.h b/libsanitizer/hwasan/hwasan_report.h
new file mode 100644
index 00000000000..de86c38fc01
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_report.h
@@ -0,0 +1,35 @@
+//===-- hwasan_report.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer. HWASan-private header for error
+/// reporting functions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_REPORT_H
+#define HWASAN_REPORT_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+
+namespace __hwasan {
+
+void ReportStats();
+void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
+                       bool is_store, bool fatal, uptr *registers_frame);
+void ReportInvalidFree(StackTrace *stack, uptr addr);
+void ReportTailOverwritten(StackTrace *stack, uptr addr, uptr orig_size,
+                           const u8 *expected);
+void ReportRegisters(uptr *registers_frame, uptr pc);
+void ReportAtExitStatistics();
+
+
+}  // namespace __hwasan
+
+#endif  // HWASAN_REPORT_H
diff --git a/libsanitizer/hwasan/hwasan_setjmp.S b/libsanitizer/hwasan/hwasan_setjmp.S
new file mode 100644
index 00000000000..0c135433194
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_setjmp.S
@@ -0,0 +1,100 @@
+//===-- hwasan_setjmp.S --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer runtime.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_asm.h"
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+#include "sanitizer_common/sanitizer_platform.h"
+
+// We want to save the context of the calling function.
+// That requires
+// 1) No modification of the link register by this function.
+// 2) No modification of the stack pointer by this function.
+// 3) (no modification of any other saved register, but that's not really going
+// to occur, and hence isn't as much of a worry).
+//
+// There's essentially no way to ensure that the compiler will not modify the
+// stack pointer when compiling a C function.
+// Hence we have to write this function in assembly.
+
+.section .text
+.file "hwasan_setjmp.S"
+
+.global __interceptor_setjmp
+ASM_TYPE_FUNCTION(__interceptor_setjmp)
+__interceptor_setjmp:
+  CFI_STARTPROC
+  mov	x1, #0
+  b	__interceptor_sigsetjmp
+  CFI_ENDPROC
+ASM_SIZE(__interceptor_setjmp)
+
+#if SANITIZER_ANDROID
+// Bionic also defines a function `setjmp` that calls `sigsetjmp` saving the
+// current signal.
+.global __interceptor_setjmp_bionic
+ASM_TYPE_FUNCTION(__interceptor_setjmp_bionic)
+__interceptor_setjmp_bionic:
+  CFI_STARTPROC
+  mov	x1, #1
+  b	__interceptor_sigsetjmp
+  CFI_ENDPROC
+ASM_SIZE(__interceptor_setjmp_bionic)
+#endif
+
+.global __interceptor_sigsetjmp
+ASM_TYPE_FUNCTION(__interceptor_sigsetjmp)
+__interceptor_sigsetjmp:
+  CFI_STARTPROC
+  stp	x19, x20, [x0, #0<<3]
+  stp	x21, x22, [x0, #2<<3]
+  stp	x23, x24, [x0, #4<<3]
+  stp	x25, x26, [x0, #6<<3]
+  stp	x27, x28, [x0, #8<<3]
+  stp	x29, x30, [x0, #10<<3]
+  stp	 d8,  d9, [x0, #14<<3]
+  stp	d10, d11, [x0, #16<<3]
+  stp	d12, d13, [x0, #18<<3]
+  stp	d14, d15, [x0, #20<<3]
+  mov	x2,  sp
+  str	x2,  [x0, #13<<3]
+  // We always have the second argument to __sigjmp_save (savemask) set, since
+  // the _setjmp function above has set it for us as `false`.
+  // This function is defined in hwasan_interceptors.cc
+  b	__sigjmp_save
+  CFI_ENDPROC
+ASM_SIZE(__interceptor_sigsetjmp)
+
+
+.macro ALIAS first second
+  .globl \second
+  .equ \second\(), \first
+.endm
+
+#if SANITIZER_ANDROID
+ALIAS __interceptor_sigsetjmp, sigsetjmp
+.weak sigsetjmp
+
+ALIAS __interceptor_setjmp_bionic, setjmp
+.weak setjmp
+#else
+ALIAS __interceptor_sigsetjmp, __sigsetjmp
+.weak __sigsetjmp
+#endif
+
+ALIAS __interceptor_setjmp, _setjmp
+.weak _setjmp
+#endif
+
+// We do not need executable stack.
+NO_EXEC_STACK_DIRECTIVE
diff --git a/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S b/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S
new file mode 100644
index 00000000000..08df12736bb
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S
@@ -0,0 +1,152 @@
+#include "sanitizer_common/sanitizer_asm.h"
+
+// The content of this file is AArch64-only:
+#if defined(__aarch64__)
+
+// The responsibility of the HWASan entry point in compiler-rt is to primarily
+// readjust the stack from the callee and save the current register values to
+// the stack.
+// This entry point function should be called from a __hwasan_check_* symbol.
+// These are generated during a lowering pass in the backend, and are found in
+// AArch64AsmPrinter::EmitHwasanMemaccessSymbols(). Please look there for
+// further information.
+// The __hwasan_check_* caller of this function should have expanded the stack
+// and saved the previous values of x0, x1, x29, and x30. This function will
+// "consume" these saved values and treats it as part of its own stack frame.
+// In this sense, the __hwasan_check_* callee and this function "share" a stack
+// frame. This allows us to omit having unwinding information (.cfi_*) present
+// in every __hwasan_check_* function, therefore reducing binary size. This is
+// particularly important as hwasan_check_* instances are duplicated in every
+// translation unit where HWASan is enabled.
+// This function calls HwasanTagMismatch to step back into the C++ code that
+// completes the stack unwinding and error printing. This function is is not
+// permitted to return.
+
+
+// Frame from __hwasan_check_:
+// |              ...                |
+// |              ...                |
+// | Previous stack frames...        |
+// +=================================+
+// | Unused 8-bytes for maintaining  |
+// | 16-byte SP alignment.           |
+// +---------------------------------+
+// | Return address (x30) for caller |
+// | of __hwasan_check_*.            |
+// +---------------------------------+
+// | Frame address (x29) for caller  |
+// | of __hwasan_check_*             |
+// +---------------------------------+ <-- [SP + 232]
+// |              ...                |
+// |                                 |
+// | Stack frame space for x2 - x28. |
+// |                                 |
+// |              ...                |
+// +---------------------------------+ <-- [SP + 16]
+// |                                 |
+// | Saved x1, as __hwasan_check_*   |
+// | clobbers it.                    |
+// +---------------------------------+
+// | Saved x0, likewise above.       |
+// +---------------------------------+ <-- [x30 / SP]
+
+// This function takes two arguments:
+//   * x0: The data address.
+//   * x1: The encoded access info for the failing access.
+
+// This function has two entry points. The first, __hwasan_tag_mismatch, is used
+// by clients that were compiled without short tag checks (i.e. binaries built
+// by older compilers and binaries targeting older runtimes). In this case the
+// outlined tag check will be missing the code handling short tags (which won't
+// be used in the binary's own stack variables but may be used on the heap
+// or stack variables in other binaries), so the check needs to be done here.
+//
+// The second, __hwasan_tag_mismatch_v2, is used by binaries targeting newer
+// runtimes. This entry point bypasses the short tag check since it will have
+// already been done as part of the outlined tag check. Since tag mismatches are
+// uncommon, there isn't a significant performance benefit to being able to
+// bypass the check; the main benefits are that we can sometimes avoid
+// clobbering the x17 register in error reports, and that the program will have
+// a runtime dependency on the __hwasan_tag_mismatch_v2 symbol therefore it will
+// fail to start up given an older (i.e. incompatible) runtime.
+.section .text
+.file "hwasan_tag_mismatch_aarch64.S"
+.global __hwasan_tag_mismatch
+.type __hwasan_tag_mismatch, %function
+__hwasan_tag_mismatch:
+  // Compute the granule position one past the end of the access.
+  mov x16, #1
+  and x17, x1, #0xf
+  lsl x16, x16, x17
+  and x17, x0, #0xf
+  add x17, x16, x17
+
+  // Load the shadow byte again and check whether it is a short tag within the
+  // range of the granule position computed above.
+  ubfx x16, x0, #4, #52
+  ldrb w16, [x9, x16]
+  cmp w16, #0xf
+  b.hi __hwasan_tag_mismatch_v2
+  cmp w16, w17
+  b.lo __hwasan_tag_mismatch_v2
+
+  // Load the real tag from the last byte of the granule and compare against
+  // the pointer tag.
+  orr x16, x0, #0xf
+  ldrb w16, [x16]
+  cmp x16, x0, lsr #56
+  b.ne __hwasan_tag_mismatch_v2
+
+  // Restore x0, x1 and sp to their values from before the __hwasan_tag_mismatch
+  // call and resume execution.
+  ldp x0, x1, [sp], #256
+  ret
+
+.global __hwasan_tag_mismatch_v2
+.type __hwasan_tag_mismatch_v2, %function
+__hwasan_tag_mismatch_v2:
+  CFI_STARTPROC
+
+  // Set the CFA to be the return address for caller of __hwasan_check_*. Note
+  // that we do not emit CFI predicates to describe the contents of this stack
+  // frame, as this proxy entry point should never be debugged. The contents
+  // are static and are handled by the unwinder after calling
+  // __hwasan_tag_mismatch. The frame pointer is already correctly setup
+  // by __hwasan_check_*.
+  add x29, sp, #232
+  CFI_DEF_CFA(w29, 24)
+  CFI_OFFSET(w30, -16)
+  CFI_OFFSET(w29, -24)
+
+  // Save the rest of the registers into the preallocated space left by
+  // __hwasan_check.
+  str     x28,      [sp, #224]
+  stp     x26, x27, [sp, #208]
+  stp     x24, x25, [sp, #192]
+  stp     x22, x23, [sp, #176]
+  stp     x20, x21, [sp, #160]
+  stp     x18, x19, [sp, #144]
+  stp     x16, x17, [sp, #128]
+  stp     x14, x15, [sp, #112]
+  stp     x12, x13, [sp, #96]
+  stp     x10, x11, [sp, #80]
+  stp     x8,  x9,  [sp, #64]
+  stp     x6,  x7,  [sp, #48]
+  stp     x4,  x5,  [sp, #32]
+  stp     x2,  x3,  [sp, #16]
+
+  // Pass the address of the frame to __hwasan_tag_mismatch4, so that it can
+  // extract the saved registers from this frame without having to worry about
+  // finding this frame.
+  mov x2, sp
+
+  bl __hwasan_tag_mismatch4
+  CFI_ENDPROC
+
+.Lfunc_end0:
+  .size __hwasan_tag_mismatch, .Lfunc_end0-__hwasan_tag_mismatch
+
+#endif  // defined(__aarch64__)
+
+// We do not need executable stack.
+NO_EXEC_STACK_DIRECTIVE
diff --git a/libsanitizer/hwasan/hwasan_thread.cpp b/libsanitizer/hwasan/hwasan_thread.cpp
new file mode 100644
index 00000000000..b81a6350c05
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_thread.cpp
@@ -0,0 +1,133 @@
+
+#include "hwasan.h"
+#include "hwasan_mapping.h"
+#include "hwasan_thread.h"
+#include "hwasan_poisoning.h"
+#include "hwasan_interface_internal.h"
+
+#include "sanitizer_common/sanitizer_file.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
+
+
+namespace __hwasan {
+
+static u32 RandomSeed() {
+  u32 seed;
+  do {
+    if (UNLIKELY(!GetRandom(reinterpret_cast<void *>(&seed), sizeof(seed),
+                            /*blocking=*/false))) {
+      seed = static_cast<u32>(
+          (NanoTime() >> 12) ^
+          (reinterpret_cast<uptr>(__builtin_frame_address(0)) >> 4));
+    }
+  } while (!seed);
+  return seed;
+}
+
+void Thread::InitRandomState() {
+  random_state_ = flags()->random_tags ? RandomSeed() : unique_id_;
+
+  // Push a random number of zeros onto the ring buffer so that the first stack
+  // tag base will be random.
+  for (tag_t i = 0, e = GenerateRandomTag(); i != e; ++i)
+    stack_allocations_->push(0);
+}
+
+void Thread::Init(uptr stack_buffer_start, uptr stack_buffer_size) {
+  static u64 unique_id;
+  unique_id_ = unique_id++;
+  if (auto sz = flags()->heap_history_size)
+    heap_allocations_ = HeapAllocationsRingBuffer::New(sz);
+
+  HwasanTSDThreadInit();  // Only needed with interceptors.
+  uptr *ThreadLong = GetCurrentThreadLongPtr();
+  // The following implicitly sets (this) as the current thread.
+  stack_allocations_ = new (ThreadLong)
+      StackAllocationsRingBuffer((void *)stack_buffer_start, stack_buffer_size);
+  // Check that it worked.
+  CHECK_EQ(GetCurrentThread(), this);
+
+  // ScopedTaggingDisable needs GetCurrentThread to be set up.
+  ScopedTaggingDisabler disabler;
+
+  uptr tls_size;
+  uptr stack_size;
+  GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size, &tls_begin_,
+                       &tls_size);
+  stack_top_ = stack_bottom_ + stack_size;
+  tls_end_ = tls_begin_ + tls_size;
+
+  if (stack_bottom_) {
+    int local;
+    CHECK(AddrIsInStack((uptr)&local));
+    CHECK(MemIsApp(stack_bottom_));
+    CHECK(MemIsApp(stack_top_ - 1));
+  }
+
+  if (flags()->verbose_threads) {
+    if (IsMainThread()) {
+      Printf("sizeof(Thread): %zd sizeof(HeapRB): %zd sizeof(StackRB): %zd\n",
+             sizeof(Thread), heap_allocations_->SizeInBytes(),
+             stack_allocations_->size() * sizeof(uptr));
+    }
+    Print("Creating  : ");
+  }
+}
+
+void Thread::ClearShadowForThreadStackAndTLS() {
+  if (stack_top_ != stack_bottom_)
+    TagMemory(stack_bottom_, stack_top_ - stack_bottom_, 0);
+  if (tls_begin_ != tls_end_)
+    TagMemory(tls_begin_, tls_end_ - tls_begin_, 0);
+}
+
+void Thread::Destroy() {
+  if (flags()->verbose_threads)
+    Print("Destroying: ");
+  AllocatorSwallowThreadLocalCache(allocator_cache());
+  ClearShadowForThreadStackAndTLS();
+  if (heap_allocations_)
+    heap_allocations_->Delete();
+  DTLS_Destroy();
+  // Unregister this as the current thread.
+  // Instrumented code can not run on this thread from this point onwards, but
+  // malloc/free can still be served. Glibc may call free() very late, after all
+  // TSD destructors are done.
+  CHECK_EQ(GetCurrentThread(), this);
+  *GetCurrentThreadLongPtr() = 0;
+}
+
+void Thread::Print(const char *Prefix) {
+  Printf("%sT%zd %p stack: [%p,%p) sz: %zd tls: [%p,%p)\n", Prefix,
+         unique_id_, this, stack_bottom(), stack_top(),
+         stack_top() - stack_bottom(),
+         tls_begin(), tls_end());
+}
+
+static u32 xorshift(u32 state) {
+  state ^= state << 13;
+  state ^= state >> 17;
+  state ^= state << 5;
+  return state;
+}
+
+// Generate a (pseudo-)random non-zero tag.
+tag_t Thread::GenerateRandomTag() {
+  if (tagging_disabled_) return 0;
+  tag_t tag;
+  do {
+    if (flags()->random_tags) {
+      if (!random_buffer_)
+        random_buffer_ = random_state_ = xorshift(random_state_);
+      CHECK(random_buffer_);
+      tag = random_buffer_ & 0xFF;
+      random_buffer_ >>= 8;
+    } else {
+      tag = random_state_ = (random_state_ + 1) & 0xFF;
+    }
+  } while (!tag);
+  return tag;
+}
+
+} // namespace __hwasan
diff --git a/libsanitizer/hwasan/hwasan_thread.h b/libsanitizer/hwasan/hwasan_thread.h
new file mode 100644
index 00000000000..ebcdb791fb3
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_thread.h
@@ -0,0 +1,98 @@
+//===-- hwasan_thread.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_THREAD_H
+#define HWASAN_THREAD_H
+
+#include "hwasan_allocator.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_ring_buffer.h"
+
+namespace __hwasan {
+
+typedef __sanitizer::CompactRingBuffer<uptr> StackAllocationsRingBuffer;
+
+class Thread {
+ public:
+  void Init(uptr stack_buffer_start, uptr stack_buffer_size);  // Must be called from the thread itself.
+  void InitRandomState();
+  void Destroy();
+
+  uptr stack_top() { return stack_top_; }
+  uptr stack_bottom() { return stack_bottom_; }
+  uptr stack_size() { return stack_top() - stack_bottom(); }
+  uptr tls_begin() { return tls_begin_; }
+  uptr tls_end() { return tls_end_; }
+  bool IsMainThread() { return unique_id_ == 0; }
+
+  bool AddrIsInStack(uptr addr) {
+    return addr >= stack_bottom_ && addr < stack_top_;
+  }
+
+  AllocatorCache *allocator_cache() { return &allocator_cache_; }
+  HeapAllocationsRingBuffer *heap_allocations() { return heap_allocations_; }
+  StackAllocationsRingBuffer *stack_allocations() { return stack_allocations_; }
+
+  tag_t GenerateRandomTag();
+
+  void DisableTagging() { tagging_disabled_++; }
+  void EnableTagging() { tagging_disabled_--; }
+
+  u64 unique_id() const { return unique_id_; }
+  void Announce() {
+    if (announced_) return;
+    announced_ = true;
+    Print("Thread: ");
+  }
+
+  uptr &vfork_spill() { return vfork_spill_; }
+
+ private:
+  // NOTE: There is no Thread constructor. It is allocated
+  // via mmap() and *must* be valid in zero-initialized state.
+  void ClearShadowForThreadStackAndTLS();
+  void Print(const char *prefix);
+  uptr vfork_spill_;
+  uptr stack_top_;
+  uptr stack_bottom_;
+  uptr tls_begin_;
+  uptr tls_end_;
+
+  u32 random_state_;
+  u32 random_buffer_;
+
+  AllocatorCache allocator_cache_;
+  HeapAllocationsRingBuffer *heap_allocations_;
+  StackAllocationsRingBuffer *stack_allocations_;
+
+  Thread *next_;  // All live threads form a linked list.
+
+  u64 unique_id_;  // counting from zero.
+
+  u32 tagging_disabled_;  // if non-zero, malloc uses zero tag in this thread.
+
+  bool announced_;
+
+  friend struct ThreadListHead;
+};
+
+Thread *GetCurrentThread();
+uptr *GetCurrentThreadLongPtr();
+
+struct ScopedTaggingDisabler {
+  ScopedTaggingDisabler() { GetCurrentThread()->DisableTagging(); }
+  ~ScopedTaggingDisabler() { GetCurrentThread()->EnableTagging(); }
+};
+
+} // namespace __hwasan
+
+#endif // HWASAN_THREAD_H
diff --git a/libsanitizer/hwasan/hwasan_thread_list.cpp b/libsanitizer/hwasan/hwasan_thread_list.cpp
new file mode 100644
index 00000000000..a31eee84ed9
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_thread_list.cpp
@@ -0,0 +1,15 @@
+#include "hwasan_thread_list.h"
+
+namespace __hwasan {
+static ALIGNED(16) char thread_list_placeholder[sizeof(HwasanThreadList)];
+static HwasanThreadList *hwasan_thread_list;
+
+HwasanThreadList &hwasanThreadList() { return *hwasan_thread_list; }
+
+void InitThreadList(uptr storage, uptr size) {
+  CHECK(hwasan_thread_list == nullptr);
+  hwasan_thread_list =
+      new (thread_list_placeholder) HwasanThreadList(storage, size);
+}
+
+} // namespace
diff --git a/libsanitizer/hwasan/hwasan_thread_list.h b/libsanitizer/hwasan/hwasan_thread_list.h
new file mode 100644
index 00000000000..914b632d977
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_thread_list.h
@@ -0,0 +1,215 @@
+//===-- hwasan_thread_list.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+// HwasanThreadList is a registry for live threads, as well as an allocator for
+// HwasanThread objects and their stack history ring buffers. There are
+// constraints on memory layout of the shadow region and CompactRingBuffer that
+// are part of the ABI contract between compiler-rt and llvm.
+//
+// * Start of the shadow memory region is aligned to 2**kShadowBaseAlignment.
+// * All stack ring buffers are located within (2**kShadowBaseAlignment)
+// sized region below and adjacent to the shadow region.
+// * Each ring buffer has a size of (2**N)*4096 where N is in [0, 8), and is
+// aligned to twice its size. The value of N can be different for each buffer.
+//
+// These constrains guarantee that, given an address A of any element of the
+// ring buffer,
+//     A_next = (A + sizeof(uptr)) & ~((1 << (N + 13)) - 1)
+//   is the address of the next element of that ring buffer (with wrap-around).
+// And, with K = kShadowBaseAlignment,
+//     S = (A | ((1 << K) - 1)) + 1
+//   (align up to kShadowBaseAlignment) is the start of the shadow region.
+//
+// These calculations are used in compiler instrumentation to update the ring
+// buffer and obtain the base address of shadow using only two inputs: address
+// of the current element of the ring buffer, and N (i.e. size of the ring
+// buffer). Since the value of N is very limited, we pack both inputs into a
+// single thread-local word as
+//   (1 << (N + 56)) | A
+// See the implementation of class CompactRingBuffer, which is what is stored in
+// said thread-local word.
+//
+// Note the unusual way of aligning up the address of the shadow:
+//   (A | ((1 << K) - 1)) + 1
+// It is only correct if A is not already equal to the shadow base address, but
+// it saves 2 instructions on AArch64.
+
+#include "hwasan.h"
+#include "hwasan_allocator.h"
+#include "hwasan_flags.h"
+#include "hwasan_thread.h"
+
+#include "sanitizer_common/sanitizer_placement_new.h"
+
+namespace __hwasan {
+
+static uptr RingBufferSize() {
+  uptr desired_bytes = flags()->stack_history_size * sizeof(uptr);
+  // FIXME: increase the limit to 8 once this bug is fixed:
+  // https://bugs.llvm.org/show_bug.cgi?id=39030
+  for (int shift = 1; shift < 7; ++shift) {
+    uptr size = 4096 * (1ULL << shift);
+    if (size >= desired_bytes)
+      return size;
+  }
+  Printf("stack history size too large: %d\n", flags()->stack_history_size);
+  CHECK(0);
+  return 0;
+}
+
+struct ThreadListHead {
+  Thread *list_;
+
+  ThreadListHead() : list_(nullptr) {}
+
+  void Push(Thread *t) {
+    t->next_ = list_;
+    list_ = t;
+  }
+
+  Thread *Pop() {
+    Thread *t = list_;
+    if (t)
+      list_ = t->next_;
+    return t;
+  }
+
+  void Remove(Thread *t) {
+    Thread **cur = &list_;
+    while (*cur != t) cur = &(*cur)->next_;
+    CHECK(*cur && "thread not found");
+    *cur = (*cur)->next_;
+  }
+
+  template <class CB>
+  void ForEach(CB cb) {
+    Thread *t = list_;
+    while (t) {
+      cb(t);
+      t = t->next_;
+    }
+  }
+};
+
+struct ThreadStats {
+  uptr n_live_threads;
+  uptr total_stack_size;
+};
+
+class HwasanThreadList {
+ public:
+  HwasanThreadList(uptr storage, uptr size)
+      : free_space_(storage), free_space_end_(storage + size) {
+    // [storage, storage + size) is used as a vector of
+    // thread_alloc_size_-sized, ring_buffer_size_*2-aligned elements.
+    // Each element contains
+    // * a ring buffer at offset 0,
+    // * a Thread object at offset ring_buffer_size_.
+    ring_buffer_size_ = RingBufferSize();
+    thread_alloc_size_ =
+        RoundUpTo(ring_buffer_size_ + sizeof(Thread), ring_buffer_size_ * 2);
+  }
+
+  Thread *CreateCurrentThread() {
+    Thread *t;
+    {
+      SpinMutexLock l(&list_mutex_);
+      t = free_list_.Pop();
+      if (t) {
+        uptr start = (uptr)t - ring_buffer_size_;
+        internal_memset((void *)start, 0, ring_buffer_size_ + sizeof(Thread));
+      } else {
+        t = AllocThread();
+      }
+      live_list_.Push(t);
+    }
+    t->Init((uptr)t - ring_buffer_size_, ring_buffer_size_);
+    AddThreadStats(t);
+    return t;
+  }
+
+  void DontNeedThread(Thread *t) {
+    uptr start = (uptr)t - ring_buffer_size_;
+    ReleaseMemoryPagesToOS(start, start + thread_alloc_size_);
+  }
+
+  void ReleaseThread(Thread *t) {
+    RemoveThreadStats(t);
+    t->Destroy();
+    SpinMutexLock l(&list_mutex_);
+    live_list_.Remove(t);
+    free_list_.Push(t);
+    DontNeedThread(t);
+  }
+
+  Thread *GetThreadByBufferAddress(uptr p) {
+    return (Thread *)(RoundDownTo(p, ring_buffer_size_ * 2) +
+                      ring_buffer_size_);
+  }
+
+  uptr MemoryUsedPerThread() {
+    uptr res = sizeof(Thread) + ring_buffer_size_;
+    if (auto sz = flags()->heap_history_size)
+      res += HeapAllocationsRingBuffer::SizeInBytes(sz);
+    return res;
+  }
+
+  template <class CB>
+  void VisitAllLiveThreads(CB cb) {
+    SpinMutexLock l(&list_mutex_);
+    live_list_.ForEach(cb);
+  }
+
+  void AddThreadStats(Thread *t) {
+    SpinMutexLock l(&stats_mutex_);
+    stats_.n_live_threads++;
+    stats_.total_stack_size += t->stack_size();
+  }
+
+  void RemoveThreadStats(Thread *t) {
+    SpinMutexLock l(&stats_mutex_);
+    stats_.n_live_threads--;
+    stats_.total_stack_size -= t->stack_size();
+  }
+
+  ThreadStats GetThreadStats() {
+    SpinMutexLock l(&stats_mutex_);
+    return stats_;
+  }
+
+ private:
+  Thread *AllocThread() {
+    uptr align = ring_buffer_size_ * 2;
+    CHECK(IsAligned(free_space_, align));
+    Thread *t = (Thread *)(free_space_ + ring_buffer_size_);
+    free_space_ += thread_alloc_size_;
+    CHECK(free_space_ <= free_space_end_ && "out of thread memory");
+    return t;
+  }
+
+  uptr free_space_;
+  uptr free_space_end_;
+  uptr ring_buffer_size_;
+  uptr thread_alloc_size_;
+
+  ThreadListHead free_list_;
+  ThreadListHead live_list_;
+  SpinMutex list_mutex_;
+
+  ThreadStats stats_;
+  SpinMutex stats_mutex_;
+};
+
+void InitThreadList(uptr storage, uptr size);
+HwasanThreadList &hwasanThreadList();
+
+} // namespace
diff --git a/libsanitizer/hwasan/hwasan_type_test.cpp b/libsanitizer/hwasan/hwasan_type_test.cpp
new file mode 100644
index 00000000000..8cff495bae1
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_type_test.cpp
@@ -0,0 +1,25 @@
+//===-- hwasan_type_test.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Compile-time tests of the internal type definitions.
+//===----------------------------------------------------------------------===//
+
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include "hwasan.h"
+#include <setjmp.h>
+
+#define CHECK_TYPE_SIZE_FITS(TYPE) \
+  COMPILER_CHECK(sizeof(__hw_##TYPE) <= sizeof(TYPE))
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+CHECK_TYPE_SIZE_FITS(jmp_buf);
+CHECK_TYPE_SIZE_FITS(sigjmp_buf);
+#endif
diff --git a/libsanitizer/merge.sh b/libsanitizer/merge.sh
index 3f4f1629a22..95ded4f9634 100755
--- a/libsanitizer/merge.sh
+++ b/libsanitizer/merge.sh
@@ -71,6 +71,7 @@ merge lib/tsan/rtl tsan
 merge lib/sanitizer_common sanitizer_common
 merge lib/interception interception
 merge lib/ubsan ubsan
+merge lib/hwasan hwasan
 
 # Need to merge lib/builtins/assembly.h file:
 mkdir -p builtins
-- 
2.28.0


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system
  2020-10-13 15:57       ` Richard Sandiford
@ 2020-10-28 13:58         ` Matthew Malcomson
  2020-10-28 15:02           ` Richard Sandiford
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-10-28 13:58 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: gcc-patches, kcc, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

Hi Richard,

I've done most of the updates you suggested, but have a few questions to
ensure I have the right end of the stick before making the remaining changes.

There are also a few clarifications I'd like to make where I hadn't explained
the rationale for certain bits of the original code, and I think that those
clarifications may lead to further changes you'd like.

I'm putting all the questions and extra clarifications in one email rather than
replying to each of the emails in turn.

--- w.r.t Patch 1

- You suggested this should also update README.gcc and merge.sh.
I believe Martin Liska wanted to perform the merge of libhwasan into the
libsanitizer directory, and that seems to make a lot of sense given that he
knows a lot more here.
He's posted a patch that would do this work here
https://gcc.gnu.org/pipermail/gcc-patches/2020-October/556365.html

--- w.r.t. Patch 2

- You correctly read that the `configure.tgt` indicates that HWASAN is supported
for ilp32, that's my mistake.  HWASAN does not actually handle ilp32 (the hwasan
library functions `__hwasan_load*` take pointers as arguments and check the top
bytes which obviously can't pass that top byte).

N.b. I've may have mentioned ilp32 a few times and put comments discussing
ptr_mode and Pmode being different, but that's more to do with trying to ensure
the code is done "properly" rather than actually ensuring the functionality
works on ilp32.

--- w.r.t. Patch 4

- Parametrising the tag size is pretty easy towards less bits, the current code
handles this quite nicely using HWASAN_TAG_SIZE and in fact I've already done
that for a WIP branch for MTE stack tagging.

Parametrising the tag size to more bits is much more involved -- especially
since the hwasan library uses a u8 data type to represent these tags
everywhere.  Hence I figure I'll leave this as it is.

Does that sound ok?

--- w.r.t. Patch 5

- Around hwasan_increment_tag, yes -- the STATIC_ASSERT made the modulus
reduntant, it should have asserted the below (since the "less than or equal"
check still works for the smaller tag size used in MTE rather than fixing it to
tag_offset).
HWASAN_TAG_SIZE <= sizeof (tag_offset) * CHAR_BIT

- Exporting hwasan_base_ptr (which will be renamed to hwasan_frame_base_ptr).
I currently export this through a function hwasan_frame_base rather than via an
exported variable.  I want to do this so that any use of the base pointer will
be recorded so we know to emit the initialisation (whereas a use of
virtual_stack_vars_rtx can rest assured that the pointer will always be
initialised).

N.b. This is also related to API for TARGET_MEMTAG_GENTAG (renamed in the
new patch to TARGET_MEMTAG_INSERT_RANDOM_TAG).  I have tried to separate
generating a register to be used as hwasan_frame_base_ptr and emitting the RTL
to initialise that register.
That way, code that knows it does not want to emit any RTL can still access this
variable, knowing that the initialisation will be emitted later.
This is largely because I didn't want to start spreading the possibility of
emitting RTL earlier in the expand pass.  At the moment hwasan_emit_prologue
is often the first time that RTL is emitted (unless there are large aligned
variables in the stack -- these are indexed off of an alternate "base register"
generated using get_dynamic_stack_base, and that function emits some code to
generate that new register).

However, an alternative API which returns a fresh register would match the
interface of get_dynamic_stack_base which is an existing API in the codebase.

- default_memtag_addtag (now *_add_tag)
I was mentioning compile-time UB, I thought that `plus_constant` taking a
poly_int64 rather than a poly_uint64 meant I can't pass such a large number.


--- w.r.t. Patch 6

- I have added a comment about C++ exceptions, but thought I'd include a bit
more information about the state of things here.
LLVM have support for C++ exceptions by using a different unwinding personality
function for all HWASAN tagged frames.  That personality function is defined in
libhwasan, and untags the entire stack frame as we unwind it.

Unfortunately, that personality function relies on the frame pointer pointing to
just before the variables on the stack (commented as not being enforced by the
ABI but being a requirement for HWASAN).  That holds in LLVM but does not for
GCC.
https://github.com/llvm-mirror/compiler-rt/blob/master/lib/hwasan/hwasan_exceptions.cpp#L51

I have a hack that modifies that function to use _Unwind_Backtrace to find the
extent of the current frame, and then adds the exception support to GCC based on
this new personality function.
Since the focus of the implementation in GCC is for the kernel (which doesn't
have C++ exceptions) I don't have plans to turn that into something
release-quality and upstream it.  The userspace story for hwasan on anything
other than Android has much bigger difficulties around not using the "platform
ABI", so I don't think putting much effort into C++ exceptions makes sense right
now.

- LLVM does nothing special with strlen, it just doesn't instrument the call.

- About maybe combining the hwasan pass and the asan pass.
Yes we could just branch the "asan" pass based on hwasan vs asan flags.
I would prefer to have the "hwasan" pass separate (I liked having the
"correct" names for the -fdump-tree* arguments and dump files).
That said -- if you feel strongly about this I'll happily change it.
________________________________
From: Richard Sandiford <richard.sandiford@arm.com>
Sent: 13 October 2020 16:57
To: Matthew Malcomson <Matthew.Malcomson@arm.com>
Cc: gcc-patches@gcc.gnu.org <gcc-patches@gcc.gnu.org>; kcc@google.com <kcc@google.com>; Richard Earnshaw <Richard.Earnshaw@arm.com>; jakub@redhat.com <jakub@redhat.com>; joseph@codesourcery.com <joseph@codesourcery.com>; dvyukov@google.com <dvyukov@google.com>; Kyrylo Tkachov <Kyrylo.Tkachov@arm.com>; dodji@redhat.com <dodji@redhat.com>; Martin Liska <mliska@suse.cz>
Subject: Re: [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system

Sorry for the slow review.

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> This patch tries to tie libhwasan into the GCC build system in the same way
> that the other sanitizer runtime libraries are handled.
>
> libsanitizer/ChangeLog:
>
>        * Makefile.am:  Build libhwasan.
>        * Makefile.in:  Build libhwasan.
>        * asan/Makefile.in:  Build libhwasan.
>        * configure:  Build libhwasan.
>        * configure.ac:  Build libhwasan.
>        * hwasan/Makefile.am: New file.
>        * hwasan/Makefile.in: New file.
>        * hwasan/libtool-version: New file.
>        * interception/Makefile.in: Build libhwasan.
>        * libbacktrace/Makefile.in: Build libhwasan.
>        * libsanitizer.spec.in: Build libhwasan.
>        * lsan/Makefile.in: Build libhwasan.
>        * sanitizer_common/Makefile.in: Build libhwasan.
>        * tsan/Makefile.in: Build libhwasan.
>        * ubsan/Makefile.in: Build libhwasan.

I think this should also update README.gcc and merge.sh.  Could you
try locally merging in a dummy change to the llvm sources with merge.sh,
to make sure it works correctly?

> new file mode 100644
> index 0000000000000000000000000000000000000000..aaa39b4536a5c5f54910a951470814bbc8a20946
> --- /dev/null
> +++ b/libsanitizer/hwasan/Makefile.am
> @@ -0,0 +1,88 @@
> +AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
> +
> +# May be used by toolexeclibdir.
> +gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
> +
> +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
> +AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -fno-ipa-icf

I realise this is just taken from the other Makefile.ams, but do
you know the reason behind -fomit-frame-pointer?  I think we should
avoid building aarch64 libraries with that flag unless there's a
specific reason.

Otherwise looks good to me, although I'm definitely not an expert
on this stuff.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system
  2020-10-28 13:58         ` Matthew Malcomson
@ 2020-10-28 15:02           ` Richard Sandiford
  0 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-10-28 15:02 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, kcc, Richard Earnshaw, jakub, joseph, dvyukov,
	Kyrylo Tkachov, dodji, Martin Liska

I have the feeling that I've picked the most awkward of each binary
choice here, sorry…

Matthew Malcomson <Matthew.Malcomson@arm.com> writes:
> - Parametrising the tag size is pretty easy towards less bits, the current code
> handles this quite nicely using HWASAN_TAG_SIZE and in fact I've already done
> that for a WIP branch for MTE stack tagging.
>
> Parametrising the tag size to more bits is much more involved -- especially
> since the hwasan library uses a u8 data type to represent these tags
> everywhere.  Hence I figure I'll leave this as it is.
>
> Does that sound ok?

If it's easy to parameterise towards fewer bits, I think it'd be worth
having a target twiddle for it and simply asserting that the value is
not more than 8.

> --- w.r.t. Patch 5
>
> - Around hwasan_increment_tag, yes -- the STATIC_ASSERT made the modulus
> reduntant, it should have asserted the below (since the "less than or equal"
> check still works for the smaller tag size used in MTE rather than fixing it to
> tag_offset).
> HWASAN_TAG_SIZE <= sizeof (tag_offset) * CHAR_BIT

OK.

> - Exporting hwasan_base_ptr (which will be renamed to hwasan_frame_base_ptr).
> I currently export this through a function hwasan_frame_base rather than via an
> exported variable.  I want to do this so that any use of the base pointer will
> be recorded so we know to emit the initialisation (whereas a use of
> virtual_stack_vars_rtx can rest assured that the pointer will always be
> initialised).

The reason for that comment was that the cfgexpand.c code seemed to have
to work around the fact that it couldn't directly tell whether a given
rtx was hwasan_base_ptr.  I think we should have the ability to test that,
even if it's via a function rather than by directly exposing the variable.

E.g. maybe you could have a little class around it or something, so that
the comparison still happens inline via a public member function, but so
that it's not possible for cfgexpand.c to use the private value directly.

> N.b. This is also related to API for TARGET_MEMTAG_GENTAG (renamed in the
> new patch to TARGET_MEMTAG_INSERT_RANDOM_TAG).  I have tried to separate
> generating a register to be used as hwasan_frame_base_ptr and emitting the RTL
> to initialise that register.
> That way, code that knows it does not want to emit any RTL can still access
> this
> variable, knowing that the initialisation will be emitted later.
> This is largely because I didn't want to start spreading the possibility of
> emitting RTL earlier in the expand pass.  At the moment hwasan_emit_prologue
> is often the first time that RTL is emitted (unless there are large aligned
> variables in the stack -- these are indexed off of an alternate "base register"
> generated using get_dynamic_stack_base, and that function emits some code to
> generate that new register).
>
> However, an alternative API which returns a fresh register would match the
> interface of get_dynamic_stack_base which is an existing API in the codebase.

Yeah, I think that would be better.

> - default_memtag_addtag (now *_add_tag)
> I was mentioning compile-time UB, I thought that `plus_constant` taking a
> poly_int64 rather than a poly_uint64 meant I can't pass such a large number.

No, it's just that C++ forces us to choose between signed or unsigned,
even though for modes of N<=64 bits, the offset is really just a
signless bag of N bits.

I think the reason a signed type was chosen is that, when operating
on 32-bit modes, adding int64_t (-1) looks more obvious than adding
~uint64_t (0), since the latter seems to be adding to bits beyond
the MSB.

> - I have added a comment about C++ exceptions, but thought I'd include a bit
> more information about the state of things here.
> LLVM have support for C++ exceptions by using a different unwinding personality
> function for all HWASAN tagged frames.  That personality function is defined in
> libhwasan, and untags the entire stack frame as we unwind it.
>
> Unfortunately, that personality function relies on the frame pointer pointing
> to
> just before the variables on the stack (commented as not being enforced by the
> ABI but being a requirement for HWASAN).  That holds in LLVM but does not for
> GCC.
> https://github.com/llvm-mirror/compiler-rt/blob/master/lib/hwasan/
> hwasan_exceptions.cpp#L51
>
> I have a hack that modifies that function to use _Unwind_Backtrace to find the
> extent of the current frame, and then adds the exception support to GCC based
> on
> this new personality function.
> Since the focus of the implementation in GCC is for the kernel (which doesn't
> have C++ exceptions) I don't have plans to turn that into something
> release-quality and upstream it.  The userspace story for hwasan on anything
> other than Android has much bigger difficulties around not using the "platform
> ABI", so I don't think putting much effort into C++ exceptions makes sense
> right
> now.

OK.

> - About maybe combining the hwasan pass and the asan pass.
> Yes we could just branch the "asan" pass based on hwasan vs asan flags.
> I would prefer to have the "hwasan" pass separate (I liked having the
> "correct" names for the -fdump-tree* arguments and dump files).
> That said -- if you feel strongly about this I'll happily change it.

Well, I'd argue that “asan” is a correct name too. :-)  It feels similar
to not having different passes for using (say) glibc vector libm routines
and proprietary vector libm routines.  The two libm libraries work
differently internally and might provide different sets of vector routines,
but at the pass level, they achieve the same basic thing and plug into the
compiler at the same points.  And at a high level, they're both vector
libm routines.

Here the (old) asan and hwasan are two different, mutually-incompatible
ways of providing an address sanitisation feature.  Although the details
differ, where and how they plug into the compiler is very similar, and
the patch is (rightly) reusing some of the asan ifns and enums.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN
  2020-10-14 16:09       ` Richard Sandiford
@ 2020-11-03 13:00         ` Matthew Malcomson
  2020-11-04 15:43           ` Richard Sandiford
  2020-11-19 12:57         ` Update: " Matthew Malcomson
  1 sibling, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-03 13:00 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: gcc-patches, Richard Earnshaw, joseph, Kyrylo Tkachov, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 56276 bytes --]

Hi Richard,

I'm sending up the revised patch 5 (introducing stack variable handling)
without the other changes to other patches.

I figure there's been quite a lot of changes to this patch and I wanted
to give you time to review them while I worked on finishing the less
widespread changes in patch 6 and before I ran the more exhaustive (and
time-consuming) tests in case you didn't like the changes and those
exhaustive tests would just have to get repeated.

The big differences between this and the last version are:

- I moved all helper functions which rely on how the tag is encoded into
  hooks.  This allows backends to choose a different ABI for hwasan
  tagging.  That said, any ABI which doesn't ensure the entire the tag
  is stored as a top byte is unsupported as the library doesn't handle
  anything else.

- I no longer delay emitting RTL to initialise the hwasan_base_pointer.
  It's now emitted as soon as the pointer is used anywhere.

- No longer delay allocating stack variables for hwasan to work.
  Instead we record stack variables when allocating through
  expand_stack_var_1 *and* when allocating through expand_stack_vars.

- Use `frame_offset` in more places to avoid having to manually handle
  the case of `!FRAME_GROWS_DOWNWARDS`.



------------------------------------------------------------------------

Handling stack variables has three features.

1) Ensure HWASAN required alignment for stack variables

When tagging shadow memory, we need to ensure that each tag granule is
only used by one variable at a time.

This is done by ensuring that each tagged variable is aligned to the tag
granule representation size and also ensure that the end of each
object is aligned to ensure the start of any other data stored on the
stack is in a different granule.

This patch ensures the above by adding alignment requirements in
`align_local_variable` and forcing the stack pointer to be aligned before
allocating any stack objects.

2) Put tags into each stack variable pointer

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

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

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

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

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

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

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

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

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

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

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


ChangeLog:

	* config/bootstrap-hwasan.mk: Disable random frame tags for
	stack-tagging during bootstrap.
	* gcc/asan.c (struct hwasan_stack_var): New.
	(hwasan_sanitize_p): New.
	(hwasan_sanitize_stack_p): New.
	(hwasan_sanitize_allocas_p): New.
	(initialize_sanitizer_builtins): Define new builtins.
	(ATTR_NOTHROW_LIST): New macro.
	(hwasan_current_frame_tag): New.
	(hwasan_frame_base): New.
	(hwasan_record_stack_var): New.
	(hwasan_get_frame_extent): New.
	(hwasan_increment_frame_tag): New.
	(hwasan_record_frame_init): New.
	(hwasan_emit_prologue): New.
	(hwasan_emit_untag_frame): New.
	(hwasan_finish_file): New.
	(hwasan_truncate_to_tag_size): New.
	* gcc/asan.h (hwasan_record_frame_init): New declaration.
	(hwasan_record_stack_var): New declaration.
	(hwasan_emit_prologue): New declaration.
	(hwasan_emit_untag_frame): New declaration.
	(hwasan_get_frame_extent): New declaration.
	(hwasan_frame_base): New declaration.
	(hwasan_current_frame_tag): New declaration.
	(hwasan_increment_frame_tag): New declaration.
	(hwasan_truncate_to_tag_size): New declaration.
	(hwasan_finish_file): New declaration.
	(hwasan_sanitize_p): New declaration.
	(hwasan_sanitize_stack_p): New declaration.
	(hwasan_sanitize_allocas_p): New declaration.
	(HWASAN_TAG_SIZE): New macro.
	(HWASAN_TAG_GRANULE_SIZE): New macro.
	(HWASAN_STACK_BACKGROUND): New macro.
	* gcc/builtin-types.def (BT_FN_VOID_PTR_UINT8_PTRMODE): New.
	* gcc/builtins.def (DEF_SANITIZER_BUILTIN): Enable for HWASAN.
	* gcc/cfgexpand.c (align_local_variable): When using hwasan ensure
	alignment to tag granule.
	(align_frame_offset): New.
	(expand_one_stack_var_at): For hwasan use tag offset.
	(expand_stack_vars): Record stack objects for hwasan.
	(expand_one_stack_var_1): Record stack objects for hwasan.
	(init_vars_expansion): Initialise hwasan state.
	(expand_used_vars): Emit hwasan prologue and generate hwasan epilogue.
	* gcc/doc/tm.texi (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
	TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
	TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
	TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
	* gcc/doc/tm.texi.in (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
	TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
	TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
	TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
	* gcc/explow.c (get_dynamic_stack_base): Take new `base` argument.
	* gcc/explow.h (get_dynamic_stack_base): Take new `base` argument.
	* gcc/sanitizer.def (BUILT_IN_HWASAN_INIT): New.
	(BUILT_IN_HWASAN_TAG_MEM): New.
	* gcc/target.def (target_memtag_tag_size,target_memtag_granule_size,
	target_memtag_insert_random_tag,target_memtag_add_tag,
	target_memtag_set_tag,target_memtag_extract_tag,
	target_memtag_untagged_pointer): New hooks.
	* gcc/targhooks.c (HWASAN_SHIFT): New.
	(HWASAN_SHIFT_RTX): New.
	(default_memtag_tag_size): New default hook.
	(default_memtag_granule_size): New default hook.
	(default_memtag_insert_random_tag): New default hook.
	(default_memtag_add_tag): New default hook.
	(default_memtag_set_tag): New default hook.
	(default_memtag_extract_tag): New default hook.
	(default_memtag_untagged_pointer): New default hook.
	* gcc/targhooks.h (default_memtag_tag_size): New default hook.
	(default_memtag_granule_size): New default hook.
	(default_memtag_insert_random_tag): New default hook.
	(default_memtag_add_tag): New default hook.
	(default_memtag_set_tag): New default hook.
	(default_memtag_extract_tag): New default hook.
	(default_memtag_untagged_pointer): New default hook.
	* gcc/toplev.c (compile_file): Call hwasan_finish_file when finished.


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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -1,7 +1,11 @@
 # This option enables -fsanitize=hwaddress for stage2 and stage3.
+# We need to disable random frame tags for bootstrap since the autoconf check
+# for which direction the stack is growing has UB that a random frame tag
+# breaks.  Running with a random frame tag gives approx. 50% chance of
+# bootstrap comparison diff in libiberty/alloca.c.
 
-STAGE2_CFLAGS += -fsanitize=hwaddress
-STAGE3_CFLAGS += -fsanitize=hwaddress
+STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
 POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
diff --git a/gcc/asan.h b/gcc/asan.h
index 9efd33f9b86babbc10c4553c31b86950a313a242..918ee63000714247c2e1b0d810f56a2330dcf46f 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -34,6 +34,20 @@ extern bool asan_expand_mark_ifn (gimple_stmt_iterator *);
 extern bool asan_expand_poison_ifn (gimple_stmt_iterator *, bool *,
 				    hash_map<tree, tree> &);
 
+extern void hwasan_record_frame_init ();
+extern void hwasan_record_stack_var (rtx, rtx, poly_int64, poly_int64);
+extern void hwasan_emit_prologue ();
+extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx);
+extern rtx hwasan_get_frame_extent ();
+extern rtx hwasan_frame_base ();
+extern uint8_t hwasan_current_frame_tag ();
+extern void hwasan_increment_frame_tag ();
+extern rtx hwasan_truncate_to_tag_size (rtx, rtx);
+extern void hwasan_finish_file (void);
+extern bool hwasan_sanitize_p (void);
+extern bool hwasan_sanitize_stack_p (void);
+extern bool hwasan_sanitize_allocas_p (void);
+
 extern gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
 
@@ -75,6 +89,26 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The values below and the hooks under targetm.memtag define an ABI and
+   are hard-coded to these values in libhwasan, hence they can't be changed
+   independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   The default version uses the entire top byte of a pointer (i.e. 8 bits).  */
+#define HWASAN_TAG_SIZE targetm.memtag.tag_size ()
+/* Tag Granule of HWASAN shadow stack.
+   This is the size in real memory that each byte in the shadow memory refers
+   to.  I.e. if a variable is X bytes long in memory then it's tag in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   Most variables will need to be aligned to this amount since two variables
+   that are neighbours in memory and share a tag granule would need to share
+   the same tag (the shared tag granule can only store one tag).  */
+#define HWASAN_TAG_GRANULE_SIZE targetm.memtag.granule_size ()
+/* Define the tag for the stack background.
+   This defines what tag the stack pointer will be and hence what tag all
+   variables that are not given special tags are (e.g. spilled registers,
+   and parameters passed on the stack).  */
+#define HWASAN_STACK_BACKGROUND gen_int_mode (0, QImode)
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/asan.c b/gcc/asan.c
index 9c9aa4cae35832c1534a2cffac1d3d13eed0e687..80dd4f0b6f92031b68c85bfea76c7012f7b5206c 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -257,6 +257,47 @@ hash_set<tree> *asan_handled_variables = NULL;
 
 hash_set <tree> *asan_used_labels = NULL;
 
+/* Global variables for HWASAN stack tagging.  */
+/* hwasan_frame_tag_offset records the offset from the frame base tag that the
+   next object should have.  */
+static uint8_t hwasan_frame_tag_offset = 0;
+/* hwasan_frame_base_ptr is a pointer with the same address as
+   `virtual_stack_vars_rtx` for the current frame, and with the frame base tag
+   stored in it.  N.b. this global RTX does not need to be marked GTY, but is
+   done so anyway.  The need is not there since all uses are in just one pass
+   (cfgexpand) and there are no calls to ggc_collect between the uses.  We mark
+   it GTY(()) anyway to allow the use of the variable later on if needed by
+   future features.  */
+static GTY(()) rtx hwasan_frame_base_ptr = NULL_RTX;
+
+/* Structure defining the extent of one object on the stack that HWASAN needs
+   to tag in the corresponding shadow stack space.
+
+   The range this object spans on the stack is between `untagged_base +
+   nearest_offset` and `untagged_base + farthest_offset`.
+   `tagged_base` is an rtx containing the same value as `untagged_base` but
+   with a random tag stored in the top byte.  We record both `untagged_base`
+   and `tagged_base` so that `hwasan_emit_prologue` can use both without having
+   to emit RTL into the instruction stream to re-calculate one from the other.
+   (`hwasan_emit_prologue` needs to use both bases since the
+   __hwasan_tag_memory call it emits uses an untagged value, and it calculates
+   the tag to store in shadow memory based on the tag_offset plus the tag in
+   tagged_base).  */
+struct hwasan_stack_var
+{
+  rtx untagged_base;
+  rtx tagged_base;
+  poly_int64 nearest_offset;
+  poly_int64 farthest_offset;
+  uint8_t tag_offset;
+};
+
+/* Variable recording all stack variables that HWASAN needs to tag.
+   Does not need to be marked as GTY(()) since every use is in the cfgexpand
+   pass and gcc_collect is not called in the middle of that pass.  */
+static vec<hwasan_stack_var *> hwasan_tagged_stack_vars;
+
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -1352,6 +1393,28 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+/* Returns whether we are tagging pointers and checking those tags on memory
+   access.  */
+bool
+hwasan_sanitize_p ()
+{
+  return sanitize_flags_p (SANITIZE_HWADDRESS);
+}
+
+/* Are we tagging the stack?  */
+bool
+hwasan_sanitize_stack_p ()
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_stack);
+}
+
+/* Are we protecting alloca objects?  */
+bool
+hwasan_sanitize_allocas_p (void)
+{
+  return (hwasan_sanitize_stack_p () && param_hwasan_protect_allocas);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -2901,6 +2964,11 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_VOID_PTR_UINT8_PTRMODE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				unsigned_char_type_node,
+				pointer_sized_int_node, NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
@@ -2951,6 +3019,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NOTHROW_LIST
+#define ATTR_NOTHROW_LIST ECF_NOTHROW
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
@@ -3702,4 +3772,330 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/* For stack tagging:
+
+   Return the offset from the frame base tag that the "next" expanded object
+   should have.  */
+uint8_t
+hwasan_current_frame_tag ()
+{
+  return hwasan_frame_tag_offset;
+}
+
+/* For stack tagging:
+
+   Return the 'base pointer' for this function.  If that base pointer has not
+   yet been created then we create a register to hold it and initialise that
+   value with a possibly random tag and the value of the
+   virtual_stack_vars_rtx.  */
+rtx
+hwasan_frame_base ()
+{
+  if (! hwasan_frame_base_ptr)
+    {
+      hwasan_frame_base_ptr
+	= targetm.memtag.insert_random_tag (virtual_stack_vars_rtx);
+    }
+
+  return hwasan_frame_base_ptr;
+}
+
+/* Record a compile-time constant size stack variable that HWASAN will need to
+   tag.  This record of the range of a stack variable will be used by
+   `hwasan_emit_prologue` to emit the RTL at the start of each frame which will
+   set tags in the shadow memory according to the assigned tag for each object.
+
+   The range that the object spans in stack space should be described by the
+   bounds `untagged_base + nearest` and `untagged_base + farthest`.
+   `tagged_base` is the base address which contains the "base frame tag" for
+   this frame, and from which the value to address this object with will be
+   calculated.
+
+   We record the `untagged_base` since the functions in the hwasan library we
+   use to tag memory take pointers without a tag.  */
+void
+hwasan_record_stack_var (rtx untagged_base, rtx tagged_base,
+			 poly_int64 nearest, poly_int64 farthest)
+{
+  hwasan_stack_var *cur_var = new hwasan_stack_var;
+  cur_var->untagged_base = untagged_base;
+  cur_var->tagged_base = tagged_base;
+  cur_var->nearest_offset = nearest;
+  cur_var->farthest_offset = farthest;
+  cur_var->tag_offset = hwasan_current_frame_tag ();
+
+  hwasan_tagged_stack_vars.safe_push (cur_var);
+}
+
+/* Return the RTX representing the farthest extent of the statically allocated
+   stack objects for this frame.  If hwasan_frame_base_ptr has not been
+   initialised then we are not storing any static variables on the stack in
+   this frame.  In this case we return NULL_RTX to represent that.
+
+   Otherwise simply return virtual_stack_vars_rtx + frame_offset.  */
+rtx
+hwasan_get_frame_extent ()
+{
+  return hwasan_frame_base_ptr ?
+    plus_constant (Pmode, virtual_stack_vars_rtx, frame_offset)
+    : NULL_RTX;
+}
+
+
+/* For stack tagging:
+
+   Increment the tag offset modulo the size a tag can represent.  */
+void
+hwasan_increment_frame_tag ()
+{
+  uint8_t tag_bits = HWASAN_TAG_SIZE;
+  gcc_assert (HWASAN_TAG_SIZE
+	      <= sizeof (hwasan_frame_tag_offset) * CHAR_BIT);
+  hwasan_frame_tag_offset = (hwasan_frame_tag_offset + 1) % (1 << tag_bits);
+  /* The "background tag" of the stack is zero by definition.
+     This is the tag that objects like parameters passed on the stack and
+     spilled registers are given.  It is handy to avoid this tag for objects
+     whose tags we decide ourselves, partly to ensure that buffer overruns
+     can't affect these important variables (e.g. saved link register, saved
+     stack pointer etc) and partly to make debugging easier (everything with a
+     tag of zero is space allocated automatically by the compiler).
+
+     This is not feasible when using random frame tags (the default
+     configuration for hwasan) since the tag for the given frame is randomly
+     chosen at runtime.  In order to avoid any tags matching the stack
+     background we would need to decide tag offsets at runtime instead of
+     compile time (and pay the resulting performance cost).
+
+     When not using random base tags for each frame (i.e. when compiled with
+     `--param hwasan-random-frame-tag=0`) the base tag for each frame is zero.
+     This means the tag that each object gets is equal to the
+     hwasan_frame_tag_offset used in determining it.
+     When this is the case we *can* ensure no object gets the tag of zero by
+     simply ensuring no object has the hwasan_frame_tag_offset of zero.
+
+     There is the extra complication that we only record the
+     hwasan_frame_tag_offset here (which is the offset from the tag stored in
+     the stack pointer).  In the kernel, the tag in the stack pointer is 0xff
+     rather than zero.  This does not cause problems since tags of 0xff are
+     never checked in the kernel.  As mentioned at the beginning of this
+     comment the background tag of the stack is zero by definition, which means
+     that for the kernel we should skip offsets of both 0 and 1 from the stack
+     pointer.  Avoiding the offset of 0 ensures we use a tag which will be
+     checked, avoiding the offset of 1 ensures we use a tag that is not the
+     same as the background.  */
+  if (hwasan_frame_tag_offset == 0 && ! param_hwasan_random_frame_tag)
+    hwasan_frame_tag_offset += 1;
+  if (hwasan_frame_tag_offset == 1 && ! param_hwasan_random_frame_tag
+      && sanitize_flags_p (SANITIZE_KERNEL_HWADDRESS))
+    hwasan_frame_tag_offset += 1;
+}
+
+/* Clear internal state for the next function.
+   This function is called before variables on the stack get expanded, in
+   `init_vars_expansion`.  */
+void
+hwasan_record_frame_init ()
+{
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
+  /* If this isn't the case then some stack variable was recorded *before*
+     hwasan_record_frame_init is called, yet *after* the hwasan prologue for
+     the previous frame was emitted.  Such stack variables would not have
+     their shadow stack filled in.  */
+  gcc_assert (hwasan_tagged_stack_vars.is_empty ());
+  hwasan_frame_base_ptr = NULL_RTX;
+
+  /* When not using a random frame tag we can avoid the background stack
+     color which gives the user a little better debug output upon a crash.
+     Meanwhile, when using a random frame tag it will be nice to avoid adding
+     tags for the first object since that is unnecessary extra work.
+     Hence set the initial hwasan_frame_tag_offset to be 0 if using a random
+     frame tag and 1 otherwise.
+
+     As described in hwasan_increment_frame_tag, in the kernel the stack
+     pointer has the tag 0xff.  That means that to avoid 0xff and 0 (the tag
+     which the kernel does not check and the background tag respectively) we
+     start with a tag offset of 2.  */
+  hwasan_frame_tag_offset = param_hwasan_random_frame_tag
+    ? 0
+    : sanitize_flags_p (SANITIZE_KERNEL_HWADDRESS) ? 2 : 1;
+}
+
+/* For stack tagging:
+   (Emits HWASAN equivalent of what is emitted by
+   `asan_emit_stack_protection`).
+
+   Emits the extra prologue code to set the shadow stack as required for HWASAN
+   stack instrumentation.
+
+   BASES is an array containing the tagged base registers for each object.
+   We map each object to a given base since large aligned objects have a
+   different base to others and we need to know which objects use which base.
+
+   UNTAGGED_BASES contains the same information as above except without tags.
+   This is needed since libhwasan only accepts untagged pointers in
+   __hwasan_tag_memory.
+
+   OFFSETS is an array with the start and end offsets for each object stored on
+   the stack in this frame.  This array is hence twice the length of the other
+   array arguments (given it has two entries for each stack object).
+
+   TAGS is an array containing the tag *offset* each object should have from
+   the tag in its base pointer.
+
+   LENGTH contains the length of the OFFSETS array.  */
+void
+hwasan_emit_prologue ()
+{
+  /* We need untagged base pointers since libhwasan only accepts untagged
+    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
+    the base tag for an offset.  */
+
+  gcc_assert ((hwasan_frame_base_ptr == NULL_RTX)
+	      == hwasan_tagged_stack_vars.is_empty ());
+  if (! hwasan_frame_base_ptr)
+    return;
+
+  size_t length = hwasan_tagged_stack_vars.length ();
+  hwasan_stack_var **vars = hwasan_tagged_stack_vars.address ();
+
+  poly_int64 bot = 0, top = 0;
+  size_t i = 0;
+  for (i = 0; i < length; i++)
+    {
+      hwasan_stack_var *cur = vars[i];
+      poly_int64 nearest = cur->nearest_offset;
+      poly_int64 farthest = cur->farthest_offset;
+
+      if (known_ge (nearest, farthest))
+	{
+	  top = nearest;
+	  bot = farthest;
+	}
+      else
+	{
+	  /* Given how these values are calculated, one must be known greater
+	     than the other.  */
+	  gcc_assert (known_le (nearest, farthest));
+	  top = farthest;
+	  bot = nearest;
+	}
+      poly_int64 size = (top - bot);
+
+      /* Assert the edge of each variable is aligned to the HWASAN tag granule
+	 size.  */
+      gcc_assert (multiple_p (top, HWASAN_TAG_GRANULE_SIZE));
+      gcc_assert (multiple_p (bot, HWASAN_TAG_GRANULE_SIZE));
+      gcc_assert (multiple_p (size, HWASAN_TAG_GRANULE_SIZE));
+
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      rtx base_tag = targetm.memtag.extract_tag (cur->tagged_base, NULL_RTX);
+      rtx tag = plus_constant (QImode, base_tag, cur->tag_offset);
+      tag = hwasan_truncate_to_tag_size (tag, NULL_RTX);
+
+      rtx bottom = convert_memory_address (ptr_mode,
+					   plus_constant (Pmode,
+							  cur->untagged_base,
+							  bot));
+      emit_library_call (ret, LCT_NORMAL, VOIDmode,
+			 bottom, ptr_mode,
+			 tag, QImode,
+			 gen_int_mode (size, ptr_mode), ptr_mode);
+      delete cur;
+    }
+  /* Clear the stack vars, we've emitted the prologue for them all now.  */
+  hwasan_tagged_stack_vars.truncate (0);
+}
+
+/* For stack tagging:
+
+   Return RTL insns to clear the tags between DYNAMIC and VARS pointers
+   into the stack.  These instructions should be emitted at the end of
+   every function.
+
+   If `dynamic` is NULL_RTX then no insns are returned.  */
+rtx_insn *
+hwasan_emit_untag_frame (rtx dynamic, rtx vars)
+{
+  if (! dynamic)
+    return NULL;
+
+  start_sequence ();
+
+  dynamic = convert_memory_address (ptr_mode, dynamic);
+  vars = convert_memory_address (ptr_mode, vars);
+
+  rtx top_rtx;
+  rtx bot_rtx;
+  if (FRAME_GROWS_DOWNWARD)
+    {
+      top_rtx = vars;
+      bot_rtx = dynamic;
+    }
+  else
+    {
+      top_rtx = dynamic;
+      bot_rtx = vars;
+    }
+
+  rtx size_rtx = expand_simple_binop (ptr_mode, MINUS, top_rtx, bot_rtx,
+				      NULL_RTX, /* unsignedp = */0,
+				      OPTAB_DIRECT);
+
+  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (ret, LCT_NORMAL, VOIDmode,
+		     bot_rtx, ptr_mode,
+		     HWASAN_STACK_BACKGROUND, QImode,
+		     size_rtx, ptr_mode);
+
+  do_pending_stack_adjust ();
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+/* Needs to be GTY(()), because cgraph_build_static_cdtor may
+   invoke ggc_collect.  */
+static GTY(()) tree hwasan_ctor_statements;
+
+/* Insert module initialization into this TU.  This initialization calls the
+   initialization code for libhwasan.  */
+void
+hwasan_finish_file (void)
+{
+  /* Do not emit constructor initialization for the kernel.
+     (the kernel has its own initialization already).  */
+  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+    return;
+
+  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
+  flag_sanitize &= ~SANITIZE_HWADDRESS;
+  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
+  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
+  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
+  flag_sanitize |= SANITIZE_HWADDRESS;
+}
+
+/* For stack tagging:
+
+   Truncate `tag` to the number of bits that a tag uses (i.e. to
+   HWASAN_TAG_SIZE).  Store the result in `target` if it's convenient.  */
+rtx
+hwasan_truncate_to_tag_size (rtx tag, rtx target)
+{
+  gcc_assert (GET_MODE (tag) == QImode);
+  if (HWASAN_TAG_SIZE != GET_MODE_PRECISION (QImode))
+    {
+      gcc_assert (GET_MODE_PRECISION (QImode) > HWASAN_TAG_SIZE);
+      tag = expand_simple_binop (QImode, AND, tag,
+				 gen_int_mode (-HWASAN_TAG_SIZE, QImode),
+				 target,
+				 /* unsignedp = */1, OPTAB_WIDEN);
+      gcc_assert (tag);
+    }
+  return tag;
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index c46b1bc5cbd1fba03b033b8d44ba186570780c3f..535366d104b9cec02ed2d07682476eebdbbe9161 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -637,6 +637,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
 DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
 DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_CONST_SIZE_BOOL,
 		     BT_PTR, BT_PTR, BT_CONST_SIZE, BT_BOOL)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_PTRMODE, BT_VOID, BT_PTR, BT_UINT8,
+		     BT_PTRMODE)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index ee67ac15d5cf98797144b9d08a75f4cf7ee5ad33..92121fb898bbf7a90aa0e43c65ff3fe7b20d7c99 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -250,6 +250,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
+				| SANITIZE_HWADDRESS \
 				| SANITIZE_UNDEFINED \
 				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index b270a4ddb9db469ba52e42f36a1bc2f02d8f03fc..52a0dd6dfda1868622b88821a6d4abc5f2c7ce03 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -365,19 +365,22 @@ align_local_variable (tree decl, bool really_expand)
 {
   unsigned int align;
 
-  if (TREE_CODE (decl) == SSA_NAME)
-    align = TYPE_ALIGN (TREE_TYPE (decl));
-  else
-    {
-      align = LOCAL_DECL_ALIGNMENT (decl);
-      /* Don't change DECL_ALIGN when called from estimated_stack_frame_size.
-	 That is done before IPA and could bump alignment based on host
-	 backend even for offloaded code which wants different
-	 LOCAL_DECL_ALIGNMENT.  */
-      if (really_expand)
-	SET_DECL_ALIGN (decl, align);
-    }
-  return align / BITS_PER_UNIT;
+  align = (TREE_CODE (decl) == SSA_NAME)
+    ? TYPE_ALIGN (TREE_TYPE (decl)) : LOCAL_DECL_ALIGNMENT (decl);
+
+  if (hwasan_sanitize_stack_p ())
+    align = MAX (align, ((unsigned)HWASAN_TAG_GRANULE_SIZE) * BITS_PER_UNIT);
+
+  if (TREE_CODE (decl) != SSA_NAME && really_expand)
+    /* Don't change DECL_ALIGN when called from estimated_stack_frame_size.
+       That is done before IPA and could bump alignment based on host
+       backend even for offloaded code which wants different
+       LOCAL_DECL_ALIGNMENT.  */
+    SET_DECL_ALIGN (decl, align);
+
+  unsigned int ret_align = align / BITS_PER_UNIT;
+
+  return ret_align;
 }
 
 /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
@@ -420,6 +423,14 @@ alloc_stack_frame_space (poly_int64 size, unsigned HOST_WIDE_INT align)
   return offset;
 }
 
+/* Ensure that the stack is aligned to ALIGN bytes.
+   Return the new frame offset.  */
+static poly_int64
+align_frame_offset (unsigned HOST_WIDE_INT align)
+{
+  return alloc_stack_frame_space (0, align);
+}
+
 /* Accumulate DECL into STACK_VARS.  */
 
 static void
@@ -996,7 +1007,12 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   /* If this fails, we've overflowed the stack frame.  Error nicely?  */
   gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
 
-  x = plus_constant (Pmode, base, offset);
+  if (hwasan_sanitize_stack_p ())
+    x = targetm.memtag.add_tag (base, offset,
+				hwasan_current_frame_tag ());
+  else
+    x = plus_constant (Pmode, base, offset);
+
   x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
 		   ? TYPE_MODE (TREE_TYPE (decl))
 		   : DECL_MODE (SSAVAR (decl)), x);
@@ -1006,7 +1022,8 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
       /* Set alignment we actually gave this decl if it isn't an SSA name.
          If it is we generate stack slots only accidentally so it isn't as
 	 important, we'll simply use the alignment that is already set.  */
-      if (base == virtual_stack_vars_rtx)
+      if (base == virtual_stack_vars_rtx
+	  || (hwasan_sanitize_stack_p () && base == hwasan_frame_base ()))
 	offset -= frame_phase;
       align = known_alignment (offset);
       align *= BITS_PER_UNIT;
@@ -1045,13 +1062,13 @@ public:
 /* A subroutine of expand_used_vars.  Give each partition representative
    a unique location within the stack frame.  Update each partition member
    with that location.  */
-
 static void
 expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 {
   size_t si, i, j, n = stack_vars_num;
   poly_uint64 large_size = 0, large_alloc = 0;
   rtx large_base = NULL;
+  rtx large_untagged_base = NULL;
   unsigned large_align = 0;
   bool large_allocation_done = false;
   tree decl;
@@ -1102,7 +1119,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
     {
       rtx base;
       unsigned base_align, alignb;
-      poly_int64 offset;
+      poly_int64 offset = 0;
 
       i = stack_vars_sorted[si];
 
@@ -1123,10 +1140,31 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       if (pred && !pred (i))
 	continue;
 
+      base = hwasan_sanitize_stack_p ()
+	? hwasan_frame_base ()
+	: virtual_stack_vars_rtx;
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  base = virtual_stack_vars_rtx;
+	  poly_int64 hwasan_orig_offset;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* There must be no tag granule "shared" between different
+		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
+		 chunk can have more than one object in it.
+
+		 We ensure this by forcing the end of the last bit of data to
+		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
+		 the start of each variable to be aligned to
+		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
+
+		 We can't align just one of the start or end, since there are
+		 untagged things stored on the stack that we have no control on
+		 the alignment and these can't share a tag granule with a
+		 tagged variable.  */
+	      hwasan_orig_offset = align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
 	  HOST_WIDE_INT prev_offset;
@@ -1137,7 +1175,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	    {
 	      if (data->asan_vec.is_empty ())
 		{
-		  alloc_stack_frame_space (0, ASAN_RED_ZONE_SIZE);
+		  align_frame_offset (ASAN_RED_ZONE_SIZE);
 		  prev_offset = frame_offset.to_constant ();
 		}
 	      prev_offset = align_base (prev_offset,
@@ -1205,6 +1243,14 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	    {
 	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
 	      base_align = crtl->max_used_stack_slot_alignment;
+
+	      if (hwasan_sanitize_stack_p ())
+		/* Use `frame_offset` here rather than offset since the
+		   frame_offset describes the extent allocated for this
+		   particular variable while `offset` describes the address
+		   that this variable starts at.  */
+		hwasan_record_stack_var (virtual_stack_vars_rtx, base,
+					 hwasan_orig_offset, frame_offset);
 	    }
 	}
       else
@@ -1225,14 +1271,33 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      loffset = alloc_stack_frame_space
 		(rtx_to_poly_int64 (large_allocsize),
 		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
-	      large_base = get_dynamic_stack_base (loffset, large_align);
+	      large_base = get_dynamic_stack_base (loffset, large_align, base);
 	      large_allocation_done = true;
 	    }
-	  gcc_assert (large_base != NULL);
 
+	  gcc_assert (large_base != NULL);
 	  large_alloc = aligned_upper_bound (large_alloc, alignb);
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* An object with a large alignment requirement means that the
+		 alignment requirement is greater than the required alignment
+		 for tags.  */
+	      if (!large_untagged_base)
+		large_untagged_base
+		  = targetm.memtag.untagged_pointer (large_base, NULL_RTX);
+	      /* Ensure the end of the variable is also aligned correctly.  */
+	      poly_int64 align_again
+		= aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
+	      /* For large allocations we always allocate a chunk of space
+		 (which is addressed by large_untagged_base/large_base) and
+		 then use positive offsets from that.  Hence the farthest
+		 offset is `align_again` and the nearest offset from the base
+		 is `offset`.  */
+	      hwasan_record_stack_var (large_untagged_base, large_base,
+				       offset, align_again);
+	    }
 
 	  base = large_base;
 	  base_align = large_align;
@@ -1243,9 +1308,10 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       for (j = i; j != EOC; j = stack_vars[j].next)
 	{
 	  expand_one_stack_var_at (stack_vars[j].decl,
-				   base, base_align,
-				   offset);
+				   base, base_align, offset);
 	}
+      if (hwasan_sanitize_stack_p ())
+	hwasan_increment_frame_tag ();
     }
 
   gcc_assert (known_eq (large_alloc, large_size));
@@ -1321,27 +1387,49 @@ expand_one_stack_var_1 (tree var)
 {
   poly_uint64 size;
   poly_int64 offset;
+  poly_int64 hwasan_orig_offset;
   unsigned byte_align;
 
   if (TREE_CODE (var) == SSA_NAME)
     {
       tree type = TREE_TYPE (var);
       size = tree_to_poly_uint64 (TYPE_SIZE_UNIT (type));
-      byte_align = TYPE_ALIGN_UNIT (type);
     }
   else
-    {
-      size = tree_to_poly_uint64 (DECL_SIZE_UNIT (var));
-      byte_align = align_local_variable (var, true);
-    }
+    size = tree_to_poly_uint64 (DECL_SIZE_UNIT (var));
+  byte_align = align_local_variable (var, true);
 
   /* We handle highly aligned variables in expand_stack_vars.  */
   gcc_assert (byte_align * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT);
 
+  if (hwasan_sanitize_stack_p ())
+    /* Allocate zero bytes to align the stack.  */
+    hwasan_orig_offset = align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
   offset = alloc_stack_frame_space (size, byte_align);
 
-  expand_one_stack_var_at (var, virtual_stack_vars_rtx,
+  rtx base;
+  if (hwasan_sanitize_stack_p ())
+    {
+      base = hwasan_frame_base ();
+      /* Use `frame_offset` to automatically account for machines where the
+	 frame grows upwards.
+
+	 `offset` will always point to the "start" of the stack object, which
+	 will be the smallest address, for ! FRAME_GROWS_DOWNWARD this is *not*
+	 the "furthest" offset from the base delimiting the current stack
+	 object.  `frame_offset` will always delimit the extent that the frame.
+	 */
+      hwasan_record_stack_var (virtual_stack_vars_rtx, base,
+			       hwasan_orig_offset, frame_offset);
+    }
+  else
+    base = virtual_stack_vars_rtx;
+
+  expand_one_stack_var_at (var, base,
 			   crtl->max_used_stack_slot_alignment, offset);
+
+  if (hwasan_sanitize_stack_p ())
+    hwasan_increment_frame_tag ();
 }
 
 /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
@@ -1947,6 +2035,8 @@ init_vars_expansion (void)
   /* Initialize local stack smashing state.  */
   has_protected_decls = false;
   has_short_buffer = false;
+  if (hwasan_sanitize_stack_p ())
+    hwasan_record_frame_init ();
 }
 
 /* Free up stack variable graph data.  */
@@ -2271,10 +2361,26 @@ expand_used_vars (void)
       expand_stack_vars (NULL, &data);
     }
 
+  if (hwasan_sanitize_stack_p ())
+    hwasan_emit_prologue ();
   if (asan_sanitize_allocas_p () && cfun->calls_alloca)
     var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
 					      virtual_stack_vars_rtx,
 					      var_end_seq);
+  else if (hwasan_sanitize_allocas_p () && cfun->calls_alloca)
+    /* When using out-of-line instrumentation we only want to emit one function
+       call for clearing the tags in a region of shadow stack.  When there are
+       alloca calls in this frame we want to emit a call using the
+       virtual_stack_dynamic_rtx, but when not we use the hwasan_frame_extent
+       rtx we created in expand_stack_vars.  */
+    var_end_seq = hwasan_emit_untag_frame (virtual_stack_dynamic_rtx,
+					   virtual_stack_vars_rtx);
+  else if (hwasan_sanitize_stack_p ())
+    /* If no variables were stored on the stack, `hwasan_get_frame_extent`
+       will return NULL_RTX and hence `hwasan_emit_untag_frame` will return
+       NULL (i.e. an empty sequence).  */
+    var_end_seq = hwasan_emit_untag_frame (hwasan_get_frame_extent (),
+					   virtual_stack_vars_rtx);
 
   fini_vars_expansion ();
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 998d940e144c31aa7309b478b92b680736f317bf..dd47400ae4100daa8d6987f1d347857d8717bfcd 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2976,8 +2976,63 @@ A target hook which lets a backend compute the set of pressure classes to  be us
 @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.
+True if backend architecture naturally supports ignoring some region of
+pointers.  This feature means that @option{-fsanitize=hwaddress} can work.
+@end deftypefn
+
+@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_TAG_SIZE ()
+Return the size of a tag (in bits) for this platform.
+
+The default returns 8.
+@end deftypefn
+
+@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_GRANULE_SIZE ()
+Return the size in real memory that each byte in shadow memory refers to.
+I.e. if a variable is X bytes long in memory, then this hook should return
+the value Y such that the tag in shadow memory spans X/Y bytes.
+
+Most variables will need to be aligned to this amount since two variables
+that are neighbours in memory and share a tag granule would need to share
+the same tag.
+
+The default returns 16.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_INSERT_RANDOM_TAG (rtx @var{base})
+Create a new register with the address value of @var{base} and a
+(possibly) random tag in it.
+This function is used to generate a tagged base for the current stack frame.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADD_TAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
+Return an RTX that represents the result of adding @var{addr_offset} to
+the address pointer @var{base} and @var{tag_offset} to the tag in pointer
+@var{base}.
+The resulting RTX must either be a valid memory address or be able to get
+put into an operand with force_operand.
+
+Unlike other memtag hooks, this must return an expression and not emit any
+RTL.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_SET_TAG (rtx @var{untagged_base}, rtx @var{tag}, rtx @var{target})
+Return an RTX representing @var{untagged_base} but with the tag @var{tag}.
+Try and store this in @var{target} if convenient.
+@var{untagged_base} is required to have a zero tag when this hook is called.
+The default of this hook is to set the top byte of @var{untagged_base} to
+@var{tag}.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_EXTRACT_TAG (rtx @var{tagged_pointer}, rtx @var{target})
+Return an RTX representing the tag stored in @var{tagged_pointer}.
+Store the result in @var{target} if it is convenient.
+The default represents the top byte of the original pointer.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_UNTAGGED_POINTER (rtx @var{tagged_pointer}, rtx @var{target})
+Return an RTX representing @var{tagged_pointer} with its tag set to zero.
+Store the result in @var{target} if convenient.
+The default clears the top byte of the original pointer.
 @end deftypefn
 
 @node Stack and Calling
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 80960c1fe041bd08ccc22a4c41ebf740eca80015..98e3c2b65581f62b7ebdcefa51723a125507e895 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2377,6 +2377,20 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_TAG_SIZE
+
+@hook TARGET_MEMTAG_GRANULE_SIZE
+
+@hook TARGET_MEMTAG_INSERT_RANDOM_TAG
+
+@hook TARGET_MEMTAG_ADD_TAG
+
+@hook TARGET_MEMTAG_SET_TAG
+
+@hook TARGET_MEMTAG_EXTRACT_TAG
+
+@hook TARGET_MEMTAG_UNTAGGED_POINTER
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/explow.h b/gcc/explow.h
index 0df8c62b82a8bf1d8d6baf0b6fb658e66361a407..581831cb19fdf9e8fd969bb30139e1358279a34d 100644
--- a/gcc/explow.h
+++ b/gcc/explow.h
@@ -106,7 +106,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
 extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
 
 /* Returns the address of the dynamic stack space without allocating it.  */
-extern rtx get_dynamic_stack_base (poly_int64, unsigned);
+extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
 
 /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
 extern rtx align_dynamic_address (rtx, unsigned);
diff --git a/gcc/explow.c b/gcc/explow.c
index 0fbc6d25b816457a3d13ed45d16b5dd0513cfacd..41c3f6ace49c0e55c080e10b917842b1b21d49eb 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1583,10 +1583,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
    OFFSET is the offset of the area into the virtual stack vars area.
 
    REQUIRED_ALIGN is the alignment (in bits) required for the region
-   of memory.  */
+   of memory.
+
+   BASE is the rtx of the base of this virtual stack vars area.
+   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+   on the stack.  */
 
 rtx
-get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
 {
   rtx target;
 
@@ -1594,7 +1598,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
     crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
 
   target = gen_reg_rtx (Pmode);
-  emit_move_insn (target, virtual_stack_vars_rtx);
+  emit_move_insn (target, base);
   target = expand_binop (Pmode, add_optab, target,
 			 gen_int_mode (offset, Pmode),
 			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index a32715ddb92e69b7ca7be28a8f17a369b891bd76..4f854fb994229fd4ed91d3b5cff7c7acff9a55bc 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
+/* Hardware Address Sanitizer.  */
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
+		      BT_FN_VOID_PTR_UINT8_PTRMODE, ATTR_NOTHROW_LIST)
+
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/target.def b/gcc/target.def
index 07064ea366a2c0bde0afbf45d78e16d7e9e9d13d..26183a42aef5f30aae7abcd474843200dd5206eb 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6835,10 +6835,73 @@ HOOK_VECTOR (TARGET_MEMTAG_, memtag)
 
 DEFHOOK
 (can_tag_addresses,
- "True if backend architecture naturally supports ignoring the top byte of\n\
- pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
+ "True if backend architecture naturally supports ignoring some region of\n\
+pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
  bool, (), default_memtag_can_tag_addresses)
 
+DEFHOOK
+(tag_size,
+ "Return the size of a tag (in bits) for this platform.\n\
+\n\
+The default returns 8.",
+  uint8_t, (), default_memtag_tag_size)
+
+DEFHOOK
+(granule_size,
+ "Return the size in real memory that each byte in shadow memory refers to.\n\
+I.e. if a variable is X bytes long in memory, then this hook should return\n\
+the value Y such that the tag in shadow memory spans X/Y bytes.\n\
+\n\
+Most variables will need to be aligned to this amount since two variables\n\
+that are neighbours in memory and share a tag granule would need to share\n\
+the same tag.\n\
+\n\
+The default returns 16.",
+  uint8_t, (), default_memtag_granule_size)
+
+DEFHOOK
+(insert_random_tag,
+ "Create a new register with the address value of @var{base} and a\n\
+(possibly) random tag in it.\n\
+This function is used to generate a tagged base for the current stack frame.",
+  rtx, (rtx base), default_memtag_insert_random_tag)
+
+DEFHOOK
+(add_tag,
+ "Return an RTX that represents the result of adding @var{addr_offset} to\n\
+the address pointer @var{base} and @var{tag_offset} to the tag in pointer\n\
+@var{base}.\n\
+The resulting RTX must either be a valid memory address or be able to get\n\
+put into an operand with force_operand.\n\
+\n\
+Unlike other memtag hooks, this must return an expression and not emit any\n\
+RTL.",
+  rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
+  default_memtag_add_tag)
+
+DEFHOOK
+(set_tag,
+ "Return an RTX representing @var{untagged_base} but with the tag @var{tag}.\n\
+Try and store this in @var{target} if convenient.\n\
+@var{untagged_base} is required to have a zero tag when this hook is called.\n\
+The default of this hook is to set the top byte of @var{untagged_base} to\n\
+@var{tag}.",
+  rtx, (rtx untagged_base, rtx tag, rtx target), default_memtag_set_tag)
+
+DEFHOOK
+(extract_tag,
+ "Return an RTX representing the tag stored in @var{tagged_pointer}.\n\
+Store the result in @var{target} if it is convenient.\n\
+The default represents the top byte of the original pointer.",
+  rtx, (rtx tagged_pointer, rtx target), default_memtag_extract_tag)
+
+DEFHOOK
+(untagged_pointer,
+ "Return an RTX representing @var{tagged_pointer} with its tag set to zero.\n\
+Store the result in @var{target} if convenient.\n\
+The default clears the top byte of the original pointer.",
+  rtx, (rtx tagged_pointer, rtx target), default_memtag_untagged_pointer)
+
 HOOK_VECTOR_END (memtag)
 #undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index c11b1879f65bb581af11963b40189028fa490623..20e16ce86cef124dc96195717352c981b8ed8700 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -286,4 +286,12 @@ extern bool speculation_safe_value_not_needed (bool);
 extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 
 extern bool default_memtag_can_tag_addresses ();
+extern uint8_t default_memtag_tag_size ();
+extern uint8_t default_memtag_granule_size ();
+extern rtx default_memtag_insert_random_tag (rtx);
+extern rtx default_memtag_add_tag (rtx, poly_int64, uint8_t);
+extern rtx default_memtag_set_tag (rtx, rtx, rtx);
+extern rtx default_memtag_extract_tag (rtx, rtx);
+extern rtx default_memtag_untagged_pointer (rtx, rtx);
+
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 8c860f91cae7ccb5ca7fb0ff2364c1f67620d661..5e2b188b09fb21e23be752347a3bcefc2dab62aa 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "flags.h"
 #include "explow.h"
+#include "expmed.h"
 #include "calls.h"
 #include "expr.h"
 #include "output.h"
@@ -83,6 +84,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "sbitmap.h"
 #include "function-abi.h"
+#include "attribs.h"
+#include "asan.h"
+#include "emit-rtl.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -2379,10 +2383,130 @@ default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
   return result;
 }
 
+/* How many bits to shift in order to access the tag bits.
+   The default is to store the tag in the top 8 bits of a 64 bit pointer, hence
+   shifting 56 bits will leave just the tag.
+   We require that the shift is less than 64 for the `const_int_rtx` use here,
+   that's also a requirement for the tag to actually be stored on any systems
+   with 64 bit (or smaller) pointers.  */
+#define HWASAN_SHIFT 56
+#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]
+
 bool
 default_memtag_can_tag_addresses ()
 {
   return false;
 }
 
+uint8_t
+default_memtag_tag_size ()
+{
+  return 8;
+}
+
+uint8_t
+default_memtag_granule_size ()
+{
+  return 16;
+}
+
+/* The default implementation of TARGET_MEMTAG_INSERT_RANDOM_TAG.  */
+rtx
+default_memtag_insert_random_tag (rtx untagged)
+{
+  gcc_assert (param_hwasan_instrument_stack);
+  if (param_hwasan_random_frame_tag)
+    {
+      rtx base = gen_reg_rtx (Pmode);
+      rtx temp = gen_reg_rtx (QImode);
+      rtx fn = init_one_libfunc ("__hwasan_generate_tag");
+      rtx new_tag = emit_library_call_value (fn, temp, LCT_NORMAL, QImode);
+      rtx ret = targetm.memtag.set_tag (untagged, new_tag, base);
+      if (ret != base)
+	emit_move_insn (base, ret);
+      return base;
+    }
+  else
+    {
+      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
+	 In the future we may add the option emit random tags with inline
+	 instrumentation instead of function calls.  This would be the same
+	 between the kernel and userland.  */
+      return untagged;
+    }
+}
+
+/* The default implementation of TARGET_MEMTAG_ADD_TAG.  */
+rtx
+default_memtag_add_tag (rtx base, poly_int64 offset, uint8_t tag_offset)
+{
+  /* Need to look into what the most efficient code sequence is.
+     This is a code sequence that would be emitted *many* times, so we
+     want it as small as possible.
+
+     There are two places where tag overflow is a question:
+       - Tagging the shadow stack.
+	  (both tagging and untagging).
+       - Tagging addressable pointers.
+
+     We need to ensure both behaviors are the same (i.e. that the tag that
+     ends up in a pointer after "overflowing" the tag bits with a tag addition
+     is the same that ends up in the shadow space).
+
+     The aim is that the behavior of tag addition should follow modulo
+     wrapping in both instances.
+
+     The libhwasan code doesn't have any path that increments a pointer's tag,
+     which means it has no opinion on what happens when a tag increment
+     overflows (and hence we can choose our own behavior).
+
+     NOTE:
+	Here we return an expression which represents the base with the
+	provided offsets.
+	This does not have to be a valid operand to anything, since the
+	`force_operand` machinery in the compiler already handles this.  */
+
+  offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
+  return plus_constant (Pmode, base, offset);
+}
+
+/* The default implementation of TARGET_MEMTAG_SET_TAG.  */
+rtx
+default_memtag_set_tag (rtx untagged, rtx tag, rtx target)
+{
+  gcc_assert (GET_MODE (untagged) == Pmode);
+  gcc_assert (GET_MODE (tag) == QImode);
+  tag = expand_simple_binop (Pmode, ASHIFT, tag, HWASAN_SHIFT_RTX, tag,
+			     /* unsignedp = */1, OPTAB_WIDEN);
+  rtx ret = expand_simple_binop (Pmode, IOR, untagged, tag, target,
+				 /* unsignedp = */1, OPTAB_DIRECT);
+  gcc_assert (ret);
+  return ret;
+}
+
+/* The default implementation of TARGET_MEMTAG_EXTRACT_TAG.  */
+rtx
+default_memtag_extract_tag (rtx tagged_pointer, rtx target)
+{
+  rtx tag = expand_simple_binop (Pmode, LSHIFTRT, tagged_pointer,
+				 HWASAN_SHIFT_RTX, target,
+				 /* unsignedp = */0,
+				 OPTAB_DIRECT);
+  rtx ret = gen_lowpart (QImode, tag);
+  gcc_assert (ret);
+  return ret;
+}
+
+/* The default implementation of TARGET_MEMTAG_UNTAGGED_POINTER.  */
+rtx
+default_memtag_untagged_pointer (rtx tagged_pointer, rtx target)
+{
+  rtx tag_mask = gen_int_mode ((HOST_WIDE_INT_1U << HWASAN_SHIFT) - 1, Pmode);
+  rtx untagged_base = expand_simple_binop (Pmode, AND, tagged_pointer,
+					   tag_mask, target, true,
+					   OPTAB_DIRECT);
+  gcc_assert (untagged_base);
+  return untagged_base;
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index ac40389b1195d98521cf94e4b8a9553961999f30..9fdd8cfc4b4375e226b2247ad1dd4e9ee31fc7b0 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -510,6 +510,9 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      if (flag_sanitize & SANITIZE_HWADDRESS)
+	hwasan_finish_file ();
+
       omp_finish_file ();
 
       hsa_output_brig ();


[-- Attachment #2: hwasan-stack-variables.patch.gz --]
[-- Type: application/gzip, Size: 14659 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN
  2020-11-03 13:00         ` Matthew Malcomson
@ 2020-11-04 15:43           ` Richard Sandiford
  0 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-11-04 15:43 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, Richard Earnshaw, joseph, Kyrylo Tkachov, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> Hi Richard,
>
> I'm sending up the revised patch 5 (introducing stack variable handling)
> without the other changes to other patches.
>
> I figure there's been quite a lot of changes to this patch and I wanted
> to give you time to review them while I worked on finishing the less
> widespread changes in patch 6 and before I ran the more exhaustive (and
> time-consuming) tests in case you didn't like the changes and those
> exhaustive tests would just have to get repeated.

Thanks, the new approach looks good to me.  Most of the comments below
are just minor.

> […]
> @@ -75,6 +89,26 @@ extern hash_set <tree> *asan_used_labels;
>  
>  #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
>  
> +/* NOTE: The values below and the hooks under targetm.memtag define an ABI and
> +   are hard-coded to these values in libhwasan, hence they can't be changed
> +   independently here.  */
> +/* How many bits are used to store a tag in a pointer.
> +   The default version uses the entire top byte of a pointer (i.e. 8 bits).  */
> +#define HWASAN_TAG_SIZE targetm.memtag.tag_size ()
> +/* Tag Granule of HWASAN shadow stack.
> +   This is the size in real memory that each byte in the shadow memory refers
> +   to.  I.e. if a variable is X bytes long in memory then it's tag in shadow

s/it's/its/

> +   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
> +   Most variables will need to be aligned to this amount since two variables
> +   that are neighbours in memory and share a tag granule would need to share

s/neighbours/neighbors/

> +   the same tag (the shared tag granule can only store one tag).  */
> +#define HWASAN_TAG_GRANULE_SIZE targetm.memtag.granule_size ()
> +/* Define the tag for the stack background.
> +   This defines what tag the stack pointer will be and hence what tag all
> +   variables that are not given special tags are (e.g. spilled registers,
> +   and parameters passed on the stack).  */
> +#define HWASAN_STACK_BACKGROUND gen_int_mode (0, QImode)
> +
>  /* Various flags for Asan builtins.  */
>  enum asan_check_flags
>  {
> […]
> @@ -1352,6 +1393,28 @@ asan_redzone_buffer::flush_if_full (void)
>      flush_redzone_payload ();
>  }
>  
> +/* Returns whether we are tagging pointers and checking those tags on memory
> +   access.  */
> +bool
> +hwasan_sanitize_p ()
> +{
> +  return sanitize_flags_p (SANITIZE_HWADDRESS);
> +}
> +
> +/* Are we tagging the stack?  */
> +bool
> +hwasan_sanitize_stack_p ()
> +{
> +  return (hwasan_sanitize_p () && param_hwasan_instrument_stack);
> +}
> +
> +/* Are we protecting alloca objects?  */

Same comment as before about avoiding the word “protect”, both in the
comment and the option name.  Maybe s/protect/sanitize/ or s/protect/tag/.

> +bool
> +hwasan_sanitize_allocas_p (void)
> +{
> +  return (hwasan_sanitize_stack_p () && param_hwasan_protect_allocas);
> +}
> +
>  /* Insert code to protect stack vars.  The prologue sequence should be emitted
>     directly, epilogue sequence returned.  BASE is the register holding the
>     stack base, against which OFFSETS array offsets are relative to, OFFSETS
> […]
> @@ -3702,4 +3772,330 @@ make_pass_asan_O0 (gcc::context *ctxt)
>    return new pass_asan_O0 (ctxt);
>  }
>  
> +/* For stack tagging:
> +
> +   Return the offset from the frame base tag that the "next" expanded object
> +   should have.  */
> +uint8_t
> +hwasan_current_frame_tag ()
> +{
> +  return hwasan_frame_tag_offset;
> +}
> +
> +/* For stack tagging:
> +
> +   Return the 'base pointer' for this function.  If that base pointer has not
> +   yet been created then we create a register to hold it and initialise that
> +   value with a possibly random tag and the value of the
> +   virtual_stack_vars_rtx.  */

As discussed offline, I think the old approach of generating the
initialisation in hwasan_emit_prologue was safer, although I agree
there doesn't seem to be a specific problem with doing things this way.

> +rtx
> +hwasan_frame_base ()
> +{
> +  if (! hwasan_frame_base_ptr)
> +    {
> +      hwasan_frame_base_ptr
> +	= targetm.memtag.insert_random_tag (virtual_stack_vars_rtx);
> +    }

Nit: should be no braces around single statements, even if they span
multiple lines.

> +
> +  return hwasan_frame_base_ptr;
> +}
> +
> +/* Record a compile-time constant size stack variable that HWASAN will need to
> +   tag.  This record of the range of a stack variable will be used by
> +   `hwasan_emit_prologue` to emit the RTL at the start of each frame which will
> +   set tags in the shadow memory according to the assigned tag for each object.
> +
> +   The range that the object spans in stack space should be described by the
> +   bounds `untagged_base + nearest` and `untagged_base + farthest`.
> +   `tagged_base` is the base address which contains the "base frame tag" for
> +   this frame, and from which the value to address this object with will be
> +   calculated.
> +
> +   We record the `untagged_base` since the functions in the hwasan library we
> +   use to tag memory take pointers without a tag.  */
> +void
> +hwasan_record_stack_var (rtx untagged_base, rtx tagged_base,
> +			 poly_int64 nearest, poly_int64 farthest)
> +{
> +  hwasan_stack_var *cur_var = new hwasan_stack_var;
> +  cur_var->untagged_base = untagged_base;
> +  cur_var->tagged_base = tagged_base;
> +  cur_var->nearest_offset = nearest;
> +  cur_var->farthest_offset = farthest;
> +  cur_var->tag_offset = hwasan_current_frame_tag ();
> +
> +  hwasan_tagged_stack_vars.safe_push (cur_var);
> +}

Very minor, but it seems more consistent to have the “_offset” on the
parameter names as well as the field names.  Doing that might also make
the comment slightly clearer.

Not sure it's necessary to use separately-allocated structures here.
Making it a vec<hawasan_stack_var> might be simpler.

> +
> +/* Return the RTX representing the farthest extent of the statically allocated
> +   stack objects for this frame.  If hwasan_frame_base_ptr has not been
> +   initialised then we are not storing any static variables on the stack in

s/initialised/initialized/

> +   this frame.  In this case we return NULL_RTX to represent that.
> +
> +   Otherwise simply return virtual_stack_vars_rtx + frame_offset.  */
> +rtx
> +hwasan_get_frame_extent ()
> +{
> +  return hwasan_frame_base_ptr ?
> +    plus_constant (Pmode, virtual_stack_vars_rtx, frame_offset)
> +    : NULL_RTX;

Formatting here should be:

  return (hwasan_frame_base_ptr
	  ? plus_constant (Pmode, virtual_stack_vars_rtx, frame_offset)
	  : NULL_RTX);

> +}
> +
> +
> +/* For stack tagging:
> +
> +   Increment the tag offset modulo the size a tag can represent.  */

Maybe “the frame tag offset”.

> […]
> +/* Clear internal state for the next function.
> +   This function is called before variables on the stack get expanded, in
> +   `init_vars_expansion`.  */
> +void
> +hwasan_record_frame_init ()
> +{
> +  delete asan_used_labels;
> +  asan_used_labels = NULL;
> +
> +  /* If this isn't the case then some stack variable was recorded *before*
> +     hwasan_record_frame_init is called, yet *after* the hwasan prologue for
> +     the previous frame was emitted.  Such stack variables would not have
> +     their shadow stack filled in.  */
> +  gcc_assert (hwasan_tagged_stack_vars.is_empty ());
> +  hwasan_frame_base_ptr = NULL_RTX;
> +
> +  /* When not using a random frame tag we can avoid the background stack
> +     color which gives the user a little better debug output upon a crash.
> +     Meanwhile, when using a random frame tag it will be nice to avoid adding
> +     tags for the first object since that is unnecessary extra work.
> +     Hence set the initial hwasan_frame_tag_offset to be 0 if using a random
> +     frame tag and 1 otherwise.
> +
> +     As described in hwasan_increment_frame_tag, in the kernel the stack
> +     pointer has the tag 0xff.  That means that to avoid 0xff and 0 (the tag
> +     which the kernel does not check and the background tag respectively) we
> +     start with a tag offset of 2.  */
> +  hwasan_frame_tag_offset = param_hwasan_random_frame_tag
> +    ? 0
> +    : sanitize_flags_p (SANITIZE_KERNEL_HWADDRESS) ? 2 : 1;

Wonder whether it's worth splitting:

+  if (hwasan_frame_tag_offset == 0 && ! param_hwasan_random_frame_tag)
+    hwasan_frame_tag_offset += 1;
+  if (hwasan_frame_tag_offset == 1 && ! param_hwasan_random_frame_tag
+      && sanitize_flags_p (SANITIZE_KERNEL_HWADDRESS))
+    hwasan_frame_tag_offset += 1;

out of hwasan_increment_frame_tag and using it here too, so that we
only need to explain the relatively subtle condition once.

Just a suggestion though.

> +}
> +
> +/* For stack tagging:
> +   (Emits HWASAN equivalent of what is emitted by
> +   `asan_emit_stack_protection`).
> +
> +   Emits the extra prologue code to set the shadow stack as required for HWASAN
> +   stack instrumentation.
> +
> +   BASES is an array containing the tagged base registers for each object.
> +   We map each object to a given base since large aligned objects have a
> +   different base to others and we need to know which objects use which base.
> +
> +   UNTAGGED_BASES contains the same information as above except without tags.
> +   This is needed since libhwasan only accepts untagged pointers in
> +   __hwasan_tag_memory.
> +
> +   OFFSETS is an array with the start and end offsets for each object stored on
> +   the stack in this frame.  This array is hence twice the length of the other
> +   array arguments (given it has two entries for each stack object).
> +
> +   TAGS is an array containing the tag *offset* each object should have from
> +   the tag in its base pointer.
> +
> +   LENGTH contains the length of the OFFSETS array.  */

The comment is out of date: “BASES is …” onwards can be removed.

> +void
> +hwasan_emit_prologue ()
> +{
> +  /* We need untagged base pointers since libhwasan only accepts untagged
> +    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
> +    the base tag for an offset.  */
> +
> +  gcc_assert ((hwasan_frame_base_ptr == NULL_RTX)
> +	      == hwasan_tagged_stack_vars.is_empty ());
> +  if (! hwasan_frame_base_ptr)
> +    return;
> +
> +  size_t length = hwasan_tagged_stack_vars.length ();
> +  hwasan_stack_var **vars = hwasan_tagged_stack_vars.address ();
> +
> +  poly_int64 bot = 0, top = 0;
> +  size_t i = 0;
> +  for (i = 0; i < length; i++)
> +    {
> +      hwasan_stack_var *cur = vars[i];
> +      poly_int64 nearest = cur->nearest_offset;
> +      poly_int64 farthest = cur->farthest_offset;
> +
> +      if (known_ge (nearest, farthest))
> +	{
> +	  top = nearest;
> +	  bot = farthest;
> +	}
> +      else
> +	{
> +	  /* Given how these values are calculated, one must be known greater
> +	     than the other.  */
> +	  gcc_assert (known_le (nearest, farthest));
> +	  top = farthest;
> +	  bot = nearest;
> +	}
> +      poly_int64 size = (top - bot);
> +
> +      /* Assert the edge of each variable is aligned to the HWASAN tag granule
> +	 size.  */
> +      gcc_assert (multiple_p (top, HWASAN_TAG_GRANULE_SIZE));
> +      gcc_assert (multiple_p (bot, HWASAN_TAG_GRANULE_SIZE));
> +      gcc_assert (multiple_p (size, HWASAN_TAG_GRANULE_SIZE));
> +
> +      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
> +      rtx base_tag = targetm.memtag.extract_tag (cur->tagged_base, NULL_RTX);
> +      rtx tag = plus_constant (QImode, base_tag, cur->tag_offset);
> +      tag = hwasan_truncate_to_tag_size (tag, NULL_RTX);
> +
> +      rtx bottom = convert_memory_address (ptr_mode,
> +					   plus_constant (Pmode,
> +							  cur->untagged_base,
> +							  bot));
> +      emit_library_call (ret, LCT_NORMAL, VOIDmode,
> +			 bottom, ptr_mode,
> +			 tag, QImode,
> +			 gen_int_mode (size, ptr_mode), ptr_mode);
> +      delete cur;
> +    }
> +  /* Clear the stack vars, we've emitted the prologue for them all now.  */
> +  hwasan_tagged_stack_vars.truncate (0);
> +}
> […]
> +/* For stack tagging:
> +
> +   Truncate `tag` to the number of bits that a tag uses (i.e. to
> +   HWASAN_TAG_SIZE).  Store the result in `target` if it's convenient.  */
> +rtx
> +hwasan_truncate_to_tag_size (rtx tag, rtx target)
> +{
> +  gcc_assert (GET_MODE (tag) == QImode);
> +  if (HWASAN_TAG_SIZE != GET_MODE_PRECISION (QImode))
> +    {
> +      gcc_assert (GET_MODE_PRECISION (QImode) > HWASAN_TAG_SIZE);
> +      tag = expand_simple_binop (QImode, AND, tag,
> +				 gen_int_mode (-HWASAN_TAG_SIZE, QImode),

Should be (1 << HWASAN_TAG_SIZE) - 1 or equivalent.

> +				 target,
> +				 /* unsignedp = */1, OPTAB_WIDEN);
> +      gcc_assert (tag);
> +    }
> +  return tag;
> +}
> +
>  #include "gt-asan.h"
> diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
> index b270a4ddb9db469ba52e42f36a1bc2f02d8f03fc..52a0dd6dfda1868622b88821a6d4abc5f2c7ce03 100644
> --- a/gcc/cfgexpand.c
> +++ b/gcc/cfgexpand.c
> @@ -365,19 +365,22 @@ align_local_variable (tree decl, bool really_expand)
>  {
>    unsigned int align;
>  
> -  if (TREE_CODE (decl) == SSA_NAME)
> -    align = TYPE_ALIGN (TREE_TYPE (decl));
> -  else
> -    {
> -      align = LOCAL_DECL_ALIGNMENT (decl);
> -      /* Don't change DECL_ALIGN when called from estimated_stack_frame_size.
> -	 That is done before IPA and could bump alignment based on host
> -	 backend even for offloaded code which wants different
> -	 LOCAL_DECL_ALIGNMENT.  */
> -      if (really_expand)
> -	SET_DECL_ALIGN (decl, align);
> -    }
> -  return align / BITS_PER_UNIT;
> +  align = (TREE_CODE (decl) == SSA_NAME)
> +    ? TYPE_ALIGN (TREE_TYPE (decl)) : LOCAL_DECL_ALIGNMENT (decl);

Formatting:

  align = (TREE_CODE (decl) == SSA_NAME
	   ? TYPE_ALIGN (TREE_TYPE (decl))
	   : LOCAL_DECL_ALIGNMENT (decl));

> +
> +  if (hwasan_sanitize_stack_p ())
> +    align = MAX (align, ((unsigned)HWASAN_TAG_GRANULE_SIZE) * BITS_PER_UNIT);

Should be formatted as:

    align = MAX (align, (unsigned) HWASAN_TAG_GRANULE_SIZE * BITS_PER_UNIT);

> +  if (TREE_CODE (decl) != SSA_NAME && really_expand)
> +    /* Don't change DECL_ALIGN when called from estimated_stack_frame_size.
> +       That is done before IPA and could bump alignment based on host
> +       backend even for offloaded code which wants different
> +       LOCAL_DECL_ALIGNMENT.  */
> +    SET_DECL_ALIGN (decl, align);
> +
> +  unsigned int ret_align = align / BITS_PER_UNIT;
> +
> +  return ret_align;

Don't think adding ret_align really adds much: the original direct
return of “align / BITS_PER_UNIT” seemed clearer.

>  }
>  
>  /* Align given offset BASE with ALIGN.  Truncate up if ALIGN_UP is true,
> […]
> @@ -1006,7 +1022,8 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
>        /* Set alignment we actually gave this decl if it isn't an SSA name.
>           If it is we generate stack slots only accidentally so it isn't as
>  	 important, we'll simply use the alignment that is already set.  */
> -      if (base == virtual_stack_vars_rtx)
> +      if (base == virtual_stack_vars_rtx
> +	  || (hwasan_sanitize_stack_p () && base == hwasan_frame_base ()))

I think it would be good to have a side-effect-free version of
hwasan_frame_base for comparisons.  There's then no reason to check
hwasan_sanitize_stack_p () first: the pointer will be null if we're
not sanitising the stack.

Also, it might be good to put this in a helper such as:

  stack_vars_base_reg_p (base)

that returns true for both virtual_stack_vars_rtx and the hwasan
frame base.  I think this is something that other code is likely
to want to test eventually.

> @@ -1123,10 +1140,31 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>        if (pred && !pred (i))
>  	continue;
>  
> +      base = hwasan_sanitize_stack_p ()
> +	? hwasan_frame_base ()
> +	: virtual_stack_vars_rtx;

Formatting:

      base = (hwasan_sanitize_stack_p ()
	      ? hwasan_frame_base ()
	      : virtual_stack_vars_rtx);

>        alignb = stack_vars[i].alignb;
>        if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
>  	{
> -	  base = virtual_stack_vars_rtx;
> +	  poly_int64 hwasan_orig_offset;
> +	  if (hwasan_sanitize_stack_p ())
> +	    {
> +	      /* There must be no tag granule "shared" between different
> +		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
> +		 chunk can have more than one object in it.
> +
> +		 We ensure this by forcing the end of the last bit of data to
> +		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
> +		 the start of each variable to be aligned to
> +		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
> +
> +		 We can't align just one of the start or end, since there are
> +		 untagged things stored on the stack that we have no control on
> +		 the alignment and these can't share a tag granule with a
> +		 tagged variable.  */

“things stored on the stack that we have no control on the alignment”
sounds odd.

> +	      hwasan_orig_offset = align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
> +	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
> +	    }
>  	  /* ASAN description strings don't yet have a syntax for expressing
>  	     polynomial offsets.  */
>  	  HOST_WIDE_INT prev_offset;
> […]
> @@ -1321,27 +1387,49 @@ expand_one_stack_var_1 (tree var)
>  {
>    poly_uint64 size;
>    poly_int64 offset;
> +  poly_int64 hwasan_orig_offset;
>    unsigned byte_align;
>  
>    if (TREE_CODE (var) == SSA_NAME)
>      {
>        tree type = TREE_TYPE (var);
>        size = tree_to_poly_uint64 (TYPE_SIZE_UNIT (type));
> -      byte_align = TYPE_ALIGN_UNIT (type);
>      }
>    else
> -    {
> -      size = tree_to_poly_uint64 (DECL_SIZE_UNIT (var));
> -      byte_align = align_local_variable (var, true);
> -    }
> +    size = tree_to_poly_uint64 (DECL_SIZE_UNIT (var));
> +  byte_align = align_local_variable (var, true);
>  
>    /* We handle highly aligned variables in expand_stack_vars.  */
>    gcc_assert (byte_align * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT);
>  
> +  if (hwasan_sanitize_stack_p ())
> +    /* Allocate zero bytes to align the stack.  */
> +    hwasan_orig_offset = align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
>    offset = alloc_stack_frame_space (size, byte_align);
>  
> -  expand_one_stack_var_at (var, virtual_stack_vars_rtx,
> +  rtx base;
> +  if (hwasan_sanitize_stack_p ())
> +    {

I think we need to zero-initialise hwasan_orig_offset to defend
against “maybe used uninitialised” warnings (which wouldn't necessarily
fire for all hosts or option combinations, but might for some).

Alternatively, since the only common code between the two
hwasan_sanitize_stack_p () blocks is the single call:

  offset = alloc_stack_frame_space (size, byte_align);

it might be easier and more readable to duplicate that call instead
of the hwasan_sanitize_stack_p () condition.

> +      base = hwasan_frame_base ();
> +      /* Use `frame_offset` to automatically account for machines where the
> +	 frame grows upwards.
> +
> +	 `offset` will always point to the "start" of the stack object, which
> +	 will be the smallest address, for ! FRAME_GROWS_DOWNWARD this is *not*
> +	 the "furthest" offset from the base delimiting the current stack
> +	 object.  `frame_offset` will always delimit the extent that the frame.
> +	 */
> +      hwasan_record_stack_var (virtual_stack_vars_rtx, base,
> +			       hwasan_orig_offset, frame_offset);
> +    }
> +  else
> +    base = virtual_stack_vars_rtx;
> +
> +  expand_one_stack_var_at (var, base,
>  			   crtl->max_used_stack_slot_alignment, offset);
> +
> +  if (hwasan_sanitize_stack_p ())
> +    hwasan_increment_frame_tag ();
>  }
>  
>  /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
> […]
> diff --git a/gcc/target.def b/gcc/target.def
> index 07064ea366a2c0bde0afbf45d78e16d7e9e9d13d..26183a42aef5f30aae7abcd474843200dd5206eb 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -6835,10 +6835,73 @@ HOOK_VECTOR (TARGET_MEMTAG_, memtag)
>  
>  DEFHOOK
>  (can_tag_addresses,
> - "True if backend architecture naturally supports ignoring the top byte of\n\
> - pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
> + "True if backend architecture naturally supports ignoring some region of\n\
> +pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
>   bool, (), default_memtag_can_tag_addresses)

Looks like this belongs in an earlier patch, but it doesn't really
matter if you're intending to push as a single commit.

> +DEFHOOK
> +(granule_size,
> + "Return the size in real memory that each byte in shadow memory refers to.\n\
> +I.e. if a variable is X bytes long in memory, then this hook should return\n\
> +the value Y such that the tag in shadow memory spans X/Y bytes.\n\
> +\n\
> +Most variables will need to be aligned to this amount since two variables\n\
> +that are neighbours in memory and share a tag granule would need to share\n\

s/neighbours/neighbors/

> +the same tag.\n\
> +\n\
> +The default returns 16.",

Should use texinfo markup, so s/X/@var{x}/g and s/Y/@var{y}/g.

> +  uint8_t, (), default_memtag_granule_size)
> +
> +DEFHOOK
> +(insert_random_tag,
> + "Create a new register with the address value of @var{base} and a\n\
> +(possibly) random tag in it.\n\
> +This function is used to generate a tagged base for the current stack frame.",
> +  rtx, (rtx base), default_memtag_insert_random_tag)
> +
> +DEFHOOK
> +(add_tag,
> + "Return an RTX that represents the result of adding @var{addr_offset} to\n\
> +the address pointer @var{base} and @var{tag_offset} to the tag in pointer\n\

IMO “the address in pointer @var{base}” would be easier to understand.

> +@var{base}.\n\
> +The resulting RTX must either be a valid memory address or be able to get\n\
> +put into an operand with force_operand.\n\

@code{force_operand}

> +\n\
> +Unlike other memtag hooks, this must return an expression and not emit any\n\
> +RTL.",
> +  rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
> +  default_memtag_add_tag)
> +
> +DEFHOOK
> +(set_tag,
> + "Return an RTX representing @var{untagged_base} but with the tag @var{tag}.\n\
> +Try and store this in @var{target} if convenient.\n\
> +@var{untagged_base} is required to have a zero tag when this hook is called.\n\
> +The default of this hook is to set the top byte of @var{untagged_base} to\n\
> +@var{tag}.",
> +  rtx, (rtx untagged_base, rtx tag, rtx target), default_memtag_set_tag)
> +
> +DEFHOOK
> +(extract_tag,
> + "Return an RTX representing the tag stored in @var{tagged_pointer}.\n\
> +Store the result in @var{target} if it is convenient.\n\
> +The default represents the top byte of the original pointer.",
> +  rtx, (rtx tagged_pointer, rtx target), default_memtag_extract_tag)
> +
> +DEFHOOK
> +(untagged_pointer,
> + "Return an RTX representing @var{tagged_pointer} with its tag set to zero.\n\
> +Store the result in @var{target} if convenient.\n\
> +The default clears the top byte of the original pointer.",
> +  rtx, (rtx tagged_pointer, rtx target), default_memtag_untagged_pointer)
> +
>  HOOK_VECTOR_END (memtag)
>  #undef HOOK_PREFIX
>  #define HOOK_PREFIX "TARGET_"

These hooks look like a nice abstraction, thanks.

> @@ -2379,10 +2383,130 @@ default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
>    return result;
>  }
>  
> +/* How many bits to shift in order to access the tag bits.
> +   The default is to store the tag in the top 8 bits of a 64 bit pointer, hence
> +   shifting 56 bits will leave just the tag.
> +   We require that the shift is less than 64 for the `const_int_rtx` use here,
> +   that's also a requirement for the tag to actually be stored on any systems
> +   with 64 bit (or smaller) pointers.  */
> +#define HWASAN_SHIFT 56

I think (GET_MODE_PRECISION (Pmode) - 8) would be better.

> +#define HWASAN_SHIFT_RTX const_int_rtx[MAX_SAVED_CONST_INT + HWASAN_SHIFT]

And similarly this can use GEN_INT (HWASAN_SHIFT).  Using GEN_INT rather
gen_int_mode isn't great, but it's safe in context, and GCC doesn't have
an interface for directly specifying the correct mode for a shift amount.

> +
>  bool
>  default_memtag_can_tag_addresses ()
>  {
>    return false;
>  }
>  
> +uint8_t
> +default_memtag_tag_size ()
> +{
> +  return 8;
> +}
> +
> +uint8_t
> +default_memtag_granule_size ()
> +{
> +  return 16;
> +}
> +
> +/* The default implementation of TARGET_MEMTAG_INSERT_RANDOM_TAG.  */
> +rtx
> +default_memtag_insert_random_tag (rtx untagged)
> +{
> +  gcc_assert (param_hwasan_instrument_stack);
> +  if (param_hwasan_random_frame_tag)
> +    {
> +      rtx base = gen_reg_rtx (Pmode);
> +      rtx temp = gen_reg_rtx (QImode);
> +      rtx fn = init_one_libfunc ("__hwasan_generate_tag");
> +      rtx new_tag = emit_library_call_value (fn, temp, LCT_NORMAL, QImode);
> +      rtx ret = targetm.memtag.set_tag (untagged, new_tag, base);
> +      if (ret != base)
> +	emit_move_insn (base, ret);
> +      return base;

The idea with “store in target if convenient” interfaces is that the
caller shouldn't (need to) create fresh registers itself.  So this
should just be:

      return targetm.memtag.set_tag (untagged, new_tag, NULL_RTX);

(with no “base” or “ret” variables).  Similarly there's no need
to create “tmp” for passing to emit_library_call_value; you can
just pass NULL_RTX instead.

FWIW, passing in a target to this hook too would be fine with me.
It's just that (as with other interfaces like this) the caller
couldn't assume that the target is actually used.

> +    }
> +  else
> +    {
> +      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
> +	 In the future we may add the option emit random tags with inline
> +	 instrumentation instead of function calls.  This would be the same
> +	 between the kernel and userland.  */
> +      return untagged;
> +    }
> +}
> +
> +/* The default implementation of TARGET_MEMTAG_ADD_TAG.  */
> +rtx
> +default_memtag_add_tag (rtx base, poly_int64 offset, uint8_t tag_offset)
> +{
> +  /* Need to look into what the most efficient code sequence is.
> +     This is a code sequence that would be emitted *many* times, so we
> +     want it as small as possible.
> +
> +     There are two places where tag overflow is a question:
> +       - Tagging the shadow stack.
> +	  (both tagging and untagging).
> +       - Tagging addressable pointers.
> +
> +     We need to ensure both behaviors are the same (i.e. that the tag that
> +     ends up in a pointer after "overflowing" the tag bits with a tag addition
> +     is the same that ends up in the shadow space).
> +
> +     The aim is that the behavior of tag addition should follow modulo
> +     wrapping in both instances.
> +
> +     The libhwasan code doesn't have any path that increments a pointer's tag,
> +     which means it has no opinion on what happens when a tag increment
> +     overflows (and hence we can choose our own behavior).
> +
> +     NOTE:
> +	Here we return an expression which represents the base with the
> +	provided offsets.
> +	This does not have to be a valid operand to anything, since the
> +	`force_operand` machinery in the compiler already handles this.  */
> +
> +  offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
> +  return plus_constant (Pmode, base, offset);
> +}

Maybe this is just me, but the code is so simple and so in line with
what I'd expect that I think it might be clearer to drop the “NOTE: …”
part of the comment.  I thought at first it was describing something
specific to this particular hook implementation, but I think really
it's just restating the interface described in the documentation.

(To be clear: the rest of the comment is good, it's just the NOTE
part that I found confusing.)

Thanks,
Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [Patch 0/X] HWASAN v4
  2020-10-16  9:03       ` Martin Liška
@ 2020-11-13 16:33         ` Martin Liška
  2020-11-13 16:57           ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2020-11-13 16:33 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: jakub, Richard Earnshaw, kcc, dodji, joseph

[-- Attachment #1: Type: text/plain, Size: 251 bytes --]

On 10/16/20 11:03 AM, Martin Liška wrote:
> Hello.
> 
> I've just merged libsanitizer and there's the corresponding part that includes
> libhwasan.
> 
> Martin

Hey.

I've just made last merge from upstream, there's corresponding hwasan part.

Martin

[-- Attachment #2: 0001-libsanitizer-add-hwasan.patch --]
[-- Type: text/x-patch, Size: 170108 bytes --]

From 77fd5479c38654b7d66334bf33292c1775eb51ac Mon Sep 17 00:00:00 2001
From: Martin Liska <mliska@suse.cz>
Date: Fri, 13 Nov 2020 17:06:48 +0100
Subject: [PATCH] libsanitizer: add hwasan.

---
 libsanitizer/MERGE                            |   2 +-
 libsanitizer/hwasan/hwasan.cpp                | 522 ++++++++++++++
 libsanitizer/hwasan/hwasan.h                  | 165 +++++
 libsanitizer/hwasan/hwasan_allocator.cpp      | 408 +++++++++++
 libsanitizer/hwasan/hwasan_allocator.h        | 107 +++
 libsanitizer/hwasan/hwasan_checks.h           | 124 ++++
 libsanitizer/hwasan/hwasan_dynamic_shadow.cpp | 126 ++++
 libsanitizer/hwasan/hwasan_dynamic_shadow.h   |  27 +
 libsanitizer/hwasan/hwasan_exceptions.cpp     |  67 ++
 libsanitizer/hwasan/hwasan_flags.h            |  29 +
 libsanitizer/hwasan/hwasan_flags.inc          |  74 ++
 libsanitizer/hwasan/hwasan_globals.cpp        |  91 +++
 libsanitizer/hwasan/hwasan_globals.h          |  49 ++
 libsanitizer/hwasan/hwasan_interceptors.cpp   | 349 ++++++++++
 .../hwasan/hwasan_interceptors_vfork.S        |  11 +
 .../hwasan/hwasan_interface_internal.h        | 227 ++++++
 libsanitizer/hwasan/hwasan_linux.cpp          | 455 ++++++++++++
 libsanitizer/hwasan/hwasan_malloc_bisect.h    |  50 ++
 libsanitizer/hwasan/hwasan_mapping.h          |  66 ++
 libsanitizer/hwasan/hwasan_memintrinsics.cpp  |  44 ++
 libsanitizer/hwasan/hwasan_new_delete.cpp     |  81 +++
 libsanitizer/hwasan/hwasan_poisoning.cpp      |  52 ++
 libsanitizer/hwasan/hwasan_poisoning.h        |  24 +
 libsanitizer/hwasan/hwasan_report.cpp         | 651 ++++++++++++++++++
 libsanitizer/hwasan/hwasan_report.h           |  35 +
 libsanitizer/hwasan/hwasan_setjmp.S           | 100 +++
 .../hwasan/hwasan_tag_mismatch_aarch64.S      | 152 ++++
 libsanitizer/hwasan/hwasan_thread.cpp         | 133 ++++
 libsanitizer/hwasan/hwasan_thread.h           |  98 +++
 libsanitizer/hwasan/hwasan_thread_list.cpp    |  15 +
 libsanitizer/hwasan/hwasan_thread_list.h      | 215 ++++++
 libsanitizer/hwasan/hwasan_type_test.cpp      |  25 +
 libsanitizer/merge.sh                         |   1 +
 33 files changed, 4574 insertions(+), 1 deletion(-)
 create mode 100644 libsanitizer/hwasan/hwasan.cpp
 create mode 100644 libsanitizer/hwasan/hwasan.h
 create mode 100644 libsanitizer/hwasan/hwasan_allocator.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_allocator.h
 create mode 100644 libsanitizer/hwasan/hwasan_checks.h
 create mode 100644 libsanitizer/hwasan/hwasan_dynamic_shadow.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_dynamic_shadow.h
 create mode 100644 libsanitizer/hwasan/hwasan_exceptions.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_flags.h
 create mode 100644 libsanitizer/hwasan/hwasan_flags.inc
 create mode 100644 libsanitizer/hwasan/hwasan_globals.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_globals.h
 create mode 100644 libsanitizer/hwasan/hwasan_interceptors.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_interceptors_vfork.S
 create mode 100644 libsanitizer/hwasan/hwasan_interface_internal.h
 create mode 100644 libsanitizer/hwasan/hwasan_linux.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_malloc_bisect.h
 create mode 100644 libsanitizer/hwasan/hwasan_mapping.h
 create mode 100644 libsanitizer/hwasan/hwasan_memintrinsics.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_new_delete.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_poisoning.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_poisoning.h
 create mode 100644 libsanitizer/hwasan/hwasan_report.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_report.h
 create mode 100644 libsanitizer/hwasan/hwasan_setjmp.S
 create mode 100644 libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S
 create mode 100644 libsanitizer/hwasan/hwasan_thread.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_thread.h
 create mode 100644 libsanitizer/hwasan/hwasan_thread_list.cpp
 create mode 100644 libsanitizer/hwasan/hwasan_thread_list.h
 create mode 100644 libsanitizer/hwasan/hwasan_type_test.cpp

diff --git a/libsanitizer/MERGE b/libsanitizer/MERGE
index 2df9b4637b1..0fb64a9567c 100644
--- a/libsanitizer/MERGE
+++ b/libsanitizer/MERGE
@@ -1,4 +1,4 @@
-a28a466210199559d38251c11f30515cc83eadd6
+6e7dd1e3e1170080b76b5dcc5716bdd974343233
 
 The first line of this file holds the git revision number of the
 last merge done from the master library sources.
diff --git a/libsanitizer/hwasan/hwasan.cpp b/libsanitizer/hwasan/hwasan.cpp
new file mode 100644
index 00000000000..c5322110cb6
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan.cpp
@@ -0,0 +1,522 @@
+//===-- hwasan.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer runtime.
+//===----------------------------------------------------------------------===//
+
+#include "hwasan.h"
+
+#include "hwasan_checks.h"
+#include "hwasan_dynamic_shadow.h"
+#include "hwasan_globals.h"
+#include "hwasan_poisoning.h"
+#include "hwasan_report.h"
+#include "hwasan_thread.h"
+#include "hwasan_thread_list.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+#include "ubsan/ubsan_flags.h"
+#include "ubsan/ubsan_init.h"
+
+// ACHTUNG! No system header includes in this file.
+
+using namespace __sanitizer;
+
+namespace __hwasan {
+
+static Flags hwasan_flags;
+
+Flags *flags() {
+  return &hwasan_flags;
+}
+
+int hwasan_inited = 0;
+int hwasan_instrumentation_inited = 0;
+bool hwasan_init_is_running;
+
+int hwasan_report_count = 0;
+
+void Flags::SetDefaults() {
+#define HWASAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "hwasan_flags.inc"
+#undef HWASAN_FLAG
+}
+
+static void RegisterHwasanFlags(FlagParser *parser, Flags *f) {
+#define HWASAN_FLAG(Type, Name, DefaultValue, Description) \
+  RegisterFlag(parser, #Name, Description, &f->Name);
+#include "hwasan_flags.inc"
+#undef HWASAN_FLAG
+}
+
+static void InitializeFlags() {
+  SetCommonFlagsDefaults();
+  {
+    CommonFlags cf;
+    cf.CopyFrom(*common_flags());
+    cf.external_symbolizer_path = GetEnv("HWASAN_SYMBOLIZER_PATH");
+    cf.malloc_context_size = 20;
+    cf.handle_ioctl = true;
+    // FIXME: test and enable.
+    cf.check_printf = false;
+    cf.intercept_tls_get_addr = true;
+    cf.exitcode = 99;
+    // 8 shadow pages ~512kB, small enough to cover common stack sizes.
+    cf.clear_shadow_mmap_threshold = 4096 * (SANITIZER_ANDROID ? 2 : 8);
+    // Sigtrap is used in error reporting.
+    cf.handle_sigtrap = kHandleSignalExclusive;
+
+#if SANITIZER_ANDROID
+    // Let platform handle other signals. It is better at reporting them then we
+    // are.
+    cf.handle_segv = kHandleSignalNo;
+    cf.handle_sigbus = kHandleSignalNo;
+    cf.handle_abort = kHandleSignalNo;
+    cf.handle_sigill = kHandleSignalNo;
+    cf.handle_sigfpe = kHandleSignalNo;
+#endif
+    OverrideCommonFlags(cf);
+  }
+
+  Flags *f = flags();
+  f->SetDefaults();
+
+  FlagParser parser;
+  RegisterHwasanFlags(&parser, f);
+  RegisterCommonFlags(&parser);
+
+#if HWASAN_CONTAINS_UBSAN
+  __ubsan::Flags *uf = __ubsan::flags();
+  uf->SetDefaults();
+
+  FlagParser ubsan_parser;
+  __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
+  RegisterCommonFlags(&ubsan_parser);
+#endif
+
+  // Override from user-specified string.
+  if (__hwasan_default_options)
+    parser.ParseString(__hwasan_default_options());
+#if HWASAN_CONTAINS_UBSAN
+  const char *ubsan_default_options = __ubsan_default_options();
+  ubsan_parser.ParseString(ubsan_default_options);
+#endif
+
+  parser.ParseStringFromEnv("HWASAN_OPTIONS");
+#if HWASAN_CONTAINS_UBSAN
+  ubsan_parser.ParseStringFromEnv("UBSAN_OPTIONS");
+#endif
+
+  InitializeCommonFlags();
+
+  if (Verbosity()) ReportUnrecognizedFlags();
+
+  if (common_flags()->help) parser.PrintFlagDescriptions();
+}
+
+static void HWAsanCheckFailed(const char *file, int line, const char *cond,
+                              u64 v1, u64 v2) {
+  Report("HWAddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file,
+         line, cond, (uptr)v1, (uptr)v2);
+  PRINT_CURRENT_STACK_CHECK();
+  Die();
+}
+
+static constexpr uptr kMemoryUsageBufferSize = 4096;
+
+static void HwasanFormatMemoryUsage(InternalScopedString &s) {
+  HwasanThreadList &thread_list = hwasanThreadList();
+  auto thread_stats = thread_list.GetThreadStats();
+  auto *sds = StackDepotGetStats();
+  AllocatorStatCounters asc;
+  GetAllocatorStats(asc);
+  s.append(
+      "HWASAN pid: %d rss: %zd threads: %zd stacks: %zd"
+      " thr_aux: %zd stack_depot: %zd uniq_stacks: %zd"
+      " heap: %zd",
+      internal_getpid(), GetRSS(), thread_stats.n_live_threads,
+      thread_stats.total_stack_size,
+      thread_stats.n_live_threads * thread_list.MemoryUsedPerThread(),
+      sds->allocated, sds->n_uniq_ids, asc[AllocatorStatMapped]);
+}
+
+#if SANITIZER_ANDROID
+static char *memory_usage_buffer = nullptr;
+
+static void InitMemoryUsage() {
+  memory_usage_buffer =
+      (char *)MmapOrDie(kMemoryUsageBufferSize, "memory usage string");
+  CHECK(memory_usage_buffer);
+  memory_usage_buffer[0] = '\0';
+  DecorateMapping((uptr)memory_usage_buffer, kMemoryUsageBufferSize,
+                  memory_usage_buffer);
+}
+
+void UpdateMemoryUsage() {
+  if (!flags()->export_memory_stats)
+    return;
+  if (!memory_usage_buffer)
+    InitMemoryUsage();
+  InternalScopedString s(kMemoryUsageBufferSize);
+  HwasanFormatMemoryUsage(s);
+  internal_strncpy(memory_usage_buffer, s.data(), kMemoryUsageBufferSize - 1);
+  memory_usage_buffer[kMemoryUsageBufferSize - 1] = '\0';
+}
+#else
+void UpdateMemoryUsage() {}
+#endif
+
+} // namespace __hwasan
+
+using namespace __hwasan;
+
+void __sanitizer::BufferedStackTrace::UnwindImpl(
+    uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
+  Thread *t = GetCurrentThread();
+  if (!t) {
+    // The thread is still being created, or has already been destroyed.
+    size = 0;
+    return;
+  }
+  Unwind(max_depth, pc, bp, context, t->stack_top(), t->stack_bottom(),
+         request_fast);
+}
+
+static bool InitializeSingleGlobal(const hwasan_global &global) {
+  uptr full_granule_size = RoundDownTo(global.size(), 16);
+  TagMemoryAligned(global.addr(), full_granule_size, global.tag());
+  if (global.size() % 16)
+    TagMemoryAligned(global.addr() + full_granule_size, 16, global.size() % 16);
+  return false;
+}
+
+static void InitLoadedGlobals() {
+  dl_iterate_phdr(
+      [](dl_phdr_info *info, size_t /* size */, void * /* data */) -> int {
+        for (const hwasan_global &global : HwasanGlobalsFor(
+                 info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum))
+          InitializeSingleGlobal(global);
+        return 0;
+      },
+      nullptr);
+}
+
+// Prepare to run instrumented code on the main thread.
+static void InitInstrumentation() {
+  if (hwasan_instrumentation_inited) return;
+
+  InitPrctl();
+
+  if (!InitShadow()) {
+    Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
+    DumpProcessMap();
+    Die();
+  }
+
+  InitThreads();
+  hwasanThreadList().CreateCurrentThread();
+
+  hwasan_instrumentation_inited = 1;
+}
+
+// Interface.
+
+uptr __hwasan_shadow_memory_dynamic_address;  // Global interface symbol.
+
+// This function was used by the old frame descriptor mechanism. We keep it
+// around to avoid breaking ABI.
+void __hwasan_init_frames(uptr beg, uptr end) {}
+
+void __hwasan_init_static() {
+  InitShadowGOT();
+  InitInstrumentation();
+
+  // In the non-static code path we call dl_iterate_phdr here. But at this point
+  // libc might not have been initialized enough for dl_iterate_phdr to work.
+  // Fortunately, since this is a statically linked executable we can use the
+  // linker-defined symbol __ehdr_start to find the only relevant set of phdrs.
+  extern ElfW(Ehdr) __ehdr_start;
+  for (const hwasan_global &global : HwasanGlobalsFor(
+           /* base */ 0,
+           reinterpret_cast<const ElfW(Phdr) *>(
+               reinterpret_cast<const char *>(&__ehdr_start) +
+               __ehdr_start.e_phoff),
+           __ehdr_start.e_phnum))
+    InitializeSingleGlobal(global);
+}
+
+void __hwasan_init() {
+  CHECK(!hwasan_init_is_running);
+  if (hwasan_inited) return;
+  hwasan_init_is_running = 1;
+  SanitizerToolName = "HWAddressSanitizer";
+
+  InitTlsSize();
+
+  CacheBinaryName();
+  InitializeFlags();
+
+  // Install tool-specific callbacks in sanitizer_common.
+  SetCheckFailedCallback(HWAsanCheckFailed);
+
+  __sanitizer_set_report_path(common_flags()->log_path);
+
+  AndroidTestTlsSlot();
+
+  DisableCoreDumperIfNecessary();
+
+  InitInstrumentation();
+  InitLoadedGlobals();
+
+  // Needs to be called here because flags()->random_tags might not have been
+  // initialized when InitInstrumentation() was called.
+  GetCurrentThread()->InitRandomState();
+
+  SetPrintfAndReportCallback(AppendToErrorMessageBuffer);
+  // This may call libc -> needs initialized shadow.
+  AndroidLogInit();
+
+  InitializeInterceptors();
+  InstallDeadlySignalHandlers(HwasanOnDeadlySignal);
+  InstallAtExitHandler(); // Needs __cxa_atexit interceptor.
+
+  InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
+
+  HwasanTSDInit();
+  HwasanTSDThreadInit();
+
+  HwasanAllocatorInit();
+
+#if HWASAN_CONTAINS_UBSAN
+  __ubsan::InitAsPlugin();
+#endif
+
+  VPrintf(1, "HWAddressSanitizer init done\n");
+
+  hwasan_init_is_running = 0;
+  hwasan_inited = 1;
+}
+
+void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                             ElfW(Half) phnum) {
+  for (const hwasan_global &global : HwasanGlobalsFor(base, phdr, phnum))
+    InitializeSingleGlobal(global);
+}
+
+void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                               ElfW(Half) phnum) {
+  for (; phnum != 0; ++phdr, --phnum)
+    if (phdr->p_type == PT_LOAD)
+      TagMemory(base + phdr->p_vaddr, phdr->p_memsz, 0);
+}
+
+void __hwasan_print_shadow(const void *p, uptr sz) {
+  uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p));
+  uptr shadow_first = MemToShadow(ptr_raw);
+  uptr shadow_last = MemToShadow(ptr_raw + sz - 1);
+  Printf("HWASan shadow map for %zx .. %zx (pointer tag %x)\n", ptr_raw,
+         ptr_raw + sz, GetTagFromPointer((uptr)p));
+  for (uptr s = shadow_first; s <= shadow_last; ++s)
+    Printf("  %zx: %x\n", ShadowToMem(s), *(tag_t *)s);
+}
+
+sptr __hwasan_test_shadow(const void *p, uptr sz) {
+  if (sz == 0)
+    return -1;
+  tag_t ptr_tag = GetTagFromPointer((uptr)p);
+  uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p));
+  uptr shadow_first = MemToShadow(ptr_raw);
+  uptr shadow_last = MemToShadow(ptr_raw + sz - 1);
+  for (uptr s = shadow_first; s <= shadow_last; ++s)
+    if (*(tag_t *)s != ptr_tag) {
+      sptr offset = ShadowToMem(s) - ptr_raw;
+      return offset < 0 ? 0 : offset;
+    }
+  return -1;
+}
+
+u16 __sanitizer_unaligned_load16(const uu16 *p) {
+  return *p;
+}
+u32 __sanitizer_unaligned_load32(const uu32 *p) {
+  return *p;
+}
+u64 __sanitizer_unaligned_load64(const uu64 *p) {
+  return *p;
+}
+void __sanitizer_unaligned_store16(uu16 *p, u16 x) {
+  *p = x;
+}
+void __sanitizer_unaligned_store32(uu32 *p, u32 x) {
+  *p = x;
+}
+void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
+  *p = x;
+}
+
+void __hwasan_loadN(uptr p, uptr sz) {
+  CheckAddressSized<ErrorAction::Abort, AccessType::Load>(p, sz);
+}
+void __hwasan_load1(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Load, 0>(p);
+}
+void __hwasan_load2(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Load, 1>(p);
+}
+void __hwasan_load4(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Load, 2>(p);
+}
+void __hwasan_load8(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Load, 3>(p);
+}
+void __hwasan_load16(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Load, 4>(p);
+}
+
+void __hwasan_loadN_noabort(uptr p, uptr sz) {
+  CheckAddressSized<ErrorAction::Recover, AccessType::Load>(p, sz);
+}
+void __hwasan_load1_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Load, 0>(p);
+}
+void __hwasan_load2_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Load, 1>(p);
+}
+void __hwasan_load4_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Load, 2>(p);
+}
+void __hwasan_load8_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Load, 3>(p);
+}
+void __hwasan_load16_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Load, 4>(p);
+}
+
+void __hwasan_storeN(uptr p, uptr sz) {
+  CheckAddressSized<ErrorAction::Abort, AccessType::Store>(p, sz);
+}
+void __hwasan_store1(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Store, 0>(p);
+}
+void __hwasan_store2(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Store, 1>(p);
+}
+void __hwasan_store4(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Store, 2>(p);
+}
+void __hwasan_store8(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Store, 3>(p);
+}
+void __hwasan_store16(uptr p) {
+  CheckAddress<ErrorAction::Abort, AccessType::Store, 4>(p);
+}
+
+void __hwasan_storeN_noabort(uptr p, uptr sz) {
+  CheckAddressSized<ErrorAction::Recover, AccessType::Store>(p, sz);
+}
+void __hwasan_store1_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Store, 0>(p);
+}
+void __hwasan_store2_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Store, 1>(p);
+}
+void __hwasan_store4_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Store, 2>(p);
+}
+void __hwasan_store8_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Store, 3>(p);
+}
+void __hwasan_store16_noabort(uptr p) {
+  CheckAddress<ErrorAction::Recover, AccessType::Store, 4>(p);
+}
+
+void __hwasan_tag_memory(uptr p, u8 tag, uptr sz) {
+  TagMemoryAligned(p, sz, tag);
+}
+
+uptr __hwasan_tag_pointer(uptr p, u8 tag) {
+  return AddTagToPointer(p, tag);
+}
+
+void __hwasan_handle_longjmp(const void *sp_dst) {
+  uptr dst = (uptr)sp_dst;
+  // HWASan does not support tagged SP.
+  CHECK(GetTagFromPointer(dst) == 0);
+
+  uptr sp = (uptr)__builtin_frame_address(0);
+  static const uptr kMaxExpectedCleanupSize = 64 << 20;  // 64M
+  if (dst < sp || dst - sp > kMaxExpectedCleanupSize) {
+    Report(
+        "WARNING: HWASan is ignoring requested __hwasan_handle_longjmp: "
+        "stack top: %p; target %p; distance: %p (%zd)\n"
+        "False positive error reports may follow\n",
+        (void *)sp, (void *)dst, dst - sp);
+    return;
+  }
+  TagMemory(sp, dst - sp, 0);
+}
+
+void __hwasan_handle_vfork(const void *sp_dst) {
+  uptr sp = (uptr)sp_dst;
+  Thread *t = GetCurrentThread();
+  CHECK(t);
+  uptr top = t->stack_top();
+  uptr bottom = t->stack_bottom();
+  if (top == 0 || bottom == 0 || sp < bottom || sp >= top) {
+    Report(
+        "WARNING: HWASan is ignoring requested __hwasan_handle_vfork: "
+        "stack top: %zx; current %zx; bottom: %zx \n"
+        "False positive error reports may follow\n",
+        top, sp, bottom);
+    return;
+  }
+  TagMemory(bottom, sp - bottom, 0);
+}
+
+extern "C" void *__hwasan_extra_spill_area() {
+  Thread *t = GetCurrentThread();
+  return &t->vfork_spill();
+}
+
+void __hwasan_print_memory_usage() {
+  InternalScopedString s(kMemoryUsageBufferSize);
+  HwasanFormatMemoryUsage(s);
+  Printf("%s\n", s.data());
+}
+
+static const u8 kFallbackTag = 0xBB;
+
+u8 __hwasan_generate_tag() {
+  Thread *t = GetCurrentThread();
+  if (!t) return kFallbackTag;
+  return t->GenerateRandomTag();
+}
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+const char* __hwasan_default_options() { return ""; }
+}  // extern "C"
+#endif
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_stack_trace() {
+  GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME());
+  stack.Print();
+}
+} // extern "C"
diff --git a/libsanitizer/hwasan/hwasan.h b/libsanitizer/hwasan/hwasan.h
new file mode 100644
index 00000000000..d4521efd089
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan.h
@@ -0,0 +1,165 @@
+//===-- hwasan.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Private Hwasan header.
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_H
+#define HWASAN_H
+
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "hwasan_interface_internal.h"
+#include "hwasan_flags.h"
+#include "ubsan/ubsan_platform.h"
+
+#ifndef HWASAN_CONTAINS_UBSAN
+# define HWASAN_CONTAINS_UBSAN CAN_SANITIZE_UB
+#endif
+
+#ifndef HWASAN_WITH_INTERCEPTORS
+#define HWASAN_WITH_INTERCEPTORS 0
+#endif
+
+#ifndef HWASAN_REPLACE_OPERATORS_NEW_AND_DELETE
+#define HWASAN_REPLACE_OPERATORS_NEW_AND_DELETE HWASAN_WITH_INTERCEPTORS
+#endif
+
+typedef u8 tag_t;
+
+// TBI (Top Byte Ignore) feature of AArch64: bits [63:56] are ignored in address
+// translation and can be used to store a tag.
+const unsigned kAddressTagShift = 56;
+const uptr kAddressTagMask = 0xFFUL << kAddressTagShift;
+
+// Minimal alignment of the shadow base address. Determines the space available
+// for threads and stack histories. This is an ABI constant.
+const unsigned kShadowBaseAlignment = 32;
+
+const unsigned kRecordAddrBaseTagShift = 3;
+const unsigned kRecordFPShift = 48;
+const unsigned kRecordFPLShift = 4;
+const unsigned kRecordFPModulus = 1 << (64 - kRecordFPShift + kRecordFPLShift);
+
+static inline tag_t GetTagFromPointer(uptr p) {
+  return p >> kAddressTagShift;
+}
+
+static inline uptr UntagAddr(uptr tagged_addr) {
+  return tagged_addr & ~kAddressTagMask;
+}
+
+static inline void *UntagPtr(const void *tagged_ptr) {
+  return reinterpret_cast<void *>(
+      UntagAddr(reinterpret_cast<uptr>(tagged_ptr)));
+}
+
+static inline uptr AddTagToPointer(uptr p, tag_t tag) {
+  return (p & ~kAddressTagMask) | ((uptr)tag << kAddressTagShift);
+}
+
+namespace __hwasan {
+
+extern int hwasan_inited;
+extern bool hwasan_init_is_running;
+extern int hwasan_report_count;
+
+bool InitShadow();
+void InitPrctl();
+void InitThreads();
+void InitializeInterceptors();
+
+void HwasanAllocatorInit();
+
+void *hwasan_malloc(uptr size, StackTrace *stack);
+void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack);
+void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack);
+void *hwasan_reallocarray(void *ptr, uptr nmemb, uptr size, StackTrace *stack);
+void *hwasan_valloc(uptr size, StackTrace *stack);
+void *hwasan_pvalloc(uptr size, StackTrace *stack);
+void *hwasan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack);
+void *hwasan_memalign(uptr alignment, uptr size, StackTrace *stack);
+int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
+                        StackTrace *stack);
+void hwasan_free(void *ptr, StackTrace *stack);
+
+void InstallAtExitHandler();
+
+#define GET_MALLOC_STACK_TRACE                                            \
+  BufferedStackTrace stack;                                               \
+  if (hwasan_inited)                                                      \
+    stack.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(),         \
+                 nullptr, common_flags()->fast_unwind_on_malloc,          \
+                 common_flags()->malloc_context_size)
+
+#define GET_FATAL_STACK_TRACE_PC_BP(pc, bp)              \
+  BufferedStackTrace stack;                              \
+  if (hwasan_inited)                                     \
+    stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal)
+
+#define GET_FATAL_STACK_TRACE_HERE \
+  GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME())
+
+#define PRINT_CURRENT_STACK_CHECK() \
+  {                                 \
+    GET_FATAL_STACK_TRACE_HERE;     \
+    stack.Print();                  \
+  }
+
+void HwasanTSDInit();
+void HwasanTSDThreadInit();
+
+void HwasanOnDeadlySignal(int signo, void *info, void *context);
+
+void UpdateMemoryUsage();
+
+void AppendToErrorMessageBuffer(const char *buffer);
+
+void AndroidTestTlsSlot();
+
+}  // namespace __hwasan
+
+#define HWASAN_MALLOC_HOOK(ptr, size)       \
+  do {                                    \
+    if (&__sanitizer_malloc_hook) {       \
+      __sanitizer_malloc_hook(ptr, size); \
+    }                                     \
+    RunMallocHooks(ptr, size);            \
+  } while (false)
+#define HWASAN_FREE_HOOK(ptr)       \
+  do {                            \
+    if (&__sanitizer_free_hook) { \
+      __sanitizer_free_hook(ptr); \
+    }                             \
+    RunFreeHooks(ptr);            \
+  } while (false)
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+// For both bionic and glibc __sigset_t is an unsigned long.
+typedef unsigned long __hw_sigset_t;
+// Setjmp and longjmp implementations are platform specific, and hence the
+// interception code is platform specific too.  As yet we've only implemented
+// the interception for AArch64.
+typedef unsigned long long __hw_register_buf[22];
+struct __hw_jmp_buf_struct {
+  // NOTE: The machine-dependent definition of `__sigsetjmp'
+  // assume that a `__hw_jmp_buf' begins with a `__hw_register_buf' and that
+  // `__mask_was_saved' follows it.  Do not move these members or add others
+  // before it.
+  __hw_register_buf __jmpbuf; // Calling environment.
+  int __mask_was_saved;       // Saved the signal mask?
+  __hw_sigset_t __saved_mask; // Saved signal mask.
+};
+typedef struct __hw_jmp_buf_struct __hw_jmp_buf[1];
+typedef struct __hw_jmp_buf_struct __hw_sigjmp_buf[1];
+#endif // HWASAN_WITH_INTERCEPTORS && __aarch64__
+
+#endif  // HWASAN_H
diff --git a/libsanitizer/hwasan/hwasan_allocator.cpp b/libsanitizer/hwasan/hwasan_allocator.cpp
new file mode 100644
index 00000000000..0b6b7347892
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_allocator.cpp
@@ -0,0 +1,408 @@
+//===-- hwasan_allocator.cpp ------------------------ ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer allocator.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "hwasan.h"
+#include "hwasan_allocator.h"
+#include "hwasan_checks.h"
+#include "hwasan_mapping.h"
+#include "hwasan_malloc_bisect.h"
+#include "hwasan_thread.h"
+#include "hwasan_report.h"
+
+namespace __hwasan {
+
+static Allocator allocator;
+static AllocatorCache fallback_allocator_cache;
+static SpinMutex fallback_mutex;
+static atomic_uint8_t hwasan_allocator_tagging_enabled;
+
+static const tag_t kFallbackAllocTag = 0xBB;
+static const tag_t kFallbackFreeTag = 0xBC;
+
+enum RightAlignMode {
+  kRightAlignNever,
+  kRightAlignSometimes,
+  kRightAlignAlways
+};
+
+// Initialized in HwasanAllocatorInit, an never changed.
+static ALIGNED(16) u8 tail_magic[kShadowAlignment - 1];
+
+bool HwasanChunkView::IsAllocated() const {
+  return metadata_ && metadata_->alloc_context_id &&
+         metadata_->get_requested_size();
+}
+
+// Aligns the 'addr' right to the granule boundary.
+static uptr AlignRight(uptr addr, uptr requested_size) {
+  uptr tail_size = requested_size % kShadowAlignment;
+  if (!tail_size) return addr;
+  return addr + kShadowAlignment - tail_size;
+}
+
+uptr HwasanChunkView::Beg() const {
+  if (metadata_ && metadata_->right_aligned)
+    return AlignRight(block_, metadata_->get_requested_size());
+  return block_;
+}
+uptr HwasanChunkView::End() const {
+  return Beg() + UsedSize();
+}
+uptr HwasanChunkView::UsedSize() const {
+  return metadata_->get_requested_size();
+}
+u32 HwasanChunkView::GetAllocStackId() const {
+  return metadata_->alloc_context_id;
+}
+
+uptr HwasanChunkView::ActualSize() const {
+  return allocator.GetActuallyAllocatedSize(reinterpret_cast<void *>(block_));
+}
+
+bool HwasanChunkView::FromSmallHeap() const {
+  return allocator.FromPrimary(reinterpret_cast<void *>(block_));
+}
+
+void GetAllocatorStats(AllocatorStatCounters s) {
+  allocator.GetStats(s);
+}
+
+void HwasanAllocatorInit() {
+  atomic_store_relaxed(&hwasan_allocator_tagging_enabled,
+                       !flags()->disable_allocator_tagging);
+  SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
+  allocator.Init(common_flags()->allocator_release_to_os_interval_ms);
+  for (uptr i = 0; i < sizeof(tail_magic); i++)
+    tail_magic[i] = GetCurrentThread()->GenerateRandomTag();
+}
+
+void AllocatorSwallowThreadLocalCache(AllocatorCache *cache) {
+  allocator.SwallowCache(cache);
+}
+
+static uptr TaggedSize(uptr size) {
+  if (!size) size = 1;
+  uptr new_size = RoundUpTo(size, kShadowAlignment);
+  CHECK_GE(new_size, size);
+  return new_size;
+}
+
+static void *HwasanAllocate(StackTrace *stack, uptr orig_size, uptr alignment,
+                            bool zeroise) {
+  if (orig_size > kMaxAllowedMallocSize) {
+    if (AllocatorMayReturnNull()) {
+      Report("WARNING: HWAddressSanitizer failed to allocate 0x%zx bytes\n",
+             orig_size);
+      return nullptr;
+    }
+    ReportAllocationSizeTooBig(orig_size, kMaxAllowedMallocSize, stack);
+  }
+
+  alignment = Max(alignment, kShadowAlignment);
+  uptr size = TaggedSize(orig_size);
+  Thread *t = GetCurrentThread();
+  void *allocated;
+  if (t) {
+    allocated = allocator.Allocate(t->allocator_cache(), size, alignment);
+  } else {
+    SpinMutexLock l(&fallback_mutex);
+    AllocatorCache *cache = &fallback_allocator_cache;
+    allocated = allocator.Allocate(cache, size, alignment);
+  }
+  if (UNLIKELY(!allocated)) {
+    SetAllocatorOutOfMemory();
+    if (AllocatorMayReturnNull())
+      return nullptr;
+    ReportOutOfMemory(size, stack);
+  }
+  Metadata *meta =
+      reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated));
+  meta->set_requested_size(orig_size);
+  meta->alloc_context_id = StackDepotPut(*stack);
+  meta->right_aligned = false;
+  if (zeroise) {
+    internal_memset(allocated, 0, size);
+  } else if (flags()->max_malloc_fill_size > 0) {
+    uptr fill_size = Min(size, (uptr)flags()->max_malloc_fill_size);
+    internal_memset(allocated, flags()->malloc_fill_byte, fill_size);
+  }
+  if (size != orig_size) {
+    internal_memcpy(reinterpret_cast<u8 *>(allocated) + orig_size, tail_magic,
+                    size - orig_size - 1);
+  }
+
+  void *user_ptr = allocated;
+  // Tagging can only be skipped when both tag_in_malloc and tag_in_free are
+  // false. When tag_in_malloc = false and tag_in_free = true malloc needs to
+  // retag to 0.
+  if ((flags()->tag_in_malloc || flags()->tag_in_free) &&
+      atomic_load_relaxed(&hwasan_allocator_tagging_enabled)) {
+    if (flags()->tag_in_malloc && malloc_bisect(stack, orig_size)) {
+      tag_t tag = t ? t->GenerateRandomTag() : kFallbackAllocTag;
+      uptr tag_size = orig_size ? orig_size : 1;
+      uptr full_granule_size = RoundDownTo(tag_size, kShadowAlignment);
+      user_ptr =
+          (void *)TagMemoryAligned((uptr)user_ptr, full_granule_size, tag);
+      if (full_granule_size != tag_size) {
+        u8 *short_granule =
+            reinterpret_cast<u8 *>(allocated) + full_granule_size;
+        TagMemoryAligned((uptr)short_granule, kShadowAlignment,
+                         tag_size % kShadowAlignment);
+        short_granule[kShadowAlignment - 1] = tag;
+      }
+    } else {
+      user_ptr = (void *)TagMemoryAligned((uptr)user_ptr, size, 0);
+    }
+  }
+
+  HWASAN_MALLOC_HOOK(user_ptr, size);
+  return user_ptr;
+}
+
+static bool PointerAndMemoryTagsMatch(void *tagged_ptr) {
+  CHECK(tagged_ptr);
+  uptr tagged_uptr = reinterpret_cast<uptr>(tagged_ptr);
+  tag_t mem_tag = *reinterpret_cast<tag_t *>(
+      MemToShadow(reinterpret_cast<uptr>(UntagPtr(tagged_ptr))));
+  return PossiblyShortTagMatches(mem_tag, tagged_uptr, 1);
+}
+
+static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) {
+  CHECK(tagged_ptr);
+  HWASAN_FREE_HOOK(tagged_ptr);
+
+  if (!PointerAndMemoryTagsMatch(tagged_ptr))
+    ReportInvalidFree(stack, reinterpret_cast<uptr>(tagged_ptr));
+
+  void *untagged_ptr = UntagPtr(tagged_ptr);
+  void *aligned_ptr = reinterpret_cast<void *>(
+      RoundDownTo(reinterpret_cast<uptr>(untagged_ptr), kShadowAlignment));
+  Metadata *meta =
+      reinterpret_cast<Metadata *>(allocator.GetMetaData(aligned_ptr));
+  uptr orig_size = meta->get_requested_size();
+  u32 free_context_id = StackDepotPut(*stack);
+  u32 alloc_context_id = meta->alloc_context_id;
+
+  // Check tail magic.
+  uptr tagged_size = TaggedSize(orig_size);
+  if (flags()->free_checks_tail_magic && orig_size &&
+      tagged_size != orig_size) {
+    uptr tail_size = tagged_size - orig_size - 1;
+    CHECK_LT(tail_size, kShadowAlignment);
+    void *tail_beg = reinterpret_cast<void *>(
+        reinterpret_cast<uptr>(aligned_ptr) + orig_size);
+    if (tail_size && internal_memcmp(tail_beg, tail_magic, tail_size))
+      ReportTailOverwritten(stack, reinterpret_cast<uptr>(tagged_ptr),
+                            orig_size, tail_magic);
+  }
+
+  meta->set_requested_size(0);
+  meta->alloc_context_id = 0;
+  // This memory will not be reused by anyone else, so we are free to keep it
+  // poisoned.
+  Thread *t = GetCurrentThread();
+  if (flags()->max_free_fill_size > 0) {
+    uptr fill_size =
+        Min(TaggedSize(orig_size), (uptr)flags()->max_free_fill_size);
+    internal_memset(aligned_ptr, flags()->free_fill_byte, fill_size);
+  }
+  if (flags()->tag_in_free && malloc_bisect(stack, 0) &&
+      atomic_load_relaxed(&hwasan_allocator_tagging_enabled))
+    TagMemoryAligned(reinterpret_cast<uptr>(aligned_ptr), TaggedSize(orig_size),
+                     t ? t->GenerateRandomTag() : kFallbackFreeTag);
+  if (t) {
+    allocator.Deallocate(t->allocator_cache(), aligned_ptr);
+    if (auto *ha = t->heap_allocations())
+      ha->push({reinterpret_cast<uptr>(tagged_ptr), alloc_context_id,
+                free_context_id, static_cast<u32>(orig_size)});
+  } else {
+    SpinMutexLock l(&fallback_mutex);
+    AllocatorCache *cache = &fallback_allocator_cache;
+    allocator.Deallocate(cache, aligned_ptr);
+  }
+}
+
+static void *HwasanReallocate(StackTrace *stack, void *tagged_ptr_old,
+                              uptr new_size, uptr alignment) {
+  if (!PointerAndMemoryTagsMatch(tagged_ptr_old))
+    ReportInvalidFree(stack, reinterpret_cast<uptr>(tagged_ptr_old));
+
+  void *tagged_ptr_new =
+      HwasanAllocate(stack, new_size, alignment, false /*zeroise*/);
+  if (tagged_ptr_old && tagged_ptr_new) {
+    void *untagged_ptr_old =  UntagPtr(tagged_ptr_old);
+    Metadata *meta =
+        reinterpret_cast<Metadata *>(allocator.GetMetaData(untagged_ptr_old));
+    internal_memcpy(
+        UntagPtr(tagged_ptr_new), untagged_ptr_old,
+        Min(new_size, static_cast<uptr>(meta->get_requested_size())));
+    HwasanDeallocate(stack, tagged_ptr_old);
+  }
+  return tagged_ptr_new;
+}
+
+static void *HwasanCalloc(StackTrace *stack, uptr nmemb, uptr size) {
+  if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
+    if (AllocatorMayReturnNull())
+      return nullptr;
+    ReportCallocOverflow(nmemb, size, stack);
+  }
+  return HwasanAllocate(stack, nmemb * size, sizeof(u64), true);
+}
+
+HwasanChunkView FindHeapChunkByAddress(uptr address) {
+  void *block = allocator.GetBlockBegin(reinterpret_cast<void*>(address));
+  if (!block)
+    return HwasanChunkView();
+  Metadata *metadata =
+      reinterpret_cast<Metadata*>(allocator.GetMetaData(block));
+  return HwasanChunkView(reinterpret_cast<uptr>(block), metadata);
+}
+
+static uptr AllocationSize(const void *tagged_ptr) {
+  const void *untagged_ptr = UntagPtr(tagged_ptr);
+  if (!untagged_ptr) return 0;
+  const void *beg = allocator.GetBlockBegin(untagged_ptr);
+  Metadata *b = (Metadata *)allocator.GetMetaData(untagged_ptr);
+  if (b->right_aligned) {
+    if (beg != reinterpret_cast<void *>(RoundDownTo(
+                   reinterpret_cast<uptr>(untagged_ptr), kShadowAlignment)))
+      return 0;
+  } else {
+    if (beg != untagged_ptr) return 0;
+  }
+  return b->get_requested_size();
+}
+
+void *hwasan_malloc(uptr size, StackTrace *stack) {
+  return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false));
+}
+
+void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
+  return SetErrnoOnNull(HwasanCalloc(stack, nmemb, size));
+}
+
+void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack) {
+  if (!ptr)
+    return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false));
+  if (size == 0) {
+    HwasanDeallocate(stack, ptr);
+    return nullptr;
+  }
+  return SetErrnoOnNull(HwasanReallocate(stack, ptr, size, sizeof(u64)));
+}
+
+void *hwasan_reallocarray(void *ptr, uptr nmemb, uptr size, StackTrace *stack) {
+  if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
+    errno = errno_ENOMEM;
+    if (AllocatorMayReturnNull())
+      return nullptr;
+    ReportReallocArrayOverflow(nmemb, size, stack);
+  }
+  return hwasan_realloc(ptr, nmemb * size, stack);
+}
+
+void *hwasan_valloc(uptr size, StackTrace *stack) {
+  return SetErrnoOnNull(
+      HwasanAllocate(stack, size, GetPageSizeCached(), false));
+}
+
+void *hwasan_pvalloc(uptr size, StackTrace *stack) {
+  uptr PageSize = GetPageSizeCached();
+  if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
+    errno = errno_ENOMEM;
+    if (AllocatorMayReturnNull())
+      return nullptr;
+    ReportPvallocOverflow(size, stack);
+  }
+  // pvalloc(0) should allocate one page.
+  size = size ? RoundUpTo(size, PageSize) : PageSize;
+  return SetErrnoOnNull(HwasanAllocate(stack, size, PageSize, false));
+}
+
+void *hwasan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack) {
+  if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
+    errno = errno_EINVAL;
+    if (AllocatorMayReturnNull())
+      return nullptr;
+    ReportInvalidAlignedAllocAlignment(size, alignment, stack);
+  }
+  return SetErrnoOnNull(HwasanAllocate(stack, size, alignment, false));
+}
+
+void *hwasan_memalign(uptr alignment, uptr size, StackTrace *stack) {
+  if (UNLIKELY(!IsPowerOfTwo(alignment))) {
+    errno = errno_EINVAL;
+    if (AllocatorMayReturnNull())
+      return nullptr;
+    ReportInvalidAllocationAlignment(alignment, stack);
+  }
+  return SetErrnoOnNull(HwasanAllocate(stack, size, alignment, false));
+}
+
+int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
+                        StackTrace *stack) {
+  if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
+    if (AllocatorMayReturnNull())
+      return errno_EINVAL;
+    ReportInvalidPosixMemalignAlignment(alignment, stack);
+  }
+  void *ptr = HwasanAllocate(stack, size, alignment, false);
+  if (UNLIKELY(!ptr))
+    // OOM error is already taken care of by HwasanAllocate.
+    return errno_ENOMEM;
+  CHECK(IsAligned((uptr)ptr, alignment));
+  *(void **)UntagPtr(memptr) = ptr;
+  return 0;
+}
+
+void hwasan_free(void *ptr, StackTrace *stack) {
+  return HwasanDeallocate(stack, ptr);
+}
+
+}  // namespace __hwasan
+
+using namespace __hwasan;
+
+void __hwasan_enable_allocator_tagging() {
+  atomic_store_relaxed(&hwasan_allocator_tagging_enabled, 1);
+}
+
+void __hwasan_disable_allocator_tagging() {
+  atomic_store_relaxed(&hwasan_allocator_tagging_enabled, 0);
+}
+
+uptr __sanitizer_get_current_allocated_bytes() {
+  uptr stats[AllocatorStatCount];
+  allocator.GetStats(stats);
+  return stats[AllocatorStatAllocated];
+}
+
+uptr __sanitizer_get_heap_size() {
+  uptr stats[AllocatorStatCount];
+  allocator.GetStats(stats);
+  return stats[AllocatorStatMapped];
+}
+
+uptr __sanitizer_get_free_bytes() { return 1; }
+
+uptr __sanitizer_get_unmapped_bytes() { return 1; }
+
+uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
+
+int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; }
+
+uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); }
diff --git a/libsanitizer/hwasan/hwasan_allocator.h b/libsanitizer/hwasan/hwasan_allocator.h
new file mode 100644
index 00000000000..43670a6a3fb
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_allocator.h
@@ -0,0 +1,107 @@
+//===-- hwasan_allocator.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_ALLOCATOR_H
+#define HWASAN_ALLOCATOR_H
+
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_checks.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_ring_buffer.h"
+#include "hwasan_poisoning.h"
+
+#if !defined(__aarch64__) && !defined(__x86_64__)
+#error Unsupported platform
+#endif
+
+namespace __hwasan {
+
+struct Metadata {
+  u32 requested_size_low;
+  u32 requested_size_high : 31;
+  u32 right_aligned : 1;
+  u32 alloc_context_id;
+  u64 get_requested_size() {
+    return (static_cast<u64>(requested_size_high) << 32) + requested_size_low;
+  }
+  void set_requested_size(u64 size) {
+    requested_size_low = size & ((1ul << 32) - 1);
+    requested_size_high = size >> 32;
+  }
+};
+
+struct HwasanMapUnmapCallback {
+  void OnMap(uptr p, uptr size) const { UpdateMemoryUsage(); }
+  void OnUnmap(uptr p, uptr size) const {
+    // We are about to unmap a chunk of user memory.
+    // It can return as user-requested mmap() or another thread stack.
+    // Make it accessible with zero-tagged pointer.
+    TagMemory(p, size, 0);
+  }
+};
+
+static const uptr kMaxAllowedMallocSize = 1UL << 40;  // 1T
+
+struct AP64 {
+  static const uptr kSpaceBeg = ~0ULL;
+  static const uptr kSpaceSize = 0x2000000000ULL;
+  static const uptr kMetadataSize = sizeof(Metadata);
+  typedef __sanitizer::VeryDenseSizeClassMap SizeClassMap;
+  using AddressSpaceView = LocalAddressSpaceView;
+  typedef HwasanMapUnmapCallback MapUnmapCallback;
+  static const uptr kFlags = 0;
+};
+typedef SizeClassAllocator64<AP64> PrimaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator> Allocator;
+typedef Allocator::AllocatorCache AllocatorCache;
+
+void AllocatorSwallowThreadLocalCache(AllocatorCache *cache);
+
+class HwasanChunkView {
+ public:
+  HwasanChunkView() : block_(0), metadata_(nullptr) {}
+  HwasanChunkView(uptr block, Metadata *metadata)
+      : block_(block), metadata_(metadata) {}
+  bool IsAllocated() const;    // Checks if the memory is currently allocated
+  uptr Beg() const;            // First byte of user memory
+  uptr End() const;            // Last byte of user memory
+  uptr UsedSize() const;       // Size requested by the user
+  uptr ActualSize() const;     // Size allocated by the allocator.
+  u32 GetAllocStackId() const;
+  bool FromSmallHeap() const;
+ private:
+  uptr block_;
+  Metadata *const metadata_;
+};
+
+HwasanChunkView FindHeapChunkByAddress(uptr address);
+
+// Information about one (de)allocation that happened in the past.
+// These are recorded in a thread-local ring buffer.
+// TODO: this is currently 24 bytes (20 bytes + alignment).
+// Compress it to 16 bytes or extend it to be more useful.
+struct HeapAllocationRecord {
+  uptr tagged_addr;
+  u32  alloc_context_id;
+  u32  free_context_id;
+  u32  requested_size;
+};
+
+typedef RingBuffer<HeapAllocationRecord> HeapAllocationsRingBuffer;
+
+void GetAllocatorStats(AllocatorStatCounters s);
+
+} // namespace __hwasan
+
+#endif // HWASAN_ALLOCATOR_H
diff --git a/libsanitizer/hwasan/hwasan_checks.h b/libsanitizer/hwasan/hwasan_checks.h
new file mode 100644
index 00000000000..a8de0fef20f
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_checks.h
@@ -0,0 +1,124 @@
+//===-- hwasan_checks.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_CHECKS_H
+#define HWASAN_CHECKS_H
+
+#include "hwasan_mapping.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+namespace __hwasan {
+template <unsigned X>
+__attribute__((always_inline)) static void SigTrap(uptr p) {
+#if defined(__aarch64__)
+  (void)p;
+  // 0x900 is added to do not interfere with the kernel use of lower values of
+  // brk immediate.
+  register uptr x0 asm("x0") = p;
+  asm("brk %1\n\t" ::"r"(x0), "n"(0x900 + X));
+#elif defined(__x86_64__)
+  // INT3 + NOP DWORD ptr [EAX + X] to pass X to our signal handler, 5 bytes
+  // total. The pointer is passed via rdi.
+  // 0x40 is added as a safeguard, to help distinguish our trap from others and
+  // to avoid 0 offsets in the command (otherwise it'll be reduced to a
+  // different nop command, the three bytes one).
+  asm volatile(
+      "int3\n"
+      "nopl %c0(%%rax)\n" ::"n"(0x40 + X),
+      "D"(p));
+#else
+  // FIXME: not always sigill.
+  __builtin_trap();
+#endif
+  // __builtin_unreachable();
+}
+
+// Version with access size which is not power of 2
+template <unsigned X>
+__attribute__((always_inline)) static void SigTrap(uptr p, uptr size) {
+#if defined(__aarch64__)
+  register uptr x0 asm("x0") = p;
+  register uptr x1 asm("x1") = size;
+  asm("brk %2\n\t" ::"r"(x0), "r"(x1), "n"(0x900 + X));
+#elif defined(__x86_64__)
+  // Size is stored in rsi.
+  asm volatile(
+      "int3\n"
+      "nopl %c0(%%rax)\n" ::"n"(0x40 + X),
+      "D"(p), "S"(size));
+#else
+  __builtin_trap();
+#endif
+  // __builtin_unreachable();
+}
+
+__attribute__((always_inline, nodebug)) static bool PossiblyShortTagMatches(
+    tag_t mem_tag, uptr ptr, uptr sz) {
+  tag_t ptr_tag = GetTagFromPointer(ptr);
+  if (ptr_tag == mem_tag)
+    return true;
+  if (mem_tag >= kShadowAlignment)
+    return false;
+  if ((ptr & (kShadowAlignment - 1)) + sz > mem_tag)
+    return false;
+#ifndef __aarch64__
+  ptr = UntagAddr(ptr);
+#endif
+  return *(u8 *)(ptr | (kShadowAlignment - 1)) == ptr_tag;
+}
+
+enum class ErrorAction { Abort, Recover };
+enum class AccessType { Load, Store };
+
+template <ErrorAction EA, AccessType AT, unsigned LogSize>
+__attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) {
+  uptr ptr_raw = p & ~kAddressTagMask;
+  tag_t mem_tag = *(tag_t *)MemToShadow(ptr_raw);
+  if (UNLIKELY(!PossiblyShortTagMatches(mem_tag, p, 1 << LogSize))) {
+    SigTrap<0x20 * (EA == ErrorAction::Recover) +
+            0x10 * (AT == AccessType::Store) + LogSize>(p);
+    if (EA == ErrorAction::Abort)
+      __builtin_unreachable();
+  }
+}
+
+template <ErrorAction EA, AccessType AT>
+__attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p,
+                                                                      uptr sz) {
+  if (sz == 0)
+    return;
+  tag_t ptr_tag = GetTagFromPointer(p);
+  uptr ptr_raw = p & ~kAddressTagMask;
+  tag_t *shadow_first = (tag_t *)MemToShadow(ptr_raw);
+  tag_t *shadow_last = (tag_t *)MemToShadow(ptr_raw + sz);
+  for (tag_t *t = shadow_first; t < shadow_last; ++t)
+    if (UNLIKELY(ptr_tag != *t)) {
+      SigTrap<0x20 * (EA == ErrorAction::Recover) +
+              0x10 * (AT == AccessType::Store) + 0xf>(p, sz);
+      if (EA == ErrorAction::Abort)
+        __builtin_unreachable();
+    }
+  uptr end = p + sz;
+  uptr tail_sz = end & 0xf;
+  if (UNLIKELY(tail_sz != 0 &&
+               !PossiblyShortTagMatches(
+                   *shadow_last, end & ~(kShadowAlignment - 1), tail_sz))) {
+    SigTrap<0x20 * (EA == ErrorAction::Recover) +
+            0x10 * (AT == AccessType::Store) + 0xf>(p, sz);
+    if (EA == ErrorAction::Abort)
+      __builtin_unreachable();
+  }
+}
+
+}  // end namespace __hwasan
+
+#endif  // HWASAN_CHECKS_H
diff --git a/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp b/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp
new file mode 100644
index 00000000000..12730b29bae
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp
@@ -0,0 +1,126 @@
+//===-- hwasan_dynamic_shadow.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer. It reserves dynamic shadow memory
+/// region and handles ifunc resolver case, when necessary.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hwasan.h"
+#include "hwasan_dynamic_shadow.h"
+#include "hwasan_mapping.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_posix.h"
+
+#include <elf.h>
+#include <link.h>
+
+// The code in this file needs to run in an unrelocated binary. It should not
+// access any external symbol, including its own non-hidden globals.
+
+#if SANITIZER_ANDROID
+extern "C" {
+
+INTERFACE_ATTRIBUTE void __hwasan_shadow();
+decltype(__hwasan_shadow)* __hwasan_premap_shadow();
+
+}  // extern "C"
+
+namespace __hwasan {
+
+// Conservative upper limit.
+static uptr PremapShadowSize() {
+  return RoundUpTo(GetMaxVirtualAddress() >> kShadowScale,
+                   GetMmapGranularity());
+}
+
+static uptr PremapShadow() {
+  return MapDynamicShadow(PremapShadowSize(), kShadowScale,
+                          kShadowBaseAlignment, kHighMemEnd);
+}
+
+static bool IsPremapShadowAvailable() {
+  const uptr shadow = reinterpret_cast<uptr>(&__hwasan_shadow);
+  const uptr resolver = reinterpret_cast<uptr>(&__hwasan_premap_shadow);
+  // shadow == resolver is how Android KitKat and older handles ifunc.
+  // shadow == 0 just in case.
+  return shadow != 0 && shadow != resolver;
+}
+
+static uptr FindPremappedShadowStart(uptr shadow_size_bytes) {
+  const uptr granularity = GetMmapGranularity();
+  const uptr shadow_start = reinterpret_cast<uptr>(&__hwasan_shadow);
+  const uptr premap_shadow_size = PremapShadowSize();
+  const uptr shadow_size = RoundUpTo(shadow_size_bytes, granularity);
+
+  // We may have mapped too much. Release extra memory.
+  UnmapFromTo(shadow_start + shadow_size, shadow_start + premap_shadow_size);
+  return shadow_start;
+}
+
+}  // namespace __hwasan
+
+extern "C" {
+
+decltype(__hwasan_shadow)* __hwasan_premap_shadow() {
+  // The resolver might be called multiple times. Map the shadow just once.
+  static __sanitizer::uptr shadow = 0;
+  if (!shadow)
+    shadow = __hwasan::PremapShadow();
+  return reinterpret_cast<decltype(__hwasan_shadow)*>(shadow);
+}
+
+// __hwasan_shadow is a "function" that has the same address as the first byte
+// of the shadow mapping.
+INTERFACE_ATTRIBUTE __attribute__((ifunc("__hwasan_premap_shadow")))
+void __hwasan_shadow();
+
+extern __attribute((weak, visibility("hidden"))) ElfW(Rela) __rela_iplt_start[],
+    __rela_iplt_end[];
+
+}  // extern "C"
+
+namespace __hwasan {
+
+void InitShadowGOT() {
+  // Call the ifunc resolver for __hwasan_shadow and fill in its GOT entry. This
+  // needs to be done before other ifunc resolvers (which are handled by libc)
+  // because a resolver might read __hwasan_shadow.
+  typedef ElfW(Addr) (*ifunc_resolver_t)(void);
+  for (ElfW(Rela) *r = __rela_iplt_start; r != __rela_iplt_end; ++r) {
+    ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset);
+    ElfW(Addr) resolver = r->r_addend;
+    if (resolver == reinterpret_cast<ElfW(Addr)>(&__hwasan_premap_shadow)) {
+      *offset = reinterpret_cast<ifunc_resolver_t>(resolver)();
+      break;
+    }
+  }
+}
+
+uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
+  if (IsPremapShadowAvailable())
+    return FindPremappedShadowStart(shadow_size_bytes);
+  return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment,
+                          kHighMemEnd);
+}
+
+}  // namespace __hwasan
+#else
+namespace __hwasan {
+
+void InitShadowGOT() {}
+
+uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
+  return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment,
+                          kHighMemEnd);
+}
+
+}  // namespace __hwasan
+
+#endif  // SANITIZER_ANDROID
diff --git a/libsanitizer/hwasan/hwasan_dynamic_shadow.h b/libsanitizer/hwasan/hwasan_dynamic_shadow.h
new file mode 100644
index 00000000000..3c2e7c716a3
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_dynamic_shadow.h
@@ -0,0 +1,27 @@
+//===-- hwasan_dynamic_shadow.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer. It reserves dynamic shadow memory
+/// region.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_PREMAP_SHADOW_H
+#define HWASAN_PREMAP_SHADOW_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+namespace __hwasan {
+
+uptr FindDynamicShadowStart(uptr shadow_size_bytes);
+void InitShadowGOT();
+
+}  // namespace __hwasan
+
+#endif  // HWASAN_PREMAP_SHADOW_H
diff --git a/libsanitizer/hwasan/hwasan_exceptions.cpp b/libsanitizer/hwasan/hwasan_exceptions.cpp
new file mode 100644
index 00000000000..169e7876cb5
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_exceptions.cpp
@@ -0,0 +1,67 @@
+//===-- hwasan_exceptions.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer runtime.
+//===----------------------------------------------------------------------===//
+
+#include "hwasan_poisoning.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+#include <unwind.h>
+
+using namespace __hwasan;
+using namespace __sanitizer;
+
+typedef _Unwind_Reason_Code PersonalityFn(int version, _Unwind_Action actions,
+                                          uint64_t exception_class,
+                                          _Unwind_Exception* unwind_exception,
+                                          _Unwind_Context* context);
+
+// Pointers to the _Unwind_GetGR and _Unwind_GetCFA functions are passed in
+// instead of being called directly. This is to handle cases where the unwinder
+// is statically linked and the sanitizer runtime and the program are linked
+// against different unwinders. The _Unwind_Context data structure is opaque so
+// it may be incompatible between unwinders.
+typedef _Unwind_Word GetGRFn(_Unwind_Context* context, int index);
+typedef _Unwind_Word GetCFAFn(_Unwind_Context* context);
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE _Unwind_Reason_Code
+__hwasan_personality_wrapper(int version, _Unwind_Action actions,
+                             uint64_t exception_class,
+                             _Unwind_Exception* unwind_exception,
+                             _Unwind_Context* context,
+                             PersonalityFn* real_personality, GetGRFn* get_gr,
+                             GetCFAFn* get_cfa) {
+  _Unwind_Reason_Code rc;
+  if (real_personality)
+    rc = real_personality(version, actions, exception_class, unwind_exception,
+                          context);
+  else
+    rc = _URC_CONTINUE_UNWIND;
+
+  // We only untag frames without a landing pad because landing pads are
+  // responsible for untagging the stack themselves if they resume.
+  //
+  // Here we assume that the frame record appears after any locals. This is not
+  // required by AAPCS but is a requirement for HWASAN instrumented functions.
+  if ((actions & _UA_CLEANUP_PHASE) && rc == _URC_CONTINUE_UNWIND) {
+#if defined(__x86_64__)
+    uptr fp = get_gr(context, 6); // rbp
+#elif defined(__aarch64__)
+    uptr fp = get_gr(context, 29); // x29
+#else
+#error Unsupported architecture
+#endif
+    uptr sp = get_cfa(context);
+    TagMemory(sp, fp - sp, 0);
+  }
+
+  return rc;
+}
diff --git a/libsanitizer/hwasan/hwasan_flags.h b/libsanitizer/hwasan/hwasan_flags.h
new file mode 100644
index 00000000000..0a6998f675d
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_flags.h
@@ -0,0 +1,29 @@
+//===-- hwasan_flags.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+#ifndef HWASAN_FLAGS_H
+#define HWASAN_FLAGS_H
+
+namespace __hwasan {
+
+struct Flags {
+#define HWASAN_FLAG(Type, Name, DefaultValue, Description) Type Name;
+#include "hwasan_flags.inc"
+#undef HWASAN_FLAG
+
+  void SetDefaults();
+};
+
+Flags *flags();
+
+}  // namespace __hwasan
+
+#endif  // HWASAN_FLAGS_H
diff --git a/libsanitizer/hwasan/hwasan_flags.inc b/libsanitizer/hwasan/hwasan_flags.inc
new file mode 100644
index 00000000000..8e431d9c4ff
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_flags.inc
@@ -0,0 +1,74 @@
+//===-- hwasan_flags.inc ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Hwasan runtime flags.
+//
+//===----------------------------------------------------------------------===//
+#ifndef HWASAN_FLAG
+# error "Define HWASAN_FLAG prior to including this file!"
+#endif
+
+// HWASAN_FLAG(Type, Name, DefaultValue, Description)
+// See COMMON_FLAG in sanitizer_flags.inc for more details.
+
+HWASAN_FLAG(bool, verbose_threads, false,
+            "inform on thread creation/destruction")
+HWASAN_FLAG(bool, tag_in_malloc, true, "")
+HWASAN_FLAG(bool, tag_in_free, true, "")
+HWASAN_FLAG(bool, print_stats, false, "")
+HWASAN_FLAG(bool, halt_on_error, true, "")
+HWASAN_FLAG(bool, atexit, false, "")
+
+// Test only flag to disable malloc/realloc/free memory tagging on startup.
+// Tagging can be reenabled with __hwasan_enable_allocator_tagging().
+HWASAN_FLAG(bool, disable_allocator_tagging, false, "")
+
+// If false, use simple increment of a thread local counter to generate new
+// tags.
+HWASAN_FLAG(bool, random_tags, true, "")
+
+HWASAN_FLAG(
+    int, max_malloc_fill_size, 0,
+    "HWASan allocator flag. max_malloc_fill_size is the maximal amount of "
+    "bytes that will be filled with malloc_fill_byte on malloc.")
+
+HWASAN_FLAG(bool, free_checks_tail_magic, 1,
+    "If set, free() will check the magic values "
+    "to the right of the allocated object "
+    "if the allocation size is not a divident of the granule size")
+HWASAN_FLAG(
+    int, max_free_fill_size, 0,
+    "HWASan allocator flag. max_free_fill_size is the maximal amount of "
+    "bytes that will be filled with free_fill_byte during free.")
+HWASAN_FLAG(int, malloc_fill_byte, 0xbe,
+          "Value used to fill the newly allocated memory.")
+HWASAN_FLAG(int, free_fill_byte, 0x55,
+          "Value used to fill deallocated memory.")
+HWASAN_FLAG(int, heap_history_size, 1023,
+          "The number of heap (de)allocations remembered per thread. "
+          "Affects the quality of heap-related reports, but not the ability "
+          "to find bugs.")
+HWASAN_FLAG(bool, export_memory_stats, true,
+            "Export up-to-date memory stats through /proc")
+HWASAN_FLAG(int, stack_history_size, 1024,
+            "The number of stack frames remembered per thread. "
+            "Affects the quality of stack-related reports, but not the ability "
+            "to find bugs.")
+
+// Malloc / free bisection. Only tag malloc and free calls when a hash of
+// allocation size and stack trace is between malloc_bisect_left and
+// malloc_bisect_right (both inclusive). [0, 0] range is special and disables
+// bisection (i.e. everything is tagged). Once the range is narrowed down
+// enough, use malloc_bisect_dump to see interesting allocations.
+HWASAN_FLAG(uptr, malloc_bisect_left, 0,
+            "Left bound of malloc bisection, inclusive.")
+HWASAN_FLAG(uptr, malloc_bisect_right, 0,
+            "Right bound of malloc bisection, inclusive.")
+HWASAN_FLAG(bool, malloc_bisect_dump, false,
+            "Print all allocations within [malloc_bisect_left, "
+            "malloc_bisect_right] range ")
diff --git a/libsanitizer/hwasan/hwasan_globals.cpp b/libsanitizer/hwasan/hwasan_globals.cpp
new file mode 100644
index 00000000000..d71bcd792e1
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_globals.cpp
@@ -0,0 +1,91 @@
+//===-- hwasan_globals.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer globals-specific runtime.
+//===----------------------------------------------------------------------===//
+
+#include "hwasan_globals.h"
+
+namespace __hwasan {
+
+enum { NT_LLVM_HWASAN_GLOBALS = 3 };
+struct hwasan_global_note {
+  s32 begin_relptr;
+  s32 end_relptr;
+};
+
+// Check that the given library meets the code model requirements for tagged
+// globals. These properties are not checked at link time so they need to be
+// checked at runtime.
+static void CheckCodeModel(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                           ElfW(Half) phnum) {
+  ElfW(Addr) min_addr = -1ull, max_addr = 0;
+  for (unsigned i = 0; i != phnum; ++i) {
+    if (phdr[i].p_type != PT_LOAD)
+      continue;
+    ElfW(Addr) lo = base + phdr[i].p_vaddr, hi = lo + phdr[i].p_memsz;
+    if (min_addr > lo)
+      min_addr = lo;
+    if (max_addr < hi)
+      max_addr = hi;
+  }
+
+  if (max_addr - min_addr > 1ull << 32) {
+    Report("FATAL: HWAddressSanitizer: library size exceeds 2^32\n");
+    Die();
+  }
+  if (max_addr > 1ull << 48) {
+    Report("FATAL: HWAddressSanitizer: library loaded above address 2^48\n");
+    Die();
+  }
+}
+
+ArrayRef<const hwasan_global> HwasanGlobalsFor(ElfW(Addr) base,
+                                               const ElfW(Phdr) * phdr,
+                                               ElfW(Half) phnum) {
+  // Read the phdrs from this DSO.
+  for (unsigned i = 0; i != phnum; ++i) {
+    if (phdr[i].p_type != PT_NOTE)
+      continue;
+
+    const char *note = reinterpret_cast<const char *>(base + phdr[i].p_vaddr);
+    const char *nend = note + phdr[i].p_memsz;
+
+    // Traverse all the notes until we find a HWASan note.
+    while (note < nend) {
+      auto *nhdr = reinterpret_cast<const ElfW(Nhdr) *>(note);
+      const char *name = note + sizeof(ElfW(Nhdr));
+      const char *desc = name + RoundUpTo(nhdr->n_namesz, 4);
+
+      // Discard non-HWASan-Globals notes.
+      if (nhdr->n_type != NT_LLVM_HWASAN_GLOBALS ||
+          internal_strcmp(name, "LLVM") != 0) {
+        note = desc + RoundUpTo(nhdr->n_descsz, 4);
+        continue;
+      }
+
+      // Only libraries with instrumented globals need to be checked against the
+      // code model since they use relocations that aren't checked at link time.
+      CheckCodeModel(base, phdr, phnum);
+
+      auto *global_note = reinterpret_cast<const hwasan_global_note *>(desc);
+      auto *globals_begin = reinterpret_cast<const hwasan_global *>(
+          note + global_note->begin_relptr);
+      auto *globals_end = reinterpret_cast<const hwasan_global *>(
+          note + global_note->end_relptr);
+
+      return {globals_begin, globals_end};
+    }
+  }
+
+  return {};
+}
+
+}  // namespace __hwasan
diff --git a/libsanitizer/hwasan/hwasan_globals.h b/libsanitizer/hwasan/hwasan_globals.h
new file mode 100644
index 00000000000..fd7adf7a058
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_globals.h
@@ -0,0 +1,49 @@
+//===-- hwasan_globals.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Private Hwasan header.
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_GLOBALS_H
+#define HWASAN_GLOBALS_H
+
+#include <link.h>
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+namespace __hwasan {
+// This object should only ever be casted over the global (i.e. not constructed)
+// in the ELF PT_NOTE in order for `addr()` to work correctly.
+struct hwasan_global {
+  // The size of this global variable. Note that the size in the descriptor is
+  // max 1 << 24. Larger globals have multiple descriptors.
+  uptr size() const { return info & 0xffffff; }
+  // The fully-relocated address of this global.
+  uptr addr() const { return reinterpret_cast<uintptr_t>(this) + gv_relptr; }
+  // The static tag of this global.
+  u8 tag() const { return info >> 24; };
+
+  // The relative address between the start of the descriptor for the HWASan
+  // global (in the PT_NOTE), and the fully relocated address of the global.
+  s32 gv_relptr;
+  u32 info;
+};
+
+// Walk through the specific DSO (as specified by the base, phdr, and phnum),
+// and return the range of the [beginning, end) of the HWASan globals descriptor
+// array.
+ArrayRef<const hwasan_global> HwasanGlobalsFor(ElfW(Addr) base,
+                                               const ElfW(Phdr) * phdr,
+                                               ElfW(Half) phnum);
+
+}  // namespace __hwasan
+
+#endif  // HWASAN_GLOBALS_H
diff --git a/libsanitizer/hwasan/hwasan_interceptors.cpp b/libsanitizer/hwasan/hwasan_interceptors.cpp
new file mode 100644
index 00000000000..44e569ee6d7
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_interceptors.cpp
@@ -0,0 +1,349 @@
+//===-- hwasan_interceptors.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Interceptors for standard library functions.
+//
+// FIXME: move as many interceptors as possible into
+// sanitizer_common/sanitizer_common_interceptors.h
+//===----------------------------------------------------------------------===//
+
+#include "interception/interception.h"
+#include "hwasan.h"
+#include "hwasan_allocator.h"
+#include "hwasan_mapping.h"
+#include "hwasan_thread.h"
+#include "hwasan_poisoning.h"
+#include "hwasan_report.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_allocator_internal.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_linux.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
+
+#include <stdarg.h>
+// ACHTUNG! No other system header includes in this file.
+// Ideally, we should get rid of stdarg.h as well.
+
+using namespace __hwasan;
+
+using __sanitizer::memory_order;
+using __sanitizer::atomic_load;
+using __sanitizer::atomic_store;
+using __sanitizer::atomic_uintptr_t;
+
+static uptr allocated_for_dlsym;
+static const uptr kDlsymAllocPoolSize = 1024;
+static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
+
+static bool IsInDlsymAllocPool(const void *ptr) {
+  uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
+  return off < sizeof(alloc_memory_for_dlsym);
+}
+
+static void *AllocateFromLocalPool(uptr size_in_bytes) {
+  uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
+  void *mem = (void *)&alloc_memory_for_dlsym[allocated_for_dlsym];
+  allocated_for_dlsym += size_in_words;
+  CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
+  return mem;
+}
+
+#define ENSURE_HWASAN_INITED() do { \
+  CHECK(!hwasan_init_is_running); \
+  if (!hwasan_inited) { \
+    __hwasan_init(); \
+  } \
+} while (0)
+
+
+int __sanitizer_posix_memalign(void **memptr, uptr alignment, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  CHECK_NE(memptr, 0);
+  int res = hwasan_posix_memalign(memptr, alignment, size, &stack);
+  return res;
+}
+
+void * __sanitizer_memalign(uptr alignment, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  return hwasan_memalign(alignment, size, &stack);
+}
+
+void * __sanitizer_aligned_alloc(uptr alignment, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  return hwasan_aligned_alloc(alignment, size, &stack);
+}
+
+void * __sanitizer___libc_memalign(uptr alignment, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  void *ptr = hwasan_memalign(alignment, size, &stack);
+  if (ptr)
+    DTLS_on_libc_memalign(ptr, size);
+  return ptr;
+}
+
+void * __sanitizer_valloc(uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  return hwasan_valloc(size, &stack);
+}
+
+void * __sanitizer_pvalloc(uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  return hwasan_pvalloc(size, &stack);
+}
+
+void __sanitizer_free(void *ptr) {
+  GET_MALLOC_STACK_TRACE;
+  if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
+  hwasan_free(ptr, &stack);
+}
+
+void __sanitizer_cfree(void *ptr) {
+  GET_MALLOC_STACK_TRACE;
+  if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
+  hwasan_free(ptr, &stack);
+}
+
+uptr __sanitizer_malloc_usable_size(const void *ptr) {
+  return __sanitizer_get_allocated_size(ptr);
+}
+
+struct __sanitizer_struct_mallinfo __sanitizer_mallinfo() {
+  __sanitizer_struct_mallinfo sret;
+  internal_memset(&sret, 0, sizeof(sret));
+  return sret;
+}
+
+int __sanitizer_mallopt(int cmd, int value) {
+  return 0;
+}
+
+void __sanitizer_malloc_stats(void) {
+  // FIXME: implement, but don't call REAL(malloc_stats)!
+}
+
+void * __sanitizer_calloc(uptr nmemb, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  if (UNLIKELY(!hwasan_inited))
+    // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
+    return AllocateFromLocalPool(nmemb * size);
+  return hwasan_calloc(nmemb, size, &stack);
+}
+
+void * __sanitizer_realloc(void *ptr, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
+    uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
+    uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
+    void *new_ptr;
+    if (UNLIKELY(!hwasan_inited)) {
+      new_ptr = AllocateFromLocalPool(copy_size);
+    } else {
+      copy_size = size;
+      new_ptr = hwasan_malloc(copy_size, &stack);
+    }
+    internal_memcpy(new_ptr, ptr, copy_size);
+    return new_ptr;
+  }
+  return hwasan_realloc(ptr, size, &stack);
+}
+
+void * __sanitizer_reallocarray(void *ptr, uptr nmemb, uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  return hwasan_reallocarray(ptr, nmemb, size, &stack);
+}
+
+void * __sanitizer_malloc(uptr size) {
+  GET_MALLOC_STACK_TRACE;
+  if (UNLIKELY(!hwasan_init_is_running))
+    ENSURE_HWASAN_INITED();
+  if (UNLIKELY(!hwasan_inited))
+    // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
+    return AllocateFromLocalPool(size);
+  return hwasan_malloc(size, &stack);
+}
+
+#if HWASAN_WITH_INTERCEPTORS
+#define INTERCEPTOR_ALIAS(RET, FN, ARGS...)                                  \
+  extern "C" SANITIZER_INTERFACE_ATTRIBUTE RET WRAP(FN)(ARGS)                \
+      ALIAS("__sanitizer_" #FN);                                             \
+  extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE RET FN(  \
+      ARGS) ALIAS("__sanitizer_" #FN)
+
+INTERCEPTOR_ALIAS(int, posix_memalign, void **memptr, SIZE_T alignment,
+                  SIZE_T size);
+INTERCEPTOR_ALIAS(void *, aligned_alloc, SIZE_T alignment, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, __libc_memalign, SIZE_T alignment, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, valloc, SIZE_T size);
+INTERCEPTOR_ALIAS(void, free, void *ptr);
+INTERCEPTOR_ALIAS(uptr, malloc_usable_size, const void *ptr);
+INTERCEPTOR_ALIAS(void *, calloc, SIZE_T nmemb, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, realloc, void *ptr, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, reallocarray, void *ptr, SIZE_T nmemb, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, malloc, SIZE_T size);
+
+#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
+INTERCEPTOR_ALIAS(void *, memalign, SIZE_T alignment, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, pvalloc, SIZE_T size);
+INTERCEPTOR_ALIAS(void, cfree, void *ptr);
+INTERCEPTOR_ALIAS(__sanitizer_struct_mallinfo, mallinfo);
+INTERCEPTOR_ALIAS(int, mallopt, int cmd, int value);
+INTERCEPTOR_ALIAS(void, malloc_stats, void);
+#endif
+
+struct ThreadStartArg {
+  thread_callback_t callback;
+  void *param;
+};
+
+static void *HwasanThreadStartFunc(void *arg) {
+  __hwasan_thread_enter();
+  ThreadStartArg A = *reinterpret_cast<ThreadStartArg*>(arg);
+  UnmapOrDie(arg, GetPageSizeCached());
+  return A.callback(A.param);
+}
+
+INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
+            void * param) {
+  ScopedTaggingDisabler disabler;
+  ThreadStartArg *A = reinterpret_cast<ThreadStartArg *> (MmapOrDie(
+      GetPageSizeCached(), "pthread_create"));
+  *A = {callback, param};
+  int res = REAL(pthread_create)(UntagPtr(th), UntagPtr(attr),
+                                 &HwasanThreadStartFunc, A);
+  return res;
+}
+
+DEFINE_REAL(int, vfork)
+DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
+#endif // HWASAN_WITH_INTERCEPTORS
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+// Get and/or change the set of blocked signals.
+extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set,
+                           __hw_sigset_t *__restrict __oset);
+#define SIG_BLOCK 0
+#define SIG_SETMASK 2
+extern "C" int __sigjmp_save(__hw_sigjmp_buf env, int savemask) {
+  env[0].__mask_was_saved =
+      (savemask && sigprocmask(SIG_BLOCK, (__hw_sigset_t *)0,
+                               &env[0].__saved_mask) == 0);
+  return 0;
+}
+
+static void __attribute__((always_inline))
+InternalLongjmp(__hw_register_buf env, int retval) {
+  // Clear all memory tags on the stack between here and where we're going.
+  unsigned long long stack_pointer = env[13];
+  // The stack pointer should never be tagged, so we don't need to clear the
+  // tag for this function call.
+  __hwasan_handle_longjmp((void *)stack_pointer);
+
+  // Run code for handling a longjmp.
+  // Need to use a register that isn't going to be loaded from the environment
+  // buffer -- hence why we need to specify the register to use.
+  // Must implement this ourselves, since we don't know the order of registers
+  // in different libc implementations and many implementations mangle the
+  // stack pointer so we can't use it without knowing the demangling scheme.
+  register long int retval_tmp asm("x1") = retval;
+  register void *env_address asm("x0") = &env[0];
+  asm volatile("ldp	x19, x20, [%0, #0<<3];"
+               "ldp	x21, x22, [%0, #2<<3];"
+               "ldp	x23, x24, [%0, #4<<3];"
+               "ldp	x25, x26, [%0, #6<<3];"
+               "ldp	x27, x28, [%0, #8<<3];"
+               "ldp	x29, x30, [%0, #10<<3];"
+               "ldp	 d8,  d9, [%0, #14<<3];"
+               "ldp	d10, d11, [%0, #16<<3];"
+               "ldp	d12, d13, [%0, #18<<3];"
+               "ldp	d14, d15, [%0, #20<<3];"
+               "ldr	x5, [%0, #13<<3];"
+               "mov	sp, x5;"
+               // Return the value requested to return through arguments.
+               // This should be in x1 given what we requested above.
+               "cmp	%1, #0;"
+               "mov	x0, #1;"
+               "csel	x0, %1, x0, ne;"
+               "br	x30;"
+               : "+r"(env_address)
+               : "r"(retval_tmp));
+}
+
+INTERCEPTOR(void, siglongjmp, __hw_sigjmp_buf env, int val) {
+  if (env[0].__mask_was_saved)
+    // Restore the saved signal mask.
+    (void)sigprocmask(SIG_SETMASK, &env[0].__saved_mask,
+                      (__hw_sigset_t *)0);
+  InternalLongjmp(env[0].__jmpbuf, val);
+}
+
+// Required since glibc libpthread calls __libc_longjmp on pthread_exit, and
+// _setjmp on start_thread.  Hence we have to intercept the longjmp on
+// pthread_exit so the __hw_jmp_buf order matches.
+INTERCEPTOR(void, __libc_longjmp, __hw_jmp_buf env, int val) {
+  InternalLongjmp(env[0].__jmpbuf, val);
+}
+
+INTERCEPTOR(void, longjmp, __hw_jmp_buf env, int val) {
+  InternalLongjmp(env[0].__jmpbuf, val);
+}
+#undef SIG_BLOCK
+#undef SIG_SETMASK
+
+#endif // HWASAN_WITH_INTERCEPTORS && __aarch64__
+
+static void BeforeFork() {
+  StackDepotLockAll();
+}
+
+static void AfterFork() {
+  StackDepotUnlockAll();
+}
+
+INTERCEPTOR(int, fork, void) {
+  ENSURE_HWASAN_INITED();
+  BeforeFork();
+  int pid = REAL(fork)();
+  AfterFork();
+  return pid;
+}
+
+namespace __hwasan {
+
+int OnExit() {
+  // FIXME: ask frontend whether we need to return failure.
+  return 0;
+}
+
+} // namespace __hwasan
+
+namespace __hwasan {
+
+void InitializeInterceptors() {
+  static int inited = 0;
+  CHECK_EQ(inited, 0);
+
+  INTERCEPT_FUNCTION(fork);
+
+#if HWASAN_WITH_INTERCEPTORS
+#if defined(__linux__)
+  INTERCEPT_FUNCTION(vfork);
+#endif  // __linux__
+  INTERCEPT_FUNCTION(pthread_create);
+#endif
+
+  inited = 1;
+}
+} // namespace __hwasan
diff --git a/libsanitizer/hwasan/hwasan_interceptors_vfork.S b/libsanitizer/hwasan/hwasan_interceptors_vfork.S
new file mode 100644
index 00000000000..23d565936d8
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_interceptors_vfork.S
@@ -0,0 +1,11 @@
+#include "sanitizer_common/sanitizer_asm.h"
+
+#if defined(__linux__) && HWASAN_WITH_INTERCEPTORS
+#define COMMON_INTERCEPTOR_SPILL_AREA __hwasan_extra_spill_area
+#define COMMON_INTERCEPTOR_HANDLE_VFORK __hwasan_handle_vfork
+#include "sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S"
+#include "sanitizer_common/sanitizer_common_interceptors_vfork_riscv64.inc.S"
+#include "sanitizer_common/sanitizer_common_interceptors_vfork_x86_64.inc.S"
+#endif
+
+NO_EXEC_STACK_DIRECTIVE
diff --git a/libsanitizer/hwasan/hwasan_interface_internal.h b/libsanitizer/hwasan/hwasan_interface_internal.h
new file mode 100644
index 00000000000..aedda317497
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_interface_internal.h
@@ -0,0 +1,227 @@
+//===-- hwasan_interface_internal.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Private Hwasan interface header.
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_INTERFACE_INTERNAL_H
+#define HWASAN_INTERFACE_INTERNAL_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include <link.h>
+
+extern "C" {
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_init_static();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_init();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                             ElfW(Half) phnum);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+                               ElfW(Half) phnum);
+
+using __sanitizer::uptr;
+using __sanitizer::sptr;
+using __sanitizer::uu64;
+using __sanitizer::uu32;
+using __sanitizer::uu16;
+using __sanitizer::u64;
+using __sanitizer::u32;
+using __sanitizer::u16;
+using __sanitizer::u8;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_init_frames(uptr, uptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+extern uptr __hwasan_shadow_memory_dynamic_address;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_loadN(uptr, uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load1(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load2(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load4(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load8(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load16(uptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_loadN_noabort(uptr, uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load1_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load2_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load4_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load8_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_load16_noabort(uptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_storeN(uptr, uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store1(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store2(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store4(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store8(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store16(uptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_storeN_noabort(uptr, uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store1_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store2_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store4_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store8_noabort(uptr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_store16_noabort(uptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_tag_memory(uptr p, u8 tag, uptr sz);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __hwasan_tag_pointer(uptr p, u8 tag);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_tag_mismatch(uptr addr, u8 ts);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_tag_mismatch4(uptr addr, uptr access_info, uptr *registers_frame,
+                            size_t outsize);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+u8 __hwasan_generate_tag();
+
+// Returns the offset of the first tag mismatch or -1 if the whole range is
+// good.
+SANITIZER_INTERFACE_ATTRIBUTE
+sptr __hwasan_test_shadow(const void *x, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+/* OPTIONAL */ const char* __hwasan_default_options();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_print_shadow(const void *x, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_handle_longjmp(const void *sp_dst);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_handle_vfork(const void *sp_dst);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+u16 __sanitizer_unaligned_load16(const uu16 *p);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+u32 __sanitizer_unaligned_load32(const uu32 *p);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+u64 __sanitizer_unaligned_load64(const uu64 *p);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store16(uu16 *p, u16 x);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store32(uu32 *p, u32 x);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store64(uu64 *p, u64 x);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_enable_allocator_tagging();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_disable_allocator_tagging();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_thread_enter();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_thread_exit();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_print_memory_usage();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_posix_memalign(void **memptr, uptr alignment, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_memalign(uptr alignment, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_aligned_alloc(uptr alignment, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer___libc_memalign(uptr alignment, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_valloc(uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_pvalloc(uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_free(void *ptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cfree(void *ptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_malloc_usable_size(const void *ptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+__hwasan::__sanitizer_struct_mallinfo __sanitizer_mallinfo();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_mallopt(int cmd, int value);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_malloc_stats(void);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_calloc(uptr nmemb, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_realloc(void *ptr, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_reallocarray(void *ptr, uptr nmemb, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_malloc(uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__hwasan_memcpy(void *dst, const void *src, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__hwasan_memset(void *s, int c, uptr n);
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__hwasan_memmove(void *dest, const void *src, uptr n);
+}  // extern "C"
+
+#endif  // HWASAN_INTERFACE_INTERNAL_H
diff --git a/libsanitizer/hwasan/hwasan_linux.cpp b/libsanitizer/hwasan/hwasan_linux.cpp
new file mode 100644
index 00000000000..e99926d355c
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_linux.cpp
@@ -0,0 +1,455 @@
+//===-- hwasan_linux.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer and contains Linux-, NetBSD- and
+/// FreeBSD-specific code.
+///
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
+
+#include "hwasan.h"
+#include "hwasan_dynamic_shadow.h"
+#include "hwasan_interface_internal.h"
+#include "hwasan_mapping.h"
+#include "hwasan_report.h"
+#include "hwasan_thread.h"
+#include "hwasan_thread_list.h"
+
+#include <dlfcn.h>
+#include <elf.h>
+#include <link.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <unwind.h>
+#include <sys/prctl.h>
+#include <errno.h>
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+
+// Configurations of HWASAN_WITH_INTERCEPTORS and SANITIZER_ANDROID.
+//
+// HWASAN_WITH_INTERCEPTORS=OFF, SANITIZER_ANDROID=OFF
+//   Not currently tested.
+// HWASAN_WITH_INTERCEPTORS=OFF, SANITIZER_ANDROID=ON
+//   Integration tests downstream exist.
+// HWASAN_WITH_INTERCEPTORS=ON, SANITIZER_ANDROID=OFF
+//    Tested with check-hwasan on x86_64-linux.
+// HWASAN_WITH_INTERCEPTORS=ON, SANITIZER_ANDROID=ON
+//    Tested with check-hwasan on aarch64-linux-android.
+#if !SANITIZER_ANDROID
+SANITIZER_INTERFACE_ATTRIBUTE
+THREADLOCAL uptr __hwasan_tls;
+#endif
+
+namespace __hwasan {
+
+// With the zero shadow base we can not actually map pages starting from 0.
+// This constant is somewhat arbitrary.
+constexpr uptr kZeroBaseShadowStart = 0;
+constexpr uptr kZeroBaseMaxShadowStart = 1 << 18;
+
+static void ProtectGap(uptr addr, uptr size) {
+  __sanitizer::ProtectGap(addr, size, kZeroBaseShadowStart,
+                          kZeroBaseMaxShadowStart);
+}
+
+uptr kLowMemStart;
+uptr kLowMemEnd;
+uptr kLowShadowEnd;
+uptr kLowShadowStart;
+uptr kHighShadowStart;
+uptr kHighShadowEnd;
+uptr kHighMemStart;
+uptr kHighMemEnd;
+
+static void PrintRange(uptr start, uptr end, const char *name) {
+  Printf("|| [%p, %p] || %.*s ||\n", (void *)start, (void *)end, 10, name);
+}
+
+static void PrintAddressSpaceLayout() {
+  PrintRange(kHighMemStart, kHighMemEnd, "HighMem");
+  if (kHighShadowEnd + 1 < kHighMemStart)
+    PrintRange(kHighShadowEnd + 1, kHighMemStart - 1, "ShadowGap");
+  else
+    CHECK_EQ(kHighShadowEnd + 1, kHighMemStart);
+  PrintRange(kHighShadowStart, kHighShadowEnd, "HighShadow");
+  if (kLowShadowEnd + 1 < kHighShadowStart)
+    PrintRange(kLowShadowEnd + 1, kHighShadowStart - 1, "ShadowGap");
+  else
+    CHECK_EQ(kLowMemEnd + 1, kHighShadowStart);
+  PrintRange(kLowShadowStart, kLowShadowEnd, "LowShadow");
+  if (kLowMemEnd + 1 < kLowShadowStart)
+    PrintRange(kLowMemEnd + 1, kLowShadowStart - 1, "ShadowGap");
+  else
+    CHECK_EQ(kLowMemEnd + 1, kLowShadowStart);
+  PrintRange(kLowMemStart, kLowMemEnd, "LowMem");
+  CHECK_EQ(0, kLowMemStart);
+}
+
+static uptr GetHighMemEnd() {
+  // HighMem covers the upper part of the address space.
+  uptr max_address = GetMaxUserVirtualAddress();
+  // Adjust max address to make sure that kHighMemEnd and kHighMemStart are
+  // properly aligned:
+  max_address |= (GetMmapGranularity() << kShadowScale) - 1;
+  return max_address;
+}
+
+static void InitializeShadowBaseAddress(uptr shadow_size_bytes) {
+  __hwasan_shadow_memory_dynamic_address =
+      FindDynamicShadowStart(shadow_size_bytes);
+}
+
+void InitPrctl() {
+#define PR_SET_TAGGED_ADDR_CTRL 55
+#define PR_GET_TAGGED_ADDR_CTRL 56
+#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
+  // Check we're running on a kernel that can use the tagged address ABI.
+  if (internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0) == (uptr)-1 &&
+      errno == EINVAL) {
+#if SANITIZER_ANDROID
+    // Some older Android kernels have the tagged pointer ABI on
+    // unconditionally, and hence don't have the tagged-addr prctl while still
+    // allow the ABI.
+    // If targeting Android and the prctl is not around we assume this is the
+    // case.
+    return;
+#else
+    Printf(
+        "FATAL: "
+        "HWAddressSanitizer requires a kernel with tagged address ABI.\n");
+    Die();
+#endif
+  }
+
+  // Turn on the tagged address ABI.
+  if (internal_prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) ==
+          (uptr)-1 ||
+      !internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0)) {
+    Printf(
+        "FATAL: HWAddressSanitizer failed to enable tagged address syscall "
+        "ABI.\nSuggest check `sysctl abi.tagged_addr_disabled` "
+        "configuration.\n");
+    Die();
+  }
+#undef PR_SET_TAGGED_ADDR_CTRL
+#undef PR_GET_TAGGED_ADDR_CTRL
+#undef PR_TAGGED_ADDR_ENABLE
+}
+
+bool InitShadow() {
+  // Define the entire memory range.
+  kHighMemEnd = GetHighMemEnd();
+
+  // Determine shadow memory base offset.
+  InitializeShadowBaseAddress(MemToShadowSize(kHighMemEnd));
+
+  // Place the low memory first.
+  kLowMemEnd = __hwasan_shadow_memory_dynamic_address - 1;
+  kLowMemStart = 0;
+
+  // Define the low shadow based on the already placed low memory.
+  kLowShadowEnd = MemToShadow(kLowMemEnd);
+  kLowShadowStart = __hwasan_shadow_memory_dynamic_address;
+
+  // High shadow takes whatever memory is left up there (making sure it is not
+  // interfering with low memory in the fixed case).
+  kHighShadowEnd = MemToShadow(kHighMemEnd);
+  kHighShadowStart = Max(kLowMemEnd, MemToShadow(kHighShadowEnd)) + 1;
+
+  // High memory starts where allocated shadow allows.
+  kHighMemStart = ShadowToMem(kHighShadowStart);
+
+  // Check the sanity of the defined memory ranges (there might be gaps).
+  CHECK_EQ(kHighMemStart % GetMmapGranularity(), 0);
+  CHECK_GT(kHighMemStart, kHighShadowEnd);
+  CHECK_GT(kHighShadowEnd, kHighShadowStart);
+  CHECK_GT(kHighShadowStart, kLowMemEnd);
+  CHECK_GT(kLowMemEnd, kLowMemStart);
+  CHECK_GT(kLowShadowEnd, kLowShadowStart);
+  CHECK_GT(kLowShadowStart, kLowMemEnd);
+
+  if (Verbosity())
+    PrintAddressSpaceLayout();
+
+  // Reserve shadow memory.
+  ReserveShadowMemoryRange(kLowShadowStart, kLowShadowEnd, "low shadow");
+  ReserveShadowMemoryRange(kHighShadowStart, kHighShadowEnd, "high shadow");
+
+  // Protect all the gaps.
+  ProtectGap(0, Min(kLowMemStart, kLowShadowStart));
+  if (kLowMemEnd + 1 < kLowShadowStart)
+    ProtectGap(kLowMemEnd + 1, kLowShadowStart - kLowMemEnd - 1);
+  if (kLowShadowEnd + 1 < kHighShadowStart)
+    ProtectGap(kLowShadowEnd + 1, kHighShadowStart - kLowShadowEnd - 1);
+  if (kHighShadowEnd + 1 < kHighMemStart)
+    ProtectGap(kHighShadowEnd + 1, kHighMemStart - kHighShadowEnd - 1);
+
+  return true;
+}
+
+void InitThreads() {
+  CHECK(__hwasan_shadow_memory_dynamic_address);
+  uptr guard_page_size = GetMmapGranularity();
+  uptr thread_space_start =
+      __hwasan_shadow_memory_dynamic_address - (1ULL << kShadowBaseAlignment);
+  uptr thread_space_end =
+      __hwasan_shadow_memory_dynamic_address - guard_page_size;
+  ReserveShadowMemoryRange(thread_space_start, thread_space_end - 1,
+                           "hwasan threads", /*madvise_shadow*/ false);
+  ProtectGap(thread_space_end,
+             __hwasan_shadow_memory_dynamic_address - thread_space_end);
+  InitThreadList(thread_space_start, thread_space_end - thread_space_start);
+}
+
+bool MemIsApp(uptr p) {
+  CHECK(GetTagFromPointer(p) == 0);
+  return p >= kHighMemStart || (p >= kLowMemStart && p <= kLowMemEnd);
+}
+
+static void HwasanAtExit(void) {
+  if (common_flags()->print_module_map)
+    DumpProcessMap();
+  if (flags()->print_stats && (flags()->atexit || hwasan_report_count > 0))
+    ReportStats();
+  if (hwasan_report_count > 0) {
+    // ReportAtExitStatistics();
+    if (common_flags()->exitcode)
+      internal__exit(common_flags()->exitcode);
+  }
+}
+
+void InstallAtExitHandler() {
+  atexit(HwasanAtExit);
+}
+
+// ---------------------- TSD ---------------- {{{1
+
+extern "C" void __hwasan_thread_enter() {
+  hwasanThreadList().CreateCurrentThread()->InitRandomState();
+}
+
+extern "C" void __hwasan_thread_exit() {
+  Thread *t = GetCurrentThread();
+  // Make sure that signal handler can not see a stale current thread pointer.
+  atomic_signal_fence(memory_order_seq_cst);
+  if (t)
+    hwasanThreadList().ReleaseThread(t);
+}
+
+#if HWASAN_WITH_INTERCEPTORS
+static pthread_key_t tsd_key;
+static bool tsd_key_inited = false;
+
+void HwasanTSDThreadInit() {
+  if (tsd_key_inited)
+    CHECK_EQ(0, pthread_setspecific(tsd_key,
+                                    (void *)GetPthreadDestructorIterations()));
+}
+
+void HwasanTSDDtor(void *tsd) {
+  uptr iterations = (uptr)tsd;
+  if (iterations > 1) {
+    CHECK_EQ(0, pthread_setspecific(tsd_key, (void *)(iterations - 1)));
+    return;
+  }
+  __hwasan_thread_exit();
+}
+
+void HwasanTSDInit() {
+  CHECK(!tsd_key_inited);
+  tsd_key_inited = true;
+  CHECK_EQ(0, pthread_key_create(&tsd_key, HwasanTSDDtor));
+}
+#else
+void HwasanTSDInit() {}
+void HwasanTSDThreadInit() {}
+#endif
+
+#if SANITIZER_ANDROID
+uptr *GetCurrentThreadLongPtr() {
+  return (uptr *)get_android_tls_ptr();
+}
+#else
+uptr *GetCurrentThreadLongPtr() {
+  return &__hwasan_tls;
+}
+#endif
+
+#if SANITIZER_ANDROID
+void AndroidTestTlsSlot() {
+  uptr kMagicValue = 0x010203040A0B0C0D;
+  uptr *tls_ptr = GetCurrentThreadLongPtr();
+  uptr old_value = *tls_ptr;
+  *tls_ptr = kMagicValue;
+  dlerror();
+  if (*(uptr *)get_android_tls_ptr() != kMagicValue) {
+    Printf(
+        "ERROR: Incompatible version of Android: TLS_SLOT_SANITIZER(6) is used "
+        "for dlerror().\n");
+    Die();
+  }
+  *tls_ptr = old_value;
+}
+#else
+void AndroidTestTlsSlot() {}
+#endif
+
+Thread *GetCurrentThread() {
+  uptr *ThreadLongPtr = GetCurrentThreadLongPtr();
+  if (UNLIKELY(*ThreadLongPtr == 0))
+    return nullptr;
+  auto *R = (StackAllocationsRingBuffer *)ThreadLongPtr;
+  return hwasanThreadList().GetThreadByBufferAddress((uptr)R->Next());
+}
+
+struct AccessInfo {
+  uptr addr;
+  uptr size;
+  bool is_store;
+  bool is_load;
+  bool recover;
+};
+
+static AccessInfo GetAccessInfo(siginfo_t *info, ucontext_t *uc) {
+  // Access type is passed in a platform dependent way (see below) and encoded
+  // as 0xXY, where X&1 is 1 for store, 0 for load, and X&2 is 1 if the error is
+  // recoverable. Valid values of Y are 0 to 4, which are interpreted as
+  // log2(access_size), and 0xF, which means that access size is passed via
+  // platform dependent register (see below).
+#if defined(__aarch64__)
+  // Access type is encoded in BRK immediate as 0x900 + 0xXY. For Y == 0xF,
+  // access size is stored in X1 register. Access address is always in X0
+  // register.
+  uptr pc = (uptr)info->si_addr;
+  const unsigned code = ((*(u32 *)pc) >> 5) & 0xffff;
+  if ((code & 0xff00) != 0x900)
+    return AccessInfo{}; // Not ours.
+
+  const bool is_store = code & 0x10;
+  const bool recover = code & 0x20;
+  const uptr addr = uc->uc_mcontext.regs[0];
+  const unsigned size_log = code & 0xf;
+  if (size_log > 4 && size_log != 0xf)
+    return AccessInfo{}; // Not ours.
+  const uptr size = size_log == 0xf ? uc->uc_mcontext.regs[1] : 1U << size_log;
+
+#elif defined(__x86_64__)
+  // Access type is encoded in the instruction following INT3 as
+  // NOP DWORD ptr [EAX + 0x40 + 0xXY]. For Y == 0xF, access size is stored in
+  // RSI register. Access address is always in RDI register.
+  uptr pc = (uptr)uc->uc_mcontext.gregs[REG_RIP];
+  uint8_t *nop = (uint8_t*)pc;
+  if (*nop != 0x0f || *(nop + 1) != 0x1f || *(nop + 2) != 0x40  ||
+      *(nop + 3) < 0x40)
+    return AccessInfo{}; // Not ours.
+  const unsigned code = *(nop + 3);
+
+  const bool is_store = code & 0x10;
+  const bool recover = code & 0x20;
+  const uptr addr = uc->uc_mcontext.gregs[REG_RDI];
+  const unsigned size_log = code & 0xf;
+  if (size_log > 4 && size_log != 0xf)
+    return AccessInfo{}; // Not ours.
+  const uptr size =
+      size_log == 0xf ? uc->uc_mcontext.gregs[REG_RSI] : 1U << size_log;
+
+#else
+# error Unsupported architecture
+#endif
+
+  return AccessInfo{addr, size, is_store, !is_store, recover};
+}
+
+static void HandleTagMismatch(AccessInfo ai, uptr pc, uptr frame,
+                              ucontext_t *uc, uptr *registers_frame = nullptr) {
+  InternalMmapVector<BufferedStackTrace> stack_buffer(1);
+  BufferedStackTrace *stack = stack_buffer.data();
+  stack->Reset();
+  stack->Unwind(pc, frame, uc, common_flags()->fast_unwind_on_fatal);
+
+  // The second stack frame contains the failure __hwasan_check function, as
+  // we have a stack frame for the registers saved in __hwasan_tag_mismatch that
+  // we wish to ignore. This (currently) only occurs on AArch64, as x64
+  // implementations use SIGTRAP to implement the failure, and thus do not go
+  // through the stack saver.
+  if (registers_frame && stack->trace && stack->size > 0) {
+    stack->trace++;
+    stack->size--;
+  }
+
+  bool fatal = flags()->halt_on_error || !ai.recover;
+  ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store, fatal,
+                    registers_frame);
+}
+
+static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) {
+  AccessInfo ai = GetAccessInfo(info, uc);
+  if (!ai.is_store && !ai.is_load)
+    return false;
+
+  SignalContext sig{info, uc};
+  HandleTagMismatch(ai, StackTrace::GetNextInstructionPc(sig.pc), sig.bp, uc);
+
+#if defined(__aarch64__)
+  uc->uc_mcontext.pc += 4;
+#elif defined(__x86_64__)
+#else
+# error Unsupported architecture
+#endif
+  return true;
+}
+
+static void OnStackUnwind(const SignalContext &sig, const void *,
+                          BufferedStackTrace *stack) {
+  stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context,
+                common_flags()->fast_unwind_on_fatal);
+}
+
+void HwasanOnDeadlySignal(int signo, void *info, void *context) {
+  // Probably a tag mismatch.
+  if (signo == SIGTRAP)
+    if (HwasanOnSIGTRAP(signo, (siginfo_t *)info, (ucontext_t*)context))
+      return;
+
+  HandleDeadlySignal(info, context, GetTid(), &OnStackUnwind, nullptr);
+}
+
+
+} // namespace __hwasan
+
+// Entry point for interoperability between __hwasan_tag_mismatch (ASM) and the
+// rest of the mismatch handling code (C++).
+void __hwasan_tag_mismatch4(uptr addr, uptr access_info, uptr *registers_frame,
+                            size_t outsize) {
+  __hwasan::AccessInfo ai;
+  ai.is_store = access_info & 0x10;
+  ai.is_load = !ai.is_store;
+  ai.recover = access_info & 0x20;
+  ai.addr = addr;
+  if ((access_info & 0xf) == 0xf)
+    ai.size = outsize;
+  else
+    ai.size = 1 << (access_info & 0xf);
+
+  __hwasan::HandleTagMismatch(ai, (uptr)__builtin_return_address(0),
+                              (uptr)__builtin_frame_address(0), nullptr,
+                              registers_frame);
+  __builtin_unreachable();
+}
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
diff --git a/libsanitizer/hwasan/hwasan_malloc_bisect.h b/libsanitizer/hwasan/hwasan_malloc_bisect.h
new file mode 100644
index 00000000000..7d134e8c4b7
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_malloc_bisect.h
@@ -0,0 +1,50 @@
+//===-- hwasan_malloc_bisect.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_hash.h"
+#include "hwasan.h"
+
+namespace __hwasan {
+
+static u32 malloc_hash(StackTrace *stack, uptr orig_size) {
+  uptr len = Min(stack->size, (unsigned)7);
+  MurMur2HashBuilder H(len);
+  H.add(orig_size);
+  // Start with frame #1 to skip __sanitizer_malloc frame, which is
+  // (a) almost always the same (well, could be operator new or new[])
+  // (b) can change hashes when compiler-rt is rebuilt, invalidating previous
+  // bisection results.
+  // Because of ASLR, use only offset inside the page.
+  for (uptr i = 1; i < len; ++i) H.add(((u32)stack->trace[i]) & 0xFFF);
+  return H.get();
+}
+
+static inline bool malloc_bisect(StackTrace *stack, uptr orig_size) {
+  uptr left = flags()->malloc_bisect_left;
+  uptr right = flags()->malloc_bisect_right;
+  if (LIKELY(left == 0 && right == 0))
+    return true;
+  if (!stack)
+    return true;
+  // Allow malloc_bisect_right > (u32)(-1) to avoid spelling the latter in
+  // decimal.
+  uptr h = (uptr)malloc_hash(stack, orig_size);
+  if (h < left || h > right)
+    return false;
+  if (flags()->malloc_bisect_dump) {
+    Printf("[alloc] %u %zu\n", h, orig_size);
+    stack->Print();
+  }
+  return true;
+}
+
+}  // namespace __hwasan
diff --git a/libsanitizer/hwasan/hwasan_mapping.h b/libsanitizer/hwasan/hwasan_mapping.h
new file mode 100644
index 00000000000..c149687bdfa
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_mapping.h
@@ -0,0 +1,66 @@
+//===-- hwasan_mapping.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer and defines memory mapping.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_MAPPING_H
+#define HWASAN_MAPPING_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "hwasan_interface_internal.h"
+
+// Typical mapping on Linux/x86_64:
+// with dynamic shadow mapped at [0x770d59f40000, 0x7f0d59f40000]:
+// || [0x7f0d59f40000, 0x7fffffffffff] || HighMem    ||
+// || [0x7efe2f934000, 0x7f0d59f3ffff] || HighShadow ||
+// || [0x7e7e2f934000, 0x7efe2f933fff] || ShadowGap  ||
+// || [0x770d59f40000, 0x7e7e2f933fff] || LowShadow  ||
+// || [0x000000000000, 0x770d59f3ffff] || LowMem     ||
+
+// Typical mapping on Android/AArch64
+// with dynamic shadow mapped: [0x007477480000, 0x007c77480000]:
+// || [0x007c77480000, 0x007fffffffff] || HighMem    ||
+// || [0x007c3ebc8000, 0x007c7747ffff] || HighShadow ||
+// || [0x007bbebc8000, 0x007c3ebc7fff] || ShadowGap  ||
+// || [0x007477480000, 0x007bbebc7fff] || LowShadow  ||
+// || [0x000000000000, 0x00747747ffff] || LowMem     ||
+
+// Reasonable values are 4 (for 1/16th shadow) and 6 (for 1/64th).
+constexpr uptr kShadowScale = 4;
+constexpr uptr kShadowAlignment = 1ULL << kShadowScale;
+
+namespace __hwasan {
+
+extern uptr kLowMemStart;
+extern uptr kLowMemEnd;
+extern uptr kLowShadowEnd;
+extern uptr kLowShadowStart;
+extern uptr kHighShadowStart;
+extern uptr kHighShadowEnd;
+extern uptr kHighMemStart;
+extern uptr kHighMemEnd;
+
+inline uptr MemToShadow(uptr untagged_addr) {
+  return (untagged_addr >> kShadowScale) +
+         __hwasan_shadow_memory_dynamic_address;
+}
+inline uptr ShadowToMem(uptr shadow_addr) {
+  return (shadow_addr - __hwasan_shadow_memory_dynamic_address) << kShadowScale;
+}
+inline uptr MemToShadowSize(uptr size) {
+  return size >> kShadowScale;
+}
+
+bool MemIsApp(uptr p);
+
+}  // namespace __hwasan
+
+#endif  // HWASAN_MAPPING_H
diff --git a/libsanitizer/hwasan/hwasan_memintrinsics.cpp b/libsanitizer/hwasan/hwasan_memintrinsics.cpp
new file mode 100644
index 00000000000..e82d77a1bc1
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_memintrinsics.cpp
@@ -0,0 +1,44 @@
+//===-- hwasan_memintrinsics.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer and contains HWASAN versions of
+/// memset, memcpy and memmove
+///
+//===----------------------------------------------------------------------===//
+
+#include <string.h>
+#include "hwasan.h"
+#include "hwasan_checks.h"
+#include "hwasan_flags.h"
+#include "hwasan_interface_internal.h"
+#include "sanitizer_common/sanitizer_libc.h"
+
+using namespace __hwasan;
+
+void *__hwasan_memset(void *block, int c, uptr size) {
+  CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
+      reinterpret_cast<uptr>(block), size);
+  return memset(UntagPtr(block), c, size);
+}
+
+void *__hwasan_memcpy(void *to, const void *from, uptr size) {
+  CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
+      reinterpret_cast<uptr>(to), size);
+  CheckAddressSized<ErrorAction::Recover, AccessType::Load>(
+      reinterpret_cast<uptr>(from), size);
+  return memcpy(UntagPtr(to), UntagPtr(from), size);
+}
+
+void *__hwasan_memmove(void *to, const void *from, uptr size) {
+  CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
+      reinterpret_cast<uptr>(to), size);
+  CheckAddressSized<ErrorAction::Recover, AccessType::Load>(
+      reinterpret_cast<uptr>(from), size);
+  return memmove(UntagPtr(to), UntagPtr(from), size);
+}
diff --git a/libsanitizer/hwasan/hwasan_new_delete.cpp b/libsanitizer/hwasan/hwasan_new_delete.cpp
new file mode 100644
index 00000000000..8d01d3944f2
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_new_delete.cpp
@@ -0,0 +1,81 @@
+//===-- hwasan_new_delete.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Interceptors for operators new and delete.
+//===----------------------------------------------------------------------===//
+
+#include "hwasan.h"
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#if HWASAN_REPLACE_OPERATORS_NEW_AND_DELETE
+
+// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
+#define OPERATOR_NEW_BODY(nothrow) \
+  GET_MALLOC_STACK_TRACE; \
+  void *res = hwasan_malloc(size, &stack);\
+  if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
+  return res
+
+#define OPERATOR_DELETE_BODY \
+  GET_MALLOC_STACK_TRACE; \
+  if (ptr) hwasan_free(ptr, &stack)
+
+#elif defined(__ANDROID__)
+
+// We don't actually want to intercept operator new and delete on Android, but
+// since we previously released a runtime that intercepted these functions,
+// removing the interceptors would break ABI. Therefore we simply forward to
+// malloc and free.
+#define OPERATOR_NEW_BODY(nothrow) return malloc(size)
+#define OPERATOR_DELETE_BODY free(ptr)
+
+#endif
+
+#ifdef OPERATOR_NEW_BODY
+
+using namespace __hwasan;
+
+// Fake std::nothrow_t to avoid including <new>.
+namespace std {
+  struct nothrow_t {};
+}  // namespace std
+
+
+
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void *operator new(size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void *operator new[](size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void *operator new(size_t size, std::nothrow_t const&) {
+  OPERATOR_NEW_BODY(true /*nothrow*/);
+}
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void *operator new[](size_t size, std::nothrow_t const&) {
+  OPERATOR_NEW_BODY(true /*nothrow*/);
+}
+
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const&) {
+  OPERATOR_DELETE_BODY;
+}
+
+#endif // OPERATOR_NEW_BODY
diff --git a/libsanitizer/hwasan/hwasan_poisoning.cpp b/libsanitizer/hwasan/hwasan_poisoning.cpp
new file mode 100644
index 00000000000..2a0816428e7
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_poisoning.cpp
@@ -0,0 +1,52 @@
+//===-- hwasan_poisoning.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "hwasan_poisoning.h"
+
+#include "hwasan_mapping.h"
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_linux.h"
+
+namespace __hwasan {
+
+uptr TagMemoryAligned(uptr p, uptr size, tag_t tag) {
+  CHECK(IsAligned(p, kShadowAlignment));
+  CHECK(IsAligned(size, kShadowAlignment));
+  uptr shadow_start = MemToShadow(p);
+  uptr shadow_size = MemToShadowSize(size);
+
+  uptr page_size = GetPageSizeCached();
+  uptr page_start = RoundUpTo(shadow_start, page_size);
+  uptr page_end = RoundDownTo(shadow_start + shadow_size, page_size);
+  uptr threshold = common_flags()->clear_shadow_mmap_threshold;
+  if (SANITIZER_LINUX &&
+      UNLIKELY(page_end >= page_start + threshold && tag == 0)) {
+    internal_memset((void *)shadow_start, tag, page_start - shadow_start);
+    internal_memset((void *)page_end, tag,
+                    shadow_start + shadow_size - page_end);
+    // For an anonymous private mapping MADV_DONTNEED will return a zero page on
+    // Linux.
+    ReleaseMemoryPagesToOSAndZeroFill(page_start, page_end);
+  } else {
+    internal_memset((void *)shadow_start, tag, shadow_size);
+  }
+  return AddTagToPointer(p, tag);
+}
+
+uptr TagMemory(uptr p, uptr size, tag_t tag) {
+  uptr start = RoundDownTo(p, kShadowAlignment);
+  uptr end = RoundUpTo(p + size, kShadowAlignment);
+  return TagMemoryAligned(start, end - start, tag);
+}
+
+}  // namespace __hwasan
diff --git a/libsanitizer/hwasan/hwasan_poisoning.h b/libsanitizer/hwasan/hwasan_poisoning.h
new file mode 100644
index 00000000000..61751f7d252
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_poisoning.h
@@ -0,0 +1,24 @@
+//===-- hwasan_poisoning.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_POISONING_H
+#define HWASAN_POISONING_H
+
+#include "hwasan.h"
+
+namespace __hwasan {
+uptr TagMemory(uptr p, uptr size, tag_t tag);
+uptr TagMemoryAligned(uptr p, uptr size, tag_t tag);
+
+}  // namespace __hwasan
+
+#endif  // HWASAN_POISONING_H
diff --git a/libsanitizer/hwasan/hwasan_report.cpp b/libsanitizer/hwasan/hwasan_report.cpp
new file mode 100644
index 00000000000..0be7deeaee1
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_report.cpp
@@ -0,0 +1,651 @@
+//===-- hwasan_report.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Error reporting.
+//===----------------------------------------------------------------------===//
+
+#include "hwasan_report.h"
+
+#include <dlfcn.h>
+
+#include "hwasan.h"
+#include "hwasan_allocator.h"
+#include "hwasan_globals.h"
+#include "hwasan_mapping.h"
+#include "hwasan_thread.h"
+#include "hwasan_thread_list.h"
+#include "sanitizer_common/sanitizer_allocator_internal.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_report_decorator.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace_printer.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+using namespace __sanitizer;
+
+namespace __hwasan {
+
+class ScopedReport {
+ public:
+  ScopedReport(bool fatal = false) : error_message_(1), fatal(fatal) {
+    BlockingMutexLock lock(&error_message_lock_);
+    error_message_ptr_ = fatal ? &error_message_ : nullptr;
+    ++hwasan_report_count;
+  }
+
+  ~ScopedReport() {
+    {
+      BlockingMutexLock lock(&error_message_lock_);
+      if (fatal)
+        SetAbortMessage(error_message_.data());
+      error_message_ptr_ = nullptr;
+    }
+    if (common_flags()->print_module_map >= 2 ||
+        (fatal && common_flags()->print_module_map))
+      DumpProcessMap();
+    if (fatal)
+      Die();
+  }
+
+  static void MaybeAppendToErrorMessage(const char *msg) {
+    BlockingMutexLock lock(&error_message_lock_);
+    if (!error_message_ptr_)
+      return;
+    uptr len = internal_strlen(msg);
+    uptr old_size = error_message_ptr_->size();
+    error_message_ptr_->resize(old_size + len);
+    // overwrite old trailing '\0', keep new trailing '\0' untouched.
+    internal_memcpy(&(*error_message_ptr_)[old_size - 1], msg, len);
+  }
+ private:
+  ScopedErrorReportLock error_report_lock_;
+  InternalMmapVector<char> error_message_;
+  bool fatal;
+
+  static InternalMmapVector<char> *error_message_ptr_;
+  static BlockingMutex error_message_lock_;
+};
+
+InternalMmapVector<char> *ScopedReport::error_message_ptr_;
+BlockingMutex ScopedReport::error_message_lock_;
+
+// If there is an active ScopedReport, append to its error message.
+void AppendToErrorMessageBuffer(const char *buffer) {
+  ScopedReport::MaybeAppendToErrorMessage(buffer);
+}
+
+static StackTrace GetStackTraceFromId(u32 id) {
+  CHECK(id);
+  StackTrace res = StackDepotGet(id);
+  CHECK(res.trace);
+  return res;
+}
+
+// A RAII object that holds a copy of the current thread stack ring buffer.
+// The actual stack buffer may change while we are iterating over it (for
+// example, Printf may call syslog() which can itself be built with hwasan).
+class SavedStackAllocations {
+ public:
+  SavedStackAllocations(StackAllocationsRingBuffer *rb) {
+    uptr size = rb->size() * sizeof(uptr);
+    void *storage =
+        MmapAlignedOrDieOnFatalError(size, size * 2, "saved stack allocations");
+    new (&rb_) StackAllocationsRingBuffer(*rb, storage);
+  }
+
+  ~SavedStackAllocations() {
+    StackAllocationsRingBuffer *rb = get();
+    UnmapOrDie(rb->StartOfStorage(), rb->size() * sizeof(uptr));
+  }
+
+  StackAllocationsRingBuffer *get() {
+    return (StackAllocationsRingBuffer *)&rb_;
+  }
+
+ private:
+  uptr rb_;
+};
+
+class Decorator: public __sanitizer::SanitizerCommonDecorator {
+ public:
+  Decorator() : SanitizerCommonDecorator() { }
+  const char *Access() { return Blue(); }
+  const char *Allocation() const { return Magenta(); }
+  const char *Origin() const { return Magenta(); }
+  const char *Name() const { return Green(); }
+  const char *Location() { return Green(); }
+  const char *Thread() { return Green(); }
+};
+
+static bool FindHeapAllocation(HeapAllocationsRingBuffer *rb, uptr tagged_addr,
+                               HeapAllocationRecord *har, uptr *ring_index,
+                               uptr *num_matching_addrs,
+                               uptr *num_matching_addrs_4b) {
+  if (!rb) return false;
+
+  *num_matching_addrs = 0;
+  *num_matching_addrs_4b = 0;
+  for (uptr i = 0, size = rb->size(); i < size; i++) {
+    auto h = (*rb)[i];
+    if (h.tagged_addr <= tagged_addr &&
+        h.tagged_addr + h.requested_size > tagged_addr) {
+      *har = h;
+      *ring_index = i;
+      return true;
+    }
+
+    // Measure the number of heap ring buffer entries that would have matched
+    // if we had only one entry per address (e.g. if the ring buffer data was
+    // stored at the address itself). This will help us tune the allocator
+    // implementation for MTE.
+    if (UntagAddr(h.tagged_addr) <= UntagAddr(tagged_addr) &&
+        UntagAddr(h.tagged_addr) + h.requested_size > UntagAddr(tagged_addr)) {
+      ++*num_matching_addrs;
+    }
+
+    // Measure the number of heap ring buffer entries that would have matched
+    // if we only had 4 tag bits, which is the case for MTE.
+    auto untag_4b = [](uptr p) {
+      return p & ((1ULL << 60) - 1);
+    };
+    if (untag_4b(h.tagged_addr) <= untag_4b(tagged_addr) &&
+        untag_4b(h.tagged_addr) + h.requested_size > untag_4b(tagged_addr)) {
+      ++*num_matching_addrs_4b;
+    }
+  }
+  return false;
+}
+
+static void PrintStackAllocations(StackAllocationsRingBuffer *sa,
+                                  tag_t addr_tag, uptr untagged_addr) {
+  uptr frames = Min((uptr)flags()->stack_history_size, sa->size());
+  bool found_local = false;
+  for (uptr i = 0; i < frames; i++) {
+    const uptr *record_addr = &(*sa)[i];
+    uptr record = *record_addr;
+    if (!record)
+      break;
+    tag_t base_tag =
+        reinterpret_cast<uptr>(record_addr) >> kRecordAddrBaseTagShift;
+    uptr fp = (record >> kRecordFPShift) << kRecordFPLShift;
+    uptr pc_mask = (1ULL << kRecordFPShift) - 1;
+    uptr pc = record & pc_mask;
+    FrameInfo frame;
+    if (Symbolizer::GetOrInit()->SymbolizeFrame(pc, &frame)) {
+      for (LocalInfo &local : frame.locals) {
+        if (!local.has_frame_offset || !local.has_size || !local.has_tag_offset)
+          continue;
+        tag_t obj_tag = base_tag ^ local.tag_offset;
+        if (obj_tag != addr_tag)
+          continue;
+        // Calculate the offset from the object address to the faulting
+        // address. Because we only store bits 4-19 of FP (bits 0-3 are
+        // guaranteed to be zero), the calculation is performed mod 2^20 and may
+        // harmlessly underflow if the address mod 2^20 is below the object
+        // address.
+        uptr obj_offset =
+            (untagged_addr - fp - local.frame_offset) & (kRecordFPModulus - 1);
+        if (obj_offset >= local.size)
+          continue;
+        if (!found_local) {
+          Printf("Potentially referenced stack objects:\n");
+          found_local = true;
+        }
+        Printf("  %s in %s %s:%d\n", local.name, local.function_name,
+               local.decl_file, local.decl_line);
+      }
+      frame.Clear();
+    }
+  }
+
+  if (found_local)
+    return;
+
+  // We didn't find any locals. Most likely we don't have symbols, so dump
+  // the information that we have for offline analysis.
+  InternalScopedString frame_desc(GetPageSizeCached() * 2);
+  Printf("Previously allocated frames:\n");
+  for (uptr i = 0; i < frames; i++) {
+    const uptr *record_addr = &(*sa)[i];
+    uptr record = *record_addr;
+    if (!record)
+      break;
+    uptr pc_mask = (1ULL << 48) - 1;
+    uptr pc = record & pc_mask;
+    frame_desc.append("  record_addr:0x%zx record:0x%zx",
+                      reinterpret_cast<uptr>(record_addr), record);
+    if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) {
+      RenderFrame(&frame_desc, " %F %L\n", 0, frame->info.address, &frame->info,
+                  common_flags()->symbolize_vs_style,
+                  common_flags()->strip_path_prefix);
+      frame->ClearAll();
+    }
+    Printf("%s", frame_desc.data());
+    frame_desc.clear();
+  }
+}
+
+// Returns true if tag == *tag_ptr, reading tags from short granules if
+// necessary. This may return a false positive if tags 1-15 are used as a
+// regular tag rather than a short granule marker.
+static bool TagsEqual(tag_t tag, tag_t *tag_ptr) {
+  if (tag == *tag_ptr)
+    return true;
+  if (*tag_ptr == 0 || *tag_ptr > kShadowAlignment - 1)
+    return false;
+  uptr mem = ShadowToMem(reinterpret_cast<uptr>(tag_ptr));
+  tag_t inline_tag = *reinterpret_cast<tag_t *>(mem + kShadowAlignment - 1);
+  return tag == inline_tag;
+}
+
+// HWASan globals store the size of the global in the descriptor. In cases where
+// we don't have a binary with symbols, we can't grab the size of the global
+// from the debug info - but we might be able to retrieve it from the
+// descriptor. Returns zero if the lookup failed.
+static uptr GetGlobalSizeFromDescriptor(uptr ptr) {
+  // Find the ELF object that this global resides in.
+  Dl_info info;
+  dladdr(reinterpret_cast<void *>(ptr), &info);
+  auto *ehdr = reinterpret_cast<const ElfW(Ehdr) *>(info.dli_fbase);
+  auto *phdr_begin = reinterpret_cast<const ElfW(Phdr) *>(
+      reinterpret_cast<const u8 *>(ehdr) + ehdr->e_phoff);
+
+  // Get the load bias. This is normally the same as the dli_fbase address on
+  // position-independent code, but can be different on non-PIE executables,
+  // binaries using LLD's partitioning feature, or binaries compiled with a
+  // linker script.
+  ElfW(Addr) load_bias = 0;
+  for (const auto &phdr :
+       ArrayRef<const ElfW(Phdr)>(phdr_begin, phdr_begin + ehdr->e_phnum)) {
+    if (phdr.p_type != PT_LOAD || phdr.p_offset != 0)
+      continue;
+    load_bias = reinterpret_cast<ElfW(Addr)>(ehdr) - phdr.p_vaddr;
+    break;
+  }
+
+  // Walk all globals in this ELF object, looking for the one we're interested
+  // in. Once we find it, we can stop iterating and return the size of the
+  // global we're interested in.
+  for (const hwasan_global &global :
+       HwasanGlobalsFor(load_bias, phdr_begin, ehdr->e_phnum))
+    if (global.addr() <= ptr && ptr < global.addr() + global.size())
+      return global.size();
+
+  return 0;
+}
+
+void PrintAddressDescription(
+    uptr tagged_addr, uptr access_size,
+    StackAllocationsRingBuffer *current_stack_allocations) {
+  Decorator d;
+  int num_descriptions_printed = 0;
+  uptr untagged_addr = UntagAddr(tagged_addr);
+
+  // Print some very basic information about the address, if it's a heap.
+  HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
+  if (uptr beg = chunk.Beg()) {
+    uptr size = chunk.ActualSize();
+    Printf("%s[%p,%p) is a %s %s heap chunk; "
+           "size: %zd offset: %zd\n%s",
+           d.Location(),
+           beg, beg + size,
+           chunk.FromSmallHeap() ? "small" : "large",
+           chunk.IsAllocated() ? "allocated" : "unallocated",
+           size, untagged_addr - beg,
+           d.Default());
+  }
+
+  // Check if this looks like a heap buffer overflow by scanning
+  // the shadow left and right and looking for the first adjacent
+  // object with a different memory tag. If that tag matches addr_tag,
+  // check the allocator if it has a live chunk there.
+  tag_t addr_tag = GetTagFromPointer(tagged_addr);
+  tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
+  tag_t *candidate = nullptr, *left = tag_ptr, *right = tag_ptr;
+  for (int i = 0; i < 1000; i++) {
+    if (TagsEqual(addr_tag, left)) {
+      candidate = left;
+      break;
+    }
+    --left;
+    if (TagsEqual(addr_tag, right)) {
+      candidate = right;
+      break;
+    }
+    ++right;
+  }
+
+  if (candidate) {
+    uptr mem = ShadowToMem(reinterpret_cast<uptr>(candidate));
+    HwasanChunkView chunk = FindHeapChunkByAddress(mem);
+    if (chunk.IsAllocated()) {
+      Printf("%s", d.Location());
+      Printf("%p is located %zd bytes to the %s of %zd-byte region [%p,%p)\n",
+             untagged_addr,
+             candidate == left ? untagged_addr - chunk.End()
+                               : chunk.Beg() - untagged_addr,
+             candidate == left ? "right" : "left", chunk.UsedSize(),
+             chunk.Beg(), chunk.End());
+      Printf("%s", d.Allocation());
+      Printf("allocated here:\n");
+      Printf("%s", d.Default());
+      GetStackTraceFromId(chunk.GetAllocStackId()).Print();
+      num_descriptions_printed++;
+    } else {
+      // Check whether the address points into a loaded library. If so, this is
+      // most likely a global variable.
+      const char *module_name;
+      uptr module_address;
+      Symbolizer *sym = Symbolizer::GetOrInit();
+      if (sym->GetModuleNameAndOffsetForPC(mem, &module_name,
+                                           &module_address)) {
+        DataInfo info;
+        if (sym->SymbolizeData(mem, &info) && info.start) {
+          Printf(
+              "%p is located %zd bytes to the %s of %zd-byte global variable "
+              "%s [%p,%p) in %s\n",
+              untagged_addr,
+              candidate == left ? untagged_addr - (info.start + info.size)
+                                : info.start - untagged_addr,
+              candidate == left ? "right" : "left", info.size, info.name,
+              info.start, info.start + info.size, module_name);
+        } else {
+          uptr size = GetGlobalSizeFromDescriptor(mem);
+          if (size == 0)
+            // We couldn't find the size of the global from the descriptors.
+            Printf(
+                "%p is located to the %s of a global variable in (%s+0x%x)\n",
+                untagged_addr, candidate == left ? "right" : "left",
+                module_name, module_address);
+          else
+            Printf(
+                "%p is located to the %s of a %zd-byte global variable in "
+                "(%s+0x%x)\n",
+                untagged_addr, candidate == left ? "right" : "left", size,
+                module_name, module_address);
+        }
+        num_descriptions_printed++;
+      }
+    }
+  }
+
+  hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
+    // Scan all threads' ring buffers to find if it's a heap-use-after-free.
+    HeapAllocationRecord har;
+    uptr ring_index, num_matching_addrs, num_matching_addrs_4b;
+    if (FindHeapAllocation(t->heap_allocations(), tagged_addr, &har,
+                           &ring_index, &num_matching_addrs,
+                           &num_matching_addrs_4b)) {
+      Printf("%s", d.Location());
+      Printf("%p is located %zd bytes inside of %zd-byte region [%p,%p)\n",
+             untagged_addr, untagged_addr - UntagAddr(har.tagged_addr),
+             har.requested_size, UntagAddr(har.tagged_addr),
+             UntagAddr(har.tagged_addr) + har.requested_size);
+      Printf("%s", d.Allocation());
+      Printf("freed by thread T%zd here:\n", t->unique_id());
+      Printf("%s", d.Default());
+      GetStackTraceFromId(har.free_context_id).Print();
+
+      Printf("%s", d.Allocation());
+      Printf("previously allocated here:\n", t);
+      Printf("%s", d.Default());
+      GetStackTraceFromId(har.alloc_context_id).Print();
+
+      // Print a developer note: the index of this heap object
+      // in the thread's deallocation ring buffer.
+      Printf("hwasan_dev_note_heap_rb_distance: %zd %zd\n", ring_index + 1,
+             flags()->heap_history_size);
+      Printf("hwasan_dev_note_num_matching_addrs: %zd\n", num_matching_addrs);
+      Printf("hwasan_dev_note_num_matching_addrs_4b: %zd\n",
+             num_matching_addrs_4b);
+
+      t->Announce();
+      num_descriptions_printed++;
+    }
+
+    // Very basic check for stack memory.
+    if (t->AddrIsInStack(untagged_addr)) {
+      Printf("%s", d.Location());
+      Printf("Address %p is located in stack of thread T%zd\n", untagged_addr,
+             t->unique_id());
+      Printf("%s", d.Default());
+      t->Announce();
+
+      auto *sa = (t == GetCurrentThread() && current_stack_allocations)
+                     ? current_stack_allocations
+                     : t->stack_allocations();
+      PrintStackAllocations(sa, addr_tag, untagged_addr);
+      num_descriptions_printed++;
+    }
+  });
+
+  // Print the remaining threads, as an extra information, 1 line per thread.
+  hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { t->Announce(); });
+
+  if (!num_descriptions_printed)
+    // We exhausted our possibilities. Bail out.
+    Printf("HWAddressSanitizer can not describe address in more detail.\n");
+}
+
+void ReportStats() {}
+
+static void PrintTagInfoAroundAddr(tag_t *tag_ptr, uptr num_rows,
+                                   void (*print_tag)(InternalScopedString &s,
+                                                     tag_t *tag)) {
+  const uptr row_len = 16;  // better be power of two.
+  tag_t *center_row_beg = reinterpret_cast<tag_t *>(
+      RoundDownTo(reinterpret_cast<uptr>(tag_ptr), row_len));
+  tag_t *beg_row = center_row_beg - row_len * (num_rows / 2);
+  tag_t *end_row = center_row_beg + row_len * ((num_rows + 1) / 2);
+  InternalScopedString s(GetPageSizeCached() * 8);
+  for (tag_t *row = beg_row; row < end_row; row += row_len) {
+    s.append("%s", row == center_row_beg ? "=>" : "  ");
+    s.append("%p:", row);
+    for (uptr i = 0; i < row_len; i++) {
+      s.append("%s", row + i == tag_ptr ? "[" : " ");
+      print_tag(s, &row[i]);
+      s.append("%s", row + i == tag_ptr ? "]" : " ");
+    }
+    s.append("\n");
+  }
+  Printf("%s", s.data());
+}
+
+static void PrintTagsAroundAddr(tag_t *tag_ptr) {
+  Printf(
+      "Memory tags around the buggy address (one tag corresponds to %zd "
+      "bytes):\n", kShadowAlignment);
+  PrintTagInfoAroundAddr(tag_ptr, 17, [](InternalScopedString &s, tag_t *tag) {
+    s.append("%02x", *tag);
+  });
+
+  Printf(
+      "Tags for short granules around the buggy address (one tag corresponds "
+      "to %zd bytes):\n",
+      kShadowAlignment);
+  PrintTagInfoAroundAddr(tag_ptr, 3, [](InternalScopedString &s, tag_t *tag) {
+    if (*tag >= 1 && *tag <= kShadowAlignment) {
+      uptr granule_addr = ShadowToMem(reinterpret_cast<uptr>(tag));
+      s.append("%02x",
+               *reinterpret_cast<u8 *>(granule_addr + kShadowAlignment - 1));
+    } else {
+      s.append("..");
+    }
+  });
+  Printf(
+      "See "
+      "https://clang.llvm.org/docs/"
+      "HardwareAssistedAddressSanitizerDesign.html#short-granules for a "
+      "description of short granule tags\n");
+}
+
+void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
+  ScopedReport R(flags()->halt_on_error);
+
+  uptr untagged_addr = UntagAddr(tagged_addr);
+  tag_t ptr_tag = GetTagFromPointer(tagged_addr);
+  tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
+  tag_t mem_tag = *tag_ptr;
+  Decorator d;
+  Printf("%s", d.Error());
+  uptr pc = stack->size ? stack->trace[0] : 0;
+  const char *bug_type = "invalid-free";
+  Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,
+         untagged_addr, pc);
+  Printf("%s", d.Access());
+  Printf("tags: %02x/%02x (ptr/mem)\n", ptr_tag, mem_tag);
+  Printf("%s", d.Default());
+
+  stack->Print();
+
+  PrintAddressDescription(tagged_addr, 0, nullptr);
+
+  PrintTagsAroundAddr(tag_ptr);
+
+  ReportErrorSummary(bug_type, stack);
+}
+
+void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
+                           const u8 *expected) {
+  uptr tail_size = kShadowAlignment - (orig_size % kShadowAlignment);
+  ScopedReport R(flags()->halt_on_error);
+  Decorator d;
+  uptr untagged_addr = UntagAddr(tagged_addr);
+  Printf("%s", d.Error());
+  const char *bug_type = "allocation-tail-overwritten";
+  Report("ERROR: %s: %s; heap object [%p,%p) of size %zd\n", SanitizerToolName,
+         bug_type, untagged_addr, untagged_addr + orig_size, orig_size);
+  Printf("\n%s", d.Default());
+  stack->Print();
+  HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
+  if (chunk.Beg()) {
+    Printf("%s", d.Allocation());
+    Printf("allocated here:\n");
+    Printf("%s", d.Default());
+    GetStackTraceFromId(chunk.GetAllocStackId()).Print();
+  }
+
+  InternalScopedString s(GetPageSizeCached() * 8);
+  CHECK_GT(tail_size, 0U);
+  CHECK_LT(tail_size, kShadowAlignment);
+  u8 *tail = reinterpret_cast<u8*>(untagged_addr + orig_size);
+  s.append("Tail contains: ");
+  for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
+    s.append(".. ");
+  for (uptr i = 0; i < tail_size; i++)
+    s.append("%02x ", tail[i]);
+  s.append("\n");
+  s.append("Expected:      ");
+  for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
+    s.append(".. ");
+  for (uptr i = 0; i < tail_size; i++)
+    s.append("%02x ", expected[i]);
+  s.append("\n");
+  s.append("               ");
+  for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
+    s.append("   ");
+  for (uptr i = 0; i < tail_size; i++)
+    s.append("%s ", expected[i] != tail[i] ? "^^" : "  ");
+
+  s.append("\nThis error occurs when a buffer overflow overwrites memory\n"
+    "to the right of a heap object, but within the %zd-byte granule, e.g.\n"
+    "   char *x = new char[20];\n"
+    "   x[25] = 42;\n"
+    "%s does not detect such bugs in uninstrumented code at the time of write,"
+    "\nbut can detect them at the time of free/delete.\n"
+    "To disable this feature set HWASAN_OPTIONS=free_checks_tail_magic=0\n",
+    kShadowAlignment, SanitizerToolName);
+  Printf("%s", s.data());
+  GetCurrentThread()->Announce();
+
+  tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
+  PrintTagsAroundAddr(tag_ptr);
+
+  ReportErrorSummary(bug_type, stack);
+}
+
+void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
+                       bool is_store, bool fatal, uptr *registers_frame) {
+  ScopedReport R(fatal);
+  SavedStackAllocations current_stack_allocations(
+      GetCurrentThread()->stack_allocations());
+
+  Decorator d;
+  Printf("%s", d.Error());
+  uptr untagged_addr = UntagAddr(tagged_addr);
+  // TODO: when possible, try to print heap-use-after-free, etc.
+  const char *bug_type = "tag-mismatch";
+  uptr pc = stack->size ? stack->trace[0] : 0;
+  Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,
+         untagged_addr, pc);
+
+  Thread *t = GetCurrentThread();
+
+  sptr offset =
+      __hwasan_test_shadow(reinterpret_cast<void *>(tagged_addr), access_size);
+  CHECK(offset >= 0 && offset < static_cast<sptr>(access_size));
+  tag_t ptr_tag = GetTagFromPointer(tagged_addr);
+  tag_t *tag_ptr =
+      reinterpret_cast<tag_t *>(MemToShadow(untagged_addr + offset));
+  tag_t mem_tag = *tag_ptr;
+
+  Printf("%s", d.Access());
+  Printf("%s of size %zu at %p tags: %02x/%02x (ptr/mem) in thread T%zd\n",
+         is_store ? "WRITE" : "READ", access_size, untagged_addr, ptr_tag,
+         mem_tag, t->unique_id());
+  if (offset != 0)
+    Printf("Invalid access starting at offset [%zu, %zu)\n", offset,
+           Min(access_size, static_cast<uptr>(offset) + (1 << kShadowScale)));
+  Printf("%s", d.Default());
+
+  stack->Print();
+
+  PrintAddressDescription(tagged_addr, access_size,
+                          current_stack_allocations.get());
+  t->Announce();
+
+  PrintTagsAroundAddr(tag_ptr);
+
+  if (registers_frame)
+    ReportRegisters(registers_frame, pc);
+
+  ReportErrorSummary(bug_type, stack);
+}
+
+// See the frame breakdown defined in __hwasan_tag_mismatch (from
+// hwasan_tag_mismatch_aarch64.S).
+void ReportRegisters(uptr *frame, uptr pc) {
+  Printf("Registers where the failure occurred (pc %p):\n", pc);
+
+  // We explicitly print a single line (4 registers/line) each iteration to
+  // reduce the amount of logcat error messages printed. Each Printf() will
+  // result in a new logcat line, irrespective of whether a newline is present,
+  // and so we wish to reduce the number of Printf() calls we have to make.
+  Printf("    x0  %016llx  x1  %016llx  x2  %016llx  x3  %016llx\n",
+       frame[0], frame[1], frame[2], frame[3]);
+  Printf("    x4  %016llx  x5  %016llx  x6  %016llx  x7  %016llx\n",
+       frame[4], frame[5], frame[6], frame[7]);
+  Printf("    x8  %016llx  x9  %016llx  x10 %016llx  x11 %016llx\n",
+       frame[8], frame[9], frame[10], frame[11]);
+  Printf("    x12 %016llx  x13 %016llx  x14 %016llx  x15 %016llx\n",
+       frame[12], frame[13], frame[14], frame[15]);
+  Printf("    x16 %016llx  x17 %016llx  x18 %016llx  x19 %016llx\n",
+       frame[16], frame[17], frame[18], frame[19]);
+  Printf("    x20 %016llx  x21 %016llx  x22 %016llx  x23 %016llx\n",
+       frame[20], frame[21], frame[22], frame[23]);
+  Printf("    x24 %016llx  x25 %016llx  x26 %016llx  x27 %016llx\n",
+       frame[24], frame[25], frame[26], frame[27]);
+  Printf("    x28 %016llx  x29 %016llx  x30 %016llx\n",
+       frame[28], frame[29], frame[30]);
+}
+
+}  // namespace __hwasan
diff --git a/libsanitizer/hwasan/hwasan_report.h b/libsanitizer/hwasan/hwasan_report.h
new file mode 100644
index 00000000000..de86c38fc01
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_report.h
@@ -0,0 +1,35 @@
+//===-- hwasan_report.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer. HWASan-private header for error
+/// reporting functions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_REPORT_H
+#define HWASAN_REPORT_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+
+namespace __hwasan {
+
+void ReportStats();
+void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
+                       bool is_store, bool fatal, uptr *registers_frame);
+void ReportInvalidFree(StackTrace *stack, uptr addr);
+void ReportTailOverwritten(StackTrace *stack, uptr addr, uptr orig_size,
+                           const u8 *expected);
+void ReportRegisters(uptr *registers_frame, uptr pc);
+void ReportAtExitStatistics();
+
+
+}  // namespace __hwasan
+
+#endif  // HWASAN_REPORT_H
diff --git a/libsanitizer/hwasan/hwasan_setjmp.S b/libsanitizer/hwasan/hwasan_setjmp.S
new file mode 100644
index 00000000000..0c135433194
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_setjmp.S
@@ -0,0 +1,100 @@
+//===-- hwasan_setjmp.S --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// HWAddressSanitizer runtime.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_asm.h"
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+#include "sanitizer_common/sanitizer_platform.h"
+
+// We want to save the context of the calling function.
+// That requires
+// 1) No modification of the link register by this function.
+// 2) No modification of the stack pointer by this function.
+// 3) (no modification of any other saved register, but that's not really going
+// to occur, and hence isn't as much of a worry).
+//
+// There's essentially no way to ensure that the compiler will not modify the
+// stack pointer when compiling a C function.
+// Hence we have to write this function in assembly.
+
+.section .text
+.file "hwasan_setjmp.S"
+
+.global __interceptor_setjmp
+ASM_TYPE_FUNCTION(__interceptor_setjmp)
+__interceptor_setjmp:
+  CFI_STARTPROC
+  mov	x1, #0
+  b	__interceptor_sigsetjmp
+  CFI_ENDPROC
+ASM_SIZE(__interceptor_setjmp)
+
+#if SANITIZER_ANDROID
+// Bionic also defines a function `setjmp` that calls `sigsetjmp` saving the
+// current signal.
+.global __interceptor_setjmp_bionic
+ASM_TYPE_FUNCTION(__interceptor_setjmp_bionic)
+__interceptor_setjmp_bionic:
+  CFI_STARTPROC
+  mov	x1, #1
+  b	__interceptor_sigsetjmp
+  CFI_ENDPROC
+ASM_SIZE(__interceptor_setjmp_bionic)
+#endif
+
+.global __interceptor_sigsetjmp
+ASM_TYPE_FUNCTION(__interceptor_sigsetjmp)
+__interceptor_sigsetjmp:
+  CFI_STARTPROC
+  stp	x19, x20, [x0, #0<<3]
+  stp	x21, x22, [x0, #2<<3]
+  stp	x23, x24, [x0, #4<<3]
+  stp	x25, x26, [x0, #6<<3]
+  stp	x27, x28, [x0, #8<<3]
+  stp	x29, x30, [x0, #10<<3]
+  stp	 d8,  d9, [x0, #14<<3]
+  stp	d10, d11, [x0, #16<<3]
+  stp	d12, d13, [x0, #18<<3]
+  stp	d14, d15, [x0, #20<<3]
+  mov	x2,  sp
+  str	x2,  [x0, #13<<3]
+  // We always have the second argument to __sigjmp_save (savemask) set, since
+  // the _setjmp function above has set it for us as `false`.
+  // This function is defined in hwasan_interceptors.cc
+  b	__sigjmp_save
+  CFI_ENDPROC
+ASM_SIZE(__interceptor_sigsetjmp)
+
+
+.macro ALIAS first second
+  .globl \second
+  .equ \second\(), \first
+.endm
+
+#if SANITIZER_ANDROID
+ALIAS __interceptor_sigsetjmp, sigsetjmp
+.weak sigsetjmp
+
+ALIAS __interceptor_setjmp_bionic, setjmp
+.weak setjmp
+#else
+ALIAS __interceptor_sigsetjmp, __sigsetjmp
+.weak __sigsetjmp
+#endif
+
+ALIAS __interceptor_setjmp, _setjmp
+.weak _setjmp
+#endif
+
+// We do not need executable stack.
+NO_EXEC_STACK_DIRECTIVE
diff --git a/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S b/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S
new file mode 100644
index 00000000000..08df12736bb
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S
@@ -0,0 +1,152 @@
+#include "sanitizer_common/sanitizer_asm.h"
+
+// The content of this file is AArch64-only:
+#if defined(__aarch64__)
+
+// The responsibility of the HWASan entry point in compiler-rt is to primarily
+// readjust the stack from the callee and save the current register values to
+// the stack.
+// This entry point function should be called from a __hwasan_check_* symbol.
+// These are generated during a lowering pass in the backend, and are found in
+// AArch64AsmPrinter::EmitHwasanMemaccessSymbols(). Please look there for
+// further information.
+// The __hwasan_check_* caller of this function should have expanded the stack
+// and saved the previous values of x0, x1, x29, and x30. This function will
+// "consume" these saved values and treats it as part of its own stack frame.
+// In this sense, the __hwasan_check_* callee and this function "share" a stack
+// frame. This allows us to omit having unwinding information (.cfi_*) present
+// in every __hwasan_check_* function, therefore reducing binary size. This is
+// particularly important as hwasan_check_* instances are duplicated in every
+// translation unit where HWASan is enabled.
+// This function calls HwasanTagMismatch to step back into the C++ code that
+// completes the stack unwinding and error printing. This function is is not
+// permitted to return.
+
+
+// Frame from __hwasan_check_:
+// |              ...                |
+// |              ...                |
+// | Previous stack frames...        |
+// +=================================+
+// | Unused 8-bytes for maintaining  |
+// | 16-byte SP alignment.           |
+// +---------------------------------+
+// | Return address (x30) for caller |
+// | of __hwasan_check_*.            |
+// +---------------------------------+
+// | Frame address (x29) for caller  |
+// | of __hwasan_check_*             |
+// +---------------------------------+ <-- [SP + 232]
+// |              ...                |
+// |                                 |
+// | Stack frame space for x2 - x28. |
+// |                                 |
+// |              ...                |
+// +---------------------------------+ <-- [SP + 16]
+// |                                 |
+// | Saved x1, as __hwasan_check_*   |
+// | clobbers it.                    |
+// +---------------------------------+
+// | Saved x0, likewise above.       |
+// +---------------------------------+ <-- [x30 / SP]
+
+// This function takes two arguments:
+//   * x0: The data address.
+//   * x1: The encoded access info for the failing access.
+
+// This function has two entry points. The first, __hwasan_tag_mismatch, is used
+// by clients that were compiled without short tag checks (i.e. binaries built
+// by older compilers and binaries targeting older runtimes). In this case the
+// outlined tag check will be missing the code handling short tags (which won't
+// be used in the binary's own stack variables but may be used on the heap
+// or stack variables in other binaries), so the check needs to be done here.
+//
+// The second, __hwasan_tag_mismatch_v2, is used by binaries targeting newer
+// runtimes. This entry point bypasses the short tag check since it will have
+// already been done as part of the outlined tag check. Since tag mismatches are
+// uncommon, there isn't a significant performance benefit to being able to
+// bypass the check; the main benefits are that we can sometimes avoid
+// clobbering the x17 register in error reports, and that the program will have
+// a runtime dependency on the __hwasan_tag_mismatch_v2 symbol therefore it will
+// fail to start up given an older (i.e. incompatible) runtime.
+.section .text
+.file "hwasan_tag_mismatch_aarch64.S"
+.global __hwasan_tag_mismatch
+.type __hwasan_tag_mismatch, %function
+__hwasan_tag_mismatch:
+  // Compute the granule position one past the end of the access.
+  mov x16, #1
+  and x17, x1, #0xf
+  lsl x16, x16, x17
+  and x17, x0, #0xf
+  add x17, x16, x17
+
+  // Load the shadow byte again and check whether it is a short tag within the
+  // range of the granule position computed above.
+  ubfx x16, x0, #4, #52
+  ldrb w16, [x9, x16]
+  cmp w16, #0xf
+  b.hi __hwasan_tag_mismatch_v2
+  cmp w16, w17
+  b.lo __hwasan_tag_mismatch_v2
+
+  // Load the real tag from the last byte of the granule and compare against
+  // the pointer tag.
+  orr x16, x0, #0xf
+  ldrb w16, [x16]
+  cmp x16, x0, lsr #56
+  b.ne __hwasan_tag_mismatch_v2
+
+  // Restore x0, x1 and sp to their values from before the __hwasan_tag_mismatch
+  // call and resume execution.
+  ldp x0, x1, [sp], #256
+  ret
+
+.global __hwasan_tag_mismatch_v2
+.type __hwasan_tag_mismatch_v2, %function
+__hwasan_tag_mismatch_v2:
+  CFI_STARTPROC
+
+  // Set the CFA to be the return address for caller of __hwasan_check_*. Note
+  // that we do not emit CFI predicates to describe the contents of this stack
+  // frame, as this proxy entry point should never be debugged. The contents
+  // are static and are handled by the unwinder after calling
+  // __hwasan_tag_mismatch. The frame pointer is already correctly setup
+  // by __hwasan_check_*.
+  add x29, sp, #232
+  CFI_DEF_CFA(w29, 24)
+  CFI_OFFSET(w30, -16)
+  CFI_OFFSET(w29, -24)
+
+  // Save the rest of the registers into the preallocated space left by
+  // __hwasan_check.
+  str     x28,      [sp, #224]
+  stp     x26, x27, [sp, #208]
+  stp     x24, x25, [sp, #192]
+  stp     x22, x23, [sp, #176]
+  stp     x20, x21, [sp, #160]
+  stp     x18, x19, [sp, #144]
+  stp     x16, x17, [sp, #128]
+  stp     x14, x15, [sp, #112]
+  stp     x12, x13, [sp, #96]
+  stp     x10, x11, [sp, #80]
+  stp     x8,  x9,  [sp, #64]
+  stp     x6,  x7,  [sp, #48]
+  stp     x4,  x5,  [sp, #32]
+  stp     x2,  x3,  [sp, #16]
+
+  // Pass the address of the frame to __hwasan_tag_mismatch4, so that it can
+  // extract the saved registers from this frame without having to worry about
+  // finding this frame.
+  mov x2, sp
+
+  bl __hwasan_tag_mismatch4
+  CFI_ENDPROC
+
+.Lfunc_end0:
+  .size __hwasan_tag_mismatch, .Lfunc_end0-__hwasan_tag_mismatch
+
+#endif  // defined(__aarch64__)
+
+// We do not need executable stack.
+NO_EXEC_STACK_DIRECTIVE
diff --git a/libsanitizer/hwasan/hwasan_thread.cpp b/libsanitizer/hwasan/hwasan_thread.cpp
new file mode 100644
index 00000000000..b81a6350c05
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_thread.cpp
@@ -0,0 +1,133 @@
+
+#include "hwasan.h"
+#include "hwasan_mapping.h"
+#include "hwasan_thread.h"
+#include "hwasan_poisoning.h"
+#include "hwasan_interface_internal.h"
+
+#include "sanitizer_common/sanitizer_file.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
+
+
+namespace __hwasan {
+
+static u32 RandomSeed() {
+  u32 seed;
+  do {
+    if (UNLIKELY(!GetRandom(reinterpret_cast<void *>(&seed), sizeof(seed),
+                            /*blocking=*/false))) {
+      seed = static_cast<u32>(
+          (NanoTime() >> 12) ^
+          (reinterpret_cast<uptr>(__builtin_frame_address(0)) >> 4));
+    }
+  } while (!seed);
+  return seed;
+}
+
+void Thread::InitRandomState() {
+  random_state_ = flags()->random_tags ? RandomSeed() : unique_id_;
+
+  // Push a random number of zeros onto the ring buffer so that the first stack
+  // tag base will be random.
+  for (tag_t i = 0, e = GenerateRandomTag(); i != e; ++i)
+    stack_allocations_->push(0);
+}
+
+void Thread::Init(uptr stack_buffer_start, uptr stack_buffer_size) {
+  static u64 unique_id;
+  unique_id_ = unique_id++;
+  if (auto sz = flags()->heap_history_size)
+    heap_allocations_ = HeapAllocationsRingBuffer::New(sz);
+
+  HwasanTSDThreadInit();  // Only needed with interceptors.
+  uptr *ThreadLong = GetCurrentThreadLongPtr();
+  // The following implicitly sets (this) as the current thread.
+  stack_allocations_ = new (ThreadLong)
+      StackAllocationsRingBuffer((void *)stack_buffer_start, stack_buffer_size);
+  // Check that it worked.
+  CHECK_EQ(GetCurrentThread(), this);
+
+  // ScopedTaggingDisable needs GetCurrentThread to be set up.
+  ScopedTaggingDisabler disabler;
+
+  uptr tls_size;
+  uptr stack_size;
+  GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size, &tls_begin_,
+                       &tls_size);
+  stack_top_ = stack_bottom_ + stack_size;
+  tls_end_ = tls_begin_ + tls_size;
+
+  if (stack_bottom_) {
+    int local;
+    CHECK(AddrIsInStack((uptr)&local));
+    CHECK(MemIsApp(stack_bottom_));
+    CHECK(MemIsApp(stack_top_ - 1));
+  }
+
+  if (flags()->verbose_threads) {
+    if (IsMainThread()) {
+      Printf("sizeof(Thread): %zd sizeof(HeapRB): %zd sizeof(StackRB): %zd\n",
+             sizeof(Thread), heap_allocations_->SizeInBytes(),
+             stack_allocations_->size() * sizeof(uptr));
+    }
+    Print("Creating  : ");
+  }
+}
+
+void Thread::ClearShadowForThreadStackAndTLS() {
+  if (stack_top_ != stack_bottom_)
+    TagMemory(stack_bottom_, stack_top_ - stack_bottom_, 0);
+  if (tls_begin_ != tls_end_)
+    TagMemory(tls_begin_, tls_end_ - tls_begin_, 0);
+}
+
+void Thread::Destroy() {
+  if (flags()->verbose_threads)
+    Print("Destroying: ");
+  AllocatorSwallowThreadLocalCache(allocator_cache());
+  ClearShadowForThreadStackAndTLS();
+  if (heap_allocations_)
+    heap_allocations_->Delete();
+  DTLS_Destroy();
+  // Unregister this as the current thread.
+  // Instrumented code can not run on this thread from this point onwards, but
+  // malloc/free can still be served. Glibc may call free() very late, after all
+  // TSD destructors are done.
+  CHECK_EQ(GetCurrentThread(), this);
+  *GetCurrentThreadLongPtr() = 0;
+}
+
+void Thread::Print(const char *Prefix) {
+  Printf("%sT%zd %p stack: [%p,%p) sz: %zd tls: [%p,%p)\n", Prefix,
+         unique_id_, this, stack_bottom(), stack_top(),
+         stack_top() - stack_bottom(),
+         tls_begin(), tls_end());
+}
+
+static u32 xorshift(u32 state) {
+  state ^= state << 13;
+  state ^= state >> 17;
+  state ^= state << 5;
+  return state;
+}
+
+// Generate a (pseudo-)random non-zero tag.
+tag_t Thread::GenerateRandomTag() {
+  if (tagging_disabled_) return 0;
+  tag_t tag;
+  do {
+    if (flags()->random_tags) {
+      if (!random_buffer_)
+        random_buffer_ = random_state_ = xorshift(random_state_);
+      CHECK(random_buffer_);
+      tag = random_buffer_ & 0xFF;
+      random_buffer_ >>= 8;
+    } else {
+      tag = random_state_ = (random_state_ + 1) & 0xFF;
+    }
+  } while (!tag);
+  return tag;
+}
+
+} // namespace __hwasan
diff --git a/libsanitizer/hwasan/hwasan_thread.h b/libsanitizer/hwasan/hwasan_thread.h
new file mode 100644
index 00000000000..ebcdb791fb3
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_thread.h
@@ -0,0 +1,98 @@
+//===-- hwasan_thread.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_THREAD_H
+#define HWASAN_THREAD_H
+
+#include "hwasan_allocator.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_ring_buffer.h"
+
+namespace __hwasan {
+
+typedef __sanitizer::CompactRingBuffer<uptr> StackAllocationsRingBuffer;
+
+class Thread {
+ public:
+  void Init(uptr stack_buffer_start, uptr stack_buffer_size);  // Must be called from the thread itself.
+  void InitRandomState();
+  void Destroy();
+
+  uptr stack_top() { return stack_top_; }
+  uptr stack_bottom() { return stack_bottom_; }
+  uptr stack_size() { return stack_top() - stack_bottom(); }
+  uptr tls_begin() { return tls_begin_; }
+  uptr tls_end() { return tls_end_; }
+  bool IsMainThread() { return unique_id_ == 0; }
+
+  bool AddrIsInStack(uptr addr) {
+    return addr >= stack_bottom_ && addr < stack_top_;
+  }
+
+  AllocatorCache *allocator_cache() { return &allocator_cache_; }
+  HeapAllocationsRingBuffer *heap_allocations() { return heap_allocations_; }
+  StackAllocationsRingBuffer *stack_allocations() { return stack_allocations_; }
+
+  tag_t GenerateRandomTag();
+
+  void DisableTagging() { tagging_disabled_++; }
+  void EnableTagging() { tagging_disabled_--; }
+
+  u64 unique_id() const { return unique_id_; }
+  void Announce() {
+    if (announced_) return;
+    announced_ = true;
+    Print("Thread: ");
+  }
+
+  uptr &vfork_spill() { return vfork_spill_; }
+
+ private:
+  // NOTE: There is no Thread constructor. It is allocated
+  // via mmap() and *must* be valid in zero-initialized state.
+  void ClearShadowForThreadStackAndTLS();
+  void Print(const char *prefix);
+  uptr vfork_spill_;
+  uptr stack_top_;
+  uptr stack_bottom_;
+  uptr tls_begin_;
+  uptr tls_end_;
+
+  u32 random_state_;
+  u32 random_buffer_;
+
+  AllocatorCache allocator_cache_;
+  HeapAllocationsRingBuffer *heap_allocations_;
+  StackAllocationsRingBuffer *stack_allocations_;
+
+  Thread *next_;  // All live threads form a linked list.
+
+  u64 unique_id_;  // counting from zero.
+
+  u32 tagging_disabled_;  // if non-zero, malloc uses zero tag in this thread.
+
+  bool announced_;
+
+  friend struct ThreadListHead;
+};
+
+Thread *GetCurrentThread();
+uptr *GetCurrentThreadLongPtr();
+
+struct ScopedTaggingDisabler {
+  ScopedTaggingDisabler() { GetCurrentThread()->DisableTagging(); }
+  ~ScopedTaggingDisabler() { GetCurrentThread()->EnableTagging(); }
+};
+
+} // namespace __hwasan
+
+#endif // HWASAN_THREAD_H
diff --git a/libsanitizer/hwasan/hwasan_thread_list.cpp b/libsanitizer/hwasan/hwasan_thread_list.cpp
new file mode 100644
index 00000000000..a31eee84ed9
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_thread_list.cpp
@@ -0,0 +1,15 @@
+#include "hwasan_thread_list.h"
+
+namespace __hwasan {
+static ALIGNED(16) char thread_list_placeholder[sizeof(HwasanThreadList)];
+static HwasanThreadList *hwasan_thread_list;
+
+HwasanThreadList &hwasanThreadList() { return *hwasan_thread_list; }
+
+void InitThreadList(uptr storage, uptr size) {
+  CHECK(hwasan_thread_list == nullptr);
+  hwasan_thread_list =
+      new (thread_list_placeholder) HwasanThreadList(storage, size);
+}
+
+} // namespace
diff --git a/libsanitizer/hwasan/hwasan_thread_list.h b/libsanitizer/hwasan/hwasan_thread_list.h
new file mode 100644
index 00000000000..914b632d977
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_thread_list.h
@@ -0,0 +1,215 @@
+//===-- hwasan_thread_list.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+// HwasanThreadList is a registry for live threads, as well as an allocator for
+// HwasanThread objects and their stack history ring buffers. There are
+// constraints on memory layout of the shadow region and CompactRingBuffer that
+// are part of the ABI contract between compiler-rt and llvm.
+//
+// * Start of the shadow memory region is aligned to 2**kShadowBaseAlignment.
+// * All stack ring buffers are located within (2**kShadowBaseAlignment)
+// sized region below and adjacent to the shadow region.
+// * Each ring buffer has a size of (2**N)*4096 where N is in [0, 8), and is
+// aligned to twice its size. The value of N can be different for each buffer.
+//
+// These constrains guarantee that, given an address A of any element of the
+// ring buffer,
+//     A_next = (A + sizeof(uptr)) & ~((1 << (N + 13)) - 1)
+//   is the address of the next element of that ring buffer (with wrap-around).
+// And, with K = kShadowBaseAlignment,
+//     S = (A | ((1 << K) - 1)) + 1
+//   (align up to kShadowBaseAlignment) is the start of the shadow region.
+//
+// These calculations are used in compiler instrumentation to update the ring
+// buffer and obtain the base address of shadow using only two inputs: address
+// of the current element of the ring buffer, and N (i.e. size of the ring
+// buffer). Since the value of N is very limited, we pack both inputs into a
+// single thread-local word as
+//   (1 << (N + 56)) | A
+// See the implementation of class CompactRingBuffer, which is what is stored in
+// said thread-local word.
+//
+// Note the unusual way of aligning up the address of the shadow:
+//   (A | ((1 << K) - 1)) + 1
+// It is only correct if A is not already equal to the shadow base address, but
+// it saves 2 instructions on AArch64.
+
+#include "hwasan.h"
+#include "hwasan_allocator.h"
+#include "hwasan_flags.h"
+#include "hwasan_thread.h"
+
+#include "sanitizer_common/sanitizer_placement_new.h"
+
+namespace __hwasan {
+
+static uptr RingBufferSize() {
+  uptr desired_bytes = flags()->stack_history_size * sizeof(uptr);
+  // FIXME: increase the limit to 8 once this bug is fixed:
+  // https://bugs.llvm.org/show_bug.cgi?id=39030
+  for (int shift = 1; shift < 7; ++shift) {
+    uptr size = 4096 * (1ULL << shift);
+    if (size >= desired_bytes)
+      return size;
+  }
+  Printf("stack history size too large: %d\n", flags()->stack_history_size);
+  CHECK(0);
+  return 0;
+}
+
+struct ThreadListHead {
+  Thread *list_;
+
+  ThreadListHead() : list_(nullptr) {}
+
+  void Push(Thread *t) {
+    t->next_ = list_;
+    list_ = t;
+  }
+
+  Thread *Pop() {
+    Thread *t = list_;
+    if (t)
+      list_ = t->next_;
+    return t;
+  }
+
+  void Remove(Thread *t) {
+    Thread **cur = &list_;
+    while (*cur != t) cur = &(*cur)->next_;
+    CHECK(*cur && "thread not found");
+    *cur = (*cur)->next_;
+  }
+
+  template <class CB>
+  void ForEach(CB cb) {
+    Thread *t = list_;
+    while (t) {
+      cb(t);
+      t = t->next_;
+    }
+  }
+};
+
+struct ThreadStats {
+  uptr n_live_threads;
+  uptr total_stack_size;
+};
+
+class HwasanThreadList {
+ public:
+  HwasanThreadList(uptr storage, uptr size)
+      : free_space_(storage), free_space_end_(storage + size) {
+    // [storage, storage + size) is used as a vector of
+    // thread_alloc_size_-sized, ring_buffer_size_*2-aligned elements.
+    // Each element contains
+    // * a ring buffer at offset 0,
+    // * a Thread object at offset ring_buffer_size_.
+    ring_buffer_size_ = RingBufferSize();
+    thread_alloc_size_ =
+        RoundUpTo(ring_buffer_size_ + sizeof(Thread), ring_buffer_size_ * 2);
+  }
+
+  Thread *CreateCurrentThread() {
+    Thread *t;
+    {
+      SpinMutexLock l(&list_mutex_);
+      t = free_list_.Pop();
+      if (t) {
+        uptr start = (uptr)t - ring_buffer_size_;
+        internal_memset((void *)start, 0, ring_buffer_size_ + sizeof(Thread));
+      } else {
+        t = AllocThread();
+      }
+      live_list_.Push(t);
+    }
+    t->Init((uptr)t - ring_buffer_size_, ring_buffer_size_);
+    AddThreadStats(t);
+    return t;
+  }
+
+  void DontNeedThread(Thread *t) {
+    uptr start = (uptr)t - ring_buffer_size_;
+    ReleaseMemoryPagesToOS(start, start + thread_alloc_size_);
+  }
+
+  void ReleaseThread(Thread *t) {
+    RemoveThreadStats(t);
+    t->Destroy();
+    SpinMutexLock l(&list_mutex_);
+    live_list_.Remove(t);
+    free_list_.Push(t);
+    DontNeedThread(t);
+  }
+
+  Thread *GetThreadByBufferAddress(uptr p) {
+    return (Thread *)(RoundDownTo(p, ring_buffer_size_ * 2) +
+                      ring_buffer_size_);
+  }
+
+  uptr MemoryUsedPerThread() {
+    uptr res = sizeof(Thread) + ring_buffer_size_;
+    if (auto sz = flags()->heap_history_size)
+      res += HeapAllocationsRingBuffer::SizeInBytes(sz);
+    return res;
+  }
+
+  template <class CB>
+  void VisitAllLiveThreads(CB cb) {
+    SpinMutexLock l(&list_mutex_);
+    live_list_.ForEach(cb);
+  }
+
+  void AddThreadStats(Thread *t) {
+    SpinMutexLock l(&stats_mutex_);
+    stats_.n_live_threads++;
+    stats_.total_stack_size += t->stack_size();
+  }
+
+  void RemoveThreadStats(Thread *t) {
+    SpinMutexLock l(&stats_mutex_);
+    stats_.n_live_threads--;
+    stats_.total_stack_size -= t->stack_size();
+  }
+
+  ThreadStats GetThreadStats() {
+    SpinMutexLock l(&stats_mutex_);
+    return stats_;
+  }
+
+ private:
+  Thread *AllocThread() {
+    uptr align = ring_buffer_size_ * 2;
+    CHECK(IsAligned(free_space_, align));
+    Thread *t = (Thread *)(free_space_ + ring_buffer_size_);
+    free_space_ += thread_alloc_size_;
+    CHECK(free_space_ <= free_space_end_ && "out of thread memory");
+    return t;
+  }
+
+  uptr free_space_;
+  uptr free_space_end_;
+  uptr ring_buffer_size_;
+  uptr thread_alloc_size_;
+
+  ThreadListHead free_list_;
+  ThreadListHead live_list_;
+  SpinMutex list_mutex_;
+
+  ThreadStats stats_;
+  SpinMutex stats_mutex_;
+};
+
+void InitThreadList(uptr storage, uptr size);
+HwasanThreadList &hwasanThreadList();
+
+} // namespace
diff --git a/libsanitizer/hwasan/hwasan_type_test.cpp b/libsanitizer/hwasan/hwasan_type_test.cpp
new file mode 100644
index 00000000000..8cff495bae1
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_type_test.cpp
@@ -0,0 +1,25 @@
+//===-- hwasan_type_test.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of HWAddressSanitizer.
+//
+// Compile-time tests of the internal type definitions.
+//===----------------------------------------------------------------------===//
+
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include "hwasan.h"
+#include <setjmp.h>
+
+#define CHECK_TYPE_SIZE_FITS(TYPE) \
+  COMPILER_CHECK(sizeof(__hw_##TYPE) <= sizeof(TYPE))
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+CHECK_TYPE_SIZE_FITS(jmp_buf);
+CHECK_TYPE_SIZE_FITS(sigjmp_buf);
+#endif
diff --git a/libsanitizer/merge.sh b/libsanitizer/merge.sh
index 3f4f1629a22..95ded4f9634 100755
--- a/libsanitizer/merge.sh
+++ b/libsanitizer/merge.sh
@@ -71,6 +71,7 @@ merge lib/tsan/rtl tsan
 merge lib/sanitizer_common sanitizer_common
 merge lib/interception interception
 merge lib/ubsan ubsan
+merge lib/hwasan hwasan
 
 # Need to merge lib/builtins/assembly.h file:
 mkdir -p builtins
-- 
2.29.2


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [Patch 0/X] HWASAN v4
  2020-11-13 16:33         ` Martin Liška
@ 2020-11-13 16:57           ` Matthew Malcomson
  2020-11-13 17:22             ` Martin Liška
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-13 16:57 UTC (permalink / raw)
  To: Martin Liška, gcc-patches
  Cc: jakub, Richard Earnshaw, kcc, dodji, joseph

Hi there,

Thanks for the heads-up.
As it turns out the most recent `libhwasan` crashes when displaying an address on the stack in Linux.

I'm currently working on getting it fixed here https://reviews.llvm.org/D91344#2393371 .
If this hwasan patch series gets approved and if that patch goes in would it be feasible to bump the libsanitizer merge to whatever version that would be?

If not (maybe because stage1 would be finished?) then could/would we end up using the LOCAL_PATCHES approach?

Thanks,
Matthew
________________________________
From: Martin Liška <mliska@suse.cz>
Sent: 13 November 2020 16:33
To: Matthew Malcomson <Matthew.Malcomson@arm.com>; gcc-patches@gcc.gnu.org <gcc-patches@gcc.gnu.org>
Cc: jakub@redhat.com <jakub@redhat.com>; Richard Earnshaw <Richard.Earnshaw@arm.com>; kcc@google.com <kcc@google.com>; dodji@redhat.com <dodji@redhat.com>; joseph@codesourcery.com <joseph@codesourcery.com>
Subject: Re: [Patch 0/X] HWASAN v4

On 10/16/20 11:03 AM, Martin Li�ka wrote:
> Hello.
>
> I've just merged libsanitizer and there's the corresponding part that includes
> libhwasan.
>
> Martin

Hey.

I've just made last merge from upstream, there's corresponding hwasan part.

Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [Patch 0/X] HWASAN v4
  2020-11-13 16:57           ` Matthew Malcomson
@ 2020-11-13 17:22             ` Martin Liška
  2020-11-20 18:42               ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2020-11-13 17:22 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: jakub, Richard Earnshaw, kcc, dodji, joseph

On 11/13/20 5:57 PM, Matthew Malcomson wrote:
> Hi there,
> 
> Thanks for the heads-up.
> As it turns out the most recent `libhwasan` crashes when displaying an address on the stack in Linux.

Hello.

What a bad luck.

> 
> I'm currently working on getting it fixed here https://reviews.llvm.org/D91344#2393371 <https://reviews.llvm.org/D91344#2393371> .
> If this hwasan patch series gets approved and if that patch goes in would it be feasible to bump the libsanitizer merge to whatever version that would be?
> 
> If not (maybe because stage1 would be finished?) then could/would we end up using the LOCAL_PATCHES approach?

Since now, I would prefer doing cherry picks. Hopefully, we'll end just with couple of patches.

Thanks,
Martin

> 
> Thanks,
> Matthew
> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> *From:* Martin Liška <mliska@suse.cz>
> *Sent:* 13 November 2020 16:33
> *To:* Matthew Malcomson <Matthew.Malcomson@arm.com>; gcc-patches@gcc.gnu.org <gcc-patches@gcc.gnu.org>
> *Cc:* jakub@redhat.com <jakub@redhat.com>; Richard Earnshaw <Richard.Earnshaw@arm.com>; kcc@google.com <kcc@google.com>; dodji@redhat.com <dodji@redhat.com>; joseph@codesourcery.com <joseph@codesourcery.com>
> *Subject:* Re: [Patch 0/X] HWASAN v4
> On 10/16/20 11:03 AM, Martin Li�ka wrote:
>> Hello.
>> 
>> I've just merged libsanitizer and there's the corresponding part that includes
>> libhwasan.
>> 
>> Martin
> 
> Hey.
> 
> I've just made last merge from upstream, there's corresponding hwasan part.
> 
> Martin


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Hwasan v5
  2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
  2020-10-16  9:03       ` Martin Liška
@ 2020-11-16 15:36       ` Matthew Malcomson
  2020-11-16 15:37       ` [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system Matthew Malcomson
                         ` (7 subsequent siblings)
  9 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-16 15:36 UTC (permalink / raw)
  To: gcc-patches
  Cc: Richard Earnshaw, Kyrylo Tkachov, Richard Sandiford, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 3499 bytes --]

Hello,

Bootstrapped and regression tested on AArch64
(have not finished running tests on the Linux Kernel yet).
Sending upstream to catch next set of comments early.

This version mainly just consists of requested changes.

Some notable exceptions:

Have rebased onto a newer version, there was a change to `align_local_variable`
that meant some merge conflict resolutions were needed (though no functional
change expected).

Alignment of stack after object:
  In Patch 5/X (for tagging objects on the stack) I realised that in some code
  paths the current method was not ensuring alignment of the stack *after* the
  object in memory for !FRAME_GROWS_DOWNWARDS.
  There is a comment in `expand_stack_vars`, alongside the update.

  Originally we used `align_frame_offset` before space for the object gets
  allocated to ensure the stack is aligned after the object, and
  `alloc_stack_frame_space` with relevant alignment to ensure the stack is
  aligned before the object.

  When !FRAME_GROWS_DOWNWARDS, using `align_frame_offset` before the object
  space is allocated does not ensure alignment of the next object.  Rather
  we must use `align_frame_offset` after space is allocated.

  I now use `align_frame_offset` before *and* after `alloc_stack_frame_space`.
  This looks nicer than putting if conditions based on FRAME_GROWS_DOWNWARDS
  and does not end up allocating any extra space than only calling the align
  function once (it just means the alignment from the `alloc_stack_frame_space`
  is superfluous).

  I still ensure `align_local_variable` returns the correct alignment, even
  though it makes no functional difference.  This seems less likely to cause
  confusion to me.  Similarly, this function still uses `SET_DECL_ALIGN` to
  correctly advertise the alignment that the stack variable is aligned to.
  (N.b. testing does show that leaving `align_local_variable` as it is doesn't
  break anything, which is as expected since the alignment is ensured by the
  calls to `align_frame_offset`).


Ensure Alloca causes hwasan frame base to get initialised.
  In the last version of this patch `expand_HWASAN_CHOOSE_TAG` would use the
  hwasan frame base using `hwasan_base`.  Since this function is used after
  `expand_stack_vars` and hence after `hwasan_emit_prologue` that would not
  cause the initialisation of the frame base to be emitted, but would just use
  the pseudo register without any initialisation.

  This patch records that initialisation should be emitted when creating the
  `HWASAN_CHOOSE_TAG` internal function -- so that even if there are no
  statically allocated local variables the hwasan frame base will be
  initialisied.

  New test was introduced to catch this.


Introduce HWASAN_SET_TAG internal function.
  This was something that popped up during testing the linux kernel with
  stack-tagging.  The __hwasan_tag_pointer interface exported by libhwasan is
  not available in the kernel.  Since the main target of putting this feature
  in GCC is for the kernel we deal with this by emitting code inline to set a
  tag in a pointer.


New test to ensure HWASAN_MARK can handle poly_int sizes.
  The code is designed to handle any variable sized object, but the only sizes
  that could actually be used happen to be poly_int sized variables.

  This is essentially the testcase from pr97696.  HWASAN can handle such size
  types (though ASAN can't) so this should pass for HWASAN.

Thanks,
Matthew
  
Entire patch series attached to cover letter.

[-- Attachment #2: all-patches.tar.gz --]
[-- Type: application/gzip, Size: 66699 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system
  2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
  2020-10-16  9:03       ` Martin Liška
  2020-11-16 15:36       ` Hwasan v5 Matthew Malcomson
@ 2020-11-16 15:37       ` Matthew Malcomson
  2020-11-18 17:03         ` Richard Sandiford
  2020-11-16 15:37       ` [PATCH 2/X] libsanitizer: Only build libhwasan when targeting AArch64 Matthew Malcomson
                         ` (6 subsequent siblings)
  9 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-16 15:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: Richard Earnshaw, Kyrylo Tkachov, Richard Sandiford, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 44074 bytes --]

This patch tries to tie libhwasan into the GCC build system in the same way
that the other sanitizer runtime libraries are handled.

libsanitizer/ChangeLog:

	* Makefile.am:  Build libhwasan.
	* Makefile.in:  Build libhwasan.
	* asan/Makefile.in:  Build libhwasan.
	* configure:  Build libhwasan.
	* configure.ac:  Build libhwasan.
	* hwasan/Makefile.am: New file.
	* hwasan/Makefile.in: New file.
	* hwasan/libtool-version: New file.
	* interception/Makefile.in: Build libhwasan.
	* libbacktrace/Makefile.in: Build libhwasan.
	* libsanitizer.spec.in: Build libhwasan.
	* lsan/Makefile.in: Build libhwasan.
	* sanitizer_common/Makefile.in: Build libhwasan.
	* tsan/Makefile.in: Build libhwasan.
	* ubsan/Makefile.in: Build libhwasan.



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


diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 65ed1e712378ef453f820f86c4d3221f9dee5f2c..2a7e8e1debe838719db0f0fad218b2543cc3111b 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,11 +14,12 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan
+SUBDIRS += lsan asan ubsan hwasan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
-  include/sanitizer/tsan_interface.h
+  include/sanitizer/tsan_interface.h \
+  include/sanitizer/hwasan_interface.h
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 02c7f70ac6578a3e93a490ce8bd2c54fc0693c50..2c57d49cbffdb486645aeb5f2c0f85d6e0fad124 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -92,7 +92,8 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@am__append_1 = include/sanitizer/common_interface_defs.h \
 @SANITIZER_SUPPORTED_TRUE@	include/sanitizer/lsan_interface.h \
 @SANITIZER_SUPPORTED_TRUE@	include/sanitizer/asan_interface.h \
-@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/tsan_interface.h
+@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/tsan_interface.h \
+@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/hwasan_interface.h
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
@@ -207,7 +208,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan tsan
+	ubsan hwasan tsan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -329,6 +330,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
@@ -362,7 +364,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	$(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in
index 29622bf466a37f819c9fade30e31195adda51190..25c7fd7b7597d6e243005a1bb7de5b6243d2cfcf 100644
--- a/libsanitizer/asan/Makefile.in
+++ b/libsanitizer/asan/Makefile.in
@@ -383,6 +383,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/configure b/libsanitizer/configure
index 04eca04fbe5e59bae1ba00597de0cf1b7cf1b5fa..27e72c089cb891dcce09494fa9e39eebe55d2598 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -657,6 +657,7 @@ USING_MAC_INTERPOSE_TRUE
 link_liblsan
 link_libubsan
 link_libtsan
+link_libhwasan
 link_libasan
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
@@ -12361,7 +12362,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12364 "configure"
+#line 12365 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12467,7 +12468,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12470 "configure"
+#line 12471 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15943,6 +15944,10 @@ fi
 link_libasan=$link_sanitizer_common
 
 
+# Set up the set of additional libraries that we need to link against for libhwasan.
+link_libhwasan=$link_sanitizer_common
+
+
 # Set up the set of additional libraries that we need to link against for libtsan.
 link_libtsan=$link_sanitizer_common
 
@@ -16813,7 +16818,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -16862,7 +16867,7 @@ case "$host" in
     case "$enable_cet" in
       auto)
 	# Check if target supports multi-byte NOPs
-	# and if assembler supports CET insn.
+	# and if compiler and assembler support CET insn.
 	cet_save_CFLAGS="$CFLAGS"
 	CFLAGS="$CFLAGS -fcf-protection"
 	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -18094,6 +18099,7 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
 
@@ -20029,6 +20035,17 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+   sed -f vpsed$$ $ac_file > tmp$$
+   mv tmp$$ $ac_file
+   rm vpsed$$
+   echo 'MULTISUBDIR =' >> $ac_file
+   ml_norecursion=yes
+   . ${multi_basedir}/config-ml.in
+   { ml_norecursion=; unset ml_norecursion;}
+ ;;
     "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index a0950c29ebe2c0bf862ffe08f9a441c554da3220..f0d526c52967112905115c8fbe3540427a27ae03 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -120,6 +120,10 @@ AC_CHECK_LIB(dl, dlsym,
 link_libasan=$link_sanitizer_common
 AC_SUBST(link_libasan)
 
+# Set up the set of additional libraries that we need to link against for libhwasan.
+link_libhwasan=$link_sanitizer_common
+AC_SUBST(link_libhwasan)
+
 # Set up the set of additional libraries that we need to link against for libtsan.
 link_libtsan=$link_sanitizer_common
 AC_SUBST(link_libtsan)
@@ -348,7 +352,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
diff --git a/libsanitizer/hwasan/Makefile.am b/libsanitizer/hwasan/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..2226f68ab29e109e592c51f4064311fcb5a177d2
--- /dev/null
+++ b/libsanitizer/hwasan/Makefile.am
@@ -0,0 +1,89 @@
+AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
+
+# May be used by toolexeclibdir.
+gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
+
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fno-rtti -funwind-tables -fvisibility=hidden -Wno-variadic-macros -fno-ipa-icf
+AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+AM_CXXFLAGS += -std=gnu++14
+AM_CXXFLAGS += $(EXTRA_CXXFLAGS)
+ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
+
+toolexeclib_LTLIBRARIES = libhwasan.la
+
+hwasan_files = \
+	hwasan_allocator.cpp \
+	hwasan.cpp \
+	hwasan_dynamic_shadow.cpp \
+	hwasan_exceptions.cpp \
+	hwasan_flags.inc \
+	hwasan_globals.cpp \
+	hwasan_interceptors.cpp \
+	hwasan_interceptors_vfork.S \
+	hwasan_linux.cpp \
+	hwasan_memintrinsics.cpp \
+	hwasan_new_delete.cpp \
+	hwasan_poisoning.cpp \
+	hwasan_report.cpp \
+	hwasan_setjmp.S \
+	hwasan_tag_mismatch_aarch64.S \
+	hwasan_thread.cpp \
+	hwasan_thread_list.cpp \
+	hwasan_type_test.cpp
+
+libhwasan_la_SOURCES = $(hwasan_files)
+libhwasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la
+if !USING_MAC_INTERPOSE
+libhwasan_la_LIBADD += $(top_builddir)/interception/libinterception.la
+endif
+if LIBBACKTRACE_SUPPORTED
+libhwasan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
+endif
+libhwasan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+
+libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan)
+
+# Work around what appears to be a GNU make bug handling MAKEFLAGS
+# values defined in terms of make variables, as is the case for CC and
+# friends when we are called from the top level Makefile.
+AM_MAKEFLAGS = \
+	"AR_FLAGS=$(AR_FLAGS)" \
+	"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
+	"CFLAGS=$(CFLAGS)" \
+	"CXXFLAGS=$(CXXFLAGS)" \
+	"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
+	"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
+	"INSTALL=$(INSTALL)" \
+	"INSTALL_DATA=$(INSTALL_DATA)" \
+	"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
+	"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
+	"JC1FLAGS=$(JC1FLAGS)" \
+	"LDFLAGS=$(LDFLAGS)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
+	"MAKE=$(MAKE)" \
+	"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
+	"PICFLAG=$(PICFLAG)" \
+	"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
+	"SHELL=$(SHELL)" \
+	"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
+	"exec_prefix=$(exec_prefix)" \
+	"infodir=$(infodir)" \
+	"libdir=$(libdir)" \
+	"prefix=$(prefix)" \
+	"includedir=$(includedir)" \
+	"AR=$(AR)" \
+	"AS=$(AS)" \
+	"LD=$(LD)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"NM=$(NM)" \
+	"PICFLAG=$(PICFLAG)" \
+	"RANLIB=$(RANLIB)" \
+	"DESTDIR=$(DESTDIR)"
+
+MAKEOVERRIDES=
+
+## ################################################################
+
+
diff --git a/libsanitizer/hwasan/Makefile.in b/libsanitizer/hwasan/Makefile.in
new file mode 100644
index 0000000000000000000000000000000000000000..542af8f193e0d460de93dff7b249edb949be8a67
--- /dev/null
+++ b/libsanitizer/hwasan/Makefile.in
@@ -0,0 +1,803 @@
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+@USING_MAC_INTERPOSE_FALSE@am__append_1 = $(top_builddir)/interception/libinterception.la
+@LIBBACKTRACE_SUPPORTED_TRUE@am__append_2 = $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
+subdir = hwasan
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
+	$(top_srcdir)/../config/depstand.m4 \
+	$(top_srcdir)/../config/lead-dot.m4 \
+	$(top_srcdir)/../config/libstdc++-raw-cxx.m4 \
+	$(top_srcdir)/../config/multi.m4 \
+	$(top_srcdir)/../config/override.m4 \
+	$(top_srcdir)/../config/stdint.m4 \
+	$(top_srcdir)/../config/toolexeclibdir.m4 \
+	$(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \
+	$(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \
+	$(top_srcdir)/acinclude.m4 $(top_srcdir)/../libtool.m4 \
+	$(top_srcdir)/../config/enable.m4 \
+	$(top_srcdir)/../config/cet.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am
+mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(toolexeclibdir)"
+LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libhwasan_la_DEPENDENCIES =  \
+	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
+	$(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1)
+am__objects_1 = hwasan_allocator.lo hwasan.lo hwasan_dynamic_shadow.lo \
+	hwasan_exceptions.lo hwasan_globals.lo hwasan_interceptors.lo \
+	hwasan_interceptors_vfork.lo hwasan_linux.lo \
+	hwasan_memintrinsics.lo hwasan_new_delete.lo \
+	hwasan_poisoning.lo hwasan_report.lo hwasan_setjmp.lo \
+	hwasan_tag_mismatch_aarch64.lo hwasan_thread.lo \
+	hwasan_thread_list.lo hwasan_type_test.lo
+am_libhwasan_la_OBJECTS = $(am__objects_1)
+libhwasan_la_OBJECTS = $(am_libhwasan_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libhwasan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+	$(CXXFLAGS) $(libhwasan_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/../depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS)
+LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CCAS) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CCASFLAGS) $(CCASFLAGS)
+AM_V_CPPAS = $(am__v_CPPAS_@AM_V@)
+am__v_CPPAS_ = $(am__v_CPPAS_@AM_DEFAULT_V@)
+am__v_CPPAS_0 = @echo "  CPPAS   " $@;
+am__v_CPPAS_1 = 
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo "  CXX     " $@;
+am__v_CXX_1 = 
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+	$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo "  CXXLD   " $@;
+am__v_CXXLD_1 = 
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libhwasan_la_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+ACLOCAL = @ACLOCAL@
+ALLOC_FILE = @ALLOC_FILE@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BACKTRACE_SUPPORTED = @BACKTRACE_SUPPORTED@
+BACKTRACE_SUPPORTS_THREADS = @BACKTRACE_SUPPORTS_THREADS@
+BACKTRACE_USES_MALLOC = @BACKTRACE_USES_MALLOC@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXTRA_ASFLAGS = @EXTRA_ASFLAGS@
+EXTRA_CFLAGS = @EXTRA_CFLAGS@
+EXTRA_CXXFLAGS = @EXTRA_CXXFLAGS@
+FGREP = @FGREP@
+FORMAT_FILE = @FORMAT_FILE@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSTDCXX_RAW_CXX_CXXFLAGS = @LIBSTDCXX_RAW_CXX_CXXFLAGS@
+LIBSTDCXX_RAW_CXX_LDFLAGS = @LIBSTDCXX_RAW_CXX_LDFLAGS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
+SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS = @SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
+VERSION = @VERSION@
+VIEW_FILE = @VIEW_FILE@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+enable_shared = @enable_shared@
+enable_static = @enable_static@
+exec_prefix = @exec_prefix@
+get_gcc_base_ver = @get_gcc_base_ver@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
+link_liblsan = @link_liblsan@
+link_libtsan = @link_libtsan@
+link_libubsan = @link_libubsan@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+multi_basedir = @multi_basedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_noncanonical = @target_noncanonical@
+target_os = @target_os@
+target_vendor = @target_vendor@
+toolexecdir = @toolexecdir@
+toolexeclibdir = @toolexeclibdir@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
+
+# May be used by toolexeclibdir.
+gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
+	-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
+	-funwind-tables -fvisibility=hidden -Wno-variadic-macros \
+	-fno-ipa-icf $(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=gnu++14 \
+	$(EXTRA_CXXFLAGS)
+ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
+toolexeclib_LTLIBRARIES = libhwasan.la
+hwasan_files = \
+	hwasan_allocator.cpp \
+	hwasan.cpp \
+	hwasan_dynamic_shadow.cpp \
+	hwasan_exceptions.cpp \
+	hwasan_flags.inc \
+	hwasan_globals.cpp \
+	hwasan_interceptors.cpp \
+	hwasan_interceptors_vfork.S \
+	hwasan_linux.cpp \
+	hwasan_memintrinsics.cpp \
+	hwasan_new_delete.cpp \
+	hwasan_poisoning.cpp \
+	hwasan_report.cpp \
+	hwasan_setjmp.S \
+	hwasan_tag_mismatch_aarch64.S \
+	hwasan_thread.cpp \
+	hwasan_thread_list.cpp \
+	hwasan_type_test.cpp
+
+libhwasan_la_SOURCES = $(hwasan_files)
+libhwasan_la_LIBADD =  \
+	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
+	$(am__append_1) $(am__append_2) $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan)
+
+# Work around what appears to be a GNU make bug handling MAKEFLAGS
+# values defined in terms of make variables, as is the case for CC and
+# friends when we are called from the top level Makefile.
+AM_MAKEFLAGS = \
+	"AR_FLAGS=$(AR_FLAGS)" \
+	"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
+	"CFLAGS=$(CFLAGS)" \
+	"CXXFLAGS=$(CXXFLAGS)" \
+	"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
+	"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
+	"INSTALL=$(INSTALL)" \
+	"INSTALL_DATA=$(INSTALL_DATA)" \
+	"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
+	"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
+	"JC1FLAGS=$(JC1FLAGS)" \
+	"LDFLAGS=$(LDFLAGS)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
+	"MAKE=$(MAKE)" \
+	"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
+	"PICFLAG=$(PICFLAG)" \
+	"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
+	"SHELL=$(SHELL)" \
+	"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
+	"exec_prefix=$(exec_prefix)" \
+	"infodir=$(infodir)" \
+	"libdir=$(libdir)" \
+	"prefix=$(prefix)" \
+	"includedir=$(includedir)" \
+	"AR=$(AR)" \
+	"AS=$(AS)" \
+	"LD=$(LD)" \
+	"LIBCFLAGS=$(LIBCFLAGS)" \
+	"NM=$(NM)" \
+	"PICFLAG=$(PICFLAG)" \
+	"RANLIB=$(RANLIB)" \
+	"DESTDIR=$(DESTDIR)"
+
+MAKEOVERRIDES = 
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .S .cpp .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign hwasan/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign hwasan/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-toolexeclibLTLIBRARIES: $(toolexeclib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(toolexeclibdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(toolexeclibdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(toolexeclibdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(toolexeclibdir)"; \
+	}
+
+uninstall-toolexeclibLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(toolexeclibdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(toolexeclibdir)/$$f"; \
+	done
+
+clean-toolexeclibLTLIBRARIES:
+	-test -z "$(toolexeclib_LTLIBRARIES)" || rm -f $(toolexeclib_LTLIBRARIES)
+	@list='$(toolexeclib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libhwasan.la: $(libhwasan_la_OBJECTS) $(libhwasan_la_DEPENDENCIES) $(EXTRA_libhwasan_la_DEPENDENCIES) 
+	$(AM_V_CXXLD)$(libhwasan_la_LINK) -rpath $(toolexeclibdir) $(libhwasan_la_OBJECTS) $(libhwasan_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_allocator.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_dynamic_shadow.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_exceptions.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_globals.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors_vfork.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_linux.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_memintrinsics.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_new_delete.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_poisoning.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_report.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_setjmp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_tag_mismatch_aarch64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread_list.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_type_test.Plo@am__quote@
+
+.S.o:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ $<
+
+.S.obj:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.S.lo:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(LTCPPASCOMPILE) -c -o $@ $<
+
+.cpp.o:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+	for dir in "$(DESTDIR)$(toolexeclibdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-toolexeclibLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-toolexeclibLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-toolexeclibLTLIBRARIES cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags dvi dvi-am html html-am info \
+	info-am install install-am install-data install-data-am \
+	install-dvi install-dvi-am install-exec install-exec-am \
+	install-html install-html-am install-info install-info-am \
+	install-man install-pdf install-pdf-am install-ps \
+	install-ps-am install-strip install-toolexeclibLTLIBRARIES \
+	installcheck installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am \
+	uninstall-toolexeclibLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libsanitizer/hwasan/libtool-version b/libsanitizer/hwasan/libtool-version
new file mode 100644
index 0000000000000000000000000000000000000000..d1f9aa1b950cc95efea87b7a70e96e44df0f9c17
--- /dev/null
+++ b/libsanitizer/hwasan/libtool-version
@@ -0,0 +1,6 @@
+# This file is used to maintain libtool version info for libhwasan.  See
+# the libtool manual to understand the meaning of the fields.  This is
+# a separate file so that version updates don't involve re-running
+# automake.
+# CURRENT:REVISION:AGE
+0:0:0
diff --git a/libsanitizer/interception/Makefile.in b/libsanitizer/interception/Makefile.in
index a20f52ea4b825e733b2c0ca24780e766ad69367d..4a872cb49690e7b739ad07d73a517849be2ba1ea 100644
--- a/libsanitizer/interception/Makefile.in
+++ b/libsanitizer/interception/Makefile.in
@@ -301,6 +301,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/libbacktrace/Makefile.in b/libsanitizer/libbacktrace/Makefile.in
index c843bcee0e4d39d788470d2d03b53d2f817aa231..3f05cdf3d621be74ee6346852c80b71f885e1203 100644
--- a/libsanitizer/libbacktrace/Makefile.in
+++ b/libsanitizer/libbacktrace/Makefile.in
@@ -351,6 +351,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/libsanitizer.spec.in b/libsanitizer/libsanitizer.spec.in
index a4fa87d8dfc51549b0f932b31a610db1a2e86c29..70a33574d7b1d494c4875cfc41f29606bde2172e 100644
--- a/libsanitizer/libsanitizer.spec.in
+++ b/libsanitizer/libsanitizer.spec.in
@@ -3,6 +3,8 @@
 
 *link_libasan: @link_libasan@
 
+*link_libhwasan: @link_libhwasan@
+
 *link_libtsan: @link_libtsan@
 
 *link_libubsan: @link_libubsan@
diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in
index 23a6fa69ec6941ec8a3f02f6a283b8015a8f706f..fbe87cd24961f0ca2d338b43c9d81af709a02802 100644
--- a/libsanitizer/lsan/Makefile.in
+++ b/libsanitizer/lsan/Makefile.in
@@ -346,6 +346,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in
index 299de50b34a6c1cbb33d6f3536d8df1a132d2f59..7e5555cc9df53c5b081313d22ffc532adbe967a5 100644
--- a/libsanitizer/sanitizer_common/Makefile.in
+++ b/libsanitizer/sanitizer_common/Makefile.in
@@ -338,6 +338,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in
index bfbdfe0d89c635c7ca1fe246722ec04949002f1b..ce11d2497b75a306f80f1701110b5eefc5330318 100644
--- a/libsanitizer/tsan/Makefile.in
+++ b/libsanitizer/tsan/Makefile.in
@@ -375,6 +375,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@
diff --git a/libsanitizer/ubsan/Makefile.in b/libsanitizer/ubsan/Makefile.in
index 312d6a5e5137ab64faef9a3f0f44ae3284c23e1b..2775c1422137d46d0d68a0a516826e6d62daf137 100644
--- a/libsanitizer/ubsan/Makefile.in
+++ b/libsanitizer/ubsan/Makefile.in
@@ -340,6 +340,7 @@ install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
 link_libasan = @link_libasan@
+link_libhwasan = @link_libhwasan@
 link_liblsan = @link_liblsan@
 link_libtsan = @link_libtsan@
 link_libubsan = @link_libubsan@


[-- Attachment #2: hwasan-v50.patch.gz --]
[-- Type: application/gzip, Size: 10747 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 2/X] libsanitizer:  Only build libhwasan when targeting AArch64
  2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
                         ` (2 preceding siblings ...)
  2020-11-16 15:37       ` [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system Matthew Malcomson
@ 2020-11-16 15:37       ` Matthew Malcomson
  2020-11-18 17:06         ` Richard Sandiford
  2020-11-16 15:37       ` [PATCH 3/X] libsanitizer: Add option to bootstrap using HWASAN Matthew Malcomson
                         ` (5 subsequent siblings)
  9 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-16 15:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: Richard Earnshaw, Kyrylo Tkachov, Richard Sandiford, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 9479 bytes --]

Though the library has limited support for x86, we don't have any
support for generating code targeting x86 so there is no point building
for that target.

Ensure we build for AArch64 but not for AArch64 ilp32.

libsanitizer/ChangeLog:

	* Makefile.am: Condition Build hwasan directory.
	* Makefile.in: Regenerate.
	* configure: Regenerate.
	* configure.ac: Set HWASAN_SUPPORTED based on target
	architecture.
	* configure.tgt: Likewise.



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


diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 2a7e8e1debe838719db0f0fad218b2543cc3111b..065a65e78d49f7689a01ecb64db1f07ca83aa987 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,7 +14,7 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan hwasan
+SUBDIRS += lsan asan ubsan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
@@ -23,6 +23,9 @@ nodist_saninclude_HEADERS += \
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
+if HWASAN_SUPPORTED
+SUBDIRS += hwasan
+endif
 endif
 
 ## May be used by toolexeclibdir.
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 2c57d49cbffdb486645aeb5f2c0f85d6e0fad124..3873ea4d7050f04a3f7bbd0dd3f2a71e9b65d287 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -97,6 +97,7 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
+@HWASAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_5 = hwasan
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
@@ -208,7 +209,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan hwasan tsan
+	ubsan tsan hwasan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -364,7 +365,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	$(am__append_4) $(am__append_5)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/configure b/libsanitizer/configure
index 27e72c089cb891dcce09494fa9e39eebe55d2598..720d4e17044170e4b91c42fede685761d98c1965 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -659,6 +659,8 @@ link_libubsan
 link_libtsan
 link_libhwasan
 link_libasan
+HWASAN_SUPPORTED_FALSE
+HWASAN_SUPPORTED_TRUE
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
 TSAN_SUPPORTED_FALSE
@@ -12362,7 +12364,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12365 "configure"
+#line 12367 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12468,7 +12470,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12471 "configure"
+#line 12473 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15819,6 +15821,7 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
  if test "x$TSAN_SUPPORTED" = "xyes"; then
   TSAN_SUPPORTED_TRUE=
@@ -15836,6 +15839,14 @@ else
   LSAN_SUPPORTED_FALSE=
 fi
 
+ if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  HWASAN_SUPPORTED_TRUE=
+  HWASAN_SUPPORTED_FALSE='#'
+else
+  HWASAN_SUPPORTED_TRUE='#'
+  HWASAN_SUPPORTED_FALSE=
+fi
+
 
 # Check for functions needed.
 for ac_func in clock_getres clock_gettime clock_settime lstat readlink
@@ -16818,7 +16829,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -16826,6 +16837,11 @@ if test "x$TSAN_SUPPORTED" = "xyes"; then
 
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  ac_config_files="$ac_config_files hwasan/Makefile"
+
+fi
+
 
 
 
@@ -17090,6 +17106,10 @@ if test -z "${LSAN_SUPPORTED_TRUE}" && test -z "${LSAN_SUPPORTED_FALSE}"; then
   as_fn_error $? "conditional \"LSAN_SUPPORTED\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HWASAN_SUPPORTED_TRUE}" && test -z "${HWASAN_SUPPORTED_FALSE}"; then
+  as_fn_error $? "conditional \"HWASAN_SUPPORTED\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${USING_MAC_INTERPOSE_TRUE}" && test -z "${USING_MAC_INTERPOSE_FALSE}"; then
   as_fn_error $? "conditional \"USING_MAC_INTERPOSE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -18099,9 +18119,9 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
-    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
@@ -20035,7 +20055,7 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
    sed -f vpsed$$ $ac_file > tmp$$
@@ -20046,18 +20066,18 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
-   sed -f vpsed$$ $ac_file > tmp$$
-   mv tmp$$ $ac_file
-   rm vpsed$$
-   echo 'MULTISUBDIR =' >> $ac_file
-   ml_norecursion=yes
-   . ${multi_basedir}/config-ml.in
-   { ml_norecursion=; unset ml_norecursion;}
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
     sed -f vpsed$$ $ac_file > tmp$$
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index f0d526c52967112905115c8fbe3540427a27ae03..13cd302030db7d0107ac8de0863d42558dea5c71 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -96,9 +96,11 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
 AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
 AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"])
+AM_CONDITIONAL(HWASAN_SUPPORTED, [test "x$HWASAN_SUPPORTED" = "xyes"])
 
 # Check for functions needed.
 AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime lstat readlink)
@@ -352,7 +354,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
@@ -380,6 +382,21 @@ _EOF
 ])
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  AC_CONFIG_FILES(AC_FOREACH([DIR], [hwasan], [DIR/Makefile ]), 
+    [cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    AS_UNSET([ml_norecursion])
+])
+fi
+
 AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS])
 AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS])
 
diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt
index 52503f1a880ba08b515b8a429ac44a262873f74b..d5b8dc4ec198c2a73dcef530a6e0d6a227eb30ee 100644
--- a/libsanitizer/configure.tgt
+++ b/libsanitizer/configure.tgt
@@ -60,6 +60,7 @@ case "${target}" in
 		TSAN_SUPPORTED=yes
 		LSAN_SUPPORTED=yes
 		TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
+		HWASAN_SUPPORTED=yes
 	fi
 	;;
   x86_64-*-darwin1[2-9]* | i?86-*-darwin1[2-9]*)


[-- Attachment #2: hwasan-v51.patch --]
[-- Type: text/plain, Size: 8955 bytes --]

diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
index 2a7e8e1debe838719db0f0fad218b2543cc3111b..065a65e78d49f7689a01ecb64db1f07ca83aa987 100644
--- a/libsanitizer/Makefile.am
+++ b/libsanitizer/Makefile.am
@@ -14,7 +14,7 @@ endif
 if LIBBACKTRACE_SUPPORTED
 SUBDIRS += libbacktrace
 endif
-SUBDIRS += lsan asan ubsan hwasan
+SUBDIRS += lsan asan ubsan
 nodist_saninclude_HEADERS += \
   include/sanitizer/lsan_interface.h \
   include/sanitizer/asan_interface.h \
@@ -23,6 +23,9 @@ nodist_saninclude_HEADERS += \
 if TSAN_SUPPORTED
 SUBDIRS += tsan
 endif
+if HWASAN_SUPPORTED
+SUBDIRS += hwasan
+endif
 endif
 
 ## May be used by toolexeclibdir.
diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
index 2c57d49cbffdb486645aeb5f2c0f85d6e0fad124..3873ea4d7050f04a3f7bbd0dd3f2a71e9b65d287 100644
--- a/libsanitizer/Makefile.in
+++ b/libsanitizer/Makefile.in
@@ -97,6 +97,7 @@ target_triplet = @target@
 @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
 @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
 @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
+@HWASAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_5 = hwasan
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
@@ -208,7 +209,7 @@ ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
-	ubsan hwasan tsan
+	ubsan tsan hwasan
 ACLOCAL = @ACLOCAL@
 ALLOC_FILE = @ALLOC_FILE@
 AMTAR = @AMTAR@
@@ -364,7 +365,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
 nodist_saninclude_HEADERS = $(am__append_1)
 @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
 @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
-@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
+@SANITIZER_SUPPORTED_TRUE@	$(am__append_4) $(am__append_5)
 gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
diff --git a/libsanitizer/configure b/libsanitizer/configure
index 27e72c089cb891dcce09494fa9e39eebe55d2598..720d4e17044170e4b91c42fede685761d98c1965 100755
--- a/libsanitizer/configure
+++ b/libsanitizer/configure
@@ -659,6 +659,8 @@ link_libubsan
 link_libtsan
 link_libhwasan
 link_libasan
+HWASAN_SUPPORTED_FALSE
+HWASAN_SUPPORTED_TRUE
 LSAN_SUPPORTED_FALSE
 LSAN_SUPPORTED_TRUE
 TSAN_SUPPORTED_FALSE
@@ -12362,7 +12364,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12365 "configure"
+#line 12367 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12468,7 +12470,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12471 "configure"
+#line 12473 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15819,6 +15821,7 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
  if test "x$TSAN_SUPPORTED" = "xyes"; then
   TSAN_SUPPORTED_TRUE=
@@ -15836,6 +15839,14 @@ else
   LSAN_SUPPORTED_FALSE=
 fi
 
+ if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  HWASAN_SUPPORTED_TRUE=
+  HWASAN_SUPPORTED_FALSE='#'
+else
+  HWASAN_SUPPORTED_TRUE='#'
+  HWASAN_SUPPORTED_FALSE=
+fi
+
 
 # Check for functions needed.
 for ac_func in clock_getres clock_gettime clock_settime lstat readlink
@@ -16818,7 +16829,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
 ac_config_headers="$ac_config_headers config.h"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -16826,6 +16837,11 @@ if test "x$TSAN_SUPPORTED" = "xyes"; then
 
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  ac_config_files="$ac_config_files hwasan/Makefile"
+
+fi
+
 
 
 
@@ -17090,6 +17106,10 @@ if test -z "${LSAN_SUPPORTED_TRUE}" && test -z "${LSAN_SUPPORTED_FALSE}"; then
   as_fn_error $? "conditional \"LSAN_SUPPORTED\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HWASAN_SUPPORTED_TRUE}" && test -z "${HWASAN_SUPPORTED_FALSE}"; then
+  as_fn_error $? "conditional \"HWASAN_SUPPORTED\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${USING_MAC_INTERPOSE_TRUE}" && test -z "${USING_MAC_INTERPOSE_FALSE}"; then
   as_fn_error $? "conditional \"USING_MAC_INTERPOSE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -18099,9 +18119,9 @@ do
     "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
     "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
-    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
     "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
+    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
@@ -20035,7 +20055,7 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
+    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
    sed -f vpsed$$ $ac_file > tmp$$
@@ -20046,18 +20066,18 @@ _EOF
    . ${multi_basedir}/config-ml.in
    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
-   sed -f vpsed$$ $ac_file > tmp$$
-   mv tmp$$ $ac_file
-   rm vpsed$$
-   echo 'MULTISUBDIR =' >> $ac_file
-   ml_norecursion=yes
-   . ${multi_basedir}/config-ml.in
-   { ml_norecursion=; unset ml_norecursion;}
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    { ml_norecursion=; unset ml_norecursion;}
  ;;
-    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
+    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
     sed -f vpsed$$ $ac_file > tmp$$
diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
index f0d526c52967112905115c8fbe3540427a27ae03..13cd302030db7d0107ac8de0863d42558dea5c71 100644
--- a/libsanitizer/configure.ac
+++ b/libsanitizer/configure.ac
@@ -96,9 +96,11 @@ fi
 # Get target configury.
 unset TSAN_SUPPORTED
 unset LSAN_SUPPORTED
+unset HWASAN_SUPPORTED
 . ${srcdir}/configure.tgt
 AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
 AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"])
+AM_CONDITIONAL(HWASAN_SUPPORTED, [test "x$HWASAN_SUPPORTED" = "xyes"])
 
 # Check for functions needed.
 AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime lstat readlink)
@@ -352,7 +354,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
 AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
 AC_CONFIG_HEADER(config.h)
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
@@ -380,6 +382,21 @@ _EOF
 ])
 fi
 
+if test "x$HWASAN_SUPPORTED" = "xyes"; then
+  AC_CONFIG_FILES(AC_FOREACH([DIR], [hwasan], [DIR/Makefile ]), 
+    [cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+    sed -f vpsed$$ $ac_file > tmp$$
+    mv tmp$$ $ac_file
+    rm vpsed$$
+    echo 'MULTISUBDIR =' >> $ac_file
+    ml_norecursion=yes
+    . ${multi_basedir}/config-ml.in
+    AS_UNSET([ml_norecursion])
+])
+fi
+
 AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS])
 AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS])
 
diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt
index 52503f1a880ba08b515b8a429ac44a262873f74b..d5b8dc4ec198c2a73dcef530a6e0d6a227eb30ee 100644
--- a/libsanitizer/configure.tgt
+++ b/libsanitizer/configure.tgt
@@ -60,6 +60,7 @@ case "${target}" in
 		TSAN_SUPPORTED=yes
 		LSAN_SUPPORTED=yes
 		TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
+		HWASAN_SUPPORTED=yes
 	fi
 	;;
   x86_64-*-darwin1[2-9]* | i?86-*-darwin1[2-9]*)


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 3/X] libsanitizer:  Add option to bootstrap using HWASAN
  2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
                         ` (3 preceding siblings ...)
  2020-11-16 15:37       ` [PATCH 2/X] libsanitizer: Only build libhwasan when targeting AArch64 Matthew Malcomson
@ 2020-11-16 15:37       ` Matthew Malcomson
  2020-11-18 17:09         ` Richard Sandiford
  2020-11-16 15:37       ` [PATCH 4/X] libsanitizer: options: Add hwasan flags and argument parsing Matthew Malcomson
                         ` (4 subsequent siblings)
  9 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-16 15:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: Richard Earnshaw, Kyrylo Tkachov, Richard Sandiford, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 5481 bytes --]

This is an analogous option to --bootstrap-asan to configure.  It allows
bootstrapping GCC using HWASAN.

For the same reasons as for ASAN we have to avoid using the HWASAN
sanitizer when compiling libiberty and the lto-plugin.

Also add a function to query whether -fsanitize=hwaddress has been
passed.

ChangeLog:

	* configure: Regenerate.
	* configure.ac: Add --bootstrap-hwasan option.

config/ChangeLog:

	* bootstrap-hwasan.mk: New file.

gcc/ChangeLog:

	* doc/install.texi: Document new option.

libiberty/ChangeLog:

	* configure: Regenerate.
	* configure.ac: Avoid using sanitizer.

lto-plugin/ChangeLog:

	* Makefile.am: Avoid using sanitizer.
	* Makefile.in: Regenerate.



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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index a2ea1a329b69de06906315e54a49c694c9704522..b41a258c80ee9f289de534185eb364bcb5ca6ae5 100755
--- a/configure
+++ b/configure
@@ -9305,7 +9305,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index 44fa75f3a329ef68f6800c8e09d49a9373f731cf..944f30cfea84e9266b4322df7902b867882d4d8c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2814,7 +2814,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index 60ee0a9dba17bf8f00d7c5320468ba847f08f8a0..e0f75b3d55582b5d1b8718f94e39c7d50656a926 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2794,6 +2794,11 @@ the build tree.
 Compiles GCC itself using Address Sanitization in order to catch invalid memory
 accesses within the GCC code.
 
+@item @samp{bootstrap-hwasan}
+Compiles GCC itself using HWAddress Sanitization in order to catch invalid
+memory accesses within the GCC code.  This option is only available on AArch64
+systems that are running Linux kernel version 5.4 or later.
+
 @end table
 
 @section Building a cross compiler
diff --git a/libiberty/configure b/libiberty/configure
index ff93c9ee9a6fa9c6bb1938bcdfffb2d0ae8c9698..b6af9baf21204a323cad0e7b40a426c72988ba3b 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5264,6 +5264,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index 4e2599c14a89bafcb8c7e523b9ce5b3d60b8c0f6..ad952963971a31968b5d109661b9cab0aa4b95fc 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index 204b25f45ef2f22bb246641a2aa9f9d09719737b..8b20e1d1d87e2dda9f37763492ddf39a8022c48c 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@ $(CET_HOST_FLAGS)
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 834699b439e03cd1213c4b7fe1867ff7b5f19e98..20611c6b1e6f5e381d4690e34ab7a69f2c827bb6 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -675,8 +675,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


[-- Attachment #2: hwasan-v52.patch --]
[-- Type: text/plain, Size: 4711 bytes --]

diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
new file mode 100644
index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
--- /dev/null
+++ b/config/bootstrap-hwasan.mk
@@ -0,0 +1,8 @@
+# This option enables -fsanitize=hwaddress for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=hwaddress
+STAGE3_CFLAGS += -fsanitize=hwaddress
+POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
+		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
diff --git a/configure b/configure
index a2ea1a329b69de06906315e54a49c694c9704522..b41a258c80ee9f289de534185eb364bcb5ca6ae5 100755
--- a/configure
+++ b/configure
@@ -9305,7 +9305,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/configure.ac b/configure.ac
index 44fa75f3a329ef68f6800c8e09d49a9373f731cf..944f30cfea84e9266b4322df7902b867882d4d8c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2814,7 +2814,7 @@ fi
 # or bootstrap-ubsan, bootstrap it.
 if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
   case "$BUILD_CONFIG" in
-    *bootstrap-asan* | *bootstrap-ubsan* )
+    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
       bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
       bootstrap_fixincludes=yes
       ;;
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index 60ee0a9dba17bf8f00d7c5320468ba847f08f8a0..e0f75b3d55582b5d1b8718f94e39c7d50656a926 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -2794,6 +2794,11 @@ the build tree.
 Compiles GCC itself using Address Sanitization in order to catch invalid memory
 accesses within the GCC code.
 
+@item @samp{bootstrap-hwasan}
+Compiles GCC itself using HWAddress Sanitization in order to catch invalid
+memory accesses within the GCC code.  This option is only available on AArch64
+systems that are running Linux kernel version 5.4 or later.
+
 @end table
 
 @section Building a cross compiler
diff --git a/libiberty/configure b/libiberty/configure
index ff93c9ee9a6fa9c6bb1938bcdfffb2d0ae8c9698..b6af9baf21204a323cad0e7b40a426c72988ba3b 100755
--- a/libiberty/configure
+++ b/libiberty/configure
@@ -5264,6 +5264,7 @@ fi
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 
 
diff --git a/libiberty/configure.ac b/libiberty/configure.ac
index 4e2599c14a89bafcb8c7e523b9ce5b3d60b8c0f6..ad952963971a31968b5d109661b9cab0aa4b95fc 100644
--- a/libiberty/configure.ac
+++ b/libiberty/configure.ac
@@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
 NOASANFLAG=
 case " ${CFLAGS} " in
   *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
+  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
 esac
 AC_SUBST(NOASANFLAG)
 
diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
index 204b25f45ef2f22bb246641a2aa9f9d09719737b..8b20e1d1d87e2dda9f37763492ddf39a8022c48c 100644
--- a/lto-plugin/Makefile.am
+++ b/lto-plugin/Makefile.am
@@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
 AM_CFLAGS = @ac_lto_plugin_warn_cflags@ $(CET_HOST_FLAGS)
 AM_LDFLAGS = @ac_lto_plugin_ldflags@
 AM_LIBTOOLFLAGS = --tag=disable-static
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 libexecsub_LTLIBRARIES = liblto_plugin.la
 gcc_build_dir = @gcc_build_dir@
diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
index 834699b439e03cd1213c4b7fe1867ff7b5f19e98..20611c6b1e6f5e381d4690e34ab7a69f2c827bb6 100644
--- a/lto-plugin/Makefile.in
+++ b/lto-plugin/Makefile.in
@@ -675,8 +675,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
 
 .PRECIOUS: Makefile
 
-override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
-override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
+override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
+override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
 
 all-local: $(in_gcc_libs)
 


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 4/X] libsanitizer: options:  Add hwasan flags and argument parsing
  2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
                         ` (4 preceding siblings ...)
  2020-11-16 15:37       ` [PATCH 3/X] libsanitizer: Add option to bootstrap using HWASAN Matthew Malcomson
@ 2020-11-16 15:37       ` Matthew Malcomson
  2020-11-18 17:57         ` Richard Sandiford
  2020-11-16 15:37       ` [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN Matthew Malcomson
                         ` (3 subsequent siblings)
  9 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-16 15:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: Richard Earnshaw, Kyrylo Tkachov, Richard Sandiford, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 26582 bytes --]

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

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

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

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

gcc/ChangeLog:

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



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


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


[-- Attachment #2: hwasan-v53.patch --]
[-- Type: text/plain, Size: 23780 bytes --]

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


^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN
  2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
                         ` (5 preceding siblings ...)
  2020-11-16 15:37       ` [PATCH 4/X] libsanitizer: options: Add hwasan flags and argument parsing Matthew Malcomson
@ 2020-11-16 15:37       ` Matthew Malcomson
  2020-11-16 15:37       ` [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes Matthew Malcomson
                         ` (2 subsequent siblings)
  9 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-16 15:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: Richard Earnshaw, Kyrylo Tkachov, Richard Sandiford, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 57966 bytes --]

Handling stack variables has three features.

1) Ensure HWASAN required alignment for stack variables

When tagging shadow memory, we need to ensure that each tag granule is
only used by one variable at a time.

This is done by ensuring that each tagged variable is aligned to the tag
granule representation size and also ensure that the end of each
object is aligned to ensure the start of any other data stored on the
stack is in a different granule.

This patch ensures the above by forcing the stack pointer to be aligned
before and after allocating any stack objects. Since we are forcing
alignment we also use `align_local_variable` to ensure this new alignment
is advertised properly through SET_DECL_ALIGN.

2) Put tags into each stack variable pointer

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

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

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

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

Backend hooks define the size of a tag, the layout of the HWASAN shadow
memory, and handle emitting the code that inserts and extracts tags from a
pointer.

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

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

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

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

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

config/ChangeLog:

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

ChangeLog:

	* gcc/asan.c (struct hwasan_stack_var): New.
	(hwasan_sanitize_p): New.
	(hwasan_sanitize_stack_p): New.
	(hwasan_sanitize_allocas_p): New.
	(initialize_sanitizer_builtins): Define new builtins.
	(ATTR_NOTHROW_LIST): New macro.
	(hwasan_current_frame_tag): New.
	(hwasan_frame_base): New.
	(stack_vars_base_reg_p): New.
	(hwasan_maybe_init_frame_base): New.
	(hwasan_record_frame_base_needed): New.
	(hwasan_record_stack_var): New.
	(hwasan_get_frame_extent): New.
	(hwasan_increment_frame_tag): New.
	(hwasan_record_frame_init): New.
	(hwasan_emit_prologue): New.
	(hwasan_emit_untag_frame): New.
	(hwasan_finish_file): New.
	(hwasan_truncate_to_tag_size): New.
	* gcc/asan.h (hwasan_record_frame_init): New declaration.
	(hwasan_record_stack_var): New declaration.
	(hwasan_emit_prologue): New declaration.
	(hwasan_emit_untag_frame): New declaration.
	(hwasan_get_frame_extent): New declaration.
	(hwasan_maybe_init_frame_base): New declaration.
	(hwasan_frame_base): New declaration.
	(stack_vars_base_reg_p): New declaration.
	(hwasan_record_frame_base_needed): New declaration.
	(hwasan_current_frame_tag): New declaration.
	(hwasan_increment_frame_tag): New declaration.
	(hwasan_truncate_to_tag_size): New declaration.
	(hwasan_finish_file): New declaration.
	(hwasan_sanitize_p): New declaration.
	(hwasan_sanitize_stack_p): New declaration.
	(hwasan_sanitize_allocas_p): New declaration.
	(HWASAN_TAG_SIZE): New macro.
	(HWASAN_TAG_GRANULE_SIZE): New macro.
	(HWASAN_STACK_BACKGROUND): New macro.
	* gcc/builtin-types.def (BT_FN_VOID_PTR_UINT8_PTRMODE): New.
	* gcc/builtins.def (DEF_SANITIZER_BUILTIN): Enable for HWASAN.
	* gcc/cfgexpand.c (align_local_variable): When using hwasan ensure
	alignment to tag granule.
	(align_frame_offset): New.
	(expand_one_stack_var_at): For hwasan use tag offset.
	(expand_stack_vars): Record stack objects for hwasan.
	(expand_one_stack_var_1): Record stack objects for hwasan.
	(init_vars_expansion): Initialise hwasan state.
	(expand_used_vars): Emit hwasan prologue and generate hwasan epilogue.
	* gcc/doc/tm.texi (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
	TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
	TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
	TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
	* gcc/doc/tm.texi.in (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
	TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
	TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
	TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
	* gcc/explow.c (get_dynamic_stack_base): Take new `base` argument.
	* gcc/explow.h (get_dynamic_stack_base): Take new `base` argument.
	* gcc/sanitizer.def (BUILT_IN_HWASAN_INIT): New.
	(BUILT_IN_HWASAN_TAG_MEM): New.
	* gcc/target.def (target_memtag_tag_size,target_memtag_granule_size,
	target_memtag_insert_random_tag,target_memtag_add_tag,
	target_memtag_set_tag,target_memtag_extract_tag,
	target_memtag_untagged_pointer): New hooks.
	* gcc/targhooks.c (HWASAN_SHIFT): New.
	(HWASAN_SHIFT_RTX): New.
	(default_memtag_tag_size): New default hook.
	(default_memtag_granule_size): New default hook.
	(default_memtag_insert_random_tag): New default hook.
	(default_memtag_add_tag): New default hook.
	(default_memtag_set_tag): New default hook.
	(default_memtag_extract_tag): New default hook.
	(default_memtag_untagged_pointer): New default hook.
	* gcc/targhooks.h (default_memtag_tag_size): New default hook.
	(default_memtag_granule_size): New default hook.
	(default_memtag_insert_random_tag): New default hook.
	(default_memtag_add_tag): New default hook.
	(default_memtag_set_tag): New default hook.
	(default_memtag_extract_tag): New default hook.
	(default_memtag_untagged_pointer): New default hook.
	* gcc/toplev.c (compile_file): Call hwasan_finish_file when finished.


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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -1,7 +1,11 @@
 # This option enables -fsanitize=hwaddress for stage2 and stage3.
+# We need to disable random frame tags for bootstrap since the autoconf check
+# for which direction the stack is growing has UB that a random frame tag
+# breaks.  Running with a random frame tag gives approx. 50% chance of
+# bootstrap comparison diff in libiberty/alloca.c.
 
-STAGE2_CFLAGS += -fsanitize=hwaddress
-STAGE3_CFLAGS += -fsanitize=hwaddress
+STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
 POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
diff --git a/gcc/asan.h b/gcc/asan.h
index 114b457ef91c4479d43774bed58c24213196ce12..9b6b9c0a9b4daeaf176990dfd8dfbe0b0127bb39 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -34,6 +34,23 @@ extern bool asan_expand_mark_ifn (gimple_stmt_iterator *);
 extern bool asan_expand_poison_ifn (gimple_stmt_iterator *, bool *,
 				    hash_map<tree, tree> &);
 
+extern void hwasan_record_frame_init ();
+extern void hwasan_record_stack_var (rtx, rtx, poly_int64, poly_int64);
+extern void hwasan_emit_prologue ();
+extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx);
+extern rtx hwasan_get_frame_extent ();
+extern void hwasan_maybe_init_frame_base ();
+extern rtx hwasan_frame_base (bool);
+extern bool stack_vars_base_reg_p (rtx);
+extern void hwasan_record_frame_base_needed ();
+extern uint8_t hwasan_current_frame_tag ();
+extern void hwasan_increment_frame_tag ();
+extern rtx hwasan_truncate_to_tag_size (rtx, rtx);
+extern void hwasan_finish_file (void);
+extern bool hwasan_sanitize_p (void);
+extern bool hwasan_sanitize_stack_p (void);
+extern bool hwasan_sanitize_allocas_p (void);
+
 extern gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
 
@@ -75,6 +92,26 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The values below and the hooks under targetm.memtag define an ABI and
+   are hard-coded to these values in libhwasan, hence they can't be changed
+   independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   The default version uses the entire top byte of a pointer (i.e. 8 bits).  */
+#define HWASAN_TAG_SIZE targetm.memtag.tag_size ()
+/* Tag Granule of HWASAN shadow stack.
+   This is the size in real memory that each byte in the shadow memory refers
+   to.  I.e. if a variable is X bytes long in memory then its tag in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   Most variables will need to be aligned to this amount since two variables
+   that are neighbors in memory and share a tag granule would need to share the
+   same tag (the shared tag granule can only store one tag).  */
+#define HWASAN_TAG_GRANULE_SIZE targetm.memtag.granule_size ()
+/* Define the tag for the stack background.
+   This defines what tag the stack pointer will be and hence what tag all
+   variables that are not given special tags are (e.g. spilled registers,
+   and parameters passed on the stack).  */
+#define HWASAN_STACK_BACKGROUND gen_int_mode (0, QImode)
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/asan.c b/gcc/asan.c
index 0b471afff64ea6a0ffbe0add71333ac688c472c6..8f0364ef77762678ecff2da36db8536c27d46b48 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -257,6 +257,53 @@ hash_set<tree> *asan_handled_variables = NULL;
 
 hash_set <tree> *asan_used_labels = NULL;
 
+/* Global variables for HWASAN stack tagging.  */
+/* hwasan_frame_tag_offset records the offset from the frame base tag that the
+   next object should have.  */
+static uint8_t hwasan_frame_tag_offset = 0;
+/* hwasan_frame_base_ptr is a pointer with the same address as
+   `virtual_stack_vars_rtx` for the current frame, and with the frame base tag
+   stored in it.  N.b. this global RTX does not need to be marked GTY, but is
+   done so anyway.  The need is not there since all uses are in just one pass
+   (cfgexpand) and there are no calls to ggc_collect between the uses.  We mark
+   it GTY(()) anyway to allow the use of the variable later on if needed by
+   future features.  */
+static GTY(()) rtx hwasan_frame_base_ptr = NULL_RTX;
+
+/* Boolean describing whether we have a need to emit the instruction that will
+   initialise hwasan_frame_base_ptr in the emitted code for this frame.
+   This is true between the first time that hwasan_frame_base_ptr is used and
+   when the instruction to initialise it in the runtime is emitted.  */
+static bool hwasan_base_init_required = false;
+
+/* Structure defining the extent of one object on the stack that HWASAN needs
+   to tag in the corresponding shadow stack space.
+
+   The range this object spans on the stack is between `untagged_base +
+   nearest_offset` and `untagged_base + farthest_offset`.
+   `tagged_base` is an rtx containing the same value as `untagged_base` but
+   with a random tag stored in the top byte.  We record both `untagged_base`
+   and `tagged_base` so that `hwasan_emit_prologue` can use both without having
+   to emit RTL into the instruction stream to re-calculate one from the other.
+   (`hwasan_emit_prologue` needs to use both bases since the
+   __hwasan_tag_memory call it emits uses an untagged value, and it calculates
+   the tag to store in shadow memory based on the tag_offset plus the tag in
+   tagged_base).  */
+struct hwasan_stack_var
+{
+  rtx untagged_base;
+  rtx tagged_base;
+  poly_int64 nearest_offset;
+  poly_int64 farthest_offset;
+  uint8_t tag_offset;
+};
+
+/* Variable recording all stack variables that HWASAN needs to tag.
+   Does not need to be marked as GTY(()) since every use is in the cfgexpand
+   pass and gcc_collect is not called in the middle of that pass.  */
+static vec<hwasan_stack_var> hwasan_tagged_stack_vars;
+
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -1359,6 +1406,28 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+/* Returns whether we are tagging pointers and checking those tags on memory
+   access.  */
+bool
+hwasan_sanitize_p ()
+{
+  return sanitize_flags_p (SANITIZE_HWADDRESS);
+}
+
+/* Are we tagging the stack?  */
+bool
+hwasan_sanitize_stack_p ()
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_stack);
+}
+
+/* Are we tagging alloca objects?  */
+bool
+hwasan_sanitize_allocas_p (void)
+{
+  return (hwasan_sanitize_stack_p () && param_hwasan_protect_allocas);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -2908,6 +2977,11 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_VOID_PTR_UINT8_PTRMODE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				unsigned_char_type_node,
+				pointer_sized_int_node, NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
@@ -2958,6 +3032,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NOTHROW_LIST
+#define ATTR_NOTHROW_LIST ECF_NOTHROW
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
@@ -3709,4 +3785,386 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/* For stack tagging:
+
+   Return the offset from the frame base tag that the "next" expanded object
+   should have.  */
+uint8_t
+hwasan_current_frame_tag ()
+{
+  return hwasan_frame_tag_offset;
+}
+
+/* For stack tagging:
+
+   Return the 'base pointer' for this function.  If that base pointer has not
+   yet been created then we create a register to hold it.
+
+   If `maybe_gen` is true, then this function will ensure that the
+   `hwasan_frame_base_ptr` is created and that hwasan_maybe_init_frame_base
+   (and by extension hwasan_emit_prologue) knows that initialisation of the
+   register is needed.
+   Otherwise (if `maybe_gen` is false) this function will simply return
+   whatever the current value of hwasan_frame_base_ptr is.  */
+rtx
+hwasan_frame_base (bool maybe_gen)
+{
+  if (! hwasan_frame_base_ptr && maybe_gen)
+    {
+      rtx base = gen_reg_rtx (Pmode);
+      hwasan_base_init_required = true;
+      hwasan_frame_base_ptr = base;
+    }
+
+  return hwasan_frame_base_ptr;
+}
+
+/* For stack tagging:
+
+   Check whether this RTX is a standard pointer addressing the base of the
+   stack variables for this frame.  Returns true if the RTX is either
+   virtual_stack_vars_rtx or hwasan_frame_base_ptr.  */
+bool
+stack_vars_base_reg_p (rtx base)
+{
+  return base == virtual_stack_vars_rtx || base == hwasan_frame_base_ptr;
+}
+
+/* For stack tagging:
+
+   If required, emit the instruction to initialise `hwasan_frame_base_ptr` with
+   the value of `virtual_stack_vars_rtx` and a (possibly random) initial tag.
+
+   We only emit this instruction if we know that the frame base pointer has
+   indeed been used for this stack frame.  */
+void
+hwasan_maybe_init_frame_base ()
+{
+  if (! hwasan_base_init_required)
+    return;
+  rtx frame_base = hwasan_frame_base (true);
+  rtx ret = targetm.memtag.insert_random_tag (virtual_stack_vars_rtx,
+					      frame_base);
+  if (frame_base != ret)
+    emit_move_insn (frame_base, ret);
+  hwasan_base_init_required = false;
+}
+
+/* For stack tagging:
+
+   Used when some action needs to record that it *will* need the hwasan frame
+   base pointer, but when it doesn't need access to the pointer right now.
+
+   This is designed for passes that know an action they're taking will require
+   the frame base to be initialized in the expand pass, but that it won't
+   necessarily be initialized by the stack variables.
+   We don't simply use `hwasan_frame_base` since that will initialise
+   `hwasan_frame_base_ptr` and try to use that.  This pointer is designed to be
+   used from the expand pass onwards and is hence reset to NULL_RTX at the
+   start of the expand pass.
+
+   E.g. when the asan pass handles an alloca call it emits
+   ifn_HWASAN_CHOOSE_TAG.  This internal function will use the frame base
+   pointer, and hence the initialisation must have been emitted.  However,
+   since this will be used for an object allocated by alloca the decl will not
+   be handled by `expand_used_vars` and hence hwasan_emit_prologue will not
+   automatically pick it up.
+   This is why `handle_builtin_alloca` above uses this function.  It marks that
+   the frame base pointer must be initialized but it does not try and create
+   a new pseudo for */
+void
+hwasan_record_frame_base_needed ()
+{
+  hwasan_base_init_required = true;
+}
+
+/* Record a compile-time constant size stack variable that HWASAN will need to
+   tag.  This record of the range of a stack variable will be used by
+   `hwasan_emit_prologue` to emit the RTL at the start of each frame which will
+   set tags in the shadow memory according to the assigned tag for each object.
+
+   The range that the object spans in stack space should be described by the
+   bounds `untagged_base + nearest_offset` and
+   `untagged_base + farthest_offset`.
+   `tagged_base` is the base address which contains the "base frame tag" for
+   this frame, and from which the value to address this object with will be
+   calculated.
+
+   We record the `untagged_base` since the functions in the hwasan library we
+   use to tag memory take pointers without a tag.  */
+void
+hwasan_record_stack_var (rtx untagged_base, rtx tagged_base,
+			 poly_int64 nearest_offset, poly_int64 farthest_offset)
+{
+  hwasan_stack_var cur_var;
+  cur_var.untagged_base = untagged_base;
+  cur_var.tagged_base = tagged_base;
+  cur_var.nearest_offset = nearest_offset;
+  cur_var.farthest_offset = farthest_offset;
+  cur_var.tag_offset = hwasan_current_frame_tag ();
+
+  hwasan_tagged_stack_vars.safe_push (cur_var);
+}
+
+/* Return the RTX representing the farthest extent of the statically allocated
+   stack objects for this frame.  If hwasan_frame_base_ptr has not been
+   initialised then we are not storing any static variables on the stack in
+   this frame.  In this case we return NULL_RTX to represent that.
+
+   Otherwise simply return virtual_stack_vars_rtx + frame_offset.  */
+rtx
+hwasan_get_frame_extent ()
+{
+  return (hwasan_frame_base_ptr
+	  ? plus_constant (Pmode, virtual_stack_vars_rtx, frame_offset)
+	  : NULL_RTX);
+}
+
+/* For stack tagging:
+
+   Increment the frame tag offset modulo the size a tag can represent.  */
+void
+hwasan_increment_frame_tag ()
+{
+  uint8_t tag_bits = HWASAN_TAG_SIZE;
+  gcc_assert (HWASAN_TAG_SIZE
+	      <= sizeof (hwasan_frame_tag_offset) * CHAR_BIT);
+  hwasan_frame_tag_offset = (hwasan_frame_tag_offset + 1) % (1 << tag_bits);
+  /* The "background tag" of the stack is zero by definition.
+     This is the tag that objects like parameters passed on the stack and
+     spilled registers are given.  It is handy to avoid this tag for objects
+     whose tags we decide ourselves, partly to ensure that buffer overruns
+     can't affect these important variables (e.g. saved link register, saved
+     stack pointer etc) and partly to make debugging easier (everything with a
+     tag of zero is space allocated automatically by the compiler).
+
+     This is not feasible when using random frame tags (the default
+     configuration for hwasan) since the tag for the given frame is randomly
+     chosen at runtime.  In order to avoid any tags matching the stack
+     background we would need to decide tag offsets at runtime instead of
+     compile time (and pay the resulting performance cost).
+
+     When not using random base tags for each frame (i.e. when compiled with
+     `--param hwasan-random-frame-tag=0`) the base tag for each frame is zero.
+     This means the tag that each object gets is equal to the
+     hwasan_frame_tag_offset used in determining it.
+     When this is the case we *can* ensure no object gets the tag of zero by
+     simply ensuring no object has the hwasan_frame_tag_offset of zero.
+
+     There is the extra complication that we only record the
+     hwasan_frame_tag_offset here (which is the offset from the tag stored in
+     the stack pointer).  In the kernel, the tag in the stack pointer is 0xff
+     rather than zero.  This does not cause problems since tags of 0xff are
+     never checked in the kernel.  As mentioned at the beginning of this
+     comment the background tag of the stack is zero by definition, which means
+     that for the kernel we should skip offsets of both 0 and 1 from the stack
+     pointer.  Avoiding the offset of 0 ensures we use a tag which will be
+     checked, avoiding the offset of 1 ensures we use a tag that is not the
+     same as the background.  */
+  if (hwasan_frame_tag_offset == 0 && ! param_hwasan_random_frame_tag)
+    hwasan_frame_tag_offset += 1;
+  if (hwasan_frame_tag_offset == 1 && ! param_hwasan_random_frame_tag
+      && sanitize_flags_p (SANITIZE_KERNEL_HWADDRESS))
+    hwasan_frame_tag_offset += 1;
+}
+
+/* Clear internal state for the next function.
+   This function is called before variables on the stack get expanded, in
+   `init_vars_expansion`.  */
+void
+hwasan_record_frame_init ()
+{
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
+  /* If this isn't the case then some stack variable was recorded *before*
+     hwasan_record_frame_init is called, yet *after* the hwasan prologue for
+     the previous frame was emitted.  Such stack variables would not have
+     their shadow stack filled in.  */
+  gcc_assert (hwasan_tagged_stack_vars.is_empty ());
+  hwasan_frame_base_ptr = NULL_RTX;
+
+  /* When not using a random frame tag we can avoid the background stack
+     color which gives the user a little better debug output upon a crash.
+     Meanwhile, when using a random frame tag it will be nice to avoid adding
+     tags for the first object since that is unnecessary extra work.
+     Hence set the initial hwasan_frame_tag_offset to be 0 if using a random
+     frame tag and 1 otherwise.
+
+     As described in hwasan_increment_frame_tag, in the kernel the stack
+     pointer has the tag 0xff.  That means that to avoid 0xff and 0 (the tag
+     which the kernel does not check and the background tag respectively) we
+     start with a tag offset of 2.  */
+  hwasan_frame_tag_offset = param_hwasan_random_frame_tag
+    ? 0
+    : sanitize_flags_p (SANITIZE_KERNEL_HWADDRESS) ? 2 : 1;
+}
+
+/* For stack tagging:
+   (Emits HWASAN equivalent of what is emitted by
+   `asan_emit_stack_protection`).
+
+   Emits the extra prologue code to set the shadow stack as required for HWASAN
+   stack instrumentation.
+
+   Uses the vector of recorded stack variables hwasan_tagged_stack_vars.  When
+   this function has completed hwasan_tagged_stack_vars is empty and all
+   objects it had pointed to are deallocated.  */
+void
+hwasan_emit_prologue ()
+{
+  /* We need untagged base pointers since libhwasan only accepts untagged
+    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
+    the base tag for an offset.  */
+
+  /* N.b. we see if we need to emit initialisation of the frame base *before*
+     checking if there are any stack variables we need to tag.
+     This is because calls to alloca will need the frame base to be
+     initialised (and if we've seen them we will have recorded that we need to
+     initialise the frame base), but they are not recorded in
+     hwasan_tagged_stack_vars.  */
+  hwasan_maybe_init_frame_base ();
+  if (hwasan_tagged_stack_vars.is_empty ())
+    return;
+
+  size_t length = hwasan_tagged_stack_vars.length ();
+  hwasan_stack_var *vars = hwasan_tagged_stack_vars.address ();
+
+  poly_int64 bot = 0, top = 0;
+  size_t i = 0;
+  for (i = 0; i < length; i++)
+    {
+      hwasan_stack_var& cur = vars[i];
+      poly_int64 nearest = cur.nearest_offset;
+      poly_int64 farthest = cur.farthest_offset;
+
+      if (known_ge (nearest, farthest))
+	{
+	  top = nearest;
+	  bot = farthest;
+	}
+      else
+	{
+	  /* Given how these values are calculated, one must be known greater
+	     than the other.  */
+	  gcc_assert (known_le (nearest, farthest));
+	  top = farthest;
+	  bot = nearest;
+	}
+      poly_int64 size = (top - bot);
+
+      /* Assert the edge of each variable is aligned to the HWASAN tag granule
+	 size.  */
+      gcc_assert (multiple_p (top, HWASAN_TAG_GRANULE_SIZE));
+      gcc_assert (multiple_p (bot, HWASAN_TAG_GRANULE_SIZE));
+      gcc_assert (multiple_p (size, HWASAN_TAG_GRANULE_SIZE));
+
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      rtx base_tag = targetm.memtag.extract_tag (cur.tagged_base, NULL_RTX);
+      rtx tag = plus_constant (QImode, base_tag, cur.tag_offset);
+      tag = hwasan_truncate_to_tag_size (tag, NULL_RTX);
+
+      rtx bottom = convert_memory_address (ptr_mode,
+					   plus_constant (Pmode,
+							  cur.untagged_base,
+							  bot));
+      emit_library_call (ret, LCT_NORMAL, VOIDmode,
+			 bottom, ptr_mode,
+			 tag, QImode,
+			 gen_int_mode (size, ptr_mode), ptr_mode);
+    }
+  /* Clear the stack vars, we've emitted the prologue for them all now.  */
+  hwasan_tagged_stack_vars.truncate (0);
+}
+
+/* For stack tagging:
+
+   Return RTL insns to clear the tags between DYNAMIC and VARS pointers
+   into the stack.  These instructions should be emitted at the end of
+   every function.
+
+   If `dynamic` is NULL_RTX then no insns are returned.  */
+rtx_insn *
+hwasan_emit_untag_frame (rtx dynamic, rtx vars)
+{
+  if (! dynamic)
+    return NULL;
+
+  start_sequence ();
+
+  dynamic = convert_memory_address (ptr_mode, dynamic);
+  vars = convert_memory_address (ptr_mode, vars);
+
+  rtx top_rtx;
+  rtx bot_rtx;
+  if (FRAME_GROWS_DOWNWARD)
+    {
+      top_rtx = vars;
+      bot_rtx = dynamic;
+    }
+  else
+    {
+      top_rtx = dynamic;
+      bot_rtx = vars;
+    }
+
+  rtx size_rtx = expand_simple_binop (ptr_mode, MINUS, top_rtx, bot_rtx,
+				      NULL_RTX, /* unsignedp = */0,
+				      OPTAB_DIRECT);
+
+  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (ret, LCT_NORMAL, VOIDmode,
+		     bot_rtx, ptr_mode,
+		     HWASAN_STACK_BACKGROUND, QImode,
+		     size_rtx, ptr_mode);
+
+  do_pending_stack_adjust ();
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+/* Needs to be GTY(()), because cgraph_build_static_cdtor may
+   invoke ggc_collect.  */
+static GTY(()) tree hwasan_ctor_statements;
+
+/* Insert module initialization into this TU.  This initialization calls the
+   initialization code for libhwasan.  */
+void
+hwasan_finish_file (void)
+{
+  /* Do not emit constructor initialization for the kernel.
+     (the kernel has its own initialization already).  */
+  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+    return;
+
+  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
+  flag_sanitize &= ~SANITIZE_HWADDRESS;
+  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
+  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
+  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
+  flag_sanitize |= SANITIZE_HWADDRESS;
+}
+
+/* For stack tagging:
+
+   Truncate `tag` to the number of bits that a tag uses (i.e. to
+   HWASAN_TAG_SIZE).  Store the result in `target` if it's convenient.  */
+rtx
+hwasan_truncate_to_tag_size (rtx tag, rtx target)
+{
+  gcc_assert (GET_MODE (tag) == QImode);
+  if (HWASAN_TAG_SIZE != GET_MODE_PRECISION (QImode))
+    {
+      gcc_assert (GET_MODE_PRECISION (QImode) > HWASAN_TAG_SIZE);
+      rtx mask = gen_int_mode ((HOST_WIDE_INT_1U << HWASAN_TAG_SIZE) - 1,
+			       QImode);
+      tag = expand_simple_binop (QImode, AND, tag, mask, target,
+				 /* unsignedp = */1, OPTAB_WIDEN);
+      gcc_assert (tag);
+    }
+  return tag;
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index c46b1bc5cbd1fba03b033b8d44ba186570780c3f..535366d104b9cec02ed2d07682476eebdbbe9161 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -637,6 +637,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
 DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
 DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_CONST_SIZE_BOOL,
 		     BT_PTR, BT_PTR, BT_CONST_SIZE, BT_BOOL)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_PTRMODE, BT_VOID, BT_PTR, BT_UINT8,
+		     BT_PTRMODE)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 68f2da6cda41e6af34325f6ba27e4d50b056280f..88f677fc683d999b4f6e2d7ca02f04dc41eaf98b 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -245,6 +245,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
+				| SANITIZE_HWADDRESS \
 				| SANITIZE_UNDEFINED \
 				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 6c41a7e21a7cef38ba063a26509e067ba81da349..92843342101f39e5bccce7affc12c32122ac62fc 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -376,15 +376,18 @@ align_local_variable (tree decl, bool really_expand)
 	align = GET_MODE_ALIGNMENT (mode);
     }
   else
-    {
-      align = LOCAL_DECL_ALIGNMENT (decl);
-      /* Don't change DECL_ALIGN when called from estimated_stack_frame_size.
-	 That is done before IPA and could bump alignment based on host
-	 backend even for offloaded code which wants different
-	 LOCAL_DECL_ALIGNMENT.  */
-      if (really_expand)
-	SET_DECL_ALIGN (decl, align);
-    }
+    align = LOCAL_DECL_ALIGNMENT (decl);
+
+  if (hwasan_sanitize_stack_p ())
+    align = MAX (align, (unsigned) HWASAN_TAG_GRANULE_SIZE * BITS_PER_UNIT);
+
+  if (TREE_CODE (decl) != SSA_NAME && really_expand)
+    /* Don't change DECL_ALIGN when called from estimated_stack_frame_size.
+       That is done before IPA and could bump alignment based on host
+       backend even for offloaded code which wants different
+       LOCAL_DECL_ALIGNMENT.  */
+    SET_DECL_ALIGN (decl, align);
+
   return align / BITS_PER_UNIT;
 }
 
@@ -428,6 +431,14 @@ alloc_stack_frame_space (poly_int64 size, unsigned HOST_WIDE_INT align)
   return offset;
 }
 
+/* Ensure that the stack is aligned to ALIGN bytes.
+   Return the new frame offset.  */
+static poly_int64
+align_frame_offset (unsigned HOST_WIDE_INT align)
+{
+  return alloc_stack_frame_space (0, align);
+}
+
 /* Accumulate DECL into STACK_VARS.  */
 
 static void
@@ -1004,7 +1015,12 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   /* If this fails, we've overflowed the stack frame.  Error nicely?  */
   gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
 
-  x = plus_constant (Pmode, base, offset);
+  if (hwasan_sanitize_stack_p ())
+    x = targetm.memtag.add_tag (base, offset,
+				hwasan_current_frame_tag ());
+  else
+    x = plus_constant (Pmode, base, offset);
+
   x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
 		   ? TYPE_MODE (TREE_TYPE (decl))
 		   : DECL_MODE (decl), x);
@@ -1013,7 +1029,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
      If it is we generate stack slots only accidentally so it isn't as
      important, we'll simply set the alignment directly on the MEM.  */
 
-  if (base == virtual_stack_vars_rtx)
+  if (base == virtual_stack_vars_rtx || base == hwasan_frame_base (false))
     offset -= frame_phase;
   align = known_alignment (offset);
   align *= BITS_PER_UNIT;
@@ -1056,13 +1072,13 @@ public:
 /* A subroutine of expand_used_vars.  Give each partition representative
    a unique location within the stack frame.  Update each partition member
    with that location.  */
-
 static void
 expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 {
   size_t si, i, j, n = stack_vars_num;
   poly_uint64 large_size = 0, large_alloc = 0;
   rtx large_base = NULL;
+  rtx large_untagged_base = NULL;
   unsigned large_align = 0;
   bool large_allocation_done = false;
   tree decl;
@@ -1113,7 +1129,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
     {
       rtx base;
       unsigned base_align, alignb;
-      poly_int64 offset;
+      poly_int64 offset = 0;
 
       i = stack_vars_sorted[si];
 
@@ -1134,10 +1150,33 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       if (pred && !pred (i))
 	continue;
 
+      base = (hwasan_sanitize_stack_p ()
+	      ? hwasan_frame_base (true)
+	      : virtual_stack_vars_rtx);
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  base = virtual_stack_vars_rtx;
+	  poly_int64 hwasan_orig_offset;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* There must be no tag granule "shared" between different
+		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
+		 chunk can have more than one object in it.
+
+		 We ensure this by forcing the end of the last bit of data to
+		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
+		 the start of each variable to be aligned to
+		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
+
+		 We can't align just one of the start or end, since there are
+		 untagged things stored on the stack which we do not align to
+		 HWASAN_TAG_GRANULE_SIZE bytes.  If we only aligned the start
+		 or the end of tagged objects then untagged objects could end
+		 up sharing the first granule of a tagged object or sharing the
+		 last granule of a tagged object respectively.  */
+	      hwasan_orig_offset = align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
 	  HOST_WIDE_INT prev_offset;
@@ -1148,7 +1187,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	    {
 	      if (data->asan_vec.is_empty ())
 		{
-		  alloc_stack_frame_space (0, ASAN_RED_ZONE_SIZE);
+		  align_frame_offset (ASAN_RED_ZONE_SIZE);
 		  prev_offset = frame_offset.to_constant ();
 		}
 	      prev_offset = align_base (prev_offset,
@@ -1216,6 +1255,24 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	    {
 	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
 	      base_align = crtl->max_used_stack_slot_alignment;
+
+	      if (hwasan_sanitize_stack_p ())
+		{
+		  /* Align again since the point of this alignment is to handle
+		     the "end" of the object (i.e. smallest address after the
+		     stack object).  For FRAME_GROWS_DOWNWARD that requires
+		     aligning the stack before allocating, but for a frame that
+		     grows upwards that requires aligning the stack after
+		     allocation.
+
+		     Use `frame_offset` to record the offset value rather than
+		     offset since the frame_offset describes the extent
+		     allocated for this particular variable while `offset`
+		     describes the address that this variable starts at.  */
+		  align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+		  hwasan_record_stack_var (virtual_stack_vars_rtx, base,
+					   hwasan_orig_offset, frame_offset);
+		}
 	    }
 	}
       else
@@ -1231,19 +1288,50 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      poly_int64 loffset;
 	      rtx large_allocsize;
 
+	      if (hwasan_sanitize_stack_p ())
+		/* `get_dynamic_stack_base` below is often the first place that
+		   an RTL insn is emitted into the instruction stream for a
+		   function.
+		   That insn will use hwasan_frame_base () if we are sanitizing
+		   the stack.  Since that instruction uses the value of
+		   hwasan_frame_base () we need to ensure that the base is
+		   initialised before the instruction is executed (and hence
+		   need to emit the insn initialising it before the insn using
+		   it).  */
+		hwasan_maybe_init_frame_base ();
+
 	      large_allocsize = gen_int_mode (large_size, Pmode);
 	      get_dynamic_stack_size (&large_allocsize, 0, large_align, NULL);
 	      loffset = alloc_stack_frame_space
 		(rtx_to_poly_int64 (large_allocsize),
 		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
-	      large_base = get_dynamic_stack_base (loffset, large_align);
+	      large_base = get_dynamic_stack_base (loffset, large_align, base);
 	      large_allocation_done = true;
 	    }
-	  gcc_assert (large_base != NULL);
 
+	  gcc_assert (large_base != NULL);
 	  large_alloc = aligned_upper_bound (large_alloc, alignb);
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* An object with a large alignment requirement means that the
+		 alignment requirement is greater than the required alignment
+		 for tags.  */
+	      if (!large_untagged_base)
+		large_untagged_base
+		  = targetm.memtag.untagged_pointer (large_base, NULL_RTX);
+	      /* Ensure the end of the variable is also aligned correctly.  */
+	      poly_int64 align_again
+		= aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
+	      /* For large allocations we always allocate a chunk of space
+		 (which is addressed by large_untagged_base/large_base) and
+		 then use positive offsets from that.  Hence the farthest
+		 offset is `align_again` and the nearest offset from the base
+		 is `offset`.  */
+	      hwasan_record_stack_var (large_untagged_base, large_base,
+				       offset, align_again);
+	    }
 
 	  base = large_base;
 	  base_align = large_align;
@@ -1254,9 +1342,10 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       for (j = i; j != EOC; j = stack_vars[j].next)
 	{
 	  expand_one_stack_var_at (stack_vars[j].decl,
-				   base, base_align,
-				   offset);
+				   base, base_align, offset);
 	}
+      if (hwasan_sanitize_stack_p ())
+	hwasan_increment_frame_tag ();
     }
 
   gcc_assert (known_eq (large_alloc, large_size));
@@ -1347,10 +1436,37 @@ expand_one_stack_var_1 (tree var)
   /* We handle highly aligned variables in expand_stack_vars.  */
   gcc_assert (byte_align * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT);
 
-  offset = alloc_stack_frame_space (size, byte_align);
+  rtx base;
+  if (hwasan_sanitize_stack_p ())
+    {
+      /* Allocate zero bytes to align the stack.  */
+      poly_int64 hwasan_orig_offset
+	= align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+      offset = alloc_stack_frame_space (size, byte_align);
+      align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+      base = hwasan_frame_base (true);
+      /* Use `frame_offset` to automatically account for machines where the
+	 frame grows upwards.
+
+	 `offset` will always point to the "start" of the stack object, which
+	 will be the smallest address, for ! FRAME_GROWS_DOWNWARD this is *not*
+	 the "furthest" offset from the base delimiting the current stack
+	 object.  `frame_offset` will always delimit the extent that the frame.
+	 */
+      hwasan_record_stack_var (virtual_stack_vars_rtx, base,
+			       hwasan_orig_offset, frame_offset);
+    }
+  else
+    {
+      offset = alloc_stack_frame_space (size, byte_align);
+      base = virtual_stack_vars_rtx;
+    }
 
-  expand_one_stack_var_at (var, virtual_stack_vars_rtx,
+  expand_one_stack_var_at (var, base,
 			   crtl->max_used_stack_slot_alignment, offset);
+
+  if (hwasan_sanitize_stack_p ())
+    hwasan_increment_frame_tag ();
 }
 
 /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
@@ -1950,6 +2066,8 @@ init_vars_expansion (void)
   /* Initialize local stack smashing state.  */
   has_protected_decls = false;
   has_short_buffer = false;
+  if (hwasan_sanitize_stack_p ())
+    hwasan_record_frame_init ();
 }
 
 /* Free up stack variable graph data.  */
@@ -2277,10 +2395,26 @@ expand_used_vars (void)
       expand_stack_vars (NULL, &data);
     }
 
+  if (hwasan_sanitize_stack_p ())
+    hwasan_emit_prologue ();
   if (asan_sanitize_allocas_p () && cfun->calls_alloca)
     var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
 					      virtual_stack_vars_rtx,
 					      var_end_seq);
+  else if (hwasan_sanitize_allocas_p () && cfun->calls_alloca)
+    /* When using out-of-line instrumentation we only want to emit one function
+       call for clearing the tags in a region of shadow stack.  When there are
+       alloca calls in this frame we want to emit a call using the
+       virtual_stack_dynamic_rtx, but when not we use the hwasan_frame_extent
+       rtx we created in expand_stack_vars.  */
+    var_end_seq = hwasan_emit_untag_frame (virtual_stack_dynamic_rtx,
+					   virtual_stack_vars_rtx);
+  else if (hwasan_sanitize_stack_p ())
+    /* If no variables were stored on the stack, `hwasan_get_frame_extent`
+       will return NULL_RTX and hence `hwasan_emit_untag_frame` will return
+       NULL (i.e. an empty sequence).  */
+    var_end_seq = hwasan_emit_untag_frame (hwasan_get_frame_extent (),
+					   virtual_stack_vars_rtx);
 
   fini_vars_expansion ();
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 321b0fe35816a257e42a1464a4ac84e849d376f2..199b6a6cbc08533a90b89ee7f58d0e38bda64463 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2980,6 +2980,63 @@ True if backend architecture naturally supports ignoring some region of
 pointers.  This feature means that @option{-fsanitize=hwaddress} can work.
 @end deftypefn
 
+@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_TAG_SIZE ()
+Return the size of a tag (in bits) for this platform.
+
+The default returns 8.
+@end deftypefn
+
+@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_GRANULE_SIZE ()
+Return the size in real memory that each byte in shadow memory refers to.
+I.e. if a variable is @var{X} bytes long in memory, then this hook should
+return the value @var{Y} such that the tag in shadow memory spans
+@var{X}/@var{Y} bytes.
+
+Most variables will need to be aligned to this amount since two variables
+that are neighbors in memory and share a tag granule would need to share
+the same tag.
+
+The default returns 16.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_INSERT_RANDOM_TAG (rtx @var{untagged}, rtx @var{target})
+Return an RTX representing the value of @var{untagged} but with a
+(possibly) random tag in it.
+Put that value into @var{target} if it is convenient to do so.
+This function is used to generate a tagged base for the current stack frame.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADD_TAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
+Return an RTX that represents the result of adding @var{addr_offset} to
+the address in pointer @var{base} and @var{tag_offset} to the tag in pointer
+@var{base}.
+The resulting RTX must either be a valid memory address or be able to get
+put into an operand with @code{force_operand}.
+
+Unlike other memtag hooks, this must return an expression and not emit any
+RTL.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_SET_TAG (rtx @var{untagged_base}, rtx @var{tag}, rtx @var{target})
+Return an RTX representing @var{untagged_base} but with the tag @var{tag}.
+Try and store this in @var{target} if convenient.
+@var{untagged_base} is required to have a zero tag when this hook is called.
+The default of this hook is to set the top byte of @var{untagged_base} to
+@var{tag}.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_EXTRACT_TAG (rtx @var{tagged_pointer}, rtx @var{target})
+Return an RTX representing the tag stored in @var{tagged_pointer}.
+Store the result in @var{target} if it is convenient.
+The default represents the top byte of the original pointer.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_UNTAGGED_POINTER (rtx @var{tagged_pointer}, rtx @var{target})
+Return an RTX representing @var{tagged_pointer} with its tag set to zero.
+Store the result in @var{target} if convenient.
+The default clears the top byte of the original pointer.
+@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 6b6313c8493636f3615cd9e5bc6cb091467a4a8e..c88057f3d19a26a0fc213775efdf395d231206ce 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2377,6 +2377,20 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_TAG_SIZE
+
+@hook TARGET_MEMTAG_GRANULE_SIZE
+
+@hook TARGET_MEMTAG_INSERT_RANDOM_TAG
+
+@hook TARGET_MEMTAG_ADD_TAG
+
+@hook TARGET_MEMTAG_SET_TAG
+
+@hook TARGET_MEMTAG_EXTRACT_TAG
+
+@hook TARGET_MEMTAG_UNTAGGED_POINTER
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/explow.h b/gcc/explow.h
index 0df8c62b82a8bf1d8d6baf0b6fb658e66361a407..581831cb19fdf9e8fd969bb30139e1358279a34d 100644
--- a/gcc/explow.h
+++ b/gcc/explow.h
@@ -106,7 +106,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
 extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
 
 /* Returns the address of the dynamic stack space without allocating it.  */
-extern rtx get_dynamic_stack_base (poly_int64, unsigned);
+extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
 
 /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
 extern rtx align_dynamic_address (rtx, unsigned);
diff --git a/gcc/explow.c b/gcc/explow.c
index 0fbc6d25b816457a3d13ed45d16b5dd0513cfacd..41c3f6ace49c0e55c080e10b917842b1b21d49eb 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1583,10 +1583,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
    OFFSET is the offset of the area into the virtual stack vars area.
 
    REQUIRED_ALIGN is the alignment (in bits) required for the region
-   of memory.  */
+   of memory.
+
+   BASE is the rtx of the base of this virtual stack vars area.
+   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+   on the stack.  */
 
 rtx
-get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
 {
   rtx target;
 
@@ -1594,7 +1598,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
     crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
 
   target = gen_reg_rtx (Pmode);
-  emit_move_insn (target, virtual_stack_vars_rtx);
+  emit_move_insn (target, base);
   target = expand_binop (Pmode, add_optab, target,
 			 gen_int_mode (offset, Pmode),
 			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index a32715ddb92e69b7ca7be28a8f17a369b891bd76..4f854fb994229fd4ed91d3b5cff7c7acff9a55bc 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
+/* Hardware Address Sanitizer.  */
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
+		      BT_FN_VOID_PTR_UINT8_PTRMODE, ATTR_NOTHROW_LIST)
+
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/target.def b/gcc/target.def
index dc20d262fd1b7da074bb15ff2cc06f9164a4ae17..0630ed7e0fa39d88316685b528cbb3077ac38721 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6859,6 +6859,71 @@ DEFHOOK
 pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
  bool, (), default_memtag_can_tag_addresses)
 
+DEFHOOK
+(tag_size,
+ "Return the size of a tag (in bits) for this platform.\n\
+\n\
+The default returns 8.",
+  uint8_t, (), default_memtag_tag_size)
+
+DEFHOOK
+(granule_size,
+ "Return the size in real memory that each byte in shadow memory refers to.\n\
+I.e. if a variable is @var{X} bytes long in memory, then this hook should\n\
+return the value @var{Y} such that the tag in shadow memory spans\n\
+@var{X}/@var{Y} bytes.\n\
+\n\
+Most variables will need to be aligned to this amount since two variables\n\
+that are neighbors in memory and share a tag granule would need to share\n\
+the same tag.\n\
+\n\
+The default returns 16.",
+  uint8_t, (), default_memtag_granule_size)
+
+DEFHOOK
+(insert_random_tag,
+ "Return an RTX representing the value of @var{untagged} but with a\n\
+(possibly) random tag in it.\n\
+Put that value into @var{target} if it is convenient to do so.\n\
+This function is used to generate a tagged base for the current stack frame.",
+  rtx, (rtx untagged, rtx target), default_memtag_insert_random_tag)
+
+DEFHOOK
+(add_tag,
+ "Return an RTX that represents the result of adding @var{addr_offset} to\n\
+the address in pointer @var{base} and @var{tag_offset} to the tag in pointer\n\
+@var{base}.\n\
+The resulting RTX must either be a valid memory address or be able to get\n\
+put into an operand with @code{force_operand}.\n\
+\n\
+Unlike other memtag hooks, this must return an expression and not emit any\n\
+RTL.",
+  rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
+  default_memtag_add_tag)
+
+DEFHOOK
+(set_tag,
+ "Return an RTX representing @var{untagged_base} but with the tag @var{tag}.\n\
+Try and store this in @var{target} if convenient.\n\
+@var{untagged_base} is required to have a zero tag when this hook is called.\n\
+The default of this hook is to set the top byte of @var{untagged_base} to\n\
+@var{tag}.",
+  rtx, (rtx untagged_base, rtx tag, rtx target), default_memtag_set_tag)
+
+DEFHOOK
+(extract_tag,
+ "Return an RTX representing the tag stored in @var{tagged_pointer}.\n\
+Store the result in @var{target} if it is convenient.\n\
+The default represents the top byte of the original pointer.",
+  rtx, (rtx tagged_pointer, rtx target), default_memtag_extract_tag)
+
+DEFHOOK
+(untagged_pointer,
+ "Return an RTX representing @var{tagged_pointer} with its tag set to zero.\n\
+Store the result in @var{target} if convenient.\n\
+The default clears the top byte of the original pointer.",
+  rtx, (rtx tagged_pointer, rtx target), default_memtag_untagged_pointer)
+
 HOOK_VECTOR_END (memtag)
 #undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 0065c686978d7120978430013c73b1055aaf95c7..68e8688a32f18481ee61f06879aacff20163105b 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -287,4 +287,12 @@ extern bool speculation_safe_value_not_needed (bool);
 extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 
 extern bool default_memtag_can_tag_addresses ();
+extern uint8_t default_memtag_tag_size ();
+extern uint8_t default_memtag_granule_size ();
+extern rtx default_memtag_insert_random_tag (rtx, rtx);
+extern rtx default_memtag_add_tag (rtx, poly_int64, uint8_t);
+extern rtx default_memtag_set_tag (rtx, rtx, rtx);
+extern rtx default_memtag_extract_tag (rtx, rtx);
+extern rtx default_memtag_untagged_pointer (rtx, rtx);
+
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 46cb536041d396c32fd08042581d6d5cd5ad0395..e66b1d0074b1921b7613f8e5444c7322c0479506 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -73,6 +73,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "flags.h"
 #include "explow.h"
+#include "expmed.h"
 #include "calls.h"
 #include "expr.h"
 #include "output.h"
@@ -86,6 +87,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "sbitmap.h"
 #include "function-abi.h"
+#include "attribs.h"
+#include "asan.h"
+#include "emit-rtl.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -2415,10 +2419,116 @@ default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
   return result;
 }
 
+/* How many bits to shift in order to access the tag bits.
+   The default is to store the tag in the top 8 bits of a 64 bit pointer, hence
+   shifting 56 bits will leave just the tag.  */
+#define HWASAN_SHIFT (GET_MODE_PRECISION (Pmode) - 8)
+#define HWASAN_SHIFT_RTX GEN_INT (HWASAN_SHIFT)
+
 bool
 default_memtag_can_tag_addresses ()
 {
   return false;
 }
 
+uint8_t
+default_memtag_tag_size ()
+{
+  return 8;
+}
+
+uint8_t
+default_memtag_granule_size ()
+{
+  return 16;
+}
+
+/* The default implementation of TARGET_MEMTAG_INSERT_RANDOM_TAG.  */
+rtx
+default_memtag_insert_random_tag (rtx untagged, rtx target)
+{
+  gcc_assert (param_hwasan_instrument_stack);
+  if (param_hwasan_random_frame_tag)
+    {
+      rtx fn = init_one_libfunc ("__hwasan_generate_tag");
+      rtx new_tag = emit_library_call_value (fn, NULL_RTX, LCT_NORMAL, QImode);
+      return targetm.memtag.set_tag (untagged, new_tag, target);
+    }
+  else
+    {
+      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
+	 In the future we may add the option emit random tags with inline
+	 instrumentation instead of function calls.  This would be the same
+	 between the kernel and userland.  */
+      return untagged;
+    }
+}
+
+/* The default implementation of TARGET_MEMTAG_ADD_TAG.  */
+rtx
+default_memtag_add_tag (rtx base, poly_int64 offset, uint8_t tag_offset)
+{
+  /* Need to look into what the most efficient code sequence is.
+     This is a code sequence that would be emitted *many* times, so we
+     want it as small as possible.
+
+     There are two places where tag overflow is a question:
+       - Tagging the shadow stack.
+	  (both tagging and untagging).
+       - Tagging addressable pointers.
+
+     We need to ensure both behaviors are the same (i.e. that the tag that
+     ends up in a pointer after "overflowing" the tag bits with a tag addition
+     is the same that ends up in the shadow space).
+
+     The aim is that the behavior of tag addition should follow modulo
+     wrapping in both instances.
+
+     The libhwasan code doesn't have any path that increments a pointer's tag,
+     which means it has no opinion on what happens when a tag increment
+     overflows (and hence we can choose our own behavior).  */
+
+  offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
+  return plus_constant (Pmode, base, offset);
+}
+
+/* The default implementation of TARGET_MEMTAG_SET_TAG.  */
+rtx
+default_memtag_set_tag (rtx untagged, rtx tag, rtx target)
+{
+  gcc_assert (GET_MODE (untagged) == Pmode);
+  gcc_assert (GET_MODE (tag) == QImode);
+  tag = expand_simple_binop (Pmode, ASHIFT, tag, HWASAN_SHIFT_RTX, tag,
+			     /* unsignedp = */1, OPTAB_WIDEN);
+  rtx ret = expand_simple_binop (Pmode, IOR, untagged, tag, target,
+				 /* unsignedp = */1, OPTAB_DIRECT);
+  gcc_assert (ret);
+  return ret;
+}
+
+/* The default implementation of TARGET_MEMTAG_EXTRACT_TAG.  */
+rtx
+default_memtag_extract_tag (rtx tagged_pointer, rtx target)
+{
+  rtx tag = expand_simple_binop (Pmode, LSHIFTRT, tagged_pointer,
+				 HWASAN_SHIFT_RTX, target,
+				 /* unsignedp = */0,
+				 OPTAB_DIRECT);
+  rtx ret = gen_lowpart (QImode, tag);
+  gcc_assert (ret);
+  return ret;
+}
+
+/* The default implementation of TARGET_MEMTAG_UNTAGGED_POINTER.  */
+rtx
+default_memtag_untagged_pointer (rtx tagged_pointer, rtx target)
+{
+  rtx tag_mask = gen_int_mode ((HOST_WIDE_INT_1U << HWASAN_SHIFT) - 1, Pmode);
+  rtx untagged_base = expand_simple_binop (Pmode, AND, tagged_pointer,
+					   tag_mask, target, true,
+					   OPTAB_DIRECT);
+  gcc_assert (untagged_base);
+  return untagged_base;
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index a644a7307970b00c5d2433b3f7f3d228db00c1e6..7e12b91f4498d1165d6aae1179217fc2b8a55c7b 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -512,6 +512,9 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      if (flag_sanitize & SANITIZE_HWADDRESS)
+	hwasan_finish_file ();
+
       omp_finish_file ();
 
       output_shared_constant_pool ();


[-- Attachment #2: hwasan-v54.patch.gz --]
[-- Type: application/gzip, Size: 15361 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 6/X] libsanitizer:  Add hwasan pass and associated gimple changes
  2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
                         ` (6 preceding siblings ...)
  2020-11-16 15:37       ` [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN Matthew Malcomson
@ 2020-11-16 15:37       ` Matthew Malcomson
  2020-11-16 15:37       ` [PATCH 7/X] libsanitizer: Add tests Matthew Malcomson
  2020-11-19 13:01       ` Update [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes Matthew Malcomson
  9 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-16 15:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: Richard Earnshaw, Kyrylo Tkachov, Richard Sandiford, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 55081 bytes --]

There are four main features to this change:

1) Check pointer tags match address tags.

When sanitizing for hwasan we now put HWASAN_CHECK internal functions before
memory accesses in the `asan` pass.  This checks that a tag in the pointer
being used match the tag stored in shadow memory for the memory region being
used.

These internal functions are expanded into actual checks in the sanopt
pass that happens just before expansion into RTL.

We use the same mechanism that currently inserts ASAN_CHECK internal
functions to insert the new HWASAN_CHECK functions.

2) Instrument known builtin function calls.

Handle all builtin functions that we know use memory accesses.
This commit uses the machinery added for ASAN to identify builtin
functions that access memory.

The main differences between the approaches for HWASAN and ASAN are:
 - libhwasan intercepts much less builtin functions.
 - Alloca needs to be transformed differently (instead of adding
   redzones it needs to tag shadow memory and return a tagged pointer).
 - stack_restore needs to untag the shadow stack between the current
   position and where it's going.
 - `noreturn` functions can not be handled by simply unpoisoning the
   entire shadow stack -- there is no "always valid" tag.
   (exceptions and things such as longjmp need to be handled in a
   different way, usually in the runtime).

For hardware implemented checking (such as AArch64's memory tagging
extension) alloca and stack_restore will need to be handled by hooks in
the backend rather than transformation at the gimple level.  This will
allow architecture specific handling of such stack modifications.

3) Introduce HWASAN block-scope poisoning

Here we use exactly the same mechanism as ASAN_MARK to poison/unpoison
variables on entry/exit of a block.

In order to simply use the exact same machinery we're using the same
internal functions until the SANOPT pass.  This means that all handling
of ASAN_MARK is the same.
This has the negative that the naming may be a little confusing, but a
positive that handling of the internal function doesn't have to be
duplicated for a function that behaves exactly the same but has a
different name.

gcc/ChangeLog:

	* asan.c (asan_instrument_reads): New.
	(asan_instrument_writes): New.
	(asan_memintrin): New.
	(handle_builtin_stack_restore): Account for HWASAN.
	(hwasan_emit_round_up): New.
	(handle_builtin_alloca): Account for HWASAN.
	(get_mem_refs_of_builtin_call): Special case strlen for HWASAN.
	(hwasan_instrument_reads): New.
	(hwasan_instrument_writes): New.
	(hwasan_memintrin): New.
	(report_error_func): Assert not HWASAN.
	(build_check_stmt): Make HWASAN_CHECK instead of ASAN_CHECK.
	(instrument_derefs): HWASAN does not tag globals.
	(instrument_builtin_call): Use new helper functions.
	(maybe_instrument_call): Don't instrument `noreturn` functions.
	(initialize_sanitizer_builtins): Add new type.
	(asan_expand_mark_ifn): Account for HWASAN.
	(asan_expand_check_ifn): Assert never called by HWASAN.
	(asan_expand_poison_ifn): Account for HWASAN.
	(asan_instrument): Branch based on whether using HWASAN or ASAN.
	(pass_asan::gate): Return true if sanitizing HWASAN.
	(pass_asan_O0::gate): Return true if sanitizing HWASAN.
	(hwasan_check_func): New.
	(hwasan_expand_check_ifn): New.
	(hwasan_expand_mark_ifn): New.
	(gate_hwasan): New.
	* asan.h (hwasan_expand_check_ifn): New decl.
	(hwasan_expand_mark_ifn): New decl.
	(gate_hwasan): New decl.
	(asan_intercepted_p): Always false for hwasan.
	(asan_sanitize_use_after_scope): Account for HWASAN.
	* builtin-types.def (BT_FN_PTR_CONST_PTR_UINT8): New.
	* gimple-pretty-print.c (dump_gimple_call_args): Account for
	HWASAN.
	* gimplify.c (asan_poison_variable): Account for HWASAN.
	(gimplify_function_tree): Remove requirement of
	SANITIZE_ADDRESS, requiring asan or hwasan is accounted for in
	`asan_sanitize_use_after_scope`.
	* internal-fn.c (expand_HWASAN_CHECK): New.
	(expand_HWASAN_ALLOCA_UNPOISON): New.
	(expand_HWASAN_CHOOSE_TAG): New.
	(expand_HWASAN_MARK): New.
	(expand_HWASAN_SET_TAG): New.
	* internal-fn.def (HWASAN_ALLOCA_UNPOISON): New.
	(HWASAN_CHOOSE_TAG): New.
	(HWASAN_CHECK): New.
	(HWASAN_MARK): New.
	(HWASAN_SET_TAG): New.
	* sanitizer.def (BUILT_IN_HWASAN_LOAD1): New.
	(BUILT_IN_HWASAN_LOAD2): New.
	(BUILT_IN_HWASAN_LOAD4): New.
	(BUILT_IN_HWASAN_LOAD8): New.
	(BUILT_IN_HWASAN_LOAD16): New.
	(BUILT_IN_HWASAN_LOADN): New.
	(BUILT_IN_HWASAN_STORE1): New.
	(BUILT_IN_HWASAN_STORE2): New.
	(BUILT_IN_HWASAN_STORE4): New.
	(BUILT_IN_HWASAN_STORE8): New.
	(BUILT_IN_HWASAN_STORE16): New.
	(BUILT_IN_HWASAN_STOREN): New.
	(BUILT_IN_HWASAN_LOAD1_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD2_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD4_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD8_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD16_NOABORT): New.
	(BUILT_IN_HWASAN_LOADN_NOABORT): New.
	(BUILT_IN_HWASAN_STORE1_NOABORT): New.
	(BUILT_IN_HWASAN_STORE2_NOABORT): New.
	(BUILT_IN_HWASAN_STORE4_NOABORT): New.
	(BUILT_IN_HWASAN_STORE8_NOABORT): New.
	(BUILT_IN_HWASAN_STORE16_NOABORT): New.
	(BUILT_IN_HWASAN_STOREN_NOABORT): New.
	(BUILT_IN_HWASAN_TAG_MISMATCH4): New.
	(BUILT_IN_HWASAN_HANDLE_LONGJMP): New.
	(BUILT_IN_HWASAN_TAG_PTR): New.
	* sanopt.c (sanopt_optimize_walker): Act during hwasan.
	(pass_sanopt::execute): Act during hwasan.
	* toplev.c (compile_file): Use `gate_hwasan` function.



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


diff --git a/gcc/asan.h b/gcc/asan.h
index 9b6b9c0a9b4daeaf176990dfd8dfbe0b0127bb39..b1ca402fc4650db0f3568db2e20cf30899a5430e 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -50,6 +50,9 @@ extern void hwasan_finish_file (void);
 extern bool hwasan_sanitize_p (void);
 extern bool hwasan_sanitize_stack_p (void);
 extern bool hwasan_sanitize_allocas_p (void);
+extern bool hwasan_expand_check_ifn (gimple_stmt_iterator *, bool);
+extern bool hwasan_expand_mark_ifn (gimple_stmt_iterator *);
+extern bool gate_hwasan (void);
 
 extern gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
@@ -182,6 +185,9 @@ extern hash_set<tree> *asan_handled_variables;
 static inline bool
 asan_intercepted_p (enum built_in_function fcode)
 {
+  if (hwasan_sanitize_p ())
+    return false;
+
   return fcode == BUILT_IN_INDEX
 	 || fcode == BUILT_IN_MEMCHR
 	 || fcode == BUILT_IN_MEMCMP
@@ -210,7 +216,8 @@ asan_intercepted_p (enum built_in_function fcode)
 static inline bool
 asan_sanitize_use_after_scope (void)
 {
-  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
+  return (flag_sanitize_address_use_after_scope
+	  && (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ()));
 }
 
 /* Return true if DECL should be guarded on the stack.  */
diff --git a/gcc/asan.c b/gcc/asan.c
index 8f0364ef77762678ecff2da36db8536c27d46b48..d2a0d9e3912da62eaacfe0d94da8cdcacca878f5 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -365,6 +365,25 @@ asan_sanitize_allocas_p (void)
   return (asan_sanitize_stack_p () && param_asan_protect_allocas);
 }
 
+bool
+asan_instrument_reads (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_instrument_reads);
+}
+
+bool
+asan_instrument_writes (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_instrument_writes);
+}
+
+bool
+asan_memintrin (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_memintrin);
+}
+
+
 /* Checks whether section SEC should be sanitized.  */
 
 static bool
@@ -625,23 +644,73 @@ get_last_alloca_addr ()
    To overcome the issue we use following trick: pass new_sp as a second
    parameter to __asan_allocas_unpoison and rewrite it during expansion with
    new_sp + (virtual_dynamic_stack_rtx - sp) later in
-   expand_asan_emit_allocas_unpoison function.  */
+   expand_asan_emit_allocas_unpoison function.
+
+   HWASAN needs to do very similar, the eventual pseudocode should be:
+      __hwasan_tag_memory (virtual_stack_dynamic_rtx,
+			   0,
+			   new_sp - sp);
+      __builtin_stack_restore (new_sp)
+
+   Need to use the same trick to handle STACK_DYNAMIC_OFFSET as described
+   above.  */
 
 static void
 handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p ()))
     return;
 
-  tree last_alloca = get_last_alloca_addr ();
   tree restored_stack = gimple_call_arg (call, 0);
-  tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
-  gimple *g = gimple_build_call (fn, 2, last_alloca, restored_stack);
-  gsi_insert_before (iter, g, GSI_SAME_STMT);
-  g = gimple_build_assign (last_alloca, restored_stack);
+
+  gimple *g;
+
+  if (hwasan_sanitize_allocas_p ())
+    {
+      enum internal_fn fn = IFN_HWASAN_ALLOCA_UNPOISON;
+      /* There is only one piece of information `expand_HWASAN_ALLOCA_UNPOISON`
+	 needs to work.  This is the length of the area that we're
+	 deallocating.  Since the stack pointer is known at expand time, the
+	 position of the new stack pointer after deallocation is enough
+	 information to calculate this length.  */
+      g = gimple_build_call_internal (fn, 1, restored_stack);
+    }
+  else
+    {
+      tree last_alloca = get_last_alloca_addr ();
+      tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
+      g = gimple_build_call (fn, 2, last_alloca, restored_stack);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      g = gimple_build_assign (last_alloca, restored_stack);
+    }
+
   gsi_insert_before (iter, g, GSI_SAME_STMT);
 }
 
+/* Emit gimple statements into &stmts that take the size given in `len` and
+   generate a size that is guaranteed to be rounded upwards to `align`.
+
+   This is a helper function for both handle_builtin_alloca and
+   asan_expand_mark_ifn when using HWASAN.
+
+   Return the tree node representing this size, it is of TREE_TYPE
+   size_type_node.  */
+
+static tree
+hwasan_emit_round_up (gimple_seq *seq, location_t loc, tree old_size,
+		      uint8_t align)
+{
+  uint8_t tg_mask = align - 1;
+  /* tree new_size = (old_size + tg_mask) & ~tg_mask;  */
+  tree tree_mask = build_int_cst (size_type_node, tg_mask);
+  tree oversize = gimple_build (seq, loc, PLUS_EXPR, size_type_node, old_size,
+				tree_mask);
+
+  tree mask = build_int_cst (size_type_node, -align);
+  return gimple_build (seq, loc, BIT_AND_EXPR, size_type_node, oversize, mask);
+}
+
 /* Deploy and poison redzones around __builtin_alloca call.  To do this, we
    should replace this call with another one with changed parameters and
    replace all its uses with new address, so
@@ -668,14 +737,12 @@ handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 static void
 handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p ()))
     return;
 
   gassign *g;
   gcall *gg;
-  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
-
-  tree last_alloca = get_last_alloca_addr ();
   tree callee = gimple_call_fndecl (call);
   tree old_size = gimple_call_arg (call, 0);
   tree ptr_type = gimple_call_lhs (call) ? TREE_TYPE (gimple_call_lhs (call))
@@ -685,6 +752,76 @@ handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
     = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
       ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
 
+  if (hwasan_sanitize_allocas_p ())
+    {
+      /* In this code sequence we are going to emit ifn_HWASAN_CHOOSE_TAG.
+	 That internal function will need to use the hwasan frame base pointer.
+	 Here we record in the hwasan stack handling machinery that the hwasan
+	 frame base pointer will need to be initialized.  */
+      hwasan_record_frame_base_needed ();
+      gimple_seq stmts = NULL;
+      location_t loc = gimple_location (gsi_stmt (*iter));
+      /*
+	 HWASAN needs a different expansion.
+
+	 addr = __builtin_alloca (size, align);
+
+	 should be replaced by
+
+	 new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment;
+	 untagged_addr = __builtin_alloca (new_size, align);
+	 tag = __hwasan_choose_alloca_tag ();
+	 addr = ifn_HWASAN_SET_TAG (untagged_addr, tag);
+	 __hwasan_tag_memory (untagged_addr, tag, new_size);
+	*/
+      /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start on
+	 a tag granule.  */
+      align = align > HWASAN_TAG_GRANULE_SIZE ? align : HWASAN_TAG_GRANULE_SIZE;
+
+      tree old_size = gimple_call_arg (call, 0);
+      tree new_size = hwasan_emit_round_up (&stmts, loc, old_size,
+					    HWASAN_TAG_GRANULE_SIZE);
+
+      /* Make the alloca call */
+      tree untagged_addr
+	= gimple_build (&stmts, loc,
+			as_combined_fn (BUILT_IN_ALLOCA_WITH_ALIGN), ptr_type,
+			new_size, build_int_cst (size_type_node, align));
+
+      /* Choose the tag.
+	 Here we use an internal function so we can choose the tag at expand
+	 time.  We need the decision to be made after stack variables have been
+	 assigned their tag (i.e. once the hwasan_frame_tag_offset variable has
+	 been set to one after the last stack variables tag).  */
+      gcall *stmt = gimple_build_call_internal (IFN_HWASAN_CHOOSE_TAG, 0);
+      tree tag = make_ssa_name (unsigned_char_type_node);
+      gimple_call_set_lhs (stmt, tag);
+      gimple_set_location (stmt, loc);
+      gimple_seq_add_stmt_without_update (&stmts, stmt);
+
+      /* Add tag to pointer.  */
+      tree addr
+	= gimple_build (&stmts, loc, as_combined_fn (IFN_HWASAN_SET_TAG),
+			ptr_type, untagged_addr, tag);
+
+      /* Tag shadow memory.
+	 NOTE: require using `untagged_addr` here for libhwasan API.  */
+      gimple_build (&stmts, loc, as_combined_fn (BUILT_IN_HWASAN_TAG_MEM),
+		    void_type_node, untagged_addr, tag, new_size);
+
+      /* Insert the built up code sequence into the original instruction stream
+	 the iterator points to.  */
+      gsi_insert_seq_before (iter, stmts, GSI_SAME_STMT);
+
+      /* Finally, replace old alloca ptr with NEW_ALLOCA.  */
+      replace_call_with_value (iter, addr);
+      return;
+    }
+
+  tree last_alloca = get_last_alloca_addr ();
+  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
+
+
   /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
      bytes of allocated space.  Otherwise, align alloca to ASAN_RED_ZONE_SIZE
      manually.  */
@@ -837,6 +974,31 @@ get_mem_refs_of_builtin_call (gcall *call,
       break;
 
     case BUILT_IN_STRLEN:
+      /* Special case strlen here since its length is taken from its return
+	 value.
+
+	 The approach taken by the sanitizers is to check a memory access
+	 before it's taken.  For ASAN strlen is intercepted by libasan, so no
+	 check is inserted by the compiler.
+
+	 This function still returns `true` and provides a length to the rest
+	 of the ASAN pass in order to record what areas have been checked,
+	 avoiding superfluous checks later on.
+
+	 HWASAN does not intercept any of these internal functions.
+	 This means that checks for memory accesses must be inserted by the
+	 compiler.
+	 strlen is a special case, because we can tell the length from the
+	 return of the function, but that is not known until after the function
+	 has returned.
+
+	 Hence we can't check the memory access before it happens.
+	 We could check the memory access after it has already happened, but
+	 for now we choose to just ignore `strlen` calls.
+	 This decision was simply made because that means the special case is
+	 limited to this one case of this one function.  */
+      if (hwasan_sanitize_p ())
+	return false;
       source0 = gimple_call_arg (call, 0);
       len = gimple_call_lhs (call);
       break;
@@ -1406,6 +1568,156 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+
+/* HWAddressSanitizer (hwasan) is a probabilistic method for detecting
+   out-of-bounds and use-after-free bugs.
+   Read more:
+   http://code.google.com/p/address-sanitizer/
+
+   Similar to AddressSanitizer (asan) it consists of two parts: the
+   instrumentation module in this file, and a run-time library.
+
+   The instrumentation module adds a run-time check before every memory insn in
+   the same manner as asan (see the block comment for AddressSanitizer above).
+   Currently, hwasan only adds out-of-line instrumentation, where each check is
+   implemented as a function call to the run-time library.  Hence a check for a
+   load of N bytes from address X would be implemented with a function call to
+   __hwasan_loadN(X), and checking a store of N bytes from address X would be
+   implemented with a function call to __hwasan_storeN(X).
+
+   The main difference between hwasan and asan is in the information stored to
+   help this checking.  Both sanitizers use a shadow memory area which stores
+   data recording the state of main memory at a corresponding address.
+
+   For hwasan, each 16 byte granule in main memory has a corresponding 1 byte
+   in shadow memory.  This shadow address can be calculated with equation:
+     (addr >> log_2(HWASAN_TAG_GRANULE_SIZE))
+	  + __hwasan_shadow_memory_dynamic_address;
+   The conversion between real and shadow memory for asan is given in the block
+   comment at the top of this file.
+   The description of how this shadow memory is laid out for asan is in the
+   block comment at the top of this file, here we describe how this shadow
+   memory is used for hwasan.
+
+   For hwasan, each variable is assigned a byte-sized 'tag'.  The extent of
+   the shadow memory for that variable is filled with the assigned tag, and
+   every pointer referencing that variable has its top byte set to the same
+   tag.  The run-time library redefines malloc so that every allocation returns
+   a tagged pointer and tags the corresponding shadow memory with the same tag.
+
+   On each pointer dereference the tag found in the pointer is compared to the
+   tag found in the shadow memory corresponding to the accessed memory address.
+   If these tags are found to differ then this memory access is judged to be
+   invalid and a report is generated.
+
+   This method of bug detection is not perfect -- it can not catch every bad
+   access -- but catches them probabilistically instead.  There is always the
+   possibility that an invalid memory access will happen to access memory
+   tagged with the same tag as the pointer that this access used.
+   The chances of this are approx. 0.4% for any two uncorrelated objects.
+
+   Random tag generation can mitigate this problem by decreasing the
+   probability that an invalid access will be missed in the same manner over
+   multiple runs.  i.e. if two objects are tagged the same in one run of the
+   binary they are unlikely to be tagged the same in the next run.
+   Both heap and stack allocated objects have random tags by default.
+
+   [16 byte granule implications]
+    Since the shadow memory only has a resolution on real memory of 16 bytes,
+    invalid accesses that are within the same 16 byte granule as a valid
+    address will not be caught.
+
+    There is a "short-granule" feature in the runtime library which does catch
+    such accesses, but this feature is not implemented for stack objects (since
+    stack objects are allocated and tagged by compiler instrumentation, and
+    this feature has not yet been implemented in GCC instrumentation).
+
+    Another outcome of this 16 byte resolution is that each tagged object must
+    be 16 byte aligned.  If two objects were to share any 16 byte granule in
+    memory, then they both would have to be given the same tag, and invalid
+    accesses to one using a pointer to the other would be undetectable.
+
+   [Compiler instrumentation]
+    Compiler instrumentation ensures that two adjacent buffers on the stack are
+    given different tags, this means an access to one buffer using a pointer
+    generated from the other (e.g. through buffer overrun) will have mismatched
+    tags and be caught by hwasan.
+
+    We don't randomly tag every object on the stack, since that would require
+    keeping many registers to record each tag.  Instead we randomly generate a
+    tag for each function frame, and each new stack object uses a tag offset
+    from that frame tag.
+    i.e. each object is tagged as RFT + offset, where RFT is the "random frame
+    tag" generated for this frame.
+    This means that randomisation does not peturb the difference between tags
+    on tagged stack objects within a frame, but this is mitigated by the fact
+    that objects with the same tag within a frame are very far apart
+    (approx. 2^HWASAN_TAG_SIZE objects apart).
+
+    As a demonstration, using the same example program as in the asan block
+    comment above:
+
+     int
+     foo ()
+     {
+       char a[23] = {0};
+       int b[2] = {0};
+
+       a[5] = 1;
+       b[1] = 2;
+
+       return a[5] + b[1];
+     }
+
+    On AArch64 the stack will be ordered as follows for the above function:
+
+    Slot 1/ [24 bytes for variable 'a']
+    Slot 2/ [8 bytes padding for alignment]
+    Slot 3/ [8 bytes for variable 'b']
+    Slot 4/ [8 bytes padding for alignment]
+
+    (The padding is there to ensure 16 byte alignment as described in the 16
+     byte granule implications).
+
+    While the shadow memory will be ordered as follows:
+
+    - 2 bytes (representing 32 bytes in real memory) tagged with RFT + 1.
+    - 1 byte (representing 16 bytes in real memory) tagged with RFT + 2.
+
+    And any pointer to "a" will have the tag RFT + 1, and any pointer to "b"
+    will have the tag RFT + 2.
+
+   [Top Byte Ignore requirements]
+    Hwasan requires the ability to store an 8 bit tag in every pointer.  There
+    is no instrumentation done to remove this tag from pointers before
+    dereferencing, which means the hardware must ignore this tag during memory
+    accesses.
+
+    Architectures where this feature is available should indicate this using
+    the TARGET_MEMTAG_CAN_TAG_ADDRESSES hook.
+
+   [Stack requires cleanup on unwinding]
+    During normal operation of a hwasan sanitized program more space in the
+    shadow memory becomes tagged as the stack grows.  As the stack shrinks this
+    shadow memory space must become untagged.  If it is not untagged then when
+    the stack grows again (during other function calls later on in the program)
+    objects on the stack that are usually not tagged (e.g. parameters passed on
+    the stack) can be placed in memory whose shadow space is tagged with
+    something else, and accesses can cause false positive reports.
+
+    Hence we place untagging code on every epilogue of functions which tag some
+    stack objects.
+
+    Moreover, the run-time library intercepts longjmp & setjmp to untag when
+    the stack is unwound this way.
+
+    C++ exceptions are not yet handled, which means this sanitizer can not
+    handle C++ code that throws exceptions -- it will give false positives
+    after an exception has been thrown.  The implementation that the hwasan
+    library has for handling these relies on the frame pointer being after any
+    local variables.  This is not generally the case for GCC.  */
+
+
 /* Returns whether we are tagging pointers and checking those tags on memory
    access.  */
 bool
@@ -1428,6 +1740,27 @@ hwasan_sanitize_allocas_p (void)
   return (hwasan_sanitize_stack_p () && param_hwasan_protect_allocas);
 }
 
+/* Should we instrument reads?  */
+bool
+hwasan_instrument_reads (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_reads);
+}
+
+/* Should we instrument writes?  */
+bool
+hwasan_instrument_writes (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_writes);
+}
+
+/* Should we instrument builtin calls?  */
+bool
+hwasan_memintrin (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_mem_intrinsics);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1923,6 +2256,8 @@ static tree
 report_error_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
 		   int *nargs)
 {
+  gcc_assert (!hwasan_sanitize_p ());
+
   static enum built_in_function report[2][2][6]
     = { { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
 	    BUILT_IN_ASAN_REPORT_LOAD4, BUILT_IN_ASAN_REPORT_LOAD8,
@@ -2215,6 +2550,7 @@ build_check_stmt (location_t loc, tree base, tree len,
   gimple *g;
 
   gcc_assert (!(size_in_bytes > 0 && !is_non_zero_len));
+  gcc_assert (size_in_bytes == -1 || size_in_bytes >= 1);
 
   gsi = *iter;
 
@@ -2259,7 +2595,11 @@ build_check_stmt (location_t loc, tree base, tree len,
   if (is_scalar_access)
     flags |= ASAN_CHECK_SCALAR_ACCESS;
 
-  g = gimple_build_call_internal (IFN_ASAN_CHECK, 4,
+  enum internal_fn fn = hwasan_sanitize_p ()
+    ? IFN_HWASAN_CHECK
+    : IFN_ASAN_CHECK;
+
+  g = gimple_build_call_internal (fn, 4,
 				  build_int_cst (integer_type_node, flags),
 				  base, len,
 				  build_int_cst (integer_type_node,
@@ -2283,9 +2623,9 @@ static void
 instrument_derefs (gimple_stmt_iterator *iter, tree t,
 		   location_t location, bool is_store)
 {
-  if (is_store && !param_asan_instrument_writes)
+  if (is_store && !(asan_instrument_writes () || hwasan_instrument_writes ()))
     return;
-  if (!is_store && !param_asan_instrument_reads)
+  if (!is_store && !(asan_instrument_reads () || hwasan_instrument_reads ()))
     return;
 
   tree type, base;
@@ -2346,7 +2686,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
     {
       if (DECL_THREAD_LOCAL_P (inner))
 	return;
-      if (!param_asan_globals && is_global_var (inner))
+      if ((hwasan_sanitize_p () || !param_asan_globals)
+	  && is_global_var (inner))
         return;
       if (!TREE_STATIC (inner))
 	{
@@ -2439,7 +2780,7 @@ instrument_mem_region_access (tree base, tree len,
 static bool
 instrument_builtin_call (gimple_stmt_iterator *iter)
 {
-  if (!param_asan_memintrin)
+  if (!(asan_memintrin () || hwasan_memintrin ()))
     return false;
 
   bool iter_advanced_p = false;
@@ -2575,10 +2916,27 @@ maybe_instrument_call (gimple_stmt_iterator *iter)
 	      break;
 	    }
 	}
-      tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
-      gimple *g = gimple_build_call (decl, 0);
-      gimple_set_location (g, gimple_location (stmt));
-      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      /* If a function does not return, then we must handle clearing up the
+	 shadow stack accordingly.  For ASAN we can simply set the entire stack
+	 to "valid" for accesses by setting the shadow space to 0 and all
+	 accesses will pass checks.  That means that some bad accesses may be
+	 missed, but we will not report any false positives.
+
+	 This is not possible for HWASAN. Since there is no "always valid" tag
+	 we can not set any space to "always valid".  If we were to clear the
+	 entire shadow stack then code resuming from `longjmp` or a caught
+	 exception would trigger false positives when correctly accessing
+	 variables on the stack.  Hence we need to handle things like
+	 `longjmp`, thread exit, and exceptions in a different way.  These
+	 problems must be handled externally to the compiler, e.g. in the
+	 language runtime.  */
+      if (! hwasan_sanitize_p ())
+	{
+	  tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
+	  gimple *g = gimple_build_call (decl, 0);
+	  gimple_set_location (g, gimple_location (stmt));
+	  gsi_insert_before (iter, g, GSI_SAME_STMT);
+	}
     }
 
   bool instrumented = false;
@@ -2977,6 +3335,9 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_PTR_CONST_PTR_UINT8
+    = build_function_type_list (ptr_type_node, const_ptr_type_node,
+				unsigned_char_type_node, NULL_TREE);
   tree BT_FN_VOID_PTR_UINT8_PTRMODE
     = build_function_type_list (void_type_node, ptr_type_node,
 				unsigned_char_type_node,
@@ -3300,6 +3661,43 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 
   gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
 
+  if (hwasan_sanitize_p ())
+    {
+      gcc_assert (param_hwasan_instrument_stack);
+      gimple_seq stmts = NULL;
+      /* Here we swap ASAN_MARK calls for HWASAN_MARK.
+	 This is because we are using the approach of using ASAN_MARK as a
+	 synonym until here.
+	 That approach means we don't yet have to duplicate all the special
+	 cases for ASAN_MARK and ASAN_POISON with the exact same handling but
+	 called HWASAN_MARK etc.
+
+	 N.b. __asan_poison_stack_memory (which implements ASAN_MARK for ASAN)
+	 rounds the size up to its shadow memory granularity, while
+	 __hwasan_tag_memory (which implements the same for HWASAN) does not.
+	 Hence we emit HWASAN_MARK with an aligned size unlike ASAN_MARK.  */
+      tree len = gimple_call_arg (g, 2);
+      tree new_len;
+      /* If the length is a simple SHWI, then we can calculate the rounded up
+	 length directly.  Otherwise we need to emit gimple to do this
+	 rounding at runtime.  */
+      if (tree_fits_shwi_p (len))
+	{
+	  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+	  uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
+	  size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
+	  new_len = build_int_cst (pointer_sized_int_node, size_in_bytes);
+	}
+      else
+	new_len = hwasan_emit_round_up (&stmts, loc, len,
+					HWASAN_TAG_GRANULE_SIZE);
+      gimple_build (&stmts, loc, as_combined_fn (IFN_HWASAN_MARK),
+		    void_type_node, gimple_call_arg (g, 0),
+		    base, new_len);
+      gsi_replace_with_seq (iter, stmts, true);
+      return false;
+    }
+
   if (is_poison)
     {
       if (asan_handled_variables == NULL)
@@ -3374,6 +3772,7 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 bool
 asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
 {
+  gcc_assert (!hwasan_sanitize_p ());
   gimple *g = gsi_stmt (*iter);
   location_t loc = gimple_location (g);
   bool recover_p;
@@ -3647,11 +4046,61 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
 
       int nargs;
       bool store_p = gimple_call_internal_p (use, IFN_ASAN_POISON_USE);
-      tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
-				    &nargs);
-
-      gcall *call = gimple_build_call (fun, 1,
-				       build_fold_addr_expr (shadow_var));
+      gcall *call;
+      if (hwasan_sanitize_p ())
+	{
+	  tree fun = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MISMATCH4);
+	  /* NOTE: hwasan has no __hwasan_report_* functions like asan does.
+		We use __hwasan_tag_mismatch4 with arguments that tell it the
+		size of access and load to report all tag mismatches.
+
+		The arguments to this function are:
+		  Address of invalid access.
+		  Bitfield containing information about the access
+		    (access_info)
+		  Pointer to a frame of registers
+		    (for use in printing the contents of registers in a dump)
+		    Not used yet -- to be used by inline instrumentation.
+		  Size of access.
+
+		The access_info bitfield encodes the following pieces of
+		information:
+		  - Is this a store or load?
+		    access_info & 0x10  =>  store
+		  - Should the program continue after reporting the error?
+		    access_info & 0x20  =>  recover
+		  - What size access is this (not used here since we can always
+		    pass the size in the last argument)
+
+		    if (access_info & 0xf == 0xf)
+		      size is taken from last argument.
+		    else
+		      size == 1 << (access_info & 0xf)
+
+		The last argument contains the size of the access iff the
+		access_info size indicator is 0xf (we always use this argument
+		rather than storing the size in the access_info bitfield).
+
+		See the function definition `__hwasan_tag_mismatch4` in
+		libsanitizer/hwasan for the full definition.
+		*/
+	  unsigned access_info = (0x20 * recover_p)
+	    + (0x10 * store_p)
+	    + (0xf);
+	  call = gimple_build_call (fun, 4,
+				    build_fold_addr_expr (shadow_var),
+				    build_int_cst (pointer_sized_int_node,
+						   access_info),
+				    build_int_cst (pointer_sized_int_node, 0),
+				    size);
+	}
+      else
+	{
+	  tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
+					&nargs);
+	  call = gimple_build_call (fun, 1,
+				    build_fold_addr_expr (shadow_var));
+	}
       gimple_set_location (call, gimple_location (use));
       gimple *call_to_insert = call;
 
@@ -3699,6 +4148,12 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
 static unsigned int
 asan_instrument (void)
 {
+  if (hwasan_sanitize_p ())
+    {
+      transform_statements ();
+      return 0;
+    }
+
   if (shadow_ptr_types[0] == NULL_TREE)
     asan_init_shadow_ptr_types ();
   transform_statements ();
@@ -3736,7 +4191,7 @@ public:
 
   /* opt_pass methods: */
   opt_pass * clone () { return new pass_asan (m_ctxt); }
-  virtual bool gate (function *) { return gate_asan (); }
+  virtual bool gate (function *) { return gate_asan () || gate_hwasan (); }
   virtual unsigned int execute (function *) { return asan_instrument (); }
 
 }; // class pass_asan
@@ -3772,7 +4227,10 @@ public:
   {}
 
   /* opt_pass methods: */
-  virtual bool gate (function *) { return !optimize && gate_asan (); }
+  virtual bool gate (function *)
+    {
+      return !optimize && (gate_asan () || gate_hwasan ());
+    }
   virtual unsigned int execute (function *) { return asan_instrument (); }
 
 }; // class pass_asan_O0
@@ -3785,6 +4243,8 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/*  HWASAN  */
+
 /* For stack tagging:
 
    Return the offset from the frame base tag that the "next" expanded object
@@ -4167,4 +4627,138 @@ hwasan_truncate_to_tag_size (rtx tag, rtx target)
   return tag;
 }
 
+/* Construct a function tree for __hwasan_{load,store}{1,2,4,8,16,_n}.
+   IS_STORE is either 1 (for a store) or 0 (for a load).  */
+static combined_fn
+hwasan_check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
+		   int *nargs)
+{
+  static enum built_in_function check[2][2][6]
+    = { { { BUILT_IN_HWASAN_LOAD1, BUILT_IN_HWASAN_LOAD2,
+	    BUILT_IN_HWASAN_LOAD4, BUILT_IN_HWASAN_LOAD8,
+	    BUILT_IN_HWASAN_LOAD16, BUILT_IN_HWASAN_LOADN },
+	  { BUILT_IN_HWASAN_STORE1, BUILT_IN_HWASAN_STORE2,
+	    BUILT_IN_HWASAN_STORE4, BUILT_IN_HWASAN_STORE8,
+	    BUILT_IN_HWASAN_STORE16, BUILT_IN_HWASAN_STOREN } },
+	{ { BUILT_IN_HWASAN_LOAD1_NOABORT,
+	    BUILT_IN_HWASAN_LOAD2_NOABORT,
+	    BUILT_IN_HWASAN_LOAD4_NOABORT,
+	    BUILT_IN_HWASAN_LOAD8_NOABORT,
+	    BUILT_IN_HWASAN_LOAD16_NOABORT,
+	    BUILT_IN_HWASAN_LOADN_NOABORT },
+	  { BUILT_IN_HWASAN_STORE1_NOABORT,
+	    BUILT_IN_HWASAN_STORE2_NOABORT,
+	    BUILT_IN_HWASAN_STORE4_NOABORT,
+	    BUILT_IN_HWASAN_STORE8_NOABORT,
+	    BUILT_IN_HWASAN_STORE16_NOABORT,
+	    BUILT_IN_HWASAN_STOREN_NOABORT } } };
+  if (size_in_bytes == -1)
+    {
+      *nargs = 2;
+      return as_combined_fn (check[recover_p][is_store][5]);
+    }
+  *nargs = 1;
+  int size_log2 = exact_log2 (size_in_bytes);
+  gcc_assert (size_log2 >= 0 && size_log2 <= 5);
+  return as_combined_fn (check[recover_p][is_store][size_log2]);
+}
+
+/* Expand the HWASAN_{LOAD,STORE} builtins.  */
+bool
+hwasan_expand_check_ifn (gimple_stmt_iterator *iter, bool)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  bool recover_p;
+  if (flag_sanitize & SANITIZE_USER_HWADDRESS)
+    recover_p = (flag_sanitize_recover & SANITIZE_USER_HWADDRESS) != 0;
+  else
+    recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_HWADDRESS) != 0;
+
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_CHECK_LAST);
+  bool is_scalar_access = (flags & ASAN_CHECK_SCALAR_ACCESS) != 0;
+  bool is_store = (flags & ASAN_CHECK_STORE) != 0;
+  bool is_non_zero_len = (flags & ASAN_CHECK_NON_ZERO_LEN) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  tree len = gimple_call_arg (g, 2);
+
+  /* `align` is unused for HWASAN_CHECK, but we pass the argument anyway
+     since that way the arguments match ASAN_CHECK.  */
+  /* HOST_WIDE_INT align = tree_to_shwi (gimple_call_arg (g, 3));  */
+
+  unsigned HOST_WIDE_INT size_in_bytes
+    = is_scalar_access ? tree_to_shwi (len) : -1;
+
+  gimple_stmt_iterator gsi = *iter;
+
+  if (!is_non_zero_len)
+    {
+      /* So, the length of the memory area to hwasan-protect is
+	 non-constant.  Let's guard the generated instrumentation code
+	 like:
+
+	 if (len != 0)
+	   {
+	     // hwasan instrumentation code goes here.
+	   }
+	 // falltrough instructions, starting with *ITER.  */
+
+      g = gimple_build_cond (NE_EXPR,
+			    len,
+			    build_int_cst (TREE_TYPE (len), 0),
+			    NULL_TREE, NULL_TREE);
+      gimple_set_location (g, loc);
+
+      basic_block then_bb, fallthrough_bb;
+      insert_if_then_before_iter (as_a <gcond *> (g), iter,
+				  /*then_more_likely_p=*/true,
+				  &then_bb, &fallthrough_bb);
+      /* Note that fallthrough_bb starts with the statement that was
+	pointed to by ITER.  */
+
+      /* The 'then block' of the 'if (len != 0) condition is where
+	we'll generate the hwasan instrumentation code now.  */
+      gsi = gsi_last_bb (then_bb);
+    }
+
+  gimple_seq stmts = NULL;
+  tree base_addr = gimple_build (&stmts, loc, NOP_EXPR,
+				 pointer_sized_int_node, base);
+
+  int nargs = 0;
+  combined_fn fn = hwasan_check_func (is_store, recover_p, size_in_bytes, &nargs);
+  if (nargs == 1)
+    gimple_build (&stmts, loc, fn, void_type_node, base_addr);
+  else
+    {
+      gcc_assert (nargs == 2);
+      tree sz_arg = gimple_build (&stmts, loc, NOP_EXPR,
+				  pointer_sized_int_node, len);
+      gimple_build (&stmts, loc, fn, void_type_node, base_addr, sz_arg);
+    }
+
+  gsi_insert_seq_after (&gsi, stmts, GSI_NEW_STMT);
+  gsi_remove (iter, true);
+  *iter = gsi;
+  return false;
+}
+
+/* For stack tagging:
+
+   Dummy: the HWASAN_MARK internal function should only ever be in the code
+   after the sanopt pass.  */
+bool
+hwasan_expand_mark_ifn (gimple_stmt_iterator *)
+{
+  gcc_unreachable ();
+}
+
+bool
+gate_hwasan ()
+{
+  return hwasan_sanitize_p ();
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index 535366d104b9cec02ed2d07682476eebdbbe9161..82a0221d63adb48345cb220ab4fca30fd85413de 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -503,6 +503,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_INT_FEXCEPT_T_PTR_INT, BT_INT, BT_FEXCEPT_T_PTR,
 		     BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_FEXCEPT_T_PTR_INT, BT_INT,
 		     BT_CONST_FEXCEPT_T_PTR, BT_INT)
+DEF_FUNCTION_TYPE_2 (BT_FN_PTR_CONST_PTR_UINT8, BT_PTR, BT_CONST_PTR, BT_UINT8)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR)
 
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index a01bf90165745b00c5316176902cc9a63ce67b39..a672c35441ce7fa82f1acf167653fc5a22c46d67 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -753,6 +753,7 @@ dump_gimple_call_args (pretty_printer *buffer, const gcall *gs,
 	  limit = ARRAY_SIZE (reduction_args);
 	  break;
 
+	case IFN_HWASAN_MARK:
 	case IFN_ASAN_MARK:
 #define DEF(X) #X
 	  static const char *const asan_mark_args[] = {IFN_ASAN_MARK_FLAGS};
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index aa3b914f6e5cdee4631ea6c7b8c68778398d49f4..03fe91fa8f1e3d0833f6043b63de8596c0238594 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1237,8 +1237,11 @@ asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
 
   /* It's necessary to have all stack variables aligned to ASAN granularity
      bytes.  */
-  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
-    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+  gcc_assert (!hwasan_sanitize_p () || hwasan_sanitize_stack_p ());
+  unsigned shadow_granularity
+    = hwasan_sanitize_p () ? HWASAN_TAG_GRANULE_SIZE : ASAN_SHADOW_GRANULARITY;
+  if (DECL_ALIGN_UNIT (decl) <= shadow_granularity)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * shadow_granularity);
 
   HOST_WIDE_INT flags = poison ? ASAN_MARK_POISON : ASAN_MARK_UNPOISON;
 
@@ -15132,7 +15135,7 @@ gimplify_function_tree (tree fndecl)
      if necessary.  */
   cfun->curr_properties |= PROP_gimple_lva;
 
-  if (asan_sanitize_use_after_scope () && sanitize_flags_p (SANITIZE_ADDRESS))
+  if (asan_sanitize_use_after_scope ())
     asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
   if (asan_poisoned_variables)
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 792d2ca568a4a11c3e3ad8abf6443f173b0650a7..176afb5a6d8a6bf9559af081b9e9918917846631 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -469,6 +469,141 @@ expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
+expand_HWASAN_CHECK (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
+
+/* For hwasan stack tagging:
+   Clear tags on the dynamically allocated space.
+   For use after an object dynamically allocated on the stack goes out of
+   scope.  */
+static void
+expand_HWASAN_ALLOCA_UNPOISON (internal_fn, gcall *gc)
+{
+  tree restored_position = gimple_call_arg (gc, 0);
+  rtx restored_rtx = expand_expr (restored_position, NULL_RTX, VOIDmode,
+				  EXPAND_NORMAL);
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  rtx off = expand_simple_binop (ptr_mode, MINUS, restored_rtx,
+				 stack_pointer_rtx, NULL_RTX, 0,
+				 OPTAB_WIDEN);
+  rtx dynamic = convert_memory_address (ptr_mode, virtual_stack_dynamic_rtx);
+  emit_library_call_value (func, NULL_RTX, LCT_NORMAL, VOIDmode,
+			   dynamic, ptr_mode,
+			   HWASAN_STACK_BACKGROUND, QImode,
+			   off, ptr_mode);
+}
+
+/* For hwasan stack tagging:
+   Return a tag to be used for a dynamic allocation.  */
+static void
+expand_HWASAN_CHOOSE_TAG (internal_fn, gcall *gc)
+{
+  tree tag = gimple_call_lhs (gc);
+  rtx target = expand_expr (tag, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  machine_mode mode = GET_MODE (target);
+  gcc_assert (mode == QImode);
+
+  gcc_assert (hwasan_frame_base (false));
+  rtx base_tag = targetm.memtag.extract_tag (hwasan_frame_base (false),
+					     NULL_RTX);
+  gcc_assert (base_tag);
+  rtx tag_offset = gen_int_mode (hwasan_current_frame_tag (), QImode);
+  rtx chosen_tag = expand_simple_binop (QImode, PLUS, base_tag, tag_offset,
+					target, /* unsignedp = */1,
+					OPTAB_WIDEN);
+  chosen_tag = hwasan_truncate_to_tag_size (chosen_tag, target);
+
+  /* Really need to put the tag into the `target` RTX.  */
+  if (chosen_tag != target)
+    {
+      rtx temp = chosen_tag;
+      gcc_assert (GET_MODE (chosen_tag) == mode);
+      emit_move_insn (target, temp);
+    }
+
+  hwasan_increment_frame_tag ();
+}
+
+/* For hwasan stack tagging:
+   Tag a region of space in the shadow stack according to the base pointer of
+   an object on the stack.  N.b. the length provided in the internal call is
+   required to be aligned to HWASAN_TAG_GRANULE_SIZE.  */
+static void
+expand_HWASAN_MARK (internal_fn, gcall *gc)
+{
+  HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
+  bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
+
+  tree base = gimple_call_arg (gc, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  /* base is a pointer argument, hence in ptr_mode.
+     We convert to Pmode for use in the targetm.memtag.extract_tag and
+     targetm.memtag.untagged_pointer hooks.
+     We then convert the result to ptr_mode for the emit_library_call.
+
+     This conversion is not for functionality so much as for the code to be
+     correctly formed.  If ptr_mode is smaller than Pmode then the top byte of
+     a Pmode value will be truncated in C code losing the tag values, and
+     HWASAN will not work.  */
+  rtx base_rtx = convert_memory_address (Pmode, expand_normal (base));
+
+  rtx tag = is_poison ? HWASAN_STACK_BACKGROUND
+    : targetm.memtag.extract_tag (base_rtx, NULL_RTX);
+  rtx address = targetm.memtag.untagged_pointer (base_rtx, NULL_RTX);
+  address = convert_memory_address (ptr_mode, address);
+
+  tree len = gimple_call_arg (gc, 2);
+  rtx r_len = expand_normal (len);
+
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (func, LCT_NORMAL, VOIDmode, address, ptr_mode,
+		     tag, QImode, r_len, ptr_mode);
+}
+
+/* For hwasan stack tagging:
+   Store a tag into a pointer.  */
+static void
+expand_HWASAN_SET_TAG (internal_fn, gcall *gc)
+{
+  tree g_target = gimple_call_lhs (gc);
+  tree g_ptr = gimple_call_arg (gc, 0);
+  tree g_tag = gimple_call_arg (gc, 1);
+
+  rtx ptr = convert_memory_address (Pmode, expand_normal (g_ptr));
+  rtx tag = expand_expr (g_tag, NULL_RTX, QImode, EXPAND_NORMAL);
+  rtx target = expand_normal (g_target);
+  machine_mode mode = GET_MODE (target);
+
+  rtx untagged = targetm.memtag.untagged_pointer (ptr, target);
+  rtx tagged_value = targetm.memtag.set_tag (untagged, tag, target);
+  /* Really need to put the return value into the `target` RTX, since that's
+     the return value of the function.
+     `target` will be in ptr_mode, while `tagged_value` will be in Pmode.
+     These can be different.  When they are different we try to truncate the
+     Pmode value into ptr_mode.  This will usually lose the tag, but since such
+     a difference between ptr_mode and Pmode will already cause problems
+     wherever the HWASAN library returns a pointer losing the tag here does not
+     introduce new incompatibilities.
+
+     We do this mainly so that compiling for such a target with ptr_mode and
+     Pmode sizes being different doesn't ICE, even if the resulting binary is
+     not usable.  */
+  if (tagged_value != target)
+    {
+      rtx temp = tagged_value;
+      machine_mode ret_mode = GET_MODE (tagged_value);
+      if (ret_mode != mode)
+	temp = simplify_gen_unary (TRUNCATE, mode, tagged_value, ret_mode);
+
+      emit_move_insn (target, temp);
+    }
+}
+
+/* This should get expanded in the sanopt pass.  */
+
+static void
 expand_ASAN_CHECK (internal_fn, gcall *)
 {
   gcc_unreachable ();
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 310d37aa53819791b5df1683afca831f08e5892a..91a7bfea3eecad747320afcd11e05eedb9f7fcbf 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -321,6 +321,13 @@ DEF_INTERNAL_FN (UBSAN_PTR, ECF_LEAF | ECF_NOTHROW, ". R . ")
 DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
 DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (HWASAN_ALLOCA_UNPOISON, ECF_LEAF | ECF_NOTHROW, ". R ")
+DEF_INTERNAL_FN (HWASAN_CHOOSE_TAG, ECF_LEAF | ECF_NOTHROW, ". ")
+DEF_INTERNAL_FN (HWASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW,
+		 ". . R . . ")
+DEF_INTERNAL_FN (HWASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (HWASAN_SET_TAG,
+		 ECF_TM_PURE | ECF_PURE | ECF_LEAF | ECF_NOTHROW, ". R R ")
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW,
 		 ". . R . . ")
 DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 4f854fb994229fd4ed91d3b5cff7c7acff9a55bc..f02731e8f2b5c7e78000d768e7c472658883ccb4 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -183,6 +183,61 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 /* Hardware Address Sanitizer.  */
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1, "__hwasan_load1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2, "__hwasan_load2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4, "__hwasan_load4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8, "__hwasan_load8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16, "__hwasan_load16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN, "__hwasan_loadN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1, "__hwasan_store1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2, "__hwasan_store2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4, "__hwasan_store4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8, "__hwasan_store8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16, "__hwasan_store16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN, "__hwasan_storeN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1_NOABORT, "__hwasan_load1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2_NOABORT, "__hwasan_load2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4_NOABORT, "__hwasan_load4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8_NOABORT, "__hwasan_load8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16_NOABORT, "__hwasan_load16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN_NOABORT, "__hwasan_loadN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1_NOABORT, "__hwasan_store1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2_NOABORT, "__hwasan_store2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4_NOABORT, "__hwasan_store4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8_NOABORT, "__hwasan_store8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16_NOABORT,
+		      "__hwasan_store16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN_NOABORT, "__hwasan_storeN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MISMATCH4, "__hwasan_tag_mismatch4",
+		      BT_FN_VOID_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_HANDLE_LONGJMP, "__hwasan_handle_longjmp",
+		      BT_FN_VOID_CONST_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_PTR, "__hwasan_tag_pointer",
+		      BT_FN_PTR_CONST_PTR_UINT8, ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
 		      BT_FN_VOID_PTR_UINT8_PTRMODE, ATTR_NOTHROW_LIST)
 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 6c3bce923785f8a2dcd324edc0ce7ceb5f633a0c..965ab367c299f9315cddeb968d1c75086c8762b4 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -776,7 +776,8 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
   basic_block son;
   gimple_stmt_iterator gsi;
   sanopt_info *info = (sanopt_info *) bb->aux;
-  bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0;
+  bool asan_check_optimize
+    = ((flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS)) != 0);
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
@@ -806,6 +807,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
       if (asan_check_optimize
 	  && gimple_call_builtin_p (stmt, BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT))
 	{
+	  gcc_assert (!hwasan_sanitize_p ());
 	  use_operand_p use;
 	  gimple *use_stmt;
 	  if (single_imm_use (gimple_vdef (stmt), &use, &use_stmt))
@@ -834,6 +836,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
 	  case IFN_UBSAN_PTR:
 	    remove = maybe_optimize_ubsan_ptr_ifn (ctx, stmt);
 	    break;
+	  case IFN_HWASAN_CHECK:
 	  case IFN_ASAN_CHECK:
 	    if (asan_check_optimize)
 	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
@@ -1262,6 +1265,10 @@ sanitize_rewrite_addressable_params (function *fun)
 unsigned int
 pass_sanopt::execute (function *fun)
 {
+  /* n.b. ASAN_MARK is used for both HWASAN and ASAN.
+     asan_num_accesses is hence used to count either HWASAN_CHECK or ASAN_CHECK
+     stuff.  This is fine because you can only have one of these active at a
+     time.  */
   basic_block bb;
   int asan_num_accesses = 0;
   bool contains_asan_mark = false;
@@ -1269,10 +1276,10 @@ pass_sanopt::execute (function *fun)
   /* Try to remove redundant checks.  */
   if (optimize
       && (flag_sanitize
-	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT
+	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_HWADDRESS
 	     | SANITIZE_ADDRESS | SANITIZE_VPTR | SANITIZE_POINTER_OVERFLOW)))
     asan_num_accesses = sanopt_optimize (fun, &contains_asan_mark);
-  else if (flag_sanitize & SANITIZE_ADDRESS)
+  else if (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS))
     {
       gimple_stmt_iterator gsi;
       FOR_EACH_BB_FN (bb, fun)
@@ -1292,7 +1299,7 @@ pass_sanopt::execute (function *fun)
       sanitize_asan_mark_poison ();
     }
 
-  if (asan_sanitize_stack_p ())
+  if (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ())
     sanitize_rewrite_addressable_params (fun);
 
   bool use_calls = param_asan_instrumentation_with_call_threshold < INT_MAX
@@ -1334,6 +1341,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_UBSAN_VPTR:
 		  no_next = ubsan_expand_vptr_ifn (&gsi);
 		  break;
+		case IFN_HWASAN_CHECK:
+		  no_next = hwasan_expand_check_ifn (&gsi, use_calls);
+		  break;
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
@@ -1345,6 +1355,9 @@ pass_sanopt::execute (function *fun)
 						    &need_commit_edge_insert,
 						    shadow_vars_mapping);
 		  break;
+		case IFN_HWASAN_MARK:
+		  no_next = hwasan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 7e12b91f4498d1165d6aae1179217fc2b8a55c7b..529ccbd7527e5a2120fb856f3771c6d33351f9a3 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -512,7 +512,7 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
-      if (flag_sanitize & SANITIZE_HWADDRESS)
+      if (gate_hwasan ())
 	hwasan_finish_file ();
 
       omp_finish_file ();


[-- Attachment #2: hwasan-v55.patch.gz --]
[-- Type: application/gzip, Size: 14732 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* [PATCH 7/X] libsanitizer:  Add tests
  2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
                         ` (7 preceding siblings ...)
  2020-11-16 15:37       ` [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes Matthew Malcomson
@ 2020-11-16 15:37       ` Matthew Malcomson
  2020-11-19 13:01       ` Update [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes Matthew Malcomson
  9 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-16 15:37 UTC (permalink / raw)
  To: gcc-patches
  Cc: Richard Earnshaw, Kyrylo Tkachov, Richard Sandiford, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 3683 bytes --]

Adding hwasan tests.

Only interesting thing here is that we have to make sure the tagging mechanism
is deterministic to avoid flaky tests.

gcc/testsuite/ChangeLog:

	* c-c++-common/ubsan/sanitize-recover-7.c:
	* c-c++-common/hwasan/aligned-alloc.c: New test.
	* c-c++-common/hwasan/alloca-array-accessible.c: New test.
	* c-c++-common/hwasan/alloca-base-init.c: New test.
	* c-c++-common/hwasan/alloca-gets-different-tag.c: New test.
	* c-c++-common/hwasan/alloca-outside-caught.c: New test.
	* c-c++-common/hwasan/arguments-1.c: New test.
	* c-c++-common/hwasan/arguments-2.c: New test.
	* c-c++-common/hwasan/arguments-3.c: New test.
	* c-c++-common/hwasan/arguments.c: New test.
	* c-c++-common/hwasan/asan-pr63316.c: New test.
	* c-c++-common/hwasan/asan-pr70541.c: New test.
	* c-c++-common/hwasan/asan-pr78106.c: New test.
	* c-c++-common/hwasan/asan-pr79944.c: New test.
	* c-c++-common/hwasan/asan-rlimit-mmap-test-1.c: New test.
	* c-c++-common/hwasan/bitfield-1.c: New test.
	* c-c++-common/hwasan/bitfield-2.c: New test.
	* c-c++-common/hwasan/builtin-special-handling.c: New test.
	* c-c++-common/hwasan/check-interface.c: New test.
	* c-c++-common/hwasan/halt_on_error-1.c: New test.
	* c-c++-common/hwasan/handles-poly_int-marked-vars.c: New test.
	* c-c++-common/hwasan/heap-overflow.c: New test.
	* c-c++-common/hwasan/hwasan-poison-optimisation.c: New test.
	* c-c++-common/hwasan/hwasan-thread-access-parent.c: New test.
	* c-c++-common/hwasan/hwasan-thread-basic-failure.c: New test.
	* c-c++-common/hwasan/hwasan-thread-clears-stack.c: New test.
	* c-c++-common/hwasan/hwasan-thread-success.c: New test.
	* c-c++-common/hwasan/kernel-defaults.c: New test.
	* c-c++-common/hwasan/large-aligned-0.c: New test.
	* c-c++-common/hwasan/large-aligned-1.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-0.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-1.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-2.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-3.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-4.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-5.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-6.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-7.c: New test.
	* c-c++-common/hwasan/macro-definition.c: New test.
	* c-c++-common/hwasan/no-sanitize-attribute.c: New test.
	* c-c++-common/hwasan/param-instrument-mem-intrinsics.c: New test.
	* c-c++-common/hwasan/param-instrument-reads-and-writes.c: New test.
	* c-c++-common/hwasan/param-instrument-reads.c: New test.
	* c-c++-common/hwasan/param-instrument-writes.c: New test.
	* c-c++-common/hwasan/random-frame-tag.c: New test.
	* c-c++-common/hwasan/sanity-check-pure-c.c: New test.
	* c-c++-common/hwasan/setjmp-longjmp-0.c: New test.
	* c-c++-common/hwasan/setjmp-longjmp-1.c: New test.
	* c-c++-common/hwasan/stack-tagging-basic-0.c: New test.
	* c-c++-common/hwasan/stack-tagging-basic-1.c: New test.
	* c-c++-common/hwasan/stack-tagging-disable.c: New test.
	* c-c++-common/hwasan/unprotected-allocas-0.c: New test.
	* c-c++-common/hwasan/unprotected-allocas-1.c: New test.
	* c-c++-common/hwasan/use-after-free.c: New test.
	* c-c++-common/hwasan/vararray-outside-caught.c: New test.
	* c-c++-common/hwasan/vararray-stack-restore-correct.c: New test.
	* c-c++-common/hwasan/very-large-objects.c: New test.
	* g++.dg/hwasan/hwasan.exp: New test.
	* g++.dg/hwasan/rvo-handled.C: New test.
	* gcc.dg/hwasan/hwasan.exp: New test.
	* gcc.dg/hwasan/nested-functions-0.c: New test.
	* gcc.dg/hwasan/nested-functions-1.c: New test.
	* gcc.dg/hwasan/nested-functions-2.c: New test.
	* lib/hwasan-dg.exp: New test.

[-- Attachment #2: hwasan-v56.patch.gz --]
[-- Type: application/gzip, Size: 16838 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system
  2020-11-16 15:37       ` [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system Matthew Malcomson
@ 2020-11-18 17:03         ` Richard Sandiford
  0 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-11-18 17:03 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, Richard Earnshaw, Kyrylo Tkachov, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> This patch tries to tie libhwasan into the GCC build system in the same way
> that the other sanitizer runtime libraries are handled.
>
> libsanitizer/ChangeLog:
>
> 	* Makefile.am:  Build libhwasan.
> 	* Makefile.in:  Build libhwasan.
> 	* asan/Makefile.in:  Build libhwasan.
> 	* configure:  Build libhwasan.
> 	* configure.ac:  Build libhwasan.
> 	* hwasan/Makefile.am: New file.
> 	* hwasan/Makefile.in: New file.
> 	* hwasan/libtool-version: New file.
> 	* interception/Makefile.in: Build libhwasan.
> 	* libbacktrace/Makefile.in: Build libhwasan.
> 	* libsanitizer.spec.in: Build libhwasan.
> 	* lsan/Makefile.in: Build libhwasan.
> 	* sanitizer_common/Makefile.in: Build libhwasan.
> 	* tsan/Makefile.in: Build libhwasan.
> 	* ubsan/Makefile.in: Build libhwasan.

OK, thanks.

Richard

> ###############     Attachment also inlined for ease of reply    ###############
>
>
> diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
> index 65ed1e712378ef453f820f86c4d3221f9dee5f2c..2a7e8e1debe838719db0f0fad218b2543cc3111b 100644
> --- a/libsanitizer/Makefile.am
> +++ b/libsanitizer/Makefile.am
> @@ -14,11 +14,12 @@ endif
>  if LIBBACKTRACE_SUPPORTED
>  SUBDIRS += libbacktrace
>  endif
> -SUBDIRS += lsan asan ubsan
> +SUBDIRS += lsan asan ubsan hwasan
>  nodist_saninclude_HEADERS += \
>    include/sanitizer/lsan_interface.h \
>    include/sanitizer/asan_interface.h \
> -  include/sanitizer/tsan_interface.h
> +  include/sanitizer/tsan_interface.h \
> +  include/sanitizer/hwasan_interface.h
>  if TSAN_SUPPORTED
>  SUBDIRS += tsan
>  endif
> diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
> index 02c7f70ac6578a3e93a490ce8bd2c54fc0693c50..2c57d49cbffdb486645aeb5f2c0f85d6e0fad124 100644
> --- a/libsanitizer/Makefile.in
> +++ b/libsanitizer/Makefile.in
> @@ -92,7 +92,8 @@ target_triplet = @target@
>  @SANITIZER_SUPPORTED_TRUE@am__append_1 = include/sanitizer/common_interface_defs.h \
>  @SANITIZER_SUPPORTED_TRUE@	include/sanitizer/lsan_interface.h \
>  @SANITIZER_SUPPORTED_TRUE@	include/sanitizer/asan_interface.h \
> -@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/tsan_interface.h
> +@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/tsan_interface.h \
> +@SANITIZER_SUPPORTED_TRUE@	include/sanitizer/hwasan_interface.h
>  @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
>  @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
>  @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
> @@ -207,7 +208,7 @@ ETAGS = etags
>  CTAGS = ctags
>  CSCOPE = cscope
>  DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
> -	ubsan tsan
> +	ubsan hwasan tsan
>  ACLOCAL = @ACLOCAL@
>  ALLOC_FILE = @ALLOC_FILE@
>  AMTAR = @AMTAR@
> @@ -329,6 +330,7 @@ install_sh = @install_sh@
>  libdir = @libdir@
>  libexecdir = @libexecdir@
>  link_libasan = @link_libasan@
> +link_libhwasan = @link_libhwasan@
>  link_liblsan = @link_liblsan@
>  link_libtsan = @link_libtsan@
>  link_libubsan = @link_libubsan@
> @@ -362,7 +364,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
>  nodist_saninclude_HEADERS = $(am__append_1)
>  @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
>  @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
> -@SANITIZER_SUPPORTED_TRUE@	$(am__append_4)
> +@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
>  gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
>  
>  # Work around what appears to be a GNU make bug handling MAKEFLAGS
> diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in
> index 29622bf466a37f819c9fade30e31195adda51190..25c7fd7b7597d6e243005a1bb7de5b6243d2cfcf 100644
> --- a/libsanitizer/asan/Makefile.in
> +++ b/libsanitizer/asan/Makefile.in
> @@ -383,6 +383,7 @@ install_sh = @install_sh@
>  libdir = @libdir@
>  libexecdir = @libexecdir@
>  link_libasan = @link_libasan@
> +link_libhwasan = @link_libhwasan@
>  link_liblsan = @link_liblsan@
>  link_libtsan = @link_libtsan@
>  link_libubsan = @link_libubsan@
> diff --git a/libsanitizer/configure b/libsanitizer/configure
> index 04eca04fbe5e59bae1ba00597de0cf1b7cf1b5fa..27e72c089cb891dcce09494fa9e39eebe55d2598 100755
> --- a/libsanitizer/configure
> +++ b/libsanitizer/configure
> @@ -657,6 +657,7 @@ USING_MAC_INTERPOSE_TRUE
>  link_liblsan
>  link_libubsan
>  link_libtsan
> +link_libhwasan
>  link_libasan
>  LSAN_SUPPORTED_FALSE
>  LSAN_SUPPORTED_TRUE
> @@ -12361,7 +12362,7 @@ else
>    lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
>    lt_status=$lt_dlunknown
>    cat > conftest.$ac_ext <<_LT_EOF
> -#line 12364 "configure"
> +#line 12365 "configure"
>  #include "confdefs.h"
>  
>  #if HAVE_DLFCN_H
> @@ -12467,7 +12468,7 @@ else
>    lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
>    lt_status=$lt_dlunknown
>    cat > conftest.$ac_ext <<_LT_EOF
> -#line 12470 "configure"
> +#line 12471 "configure"
>  #include "confdefs.h"
>  
>  #if HAVE_DLFCN_H
> @@ -15943,6 +15944,10 @@ fi
>  link_libasan=$link_sanitizer_common
>  
>  
> +# Set up the set of additional libraries that we need to link against for libhwasan.
> +link_libhwasan=$link_sanitizer_common
> +
> +
>  # Set up the set of additional libraries that we need to link against for libtsan.
>  link_libtsan=$link_sanitizer_common
>  
> @@ -16813,7 +16818,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
>  ac_config_headers="$ac_config_headers config.h"
>  
>  
> -ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
> +ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
>  
>  
>  if test "x$TSAN_SUPPORTED" = "xyes"; then
> @@ -16862,7 +16867,7 @@ case "$host" in
>      case "$enable_cet" in
>        auto)
>  	# Check if target supports multi-byte NOPs
> -	# and if assembler supports CET insn.
> +	# and if compiler and assembler support CET insn.
>  	cet_save_CFLAGS="$CFLAGS"
>  	CFLAGS="$CFLAGS -fcf-protection"
>  	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> @@ -18094,6 +18099,7 @@ do
>      "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
>      "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
>      "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
> +    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
>      "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
>      "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
>  
> @@ -20029,6 +20035,17 @@ _EOF
>     . ${multi_basedir}/config-ml.in
>     { ml_norecursion=; unset ml_norecursion;}
>   ;;
> +    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
> +s!`test -f '$<' || echo '$(srcdir)/'`!!
> +_EOF
> +   sed -f vpsed$$ $ac_file > tmp$$
> +   mv tmp$$ $ac_file
> +   rm vpsed$$
> +   echo 'MULTISUBDIR =' >> $ac_file
> +   ml_norecursion=yes
> +   . ${multi_basedir}/config-ml.in
> +   { ml_norecursion=; unset ml_norecursion;}
> + ;;
>      "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
>  s!`test -f '$<' || echo '$(srcdir)/'`!!
>  _EOF
> diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
> index a0950c29ebe2c0bf862ffe08f9a441c554da3220..f0d526c52967112905115c8fbe3540427a27ae03 100644
> --- a/libsanitizer/configure.ac
> +++ b/libsanitizer/configure.ac
> @@ -120,6 +120,10 @@ AC_CHECK_LIB(dl, dlsym,
>  link_libasan=$link_sanitizer_common
>  AC_SUBST(link_libasan)
>  
> +# Set up the set of additional libraries that we need to link against for libhwasan.
> +link_libhwasan=$link_sanitizer_common
> +AC_SUBST(link_libhwasan)
> +
>  # Set up the set of additional libraries that we need to link against for libtsan.
>  link_libtsan=$link_sanitizer_common
>  AC_SUBST(link_libtsan)
> @@ -348,7 +352,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
>  AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
>  AC_CONFIG_HEADER(config.h)
>  
> -AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
> +AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
>    [cat > vpsed$$ << \_EOF
>  s!`test -f '$<' || echo '$(srcdir)/'`!!
>  _EOF
> diff --git a/libsanitizer/hwasan/Makefile.am b/libsanitizer/hwasan/Makefile.am
> new file mode 100644
> index 0000000000000000000000000000000000000000..2226f68ab29e109e592c51f4064311fcb5a177d2
> --- /dev/null
> +++ b/libsanitizer/hwasan/Makefile.am
> @@ -0,0 +1,89 @@
> +AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
> +
> +# May be used by toolexeclibdir.
> +gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
> +
> +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
> +AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fno-rtti -funwind-tables -fvisibility=hidden -Wno-variadic-macros -fno-ipa-icf
> +AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
> +AM_CXXFLAGS += -std=gnu++14
> +AM_CXXFLAGS += $(EXTRA_CXXFLAGS)
> +ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
> +
> +toolexeclib_LTLIBRARIES = libhwasan.la
> +
> +hwasan_files = \
> +	hwasan_allocator.cpp \
> +	hwasan.cpp \
> +	hwasan_dynamic_shadow.cpp \
> +	hwasan_exceptions.cpp \
> +	hwasan_flags.inc \
> +	hwasan_globals.cpp \
> +	hwasan_interceptors.cpp \
> +	hwasan_interceptors_vfork.S \
> +	hwasan_linux.cpp \
> +	hwasan_memintrinsics.cpp \
> +	hwasan_new_delete.cpp \
> +	hwasan_poisoning.cpp \
> +	hwasan_report.cpp \
> +	hwasan_setjmp.S \
> +	hwasan_tag_mismatch_aarch64.S \
> +	hwasan_thread.cpp \
> +	hwasan_thread_list.cpp \
> +	hwasan_type_test.cpp
> +
> +libhwasan_la_SOURCES = $(hwasan_files)
> +libhwasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la
> +if !USING_MAC_INTERPOSE
> +libhwasan_la_LIBADD += $(top_builddir)/interception/libinterception.la
> +endif
> +if LIBBACKTRACE_SUPPORTED
> +libhwasan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
> +endif
> +libhwasan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS)
> +
> +libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan)
> +
> +# Work around what appears to be a GNU make bug handling MAKEFLAGS
> +# values defined in terms of make variables, as is the case for CC and
> +# friends when we are called from the top level Makefile.
> +AM_MAKEFLAGS = \
> +	"AR_FLAGS=$(AR_FLAGS)" \
> +	"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
> +	"CFLAGS=$(CFLAGS)" \
> +	"CXXFLAGS=$(CXXFLAGS)" \
> +	"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
> +	"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
> +	"INSTALL=$(INSTALL)" \
> +	"INSTALL_DATA=$(INSTALL_DATA)" \
> +	"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
> +	"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
> +	"JC1FLAGS=$(JC1FLAGS)" \
> +	"LDFLAGS=$(LDFLAGS)" \
> +	"LIBCFLAGS=$(LIBCFLAGS)" \
> +	"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
> +	"MAKE=$(MAKE)" \
> +	"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
> +	"PICFLAG=$(PICFLAG)" \
> +	"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
> +	"SHELL=$(SHELL)" \
> +	"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
> +	"exec_prefix=$(exec_prefix)" \
> +	"infodir=$(infodir)" \
> +	"libdir=$(libdir)" \
> +	"prefix=$(prefix)" \
> +	"includedir=$(includedir)" \
> +	"AR=$(AR)" \
> +	"AS=$(AS)" \
> +	"LD=$(LD)" \
> +	"LIBCFLAGS=$(LIBCFLAGS)" \
> +	"NM=$(NM)" \
> +	"PICFLAG=$(PICFLAG)" \
> +	"RANLIB=$(RANLIB)" \
> +	"DESTDIR=$(DESTDIR)"
> +
> +MAKEOVERRIDES=
> +
> +## ################################################################
> +
> +
> diff --git a/libsanitizer/hwasan/Makefile.in b/libsanitizer/hwasan/Makefile.in
> new file mode 100644
> index 0000000000000000000000000000000000000000..542af8f193e0d460de93dff7b249edb949be8a67
> --- /dev/null
> +++ b/libsanitizer/hwasan/Makefile.in
> @@ -0,0 +1,803 @@
> +# Makefile.in generated by automake 1.15.1 from Makefile.am.
> +# @configure_input@
> +
> +# Copyright (C) 1994-2017 Free Software Foundation, Inc.
> +
> +# This Makefile.in is free software; the Free Software Foundation
> +# gives unlimited permission to copy and/or distribute it,
> +# with or without modifications, as long as this notice is preserved.
> +
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
> +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
> +# PARTICULAR PURPOSE.
> +
> +@SET_MAKE@
> +
> +VPATH = @srcdir@
> +am__is_gnu_make = { \
> +  if test -z '$(MAKELEVEL)'; then \
> +    false; \
> +  elif test -n '$(MAKE_HOST)'; then \
> +    true; \
> +  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
> +    true; \
> +  else \
> +    false; \
> +  fi; \
> +}
> +am__make_running_with_option = \
> +  case $${target_option-} in \
> +      ?) ;; \
> +      *) echo "am__make_running_with_option: internal error: invalid" \
> +              "target option '$${target_option-}' specified" >&2; \
> +         exit 1;; \
> +  esac; \
> +  has_opt=no; \
> +  sane_makeflags=$$MAKEFLAGS; \
> +  if $(am__is_gnu_make); then \
> +    sane_makeflags=$$MFLAGS; \
> +  else \
> +    case $$MAKEFLAGS in \
> +      *\\[\ \	]*) \
> +        bs=\\; \
> +        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
> +          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
> +    esac; \
> +  fi; \
> +  skip_next=no; \
> +  strip_trailopt () \
> +  { \
> +    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
> +  }; \
> +  for flg in $$sane_makeflags; do \
> +    test $$skip_next = yes && { skip_next=no; continue; }; \
> +    case $$flg in \
> +      *=*|--*) continue;; \
> +        -*I) strip_trailopt 'I'; skip_next=yes;; \
> +      -*I?*) strip_trailopt 'I';; \
> +        -*O) strip_trailopt 'O'; skip_next=yes;; \
> +      -*O?*) strip_trailopt 'O';; \
> +        -*l) strip_trailopt 'l'; skip_next=yes;; \
> +      -*l?*) strip_trailopt 'l';; \
> +      -[dEDm]) skip_next=yes;; \
> +      -[JT]) skip_next=yes;; \
> +    esac; \
> +    case $$flg in \
> +      *$$target_option*) has_opt=yes; break;; \
> +    esac; \
> +  done; \
> +  test $$has_opt = yes
> +am__make_dryrun = (target_option=n; $(am__make_running_with_option))
> +am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
> +pkgdatadir = $(datadir)/@PACKAGE@
> +pkgincludedir = $(includedir)/@PACKAGE@
> +pkglibdir = $(libdir)/@PACKAGE@
> +pkglibexecdir = $(libexecdir)/@PACKAGE@
> +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
> +install_sh_DATA = $(install_sh) -c -m 644
> +install_sh_PROGRAM = $(install_sh) -c
> +install_sh_SCRIPT = $(install_sh) -c
> +INSTALL_HEADER = $(INSTALL_DATA)
> +transform = $(program_transform_name)
> +NORMAL_INSTALL = :
> +PRE_INSTALL = :
> +POST_INSTALL = :
> +NORMAL_UNINSTALL = :
> +PRE_UNINSTALL = :
> +POST_UNINSTALL = :
> +build_triplet = @build@
> +host_triplet = @host@
> +target_triplet = @target@
> +@USING_MAC_INTERPOSE_FALSE@am__append_1 = $(top_builddir)/interception/libinterception.la
> +@LIBBACKTRACE_SUPPORTED_TRUE@am__append_2 = $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
> +subdir = hwasan
> +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
> +am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
> +	$(top_srcdir)/../config/depstand.m4 \
> +	$(top_srcdir)/../config/lead-dot.m4 \
> +	$(top_srcdir)/../config/libstdc++-raw-cxx.m4 \
> +	$(top_srcdir)/../config/multi.m4 \
> +	$(top_srcdir)/../config/override.m4 \
> +	$(top_srcdir)/../config/stdint.m4 \
> +	$(top_srcdir)/../config/toolexeclibdir.m4 \
> +	$(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \
> +	$(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \
> +	$(top_srcdir)/acinclude.m4 $(top_srcdir)/../libtool.m4 \
> +	$(top_srcdir)/../config/enable.m4 \
> +	$(top_srcdir)/../config/cet.m4 $(top_srcdir)/configure.ac
> +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
> +	$(ACLOCAL_M4)
> +DIST_COMMON = $(srcdir)/Makefile.am
> +mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
> +CONFIG_HEADER = $(top_builddir)/config.h
> +CONFIG_CLEAN_FILES =
> +CONFIG_CLEAN_VPATH_FILES =
> +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
> +am__vpath_adj = case $$p in \
> +    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
> +    *) f=$$p;; \
> +  esac;
> +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
> +am__install_max = 40
> +am__nobase_strip_setup = \
> +  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
> +am__nobase_strip = \
> +  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
> +am__nobase_list = $(am__nobase_strip_setup); \
> +  for p in $$list; do echo "$$p $$p"; done | \
> +  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
> +  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
> +    if (++n[$$2] == $(am__install_max)) \
> +      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
> +    END { for (dir in files) print dir, files[dir] }'
> +am__base_list = \
> +  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
> +  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
> +am__uninstall_files_from_dir = { \
> +  test -z "$$files" \
> +    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
> +    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
> +         $(am__cd) "$$dir" && rm -f $$files; }; \
> +  }
> +am__installdirs = "$(DESTDIR)$(toolexeclibdir)"
> +LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
> +am__DEPENDENCIES_1 =
> +libhwasan_la_DEPENDENCIES =  \
> +	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
> +	$(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1)
> +am__objects_1 = hwasan_allocator.lo hwasan.lo hwasan_dynamic_shadow.lo \
> +	hwasan_exceptions.lo hwasan_globals.lo hwasan_interceptors.lo \
> +	hwasan_interceptors_vfork.lo hwasan_linux.lo \
> +	hwasan_memintrinsics.lo hwasan_new_delete.lo \
> +	hwasan_poisoning.lo hwasan_report.lo hwasan_setjmp.lo \
> +	hwasan_tag_mismatch_aarch64.lo hwasan_thread.lo \
> +	hwasan_thread_list.lo hwasan_type_test.lo
> +am_libhwasan_la_OBJECTS = $(am__objects_1)
> +libhwasan_la_OBJECTS = $(am_libhwasan_la_OBJECTS)
> +AM_V_lt = $(am__v_lt_@AM_V@)
> +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
> +am__v_lt_0 = --silent
> +am__v_lt_1 = 
> +libhwasan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
> +	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
> +	$(CXXFLAGS) $(libhwasan_la_LDFLAGS) $(LDFLAGS) -o $@
> +AM_V_P = $(am__v_P_@AM_V@)
> +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
> +am__v_P_0 = false
> +am__v_P_1 = :
> +AM_V_GEN = $(am__v_GEN_@AM_V@)
> +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
> +am__v_GEN_0 = @echo "  GEN     " $@;
> +am__v_GEN_1 = 
> +AM_V_at = $(am__v_at_@AM_V@)
> +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
> +am__v_at_0 = @
> +am__v_at_1 = 
> +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
> +depcomp = $(SHELL) $(top_srcdir)/../depcomp
> +am__depfiles_maybe = depfiles
> +am__mv = mv -f
> +CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
> +	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS)
> +LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
> +	$(LIBTOOLFLAGS) --mode=compile $(CCAS) $(DEFS) \
> +	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
> +	$(AM_CCASFLAGS) $(CCASFLAGS)
> +AM_V_CPPAS = $(am__v_CPPAS_@AM_V@)
> +am__v_CPPAS_ = $(am__v_CPPAS_@AM_DEFAULT_V@)
> +am__v_CPPAS_0 = @echo "  CPPAS   " $@;
> +am__v_CPPAS_1 = 
> +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
> +	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
> +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
> +	$(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
> +	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
> +	$(AM_CXXFLAGS) $(CXXFLAGS)
> +AM_V_CXX = $(am__v_CXX_@AM_V@)
> +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
> +am__v_CXX_0 = @echo "  CXX     " $@;
> +am__v_CXX_1 = 
> +CXXLD = $(CXX)
> +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
> +	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
> +	$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
> +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
> +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
> +am__v_CXXLD_0 = @echo "  CXXLD   " $@;
> +am__v_CXXLD_1 = 
> +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
> +	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
> +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
> +	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
> +	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
> +	$(AM_CFLAGS) $(CFLAGS)
> +AM_V_CC = $(am__v_CC_@AM_V@)
> +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
> +am__v_CC_0 = @echo "  CC      " $@;
> +am__v_CC_1 = 
> +CCLD = $(CC)
> +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
> +	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
> +	$(AM_LDFLAGS) $(LDFLAGS) -o $@
> +AM_V_CCLD = $(am__v_CCLD_@AM_V@)
> +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
> +am__v_CCLD_0 = @echo "  CCLD    " $@;
> +am__v_CCLD_1 = 
> +SOURCES = $(libhwasan_la_SOURCES)
> +am__can_run_installinfo = \
> +  case $$AM_UPDATE_INFO_DIR in \
> +    n|no|NO) false;; \
> +    *) (install-info --version) >/dev/null 2>&1;; \
> +  esac
> +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
> +# Read a list of newline-separated strings from the standard input,
> +# and print each of them once, without duplicates.  Input order is
> +# *not* preserved.
> +am__uniquify_input = $(AWK) '\
> +  BEGIN { nonempty = 0; } \
> +  { items[$$0] = 1; nonempty = 1; } \
> +  END { if (nonempty) { for (i in items) print i; }; } \
> +'
> +# Make sure the list of sources is unique.  This is necessary because,
> +# e.g., the same source file might be shared among _SOURCES variables
> +# for different programs/libraries.
> +am__define_uniq_tagged_files = \
> +  list='$(am__tagged_files)'; \
> +  unique=`for i in $$list; do \
> +    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
> +  done | $(am__uniquify_input)`
> +ETAGS = etags
> +CTAGS = ctags
> +ACLOCAL = @ACLOCAL@
> +ALLOC_FILE = @ALLOC_FILE@
> +AMTAR = @AMTAR@
> +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
> +AR = @AR@
> +AUTOCONF = @AUTOCONF@
> +AUTOHEADER = @AUTOHEADER@
> +AUTOMAKE = @AUTOMAKE@
> +AWK = @AWK@
> +BACKTRACE_SUPPORTED = @BACKTRACE_SUPPORTED@
> +BACKTRACE_SUPPORTS_THREADS = @BACKTRACE_SUPPORTS_THREADS@
> +BACKTRACE_USES_MALLOC = @BACKTRACE_USES_MALLOC@
> +CC = @CC@
> +CCAS = @CCAS@
> +CCASDEPMODE = @CCASDEPMODE@
> +CCASFLAGS = @CCASFLAGS@
> +CCDEPMODE = @CCDEPMODE@
> +CFLAGS = @CFLAGS@
> +CPP = @CPP@
> +CPPFLAGS = @CPPFLAGS@
> +CXX = @CXX@
> +CXXCPP = @CXXCPP@
> +CXXDEPMODE = @CXXDEPMODE@
> +CXXFLAGS = @CXXFLAGS@
> +CYGPATH_W = @CYGPATH_W@
> +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 -DHWASAN_WITH_INTERCEPTORS=1
> +DEPDIR = @DEPDIR@
> +DSYMUTIL = @DSYMUTIL@
> +DUMPBIN = @DUMPBIN@
> +ECHO_C = @ECHO_C@
> +ECHO_N = @ECHO_N@
> +ECHO_T = @ECHO_T@
> +EGREP = @EGREP@
> +EXEEXT = @EXEEXT@
> +EXTRA_ASFLAGS = @EXTRA_ASFLAGS@
> +EXTRA_CFLAGS = @EXTRA_CFLAGS@
> +EXTRA_CXXFLAGS = @EXTRA_CXXFLAGS@
> +FGREP = @FGREP@
> +FORMAT_FILE = @FORMAT_FILE@
> +GREP = @GREP@
> +INSTALL = @INSTALL@
> +INSTALL_DATA = @INSTALL_DATA@
> +INSTALL_PROGRAM = @INSTALL_PROGRAM@
> +INSTALL_SCRIPT = @INSTALL_SCRIPT@
> +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
> +LD = @LD@
> +LDFLAGS = @LDFLAGS@
> +LIBOBJS = @LIBOBJS@
> +LIBS = @LIBS@
> +LIBSTDCXX_RAW_CXX_CXXFLAGS = @LIBSTDCXX_RAW_CXX_CXXFLAGS@
> +LIBSTDCXX_RAW_CXX_LDFLAGS = @LIBSTDCXX_RAW_CXX_LDFLAGS@
> +LIBTOOL = @LIBTOOL@
> +LIPO = @LIPO@
> +LN_S = @LN_S@
> +LTLIBOBJS = @LTLIBOBJS@
> +MAINT = @MAINT@
> +MAKEINFO = @MAKEINFO@
> +MKDIR_P = @MKDIR_P@
> +NM = @NM@
> +NMEDIT = @NMEDIT@
> +OBJDUMP = @OBJDUMP@
> +OBJEXT = @OBJEXT@
> +OTOOL = @OTOOL@
> +OTOOL64 = @OTOOL64@
> +PACKAGE = @PACKAGE@
> +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
> +PACKAGE_NAME = @PACKAGE_NAME@
> +PACKAGE_STRING = @PACKAGE_STRING@
> +PACKAGE_TARNAME = @PACKAGE_TARNAME@
> +PACKAGE_URL = @PACKAGE_URL@
> +PACKAGE_VERSION = @PACKAGE_VERSION@
> +PATH_SEPARATOR = @PATH_SEPARATOR@
> +RANLIB = @RANLIB@
> +RPC_DEFS = @RPC_DEFS@
> +SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS = @SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS@
> +SED = @SED@
> +SET_MAKE = @SET_MAKE@
> +SHELL = @SHELL@
> +STRIP = @STRIP@
> +TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
> +VERSION = @VERSION@
> +VIEW_FILE = @VIEW_FILE@
> +abs_builddir = @abs_builddir@
> +abs_srcdir = @abs_srcdir@
> +abs_top_builddir = @abs_top_builddir@
> +abs_top_srcdir = @abs_top_srcdir@
> +ac_ct_CC = @ac_ct_CC@
> +ac_ct_CXX = @ac_ct_CXX@
> +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
> +am__include = @am__include@
> +am__leading_dot = @am__leading_dot@
> +am__quote = @am__quote@
> +am__tar = @am__tar@
> +am__untar = @am__untar@
> +bindir = @bindir@
> +build = @build@
> +build_alias = @build_alias@
> +build_cpu = @build_cpu@
> +build_os = @build_os@
> +build_vendor = @build_vendor@
> +builddir = @builddir@
> +datadir = @datadir@
> +datarootdir = @datarootdir@
> +docdir = @docdir@
> +dvidir = @dvidir@
> +enable_shared = @enable_shared@
> +enable_static = @enable_static@
> +exec_prefix = @exec_prefix@
> +get_gcc_base_ver = @get_gcc_base_ver@
> +host = @host@
> +host_alias = @host_alias@
> +host_cpu = @host_cpu@
> +host_os = @host_os@
> +host_vendor = @host_vendor@
> +htmldir = @htmldir@
> +includedir = @includedir@
> +infodir = @infodir@
> +install_sh = @install_sh@
> +libdir = @libdir@
> +libexecdir = @libexecdir@
> +link_libasan = @link_libasan@
> +link_libhwasan = @link_libhwasan@
> +link_liblsan = @link_liblsan@
> +link_libtsan = @link_libtsan@
> +link_libubsan = @link_libubsan@
> +localedir = @localedir@
> +localstatedir = @localstatedir@
> +mandir = @mandir@
> +mkdir_p = @mkdir_p@
> +multi_basedir = @multi_basedir@
> +oldincludedir = @oldincludedir@
> +pdfdir = @pdfdir@
> +prefix = @prefix@
> +program_transform_name = @program_transform_name@
> +psdir = @psdir@
> +sbindir = @sbindir@
> +sharedstatedir = @sharedstatedir@
> +srcdir = @srcdir@
> +sysconfdir = @sysconfdir@
> +target = @target@
> +target_alias = @target_alias@
> +target_cpu = @target_cpu@
> +target_noncanonical = @target_noncanonical@
> +target_os = @target_os@
> +target_vendor = @target_vendor@
> +toolexecdir = @toolexecdir@
> +toolexeclibdir = @toolexeclibdir@
> +top_build_prefix = @top_build_prefix@
> +top_builddir = @top_builddir@
> +top_srcdir = @top_srcdir@
> +AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir)
> +
> +# May be used by toolexeclibdir.
> +gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
> +AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
> +	-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
> +	-funwind-tables -fvisibility=hidden -Wno-variadic-macros \
> +	-fno-ipa-icf $(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=gnu++14 \
> +	$(EXTRA_CXXFLAGS)
> +ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
> +toolexeclib_LTLIBRARIES = libhwasan.la
> +hwasan_files = \
> +	hwasan_allocator.cpp \
> +	hwasan.cpp \
> +	hwasan_dynamic_shadow.cpp \
> +	hwasan_exceptions.cpp \
> +	hwasan_flags.inc \
> +	hwasan_globals.cpp \
> +	hwasan_interceptors.cpp \
> +	hwasan_interceptors_vfork.S \
> +	hwasan_linux.cpp \
> +	hwasan_memintrinsics.cpp \
> +	hwasan_new_delete.cpp \
> +	hwasan_poisoning.cpp \
> +	hwasan_report.cpp \
> +	hwasan_setjmp.S \
> +	hwasan_tag_mismatch_aarch64.S \
> +	hwasan_thread.cpp \
> +	hwasan_thread_list.cpp \
> +	hwasan_type_test.cpp
> +
> +libhwasan_la_SOURCES = $(hwasan_files)
> +libhwasan_la_LIBADD =  \
> +	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
> +	$(am__append_1) $(am__append_2) $(LIBSTDCXX_RAW_CXX_LDFLAGS)
> +libhwasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libhwasan)
> +
> +# Work around what appears to be a GNU make bug handling MAKEFLAGS
> +# values defined in terms of make variables, as is the case for CC and
> +# friends when we are called from the top level Makefile.
> +AM_MAKEFLAGS = \
> +	"AR_FLAGS=$(AR_FLAGS)" \
> +	"CC_FOR_BUILD=$(CC_FOR_BUILD)" \
> +	"CFLAGS=$(CFLAGS)" \
> +	"CXXFLAGS=$(CXXFLAGS)" \
> +	"CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
> +	"CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
> +	"INSTALL=$(INSTALL)" \
> +	"INSTALL_DATA=$(INSTALL_DATA)" \
> +	"INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
> +	"INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
> +	"JC1FLAGS=$(JC1FLAGS)" \
> +	"LDFLAGS=$(LDFLAGS)" \
> +	"LIBCFLAGS=$(LIBCFLAGS)" \
> +	"LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
> +	"MAKE=$(MAKE)" \
> +	"MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
> +	"PICFLAG=$(PICFLAG)" \
> +	"PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
> +	"SHELL=$(SHELL)" \
> +	"RUNTESTFLAGS=$(RUNTESTFLAGS)" \
> +	"exec_prefix=$(exec_prefix)" \
> +	"infodir=$(infodir)" \
> +	"libdir=$(libdir)" \
> +	"prefix=$(prefix)" \
> +	"includedir=$(includedir)" \
> +	"AR=$(AR)" \
> +	"AS=$(AS)" \
> +	"LD=$(LD)" \
> +	"LIBCFLAGS=$(LIBCFLAGS)" \
> +	"NM=$(NM)" \
> +	"PICFLAG=$(PICFLAG)" \
> +	"RANLIB=$(RANLIB)" \
> +	"DESTDIR=$(DESTDIR)"
> +
> +MAKEOVERRIDES = 
> +all: all-am
> +
> +.SUFFIXES:
> +.SUFFIXES: .S .cpp .lo .o .obj
> +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
> +	@for dep in $?; do \
> +	  case '$(am__configure_deps)' in \
> +	    *$$dep*) \
> +	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
> +	        && { if test -f $@; then exit 0; else break; fi; }; \
> +	      exit 1;; \
> +	  esac; \
> +	done; \
> +	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign hwasan/Makefile'; \
> +	$(am__cd) $(top_srcdir) && \
> +	  $(AUTOMAKE) --foreign hwasan/Makefile
> +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
> +	@case '$?' in \
> +	  *config.status*) \
> +	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
> +	  *) \
> +	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
> +	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
> +	esac;
> +
> +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
> +	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
> +
> +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
> +	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
> +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
> +	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
> +$(am__aclocal_m4_deps):
> +
> +install-toolexeclibLTLIBRARIES: $(toolexeclib_LTLIBRARIES)
> +	@$(NORMAL_INSTALL)
> +	@list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
> +	list2=; for p in $$list; do \
> +	  if test -f $$p; then \
> +	    list2="$$list2 $$p"; \
> +	  else :; fi; \
> +	done; \
> +	test -z "$$list2" || { \
> +	  echo " $(MKDIR_P) '$(DESTDIR)$(toolexeclibdir)'"; \
> +	  $(MKDIR_P) "$(DESTDIR)$(toolexeclibdir)" || exit 1; \
> +	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(toolexeclibdir)'"; \
> +	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(toolexeclibdir)"; \
> +	}
> +
> +uninstall-toolexeclibLTLIBRARIES:
> +	@$(NORMAL_UNINSTALL)
> +	@list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
> +	for p in $$list; do \
> +	  $(am__strip_dir) \
> +	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(toolexeclibdir)/$$f'"; \
> +	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(toolexeclibdir)/$$f"; \
> +	done
> +
> +clean-toolexeclibLTLIBRARIES:
> +	-test -z "$(toolexeclib_LTLIBRARIES)" || rm -f $(toolexeclib_LTLIBRARIES)
> +	@list='$(toolexeclib_LTLIBRARIES)'; \
> +	locs=`for p in $$list; do echo $$p; done | \
> +	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
> +	      sort -u`; \
> +	test -z "$$locs" || { \
> +	  echo rm -f $${locs}; \
> +	  rm -f $${locs}; \
> +	}
> +
> +libhwasan.la: $(libhwasan_la_OBJECTS) $(libhwasan_la_DEPENDENCIES) $(EXTRA_libhwasan_la_DEPENDENCIES) 
> +	$(AM_V_CXXLD)$(libhwasan_la_LINK) -rpath $(toolexeclibdir) $(libhwasan_la_OBJECTS) $(libhwasan_la_LIBADD) $(LIBS)
> +
> +mostlyclean-compile:
> +	-rm -f *.$(OBJEXT)
> +
> +distclean-compile:
> +	-rm -f *.tab.c
> +
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_allocator.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_dynamic_shadow.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_exceptions.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_globals.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors_vfork.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_linux.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_memintrinsics.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_new_delete.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_poisoning.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_report.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_setjmp.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_tag_mismatch_aarch64.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread_list.Plo@am__quote@
> +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_type_test.Plo@am__quote@
> +
> +.S.o:
> +@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
> +@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
> +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
> +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
> +@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ $<
> +
> +.S.obj:
> +@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
> +@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
> +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
> +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
> +@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
> +
> +.S.lo:
> +@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
> +@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
> +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
> +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
> +@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(LTCPPASCOMPILE) -c -o $@ $<
> +
> +.cpp.o:
> +@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
> +@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
> +@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
> +@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
> +@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
> +
> +.cpp.obj:
> +@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
> +@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
> +@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
> +@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
> +@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
> +
> +.cpp.lo:
> +@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
> +@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
> +@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
> +@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
> +@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
> +
> +mostlyclean-libtool:
> +	-rm -f *.lo
> +
> +clean-libtool:
> +	-rm -rf .libs _libs
> +
> +ID: $(am__tagged_files)
> +	$(am__define_uniq_tagged_files); mkid -fID $$unique
> +tags: tags-am
> +TAGS: tags
> +
> +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
> +	set x; \
> +	here=`pwd`; \
> +	$(am__define_uniq_tagged_files); \
> +	shift; \
> +	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
> +	  test -n "$$unique" || unique=$$empty_fix; \
> +	  if test $$# -gt 0; then \
> +	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
> +	      "$$@" $$unique; \
> +	  else \
> +	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
> +	      $$unique; \
> +	  fi; \
> +	fi
> +ctags: ctags-am
> +
> +CTAGS: ctags
> +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
> +	$(am__define_uniq_tagged_files); \
> +	test -z "$(CTAGS_ARGS)$$unique" \
> +	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
> +	     $$unique
> +
> +GTAGS:
> +	here=`$(am__cd) $(top_builddir) && pwd` \
> +	  && $(am__cd) $(top_srcdir) \
> +	  && gtags -i $(GTAGS_ARGS) "$$here"
> +cscopelist: cscopelist-am
> +
> +cscopelist-am: $(am__tagged_files)
> +	list='$(am__tagged_files)'; \
> +	case "$(srcdir)" in \
> +	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
> +	  *) sdir=$(subdir)/$(srcdir) ;; \
> +	esac; \
> +	for i in $$list; do \
> +	  if test -f "$$i"; then \
> +	    echo "$(subdir)/$$i"; \
> +	  else \
> +	    echo "$$sdir/$$i"; \
> +	  fi; \
> +	done >> $(top_builddir)/cscope.files
> +
> +distclean-tags:
> +	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
> +check-am: all-am
> +check: check-am
> +all-am: Makefile $(LTLIBRARIES)
> +installdirs:
> +	for dir in "$(DESTDIR)$(toolexeclibdir)"; do \
> +	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
> +	done
> +install: install-am
> +install-exec: install-exec-am
> +install-data: install-data-am
> +uninstall: uninstall-am
> +
> +install-am: all-am
> +	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
> +
> +installcheck: installcheck-am
> +install-strip:
> +	if test -z '$(STRIP)'; then \
> +	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
> +	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
> +	      install; \
> +	else \
> +	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
> +	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
> +	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
> +	fi
> +mostlyclean-generic:
> +
> +clean-generic:
> +
> +distclean-generic:
> +	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
> +	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
> +
> +maintainer-clean-generic:
> +	@echo "This command is intended for maintainers to use"
> +	@echo "it deletes files that may require special tools to rebuild."
> +clean: clean-am
> +
> +clean-am: clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \
> +	mostlyclean-am
> +
> +distclean: distclean-am
> +	-rm -rf ./$(DEPDIR)
> +	-rm -f Makefile
> +distclean-am: clean-am distclean-compile distclean-generic \
> +	distclean-tags
> +
> +dvi: dvi-am
> +
> +dvi-am:
> +
> +html: html-am
> +
> +html-am:
> +
> +info: info-am
> +
> +info-am:
> +
> +install-data-am:
> +
> +install-dvi: install-dvi-am
> +
> +install-dvi-am:
> +
> +install-exec-am: install-toolexeclibLTLIBRARIES
> +
> +install-html: install-html-am
> +
> +install-html-am:
> +
> +install-info: install-info-am
> +
> +install-info-am:
> +
> +install-man:
> +
> +install-pdf: install-pdf-am
> +
> +install-pdf-am:
> +
> +install-ps: install-ps-am
> +
> +install-ps-am:
> +
> +installcheck-am:
> +
> +maintainer-clean: maintainer-clean-am
> +	-rm -rf ./$(DEPDIR)
> +	-rm -f Makefile
> +maintainer-clean-am: distclean-am maintainer-clean-generic
> +
> +mostlyclean: mostlyclean-am
> +
> +mostlyclean-am: mostlyclean-compile mostlyclean-generic \
> +	mostlyclean-libtool
> +
> +pdf: pdf-am
> +
> +pdf-am:
> +
> +ps: ps-am
> +
> +ps-am:
> +
> +uninstall-am: uninstall-toolexeclibLTLIBRARIES
> +
> +.MAKE: install-am install-strip
> +
> +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
> +	clean-libtool clean-toolexeclibLTLIBRARIES cscopelist-am ctags \
> +	ctags-am distclean distclean-compile distclean-generic \
> +	distclean-libtool distclean-tags dvi dvi-am html html-am info \
> +	info-am install install-am install-data install-data-am \
> +	install-dvi install-dvi-am install-exec install-exec-am \
> +	install-html install-html-am install-info install-info-am \
> +	install-man install-pdf install-pdf-am install-ps \
> +	install-ps-am install-strip install-toolexeclibLTLIBRARIES \
> +	installcheck installcheck-am installdirs maintainer-clean \
> +	maintainer-clean-generic mostlyclean mostlyclean-compile \
> +	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
> +	tags tags-am uninstall uninstall-am \
> +	uninstall-toolexeclibLTLIBRARIES
> +
> +.PRECIOUS: Makefile
> +
> +
> +# Tell versions [3.59,3.63) of GNU make to not export all variables.
> +# Otherwise a system limit (for SysV at least) may be exceeded.
> +.NOEXPORT:
> diff --git a/libsanitizer/hwasan/libtool-version b/libsanitizer/hwasan/libtool-version
> new file mode 100644
> index 0000000000000000000000000000000000000000..d1f9aa1b950cc95efea87b7a70e96e44df0f9c17
> --- /dev/null
> +++ b/libsanitizer/hwasan/libtool-version
> @@ -0,0 +1,6 @@
> +# This file is used to maintain libtool version info for libhwasan.  See
> +# the libtool manual to understand the meaning of the fields.  This is
> +# a separate file so that version updates don't involve re-running
> +# automake.
> +# CURRENT:REVISION:AGE
> +0:0:0
> diff --git a/libsanitizer/interception/Makefile.in b/libsanitizer/interception/Makefile.in
> index a20f52ea4b825e733b2c0ca24780e766ad69367d..4a872cb49690e7b739ad07d73a517849be2ba1ea 100644
> --- a/libsanitizer/interception/Makefile.in
> +++ b/libsanitizer/interception/Makefile.in
> @@ -301,6 +301,7 @@ install_sh = @install_sh@
>  libdir = @libdir@
>  libexecdir = @libexecdir@
>  link_libasan = @link_libasan@
> +link_libhwasan = @link_libhwasan@
>  link_liblsan = @link_liblsan@
>  link_libtsan = @link_libtsan@
>  link_libubsan = @link_libubsan@
> diff --git a/libsanitizer/libbacktrace/Makefile.in b/libsanitizer/libbacktrace/Makefile.in
> index c843bcee0e4d39d788470d2d03b53d2f817aa231..3f05cdf3d621be74ee6346852c80b71f885e1203 100644
> --- a/libsanitizer/libbacktrace/Makefile.in
> +++ b/libsanitizer/libbacktrace/Makefile.in
> @@ -351,6 +351,7 @@ install_sh = @install_sh@
>  libdir = @libdir@
>  libexecdir = @libexecdir@
>  link_libasan = @link_libasan@
> +link_libhwasan = @link_libhwasan@
>  link_liblsan = @link_liblsan@
>  link_libtsan = @link_libtsan@
>  link_libubsan = @link_libubsan@
> diff --git a/libsanitizer/libsanitizer.spec.in b/libsanitizer/libsanitizer.spec.in
> index a4fa87d8dfc51549b0f932b31a610db1a2e86c29..70a33574d7b1d494c4875cfc41f29606bde2172e 100644
> --- a/libsanitizer/libsanitizer.spec.in
> +++ b/libsanitizer/libsanitizer.spec.in
> @@ -3,6 +3,8 @@
>  
>  *link_libasan: @link_libasan@
>  
> +*link_libhwasan: @link_libhwasan@
> +
>  *link_libtsan: @link_libtsan@
>  
>  *link_libubsan: @link_libubsan@
> diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in
> index 23a6fa69ec6941ec8a3f02f6a283b8015a8f706f..fbe87cd24961f0ca2d338b43c9d81af709a02802 100644
> --- a/libsanitizer/lsan/Makefile.in
> +++ b/libsanitizer/lsan/Makefile.in
> @@ -346,6 +346,7 @@ install_sh = @install_sh@
>  libdir = @libdir@
>  libexecdir = @libexecdir@
>  link_libasan = @link_libasan@
> +link_libhwasan = @link_libhwasan@
>  link_liblsan = @link_liblsan@
>  link_libtsan = @link_libtsan@
>  link_libubsan = @link_libubsan@
> diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in
> index 299de50b34a6c1cbb33d6f3536d8df1a132d2f59..7e5555cc9df53c5b081313d22ffc532adbe967a5 100644
> --- a/libsanitizer/sanitizer_common/Makefile.in
> +++ b/libsanitizer/sanitizer_common/Makefile.in
> @@ -338,6 +338,7 @@ install_sh = @install_sh@
>  libdir = @libdir@
>  libexecdir = @libexecdir@
>  link_libasan = @link_libasan@
> +link_libhwasan = @link_libhwasan@
>  link_liblsan = @link_liblsan@
>  link_libtsan = @link_libtsan@
>  link_libubsan = @link_libubsan@
> diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in
> index bfbdfe0d89c635c7ca1fe246722ec04949002f1b..ce11d2497b75a306f80f1701110b5eefc5330318 100644
> --- a/libsanitizer/tsan/Makefile.in
> +++ b/libsanitizer/tsan/Makefile.in
> @@ -375,6 +375,7 @@ install_sh = @install_sh@
>  libdir = @libdir@
>  libexecdir = @libexecdir@
>  link_libasan = @link_libasan@
> +link_libhwasan = @link_libhwasan@
>  link_liblsan = @link_liblsan@
>  link_libtsan = @link_libtsan@
>  link_libubsan = @link_libubsan@
> diff --git a/libsanitizer/ubsan/Makefile.in b/libsanitizer/ubsan/Makefile.in
> index 312d6a5e5137ab64faef9a3f0f44ae3284c23e1b..2775c1422137d46d0d68a0a516826e6d62daf137 100644
> --- a/libsanitizer/ubsan/Makefile.in
> +++ b/libsanitizer/ubsan/Makefile.in
> @@ -340,6 +340,7 @@ install_sh = @install_sh@
>  libdir = @libdir@
>  libexecdir = @libexecdir@
>  link_libasan = @link_libasan@
> +link_libhwasan = @link_libhwasan@
>  link_liblsan = @link_liblsan@
>  link_libtsan = @link_libtsan@
>  link_libubsan = @link_libubsan@

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 2/X] libsanitizer: Only build libhwasan when targeting AArch64
  2020-11-16 15:37       ` [PATCH 2/X] libsanitizer: Only build libhwasan when targeting AArch64 Matthew Malcomson
@ 2020-11-18 17:06         ` Richard Sandiford
  0 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-11-18 17:06 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, Richard Earnshaw, Kyrylo Tkachov, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> Though the library has limited support for x86, we don't have any
> support for generating code targeting x86 so there is no point building
> for that target.
>
> Ensure we build for AArch64 but not for AArch64 ilp32.
>
> libsanitizer/ChangeLog:
>
> 	* Makefile.am: Condition Build hwasan directory.
> 	* Makefile.in: Regenerate.
> 	* configure: Regenerate.
> 	* configure.ac: Set HWASAN_SUPPORTED based on target
> 	architecture.
> 	* configure.tgt: Likewise.

OK, thanks.

Richard

> ###############     Attachment also inlined for ease of reply    ###############
>
>
> diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am
> index 2a7e8e1debe838719db0f0fad218b2543cc3111b..065a65e78d49f7689a01ecb64db1f07ca83aa987 100644
> --- a/libsanitizer/Makefile.am
> +++ b/libsanitizer/Makefile.am
> @@ -14,7 +14,7 @@ endif
>  if LIBBACKTRACE_SUPPORTED
>  SUBDIRS += libbacktrace
>  endif
> -SUBDIRS += lsan asan ubsan hwasan
> +SUBDIRS += lsan asan ubsan
>  nodist_saninclude_HEADERS += \
>    include/sanitizer/lsan_interface.h \
>    include/sanitizer/asan_interface.h \
> @@ -23,6 +23,9 @@ nodist_saninclude_HEADERS += \
>  if TSAN_SUPPORTED
>  SUBDIRS += tsan
>  endif
> +if HWASAN_SUPPORTED
> +SUBDIRS += hwasan
> +endif
>  endif
>  
>  ## May be used by toolexeclibdir.
> diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in
> index 2c57d49cbffdb486645aeb5f2c0f85d6e0fad124..3873ea4d7050f04a3f7bbd0dd3f2a71e9b65d287 100644
> --- a/libsanitizer/Makefile.in
> +++ b/libsanitizer/Makefile.in
> @@ -97,6 +97,7 @@ target_triplet = @target@
>  @SANITIZER_SUPPORTED_TRUE@@USING_MAC_INTERPOSE_FALSE@am__append_2 = interception
>  @LIBBACKTRACE_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_3 = libbacktrace
>  @SANITIZER_SUPPORTED_TRUE@@TSAN_SUPPORTED_TRUE@am__append_4 = tsan
> +@HWASAN_SUPPORTED_TRUE@@SANITIZER_SUPPORTED_TRUE@am__append_5 = hwasan
>  subdir = .
>  ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
>  am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
> @@ -208,7 +209,7 @@ ETAGS = etags
>  CTAGS = ctags
>  CSCOPE = cscope
>  DIST_SUBDIRS = sanitizer_common interception libbacktrace lsan asan \
> -	ubsan hwasan tsan
> +	ubsan tsan hwasan
>  ACLOCAL = @ACLOCAL@
>  ALLOC_FILE = @ALLOC_FILE@
>  AMTAR = @AMTAR@
> @@ -364,7 +365,7 @@ sanincludedir = $(libdir)/gcc/$(target_alias)/$(gcc_version)/include/sanitizer
>  nodist_saninclude_HEADERS = $(am__append_1)
>  @SANITIZER_SUPPORTED_TRUE@SUBDIRS = sanitizer_common $(am__append_2) \
>  @SANITIZER_SUPPORTED_TRUE@	$(am__append_3) lsan asan ubsan \
> -@SANITIZER_SUPPORTED_TRUE@	hwasan $(am__append_4)
> +@SANITIZER_SUPPORTED_TRUE@	$(am__append_4) $(am__append_5)
>  gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
>  
>  # Work around what appears to be a GNU make bug handling MAKEFLAGS
> diff --git a/libsanitizer/configure b/libsanitizer/configure
> index 27e72c089cb891dcce09494fa9e39eebe55d2598..720d4e17044170e4b91c42fede685761d98c1965 100755
> --- a/libsanitizer/configure
> +++ b/libsanitizer/configure
> @@ -659,6 +659,8 @@ link_libubsan
>  link_libtsan
>  link_libhwasan
>  link_libasan
> +HWASAN_SUPPORTED_FALSE
> +HWASAN_SUPPORTED_TRUE
>  LSAN_SUPPORTED_FALSE
>  LSAN_SUPPORTED_TRUE
>  TSAN_SUPPORTED_FALSE
> @@ -12362,7 +12364,7 @@ else
>    lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
>    lt_status=$lt_dlunknown
>    cat > conftest.$ac_ext <<_LT_EOF
> -#line 12365 "configure"
> +#line 12367 "configure"
>  #include "confdefs.h"
>  
>  #if HAVE_DLFCN_H
> @@ -12468,7 +12470,7 @@ else
>    lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
>    lt_status=$lt_dlunknown
>    cat > conftest.$ac_ext <<_LT_EOF
> -#line 12471 "configure"
> +#line 12473 "configure"
>  #include "confdefs.h"
>  
>  #if HAVE_DLFCN_H
> @@ -15819,6 +15821,7 @@ fi
>  # Get target configury.
>  unset TSAN_SUPPORTED
>  unset LSAN_SUPPORTED
> +unset HWASAN_SUPPORTED
>  . ${srcdir}/configure.tgt
>   if test "x$TSAN_SUPPORTED" = "xyes"; then
>    TSAN_SUPPORTED_TRUE=
> @@ -15836,6 +15839,14 @@ else
>    LSAN_SUPPORTED_FALSE=
>  fi
>  
> + if test "x$HWASAN_SUPPORTED" = "xyes"; then
> +  HWASAN_SUPPORTED_TRUE=
> +  HWASAN_SUPPORTED_FALSE='#'
> +else
> +  HWASAN_SUPPORTED_TRUE='#'
> +  HWASAN_SUPPORTED_FALSE=
> +fi
> +
>  
>  # Check for functions needed.
>  for ac_func in clock_getres clock_gettime clock_settime lstat readlink
> @@ -16818,7 +16829,7 @@ ac_config_files="$ac_config_files Makefile libsanitizer.spec libbacktrace/backtr
>  ac_config_headers="$ac_config_headers config.h"
>  
>  
> -ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile hwasan/Makefile ubsan/Makefile"
> +ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile libbacktrace/Makefile lsan/Makefile asan/Makefile ubsan/Makefile"
>  
>  
>  if test "x$TSAN_SUPPORTED" = "xyes"; then
> @@ -16826,6 +16837,11 @@ if test "x$TSAN_SUPPORTED" = "xyes"; then
>  
>  fi
>  
> +if test "x$HWASAN_SUPPORTED" = "xyes"; then
> +  ac_config_files="$ac_config_files hwasan/Makefile"
> +
> +fi
> +
>  
>  
>  
> @@ -17090,6 +17106,10 @@ if test -z "${LSAN_SUPPORTED_TRUE}" && test -z "${LSAN_SUPPORTED_FALSE}"; then
>    as_fn_error $? "conditional \"LSAN_SUPPORTED\" was never defined.
>  Usually this means the macro was only invoked conditionally." "$LINENO" 5
>  fi
> +if test -z "${HWASAN_SUPPORTED_TRUE}" && test -z "${HWASAN_SUPPORTED_FALSE}"; then
> +  as_fn_error $? "conditional \"HWASAN_SUPPORTED\" was never defined.
> +Usually this means the macro was only invoked conditionally." "$LINENO" 5
> +fi
>  if test -z "${USING_MAC_INTERPOSE_TRUE}" && test -z "${USING_MAC_INTERPOSE_FALSE}"; then
>    as_fn_error $? "conditional \"USING_MAC_INTERPOSE\" was never defined.
>  Usually this means the macro was only invoked conditionally." "$LINENO" 5
> @@ -18099,9 +18119,9 @@ do
>      "libbacktrace/Makefile") CONFIG_FILES="$CONFIG_FILES libbacktrace/Makefile" ;;
>      "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;;
>      "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
> -    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
>      "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
>      "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
> +    "hwasan/Makefile") CONFIG_FILES="$CONFIG_FILES hwasan/Makefile" ;;
>  
>    *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
>    esac
> @@ -20035,7 +20055,7 @@ _EOF
>     . ${multi_basedir}/config-ml.in
>     { ml_norecursion=; unset ml_norecursion;}
>   ;;
> -    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
> +    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
>  s!`test -f '$<' || echo '$(srcdir)/'`!!
>  _EOF
>     sed -f vpsed$$ $ac_file > tmp$$
> @@ -20046,18 +20066,18 @@ _EOF
>     . ${multi_basedir}/config-ml.in
>     { ml_norecursion=; unset ml_norecursion;}
>   ;;
> -    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
> +    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
>  s!`test -f '$<' || echo '$(srcdir)/'`!!
>  _EOF
> -   sed -f vpsed$$ $ac_file > tmp$$
> -   mv tmp$$ $ac_file
> -   rm vpsed$$
> -   echo 'MULTISUBDIR =' >> $ac_file
> -   ml_norecursion=yes
> -   . ${multi_basedir}/config-ml.in
> -   { ml_norecursion=; unset ml_norecursion;}
> +    sed -f vpsed$$ $ac_file > tmp$$
> +    mv tmp$$ $ac_file
> +    rm vpsed$$
> +    echo 'MULTISUBDIR =' >> $ac_file
> +    ml_norecursion=yes
> +    . ${multi_basedir}/config-ml.in
> +    { ml_norecursion=; unset ml_norecursion;}
>   ;;
> -    "tsan/Makefile":F) cat > vpsed$$ << \_EOF
> +    "hwasan/Makefile":F) cat > vpsed$$ << \_EOF
>  s!`test -f '$<' || echo '$(srcdir)/'`!!
>  _EOF
>      sed -f vpsed$$ $ac_file > tmp$$
> diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac
> index f0d526c52967112905115c8fbe3540427a27ae03..13cd302030db7d0107ac8de0863d42558dea5c71 100644
> --- a/libsanitizer/configure.ac
> +++ b/libsanitizer/configure.ac
> @@ -96,9 +96,11 @@ fi
>  # Get target configury.
>  unset TSAN_SUPPORTED
>  unset LSAN_SUPPORTED
> +unset HWASAN_SUPPORTED
>  . ${srcdir}/configure.tgt
>  AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
>  AM_CONDITIONAL(LSAN_SUPPORTED, [test "x$LSAN_SUPPORTED" = "xyes"])
> +AM_CONDITIONAL(HWASAN_SUPPORTED, [test "x$HWASAN_SUPPORTED" = "xyes"])
>  
>  # Check for functions needed.
>  AC_CHECK_FUNCS(clock_getres clock_gettime clock_settime lstat readlink)
> @@ -352,7 +354,7 @@ AH_BOTTOM([#include "libbacktrace/backtrace-rename.h"])
>  AC_CONFIG_FILES([Makefile libsanitizer.spec libbacktrace/backtrace-supported.h])
>  AC_CONFIG_HEADER(config.h)
>  
> -AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan hwasan ubsan], [DIR/Makefile ]),
> +AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common libbacktrace lsan asan ubsan], [DIR/Makefile ]),
>    [cat > vpsed$$ << \_EOF
>  s!`test -f '$<' || echo '$(srcdir)/'`!!
>  _EOF
> @@ -380,6 +382,21 @@ _EOF
>  ])
>  fi
>  
> +if test "x$HWASAN_SUPPORTED" = "xyes"; then
> +  AC_CONFIG_FILES(AC_FOREACH([DIR], [hwasan], [DIR/Makefile ]), 
> +    [cat > vpsed$$ << \_EOF
> +s!`test -f '$<' || echo '$(srcdir)/'`!!
> +_EOF
> +    sed -f vpsed$$ $ac_file > tmp$$
> +    mv tmp$$ $ac_file
> +    rm vpsed$$
> +    echo 'MULTISUBDIR =' >> $ac_file
> +    ml_norecursion=yes
> +    . ${multi_basedir}/config-ml.in
> +    AS_UNSET([ml_norecursion])
> +])
> +fi
> +
>  AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS])
>  AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS])
>  
> diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt
> index 52503f1a880ba08b515b8a429ac44a262873f74b..d5b8dc4ec198c2a73dcef530a6e0d6a227eb30ee 100644
> --- a/libsanitizer/configure.tgt
> +++ b/libsanitizer/configure.tgt
> @@ -60,6 +60,7 @@ case "${target}" in
>  		TSAN_SUPPORTED=yes
>  		LSAN_SUPPORTED=yes
>  		TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
> +		HWASAN_SUPPORTED=yes
>  	fi
>  	;;
>    x86_64-*-darwin1[2-9]* | i?86-*-darwin1[2-9]*)

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 3/X] libsanitizer:  Add option to bootstrap using HWASAN
  2020-11-16 15:37       ` [PATCH 3/X] libsanitizer: Add option to bootstrap using HWASAN Matthew Malcomson
@ 2020-11-18 17:09         ` Richard Sandiford
  0 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-11-18 17:09 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, Richard Earnshaw, Kyrylo Tkachov, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> This is an analogous option to --bootstrap-asan to configure.  It allows
> bootstrapping GCC using HWASAN.
>
> For the same reasons as for ASAN we have to avoid using the HWASAN
> sanitizer when compiling libiberty and the lto-plugin.
>
> Also add a function to query whether -fsanitize=hwaddress has been
> passed.
>
> ChangeLog:
>
> 	* configure: Regenerate.
> 	* configure.ac: Add --bootstrap-hwasan option.
>
> config/ChangeLog:
>
> 	* bootstrap-hwasan.mk: New file.
>
> gcc/ChangeLog:
>
> 	* doc/install.texi: Document new option.
>
> libiberty/ChangeLog:
>
> 	* configure: Regenerate.
> 	* configure.ac: Avoid using sanitizer.
>
> lto-plugin/ChangeLog:
>
> 	* Makefile.am: Avoid using sanitizer.
> 	* Makefile.in: Regenerate.

OK, thanks.

> ###############     Attachment also inlined for ease of reply    ###############
>
>
> diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
> new file mode 100644
> index 0000000000000000000000000000000000000000..4f60bed3fd6e98b47a3a38aea6eba2a7c320da25
> --- /dev/null
> +++ b/config/bootstrap-hwasan.mk
> @@ -0,0 +1,8 @@
> +# This option enables -fsanitize=hwaddress for stage2 and stage3.
> +
> +STAGE2_CFLAGS += -fsanitize=hwaddress
> +STAGE3_CFLAGS += -fsanitize=hwaddress
> +POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
> +		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
> +		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
> +		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/.libs
> diff --git a/configure b/configure
> index a2ea1a329b69de06906315e54a49c694c9704522..b41a258c80ee9f289de534185eb364bcb5ca6ae5 100755
> --- a/configure
> +++ b/configure
> @@ -9305,7 +9305,7 @@ fi
>  # or bootstrap-ubsan, bootstrap it.
>  if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
>    case "$BUILD_CONFIG" in
> -    *bootstrap-asan* | *bootstrap-ubsan* )
> +    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
>        bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
>        bootstrap_fixincludes=yes
>        ;;
> diff --git a/configure.ac b/configure.ac
> index 44fa75f3a329ef68f6800c8e09d49a9373f731cf..944f30cfea84e9266b4322df7902b867882d4d8c 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -2814,7 +2814,7 @@ fi
>  # or bootstrap-ubsan, bootstrap it.
>  if echo " ${target_configdirs} " | grep " libsanitizer " > /dev/null 2>&1; then
>    case "$BUILD_CONFIG" in
> -    *bootstrap-asan* | *bootstrap-ubsan* )
> +    *bootstrap-hwasan* | *bootstrap-asan* | *bootstrap-ubsan* )
>        bootstrap_target_libs=${bootstrap_target_libs}target-libsanitizer,
>        bootstrap_fixincludes=yes
>        ;;
> diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
> index 60ee0a9dba17bf8f00d7c5320468ba847f08f8a0..e0f75b3d55582b5d1b8718f94e39c7d50656a926 100644
> --- a/gcc/doc/install.texi
> +++ b/gcc/doc/install.texi
> @@ -2794,6 +2794,11 @@ the build tree.
>  Compiles GCC itself using Address Sanitization in order to catch invalid memory
>  accesses within the GCC code.
>  
> +@item @samp{bootstrap-hwasan}
> +Compiles GCC itself using HWAddress Sanitization in order to catch invalid
> +memory accesses within the GCC code.  This option is only available on AArch64
> +systems that are running Linux kernel version 5.4 or later.
> +
>  @end table
>  
>  @section Building a cross compiler
> diff --git a/libiberty/configure b/libiberty/configure
> index ff93c9ee9a6fa9c6bb1938bcdfffb2d0ae8c9698..b6af9baf21204a323cad0e7b40a426c72988ba3b 100755
> --- a/libiberty/configure
> +++ b/libiberty/configure
> @@ -5264,6 +5264,7 @@ fi
>  NOASANFLAG=
>  case " ${CFLAGS} " in
>    *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
> +  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
>  esac
>  
>  
> diff --git a/libiberty/configure.ac b/libiberty/configure.ac
> index 4e2599c14a89bafcb8c7e523b9ce5b3d60b8c0f6..ad952963971a31968b5d109661b9cab0aa4b95fc 100644
> --- a/libiberty/configure.ac
> +++ b/libiberty/configure.ac
> @@ -240,6 +240,7 @@ AC_SUBST(PICFLAG)
>  NOASANFLAG=
>  case " ${CFLAGS} " in
>    *\ -fsanitize=address\ *) NOASANFLAG=-fno-sanitize=address ;;
> +  *\ -fsanitize=hwaddress\ *) NOASANFLAG=-fno-sanitize=hwaddress ;;
>  esac
>  AC_SUBST(NOASANFLAG)
>  
> diff --git a/lto-plugin/Makefile.am b/lto-plugin/Makefile.am
> index 204b25f45ef2f22bb246641a2aa9f9d09719737b..8b20e1d1d87e2dda9f37763492ddf39a8022c48c 100644
> --- a/lto-plugin/Makefile.am
> +++ b/lto-plugin/Makefile.am
> @@ -11,8 +11,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/../include $(DEFS)
>  AM_CFLAGS = @ac_lto_plugin_warn_cflags@ $(CET_HOST_FLAGS)
>  AM_LDFLAGS = @ac_lto_plugin_ldflags@
>  AM_LIBTOOLFLAGS = --tag=disable-static
> -override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
> -override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
> +override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
> +override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
>  
>  libexecsub_LTLIBRARIES = liblto_plugin.la
>  gcc_build_dir = @gcc_build_dir@
> diff --git a/lto-plugin/Makefile.in b/lto-plugin/Makefile.in
> index 834699b439e03cd1213c4b7fe1867ff7b5f19e98..20611c6b1e6f5e381d4690e34ab7a69f2c827bb6 100644
> --- a/lto-plugin/Makefile.in
> +++ b/lto-plugin/Makefile.in
> @@ -675,8 +675,8 @@ uninstall-am: uninstall-libexecsubLTLIBRARIES
>  
>  .PRECIOUS: Makefile
>  
> -override CFLAGS := $(filter-out -fsanitize=address,$(CFLAGS))
> -override LDFLAGS := $(filter-out -fsanitize=address,$(LDFLAGS))
> +override CFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(CFLAGS))
> +override LDFLAGS := $(filter-out -fsanitize=address -fsanitize=hwaddress,$(LDFLAGS))
>  
>  all-local: $(in_gcc_libs)
>  

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 4/X] libsanitizer: options: Add hwasan flags and argument parsing
  2020-11-16 15:37       ` [PATCH 4/X] libsanitizer: options: Add hwasan flags and argument parsing Matthew Malcomson
@ 2020-11-18 17:57         ` Richard Sandiford
  2020-11-20 18:48           ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Richard Sandiford @ 2020-11-18 17:57 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, Richard Earnshaw, Kyrylo Tkachov, Martin Liska

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

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

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

initialization

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

texinfo has:

  @quotation Note
  @end quotation

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

  @emph{Note:}

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

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

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

> +possible by specifying it on the command line with

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

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

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

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

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

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

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

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

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

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

Nit: long line.

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

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

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

“…if the backend architecture…”

LGTM otherwise, thanks.

Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Update: [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN
  2020-10-14 16:09       ` Richard Sandiford
  2020-11-03 13:00         ` Matthew Malcomson
@ 2020-11-19 12:57         ` Matthew Malcomson
  2020-11-19 15:28           ` Richard Sandiford
  1 sibling, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-19 12:57 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: Richard Earnshaw, Kyrylo Tkachov, gcc-patches, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 56567 bytes --]

Hi there,

After offline discussion with Richard I've modified the way in which the
initialisation for the hwasan base pointer is emitted.
Originally it was getting emitted during `expand_used_vars`, and
requiring `handle_builtin_alloca` to register a need for it to be
emitted so that `expand_HWASAN_CHOOSE_TAG` can use an initialised base
pointer.

Now we go through the entire expansion of the function body, and then if
`hwasan_frame_base_ptr` was used anywhere we emit the initialisation
just before `parm_birth_insn`.

This is the updated patch for the stack variable handling.
(Testing underway)

MM

---


Handling stack variables has three features.

1) Ensure HWASAN required alignment for stack variables

When tagging shadow memory, we need to ensure that each tag granule is
only used by one variable at a time.

This is done by ensuring that each tagged variable is aligned to the tag
granule representation size and also ensure that the end of each
object is aligned to ensure the start of any other data stored on the
stack is in a different granule.

This patch ensures the above by forcing the stack pointer to be aligned
before and after allocating any stack objects. Since we are forcing
alignment we also use `align_local_variable` to ensure this new alignment
is advertised properly through SET_DECL_ALIGN.

2) Put tags into each stack variable pointer

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

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

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

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

Backend hooks define the size of a tag, the layout of the HWASAN shadow
memory, and handle emitting the code that inserts and extracts tags from a
pointer.

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

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

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

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

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

config/ChangeLog:

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

ChangeLog:

	* gcc/asan.c (struct hwasan_stack_var): New.
	(hwasan_sanitize_p): New.
	(hwasan_sanitize_stack_p): New.
	(hwasan_sanitize_allocas_p): New.
	(initialize_sanitizer_builtins): Define new builtins.
	(ATTR_NOTHROW_LIST): New macro.
	(hwasan_current_frame_tag): New.
	(hwasan_frame_base): New.
	(stack_vars_base_reg_p): New.
	(hwasan_maybe_emit_frame_base_init): New.
	(hwasan_record_stack_var): New.
	(hwasan_get_frame_extent): New.
	(hwasan_increment_frame_tag): New.
	(hwasan_record_frame_init): New.
	(hwasan_emit_prologue): New.
	(hwasan_emit_untag_frame): New.
	(hwasan_finish_file): New.
	(hwasan_truncate_to_tag_size): New.
	* gcc/asan.h (hwasan_record_frame_init): New declaration.
	(hwasan_record_stack_var): New declaration.
	(hwasan_emit_prologue): New declaration.
	(hwasan_emit_untag_frame): New declaration.
	(hwasan_get_frame_extent): New declaration.
	(hwasan_maybe_emit_frame_base_init): New declaration.
	(hwasan_frame_base): New declaration.
	(stack_vars_base_reg_p): New declaration.
	(hwasan_current_frame_tag): New declaration.
	(hwasan_increment_frame_tag): New declaration.
	(hwasan_truncate_to_tag_size): New declaration.
	(hwasan_finish_file): New declaration.
	(hwasan_sanitize_p): New declaration.
	(hwasan_sanitize_stack_p): New declaration.
	(hwasan_sanitize_allocas_p): New declaration.
	(HWASAN_TAG_SIZE): New macro.
	(HWASAN_TAG_GRANULE_SIZE): New macro.
	(HWASAN_STACK_BACKGROUND): New macro.
	* gcc/builtin-types.def (BT_FN_VOID_PTR_UINT8_PTRMODE): New.
	* gcc/builtins.def (DEF_SANITIZER_BUILTIN): Enable for HWASAN.
	* gcc/cfgexpand.c (align_local_variable): When using hwasan ensure
	alignment to tag granule.
	(align_frame_offset): New.
	(expand_one_stack_var_at): For hwasan use tag offset.
	(expand_stack_vars): Record stack objects for hwasan.
	(expand_one_stack_var_1): Record stack objects for hwasan.
	(init_vars_expansion): Initialise hwasan state.
	(expand_used_vars): Emit hwasan prologue and generate hwasan epilogue.
	(pass_expand::execute): Emit hwasan base initialization if needed.
	* gcc/doc/tm.texi (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
	TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
	TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
	TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
	* gcc/doc/tm.texi.in (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
	TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
	TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
	TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
	* gcc/explow.c (get_dynamic_stack_base): Take new `base` argument.
	* gcc/explow.h (get_dynamic_stack_base): Take new `base` argument.
	* gcc/sanitizer.def (BUILT_IN_HWASAN_INIT): New.
	(BUILT_IN_HWASAN_TAG_MEM): New.
	* gcc/target.def (target_memtag_tag_size,target_memtag_granule_size,
	target_memtag_insert_random_tag,target_memtag_add_tag,
	target_memtag_set_tag,target_memtag_extract_tag,
	target_memtag_untagged_pointer): New hooks.
	* gcc/targhooks.c (HWASAN_SHIFT): New.
	(HWASAN_SHIFT_RTX): New.
	(default_memtag_tag_size): New default hook.
	(default_memtag_granule_size): New default hook.
	(default_memtag_insert_random_tag): New default hook.
	(default_memtag_add_tag): New default hook.
	(default_memtag_set_tag): New default hook.
	(default_memtag_extract_tag): New default hook.
	(default_memtag_untagged_pointer): New default hook.
	* gcc/targhooks.h (default_memtag_tag_size): New default hook.
	(default_memtag_granule_size): New default hook.
	(default_memtag_insert_random_tag): New default hook.
	(default_memtag_add_tag): New default hook.
	(default_memtag_set_tag): New default hook.
	(default_memtag_extract_tag): New default hook.
	(default_memtag_untagged_pointer): New default hook.
	* gcc/toplev.c (compile_file): Call hwasan_finish_file when finished.


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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -1,7 +1,11 @@
 # This option enables -fsanitize=hwaddress for stage2 and stage3.
+# We need to disable random frame tags for bootstrap since the autoconf check
+# for which direction the stack is growing has UB that a random frame tag
+# breaks.  Running with a random frame tag gives approx. 50% chance of
+# bootstrap comparison diff in libiberty/alloca.c.
 
-STAGE2_CFLAGS += -fsanitize=hwaddress
-STAGE3_CFLAGS += -fsanitize=hwaddress
+STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
 POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
diff --git a/gcc/asan.h b/gcc/asan.h
index 114b457ef91c4479d43774bed58c24213196ce12..8d5271e6b575d74da277420798557f3274e966ce 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -34,6 +34,22 @@ extern bool asan_expand_mark_ifn (gimple_stmt_iterator *);
 extern bool asan_expand_poison_ifn (gimple_stmt_iterator *, bool *,
 				    hash_map<tree, tree> &);
 
+extern void hwasan_record_frame_init ();
+extern void hwasan_record_stack_var (rtx, rtx, poly_int64, poly_int64);
+extern void hwasan_emit_prologue ();
+extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx);
+extern rtx hwasan_get_frame_extent ();
+extern rtx hwasan_frame_base ();
+extern void hwasan_maybe_emit_frame_base_init (void);
+extern bool stack_vars_base_reg_p (rtx);
+extern uint8_t hwasan_current_frame_tag ();
+extern void hwasan_increment_frame_tag ();
+extern rtx hwasan_truncate_to_tag_size (rtx, rtx);
+extern void hwasan_finish_file (void);
+extern bool hwasan_sanitize_p (void);
+extern bool hwasan_sanitize_stack_p (void);
+extern bool hwasan_sanitize_allocas_p (void);
+
 extern gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
 
@@ -75,6 +91,26 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The values below and the hooks under targetm.memtag define an ABI and
+   are hard-coded to these values in libhwasan, hence they can't be changed
+   independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   The default version uses the entire top byte of a pointer (i.e. 8 bits).  */
+#define HWASAN_TAG_SIZE targetm.memtag.tag_size ()
+/* Tag Granule of HWASAN shadow stack.
+   This is the size in real memory that each byte in the shadow memory refers
+   to.  I.e. if a variable is X bytes long in memory then its tag in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   Most variables will need to be aligned to this amount since two variables
+   that are neighbors in memory and share a tag granule would need to share the
+   same tag (the shared tag granule can only store one tag).  */
+#define HWASAN_TAG_GRANULE_SIZE targetm.memtag.granule_size ()
+/* Define the tag for the stack background.
+   This defines what tag the stack pointer will be and hence what tag all
+   variables that are not given special tags are (e.g. spilled registers,
+   and parameters passed on the stack).  */
+#define HWASAN_STACK_BACKGROUND gen_int_mode (0, QImode)
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/asan.c b/gcc/asan.c
index 0b471afff64ea6a0ffbe0add71333ac688c472c6..157774f4cb666515b5862f48d13d7211f35ffa12 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -257,6 +257,58 @@ hash_set<tree> *asan_handled_variables = NULL;
 
 hash_set <tree> *asan_used_labels = NULL;
 
+/* Global variables for HWASAN stack tagging.  */
+/* hwasan_frame_tag_offset records the offset from the frame base tag that the
+   next object should have.  */
+static uint8_t hwasan_frame_tag_offset = 0;
+/* hwasan_frame_base_ptr is a pointer with the same address as
+   `virtual_stack_vars_rtx` for the current frame, and with the frame base tag
+   stored in it.  N.b. this global RTX does not need to be marked GTY, but is
+   done so anyway.  The need is not there since all uses are in just one pass
+   (cfgexpand) and there are no calls to ggc_collect between the uses.  We mark
+   it GTY(()) anyway to allow the use of the variable later on if needed by
+   future features.  */
+static GTY(()) rtx hwasan_frame_base_ptr = NULL_RTX;
+/* hwasan_frame_base_init_seq is the sequence of RTL insns that will initialize
+   the hwasan_frame_base_ptr.  When the hwasan_frame_base_ptr is requested, we
+   generate this sequence but do not emit it.  If the sequence was created it
+   is emitted once the function body has been expanded.
+
+   This delay is because the frame base pointer may be needed anywhere in the
+   function body, or needed by the expand_used_vars function.  Emitting once in
+   a known place is simpler than requiring the emition of the instructions to
+   be know where it should go depending on the first place the hwasan frame
+   base is needed.  */
+static GTY(()) rtx_insn *hwasan_frame_base_init_seq = NULL;
+
+/* Structure defining the extent of one object on the stack that HWASAN needs
+   to tag in the corresponding shadow stack space.
+
+   The range this object spans on the stack is between `untagged_base +
+   nearest_offset` and `untagged_base + farthest_offset`.
+   `tagged_base` is an rtx containing the same value as `untagged_base` but
+   with a random tag stored in the top byte.  We record both `untagged_base`
+   and `tagged_base` so that `hwasan_emit_prologue` can use both without having
+   to emit RTL into the instruction stream to re-calculate one from the other.
+   (`hwasan_emit_prologue` needs to use both bases since the
+   __hwasan_tag_memory call it emits uses an untagged value, and it calculates
+   the tag to store in shadow memory based on the tag_offset plus the tag in
+   tagged_base).  */
+struct hwasan_stack_var
+{
+  rtx untagged_base;
+  rtx tagged_base;
+  poly_int64 nearest_offset;
+  poly_int64 farthest_offset;
+  uint8_t tag_offset;
+};
+
+/* Variable recording all stack variables that HWASAN needs to tag.
+   Does not need to be marked as GTY(()) since every use is in the cfgexpand
+   pass and gcc_collect is not called in the middle of that pass.  */
+static vec<hwasan_stack_var> hwasan_tagged_stack_vars;
+
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -1359,6 +1411,28 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+/* Returns whether we are tagging pointers and checking those tags on memory
+   access.  */
+bool
+hwasan_sanitize_p ()
+{
+  return sanitize_flags_p (SANITIZE_HWADDRESS);
+}
+
+/* Are we tagging the stack?  */
+bool
+hwasan_sanitize_stack_p ()
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_stack);
+}
+
+/* Are we tagging alloca objects?  */
+bool
+hwasan_sanitize_allocas_p (void)
+{
+  return (hwasan_sanitize_stack_p () && param_hwasan_protect_allocas);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -2908,6 +2982,11 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_VOID_PTR_UINT8_PTRMODE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				unsigned_char_type_node,
+				pointer_sized_int_node, NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
@@ -2958,6 +3037,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NOTHROW_LIST
+#define ATTR_NOTHROW_LIST ECF_NOTHROW
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
@@ -3709,4 +3790,352 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/* For stack tagging:
+
+   Return the offset from the frame base tag that the "next" expanded object
+   should have.  */
+uint8_t
+hwasan_current_frame_tag ()
+{
+  return hwasan_frame_tag_offset;
+}
+
+/* For stack tagging:
+
+   Return the 'base pointer' for this function.  If that base pointer has not
+   yet been created then we create a register to hold it and record the insns
+   to initialize the register in `hwasan_frame_base_init_seq` for later
+   emission.  */
+rtx
+hwasan_frame_base ()
+{
+  if (! hwasan_frame_base_ptr)
+    {
+      start_sequence ();
+      hwasan_frame_base_ptr =
+	force_reg (Pmode,
+		   targetm.memtag.insert_random_tag (virtual_stack_vars_rtx,
+						     NULL_RTX));
+      hwasan_frame_base_init_seq = get_insns ();
+      end_sequence ();
+    }
+
+  return hwasan_frame_base_ptr;
+}
+
+/* For stack tagging:
+
+   Check whether this RTX is a standard pointer addressing the base of the
+   stack variables for this frame.  Returns true if the RTX is either
+   virtual_stack_vars_rtx or hwasan_frame_base_ptr.  */
+bool
+stack_vars_base_reg_p (rtx base)
+{
+  return base == virtual_stack_vars_rtx || base == hwasan_frame_base_ptr;
+}
+
+/* For stack tagging:
+
+   Emit frame base initialisation.
+   If hwasan_frame_base has been used before here then
+   hwasan_frame_base_init_seq contains the sequence of instructions to
+   initialize it.  This must be put just before the hwasan prologue, so we emit
+   the insns before parm_birth_insn (which will point to the first instruction
+   of the hwasan prologue if it exists).
+
+   We update `parm_birth_insn` to point to the start of this initialisation
+   since that represents the end of the initialisation done by
+   expand_function_{start,end} functions and we want to maintain that.  */
+void
+hwasan_maybe_emit_frame_base_init ()
+{
+  if (! hwasan_frame_base_init_seq)
+    return;
+  emit_insn_before (hwasan_frame_base_init_seq, parm_birth_insn);
+  parm_birth_insn = hwasan_frame_base_init_seq;
+}
+
+/* Record a compile-time constant size stack variable that HWASAN will need to
+   tag.  This record of the range of a stack variable will be used by
+   `hwasan_emit_prologue` to emit the RTL at the start of each frame which will
+   set tags in the shadow memory according to the assigned tag for each object.
+
+   The range that the object spans in stack space should be described by the
+   bounds `untagged_base + nearest_offset` and
+   `untagged_base + farthest_offset`.
+   `tagged_base` is the base address which contains the "base frame tag" for
+   this frame, and from which the value to address this object with will be
+   calculated.
+
+   We record the `untagged_base` since the functions in the hwasan library we
+   use to tag memory take pointers without a tag.  */
+void
+hwasan_record_stack_var (rtx untagged_base, rtx tagged_base,
+			 poly_int64 nearest_offset, poly_int64 farthest_offset)
+{
+  hwasan_stack_var cur_var;
+  cur_var.untagged_base = untagged_base;
+  cur_var.tagged_base = tagged_base;
+  cur_var.nearest_offset = nearest_offset;
+  cur_var.farthest_offset = farthest_offset;
+  cur_var.tag_offset = hwasan_current_frame_tag ();
+
+  hwasan_tagged_stack_vars.safe_push (cur_var);
+}
+
+/* Return the RTX representing the farthest extent of the statically allocated
+   stack objects for this frame.  If hwasan_frame_base_ptr has not been
+   initialized then we are not storing any static variables on the stack in
+   this frame.  In this case we return NULL_RTX to represent that.
+
+   Otherwise simply return virtual_stack_vars_rtx + frame_offset.  */
+rtx
+hwasan_get_frame_extent ()
+{
+  return (hwasan_frame_base_ptr
+	  ? plus_constant (Pmode, virtual_stack_vars_rtx, frame_offset)
+	  : NULL_RTX);
+}
+
+/* For stack tagging:
+
+   Increment the frame tag offset modulo the size a tag can represent.  */
+void
+hwasan_increment_frame_tag ()
+{
+  uint8_t tag_bits = HWASAN_TAG_SIZE;
+  gcc_assert (HWASAN_TAG_SIZE
+	      <= sizeof (hwasan_frame_tag_offset) * CHAR_BIT);
+  hwasan_frame_tag_offset = (hwasan_frame_tag_offset + 1) % (1 << tag_bits);
+  /* The "background tag" of the stack is zero by definition.
+     This is the tag that objects like parameters passed on the stack and
+     spilled registers are given.  It is handy to avoid this tag for objects
+     whose tags we decide ourselves, partly to ensure that buffer overruns
+     can't affect these important variables (e.g. saved link register, saved
+     stack pointer etc) and partly to make debugging easier (everything with a
+     tag of zero is space allocated automatically by the compiler).
+
+     This is not feasible when using random frame tags (the default
+     configuration for hwasan) since the tag for the given frame is randomly
+     chosen at runtime.  In order to avoid any tags matching the stack
+     background we would need to decide tag offsets at runtime instead of
+     compile time (and pay the resulting performance cost).
+
+     When not using random base tags for each frame (i.e. when compiled with
+     `--param hwasan-random-frame-tag=0`) the base tag for each frame is zero.
+     This means the tag that each object gets is equal to the
+     hwasan_frame_tag_offset used in determining it.
+     When this is the case we *can* ensure no object gets the tag of zero by
+     simply ensuring no object has the hwasan_frame_tag_offset of zero.
+
+     There is the extra complication that we only record the
+     hwasan_frame_tag_offset here (which is the offset from the tag stored in
+     the stack pointer).  In the kernel, the tag in the stack pointer is 0xff
+     rather than zero.  This does not cause problems since tags of 0xff are
+     never checked in the kernel.  As mentioned at the beginning of this
+     comment the background tag of the stack is zero by definition, which means
+     that for the kernel we should skip offsets of both 0 and 1 from the stack
+     pointer.  Avoiding the offset of 0 ensures we use a tag which will be
+     checked, avoiding the offset of 1 ensures we use a tag that is not the
+     same as the background.  */
+  if (hwasan_frame_tag_offset == 0 && ! param_hwasan_random_frame_tag)
+    hwasan_frame_tag_offset += 1;
+  if (hwasan_frame_tag_offset == 1 && ! param_hwasan_random_frame_tag
+      && sanitize_flags_p (SANITIZE_KERNEL_HWADDRESS))
+    hwasan_frame_tag_offset += 1;
+}
+
+/* Clear internal state for the next function.
+   This function is called before variables on the stack get expanded, in
+   `init_vars_expansion`.  */
+void
+hwasan_record_frame_init ()
+{
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
+  /* If this isn't the case then some stack variable was recorded *before*
+     hwasan_record_frame_init is called, yet *after* the hwasan prologue for
+     the previous frame was emitted.  Such stack variables would not have
+     their shadow stack filled in.  */
+  gcc_assert (hwasan_tagged_stack_vars.is_empty ());
+  hwasan_frame_base_ptr = NULL_RTX;
+  hwasan_frame_base_init_seq = NULL;
+
+  /* When not using a random frame tag we can avoid the background stack
+     color which gives the user a little better debug output upon a crash.
+     Meanwhile, when using a random frame tag it will be nice to avoid adding
+     tags for the first object since that is unnecessary extra work.
+     Hence set the initial hwasan_frame_tag_offset to be 0 if using a random
+     frame tag and 1 otherwise.
+
+     As described in hwasan_increment_frame_tag, in the kernel the stack
+     pointer has the tag 0xff.  That means that to avoid 0xff and 0 (the tag
+     which the kernel does not check and the background tag respectively) we
+     start with a tag offset of 2.  */
+  hwasan_frame_tag_offset = param_hwasan_random_frame_tag
+    ? 0
+    : sanitize_flags_p (SANITIZE_KERNEL_HWADDRESS) ? 2 : 1;
+}
+
+/* For stack tagging:
+   (Emits HWASAN equivalent of what is emitted by
+   `asan_emit_stack_protection`).
+
+   Emits the extra prologue code to set the shadow stack as required for HWASAN
+   stack instrumentation.
+
+   Uses the vector of recorded stack variables hwasan_tagged_stack_vars.  When
+   this function has completed hwasan_tagged_stack_vars is empty and all
+   objects it had pointed to are deallocated.  */
+void
+hwasan_emit_prologue ()
+{
+  /* We need untagged base pointers since libhwasan only accepts untagged
+    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
+    the base tag for an offset.  */
+
+  if (hwasan_tagged_stack_vars.is_empty ())
+    return;
+
+  size_t length = hwasan_tagged_stack_vars.length ();
+  hwasan_stack_var *vars = hwasan_tagged_stack_vars.address ();
+
+  poly_int64 bot = 0, top = 0;
+  size_t i = 0;
+  for (i = 0; i < length; i++)
+    {
+      hwasan_stack_var& cur = vars[i];
+      poly_int64 nearest = cur.nearest_offset;
+      poly_int64 farthest = cur.farthest_offset;
+
+      if (known_ge (nearest, farthest))
+	{
+	  top = nearest;
+	  bot = farthest;
+	}
+      else
+	{
+	  /* Given how these values are calculated, one must be known greater
+	     than the other.  */
+	  gcc_assert (known_le (nearest, farthest));
+	  top = farthest;
+	  bot = nearest;
+	}
+      poly_int64 size = (top - bot);
+
+      /* Assert the edge of each variable is aligned to the HWASAN tag granule
+	 size.  */
+      gcc_assert (multiple_p (top, HWASAN_TAG_GRANULE_SIZE));
+      gcc_assert (multiple_p (bot, HWASAN_TAG_GRANULE_SIZE));
+      gcc_assert (multiple_p (size, HWASAN_TAG_GRANULE_SIZE));
+
+      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+      rtx base_tag = targetm.memtag.extract_tag (cur.tagged_base, NULL_RTX);
+      rtx tag = plus_constant (QImode, base_tag, cur.tag_offset);
+      tag = hwasan_truncate_to_tag_size (tag, NULL_RTX);
+
+      rtx bottom = convert_memory_address (ptr_mode,
+					   plus_constant (Pmode,
+							  cur.untagged_base,
+							  bot));
+      emit_library_call (ret, LCT_NORMAL, VOIDmode,
+			 bottom, ptr_mode,
+			 tag, QImode,
+			 gen_int_mode (size, ptr_mode), ptr_mode);
+    }
+  /* Clear the stack vars, we've emitted the prologue for them all now.  */
+  hwasan_tagged_stack_vars.truncate (0);
+}
+
+/* For stack tagging:
+
+   Return RTL insns to clear the tags between DYNAMIC and VARS pointers
+   into the stack.  These instructions should be emitted at the end of
+   every function.
+
+   If `dynamic` is NULL_RTX then no insns are returned.  */
+rtx_insn *
+hwasan_emit_untag_frame (rtx dynamic, rtx vars)
+{
+  if (! dynamic)
+    return NULL;
+
+  start_sequence ();
+
+  dynamic = convert_memory_address (ptr_mode, dynamic);
+  vars = convert_memory_address (ptr_mode, vars);
+
+  rtx top_rtx;
+  rtx bot_rtx;
+  if (FRAME_GROWS_DOWNWARD)
+    {
+      top_rtx = vars;
+      bot_rtx = dynamic;
+    }
+  else
+    {
+      top_rtx = dynamic;
+      bot_rtx = vars;
+    }
+
+  rtx size_rtx = expand_simple_binop (ptr_mode, MINUS, top_rtx, bot_rtx,
+				      NULL_RTX, /* unsignedp = */0,
+				      OPTAB_DIRECT);
+
+  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (ret, LCT_NORMAL, VOIDmode,
+		     bot_rtx, ptr_mode,
+		     HWASAN_STACK_BACKGROUND, QImode,
+		     size_rtx, ptr_mode);
+
+  do_pending_stack_adjust ();
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+/* Needs to be GTY(()), because cgraph_build_static_cdtor may
+   invoke ggc_collect.  */
+static GTY(()) tree hwasan_ctor_statements;
+
+/* Insert module initialization into this TU.  This initialization calls the
+   initialization code for libhwasan.  */
+void
+hwasan_finish_file (void)
+{
+  /* Do not emit constructor initialization for the kernel.
+     (the kernel has its own initialization already).  */
+  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+    return;
+
+  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
+  flag_sanitize &= ~SANITIZE_HWADDRESS;
+  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
+  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
+  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
+  flag_sanitize |= SANITIZE_HWADDRESS;
+}
+
+/* For stack tagging:
+
+   Truncate `tag` to the number of bits that a tag uses (i.e. to
+   HWASAN_TAG_SIZE).  Store the result in `target` if it's convenient.  */
+rtx
+hwasan_truncate_to_tag_size (rtx tag, rtx target)
+{
+  gcc_assert (GET_MODE (tag) == QImode);
+  if (HWASAN_TAG_SIZE != GET_MODE_PRECISION (QImode))
+    {
+      gcc_assert (GET_MODE_PRECISION (QImode) > HWASAN_TAG_SIZE);
+      rtx mask = gen_int_mode ((HOST_WIDE_INT_1U << HWASAN_TAG_SIZE) - 1,
+			       QImode);
+      tag = expand_simple_binop (QImode, AND, tag, mask, target,
+				 /* unsignedp = */1, OPTAB_WIDEN);
+      gcc_assert (tag);
+    }
+  return tag;
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index 4a82ee421bef42154ccd88e52f7a19f48b340c73..1ad6657da45cc4976532e1b8bc233f67d8da9ccf 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -639,6 +639,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_CONST_SIZE_BOOL,
 		     BT_PTR, BT_PTR, BT_CONST_SIZE, BT_BOOL)
 DEF_FUNCTION_TYPE_3 (BT_FN_PTR_SIZE_SIZE_PTRMODE,
 		     BT_PTR, BT_SIZE, BT_SIZE, BT_PTRMODE)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_PTRMODE, BT_VOID, BT_PTR, BT_UINT8,
+		     BT_PTRMODE)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index b4494c712a1751fbb37378f38cc1411d11a37331..97bb5d0b0aee7fa9ee4c82e2d80eae866fc23829 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -245,6 +245,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
+				| SANITIZE_HWADDRESS \
 				| SANITIZE_UNDEFINED \
 				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 1df6f4bc55a39230c98e58af6c2d765652db8324..60f79ce799302a0a276199848a6f1b7d8a4aa4eb 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -376,15 +376,18 @@ align_local_variable (tree decl, bool really_expand)
 	align = GET_MODE_ALIGNMENT (mode);
     }
   else
-    {
-      align = LOCAL_DECL_ALIGNMENT (decl);
-      /* Don't change DECL_ALIGN when called from estimated_stack_frame_size.
-	 That is done before IPA and could bump alignment based on host
-	 backend even for offloaded code which wants different
-	 LOCAL_DECL_ALIGNMENT.  */
-      if (really_expand)
-	SET_DECL_ALIGN (decl, align);
-    }
+    align = LOCAL_DECL_ALIGNMENT (decl);
+
+  if (hwasan_sanitize_stack_p ())
+    align = MAX (align, (unsigned) HWASAN_TAG_GRANULE_SIZE * BITS_PER_UNIT);
+
+  if (TREE_CODE (decl) != SSA_NAME && really_expand)
+    /* Don't change DECL_ALIGN when called from estimated_stack_frame_size.
+       That is done before IPA and could bump alignment based on host
+       backend even for offloaded code which wants different
+       LOCAL_DECL_ALIGNMENT.  */
+    SET_DECL_ALIGN (decl, align);
+
   return align / BITS_PER_UNIT;
 }
 
@@ -428,6 +431,14 @@ alloc_stack_frame_space (poly_int64 size, unsigned HOST_WIDE_INT align)
   return offset;
 }
 
+/* Ensure that the stack is aligned to ALIGN bytes.
+   Return the new frame offset.  */
+static poly_int64
+align_frame_offset (unsigned HOST_WIDE_INT align)
+{
+  return alloc_stack_frame_space (0, align);
+}
+
 /* Accumulate DECL into STACK_VARS.  */
 
 static void
@@ -1004,7 +1015,12 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   /* If this fails, we've overflowed the stack frame.  Error nicely?  */
   gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
 
-  x = plus_constant (Pmode, base, offset);
+  if (hwasan_sanitize_stack_p ())
+    x = targetm.memtag.add_tag (base, offset,
+				hwasan_current_frame_tag ());
+  else
+    x = plus_constant (Pmode, base, offset);
+
   x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
 		   ? TYPE_MODE (TREE_TYPE (decl))
 		   : DECL_MODE (decl), x);
@@ -1013,7 +1029,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
      If it is we generate stack slots only accidentally so it isn't as
      important, we'll simply set the alignment directly on the MEM.  */
 
-  if (base == virtual_stack_vars_rtx)
+  if (stack_vars_base_reg_p (base))
     offset -= frame_phase;
   align = known_alignment (offset);
   align *= BITS_PER_UNIT;
@@ -1056,13 +1072,13 @@ public:
 /* A subroutine of expand_used_vars.  Give each partition representative
    a unique location within the stack frame.  Update each partition member
    with that location.  */
-
 static void
 expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 {
   size_t si, i, j, n = stack_vars_num;
   poly_uint64 large_size = 0, large_alloc = 0;
   rtx large_base = NULL;
+  rtx large_untagged_base = NULL;
   unsigned large_align = 0;
   bool large_allocation_done = false;
   tree decl;
@@ -1113,7 +1129,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
     {
       rtx base;
       unsigned base_align, alignb;
-      poly_int64 offset;
+      poly_int64 offset = 0;
 
       i = stack_vars_sorted[si];
 
@@ -1134,10 +1150,33 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       if (pred && !pred (i))
 	continue;
 
+      base = (hwasan_sanitize_stack_p ()
+	      ? hwasan_frame_base ()
+	      : virtual_stack_vars_rtx);
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  base = virtual_stack_vars_rtx;
+	  poly_int64 hwasan_orig_offset;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* There must be no tag granule "shared" between different
+		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
+		 chunk can have more than one object in it.
+
+		 We ensure this by forcing the end of the last bit of data to
+		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
+		 the start of each variable to be aligned to
+		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
+
+		 We can't align just one of the start or end, since there are
+		 untagged things stored on the stack which we do not align to
+		 HWASAN_TAG_GRANULE_SIZE bytes.  If we only aligned the start
+		 or the end of tagged objects then untagged objects could end
+		 up sharing the first granule of a tagged object or sharing the
+		 last granule of a tagged object respectively.  */
+	      hwasan_orig_offset = align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
 	  HOST_WIDE_INT prev_offset;
@@ -1148,7 +1187,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	    {
 	      if (data->asan_vec.is_empty ())
 		{
-		  alloc_stack_frame_space (0, ASAN_RED_ZONE_SIZE);
+		  align_frame_offset (ASAN_RED_ZONE_SIZE);
 		  prev_offset = frame_offset.to_constant ();
 		}
 	      prev_offset = align_base (prev_offset,
@@ -1216,6 +1255,24 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	    {
 	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
 	      base_align = crtl->max_used_stack_slot_alignment;
+
+	      if (hwasan_sanitize_stack_p ())
+		{
+		  /* Align again since the point of this alignment is to handle
+		     the "end" of the object (i.e. smallest address after the
+		     stack object).  For FRAME_GROWS_DOWNWARD that requires
+		     aligning the stack before allocating, but for a frame that
+		     grows upwards that requires aligning the stack after
+		     allocation.
+
+		     Use `frame_offset` to record the offset value rather than
+		     offset since the frame_offset describes the extent
+		     allocated for this particular variable while `offset`
+		     describes the address that this variable starts at.  */
+		  align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+		  hwasan_record_stack_var (virtual_stack_vars_rtx, base,
+					   hwasan_orig_offset, frame_offset);
+		}
 	    }
 	}
       else
@@ -1236,14 +1293,33 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      loffset = alloc_stack_frame_space
 		(rtx_to_poly_int64 (large_allocsize),
 		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
-	      large_base = get_dynamic_stack_base (loffset, large_align);
+	      large_base = get_dynamic_stack_base (loffset, large_align, base);
 	      large_allocation_done = true;
 	    }
-	  gcc_assert (large_base != NULL);
 
+	  gcc_assert (large_base != NULL);
 	  large_alloc = aligned_upper_bound (large_alloc, alignb);
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* An object with a large alignment requirement means that the
+		 alignment requirement is greater than the required alignment
+		 for tags.  */
+	      if (!large_untagged_base)
+		large_untagged_base
+		  = targetm.memtag.untagged_pointer (large_base, NULL_RTX);
+	      /* Ensure the end of the variable is also aligned correctly.  */
+	      poly_int64 align_again
+		= aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
+	      /* For large allocations we always allocate a chunk of space
+		 (which is addressed by large_untagged_base/large_base) and
+		 then use positive offsets from that.  Hence the farthest
+		 offset is `align_again` and the nearest offset from the base
+		 is `offset`.  */
+	      hwasan_record_stack_var (large_untagged_base, large_base,
+				       offset, align_again);
+	    }
 
 	  base = large_base;
 	  base_align = large_align;
@@ -1254,9 +1330,10 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       for (j = i; j != EOC; j = stack_vars[j].next)
 	{
 	  expand_one_stack_var_at (stack_vars[j].decl,
-				   base, base_align,
-				   offset);
+				   base, base_align, offset);
 	}
+      if (hwasan_sanitize_stack_p ())
+	hwasan_increment_frame_tag ();
     }
 
   gcc_assert (known_eq (large_alloc, large_size));
@@ -1347,10 +1424,37 @@ expand_one_stack_var_1 (tree var)
   /* We handle highly aligned variables in expand_stack_vars.  */
   gcc_assert (byte_align * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT);
 
-  offset = alloc_stack_frame_space (size, byte_align);
+  rtx base;
+  if (hwasan_sanitize_stack_p ())
+    {
+      /* Allocate zero bytes to align the stack.  */
+      poly_int64 hwasan_orig_offset
+	= align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+      offset = alloc_stack_frame_space (size, byte_align);
+      align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+      base = hwasan_frame_base ();
+      /* Use `frame_offset` to automatically account for machines where the
+	 frame grows upwards.
+
+	 `offset` will always point to the "start" of the stack object, which
+	 will be the smallest address, for ! FRAME_GROWS_DOWNWARD this is *not*
+	 the "furthest" offset from the base delimiting the current stack
+	 object.  `frame_offset` will always delimit the extent that the frame.
+	 */
+      hwasan_record_stack_var (virtual_stack_vars_rtx, base,
+			       hwasan_orig_offset, frame_offset);
+    }
+  else
+    {
+      offset = alloc_stack_frame_space (size, byte_align);
+      base = virtual_stack_vars_rtx;
+    }
 
-  expand_one_stack_var_at (var, virtual_stack_vars_rtx,
+  expand_one_stack_var_at (var, base,
 			   crtl->max_used_stack_slot_alignment, offset);
+
+  if (hwasan_sanitize_stack_p ())
+    hwasan_increment_frame_tag ();
 }
 
 /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
@@ -1950,6 +2054,8 @@ init_vars_expansion (void)
   /* Initialize local stack smashing state.  */
   has_protected_decls = false;
   has_short_buffer = false;
+  if (hwasan_sanitize_stack_p ())
+    hwasan_record_frame_init ();
 }
 
 /* Free up stack variable graph data.  */
@@ -2277,10 +2383,26 @@ expand_used_vars (void)
       expand_stack_vars (NULL, &data);
     }
 
+  if (hwasan_sanitize_stack_p ())
+    hwasan_emit_prologue ();
   if (asan_sanitize_allocas_p () && cfun->calls_alloca)
     var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
 					      virtual_stack_vars_rtx,
 					      var_end_seq);
+  else if (hwasan_sanitize_allocas_p () && cfun->calls_alloca)
+    /* When using out-of-line instrumentation we only want to emit one function
+       call for clearing the tags in a region of shadow stack.  When there are
+       alloca calls in this frame we want to emit a call using the
+       virtual_stack_dynamic_rtx, but when not we use the hwasan_frame_extent
+       rtx we created in expand_stack_vars.  */
+    var_end_seq = hwasan_emit_untag_frame (virtual_stack_dynamic_rtx,
+					   virtual_stack_vars_rtx);
+  else if (hwasan_sanitize_stack_p ())
+    /* If no variables were stored on the stack, `hwasan_get_frame_extent`
+       will return NULL_RTX and hence `hwasan_emit_untag_frame` will return
+       NULL (i.e. an empty sequence).  */
+    var_end_seq = hwasan_emit_untag_frame (hwasan_get_frame_extent (),
+					   virtual_stack_vars_rtx);
 
   fini_vars_expansion ();
 
@@ -6641,6 +6763,9 @@ pass_expand::execute (function *fun)
       emit_insn_after (var_ret_seq, after);
     }
 
+  if (hwasan_sanitize_stack_p ())
+    hwasan_maybe_emit_frame_base_init ();
+
   /* Zap the tree EH table.  */
   set_eh_throw_stmt_table (fun, NULL);
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 45f902d370e39e85865bdab4c9bb837e8e20220c..f459ff22fd94a57cf5c694bd83674b8f7bf3260c 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2980,6 +2980,63 @@ True if backend architecture naturally supports ignoring some region of
 pointers.  This feature means that @option{-fsanitize=hwaddress} can work.
 @end deftypefn
 
+@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_TAG_SIZE ()
+Return the size of a tag (in bits) for this platform.
+
+The default returns 8.
+@end deftypefn
+
+@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_GRANULE_SIZE ()
+Return the size in real memory that each byte in shadow memory refers to.
+I.e. if a variable is @var{X} bytes long in memory, then this hook should
+return the value @var{Y} such that the tag in shadow memory spans
+@var{X}/@var{Y} bytes.
+
+Most variables will need to be aligned to this amount since two variables
+that are neighbors in memory and share a tag granule would need to share
+the same tag.
+
+The default returns 16.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_INSERT_RANDOM_TAG (rtx @var{untagged}, rtx @var{target})
+Return an RTX representing the value of @var{untagged} but with a
+(possibly) random tag in it.
+Put that value into @var{target} if it is convenient to do so.
+This function is used to generate a tagged base for the current stack frame.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADD_TAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
+Return an RTX that represents the result of adding @var{addr_offset} to
+the address in pointer @var{base} and @var{tag_offset} to the tag in pointer
+@var{base}.
+The resulting RTX must either be a valid memory address or be able to get
+put into an operand with @code{force_operand}.
+
+Unlike other memtag hooks, this must return an expression and not emit any
+RTL.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_SET_TAG (rtx @var{untagged_base}, rtx @var{tag}, rtx @var{target})
+Return an RTX representing @var{untagged_base} but with the tag @var{tag}.
+Try and store this in @var{target} if convenient.
+@var{untagged_base} is required to have a zero tag when this hook is called.
+The default of this hook is to set the top byte of @var{untagged_base} to
+@var{tag}.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_EXTRACT_TAG (rtx @var{tagged_pointer}, rtx @var{target})
+Return an RTX representing the tag stored in @var{tagged_pointer}.
+Store the result in @var{target} if it is convenient.
+The default represents the top byte of the original pointer.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_UNTAGGED_POINTER (rtx @var{tagged_pointer}, rtx @var{target})
+Return an RTX representing @var{tagged_pointer} with its tag set to zero.
+Store the result in @var{target} if convenient.
+The default clears the top byte of the original pointer.
+@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 2a298f05da40ef37373e8d8138c85700de9c3532..b675d01e346d52e4df241a75902b21984924b0f1 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2377,6 +2377,20 @@ in the reload pass.
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
 
+@hook TARGET_MEMTAG_TAG_SIZE
+
+@hook TARGET_MEMTAG_GRANULE_SIZE
+
+@hook TARGET_MEMTAG_INSERT_RANDOM_TAG
+
+@hook TARGET_MEMTAG_ADD_TAG
+
+@hook TARGET_MEMTAG_SET_TAG
+
+@hook TARGET_MEMTAG_EXTRACT_TAG
+
+@hook TARGET_MEMTAG_UNTAGGED_POINTER
+
 @node Stack and Calling
 @section Stack Layout and Calling Conventions
 @cindex calling conventions
diff --git a/gcc/explow.h b/gcc/explow.h
index 0df8c62b82a8bf1d8d6baf0b6fb658e66361a407..581831cb19fdf9e8fd969bb30139e1358279a34d 100644
--- a/gcc/explow.h
+++ b/gcc/explow.h
@@ -106,7 +106,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
 extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
 
 /* Returns the address of the dynamic stack space without allocating it.  */
-extern rtx get_dynamic_stack_base (poly_int64, unsigned);
+extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
 
 /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
 extern rtx align_dynamic_address (rtx, unsigned);
diff --git a/gcc/explow.c b/gcc/explow.c
index 0fbc6d25b816457a3d13ed45d16b5dd0513cfacd..41c3f6ace49c0e55c080e10b917842b1b21d49eb 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1583,10 +1583,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
    OFFSET is the offset of the area into the virtual stack vars area.
 
    REQUIRED_ALIGN is the alignment (in bits) required for the region
-   of memory.  */
+   of memory.
+
+   BASE is the rtx of the base of this virtual stack vars area.
+   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+   on the stack.  */
 
 rtx
-get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
 {
   rtx target;
 
@@ -1594,7 +1598,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
     crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
 
   target = gen_reg_rtx (Pmode);
-  emit_move_insn (target, virtual_stack_vars_rtx);
+  emit_move_insn (target, base);
   target = expand_binop (Pmode, add_optab, target,
 			 gen_int_mode (offset, Pmode),
 			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index a32715ddb92e69b7ca7be28a8f17a369b891bd76..4f854fb994229fd4ed91d3b5cff7c7acff9a55bc 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
+/* Hardware Address Sanitizer.  */
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
+		      BT_FN_VOID_PTR_UINT8_PTRMODE, ATTR_NOTHROW_LIST)
+
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/target.def b/gcc/target.def
index 0945aa71ebbc5776ae036778f8588cf5bd0436f2..489cd6f215ef3ad9031749e7428e3664eb0abc09 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6870,6 +6870,71 @@ DEFHOOK
 pointers.  This feature means that @option{-fsanitize=hwaddress} can work.",
  bool, (), default_memtag_can_tag_addresses)
 
+DEFHOOK
+(tag_size,
+ "Return the size of a tag (in bits) for this platform.\n\
+\n\
+The default returns 8.",
+  uint8_t, (), default_memtag_tag_size)
+
+DEFHOOK
+(granule_size,
+ "Return the size in real memory that each byte in shadow memory refers to.\n\
+I.e. if a variable is @var{X} bytes long in memory, then this hook should\n\
+return the value @var{Y} such that the tag in shadow memory spans\n\
+@var{X}/@var{Y} bytes.\n\
+\n\
+Most variables will need to be aligned to this amount since two variables\n\
+that are neighbors in memory and share a tag granule would need to share\n\
+the same tag.\n\
+\n\
+The default returns 16.",
+  uint8_t, (), default_memtag_granule_size)
+
+DEFHOOK
+(insert_random_tag,
+ "Return an RTX representing the value of @var{untagged} but with a\n\
+(possibly) random tag in it.\n\
+Put that value into @var{target} if it is convenient to do so.\n\
+This function is used to generate a tagged base for the current stack frame.",
+  rtx, (rtx untagged, rtx target), default_memtag_insert_random_tag)
+
+DEFHOOK
+(add_tag,
+ "Return an RTX that represents the result of adding @var{addr_offset} to\n\
+the address in pointer @var{base} and @var{tag_offset} to the tag in pointer\n\
+@var{base}.\n\
+The resulting RTX must either be a valid memory address or be able to get\n\
+put into an operand with @code{force_operand}.\n\
+\n\
+Unlike other memtag hooks, this must return an expression and not emit any\n\
+RTL.",
+  rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
+  default_memtag_add_tag)
+
+DEFHOOK
+(set_tag,
+ "Return an RTX representing @var{untagged_base} but with the tag @var{tag}.\n\
+Try and store this in @var{target} if convenient.\n\
+@var{untagged_base} is required to have a zero tag when this hook is called.\n\
+The default of this hook is to set the top byte of @var{untagged_base} to\n\
+@var{tag}.",
+  rtx, (rtx untagged_base, rtx tag, rtx target), default_memtag_set_tag)
+
+DEFHOOK
+(extract_tag,
+ "Return an RTX representing the tag stored in @var{tagged_pointer}.\n\
+Store the result in @var{target} if it is convenient.\n\
+The default represents the top byte of the original pointer.",
+  rtx, (rtx tagged_pointer, rtx target), default_memtag_extract_tag)
+
+DEFHOOK
+(untagged_pointer,
+ "Return an RTX representing @var{tagged_pointer} with its tag set to zero.\n\
+Store the result in @var{target} if convenient.\n\
+The default clears the top byte of the original pointer.",
+  rtx, (rtx tagged_pointer, rtx target), default_memtag_untagged_pointer)
+
 HOOK_VECTOR_END (memtag)
 #undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 0065c686978d7120978430013c73b1055aaf95c7..68e8688a32f18481ee61f06879aacff20163105b 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -287,4 +287,12 @@ extern bool speculation_safe_value_not_needed (bool);
 extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 
 extern bool default_memtag_can_tag_addresses ();
+extern uint8_t default_memtag_tag_size ();
+extern uint8_t default_memtag_granule_size ();
+extern rtx default_memtag_insert_random_tag (rtx, rtx);
+extern rtx default_memtag_add_tag (rtx, poly_int64, uint8_t);
+extern rtx default_memtag_set_tag (rtx, rtx, rtx);
+extern rtx default_memtag_extract_tag (rtx, rtx);
+extern rtx default_memtag_untagged_pointer (rtx, rtx);
+
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 46cb536041d396c32fd08042581d6d5cd5ad0395..e66b1d0074b1921b7613f8e5444c7322c0479506 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -73,6 +73,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "flags.h"
 #include "explow.h"
+#include "expmed.h"
 #include "calls.h"
 #include "expr.h"
 #include "output.h"
@@ -86,6 +87,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "sbitmap.h"
 #include "function-abi.h"
+#include "attribs.h"
+#include "asan.h"
+#include "emit-rtl.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -2415,10 +2419,116 @@ default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
   return result;
 }
 
+/* How many bits to shift in order to access the tag bits.
+   The default is to store the tag in the top 8 bits of a 64 bit pointer, hence
+   shifting 56 bits will leave just the tag.  */
+#define HWASAN_SHIFT (GET_MODE_PRECISION (Pmode) - 8)
+#define HWASAN_SHIFT_RTX GEN_INT (HWASAN_SHIFT)
+
 bool
 default_memtag_can_tag_addresses ()
 {
   return false;
 }
 
+uint8_t
+default_memtag_tag_size ()
+{
+  return 8;
+}
+
+uint8_t
+default_memtag_granule_size ()
+{
+  return 16;
+}
+
+/* The default implementation of TARGET_MEMTAG_INSERT_RANDOM_TAG.  */
+rtx
+default_memtag_insert_random_tag (rtx untagged, rtx target)
+{
+  gcc_assert (param_hwasan_instrument_stack);
+  if (param_hwasan_random_frame_tag)
+    {
+      rtx fn = init_one_libfunc ("__hwasan_generate_tag");
+      rtx new_tag = emit_library_call_value (fn, NULL_RTX, LCT_NORMAL, QImode);
+      return targetm.memtag.set_tag (untagged, new_tag, target);
+    }
+  else
+    {
+      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
+	 In the future we may add the option emit random tags with inline
+	 instrumentation instead of function calls.  This would be the same
+	 between the kernel and userland.  */
+      return untagged;
+    }
+}
+
+/* The default implementation of TARGET_MEMTAG_ADD_TAG.  */
+rtx
+default_memtag_add_tag (rtx base, poly_int64 offset, uint8_t tag_offset)
+{
+  /* Need to look into what the most efficient code sequence is.
+     This is a code sequence that would be emitted *many* times, so we
+     want it as small as possible.
+
+     There are two places where tag overflow is a question:
+       - Tagging the shadow stack.
+	  (both tagging and untagging).
+       - Tagging addressable pointers.
+
+     We need to ensure both behaviors are the same (i.e. that the tag that
+     ends up in a pointer after "overflowing" the tag bits with a tag addition
+     is the same that ends up in the shadow space).
+
+     The aim is that the behavior of tag addition should follow modulo
+     wrapping in both instances.
+
+     The libhwasan code doesn't have any path that increments a pointer's tag,
+     which means it has no opinion on what happens when a tag increment
+     overflows (and hence we can choose our own behavior).  */
+
+  offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
+  return plus_constant (Pmode, base, offset);
+}
+
+/* The default implementation of TARGET_MEMTAG_SET_TAG.  */
+rtx
+default_memtag_set_tag (rtx untagged, rtx tag, rtx target)
+{
+  gcc_assert (GET_MODE (untagged) == Pmode);
+  gcc_assert (GET_MODE (tag) == QImode);
+  tag = expand_simple_binop (Pmode, ASHIFT, tag, HWASAN_SHIFT_RTX, tag,
+			     /* unsignedp = */1, OPTAB_WIDEN);
+  rtx ret = expand_simple_binop (Pmode, IOR, untagged, tag, target,
+				 /* unsignedp = */1, OPTAB_DIRECT);
+  gcc_assert (ret);
+  return ret;
+}
+
+/* The default implementation of TARGET_MEMTAG_EXTRACT_TAG.  */
+rtx
+default_memtag_extract_tag (rtx tagged_pointer, rtx target)
+{
+  rtx tag = expand_simple_binop (Pmode, LSHIFTRT, tagged_pointer,
+				 HWASAN_SHIFT_RTX, target,
+				 /* unsignedp = */0,
+				 OPTAB_DIRECT);
+  rtx ret = gen_lowpart (QImode, tag);
+  gcc_assert (ret);
+  return ret;
+}
+
+/* The default implementation of TARGET_MEMTAG_UNTAGGED_POINTER.  */
+rtx
+default_memtag_untagged_pointer (rtx tagged_pointer, rtx target)
+{
+  rtx tag_mask = gen_int_mode ((HOST_WIDE_INT_1U << HWASAN_SHIFT) - 1, Pmode);
+  rtx untagged_base = expand_simple_binop (Pmode, AND, tagged_pointer,
+					   tag_mask, target, true,
+					   OPTAB_DIRECT);
+  gcc_assert (untagged_base);
+  return untagged_base;
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 2a3e7c064a5fbb6913481104975ca85615e49f8e..9938b6afbd4fa22898dbc3c29b92061a71810b08 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -512,6 +512,9 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      if (flag_sanitize & SANITIZE_HWADDRESS)
+	hwasan_finish_file ();
+
       omp_finish_file ();
 
       output_shared_constant_pool ();


[-- Attachment #2: hwasan5-v6.patch.gz --]
[-- Type: application/gzip, Size: 14780 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Update [PATCH 6/X] libsanitizer:  Add hwasan pass and associated gimple changes
  2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
                         ` (8 preceding siblings ...)
  2020-11-16 15:37       ` [PATCH 7/X] libsanitizer: Add tests Matthew Malcomson
@ 2020-11-19 13:01       ` Matthew Malcomson
  2020-11-19 16:28         ` Richard Sandiford
  9 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-19 13:01 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: Richard Earnshaw, Kyrylo Tkachov, gcc-patches, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 54772 bytes --]


Update to match the change in initialisation for patch 5/X.

MM

---

There are four main features to this change:

1) Check pointer tags match address tags.

When sanitizing for hwasan we now put HWASAN_CHECK internal functions before
memory accesses in the `asan` pass.  This checks that a tag in the pointer
being used match the tag stored in shadow memory for the memory region being
used.

These internal functions are expanded into actual checks in the sanopt
pass that happens just before expansion into RTL.

We use the same mechanism that currently inserts ASAN_CHECK internal
functions to insert the new HWASAN_CHECK functions.

2) Instrument known builtin function calls.

Handle all builtin functions that we know use memory accesses.
This commit uses the machinery added for ASAN to identify builtin
functions that access memory.

The main differences between the approaches for HWASAN and ASAN are:
 - libhwasan intercepts much less builtin functions.
 - Alloca needs to be transformed differently (instead of adding
   redzones it needs to tag shadow memory and return a tagged pointer).
 - stack_restore needs to untag the shadow stack between the current
   position and where it's going.
 - `noreturn` functions can not be handled by simply unpoisoning the
   entire shadow stack -- there is no "always valid" tag.
   (exceptions and things such as longjmp need to be handled in a
   different way, usually in the runtime).

For hardware implemented checking (such as AArch64's memory tagging
extension) alloca and stack_restore will need to be handled by hooks in
the backend rather than transformation at the gimple level.  This will
allow architecture specific handling of such stack modifications.

3) Introduce HWASAN block-scope poisoning

Here we use exactly the same mechanism as ASAN_MARK to poison/unpoison
variables on entry/exit of a block.

In order to simply use the exact same machinery we're using the same
internal functions until the SANOPT pass.  This means that all handling
of ASAN_MARK is the same.
This has the negative that the naming may be a little confusing, but a
positive that handling of the internal function doesn't have to be
duplicated for a function that behaves exactly the same but has a
different name.

gcc/ChangeLog:

	* asan.c (asan_instrument_reads): New.
	(asan_instrument_writes): New.
	(asan_memintrin): New.
	(handle_builtin_stack_restore): Account for HWASAN.
	(hwasan_emit_round_up): New.
	(handle_builtin_alloca): Account for HWASAN.
	(get_mem_refs_of_builtin_call): Special case strlen for HWASAN.
	(hwasan_instrument_reads): New.
	(hwasan_instrument_writes): New.
	(hwasan_memintrin): New.
	(report_error_func): Assert not HWASAN.
	(build_check_stmt): Make HWASAN_CHECK instead of ASAN_CHECK.
	(instrument_derefs): HWASAN does not tag globals.
	(instrument_builtin_call): Use new helper functions.
	(maybe_instrument_call): Don't instrument `noreturn` functions.
	(initialize_sanitizer_builtins): Add new type.
	(asan_expand_mark_ifn): Account for HWASAN.
	(asan_expand_check_ifn): Assert never called by HWASAN.
	(asan_expand_poison_ifn): Account for HWASAN.
	(asan_instrument): Branch based on whether using HWASAN or ASAN.
	(pass_asan::gate): Return true if sanitizing HWASAN.
	(pass_asan_O0::gate): Return true if sanitizing HWASAN.
	(hwasan_check_func): New.
	(hwasan_expand_check_ifn): New.
	(hwasan_expand_mark_ifn): New.
	(gate_hwasan): New.
	* asan.h (hwasan_expand_check_ifn): New decl.
	(hwasan_expand_mark_ifn): New decl.
	(gate_hwasan): New decl.
	(asan_intercepted_p): Always false for hwasan.
	(asan_sanitize_use_after_scope): Account for HWASAN.
	* builtin-types.def (BT_FN_PTR_CONST_PTR_UINT8): New.
	* gimple-pretty-print.c (dump_gimple_call_args): Account for
	HWASAN.
	* gimplify.c (asan_poison_variable): Account for HWASAN.
	(gimplify_function_tree): Remove requirement of
	SANITIZE_ADDRESS, requiring asan or hwasan is accounted for in
	`asan_sanitize_use_after_scope`.
	* internal-fn.c (expand_HWASAN_CHECK): New.
	(expand_HWASAN_ALLOCA_UNPOISON): New.
	(expand_HWASAN_CHOOSE_TAG): New.
	(expand_HWASAN_MARK): New.
	(expand_HWASAN_SET_TAG): New.
	* internal-fn.def (HWASAN_ALLOCA_UNPOISON): New.
	(HWASAN_CHOOSE_TAG): New.
	(HWASAN_CHECK): New.
	(HWASAN_MARK): New.
	(HWASAN_SET_TAG): New.
	* sanitizer.def (BUILT_IN_HWASAN_LOAD1): New.
	(BUILT_IN_HWASAN_LOAD2): New.
	(BUILT_IN_HWASAN_LOAD4): New.
	(BUILT_IN_HWASAN_LOAD8): New.
	(BUILT_IN_HWASAN_LOAD16): New.
	(BUILT_IN_HWASAN_LOADN): New.
	(BUILT_IN_HWASAN_STORE1): New.
	(BUILT_IN_HWASAN_STORE2): New.
	(BUILT_IN_HWASAN_STORE4): New.
	(BUILT_IN_HWASAN_STORE8): New.
	(BUILT_IN_HWASAN_STORE16): New.
	(BUILT_IN_HWASAN_STOREN): New.
	(BUILT_IN_HWASAN_LOAD1_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD2_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD4_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD8_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD16_NOABORT): New.
	(BUILT_IN_HWASAN_LOADN_NOABORT): New.
	(BUILT_IN_HWASAN_STORE1_NOABORT): New.
	(BUILT_IN_HWASAN_STORE2_NOABORT): New.
	(BUILT_IN_HWASAN_STORE4_NOABORT): New.
	(BUILT_IN_HWASAN_STORE8_NOABORT): New.
	(BUILT_IN_HWASAN_STORE16_NOABORT): New.
	(BUILT_IN_HWASAN_STOREN_NOABORT): New.
	(BUILT_IN_HWASAN_TAG_MISMATCH4): New.
	(BUILT_IN_HWASAN_HANDLE_LONGJMP): New.
	(BUILT_IN_HWASAN_TAG_PTR): New.
	* sanopt.c (sanopt_optimize_walker): Act during hwasan.
	(pass_sanopt::execute): Act during hwasan.
	* toplev.c (compile_file): Use `gate_hwasan` function.



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


diff --git a/gcc/asan.h b/gcc/asan.h
index 8d5271e6b575d74da277420798557f3274e966ce..4b873d6696026839025926581ccc9aceca225196 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -49,6 +49,9 @@ extern void hwasan_finish_file (void);
 extern bool hwasan_sanitize_p (void);
 extern bool hwasan_sanitize_stack_p (void);
 extern bool hwasan_sanitize_allocas_p (void);
+extern bool hwasan_expand_check_ifn (gimple_stmt_iterator *, bool);
+extern bool hwasan_expand_mark_ifn (gimple_stmt_iterator *);
+extern bool gate_hwasan (void);
 
 extern gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
@@ -181,6 +184,9 @@ extern hash_set<tree> *asan_handled_variables;
 static inline bool
 asan_intercepted_p (enum built_in_function fcode)
 {
+  if (hwasan_sanitize_p ())
+    return false;
+
   return fcode == BUILT_IN_INDEX
 	 || fcode == BUILT_IN_MEMCHR
 	 || fcode == BUILT_IN_MEMCMP
@@ -209,7 +215,8 @@ asan_intercepted_p (enum built_in_function fcode)
 static inline bool
 asan_sanitize_use_after_scope (void)
 {
-  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
+  return (flag_sanitize_address_use_after_scope
+	  && (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ()));
 }
 
 /* Return true if DECL should be guarded on the stack.  */
diff --git a/gcc/asan.c b/gcc/asan.c
index b8e3e9f35535549967f72b0a60eb8b9b17ef5baf..39db40fd0fd558abaf72612eec2d36120ebb7362 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -370,6 +370,25 @@ asan_sanitize_allocas_p (void)
   return (asan_sanitize_stack_p () && param_asan_protect_allocas);
 }
 
+bool
+asan_instrument_reads (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_instrument_reads);
+}
+
+bool
+asan_instrument_writes (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_instrument_writes);
+}
+
+bool
+asan_memintrin (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_memintrin);
+}
+
+
 /* Checks whether section SEC should be sanitized.  */
 
 static bool
@@ -630,23 +649,73 @@ get_last_alloca_addr ()
    To overcome the issue we use following trick: pass new_sp as a second
    parameter to __asan_allocas_unpoison and rewrite it during expansion with
    new_sp + (virtual_dynamic_stack_rtx - sp) later in
-   expand_asan_emit_allocas_unpoison function.  */
+   expand_asan_emit_allocas_unpoison function.
+
+   HWASAN needs to do very similar, the eventual pseudocode should be:
+      __hwasan_tag_memory (virtual_stack_dynamic_rtx,
+			   0,
+			   new_sp - sp);
+      __builtin_stack_restore (new_sp)
+
+   Need to use the same trick to handle STACK_DYNAMIC_OFFSET as described
+   above.  */
 
 static void
 handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p ()))
     return;
 
-  tree last_alloca = get_last_alloca_addr ();
   tree restored_stack = gimple_call_arg (call, 0);
-  tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
-  gimple *g = gimple_build_call (fn, 2, last_alloca, restored_stack);
-  gsi_insert_before (iter, g, GSI_SAME_STMT);
-  g = gimple_build_assign (last_alloca, restored_stack);
+
+  gimple *g;
+
+  if (hwasan_sanitize_allocas_p ())
+    {
+      enum internal_fn fn = IFN_HWASAN_ALLOCA_UNPOISON;
+      /* There is only one piece of information `expand_HWASAN_ALLOCA_UNPOISON`
+	 needs to work.  This is the length of the area that we're
+	 deallocating.  Since the stack pointer is known at expand time, the
+	 position of the new stack pointer after deallocation is enough
+	 information to calculate this length.  */
+      g = gimple_build_call_internal (fn, 1, restored_stack);
+    }
+  else
+    {
+      tree last_alloca = get_last_alloca_addr ();
+      tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
+      g = gimple_build_call (fn, 2, last_alloca, restored_stack);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      g = gimple_build_assign (last_alloca, restored_stack);
+    }
+
   gsi_insert_before (iter, g, GSI_SAME_STMT);
 }
 
+/* Emit gimple statements into &stmts that take the size given in `len` and
+   generate a size that is guaranteed to be rounded upwards to `align`.
+
+   This is a helper function for both handle_builtin_alloca and
+   asan_expand_mark_ifn when using HWASAN.
+
+   Return the tree node representing this size, it is of TREE_TYPE
+   size_type_node.  */
+
+static tree
+hwasan_emit_round_up (gimple_seq *seq, location_t loc, tree old_size,
+		      uint8_t align)
+{
+  uint8_t tg_mask = align - 1;
+  /* tree new_size = (old_size + tg_mask) & ~tg_mask;  */
+  tree tree_mask = build_int_cst (size_type_node, tg_mask);
+  tree oversize = gimple_build (seq, loc, PLUS_EXPR, size_type_node, old_size,
+				tree_mask);
+
+  tree mask = build_int_cst (size_type_node, -align);
+  return gimple_build (seq, loc, BIT_AND_EXPR, size_type_node, oversize, mask);
+}
+
 /* Deploy and poison redzones around __builtin_alloca call.  To do this, we
    should replace this call with another one with changed parameters and
    replace all its uses with new address, so
@@ -673,14 +742,12 @@ handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 static void
 handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p ()))
     return;
 
   gassign *g;
   gcall *gg;
-  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
-
-  tree last_alloca = get_last_alloca_addr ();
   tree callee = gimple_call_fndecl (call);
   tree old_size = gimple_call_arg (call, 0);
   tree ptr_type = gimple_call_lhs (call) ? TREE_TYPE (gimple_call_lhs (call))
@@ -690,6 +757,71 @@ handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
     = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
       ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
 
+  if (hwasan_sanitize_allocas_p ())
+    {
+      gimple_seq stmts = NULL;
+      location_t loc = gimple_location (gsi_stmt (*iter));
+      /*
+	 HWASAN needs a different expansion.
+
+	 addr = __builtin_alloca (size, align);
+
+	 should be replaced by
+
+	 new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment;
+	 untagged_addr = __builtin_alloca (new_size, align);
+	 tag = __hwasan_choose_alloca_tag ();
+	 addr = ifn_HWASAN_SET_TAG (untagged_addr, tag);
+	 __hwasan_tag_memory (untagged_addr, tag, new_size);
+	*/
+      /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start on
+	 a tag granule.  */
+      align = align > HWASAN_TAG_GRANULE_SIZE ? align : HWASAN_TAG_GRANULE_SIZE;
+
+      tree old_size = gimple_call_arg (call, 0);
+      tree new_size = hwasan_emit_round_up (&stmts, loc, old_size,
+					    HWASAN_TAG_GRANULE_SIZE);
+
+      /* Make the alloca call */
+      tree untagged_addr
+	= gimple_build (&stmts, loc,
+			as_combined_fn (BUILT_IN_ALLOCA_WITH_ALIGN), ptr_type,
+			new_size, build_int_cst (size_type_node, align));
+
+      /* Choose the tag.
+	 Here we use an internal function so we can choose the tag at expand
+	 time.  We need the decision to be made after stack variables have been
+	 assigned their tag (i.e. once the hwasan_frame_tag_offset variable has
+	 been set to one after the last stack variables tag).  */
+      gcall *stmt = gimple_build_call_internal (IFN_HWASAN_CHOOSE_TAG, 0);
+      tree tag = make_ssa_name (unsigned_char_type_node);
+      gimple_call_set_lhs (stmt, tag);
+      gimple_set_location (stmt, loc);
+      gimple_seq_add_stmt_without_update (&stmts, stmt);
+
+      /* Add tag to pointer.  */
+      tree addr
+	= gimple_build (&stmts, loc, as_combined_fn (IFN_HWASAN_SET_TAG),
+			ptr_type, untagged_addr, tag);
+
+      /* Tag shadow memory.
+	 NOTE: require using `untagged_addr` here for libhwasan API.  */
+      gimple_build (&stmts, loc, as_combined_fn (BUILT_IN_HWASAN_TAG_MEM),
+		    void_type_node, untagged_addr, tag, new_size);
+
+      /* Insert the built up code sequence into the original instruction stream
+	 the iterator points to.  */
+      gsi_insert_seq_before (iter, stmts, GSI_SAME_STMT);
+
+      /* Finally, replace old alloca ptr with NEW_ALLOCA.  */
+      replace_call_with_value (iter, addr);
+      return;
+    }
+
+  tree last_alloca = get_last_alloca_addr ();
+  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
+
+
   /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
      bytes of allocated space.  Otherwise, align alloca to ASAN_RED_ZONE_SIZE
      manually.  */
@@ -842,6 +974,31 @@ get_mem_refs_of_builtin_call (gcall *call,
       break;
 
     case BUILT_IN_STRLEN:
+      /* Special case strlen here since its length is taken from its return
+	 value.
+
+	 The approach taken by the sanitizers is to check a memory access
+	 before it's taken.  For ASAN strlen is intercepted by libasan, so no
+	 check is inserted by the compiler.
+
+	 This function still returns `true` and provides a length to the rest
+	 of the ASAN pass in order to record what areas have been checked,
+	 avoiding superfluous checks later on.
+
+	 HWASAN does not intercept any of these internal functions.
+	 This means that checks for memory accesses must be inserted by the
+	 compiler.
+	 strlen is a special case, because we can tell the length from the
+	 return of the function, but that is not known until after the function
+	 has returned.
+
+	 Hence we can't check the memory access before it happens.
+	 We could check the memory access after it has already happened, but
+	 for now we choose to just ignore `strlen` calls.
+	 This decision was simply made because that means the special case is
+	 limited to this one case of this one function.  */
+      if (hwasan_sanitize_p ())
+	return false;
       source0 = gimple_call_arg (call, 0);
       len = gimple_call_lhs (call);
       break;
@@ -1411,6 +1568,156 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+
+/* HWAddressSanitizer (hwasan) is a probabilistic method for detecting
+   out-of-bounds and use-after-free bugs.
+   Read more:
+   http://code.google.com/p/address-sanitizer/
+
+   Similar to AddressSanitizer (asan) it consists of two parts: the
+   instrumentation module in this file, and a run-time library.
+
+   The instrumentation module adds a run-time check before every memory insn in
+   the same manner as asan (see the block comment for AddressSanitizer above).
+   Currently, hwasan only adds out-of-line instrumentation, where each check is
+   implemented as a function call to the run-time library.  Hence a check for a
+   load of N bytes from address X would be implemented with a function call to
+   __hwasan_loadN(X), and checking a store of N bytes from address X would be
+   implemented with a function call to __hwasan_storeN(X).
+
+   The main difference between hwasan and asan is in the information stored to
+   help this checking.  Both sanitizers use a shadow memory area which stores
+   data recording the state of main memory at a corresponding address.
+
+   For hwasan, each 16 byte granule in main memory has a corresponding 1 byte
+   in shadow memory.  This shadow address can be calculated with equation:
+     (addr >> log_2(HWASAN_TAG_GRANULE_SIZE))
+	  + __hwasan_shadow_memory_dynamic_address;
+   The conversion between real and shadow memory for asan is given in the block
+   comment at the top of this file.
+   The description of how this shadow memory is laid out for asan is in the
+   block comment at the top of this file, here we describe how this shadow
+   memory is used for hwasan.
+
+   For hwasan, each variable is assigned a byte-sized 'tag'.  The extent of
+   the shadow memory for that variable is filled with the assigned tag, and
+   every pointer referencing that variable has its top byte set to the same
+   tag.  The run-time library redefines malloc so that every allocation returns
+   a tagged pointer and tags the corresponding shadow memory with the same tag.
+
+   On each pointer dereference the tag found in the pointer is compared to the
+   tag found in the shadow memory corresponding to the accessed memory address.
+   If these tags are found to differ then this memory access is judged to be
+   invalid and a report is generated.
+
+   This method of bug detection is not perfect -- it can not catch every bad
+   access -- but catches them probabilistically instead.  There is always the
+   possibility that an invalid memory access will happen to access memory
+   tagged with the same tag as the pointer that this access used.
+   The chances of this are approx. 0.4% for any two uncorrelated objects.
+
+   Random tag generation can mitigate this problem by decreasing the
+   probability that an invalid access will be missed in the same manner over
+   multiple runs.  i.e. if two objects are tagged the same in one run of the
+   binary they are unlikely to be tagged the same in the next run.
+   Both heap and stack allocated objects have random tags by default.
+
+   [16 byte granule implications]
+    Since the shadow memory only has a resolution on real memory of 16 bytes,
+    invalid accesses that are within the same 16 byte granule as a valid
+    address will not be caught.
+
+    There is a "short-granule" feature in the runtime library which does catch
+    such accesses, but this feature is not implemented for stack objects (since
+    stack objects are allocated and tagged by compiler instrumentation, and
+    this feature has not yet been implemented in GCC instrumentation).
+
+    Another outcome of this 16 byte resolution is that each tagged object must
+    be 16 byte aligned.  If two objects were to share any 16 byte granule in
+    memory, then they both would have to be given the same tag, and invalid
+    accesses to one using a pointer to the other would be undetectable.
+
+   [Compiler instrumentation]
+    Compiler instrumentation ensures that two adjacent buffers on the stack are
+    given different tags, this means an access to one buffer using a pointer
+    generated from the other (e.g. through buffer overrun) will have mismatched
+    tags and be caught by hwasan.
+
+    We don't randomly tag every object on the stack, since that would require
+    keeping many registers to record each tag.  Instead we randomly generate a
+    tag for each function frame, and each new stack object uses a tag offset
+    from that frame tag.
+    i.e. each object is tagged as RFT + offset, where RFT is the "random frame
+    tag" generated for this frame.
+    This means that randomisation does not peturb the difference between tags
+    on tagged stack objects within a frame, but this is mitigated by the fact
+    that objects with the same tag within a frame are very far apart
+    (approx. 2^HWASAN_TAG_SIZE objects apart).
+
+    As a demonstration, using the same example program as in the asan block
+    comment above:
+
+     int
+     foo ()
+     {
+       char a[23] = {0};
+       int b[2] = {0};
+
+       a[5] = 1;
+       b[1] = 2;
+
+       return a[5] + b[1];
+     }
+
+    On AArch64 the stack will be ordered as follows for the above function:
+
+    Slot 1/ [24 bytes for variable 'a']
+    Slot 2/ [8 bytes padding for alignment]
+    Slot 3/ [8 bytes for variable 'b']
+    Slot 4/ [8 bytes padding for alignment]
+
+    (The padding is there to ensure 16 byte alignment as described in the 16
+     byte granule implications).
+
+    While the shadow memory will be ordered as follows:
+
+    - 2 bytes (representing 32 bytes in real memory) tagged with RFT + 1.
+    - 1 byte (representing 16 bytes in real memory) tagged with RFT + 2.
+
+    And any pointer to "a" will have the tag RFT + 1, and any pointer to "b"
+    will have the tag RFT + 2.
+
+   [Top Byte Ignore requirements]
+    Hwasan requires the ability to store an 8 bit tag in every pointer.  There
+    is no instrumentation done to remove this tag from pointers before
+    dereferencing, which means the hardware must ignore this tag during memory
+    accesses.
+
+    Architectures where this feature is available should indicate this using
+    the TARGET_MEMTAG_CAN_TAG_ADDRESSES hook.
+
+   [Stack requires cleanup on unwinding]
+    During normal operation of a hwasan sanitized program more space in the
+    shadow memory becomes tagged as the stack grows.  As the stack shrinks this
+    shadow memory space must become untagged.  If it is not untagged then when
+    the stack grows again (during other function calls later on in the program)
+    objects on the stack that are usually not tagged (e.g. parameters passed on
+    the stack) can be placed in memory whose shadow space is tagged with
+    something else, and accesses can cause false positive reports.
+
+    Hence we place untagging code on every epilogue of functions which tag some
+    stack objects.
+
+    Moreover, the run-time library intercepts longjmp & setjmp to untag when
+    the stack is unwound this way.
+
+    C++ exceptions are not yet handled, which means this sanitizer can not
+    handle C++ code that throws exceptions -- it will give false positives
+    after an exception has been thrown.  The implementation that the hwasan
+    library has for handling these relies on the frame pointer being after any
+    local variables.  This is not generally the case for GCC.  */
+
+
 /* Returns whether we are tagging pointers and checking those tags on memory
    access.  */
 bool
@@ -1433,6 +1740,27 @@ hwasan_sanitize_allocas_p (void)
   return (hwasan_sanitize_stack_p () && param_hwasan_protect_allocas);
 }
 
+/* Should we instrument reads?  */
+bool
+hwasan_instrument_reads (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_reads);
+}
+
+/* Should we instrument writes?  */
+bool
+hwasan_instrument_writes (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_writes);
+}
+
+/* Should we instrument builtin calls?  */
+bool
+hwasan_memintrin (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_mem_intrinsics);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1928,6 +2256,8 @@ static tree
 report_error_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
 		   int *nargs)
 {
+  gcc_assert (!hwasan_sanitize_p ());
+
   static enum built_in_function report[2][2][6]
     = { { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
 	    BUILT_IN_ASAN_REPORT_LOAD4, BUILT_IN_ASAN_REPORT_LOAD8,
@@ -2220,6 +2550,7 @@ build_check_stmt (location_t loc, tree base, tree len,
   gimple *g;
 
   gcc_assert (!(size_in_bytes > 0 && !is_non_zero_len));
+  gcc_assert (size_in_bytes == -1 || size_in_bytes >= 1);
 
   gsi = *iter;
 
@@ -2264,7 +2595,11 @@ build_check_stmt (location_t loc, tree base, tree len,
   if (is_scalar_access)
     flags |= ASAN_CHECK_SCALAR_ACCESS;
 
-  g = gimple_build_call_internal (IFN_ASAN_CHECK, 4,
+  enum internal_fn fn = hwasan_sanitize_p ()
+    ? IFN_HWASAN_CHECK
+    : IFN_ASAN_CHECK;
+
+  g = gimple_build_call_internal (fn, 4,
 				  build_int_cst (integer_type_node, flags),
 				  base, len,
 				  build_int_cst (integer_type_node,
@@ -2288,9 +2623,9 @@ static void
 instrument_derefs (gimple_stmt_iterator *iter, tree t,
 		   location_t location, bool is_store)
 {
-  if (is_store && !param_asan_instrument_writes)
+  if (is_store && !(asan_instrument_writes () || hwasan_instrument_writes ()))
     return;
-  if (!is_store && !param_asan_instrument_reads)
+  if (!is_store && !(asan_instrument_reads () || hwasan_instrument_reads ()))
     return;
 
   tree type, base;
@@ -2351,7 +2686,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
     {
       if (DECL_THREAD_LOCAL_P (inner))
 	return;
-      if (!param_asan_globals && is_global_var (inner))
+      if ((hwasan_sanitize_p () || !param_asan_globals)
+	  && is_global_var (inner))
         return;
       if (!TREE_STATIC (inner))
 	{
@@ -2444,7 +2780,7 @@ instrument_mem_region_access (tree base, tree len,
 static bool
 instrument_builtin_call (gimple_stmt_iterator *iter)
 {
-  if (!param_asan_memintrin)
+  if (!(asan_memintrin () || hwasan_memintrin ()))
     return false;
 
   bool iter_advanced_p = false;
@@ -2580,10 +2916,27 @@ maybe_instrument_call (gimple_stmt_iterator *iter)
 	      break;
 	    }
 	}
-      tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
-      gimple *g = gimple_build_call (decl, 0);
-      gimple_set_location (g, gimple_location (stmt));
-      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      /* If a function does not return, then we must handle clearing up the
+	 shadow stack accordingly.  For ASAN we can simply set the entire stack
+	 to "valid" for accesses by setting the shadow space to 0 and all
+	 accesses will pass checks.  That means that some bad accesses may be
+	 missed, but we will not report any false positives.
+
+	 This is not possible for HWASAN. Since there is no "always valid" tag
+	 we can not set any space to "always valid".  If we were to clear the
+	 entire shadow stack then code resuming from `longjmp` or a caught
+	 exception would trigger false positives when correctly accessing
+	 variables on the stack.  Hence we need to handle things like
+	 `longjmp`, thread exit, and exceptions in a different way.  These
+	 problems must be handled externally to the compiler, e.g. in the
+	 language runtime.  */
+      if (! hwasan_sanitize_p ())
+	{
+	  tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
+	  gimple *g = gimple_build_call (decl, 0);
+	  gimple_set_location (g, gimple_location (stmt));
+	  gsi_insert_before (iter, g, GSI_SAME_STMT);
+	}
     }
 
   bool instrumented = false;
@@ -2982,6 +3335,9 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_PTR_CONST_PTR_UINT8
+    = build_function_type_list (ptr_type_node, const_ptr_type_node,
+				unsigned_char_type_node, NULL_TREE);
   tree BT_FN_VOID_PTR_UINT8_PTRMODE
     = build_function_type_list (void_type_node, ptr_type_node,
 				unsigned_char_type_node,
@@ -3305,6 +3661,43 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 
   gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
 
+  if (hwasan_sanitize_p ())
+    {
+      gcc_assert (param_hwasan_instrument_stack);
+      gimple_seq stmts = NULL;
+      /* Here we swap ASAN_MARK calls for HWASAN_MARK.
+	 This is because we are using the approach of using ASAN_MARK as a
+	 synonym until here.
+	 That approach means we don't yet have to duplicate all the special
+	 cases for ASAN_MARK and ASAN_POISON with the exact same handling but
+	 called HWASAN_MARK etc.
+
+	 N.b. __asan_poison_stack_memory (which implements ASAN_MARK for ASAN)
+	 rounds the size up to its shadow memory granularity, while
+	 __hwasan_tag_memory (which implements the same for HWASAN) does not.
+	 Hence we emit HWASAN_MARK with an aligned size unlike ASAN_MARK.  */
+      tree len = gimple_call_arg (g, 2);
+      tree new_len;
+      /* If the length is a simple SHWI, then we can calculate the rounded up
+	 length directly.  Otherwise we need to emit gimple to do this
+	 rounding at runtime.  */
+      if (tree_fits_shwi_p (len))
+	{
+	  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+	  uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
+	  size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
+	  new_len = build_int_cst (pointer_sized_int_node, size_in_bytes);
+	}
+      else
+	new_len = hwasan_emit_round_up (&stmts, loc, len,
+					HWASAN_TAG_GRANULE_SIZE);
+      gimple_build (&stmts, loc, as_combined_fn (IFN_HWASAN_MARK),
+		    void_type_node, gimple_call_arg (g, 0),
+		    base, new_len);
+      gsi_replace_with_seq (iter, stmts, true);
+      return false;
+    }
+
   if (is_poison)
     {
       if (asan_handled_variables == NULL)
@@ -3379,6 +3772,7 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 bool
 asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
 {
+  gcc_assert (!hwasan_sanitize_p ());
   gimple *g = gsi_stmt (*iter);
   location_t loc = gimple_location (g);
   bool recover_p;
@@ -3652,11 +4046,61 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
 
       int nargs;
       bool store_p = gimple_call_internal_p (use, IFN_ASAN_POISON_USE);
-      tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
-				    &nargs);
-
-      gcall *call = gimple_build_call (fun, 1,
-				       build_fold_addr_expr (shadow_var));
+      gcall *call;
+      if (hwasan_sanitize_p ())
+	{
+	  tree fun = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MISMATCH4);
+	  /* NOTE: hwasan has no __hwasan_report_* functions like asan does.
+		We use __hwasan_tag_mismatch4 with arguments that tell it the
+		size of access and load to report all tag mismatches.
+
+		The arguments to this function are:
+		  Address of invalid access.
+		  Bitfield containing information about the access
+		    (access_info)
+		  Pointer to a frame of registers
+		    (for use in printing the contents of registers in a dump)
+		    Not used yet -- to be used by inline instrumentation.
+		  Size of access.
+
+		The access_info bitfield encodes the following pieces of
+		information:
+		  - Is this a store or load?
+		    access_info & 0x10  =>  store
+		  - Should the program continue after reporting the error?
+		    access_info & 0x20  =>  recover
+		  - What size access is this (not used here since we can always
+		    pass the size in the last argument)
+
+		    if (access_info & 0xf == 0xf)
+		      size is taken from last argument.
+		    else
+		      size == 1 << (access_info & 0xf)
+
+		The last argument contains the size of the access iff the
+		access_info size indicator is 0xf (we always use this argument
+		rather than storing the size in the access_info bitfield).
+
+		See the function definition `__hwasan_tag_mismatch4` in
+		libsanitizer/hwasan for the full definition.
+		*/
+	  unsigned access_info = (0x20 * recover_p)
+	    + (0x10 * store_p)
+	    + (0xf);
+	  call = gimple_build_call (fun, 4,
+				    build_fold_addr_expr (shadow_var),
+				    build_int_cst (pointer_sized_int_node,
+						   access_info),
+				    build_int_cst (pointer_sized_int_node, 0),
+				    size);
+	}
+      else
+	{
+	  tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
+					&nargs);
+	  call = gimple_build_call (fun, 1,
+				    build_fold_addr_expr (shadow_var));
+	}
       gimple_set_location (call, gimple_location (use));
       gimple *call_to_insert = call;
 
@@ -3704,6 +4148,12 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
 static unsigned int
 asan_instrument (void)
 {
+  if (hwasan_sanitize_p ())
+    {
+      transform_statements ();
+      return 0;
+    }
+
   if (shadow_ptr_types[0] == NULL_TREE)
     asan_init_shadow_ptr_types ();
   transform_statements ();
@@ -3741,7 +4191,7 @@ public:
 
   /* opt_pass methods: */
   opt_pass * clone () { return new pass_asan (m_ctxt); }
-  virtual bool gate (function *) { return gate_asan (); }
+  virtual bool gate (function *) { return gate_asan () || gate_hwasan (); }
   virtual unsigned int execute (function *) { return asan_instrument (); }
 
 }; // class pass_asan
@@ -3777,7 +4227,10 @@ public:
   {}
 
   /* opt_pass methods: */
-  virtual bool gate (function *) { return !optimize && gate_asan (); }
+  virtual bool gate (function *)
+    {
+      return !optimize && (gate_asan () || gate_hwasan ());
+    }
   virtual unsigned int execute (function *) { return asan_instrument (); }
 
 }; // class pass_asan_O0
@@ -3790,6 +4243,8 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/*  HWASAN  */
+
 /* For stack tagging:
 
    Return the offset from the frame base tag that the "next" expanded object
@@ -4138,4 +4593,138 @@ hwasan_truncate_to_tag_size (rtx tag, rtx target)
   return tag;
 }
 
+/* Construct a function tree for __hwasan_{load,store}{1,2,4,8,16,_n}.
+   IS_STORE is either 1 (for a store) or 0 (for a load).  */
+static combined_fn
+hwasan_check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
+		   int *nargs)
+{
+  static enum built_in_function check[2][2][6]
+    = { { { BUILT_IN_HWASAN_LOAD1, BUILT_IN_HWASAN_LOAD2,
+	    BUILT_IN_HWASAN_LOAD4, BUILT_IN_HWASAN_LOAD8,
+	    BUILT_IN_HWASAN_LOAD16, BUILT_IN_HWASAN_LOADN },
+	  { BUILT_IN_HWASAN_STORE1, BUILT_IN_HWASAN_STORE2,
+	    BUILT_IN_HWASAN_STORE4, BUILT_IN_HWASAN_STORE8,
+	    BUILT_IN_HWASAN_STORE16, BUILT_IN_HWASAN_STOREN } },
+	{ { BUILT_IN_HWASAN_LOAD1_NOABORT,
+	    BUILT_IN_HWASAN_LOAD2_NOABORT,
+	    BUILT_IN_HWASAN_LOAD4_NOABORT,
+	    BUILT_IN_HWASAN_LOAD8_NOABORT,
+	    BUILT_IN_HWASAN_LOAD16_NOABORT,
+	    BUILT_IN_HWASAN_LOADN_NOABORT },
+	  { BUILT_IN_HWASAN_STORE1_NOABORT,
+	    BUILT_IN_HWASAN_STORE2_NOABORT,
+	    BUILT_IN_HWASAN_STORE4_NOABORT,
+	    BUILT_IN_HWASAN_STORE8_NOABORT,
+	    BUILT_IN_HWASAN_STORE16_NOABORT,
+	    BUILT_IN_HWASAN_STOREN_NOABORT } } };
+  if (size_in_bytes == -1)
+    {
+      *nargs = 2;
+      return as_combined_fn (check[recover_p][is_store][5]);
+    }
+  *nargs = 1;
+  int size_log2 = exact_log2 (size_in_bytes);
+  gcc_assert (size_log2 >= 0 && size_log2 <= 5);
+  return as_combined_fn (check[recover_p][is_store][size_log2]);
+}
+
+/* Expand the HWASAN_{LOAD,STORE} builtins.  */
+bool
+hwasan_expand_check_ifn (gimple_stmt_iterator *iter, bool)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  bool recover_p;
+  if (flag_sanitize & SANITIZE_USER_HWADDRESS)
+    recover_p = (flag_sanitize_recover & SANITIZE_USER_HWADDRESS) != 0;
+  else
+    recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_HWADDRESS) != 0;
+
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_CHECK_LAST);
+  bool is_scalar_access = (flags & ASAN_CHECK_SCALAR_ACCESS) != 0;
+  bool is_store = (flags & ASAN_CHECK_STORE) != 0;
+  bool is_non_zero_len = (flags & ASAN_CHECK_NON_ZERO_LEN) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  tree len = gimple_call_arg (g, 2);
+
+  /* `align` is unused for HWASAN_CHECK, but we pass the argument anyway
+     since that way the arguments match ASAN_CHECK.  */
+  /* HOST_WIDE_INT align = tree_to_shwi (gimple_call_arg (g, 3));  */
+
+  unsigned HOST_WIDE_INT size_in_bytes
+    = is_scalar_access ? tree_to_shwi (len) : -1;
+
+  gimple_stmt_iterator gsi = *iter;
+
+  if (!is_non_zero_len)
+    {
+      /* So, the length of the memory area to hwasan-protect is
+	 non-constant.  Let's guard the generated instrumentation code
+	 like:
+
+	 if (len != 0)
+	   {
+	     // hwasan instrumentation code goes here.
+	   }
+	 // falltrough instructions, starting with *ITER.  */
+
+      g = gimple_build_cond (NE_EXPR,
+			    len,
+			    build_int_cst (TREE_TYPE (len), 0),
+			    NULL_TREE, NULL_TREE);
+      gimple_set_location (g, loc);
+
+      basic_block then_bb, fallthrough_bb;
+      insert_if_then_before_iter (as_a <gcond *> (g), iter,
+				  /*then_more_likely_p=*/true,
+				  &then_bb, &fallthrough_bb);
+      /* Note that fallthrough_bb starts with the statement that was
+	pointed to by ITER.  */
+
+      /* The 'then block' of the 'if (len != 0) condition is where
+	we'll generate the hwasan instrumentation code now.  */
+      gsi = gsi_last_bb (then_bb);
+    }
+
+  gimple_seq stmts = NULL;
+  tree base_addr = gimple_build (&stmts, loc, NOP_EXPR,
+				 pointer_sized_int_node, base);
+
+  int nargs = 0;
+  combined_fn fn = hwasan_check_func (is_store, recover_p, size_in_bytes, &nargs);
+  if (nargs == 1)
+    gimple_build (&stmts, loc, fn, void_type_node, base_addr);
+  else
+    {
+      gcc_assert (nargs == 2);
+      tree sz_arg = gimple_build (&stmts, loc, NOP_EXPR,
+				  pointer_sized_int_node, len);
+      gimple_build (&stmts, loc, fn, void_type_node, base_addr, sz_arg);
+    }
+
+  gsi_insert_seq_after (&gsi, stmts, GSI_NEW_STMT);
+  gsi_remove (iter, true);
+  *iter = gsi;
+  return false;
+}
+
+/* For stack tagging:
+
+   Dummy: the HWASAN_MARK internal function should only ever be in the code
+   after the sanopt pass.  */
+bool
+hwasan_expand_mark_ifn (gimple_stmt_iterator *)
+{
+  gcc_unreachable ();
+}
+
+bool
+gate_hwasan ()
+{
+  return hwasan_sanitize_p ();
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index 1ad6657da45cc4976532e1b8bc233f67d8da9ccf..770f3575cee2c6a4a5cfb16354bd8ba2d65f574e 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -503,6 +503,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_INT_FEXCEPT_T_PTR_INT, BT_INT, BT_FEXCEPT_T_PTR,
 		     BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_FEXCEPT_T_PTR_INT, BT_INT,
 		     BT_CONST_FEXCEPT_T_PTR, BT_INT)
+DEF_FUNCTION_TYPE_2 (BT_FN_PTR_CONST_PTR_UINT8, BT_PTR, BT_CONST_PTR, BT_UINT8)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR)
 
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index d97a231e7e8333c1b32a39d03851b974dc55c1f7..075d6e5208a392956436bf92cb88a9e3378e5216 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -753,6 +753,7 @@ dump_gimple_call_args (pretty_printer *buffer, const gcall *gs,
 	  limit = ARRAY_SIZE (reduction_args);
 	  break;
 
+	case IFN_HWASAN_MARK:
 	case IFN_ASAN_MARK:
 #define DEF(X) #X
 	  static const char *const asan_mark_args[] = {IFN_ASAN_MARK_FLAGS};
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 2566ec7f0af8dc844901ad0bda2a73fdb4383b4d..7b7c5e348893b7249638ff9c5a04d16af7498698 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1237,8 +1237,11 @@ asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
 
   /* It's necessary to have all stack variables aligned to ASAN granularity
      bytes.  */
-  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
-    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+  gcc_assert (!hwasan_sanitize_p () || hwasan_sanitize_stack_p ());
+  unsigned shadow_granularity
+    = hwasan_sanitize_p () ? HWASAN_TAG_GRANULE_SIZE : ASAN_SHADOW_GRANULARITY;
+  if (DECL_ALIGN_UNIT (decl) <= shadow_granularity)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * shadow_granularity);
 
   HOST_WIDE_INT flags = poison ? ASAN_MARK_POISON : ASAN_MARK_UNPOISON;
 
@@ -15365,7 +15368,7 @@ gimplify_function_tree (tree fndecl)
      if necessary.  */
   cfun->curr_properties |= PROP_gimple_lva;
 
-  if (asan_sanitize_use_after_scope () && sanitize_flags_p (SANITIZE_ADDRESS))
+  if (asan_sanitize_use_after_scope ())
     asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
   if (asan_poisoned_variables)
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 792d2ca568a4a11c3e3ad8abf6443f173b0650a7..5adf1680ef918ccbee0d4af2ba0f0b097470484a 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -469,6 +469,139 @@ expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
+expand_HWASAN_CHECK (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
+
+/* For hwasan stack tagging:
+   Clear tags on the dynamically allocated space.
+   For use after an object dynamically allocated on the stack goes out of
+   scope.  */
+static void
+expand_HWASAN_ALLOCA_UNPOISON (internal_fn, gcall *gc)
+{
+  tree restored_position = gimple_call_arg (gc, 0);
+  rtx restored_rtx = expand_expr (restored_position, NULL_RTX, VOIDmode,
+				  EXPAND_NORMAL);
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  rtx off = expand_simple_binop (ptr_mode, MINUS, restored_rtx,
+				 stack_pointer_rtx, NULL_RTX, 0,
+				 OPTAB_WIDEN);
+  rtx dynamic = convert_memory_address (ptr_mode, virtual_stack_dynamic_rtx);
+  emit_library_call_value (func, NULL_RTX, LCT_NORMAL, VOIDmode,
+			   dynamic, ptr_mode,
+			   HWASAN_STACK_BACKGROUND, QImode,
+			   off, ptr_mode);
+}
+
+/* For hwasan stack tagging:
+   Return a tag to be used for a dynamic allocation.  */
+static void
+expand_HWASAN_CHOOSE_TAG (internal_fn, gcall *gc)
+{
+  tree tag = gimple_call_lhs (gc);
+  rtx target = expand_expr (tag, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  machine_mode mode = GET_MODE (target);
+  gcc_assert (mode == QImode);
+
+  rtx base_tag = targetm.memtag.extract_tag (hwasan_frame_base (), NULL_RTX);
+  gcc_assert (base_tag);
+  rtx tag_offset = gen_int_mode (hwasan_current_frame_tag (), QImode);
+  rtx chosen_tag = expand_simple_binop (QImode, PLUS, base_tag, tag_offset,
+					target, /* unsignedp = */1,
+					OPTAB_WIDEN);
+  chosen_tag = hwasan_truncate_to_tag_size (chosen_tag, target);
+
+  /* Really need to put the tag into the `target` RTX.  */
+  if (chosen_tag != target)
+    {
+      rtx temp = chosen_tag;
+      gcc_assert (GET_MODE (chosen_tag) == mode);
+      emit_move_insn (target, temp);
+    }
+
+  hwasan_increment_frame_tag ();
+}
+
+/* For hwasan stack tagging:
+   Tag a region of space in the shadow stack according to the base pointer of
+   an object on the stack.  N.b. the length provided in the internal call is
+   required to be aligned to HWASAN_TAG_GRANULE_SIZE.  */
+static void
+expand_HWASAN_MARK (internal_fn, gcall *gc)
+{
+  HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
+  bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
+
+  tree base = gimple_call_arg (gc, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  /* base is a pointer argument, hence in ptr_mode.
+     We convert to Pmode for use in the targetm.memtag.extract_tag and
+     targetm.memtag.untagged_pointer hooks.
+     We then convert the result to ptr_mode for the emit_library_call.
+
+     This conversion is not for functionality so much as for the code to be
+     correctly formed.  If ptr_mode is smaller than Pmode then the top byte of
+     a Pmode value will be truncated in C code losing the tag values, and
+     HWASAN will not work.  */
+  rtx base_rtx = convert_memory_address (Pmode, expand_normal (base));
+
+  rtx tag = is_poison ? HWASAN_STACK_BACKGROUND
+    : targetm.memtag.extract_tag (base_rtx, NULL_RTX);
+  rtx address = targetm.memtag.untagged_pointer (base_rtx, NULL_RTX);
+  address = convert_memory_address (ptr_mode, address);
+
+  tree len = gimple_call_arg (gc, 2);
+  rtx r_len = expand_normal (len);
+
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (func, LCT_NORMAL, VOIDmode, address, ptr_mode,
+		     tag, QImode, r_len, ptr_mode);
+}
+
+/* For hwasan stack tagging:
+   Store a tag into a pointer.  */
+static void
+expand_HWASAN_SET_TAG (internal_fn, gcall *gc)
+{
+  tree g_target = gimple_call_lhs (gc);
+  tree g_ptr = gimple_call_arg (gc, 0);
+  tree g_tag = gimple_call_arg (gc, 1);
+
+  rtx ptr = convert_memory_address (Pmode, expand_normal (g_ptr));
+  rtx tag = expand_expr (g_tag, NULL_RTX, QImode, EXPAND_NORMAL);
+  rtx target = expand_normal (g_target);
+  machine_mode mode = GET_MODE (target);
+
+  rtx untagged = targetm.memtag.untagged_pointer (ptr, target);
+  rtx tagged_value = targetm.memtag.set_tag (untagged, tag, target);
+  /* Really need to put the return value into the `target` RTX, since that's
+     the return value of the function.
+     `target` will be in ptr_mode, while `tagged_value` will be in Pmode.
+     These can be different.  When they are different we try to truncate the
+     Pmode value into ptr_mode.  This will usually lose the tag, but since such
+     a difference between ptr_mode and Pmode will already cause problems
+     wherever the HWASAN library returns a pointer losing the tag here does not
+     introduce new incompatibilities.
+
+     We do this mainly so that compiling for such a target with ptr_mode and
+     Pmode sizes being different doesn't ICE, even if the resulting binary is
+     not usable.  */
+  if (tagged_value != target)
+    {
+      rtx temp = tagged_value;
+      machine_mode ret_mode = GET_MODE (tagged_value);
+      if (ret_mode != mode)
+	temp = simplify_gen_unary (TRUNCATE, mode, tagged_value, ret_mode);
+
+      emit_move_insn (target, temp);
+    }
+}
+
+/* This should get expanded in the sanopt pass.  */
+
+static void
 expand_ASAN_CHECK (internal_fn, gcall *)
 {
   gcc_unreachable ();
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 310d37aa53819791b5df1683afca831f08e5892a..91a7bfea3eecad747320afcd11e05eedb9f7fcbf 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -321,6 +321,13 @@ DEF_INTERNAL_FN (UBSAN_PTR, ECF_LEAF | ECF_NOTHROW, ". R . ")
 DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
 DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (HWASAN_ALLOCA_UNPOISON, ECF_LEAF | ECF_NOTHROW, ". R ")
+DEF_INTERNAL_FN (HWASAN_CHOOSE_TAG, ECF_LEAF | ECF_NOTHROW, ". ")
+DEF_INTERNAL_FN (HWASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW,
+		 ". . R . . ")
+DEF_INTERNAL_FN (HWASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (HWASAN_SET_TAG,
+		 ECF_TM_PURE | ECF_PURE | ECF_LEAF | ECF_NOTHROW, ". R R ")
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW,
 		 ". . R . . ")
 DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 4f854fb994229fd4ed91d3b5cff7c7acff9a55bc..f02731e8f2b5c7e78000d768e7c472658883ccb4 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -183,6 +183,61 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 /* Hardware Address Sanitizer.  */
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1, "__hwasan_load1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2, "__hwasan_load2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4, "__hwasan_load4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8, "__hwasan_load8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16, "__hwasan_load16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN, "__hwasan_loadN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1, "__hwasan_store1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2, "__hwasan_store2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4, "__hwasan_store4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8, "__hwasan_store8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16, "__hwasan_store16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN, "__hwasan_storeN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1_NOABORT, "__hwasan_load1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2_NOABORT, "__hwasan_load2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4_NOABORT, "__hwasan_load4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8_NOABORT, "__hwasan_load8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16_NOABORT, "__hwasan_load16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN_NOABORT, "__hwasan_loadN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1_NOABORT, "__hwasan_store1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2_NOABORT, "__hwasan_store2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4_NOABORT, "__hwasan_store4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8_NOABORT, "__hwasan_store8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16_NOABORT,
+		      "__hwasan_store16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN_NOABORT, "__hwasan_storeN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MISMATCH4, "__hwasan_tag_mismatch4",
+		      BT_FN_VOID_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_HANDLE_LONGJMP, "__hwasan_handle_longjmp",
+		      BT_FN_VOID_CONST_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_PTR, "__hwasan_tag_pointer",
+		      BT_FN_PTR_CONST_PTR_UINT8, ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
 		      BT_FN_VOID_PTR_UINT8_PTRMODE, ATTR_NOTHROW_LIST)
 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 6c3bce923785f8a2dcd324edc0ce7ceb5f633a0c..965ab367c299f9315cddeb968d1c75086c8762b4 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -776,7 +776,8 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
   basic_block son;
   gimple_stmt_iterator gsi;
   sanopt_info *info = (sanopt_info *) bb->aux;
-  bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0;
+  bool asan_check_optimize
+    = ((flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS)) != 0);
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
@@ -806,6 +807,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
       if (asan_check_optimize
 	  && gimple_call_builtin_p (stmt, BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT))
 	{
+	  gcc_assert (!hwasan_sanitize_p ());
 	  use_operand_p use;
 	  gimple *use_stmt;
 	  if (single_imm_use (gimple_vdef (stmt), &use, &use_stmt))
@@ -834,6 +836,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
 	  case IFN_UBSAN_PTR:
 	    remove = maybe_optimize_ubsan_ptr_ifn (ctx, stmt);
 	    break;
+	  case IFN_HWASAN_CHECK:
 	  case IFN_ASAN_CHECK:
 	    if (asan_check_optimize)
 	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
@@ -1262,6 +1265,10 @@ sanitize_rewrite_addressable_params (function *fun)
 unsigned int
 pass_sanopt::execute (function *fun)
 {
+  /* n.b. ASAN_MARK is used for both HWASAN and ASAN.
+     asan_num_accesses is hence used to count either HWASAN_CHECK or ASAN_CHECK
+     stuff.  This is fine because you can only have one of these active at a
+     time.  */
   basic_block bb;
   int asan_num_accesses = 0;
   bool contains_asan_mark = false;
@@ -1269,10 +1276,10 @@ pass_sanopt::execute (function *fun)
   /* Try to remove redundant checks.  */
   if (optimize
       && (flag_sanitize
-	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT
+	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_HWADDRESS
 	     | SANITIZE_ADDRESS | SANITIZE_VPTR | SANITIZE_POINTER_OVERFLOW)))
     asan_num_accesses = sanopt_optimize (fun, &contains_asan_mark);
-  else if (flag_sanitize & SANITIZE_ADDRESS)
+  else if (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS))
     {
       gimple_stmt_iterator gsi;
       FOR_EACH_BB_FN (bb, fun)
@@ -1292,7 +1299,7 @@ pass_sanopt::execute (function *fun)
       sanitize_asan_mark_poison ();
     }
 
-  if (asan_sanitize_stack_p ())
+  if (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ())
     sanitize_rewrite_addressable_params (fun);
 
   bool use_calls = param_asan_instrumentation_with_call_threshold < INT_MAX
@@ -1334,6 +1341,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_UBSAN_VPTR:
 		  no_next = ubsan_expand_vptr_ifn (&gsi);
 		  break;
+		case IFN_HWASAN_CHECK:
+		  no_next = hwasan_expand_check_ifn (&gsi, use_calls);
+		  break;
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
@@ -1345,6 +1355,9 @@ pass_sanopt::execute (function *fun)
 						    &need_commit_edge_insert,
 						    shadow_vars_mapping);
 		  break;
+		case IFN_HWASAN_MARK:
+		  no_next = hwasan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 9938b6afbd4fa22898dbc3c29b92061a71810b08..cb4ae77fccb44769732f9677a65c96be6321c9fa 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -512,7 +512,7 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
-      if (flag_sanitize & SANITIZE_HWADDRESS)
+      if (gate_hwasan ())
 	hwasan_finish_file ();
 
       omp_finish_file ();


[-- Attachment #2: hwasan6-v6.patch.gz --]
[-- Type: application/gzip, Size: 14624 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Update: [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN
  2020-11-19 12:57         ` Update: " Matthew Malcomson
@ 2020-11-19 15:28           ` Richard Sandiford
  2020-11-20 18:46             ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Richard Sandiford @ 2020-11-19 15:28 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: Richard Earnshaw, Kyrylo Tkachov, gcc-patches, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> […]
> +/* hwasan_frame_base_init_seq is the sequence of RTL insns that will initialize
> +   the hwasan_frame_base_ptr.  When the hwasan_frame_base_ptr is requested, we
> +   generate this sequence but do not emit it.  If the sequence was created it
> +   is emitted once the function body has been expanded.
> +
> +   This delay is because the frame base pointer may be needed anywhere in the
> +   function body, or needed by the expand_used_vars function.  Emitting once in
> +   a known place is simpler than requiring the emition of the instructions to

s/emition/emission/

> +   be know where it should go depending on the first place the hwasan frame
> +   base is needed.  */
> +static GTY(()) rtx_insn *hwasan_frame_base_init_seq = NULL;
> […]
> +/* For stack tagging:
> +
> +   Return the 'base pointer' for this function.  If that base pointer has not
> +   yet been created then we create a register to hold it and record the insns
> +   to initialize the register in `hwasan_frame_base_init_seq` for later
> +   emission.  */
> +rtx
> +hwasan_frame_base ()
> +{
> +  if (! hwasan_frame_base_ptr)
> +    {
> +      start_sequence ();
> +      hwasan_frame_base_ptr =
> +	force_reg (Pmode,
> +		   targetm.memtag.insert_random_tag (virtual_stack_vars_rtx,
> +						     NULL_RTX));

Nit: should be formatted as:

      hwasan_frame_base_ptr
	= force_reg (Pmode,
		     targetm.memtag.insert_random_tag (virtual_stack_vars_rtx,
						       NULL_RTX));

> […]
> +  size_t length = hwasan_tagged_stack_vars.length ();
> +  hwasan_stack_var *vars = hwasan_tagged_stack_vars.address ();
> +
> +  poly_int64 bot = 0, top = 0;
> +  size_t i = 0;
> +  for (i = 0; i < length; i++)
> +    {
> +      hwasan_stack_var& cur = vars[i];

Simpler as:

  poly_int64 bot = 0, top = 0;
  for (hwasan_stack_var &cur : hwasan_tagged_stack_vars)

(GCC style is to add a space before “&”, as for “*”)

> +      poly_int64 nearest = cur.nearest_offset;
> +      poly_int64 farthest = cur.farthest_offset;
> +
> +      if (known_ge (nearest, farthest))
> +	{
> +	  top = nearest;
> +	  bot = farthest;
> +	}
> +      else
> +	{
> +	  /* Given how these values are calculated, one must be known greater
> +	     than the other.  */
> +	  gcc_assert (known_le (nearest, farthest));
> +	  top = farthest;
> +	  bot = nearest;
> +	}
> +      poly_int64 size = (top - bot);
> +
> +      /* Assert the edge of each variable is aligned to the HWASAN tag granule
> +	 size.  */
> +      gcc_assert (multiple_p (top, HWASAN_TAG_GRANULE_SIZE));
> +      gcc_assert (multiple_p (bot, HWASAN_TAG_GRANULE_SIZE));
> +      gcc_assert (multiple_p (size, HWASAN_TAG_GRANULE_SIZE));
> +
> +      rtx ret = init_one_libfunc ("__hwasan_tag_memory");
> +      rtx base_tag = targetm.memtag.extract_tag (cur.tagged_base, NULL_RTX);
> +      rtx tag = plus_constant (QImode, base_tag, cur.tag_offset);
> +      tag = hwasan_truncate_to_tag_size (tag, NULL_RTX);
> +
> +      rtx bottom = convert_memory_address (ptr_mode,
> +					   plus_constant (Pmode,
> +							  cur.untagged_base,
> +							  bot));
> +      emit_library_call (ret, LCT_NORMAL, VOIDmode,
> +			 bottom, ptr_mode,
> +			 tag, QImode,
> +			 gen_int_mode (size, ptr_mode), ptr_mode);
> +    }
> +  /* Clear the stack vars, we've emitted the prologue for them all now.  */
> +  hwasan_tagged_stack_vars.truncate (0);
> +}
> +
> +/* For stack tagging:
> +
> +   Return RTL insns to clear the tags between DYNAMIC and VARS pointers
> +   into the stack.  These instructions should be emitted at the end of
> +   every function.
> +
> +   If `dynamic` is NULL_RTX then no insns are returned.  */
> +rtx_insn *
> +hwasan_emit_untag_frame (rtx dynamic, rtx vars)
> +{
> +  if (! dynamic)
> +    return NULL;
> +
> +  start_sequence ();
> +
> +  dynamic = convert_memory_address (ptr_mode, dynamic);
> +  vars = convert_memory_address (ptr_mode, vars);
> +
> +  rtx top_rtx;
> +  rtx bot_rtx;
> +  if (FRAME_GROWS_DOWNWARD)
> +    {
> +      top_rtx = vars;
> +      bot_rtx = dynamic;
> +    }
> +  else
> +    {
> +      top_rtx = dynamic;
> +      bot_rtx = vars;
> +    }
> +
> +  rtx size_rtx = expand_simple_binop (ptr_mode, MINUS, top_rtx, bot_rtx,
> +				      NULL_RTX, /* unsignedp = */0,
> +				      OPTAB_DIRECT);
> +
> +  rtx ret = init_one_libfunc ("__hwasan_tag_memory");
> +  emit_library_call (ret, LCT_NORMAL, VOIDmode,
> +		     bot_rtx, ptr_mode,
> +		     HWASAN_STACK_BACKGROUND, QImode,
> +		     size_rtx, ptr_mode);

Nit: “ret” seems like a strange name for this variable, since it implies
that it's a return value of the library call.  Maybe “fn” or something
would be better.

> […]
> @@ -1216,6 +1255,24 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>  	    {
>  	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
>  	      base_align = crtl->max_used_stack_slot_alignment;
> +
> +	      if (hwasan_sanitize_stack_p ())
> +		{
> +		  /* Align again since the point of this alignment is to handle
> +		     the "end" of the object (i.e. smallest address after the
> +		     stack object).  For FRAME_GROWS_DOWNWARD that requires
> +		     aligning the stack before allocating, but for a frame that
> +		     grows upwards that requires aligning the stack after
> +		     allocation.
> +
> +		     Use `frame_offset` to record the offset value rather than
> +		     offset since the frame_offset describes the extent

Would be easier to parse if the second “offset” was in quotes too.

> +		     allocated for this particular variable while `offset`
> +		     describes the address that this variable starts at.  */
> +		  align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
> +		  hwasan_record_stack_var (virtual_stack_vars_rtx, base,
> +					   hwasan_orig_offset, frame_offset);
> +		}
>  	    }
>  	}
>        else
> […]
> +/* The default implementation of TARGET_MEMTAG_SET_TAG.  */
> +rtx
> +default_memtag_set_tag (rtx untagged, rtx tag, rtx target)
> +{
> +  gcc_assert (GET_MODE (untagged) == Pmode);
> +  gcc_assert (GET_MODE (tag) == QImode);

Nit: I think the general preference is to have a single gcc_assert that
combines both conditions, for code size reasons.

> +  tag = expand_simple_binop (Pmode, ASHIFT, tag, HWASAN_SHIFT_RTX, tag,
> +			     /* unsignedp = */1, OPTAB_WIDEN);

This should pass NULL_RTX as the target instead of “tag”, since we can't
guarantee that “tag” is overwritable.  (In practice “tag” would probably
never get chosen as the target anyway since it has the wrong mode.)

OK with those changes, thanks.

Richard

> +  rtx ret = expand_simple_binop (Pmode, IOR, untagged, tag, target,
> +				 /* unsignedp = */1, OPTAB_DIRECT);
> +  gcc_assert (ret);
> +  return ret;
> +}
> […]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Update [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes
  2020-11-19 13:01       ` Update [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes Matthew Malcomson
@ 2020-11-19 16:28         ` Richard Sandiford
  2020-11-20 17:28           ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Richard Sandiford @ 2020-11-19 16:28 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: Richard Earnshaw, Kyrylo Tkachov, gcc-patches, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> +/* Emit gimple statements into &stmts that take the size given in `len` and
> +   generate a size that is guaranteed to be rounded upwards to `align`.
> +
> +   This is a helper function for both handle_builtin_alloca and
> +   asan_expand_mark_ifn when using HWASAN.
> +
> +   Return the tree node representing this size, it is of TREE_TYPE
> +   size_type_node.  */
> +
> +static tree
> +hwasan_emit_round_up (gimple_seq *seq, location_t loc, tree old_size,
> +		      uint8_t align)
> +{
> +  uint8_t tg_mask = align - 1;
> +  /* tree new_size = (old_size + tg_mask) & ~tg_mask;  */
> +  tree tree_mask = build_int_cst (size_type_node, tg_mask);
> +  tree oversize = gimple_build (seq, loc, PLUS_EXPR, size_type_node, old_size,
> +				tree_mask);
> +
> +  tree mask = build_int_cst (size_type_node, -align);
> +  return gimple_build (seq, loc, BIT_AND_EXPR, size_type_node, oversize, mask);
> +}
> +

There's nothing really hwasan-specific about this, apart from the choice
“uint8_t” for the alignment and mask.  So I think we should:

- chnage “align” and “tg_mask” to “unsigned HOST_WIDE_INT”
- change the name to “gimple_build_round_up”
- take the type as a parameter, in the same position as other
  gimple_build_* type parameters
- move the function to gimple-fold.c, exported via gimple-fold.h
- drop:

   This is a helper function for both handle_builtin_alloca and
   asan_expand_mark_ifn when using HWASAN.

> […]
> @@ -690,6 +757,71 @@ handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
>      = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
>        ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
>  
> +  if (hwasan_sanitize_allocas_p ())
> +    {
> +      gimple_seq stmts = NULL;
> +      location_t loc = gimple_location (gsi_stmt (*iter));
> +      /*
> +	 HWASAN needs a different expansion.
> +
> +	 addr = __builtin_alloca (size, align);
> +
> +	 should be replaced by
> +
> +	 new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment;
> +	 untagged_addr = __builtin_alloca (new_size, align);
> +	 tag = __hwasan_choose_alloca_tag ();
> +	 addr = ifn_HWASAN_SET_TAG (untagged_addr, tag);
> +	 __hwasan_tag_memory (untagged_addr, tag, new_size);
> +	*/
> +      /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start on
> +	 a tag granule.  */
> +      align = align > HWASAN_TAG_GRANULE_SIZE ? align : HWASAN_TAG_GRANULE_SIZE;
> +
> +      tree old_size = gimple_call_arg (call, 0);
> +      tree new_size = hwasan_emit_round_up (&stmts, loc, old_size,
> +					    HWASAN_TAG_GRANULE_SIZE);
> +
> +      /* Make the alloca call */
> +      tree untagged_addr
> +	= gimple_build (&stmts, loc,
> +			as_combined_fn (BUILT_IN_ALLOCA_WITH_ALIGN), ptr_type,
> +			new_size, build_int_cst (size_type_node, align));
> +
> +      /* Choose the tag.
> +	 Here we use an internal function so we can choose the tag at expand
> +	 time.  We need the decision to be made after stack variables have been
> +	 assigned their tag (i.e. once the hwasan_frame_tag_offset variable has
> +	 been set to one after the last stack variables tag).  */
> +      gcall *stmt = gimple_build_call_internal (IFN_HWASAN_CHOOSE_TAG, 0);
> +      tree tag = make_ssa_name (unsigned_char_type_node);
> +      gimple_call_set_lhs (stmt, tag);
> +      gimple_set_location (stmt, loc);
> +      gimple_seq_add_stmt_without_update (&stmts, stmt);

Even though there are currently no folds defined for argumentless
functions, I think it would be worth adding a gimple_build overload
for this instead of writing it out by hand.  I.e. have a zero-argument
version of:

tree
gimple_build (gimple_seq *seq, location_t loc, combined_fn fn,
	      tree type, tree arg0)
{
  tree res = gimple_simplify (fn, type, arg0, seq, gimple_build_valueize);
  if (!res)
    {
      gcall *stmt;
      if (internal_fn_p (fn))
	stmt = gimple_build_call_internal (as_internal_fn (fn), 1, arg0);
      else
	{
	  tree decl = builtin_decl_implicit (as_builtin_fn (fn));
	  stmt = gimple_build_call (decl, 1, arg0);
	}
      if (!VOID_TYPE_P (type))
	{
	  res = create_tmp_reg_or_ssa_name (type);
	  gimple_call_set_lhs (stmt, res);
	}
      gimple_set_location (stmt, loc);
      gimple_seq_add_stmt_without_update (seq, stmt);
    }
  return res;
}

without the gimple_simplify call.

> +
> +      /* Add tag to pointer.  */
> +      tree addr
> +	= gimple_build (&stmts, loc, as_combined_fn (IFN_HWASAN_SET_TAG),

This is CFN_HWASAN_SET_TAG.

> +			ptr_type, untagged_addr, tag);
> +
> +      /* Tag shadow memory.
> +	 NOTE: require using `untagged_addr` here for libhwasan API.  */
> +      gimple_build (&stmts, loc, as_combined_fn (BUILT_IN_HWASAN_TAG_MEM),
> +		    void_type_node, untagged_addr, tag, new_size);
> +
> +      /* Insert the built up code sequence into the original instruction stream
> +	 the iterator points to.  */
> +      gsi_insert_seq_before (iter, stmts, GSI_SAME_STMT);
> +
> +      /* Finally, replace old alloca ptr with NEW_ALLOCA.  */
> +      replace_call_with_value (iter, addr);
> +      return;
> +    }
> +
> +  tree last_alloca = get_last_alloca_addr ();
> +  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
> +
> +

Nit: excess blank line.

>    /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
>       bytes of allocated space.  Otherwise, align alloca to ASAN_RED_ZONE_SIZE
>       manually.  */
> […]
> @@ -2351,7 +2686,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
>      {
>        if (DECL_THREAD_LOCAL_P (inner))
>  	return;
> -      if (!param_asan_globals && is_global_var (inner))
> +      if ((hwasan_sanitize_p () || !param_asan_globals)
> +	  && is_global_var (inner))
>          return;
>        if (!TREE_STATIC (inner))
>  	{

As mentioned in the previous round, I think it would be good to have
some commentary explaining this.

> […]
> +      /* If the length is a simple SHWI, then we can calculate the rounded up
> +	 length directly.  Otherwise we need to emit gimple to do this
> +	 rounding at runtime.  */
> +      if (tree_fits_shwi_p (len))
> +	{
> +	  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
> +	  uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
> +	  size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
> +	  new_len = build_int_cst (pointer_sized_int_node, size_in_bytes);
> +	}
> +      else
> +	new_len = hwasan_emit_round_up (&stmts, loc, len,
> +					HWASAN_TAG_GRANULE_SIZE);

I'm not sure the special case is worth it.  We might as well call
(hwasan_emit/gimple_build)_round_up unconditionally, which would also
have the nice effect of testing it more.

> +      gimple_build (&stmts, loc, as_combined_fn (IFN_HWASAN_MARK),

CFN_HWASAN_MARK

> +		    void_type_node, gimple_call_arg (g, 0),
> +		    base, new_len);
> +      gsi_replace_with_seq (iter, stmts, true);
> +      return false;
> +    }
> +
>    if (is_poison)
>      {
>        if (asan_handled_variables == NULL)
> […]
> +/* For hwasan stack tagging:
> +   Tag a region of space in the shadow stack according to the base pointer of
> +   an object on the stack.  N.b. the length provided in the internal call is
> +   required to be aligned to HWASAN_TAG_GRANULE_SIZE.  */
> +static void
> +expand_HWASAN_MARK (internal_fn, gcall *gc)
> +{
> +  HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
> +  bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
> +
> +  tree base = gimple_call_arg (gc, 1);
> +  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
> +  /* base is a pointer argument, hence in ptr_mode.
> +     We convert to Pmode for use in the targetm.memtag.extract_tag and
> +     targetm.memtag.untagged_pointer hooks.
> +     We then convert the result to ptr_mode for the emit_library_call.
> +
> +     This conversion is not for functionality so much as for the code to be
> +     correctly formed.  If ptr_mode is smaller than Pmode then the top byte of
> +     a Pmode value will be truncated in C code losing the tag values, and
> +     HWASAN will not work.  */
> +  rtx base_rtx = convert_memory_address (Pmode, expand_normal (base));
> +
> +  rtx tag = is_poison ? HWASAN_STACK_BACKGROUND
> +    : targetm.memtag.extract_tag (base_rtx, NULL_RTX);
> +  rtx address = targetm.memtag.untagged_pointer (base_rtx, NULL_RTX);
> +  address = convert_memory_address (ptr_mode, address);
> +
> +  tree len = gimple_call_arg (gc, 2);
> +  rtx r_len = expand_normal (len);
> +
> +  rtx func = init_one_libfunc ("__hwasan_tag_memory");
> +  emit_library_call (func, LCT_NORMAL, VOIDmode, address, ptr_mode,
> +		     tag, QImode, r_len, ptr_mode);
> +}
> +
> +/* For hwasan stack tagging:
> +   Store a tag into a pointer.  */
> +static void
> +expand_HWASAN_SET_TAG (internal_fn, gcall *gc)
> +{
> +  tree g_target = gimple_call_lhs (gc);
> +  tree g_ptr = gimple_call_arg (gc, 0);
> +  tree g_tag = gimple_call_arg (gc, 1);
> +
> +  rtx ptr = convert_memory_address (Pmode, expand_normal (g_ptr));
> +  rtx tag = expand_expr (g_tag, NULL_RTX, QImode, EXPAND_NORMAL);
> +  rtx target = expand_normal (g_target);
> +  machine_mode mode = GET_MODE (target);
> +
> +  rtx untagged = targetm.memtag.untagged_pointer (ptr, target);
> +  rtx tagged_value = targetm.memtag.set_tag (untagged, tag, target);
> +  /* Really need to put the return value into the `target` RTX, since that's
> +     the return value of the function.
> +     `target` will be in ptr_mode, while `tagged_value` will be in Pmode.
> +     These can be different.  When they are different we try to truncate the
> +     Pmode value into ptr_mode.  This will usually lose the tag, but since such
> +     a difference between ptr_mode and Pmode will already cause problems
> +     wherever the HWASAN library returns a pointer losing the tag here does not
> +     introduce new incompatibilities.
> +
> +     We do this mainly so that compiling for such a target with ptr_mode and
> +     Pmode sizes being different doesn't ICE, even if the resulting binary is
> +     not usable.  */

I'm not sure that's the right trade-off.  Producing wrong code is a more
serious class of bug than an ICE.

Could the code in principle be reached on ILP32 targets?  If so,
that seems like something we should fix.  If not, then I think we
should just do everything in Pmode, perhaps with an assertion that
Pmode == ptr_mode to make it clear that this is a deliberate choice.

I see what you mean about trying to make sure that types “agree”
at the appropriate interface.  But in a sense we're already failing
to do that because we assume Pmode is correct for all addresses,
without taking address spaces into account.  Assuming Pmode == ptr_mode
doesn't seem worse than assuming a unified address space.

LGTM otherwise though.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Update [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes
  2020-11-19 16:28         ` Richard Sandiford
@ 2020-11-20 17:28           ` Matthew Malcomson
  2020-11-20 18:09             ` Richard Sandiford
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-20 17:28 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: Richard Earnshaw, Kyrylo Tkachov, gcc-patches, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 55710 bytes --]


Updates after latest review.
(testing underway)

-------------------
There are four main features to this change:

1) Check pointer tags match address tags.

When sanitizing for hwasan we now put HWASAN_CHECK internal functions before
memory accesses in the `asan` pass.  This checks that a tag in the pointer
being used match the tag stored in shadow memory for the memory region being
used.

These internal functions are expanded into actual checks in the sanopt
pass that happens just before expansion into RTL.

We use the same mechanism that currently inserts ASAN_CHECK internal
functions to insert the new HWASAN_CHECK functions.

2) Instrument known builtin function calls.

Handle all builtin functions that we know use memory accesses.
This commit uses the machinery added for ASAN to identify builtin
functions that access memory.

The main differences between the approaches for HWASAN and ASAN are:
 - libhwasan intercepts much less builtin functions.
 - Alloca needs to be transformed differently (instead of adding
   redzones it needs to tag shadow memory and return a tagged pointer).
 - stack_restore needs to untag the shadow stack between the current
   position and where it's going.
 - `noreturn` functions can not be handled by simply unpoisoning the
   entire shadow stack -- there is no "always valid" tag.
   (exceptions and things such as longjmp need to be handled in a
   different way, usually in the runtime).

For hardware implemented checking (such as AArch64's memory tagging
extension) alloca and stack_restore will need to be handled by hooks in
the backend rather than transformation at the gimple level.  This will
allow architecture specific handling of such stack modifications.

3) Introduce HWASAN block-scope poisoning

Here we use exactly the same mechanism as ASAN_MARK to poison/unpoison
variables on entry/exit of a block.

In order to simply use the exact same machinery we're using the same
internal functions until the SANOPT pass.  This means that all handling
of ASAN_MARK is the same.
This has the negative that the naming may be a little confusing, but a
positive that handling of the internal function doesn't have to be
duplicated for a function that behaves exactly the same but has a
different name.

gcc/ChangeLog:

	* asan.c (asan_instrument_reads): New.
	(asan_instrument_writes): New.
	(asan_memintrin): New.
	(handle_builtin_stack_restore): Account for HWASAN.
	(handle_builtin_alloca): Account for HWASAN.
	(get_mem_refs_of_builtin_call): Special case strlen for HWASAN.
	(hwasan_instrument_reads): New.
	(hwasan_instrument_writes): New.
	(hwasan_memintrin): New.
	(report_error_func): Assert not HWASAN.
	(build_check_stmt): Make HWASAN_CHECK instead of ASAN_CHECK.
	(instrument_derefs): HWASAN does not tag globals.
	(instrument_builtin_call): Use new helper functions.
	(maybe_instrument_call): Don't instrument `noreturn` functions.
	(initialize_sanitizer_builtins): Add new type.
	(asan_expand_mark_ifn): Account for HWASAN.
	(asan_expand_check_ifn): Assert never called by HWASAN.
	(asan_expand_poison_ifn): Account for HWASAN.
	(asan_instrument): Branch based on whether using HWASAN or ASAN.
	(pass_asan::gate): Return true if sanitizing HWASAN.
	(pass_asan_O0::gate): Return true if sanitizing HWASAN.
	(hwasan_check_func): New.
	(hwasan_expand_check_ifn): New.
	(hwasan_expand_mark_ifn): New.
	(gate_hwasan): New.
	* asan.h (hwasan_expand_check_ifn): New decl.
	(hwasan_expand_mark_ifn): New decl.
	(gate_hwasan): New decl.
	(asan_intercepted_p): Always false for hwasan.
	(asan_sanitize_use_after_scope): Account for HWASAN.
	* builtin-types.def (BT_FN_PTR_CONST_PTR_UINT8): New.
	* gimple-fold.c (gimple_build): New overload for building function
	calls without arguments.
	(gimple_build_round_up): New.
	* gimple-fold.h (gimple_build): New decl.
	(gimple_build): New inline function.
	(gimple_build_round_up): New decl.
	(gimple_build_round_up): New inline function.
	* gimple-pretty-print.c (dump_gimple_call_args): Account for
	HWASAN.
	* gimplify.c (asan_poison_variable): Account for HWASAN.
	(gimplify_function_tree): Remove requirement of
	SANITIZE_ADDRESS, requiring asan or hwasan is accounted for in
	`asan_sanitize_use_after_scope`.
	* internal-fn.c (expand_HWASAN_CHECK): New.
	(expand_HWASAN_ALLOCA_UNPOISON): New.
	(expand_HWASAN_CHOOSE_TAG): New.
	(expand_HWASAN_MARK): New.
	(expand_HWASAN_SET_TAG): New.
	* internal-fn.def (HWASAN_ALLOCA_UNPOISON): New.
	(HWASAN_CHOOSE_TAG): New.
	(HWASAN_CHECK): New.
	(HWASAN_MARK): New.
	(HWASAN_SET_TAG): New.
	* sanitizer.def (BUILT_IN_HWASAN_LOAD1): New.
	(BUILT_IN_HWASAN_LOAD2): New.
	(BUILT_IN_HWASAN_LOAD4): New.
	(BUILT_IN_HWASAN_LOAD8): New.
	(BUILT_IN_HWASAN_LOAD16): New.
	(BUILT_IN_HWASAN_LOADN): New.
	(BUILT_IN_HWASAN_STORE1): New.
	(BUILT_IN_HWASAN_STORE2): New.
	(BUILT_IN_HWASAN_STORE4): New.
	(BUILT_IN_HWASAN_STORE8): New.
	(BUILT_IN_HWASAN_STORE16): New.
	(BUILT_IN_HWASAN_STOREN): New.
	(BUILT_IN_HWASAN_LOAD1_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD2_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD4_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD8_NOABORT): New.
	(BUILT_IN_HWASAN_LOAD16_NOABORT): New.
	(BUILT_IN_HWASAN_LOADN_NOABORT): New.
	(BUILT_IN_HWASAN_STORE1_NOABORT): New.
	(BUILT_IN_HWASAN_STORE2_NOABORT): New.
	(BUILT_IN_HWASAN_STORE4_NOABORT): New.
	(BUILT_IN_HWASAN_STORE8_NOABORT): New.
	(BUILT_IN_HWASAN_STORE16_NOABORT): New.
	(BUILT_IN_HWASAN_STOREN_NOABORT): New.
	(BUILT_IN_HWASAN_TAG_MISMATCH4): New.
	(BUILT_IN_HWASAN_HANDLE_LONGJMP): New.
	(BUILT_IN_HWASAN_TAG_PTR): New.
	* sanopt.c (sanopt_optimize_walker): Act for hwasan.
	(pass_sanopt::execute): Act for hwasan.
	* toplev.c (compile_file): Use `gate_hwasan` function.



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


diff --git a/gcc/asan.h b/gcc/asan.h
index 8d5271e6b575d74da277420798557f3274e966ce..4b873d6696026839025926581ccc9aceca225196 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -49,6 +49,9 @@ extern void hwasan_finish_file (void);
 extern bool hwasan_sanitize_p (void);
 extern bool hwasan_sanitize_stack_p (void);
 extern bool hwasan_sanitize_allocas_p (void);
+extern bool hwasan_expand_check_ifn (gimple_stmt_iterator *, bool);
+extern bool hwasan_expand_mark_ifn (gimple_stmt_iterator *);
+extern bool gate_hwasan (void);
 
 extern gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
@@ -181,6 +184,9 @@ extern hash_set<tree> *asan_handled_variables;
 static inline bool
 asan_intercepted_p (enum built_in_function fcode)
 {
+  if (hwasan_sanitize_p ())
+    return false;
+
   return fcode == BUILT_IN_INDEX
 	 || fcode == BUILT_IN_MEMCHR
 	 || fcode == BUILT_IN_MEMCMP
@@ -209,7 +215,8 @@ asan_intercepted_p (enum built_in_function fcode)
 static inline bool
 asan_sanitize_use_after_scope (void)
 {
-  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
+  return (flag_sanitize_address_use_after_scope
+	  && (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ()));
 }
 
 /* Return true if DECL should be guarded on the stack.  */
diff --git a/gcc/asan.c b/gcc/asan.c
index d1ede3b62291eba698948e06208c482b6f197be5..5968e12e5dc7bc535840ace9d44286d320b89fb4 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -370,6 +370,25 @@ asan_sanitize_allocas_p (void)
   return (asan_sanitize_stack_p () && param_asan_protect_allocas);
 }
 
+bool
+asan_instrument_reads (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_instrument_reads);
+}
+
+bool
+asan_instrument_writes (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_instrument_writes);
+}
+
+bool
+asan_memintrin (void)
+{
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && param_asan_memintrin);
+}
+
+
 /* Checks whether section SEC should be sanitized.  */
 
 static bool
@@ -630,20 +649,47 @@ get_last_alloca_addr ()
    To overcome the issue we use following trick: pass new_sp as a second
    parameter to __asan_allocas_unpoison and rewrite it during expansion with
    new_sp + (virtual_dynamic_stack_rtx - sp) later in
-   expand_asan_emit_allocas_unpoison function.  */
+   expand_asan_emit_allocas_unpoison function.
+
+   HWASAN needs to do very similar, the eventual pseudocode should be:
+      __hwasan_tag_memory (virtual_stack_dynamic_rtx,
+			   0,
+			   new_sp - sp);
+      __builtin_stack_restore (new_sp)
+
+   Need to use the same trick to handle STACK_DYNAMIC_OFFSET as described
+   above.  */
 
 static void
 handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p ()))
     return;
 
-  tree last_alloca = get_last_alloca_addr ();
   tree restored_stack = gimple_call_arg (call, 0);
-  tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
-  gimple *g = gimple_build_call (fn, 2, last_alloca, restored_stack);
-  gsi_insert_before (iter, g, GSI_SAME_STMT);
-  g = gimple_build_assign (last_alloca, restored_stack);
+
+  gimple *g;
+
+  if (hwasan_sanitize_allocas_p ())
+    {
+      enum internal_fn fn = IFN_HWASAN_ALLOCA_UNPOISON;
+      /* There is only one piece of information `expand_HWASAN_ALLOCA_UNPOISON`
+	 needs to work.  This is the length of the area that we're
+	 deallocating.  Since the stack pointer is known at expand time, the
+	 position of the new stack pointer after deallocation is enough
+	 information to calculate this length.  */
+      g = gimple_build_call_internal (fn, 1, restored_stack);
+    }
+  else
+    {
+      tree last_alloca = get_last_alloca_addr ();
+      tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
+      g = gimple_build_call (fn, 2, last_alloca, restored_stack);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      g = gimple_build_assign (last_alloca, restored_stack);
+    }
+
   gsi_insert_before (iter, g, GSI_SAME_STMT);
 }
 
@@ -673,14 +719,12 @@ handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
 static void
 handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
 {
-  if (!iter || !asan_sanitize_allocas_p ())
+  if (!iter
+      || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p ()))
     return;
 
   gassign *g;
   gcall *gg;
-  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
-
-  tree last_alloca = get_last_alloca_addr ();
   tree callee = gimple_call_fndecl (call);
   tree old_size = gimple_call_arg (call, 0);
   tree ptr_type = gimple_call_lhs (call) ? TREE_TYPE (gimple_call_lhs (call))
@@ -690,6 +734,68 @@ handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
     = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
       ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
 
+  if (hwasan_sanitize_allocas_p ())
+    {
+      gimple_seq stmts = NULL;
+      location_t loc = gimple_location (gsi_stmt (*iter));
+      /*
+	 HWASAN needs a different expansion.
+
+	 addr = __builtin_alloca (size, align);
+
+	 should be replaced by
+
+	 new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment;
+	 untagged_addr = __builtin_alloca (new_size, align);
+	 tag = __hwasan_choose_alloca_tag ();
+	 addr = ifn_HWASAN_SET_TAG (untagged_addr, tag);
+	 __hwasan_tag_memory (untagged_addr, tag, new_size);
+	*/
+      /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start on
+	 a tag granule.  */
+      align = align > HWASAN_TAG_GRANULE_SIZE ? align : HWASAN_TAG_GRANULE_SIZE;
+
+      tree old_size = gimple_call_arg (call, 0);
+      tree new_size = gimple_build_round_up (&stmts, loc, size_type_node,
+					     old_size,
+					     HWASAN_TAG_GRANULE_SIZE);
+
+      /* Make the alloca call */
+      tree untagged_addr
+	= gimple_build (&stmts, loc,
+			as_combined_fn (BUILT_IN_ALLOCA_WITH_ALIGN), ptr_type,
+			new_size, build_int_cst (size_type_node, align));
+
+      /* Choose the tag.
+	 Here we use an internal function so we can choose the tag at expand
+	 time.  We need the decision to be made after stack variables have been
+	 assigned their tag (i.e. once the hwasan_frame_tag_offset variable has
+	 been set to one after the last stack variables tag).  */
+      tree tag = gimple_build (&stmts, loc, CFN_HWASAN_CHOOSE_TAG,
+			       unsigned_char_type_node);
+
+      /* Add tag to pointer.  */
+      tree addr
+	= gimple_build (&stmts, loc, CFN_HWASAN_SET_TAG, ptr_type,
+			untagged_addr, tag);
+
+      /* Tag shadow memory.
+	 NOTE: require using `untagged_addr` here for libhwasan API.  */
+      gimple_build (&stmts, loc, as_combined_fn (BUILT_IN_HWASAN_TAG_MEM),
+		    void_type_node, untagged_addr, tag, new_size);
+
+      /* Insert the built up code sequence into the original instruction stream
+	 the iterator points to.  */
+      gsi_insert_seq_before (iter, stmts, GSI_SAME_STMT);
+
+      /* Finally, replace old alloca ptr with NEW_ALLOCA.  */
+      replace_call_with_value (iter, addr);
+      return;
+    }
+
+  tree last_alloca = get_last_alloca_addr ();
+  const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
+
   /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
      bytes of allocated space.  Otherwise, align alloca to ASAN_RED_ZONE_SIZE
      manually.  */
@@ -842,6 +948,31 @@ get_mem_refs_of_builtin_call (gcall *call,
       break;
 
     case BUILT_IN_STRLEN:
+      /* Special case strlen here since its length is taken from its return
+	 value.
+
+	 The approach taken by the sanitizers is to check a memory access
+	 before it's taken.  For ASAN strlen is intercepted by libasan, so no
+	 check is inserted by the compiler.
+
+	 This function still returns `true` and provides a length to the rest
+	 of the ASAN pass in order to record what areas have been checked,
+	 avoiding superfluous checks later on.
+
+	 HWASAN does not intercept any of these internal functions.
+	 This means that checks for memory accesses must be inserted by the
+	 compiler.
+	 strlen is a special case, because we can tell the length from the
+	 return of the function, but that is not known until after the function
+	 has returned.
+
+	 Hence we can't check the memory access before it happens.
+	 We could check the memory access after it has already happened, but
+	 for now we choose to just ignore `strlen` calls.
+	 This decision was simply made because that means the special case is
+	 limited to this one case of this one function.  */
+      if (hwasan_sanitize_p ())
+	return false;
       source0 = gimple_call_arg (call, 0);
       len = gimple_call_lhs (call);
       break;
@@ -1411,6 +1542,156 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+
+/* HWAddressSanitizer (hwasan) is a probabilistic method for detecting
+   out-of-bounds and use-after-free bugs.
+   Read more:
+   http://code.google.com/p/address-sanitizer/
+
+   Similar to AddressSanitizer (asan) it consists of two parts: the
+   instrumentation module in this file, and a run-time library.
+
+   The instrumentation module adds a run-time check before every memory insn in
+   the same manner as asan (see the block comment for AddressSanitizer above).
+   Currently, hwasan only adds out-of-line instrumentation, where each check is
+   implemented as a function call to the run-time library.  Hence a check for a
+   load of N bytes from address X would be implemented with a function call to
+   __hwasan_loadN(X), and checking a store of N bytes from address X would be
+   implemented with a function call to __hwasan_storeN(X).
+
+   The main difference between hwasan and asan is in the information stored to
+   help this checking.  Both sanitizers use a shadow memory area which stores
+   data recording the state of main memory at a corresponding address.
+
+   For hwasan, each 16 byte granule in main memory has a corresponding 1 byte
+   in shadow memory.  This shadow address can be calculated with equation:
+     (addr >> log_2(HWASAN_TAG_GRANULE_SIZE))
+	  + __hwasan_shadow_memory_dynamic_address;
+   The conversion between real and shadow memory for asan is given in the block
+   comment at the top of this file.
+   The description of how this shadow memory is laid out for asan is in the
+   block comment at the top of this file, here we describe how this shadow
+   memory is used for hwasan.
+
+   For hwasan, each variable is assigned a byte-sized 'tag'.  The extent of
+   the shadow memory for that variable is filled with the assigned tag, and
+   every pointer referencing that variable has its top byte set to the same
+   tag.  The run-time library redefines malloc so that every allocation returns
+   a tagged pointer and tags the corresponding shadow memory with the same tag.
+
+   On each pointer dereference the tag found in the pointer is compared to the
+   tag found in the shadow memory corresponding to the accessed memory address.
+   If these tags are found to differ then this memory access is judged to be
+   invalid and a report is generated.
+
+   This method of bug detection is not perfect -- it can not catch every bad
+   access -- but catches them probabilistically instead.  There is always the
+   possibility that an invalid memory access will happen to access memory
+   tagged with the same tag as the pointer that this access used.
+   The chances of this are approx. 0.4% for any two uncorrelated objects.
+
+   Random tag generation can mitigate this problem by decreasing the
+   probability that an invalid access will be missed in the same manner over
+   multiple runs.  i.e. if two objects are tagged the same in one run of the
+   binary they are unlikely to be tagged the same in the next run.
+   Both heap and stack allocated objects have random tags by default.
+
+   [16 byte granule implications]
+    Since the shadow memory only has a resolution on real memory of 16 bytes,
+    invalid accesses that are within the same 16 byte granule as a valid
+    address will not be caught.
+
+    There is a "short-granule" feature in the runtime library which does catch
+    such accesses, but this feature is not implemented for stack objects (since
+    stack objects are allocated and tagged by compiler instrumentation, and
+    this feature has not yet been implemented in GCC instrumentation).
+
+    Another outcome of this 16 byte resolution is that each tagged object must
+    be 16 byte aligned.  If two objects were to share any 16 byte granule in
+    memory, then they both would have to be given the same tag, and invalid
+    accesses to one using a pointer to the other would be undetectable.
+
+   [Compiler instrumentation]
+    Compiler instrumentation ensures that two adjacent buffers on the stack are
+    given different tags, this means an access to one buffer using a pointer
+    generated from the other (e.g. through buffer overrun) will have mismatched
+    tags and be caught by hwasan.
+
+    We don't randomly tag every object on the stack, since that would require
+    keeping many registers to record each tag.  Instead we randomly generate a
+    tag for each function frame, and each new stack object uses a tag offset
+    from that frame tag.
+    i.e. each object is tagged as RFT + offset, where RFT is the "random frame
+    tag" generated for this frame.
+    This means that randomisation does not peturb the difference between tags
+    on tagged stack objects within a frame, but this is mitigated by the fact
+    that objects with the same tag within a frame are very far apart
+    (approx. 2^HWASAN_TAG_SIZE objects apart).
+
+    As a demonstration, using the same example program as in the asan block
+    comment above:
+
+     int
+     foo ()
+     {
+       char a[23] = {0};
+       int b[2] = {0};
+
+       a[5] = 1;
+       b[1] = 2;
+
+       return a[5] + b[1];
+     }
+
+    On AArch64 the stack will be ordered as follows for the above function:
+
+    Slot 1/ [24 bytes for variable 'a']
+    Slot 2/ [8 bytes padding for alignment]
+    Slot 3/ [8 bytes for variable 'b']
+    Slot 4/ [8 bytes padding for alignment]
+
+    (The padding is there to ensure 16 byte alignment as described in the 16
+     byte granule implications).
+
+    While the shadow memory will be ordered as follows:
+
+    - 2 bytes (representing 32 bytes in real memory) tagged with RFT + 1.
+    - 1 byte (representing 16 bytes in real memory) tagged with RFT + 2.
+
+    And any pointer to "a" will have the tag RFT + 1, and any pointer to "b"
+    will have the tag RFT + 2.
+
+   [Top Byte Ignore requirements]
+    Hwasan requires the ability to store an 8 bit tag in every pointer.  There
+    is no instrumentation done to remove this tag from pointers before
+    dereferencing, which means the hardware must ignore this tag during memory
+    accesses.
+
+    Architectures where this feature is available should indicate this using
+    the TARGET_MEMTAG_CAN_TAG_ADDRESSES hook.
+
+   [Stack requires cleanup on unwinding]
+    During normal operation of a hwasan sanitized program more space in the
+    shadow memory becomes tagged as the stack grows.  As the stack shrinks this
+    shadow memory space must become untagged.  If it is not untagged then when
+    the stack grows again (during other function calls later on in the program)
+    objects on the stack that are usually not tagged (e.g. parameters passed on
+    the stack) can be placed in memory whose shadow space is tagged with
+    something else, and accesses can cause false positive reports.
+
+    Hence we place untagging code on every epilogue of functions which tag some
+    stack objects.
+
+    Moreover, the run-time library intercepts longjmp & setjmp to untag when
+    the stack is unwound this way.
+
+    C++ exceptions are not yet handled, which means this sanitizer can not
+    handle C++ code that throws exceptions -- it will give false positives
+    after an exception has been thrown.  The implementation that the hwasan
+    library has for handling these relies on the frame pointer being after any
+    local variables.  This is not generally the case for GCC.  */
+
+
 /* Returns whether we are tagging pointers and checking those tags on memory
    access.  */
 bool
@@ -1433,6 +1714,27 @@ hwasan_sanitize_allocas_p (void)
   return (hwasan_sanitize_stack_p () && param_hwasan_instrument_allocas);
 }
 
+/* Should we instrument reads?  */
+bool
+hwasan_instrument_reads (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_reads);
+}
+
+/* Should we instrument writes?  */
+bool
+hwasan_instrument_writes (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_writes);
+}
+
+/* Should we instrument builtin calls?  */
+bool
+hwasan_memintrin (void)
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_mem_intrinsics);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1928,6 +2230,8 @@ static tree
 report_error_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
 		   int *nargs)
 {
+  gcc_assert (!hwasan_sanitize_p ());
+
   static enum built_in_function report[2][2][6]
     = { { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
 	    BUILT_IN_ASAN_REPORT_LOAD4, BUILT_IN_ASAN_REPORT_LOAD8,
@@ -2220,6 +2524,7 @@ build_check_stmt (location_t loc, tree base, tree len,
   gimple *g;
 
   gcc_assert (!(size_in_bytes > 0 && !is_non_zero_len));
+  gcc_assert (size_in_bytes == -1 || size_in_bytes >= 1);
 
   gsi = *iter;
 
@@ -2264,7 +2569,11 @@ build_check_stmt (location_t loc, tree base, tree len,
   if (is_scalar_access)
     flags |= ASAN_CHECK_SCALAR_ACCESS;
 
-  g = gimple_build_call_internal (IFN_ASAN_CHECK, 4,
+  enum internal_fn fn = hwasan_sanitize_p ()
+    ? IFN_HWASAN_CHECK
+    : IFN_ASAN_CHECK;
+
+  g = gimple_build_call_internal (fn, 4,
 				  build_int_cst (integer_type_node, flags),
 				  base, len,
 				  build_int_cst (integer_type_node,
@@ -2288,9 +2597,9 @@ static void
 instrument_derefs (gimple_stmt_iterator *iter, tree t,
 		   location_t location, bool is_store)
 {
-  if (is_store && !param_asan_instrument_writes)
+  if (is_store && !(asan_instrument_writes () || hwasan_instrument_writes ()))
     return;
-  if (!is_store && !param_asan_instrument_reads)
+  if (!is_store && !(asan_instrument_reads () || hwasan_instrument_reads ()))
     return;
 
   tree type, base;
@@ -2351,7 +2660,12 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
     {
       if (DECL_THREAD_LOCAL_P (inner))
 	return;
-      if (!param_asan_globals && is_global_var (inner))
+      /* If we're not sanitizing globals and we can tell statically that this
+	 access is inside a global variable, then there's no point adding
+	 instrumentation to check the access.  N.b. hwasan currently never
+	 sanitizes globals.  */
+      if ((hwasan_sanitize_p () || !param_asan_globals)
+	  && is_global_var (inner))
         return;
       if (!TREE_STATIC (inner))
 	{
@@ -2444,7 +2758,7 @@ instrument_mem_region_access (tree base, tree len,
 static bool
 instrument_builtin_call (gimple_stmt_iterator *iter)
 {
-  if (!param_asan_memintrin)
+  if (!(asan_memintrin () || hwasan_memintrin ()))
     return false;
 
   bool iter_advanced_p = false;
@@ -2580,10 +2894,27 @@ maybe_instrument_call (gimple_stmt_iterator *iter)
 	      break;
 	    }
 	}
-      tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
-      gimple *g = gimple_build_call (decl, 0);
-      gimple_set_location (g, gimple_location (stmt));
-      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      /* If a function does not return, then we must handle clearing up the
+	 shadow stack accordingly.  For ASAN we can simply set the entire stack
+	 to "valid" for accesses by setting the shadow space to 0 and all
+	 accesses will pass checks.  That means that some bad accesses may be
+	 missed, but we will not report any false positives.
+
+	 This is not possible for HWASAN.  Since there is no "always valid" tag
+	 we can not set any space to "always valid".  If we were to clear the
+	 entire shadow stack then code resuming from `longjmp` or a caught
+	 exception would trigger false positives when correctly accessing
+	 variables on the stack.  Hence we need to handle things like
+	 `longjmp`, thread exit, and exceptions in a different way.  These
+	 problems must be handled externally to the compiler, e.g. in the
+	 language runtime.  */
+      if (! hwasan_sanitize_p ())
+	{
+	  tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
+	  gimple *g = gimple_build_call (decl, 0);
+	  gimple_set_location (g, gimple_location (stmt));
+	  gsi_insert_before (iter, g, GSI_SAME_STMT);
+	}
     }
 
   bool instrumented = false;
@@ -2982,6 +3313,9 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_PTR_CONST_PTR_UINT8
+    = build_function_type_list (ptr_type_node, const_ptr_type_node,
+				unsigned_char_type_node, NULL_TREE);
   tree BT_FN_VOID_PTR_UINT8_PTRMODE
     = build_function_type_list (void_type_node, ptr_type_node,
 				unsigned_char_type_node,
@@ -3305,6 +3639,31 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 
   gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
 
+  if (hwasan_sanitize_p ())
+    {
+      gcc_assert (param_hwasan_instrument_stack);
+      gimple_seq stmts = NULL;
+      /* Here we swap ASAN_MARK calls for HWASAN_MARK.
+	 This is because we are using the approach of using ASAN_MARK as a
+	 synonym until here.
+	 That approach means we don't yet have to duplicate all the special
+	 cases for ASAN_MARK and ASAN_POISON with the exact same handling but
+	 called HWASAN_MARK etc.
+
+	 N.b. __asan_poison_stack_memory (which implements ASAN_MARK for ASAN)
+	 rounds the size up to its shadow memory granularity, while
+	 __hwasan_tag_memory (which implements the same for HWASAN) does not.
+	 Hence we emit HWASAN_MARK with an aligned size unlike ASAN_MARK.  */
+      tree len = gimple_call_arg (g, 2);
+      tree new_len = gimple_build_round_up (&stmts, loc, size_type_node, len,
+					    HWASAN_TAG_GRANULE_SIZE);
+      gimple_build (&stmts, loc, CFN_HWASAN_MARK,
+		    void_type_node, gimple_call_arg (g, 0),
+		    base, new_len);
+      gsi_replace_with_seq (iter, stmts, true);
+      return false;
+    }
+
   if (is_poison)
     {
       if (asan_handled_variables == NULL)
@@ -3379,6 +3738,7 @@ asan_expand_mark_ifn (gimple_stmt_iterator *iter)
 bool
 asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
 {
+  gcc_assert (!hwasan_sanitize_p ());
   gimple *g = gsi_stmt (*iter);
   location_t loc = gimple_location (g);
   bool recover_p;
@@ -3652,11 +4012,61 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
 
       int nargs;
       bool store_p = gimple_call_internal_p (use, IFN_ASAN_POISON_USE);
-      tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
-				    &nargs);
-
-      gcall *call = gimple_build_call (fun, 1,
-				       build_fold_addr_expr (shadow_var));
+      gcall *call;
+      if (hwasan_sanitize_p ())
+	{
+	  tree fun = builtin_decl_implicit (BUILT_IN_HWASAN_TAG_MISMATCH4);
+	  /* NOTE: hwasan has no __hwasan_report_* functions like asan does.
+		We use __hwasan_tag_mismatch4 with arguments that tell it the
+		size of access and load to report all tag mismatches.
+
+		The arguments to this function are:
+		  Address of invalid access.
+		  Bitfield containing information about the access
+		    (access_info)
+		  Pointer to a frame of registers
+		    (for use in printing the contents of registers in a dump)
+		    Not used yet -- to be used by inline instrumentation.
+		  Size of access.
+
+		The access_info bitfield encodes the following pieces of
+		information:
+		  - Is this a store or load?
+		    access_info & 0x10  =>  store
+		  - Should the program continue after reporting the error?
+		    access_info & 0x20  =>  recover
+		  - What size access is this (not used here since we can always
+		    pass the size in the last argument)
+
+		    if (access_info & 0xf == 0xf)
+		      size is taken from last argument.
+		    else
+		      size == 1 << (access_info & 0xf)
+
+		The last argument contains the size of the access iff the
+		access_info size indicator is 0xf (we always use this argument
+		rather than storing the size in the access_info bitfield).
+
+		See the function definition `__hwasan_tag_mismatch4` in
+		libsanitizer/hwasan for the full definition.
+		*/
+	  unsigned access_info = (0x20 * recover_p)
+	    + (0x10 * store_p)
+	    + (0xf);
+	  call = gimple_build_call (fun, 4,
+				    build_fold_addr_expr (shadow_var),
+				    build_int_cst (pointer_sized_int_node,
+						   access_info),
+				    build_int_cst (pointer_sized_int_node, 0),
+				    size);
+	}
+      else
+	{
+	  tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
+					&nargs);
+	  call = gimple_build_call (fun, 1,
+				    build_fold_addr_expr (shadow_var));
+	}
       gimple_set_location (call, gimple_location (use));
       gimple *call_to_insert = call;
 
@@ -3704,6 +4114,12 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
 static unsigned int
 asan_instrument (void)
 {
+  if (hwasan_sanitize_p ())
+    {
+      transform_statements ();
+      return 0;
+    }
+
   if (shadow_ptr_types[0] == NULL_TREE)
     asan_init_shadow_ptr_types ();
   transform_statements ();
@@ -3741,7 +4157,7 @@ public:
 
   /* opt_pass methods: */
   opt_pass * clone () { return new pass_asan (m_ctxt); }
-  virtual bool gate (function *) { return gate_asan (); }
+  virtual bool gate (function *) { return gate_asan () || gate_hwasan (); }
   virtual unsigned int execute (function *) { return asan_instrument (); }
 
 }; // class pass_asan
@@ -3777,7 +4193,10 @@ public:
   {}
 
   /* opt_pass methods: */
-  virtual bool gate (function *) { return !optimize && gate_asan (); }
+  virtual bool gate (function *)
+    {
+      return !optimize && (gate_asan () || gate_hwasan ());
+    }
   virtual unsigned int execute (function *) { return asan_instrument (); }
 
 }; // class pass_asan_O0
@@ -3790,6 +4209,8 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/*  HWASAN  */
+
 /* For stack tagging:
 
    Return the offset from the frame base tag that the "next" expanded object
@@ -4133,4 +4554,139 @@ hwasan_truncate_to_tag_size (rtx tag, rtx target)
   return tag;
 }
 
+/* Construct a function tree for __hwasan_{load,store}{1,2,4,8,16,_n}.
+   IS_STORE is either 1 (for a store) or 0 (for a load).  */
+static combined_fn
+hwasan_check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
+		   int *nargs)
+{
+  static enum built_in_function check[2][2][6]
+    = { { { BUILT_IN_HWASAN_LOAD1, BUILT_IN_HWASAN_LOAD2,
+	    BUILT_IN_HWASAN_LOAD4, BUILT_IN_HWASAN_LOAD8,
+	    BUILT_IN_HWASAN_LOAD16, BUILT_IN_HWASAN_LOADN },
+	  { BUILT_IN_HWASAN_STORE1, BUILT_IN_HWASAN_STORE2,
+	    BUILT_IN_HWASAN_STORE4, BUILT_IN_HWASAN_STORE8,
+	    BUILT_IN_HWASAN_STORE16, BUILT_IN_HWASAN_STOREN } },
+	{ { BUILT_IN_HWASAN_LOAD1_NOABORT,
+	    BUILT_IN_HWASAN_LOAD2_NOABORT,
+	    BUILT_IN_HWASAN_LOAD4_NOABORT,
+	    BUILT_IN_HWASAN_LOAD8_NOABORT,
+	    BUILT_IN_HWASAN_LOAD16_NOABORT,
+	    BUILT_IN_HWASAN_LOADN_NOABORT },
+	  { BUILT_IN_HWASAN_STORE1_NOABORT,
+	    BUILT_IN_HWASAN_STORE2_NOABORT,
+	    BUILT_IN_HWASAN_STORE4_NOABORT,
+	    BUILT_IN_HWASAN_STORE8_NOABORT,
+	    BUILT_IN_HWASAN_STORE16_NOABORT,
+	    BUILT_IN_HWASAN_STOREN_NOABORT } } };
+  if (size_in_bytes == -1)
+    {
+      *nargs = 2;
+      return as_combined_fn (check[recover_p][is_store][5]);
+    }
+  *nargs = 1;
+  int size_log2 = exact_log2 (size_in_bytes);
+  gcc_assert (size_log2 >= 0 && size_log2 <= 5);
+  return as_combined_fn (check[recover_p][is_store][size_log2]);
+}
+
+/* Expand the HWASAN_{LOAD,STORE} builtins.  */
+bool
+hwasan_expand_check_ifn (gimple_stmt_iterator *iter, bool)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  bool recover_p;
+  if (flag_sanitize & SANITIZE_USER_HWADDRESS)
+    recover_p = (flag_sanitize_recover & SANITIZE_USER_HWADDRESS) != 0;
+  else
+    recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_HWADDRESS) != 0;
+
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_CHECK_LAST);
+  bool is_scalar_access = (flags & ASAN_CHECK_SCALAR_ACCESS) != 0;
+  bool is_store = (flags & ASAN_CHECK_STORE) != 0;
+  bool is_non_zero_len = (flags & ASAN_CHECK_NON_ZERO_LEN) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  tree len = gimple_call_arg (g, 2);
+
+  /* `align` is unused for HWASAN_CHECK, but we pass the argument anyway
+     since that way the arguments match ASAN_CHECK.  */
+  /* HOST_WIDE_INT align = tree_to_shwi (gimple_call_arg (g, 3));  */
+
+  unsigned HOST_WIDE_INT size_in_bytes
+    = is_scalar_access ? tree_to_shwi (len) : -1;
+
+  gimple_stmt_iterator gsi = *iter;
+
+  if (!is_non_zero_len)
+    {
+      /* So, the length of the memory area to hwasan-protect is
+	 non-constant.  Let's guard the generated instrumentation code
+	 like:
+
+	 if (len != 0)
+	   {
+	     // hwasan instrumentation code goes here.
+	   }
+	 // falltrough instructions, starting with *ITER.  */
+
+      g = gimple_build_cond (NE_EXPR,
+			    len,
+			    build_int_cst (TREE_TYPE (len), 0),
+			    NULL_TREE, NULL_TREE);
+      gimple_set_location (g, loc);
+
+      basic_block then_bb, fallthrough_bb;
+      insert_if_then_before_iter (as_a <gcond *> (g), iter,
+				  /*then_more_likely_p=*/true,
+				  &then_bb, &fallthrough_bb);
+      /* Note that fallthrough_bb starts with the statement that was
+	pointed to by ITER.  */
+
+      /* The 'then block' of the 'if (len != 0) condition is where
+	we'll generate the hwasan instrumentation code now.  */
+      gsi = gsi_last_bb (then_bb);
+    }
+
+  gimple_seq stmts = NULL;
+  tree base_addr = gimple_build (&stmts, loc, NOP_EXPR,
+				 pointer_sized_int_node, base);
+
+  int nargs = 0;
+  combined_fn fn
+    = hwasan_check_func (is_store, recover_p, size_in_bytes, &nargs);
+  if (nargs == 1)
+    gimple_build (&stmts, loc, fn, void_type_node, base_addr);
+  else
+    {
+      gcc_assert (nargs == 2);
+      tree sz_arg = gimple_build (&stmts, loc, NOP_EXPR,
+				  pointer_sized_int_node, len);
+      gimple_build (&stmts, loc, fn, void_type_node, base_addr, sz_arg);
+    }
+
+  gsi_insert_seq_after (&gsi, stmts, GSI_NEW_STMT);
+  gsi_remove (iter, true);
+  *iter = gsi;
+  return false;
+}
+
+/* For stack tagging:
+
+   Dummy: the HWASAN_MARK internal function should only ever be in the code
+   after the sanopt pass.  */
+bool
+hwasan_expand_mark_ifn (gimple_stmt_iterator *)
+{
+  gcc_unreachable ();
+}
+
+bool
+gate_hwasan ()
+{
+  return hwasan_sanitize_p ();
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index 1ad6657da45cc4976532e1b8bc233f67d8da9ccf..770f3575cee2c6a4a5cfb16354bd8ba2d65f574e 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -503,6 +503,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_INT_FEXCEPT_T_PTR_INT, BT_INT, BT_FEXCEPT_T_PTR,
 		     BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_FEXCEPT_T_PTR_INT, BT_INT,
 		     BT_CONST_FEXCEPT_T_PTR, BT_INT)
+DEF_FUNCTION_TYPE_2 (BT_FN_PTR_CONST_PTR_UINT8, BT_PTR, BT_CONST_PTR, BT_UINT8)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR)
 
diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h
index 0ed1d1ffe8365142cf1478298e8afe269e966eae..a131e364f9a44a216c5752c6b58ef589d5bb5c35 100644
--- a/gcc/gimple-fold.h
+++ b/gcc/gimple-fold.h
@@ -90,6 +90,12 @@ gimple_build (gimple_seq *seq,
 {
   return gimple_build (seq, UNKNOWN_LOCATION, code, type, op0, op1, op2);
 }
+extern tree gimple_build (gimple_seq *, location_t, combined_fn, tree);
+inline tree
+gimple_build (gimple_seq *seq, combined_fn fn, tree type)
+{
+  return gimple_build (seq, UNKNOWN_LOCATION, fn, type);
+}
 extern tree gimple_build (gimple_seq *, location_t, combined_fn, tree, tree);
 inline tree
 gimple_build (gimple_seq *seq, combined_fn fn, tree type, tree arg0)
@@ -144,6 +150,15 @@ gimple_build_vector (gimple_seq *seq, tree_vector_builder *builder)
   return gimple_build_vector (seq, UNKNOWN_LOCATION, builder);
 }
 
+extern tree gimple_build_round_up (gimple_seq *, location_t, tree, tree,
+				   unsigned HOST_WIDE_INT);
+inline tree
+gimple_build_round_up (gimple_seq *seq, tree type, tree old_size,
+		       unsigned HOST_WIDE_INT align)
+{
+  return gimple_build_round_up (seq, UNKNOWN_LOCATION, type, old_size, align);
+}
+
 extern bool gimple_stmt_nonnegative_warnv_p (gimple *, bool *, int = 0);
 extern bool gimple_stmt_integer_valued_real_p (gimple *, int = 0);
 
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index ca38a31c3c21ea28d9050c03e64312843e9fa2f7..6a67332a366011b51d315bbc0f67b021b1942210 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -7688,6 +7688,32 @@ gimple_build (gimple_seq *seq, location_t loc,
   return res;
 }
 
+/* Build the call FN () with a result of type TYPE (or no result if TYPE is
+   void) with a location LOC.  Returns the built expression value (or NULL_TREE
+   if TYPE is void) and appends statements possibly defining it to SEQ.  */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc, combined_fn fn, tree type)
+{
+  tree res = NULL_TREE;
+  gcall *stmt;
+  if (internal_fn_p (fn))
+    stmt = gimple_build_call_internal (as_internal_fn (fn), 0);
+  else
+    {
+      tree decl = builtin_decl_implicit (as_builtin_fn (fn));
+      stmt = gimple_build_call (decl, 0);
+    }
+  if (!VOID_TYPE_P (type))
+    {
+      res = create_tmp_reg_or_ssa_name (type);
+      gimple_call_set_lhs (stmt, res);
+    }
+  gimple_set_location (stmt, loc);
+  gimple_seq_add_stmt_without_update (seq, stmt);
+  return res;
+}
+
 /* Build the call FN (ARG0) with a result of type TYPE
    (or no result if TYPE is void) with location LOC,
    simplifying it first if possible.  Returns the built
@@ -7877,6 +7903,26 @@ gimple_build_vector (gimple_seq *seq, location_t loc,
   return builder->build ();
 }
 
+/* Emit gimple statements into &stmts that take a value given in `old_size`
+   and generate a value guaranteed to be rounded upwards to `align`.
+
+   Return the tree node representing this size, it is of TREE_TYPE `type`.  */
+
+tree
+gimple_build_round_up (gimple_seq *seq, location_t loc, tree type,
+		       tree old_size, unsigned HOST_WIDE_INT align)
+{
+  unsigned HOST_WIDE_INT tg_mask = align - 1;
+  /* tree new_size = (old_size + tg_mask) & ~tg_mask;  */
+  gcc_assert (INTEGRAL_TYPE_P (type));
+  tree tree_mask = build_int_cst (type, tg_mask);
+  tree oversize = gimple_build (seq, loc, PLUS_EXPR, type, old_size,
+				tree_mask);
+
+  tree mask = build_int_cst (type, -align);
+  return gimple_build (seq, loc, BIT_AND_EXPR, type, oversize, mask);
+}
+
 /* Return true if the result of assignment STMT is known to be non-negative.
    If the return value is based on the assumption that signed overflow is
    undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index d97a231e7e8333c1b32a39d03851b974dc55c1f7..075d6e5208a392956436bf92cb88a9e3378e5216 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -753,6 +753,7 @@ dump_gimple_call_args (pretty_printer *buffer, const gcall *gs,
 	  limit = ARRAY_SIZE (reduction_args);
 	  break;
 
+	case IFN_HWASAN_MARK:
 	case IFN_ASAN_MARK:
 #define DEF(X) #X
 	  static const char *const asan_mark_args[] = {IFN_ASAN_MARK_FLAGS};
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 2566ec7f0af8dc844901ad0bda2a73fdb4383b4d..7b7c5e348893b7249638ff9c5a04d16af7498698 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1237,8 +1237,11 @@ asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
 
   /* It's necessary to have all stack variables aligned to ASAN granularity
      bytes.  */
-  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
-    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+  gcc_assert (!hwasan_sanitize_p () || hwasan_sanitize_stack_p ());
+  unsigned shadow_granularity
+    = hwasan_sanitize_p () ? HWASAN_TAG_GRANULE_SIZE : ASAN_SHADOW_GRANULARITY;
+  if (DECL_ALIGN_UNIT (decl) <= shadow_granularity)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * shadow_granularity);
 
   HOST_WIDE_INT flags = poison ? ASAN_MARK_POISON : ASAN_MARK_UNPOISON;
 
@@ -15365,7 +15368,7 @@ gimplify_function_tree (tree fndecl)
      if necessary.  */
   cfun->curr_properties |= PROP_gimple_lva;
 
-  if (asan_sanitize_use_after_scope () && sanitize_flags_p (SANITIZE_ADDRESS))
+  if (asan_sanitize_use_after_scope ())
     asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
   if (asan_poisoned_variables)
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 792d2ca568a4a11c3e3ad8abf6443f173b0650a7..87e73170feb57efdfd395790850160c116bf5169 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -469,6 +469,111 @@ expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
+expand_HWASAN_CHECK (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
+
+/* For hwasan stack tagging:
+   Clear tags on the dynamically allocated space.
+   For use after an object dynamically allocated on the stack goes out of
+   scope.  */
+static void
+expand_HWASAN_ALLOCA_UNPOISON (internal_fn, gcall *gc)
+{
+  gcc_assert (Pmode == ptr_mode);
+  tree restored_position = gimple_call_arg (gc, 0);
+  rtx restored_rtx = expand_expr (restored_position, NULL_RTX, VOIDmode,
+				  EXPAND_NORMAL);
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  rtx off = expand_simple_binop (Pmode, MINUS, restored_rtx,
+				 stack_pointer_rtx, NULL_RTX, 0,
+				 OPTAB_WIDEN);
+  emit_library_call_value (func, NULL_RTX, LCT_NORMAL, VOIDmode,
+			   virtual_stack_dynamic_rtx, Pmode,
+			   HWASAN_STACK_BACKGROUND, QImode,
+			   off, Pmode);
+}
+
+/* For hwasan stack tagging:
+   Return a tag to be used for a dynamic allocation.  */
+static void
+expand_HWASAN_CHOOSE_TAG (internal_fn, gcall *gc)
+{
+  tree tag = gimple_call_lhs (gc);
+  rtx target = expand_expr (tag, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  machine_mode mode = GET_MODE (target);
+  gcc_assert (mode == QImode);
+
+  rtx base_tag = targetm.memtag.extract_tag (hwasan_frame_base (), NULL_RTX);
+  gcc_assert (base_tag);
+  rtx tag_offset = gen_int_mode (hwasan_current_frame_tag (), QImode);
+  rtx chosen_tag = expand_simple_binop (QImode, PLUS, base_tag, tag_offset,
+					target, /* unsignedp = */1,
+					OPTAB_WIDEN);
+  chosen_tag = hwasan_truncate_to_tag_size (chosen_tag, target);
+
+  /* Really need to put the tag into the `target` RTX.  */
+  if (chosen_tag != target)
+    {
+      rtx temp = chosen_tag;
+      gcc_assert (GET_MODE (chosen_tag) == mode);
+      emit_move_insn (target, temp);
+    }
+
+  hwasan_increment_frame_tag ();
+}
+
+/* For hwasan stack tagging:
+   Tag a region of space in the shadow stack according to the base pointer of
+   an object on the stack.  N.b. the length provided in the internal call is
+   required to be aligned to HWASAN_TAG_GRANULE_SIZE.  */
+static void
+expand_HWASAN_MARK (internal_fn, gcall *gc)
+{
+  gcc_assert (ptr_mode == Pmode);
+  HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
+  bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
+
+  tree base = gimple_call_arg (gc, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  rtx base_rtx = expand_normal (base);
+
+  rtx tag = is_poison ? HWASAN_STACK_BACKGROUND
+    : targetm.memtag.extract_tag (base_rtx, NULL_RTX);
+  rtx address = targetm.memtag.untagged_pointer (base_rtx, NULL_RTX);
+
+  tree len = gimple_call_arg (gc, 2);
+  rtx r_len = expand_normal (len);
+
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (func, LCT_NORMAL, VOIDmode, address, Pmode,
+		     tag, QImode, r_len, Pmode);
+}
+
+/* For hwasan stack tagging:
+   Store a tag into a pointer.  */
+static void
+expand_HWASAN_SET_TAG (internal_fn, gcall *gc)
+{
+  gcc_assert (ptr_mode == Pmode);
+  tree g_target = gimple_call_lhs (gc);
+  tree g_ptr = gimple_call_arg (gc, 0);
+  tree g_tag = gimple_call_arg (gc, 1);
+
+  rtx ptr = expand_normal (g_ptr);
+  rtx tag = expand_expr (g_tag, NULL_RTX, QImode, EXPAND_NORMAL);
+  rtx target = expand_normal (g_target);
+
+  rtx untagged = targetm.memtag.untagged_pointer (ptr, target);
+  rtx tagged_value = targetm.memtag.set_tag (untagged, tag, target);
+  if (tagged_value != target)
+    emit_move_insn (target, tagged_value);
+}
+
+/* This should get expanded in the sanopt pass.  */
+
+static void
 expand_ASAN_CHECK (internal_fn, gcall *)
 {
   gcc_unreachable ();
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 310d37aa53819791b5df1683afca831f08e5892a..91a7bfea3eecad747320afcd11e05eedb9f7fcbf 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -321,6 +321,13 @@ DEF_INTERNAL_FN (UBSAN_PTR, ECF_LEAF | ECF_NOTHROW, ". R . ")
 DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
 DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (HWASAN_ALLOCA_UNPOISON, ECF_LEAF | ECF_NOTHROW, ". R ")
+DEF_INTERNAL_FN (HWASAN_CHOOSE_TAG, ECF_LEAF | ECF_NOTHROW, ". ")
+DEF_INTERNAL_FN (HWASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW,
+		 ". . R . . ")
+DEF_INTERNAL_FN (HWASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (HWASAN_SET_TAG,
+		 ECF_TM_PURE | ECF_PURE | ECF_LEAF | ECF_NOTHROW, ". R R ")
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW,
 		 ". . R . . ")
 DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, NULL)
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 4f854fb994229fd4ed91d3b5cff7c7acff9a55bc..f02731e8f2b5c7e78000d768e7c472658883ccb4 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -183,6 +183,61 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 /* Hardware Address Sanitizer.  */
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1, "__hwasan_load1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2, "__hwasan_load2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4, "__hwasan_load4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8, "__hwasan_load8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16, "__hwasan_load16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN, "__hwasan_loadN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1, "__hwasan_store1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2, "__hwasan_store2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4, "__hwasan_store4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8, "__hwasan_store8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16, "__hwasan_store16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN, "__hwasan_storeN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD1_NOABORT, "__hwasan_load1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD2_NOABORT, "__hwasan_load2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD4_NOABORT, "__hwasan_load4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD8_NOABORT, "__hwasan_load8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOAD16_NOABORT, "__hwasan_load16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_LOADN_NOABORT, "__hwasan_loadN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE1_NOABORT, "__hwasan_store1_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE2_NOABORT, "__hwasan_store2_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE4_NOABORT, "__hwasan_store4_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE8_NOABORT, "__hwasan_store8_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STORE16_NOABORT,
+		      "__hwasan_store16_noabort",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_STOREN_NOABORT, "__hwasan_storeN_noabort",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MISMATCH4, "__hwasan_tag_mismatch4",
+		      BT_FN_VOID_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_HANDLE_LONGJMP, "__hwasan_handle_longjmp",
+		      BT_FN_VOID_CONST_PTR, ATTR_NOTHROW_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_PTR, "__hwasan_tag_pointer",
+		      BT_FN_PTR_CONST_PTR_UINT8, ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
 		      BT_FN_VOID_PTR_UINT8_PTRMODE, ATTR_NOTHROW_LIST)
 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 6c3bce923785f8a2dcd324edc0ce7ceb5f633a0c..965ab367c299f9315cddeb968d1c75086c8762b4 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -776,7 +776,8 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
   basic_block son;
   gimple_stmt_iterator gsi;
   sanopt_info *info = (sanopt_info *) bb->aux;
-  bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0;
+  bool asan_check_optimize
+    = ((flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS)) != 0);
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
@@ -806,6 +807,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
       if (asan_check_optimize
 	  && gimple_call_builtin_p (stmt, BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT))
 	{
+	  gcc_assert (!hwasan_sanitize_p ());
 	  use_operand_p use;
 	  gimple *use_stmt;
 	  if (single_imm_use (gimple_vdef (stmt), &use, &use_stmt))
@@ -834,6 +836,7 @@ sanopt_optimize_walker (basic_block bb, class sanopt_ctx *ctx)
 	  case IFN_UBSAN_PTR:
 	    remove = maybe_optimize_ubsan_ptr_ifn (ctx, stmt);
 	    break;
+	  case IFN_HWASAN_CHECK:
 	  case IFN_ASAN_CHECK:
 	    if (asan_check_optimize)
 	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
@@ -1262,6 +1265,10 @@ sanitize_rewrite_addressable_params (function *fun)
 unsigned int
 pass_sanopt::execute (function *fun)
 {
+  /* n.b. ASAN_MARK is used for both HWASAN and ASAN.
+     asan_num_accesses is hence used to count either HWASAN_CHECK or ASAN_CHECK
+     stuff.  This is fine because you can only have one of these active at a
+     time.  */
   basic_block bb;
   int asan_num_accesses = 0;
   bool contains_asan_mark = false;
@@ -1269,10 +1276,10 @@ pass_sanopt::execute (function *fun)
   /* Try to remove redundant checks.  */
   if (optimize
       && (flag_sanitize
-	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT
+	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_HWADDRESS
 	     | SANITIZE_ADDRESS | SANITIZE_VPTR | SANITIZE_POINTER_OVERFLOW)))
     asan_num_accesses = sanopt_optimize (fun, &contains_asan_mark);
-  else if (flag_sanitize & SANITIZE_ADDRESS)
+  else if (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS))
     {
       gimple_stmt_iterator gsi;
       FOR_EACH_BB_FN (bb, fun)
@@ -1292,7 +1299,7 @@ pass_sanopt::execute (function *fun)
       sanitize_asan_mark_poison ();
     }
 
-  if (asan_sanitize_stack_p ())
+  if (asan_sanitize_stack_p () || hwasan_sanitize_stack_p ())
     sanitize_rewrite_addressable_params (fun);
 
   bool use_calls = param_asan_instrumentation_with_call_threshold < INT_MAX
@@ -1334,6 +1341,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_UBSAN_VPTR:
 		  no_next = ubsan_expand_vptr_ifn (&gsi);
 		  break;
+		case IFN_HWASAN_CHECK:
+		  no_next = hwasan_expand_check_ifn (&gsi, use_calls);
+		  break;
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
@@ -1345,6 +1355,9 @@ pass_sanopt::execute (function *fun)
 						    &need_commit_edge_insert,
 						    shadow_vars_mapping);
 		  break;
+		case IFN_HWASAN_MARK:
+		  no_next = hwasan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 9938b6afbd4fa22898dbc3c29b92061a71810b08..cb4ae77fccb44769732f9677a65c96be6321c9fa 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -512,7 +512,7 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
-      if (flag_sanitize & SANITIZE_HWADDRESS)
+      if (gate_hwasan ())
 	hwasan_finish_file ();
 
       omp_finish_file ();


[-- Attachment #2: hwasan-6-v7.patch.gz --]
[-- Type: application/gzip, Size: 14845 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Update [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes
  2020-11-20 17:28           ` Matthew Malcomson
@ 2020-11-20 18:09             ` Richard Sandiford
  0 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-11-20 18:09 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: Richard Earnshaw, Kyrylo Tkachov, gcc-patches, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> @@ -7877,6 +7903,26 @@ gimple_build_vector (gimple_seq *seq, location_t loc,
>    return builder->build ();
>  }
>  
> +/* Emit gimple statements into &stmts that take a value given in `old_size`
> +   and generate a value guaranteed to be rounded upwards to `align`.
> +
> +   Return the tree node representing this size, it is of TREE_TYPE `type`.  */

Nit, but: the usual way of referring to parameter names is to use caps
(OLD_SIZE, ALIGN, TYPE) rather than backticks.  I don't think it's
necessary to change the hwasan-specific code to follow that style,
since what you have is self-consistent and readable as-is (although
changing it would be fine too if you prefer).  But since the surrounding
code consistently follows the caps style, I think it would be better to
use it here too.

OK with that change, thanks.

> +
> +tree
> +gimple_build_round_up (gimple_seq *seq, location_t loc, tree type,
> +		       tree old_size, unsigned HOST_WIDE_INT align)
> +{
> +  unsigned HOST_WIDE_INT tg_mask = align - 1;
> +  /* tree new_size = (old_size + tg_mask) & ~tg_mask;  */
> +  gcc_assert (INTEGRAL_TYPE_P (type));
> +  tree tree_mask = build_int_cst (type, tg_mask);
> +  tree oversize = gimple_build (seq, loc, PLUS_EXPR, type, old_size,
> +				tree_mask);
> +
> +  tree mask = build_int_cst (type, -align);
> +  return gimple_build (seq, loc, BIT_AND_EXPR, type, oversize, mask);
> +}
> +
>  /* Return true if the result of assignment STMT is known to be non-negative.
>     If the return value is based on the assumption that signed overflow is
>     undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change

Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Document --with-build-config=bootstrap-asan option.
  2020-01-13 10:52         ` Matthew Malcomson
@ 2020-11-20 18:11           ` Matthew Malcomson
  2020-11-22 22:49             ` Gerald Pfeifer
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-20 18:11 UTC (permalink / raw)
  To: Gerald Pfeifer
  Cc: gcc-patches, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	Joseph S. Myers, Richard Sandiford

On 13/01/2020 10:40, Matthew Malcomson wrote:
> On 11/01/2020 07:19, Gerald Pfeifer wrote:
>> On Thu, 12 Dec 2019, Matthew Malcomson wrote:
>>> gcc/ChangeLog:
>>>
>>> 2019-12-12  Matthew Malcomson  <matthew.malcomson@arm.com>
>>>
>>> 	* doc/install.texi: Document bootstrap-asan configuration option.
>>
>> I see this introduces a new table.
>>
>>> +Some examples of build configurations designed for developers of GCC are:
>>
>> @samp{bootstrap-time}, @samp{bootstrap-debug-ckovw} and others appear
>> to fall into the same camp, essentially expected to be used by maintainers
>> only.
>>
>> Would it make sense to add your new option to the existing table, or
>> perhaps see which other options from the existing table to move into
>> your new one?  Thoughts?
> 
> Sounds good me.
> 
> 
>>
>>
>> The patch is okay modulo the question above.


Hi,

Apologies for bringing up something from this far back, but I've just 
noticed I never actually committed this patch.

Did the above line mean approval once the option was moved into the 
existing table?  Or was it just explaining that the position was the 
only problem you saw?

In other words: is the patch I proposed below Ok for trunk?

Thanks,
Matthew

>>
>> Thanks,
>> Gerald
>>
> 
> Patch with above suggestion.
> 
> #####################
> 
> diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
> index
> 80b47812fe66a8ef50edf3aad9708ab3409ba7dc..0705759c69f64c6d06e91f7ae83bb8c1ad210f34
> 100644
> --- a/gcc/doc/install.texi
> +++ b/gcc/doc/install.texi
> @@ -2668,6 +2668,10 @@ Arranges for the run time of each program started
> by the GCC driver,
>    built in any stage, to be logged to @file{time.log}, in the top level of
>    the build tree.
> 
> +@item @samp{bootstrap-asan}
> +Compiles GCC itself using Address Sanitization in order to catch
> invalid memory
> +accesses within the GCC code.
> +
>    @end table
> 
>    @section Building a cross compiler
> 


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [Patch 0/X] HWASAN v4
  2020-11-13 17:22             ` Martin Liška
@ 2020-11-20 18:42               ` Matthew Malcomson
  2020-11-23  8:18                 ` Martin Liška
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-20 18:42 UTC (permalink / raw)
  To: Martin Liška, gcc-patches
  Cc: jakub, Richard Earnshaw, kcc, dodji, joseph

On 13/11/2020 17:22, Martin Liška wrote:
> On 11/13/20 5:57 PM, Matthew Malcomson wrote:
>> Hi there,
>>
>> Thanks for the heads-up.
>> As it turns out the most recent `libhwasan` crashes when displaying an 
>> address on the stack in Linux.
> 
> Hello.
> 
> What a bad luck.
> 
>>
>> I'm currently working on getting it fixed here 
>> https://reviews.llvm.org/D91344#2393371 
>> <https://reviews.llvm.org/D91344#2393371> .
>> If this hwasan patch series gets approved and if that patch goes in 
>> would it be feasible to bump the libsanitizer merge to whatever 
>> version that would be?
>>
>> If not (maybe because stage1 would be finished?) then could/would we 
>> end up using the LOCAL_PATCHES approach?
> 
> Since now, I would prefer doing cherry picks. Hopefully, we'll end just 
> with couple of patches.

That makes sense, there's just one patch I need 83ac1820.

As far as I can tell from the history, the process is simply to apply 
the patch in GCC, commit it with a ChangeLog etc as if it were a GCC 
patch, and then add the hash into LOCAL_PATCHES as a separate commit.

Is that right?


Given that it looks like the hwasan patch series is nearing going in 
now, I'd like to make sure I know how the library is getting added.

Is the plan something like the below?
1) You add the libhwasan update (i.e. this patch you posted).
2) I add the cherry-pick from LLVM compiler-rt (once it's approved)
     and a separate commit updating LOCAL_PATCHES.
3) I add the hwasan patch series.

Or would it make more sense for me to apply your patch below with 
`--author` using your details (so it goes in the ChangeLog that way)?

Thanks!
Matthew


> 
> Thanks,
> Martin
> 
>>
>> Thanks,
>> Matthew
>> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 
>>
>> *From:* Martin Liška <mliska@suse.cz>
>> *Sent:* 13 November 2020 16:33
>> *To:* Matthew Malcomson <Matthew.Malcomson@arm.com>; 
>> gcc-patches@gcc.gnu.org <gcc-patches@gcc.gnu.org>
>> *Cc:* jakub@redhat.com <jakub@redhat.com>; Richard Earnshaw 
>> <Richard.Earnshaw@arm.com>; kcc@google.com <kcc@google.com>; 
>> dodji@redhat.com <dodji@redhat.com>; joseph@codesourcery.com 
>> <joseph@codesourcery.com>
>> *Subject:* Re: [Patch 0/X] HWASAN v4
>> On 10/16/20 11:03 AM, Martin Li�ka wrote:
>>> Hello.
>>>
>>> I've just merged libsanitizer and there's the corresponding part that 
>>> includes
>>> libhwasan.
>>>
>>> Martin
>>
>> Hey.
>>
>> I've just made last merge from upstream, there's corresponding hwasan 
>> part.
>>
>> Martin
> 


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Update: [PATCH 5/X] libsanitizer: mid-end:  Introduce stack variable handling for HWASAN
  2020-11-19 15:28           ` Richard Sandiford
@ 2020-11-20 18:46             ` Matthew Malcomson
  2020-11-23 20:10               ` Richard Sandiford
                                 ` (2 more replies)
  0 siblings, 3 replies; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-20 18:46 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: Richard Earnshaw, Kyrylo Tkachov, gcc-patches, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 56015 bytes --]



Hi there,

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

That's the only change, Ok for trunk?

Matthew


------------------------------------------------------------



Handling stack variables has three features.

1) Ensure HWASAN required alignment for stack variables

When tagging shadow memory, we need to ensure that each tag granule is
only used by one variable at a time.

This is done by ensuring that each tagged variable is aligned to the tag
granule representation size and also ensure that the end of each
object is aligned to ensure the start of any other data stored on the
stack is in a different granule.

This patch ensures the above by forcing the stack pointer to be aligned
before and after allocating any stack objects. Since we are forcing
alignment we also use `align_local_variable` to ensure this new alignment
is advertised properly through SET_DECL_ALIGN.

2) Put tags into each stack variable pointer

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

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

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

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

Backend hooks define the size of a tag, the layout of the HWASAN shadow
memory, and handle emitting the code that inserts and extracts tags from a
pointer.

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

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

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

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

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

config/ChangeLog:

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

ChangeLog:

	* gcc/asan.c (struct hwasan_stack_var): New.
	(hwasan_sanitize_p): New.
	(hwasan_sanitize_stack_p): New.
	(hwasan_sanitize_allocas_p): New.
	(initialize_sanitizer_builtins): Define new builtins.
	(ATTR_NOTHROW_LIST): New macro.
	(hwasan_current_frame_tag): New.
	(hwasan_frame_base): New.
	(stack_vars_base_reg_p): New.
	(hwasan_maybe_init_frame_base_init): New.
	(hwasan_record_stack_var): New.
	(hwasan_get_frame_extent): New.
	(hwasan_increment_frame_tag): New.
	(hwasan_record_frame_init): New.
	(hwasan_emit_prologue): New.
	(hwasan_emit_untag_frame): New.
	(hwasan_finish_file): New.
	(hwasan_truncate_to_tag_size): New.
	* gcc/asan.h (hwasan_record_frame_init): New declaration.
	(hwasan_record_stack_var): New declaration.
	(hwasan_emit_prologue): New declaration.
	(hwasan_emit_untag_frame): New declaration.
	(hwasan_get_frame_extent): New declaration.
	(hwasan_maybe_enit_frame_base_init): New declaration.
	(hwasan_frame_base): New declaration.
	(stack_vars_base_reg_p): New declaration.
	(hwasan_current_frame_tag): New declaration.
	(hwasan_increment_frame_tag): New declaration.
	(hwasan_truncate_to_tag_size): New declaration.
	(hwasan_finish_file): New declaration.
	(hwasan_sanitize_p): New declaration.
	(hwasan_sanitize_stack_p): New declaration.
	(hwasan_sanitize_allocas_p): New declaration.
	(HWASAN_TAG_SIZE): New macro.
	(HWASAN_TAG_GRANULE_SIZE): New macro.
	(HWASAN_STACK_BACKGROUND): New macro.
	* gcc/builtin-types.def (BT_FN_VOID_PTR_UINT8_PTRMODE): New.
	* gcc/builtins.def (DEF_SANITIZER_BUILTIN): Enable for HWASAN.
	* gcc/cfgexpand.c (align_local_variable): When using hwasan ensure
	alignment to tag granule.
	(align_frame_offset): New.
	(expand_one_stack_var_at): For hwasan use tag offset.
	(expand_stack_vars): Record stack objects for hwasan.
	(expand_one_stack_var_1): Record stack objects for hwasan.
	(init_vars_expansion): Initialise hwasan state.
	(expand_used_vars): Emit hwasan prologue and generate hwasan epilogue.
	(pass_expand::execute): Emit hwasan base initialization if needed.
	* gcc/doc/tm.texi (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
	TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
	TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
	TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
	* gcc/doc/tm.texi.in (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
	TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
	TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
	TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
	* gcc/explow.c (get_dynamic_stack_base): Take new `base` argument.
	* gcc/explow.h (get_dynamic_stack_base): Take new `base` argument.
	* gcc/sanitizer.def (BUILT_IN_HWASAN_INIT): New.
	(BUILT_IN_HWASAN_TAG_MEM): New.
	* gcc/target.def (target_memtag_tag_size,target_memtag_granule_size,
	target_memtag_insert_random_tag,target_memtag_add_tag,
	target_memtag_set_tag,target_memtag_extract_tag,
	target_memtag_untagged_pointer): New hooks.
	* gcc/targhooks.c (HWASAN_SHIFT): New.
	(HWASAN_SHIFT_RTX): New.
	(default_memtag_tag_size): New default hook.
	(default_memtag_granule_size): New default hook.
	(default_memtag_insert_random_tag): New default hook.
	(default_memtag_add_tag): New default hook.
	(default_memtag_set_tag): New default hook.
	(default_memtag_extract_tag): New default hook.
	(default_memtag_untagged_pointer): New default hook.
	* gcc/targhooks.h (default_memtag_tag_size): New default hook.
	(default_memtag_granule_size): New default hook.
	(default_memtag_insert_random_tag): New default hook.
	(default_memtag_add_tag): New default hook.
	(default_memtag_set_tag): New default hook.
	(default_memtag_extract_tag): New default hook.
	(default_memtag_untagged_pointer): New default hook.
	* gcc/toplev.c (compile_file): Call hwasan_finish_file when finished.


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


diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
--- a/config/bootstrap-hwasan.mk
+++ b/config/bootstrap-hwasan.mk
@@ -1,7 +1,11 @@
 # This option enables -fsanitize=hwaddress for stage2 and stage3.
+# We need to disable random frame tags for bootstrap since the autoconf check
+# for which direction the stack is growing has UB that a random frame tag
+# breaks.  Running with a random frame tag gives approx. 50% chance of
+# bootstrap comparison diff in libiberty/alloca.c.
 
-STAGE2_CFLAGS += -fsanitize=hwaddress
-STAGE3_CFLAGS += -fsanitize=hwaddress
+STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
+STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
 POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
diff --git a/gcc/asan.h b/gcc/asan.h
index 114b457ef91c4479d43774bed58c24213196ce12..8d5271e6b575d74da277420798557f3274e966ce 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -34,6 +34,22 @@ extern bool asan_expand_mark_ifn (gimple_stmt_iterator *);
 extern bool asan_expand_poison_ifn (gimple_stmt_iterator *, bool *,
 				    hash_map<tree, tree> &);
 
+extern void hwasan_record_frame_init ();
+extern void hwasan_record_stack_var (rtx, rtx, poly_int64, poly_int64);
+extern void hwasan_emit_prologue ();
+extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx);
+extern rtx hwasan_get_frame_extent ();
+extern rtx hwasan_frame_base ();
+extern void hwasan_maybe_emit_frame_base_init (void);
+extern bool stack_vars_base_reg_p (rtx);
+extern uint8_t hwasan_current_frame_tag ();
+extern void hwasan_increment_frame_tag ();
+extern rtx hwasan_truncate_to_tag_size (rtx, rtx);
+extern void hwasan_finish_file (void);
+extern bool hwasan_sanitize_p (void);
+extern bool hwasan_sanitize_stack_p (void);
+extern bool hwasan_sanitize_allocas_p (void);
+
 extern gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
 
@@ -75,6 +91,26 @@ extern hash_set <tree> *asan_used_labels;
 
 #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE	"use after scope memory"
 
+/* NOTE: The values below and the hooks under targetm.memtag define an ABI and
+   are hard-coded to these values in libhwasan, hence they can't be changed
+   independently here.  */
+/* How many bits are used to store a tag in a pointer.
+   The default version uses the entire top byte of a pointer (i.e. 8 bits).  */
+#define HWASAN_TAG_SIZE targetm.memtag.tag_size ()
+/* Tag Granule of HWASAN shadow stack.
+   This is the size in real memory that each byte in the shadow memory refers
+   to.  I.e. if a variable is X bytes long in memory then its tag in shadow
+   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
+   Most variables will need to be aligned to this amount since two variables
+   that are neighbors in memory and share a tag granule would need to share the
+   same tag (the shared tag granule can only store one tag).  */
+#define HWASAN_TAG_GRANULE_SIZE targetm.memtag.granule_size ()
+/* Define the tag for the stack background.
+   This defines what tag the stack pointer will be and hence what tag all
+   variables that are not given special tags are (e.g. spilled registers,
+   and parameters passed on the stack).  */
+#define HWASAN_STACK_BACKGROUND gen_int_mode (0, QImode)
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
diff --git a/gcc/asan.c b/gcc/asan.c
index 0b471afff64ea6a0ffbe0add71333ac688c472c6..d1ede3b62291eba698948e06208c482b6f197be5 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -257,6 +257,58 @@ hash_set<tree> *asan_handled_variables = NULL;
 
 hash_set <tree> *asan_used_labels = NULL;
 
+/* Global variables for HWASAN stack tagging.  */
+/* hwasan_frame_tag_offset records the offset from the frame base tag that the
+   next object should have.  */
+static uint8_t hwasan_frame_tag_offset = 0;
+/* hwasan_frame_base_ptr is a pointer with the same address as
+   `virtual_stack_vars_rtx` for the current frame, and with the frame base tag
+   stored in it.  N.b. this global RTX does not need to be marked GTY, but is
+   done so anyway.  The need is not there since all uses are in just one pass
+   (cfgexpand) and there are no calls to ggc_collect between the uses.  We mark
+   it GTY(()) anyway to allow the use of the variable later on if needed by
+   future features.  */
+static GTY(()) rtx hwasan_frame_base_ptr = NULL_RTX;
+/* hwasan_frame_base_init_seq is the sequence of RTL insns that will initialize
+   the hwasan_frame_base_ptr.  When the hwasan_frame_base_ptr is requested, we
+   generate this sequence but do not emit it.  If the sequence was created it
+   is emitted once the function body has been expanded.
+
+   This delay is because the frame base pointer may be needed anywhere in the
+   function body, or needed by the expand_used_vars function.  Emitting once in
+   a known place is simpler than requiring the emission of the instructions to
+   be know where it should go depending on the first place the hwasan frame
+   base is needed.  */
+static GTY(()) rtx_insn *hwasan_frame_base_init_seq = NULL;
+
+/* Structure defining the extent of one object on the stack that HWASAN needs
+   to tag in the corresponding shadow stack space.
+
+   The range this object spans on the stack is between `untagged_base +
+   nearest_offset` and `untagged_base + farthest_offset`.
+   `tagged_base` is an rtx containing the same value as `untagged_base` but
+   with a random tag stored in the top byte.  We record both `untagged_base`
+   and `tagged_base` so that `hwasan_emit_prologue` can use both without having
+   to emit RTL into the instruction stream to re-calculate one from the other.
+   (`hwasan_emit_prologue` needs to use both bases since the
+   __hwasan_tag_memory call it emits uses an untagged value, and it calculates
+   the tag to store in shadow memory based on the tag_offset plus the tag in
+   tagged_base).  */
+struct hwasan_stack_var
+{
+  rtx untagged_base;
+  rtx tagged_base;
+  poly_int64 nearest_offset;
+  poly_int64 farthest_offset;
+  uint8_t tag_offset;
+};
+
+/* Variable recording all stack variables that HWASAN needs to tag.
+   Does not need to be marked as GTY(()) since every use is in the cfgexpand
+   pass and gcc_collect is not called in the middle of that pass.  */
+static vec<hwasan_stack_var> hwasan_tagged_stack_vars;
+
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -1359,6 +1411,28 @@ asan_redzone_buffer::flush_if_full (void)
     flush_redzone_payload ();
 }
 
+/* Returns whether we are tagging pointers and checking those tags on memory
+   access.  */
+bool
+hwasan_sanitize_p ()
+{
+  return sanitize_flags_p (SANITIZE_HWADDRESS);
+}
+
+/* Are we tagging the stack?  */
+bool
+hwasan_sanitize_stack_p ()
+{
+  return (hwasan_sanitize_p () && param_hwasan_instrument_stack);
+}
+
+/* Are we tagging alloca objects?  */
+bool
+hwasan_sanitize_allocas_p (void)
+{
+  return (hwasan_sanitize_stack_p () && param_hwasan_instrument_allocas);
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -2908,6 +2982,11 @@ initialize_sanitizer_builtins (void)
     = build_function_type_list (void_type_node, uint64_type_node,
 				ptr_type_node, NULL_TREE);
 
+  tree BT_FN_VOID_PTR_UINT8_PTRMODE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				unsigned_char_type_node,
+				pointer_sized_int_node, NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
@@ -2958,6 +3037,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NOTHROW_LIST
+#define ATTR_NOTHROW_LIST ECF_NOTHROW
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
@@ -3709,4 +3790,347 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/* For stack tagging:
+
+   Return the offset from the frame base tag that the "next" expanded object
+   should have.  */
+uint8_t
+hwasan_current_frame_tag ()
+{
+  return hwasan_frame_tag_offset;
+}
+
+/* For stack tagging:
+
+   Return the 'base pointer' for this function.  If that base pointer has not
+   yet been created then we create a register to hold it and record the insns
+   to initialize the register in `hwasan_frame_base_init_seq` for later
+   emission.  */
+rtx
+hwasan_frame_base ()
+{
+  if (! hwasan_frame_base_ptr)
+    {
+      start_sequence ();
+      hwasan_frame_base_ptr
+	= force_reg (Pmode,
+		     targetm.memtag.insert_random_tag (virtual_stack_vars_rtx,
+						       NULL_RTX));
+      hwasan_frame_base_init_seq = get_insns ();
+      end_sequence ();
+    }
+
+  return hwasan_frame_base_ptr;
+}
+
+/* For stack tagging:
+
+   Check whether this RTX is a standard pointer addressing the base of the
+   stack variables for this frame.  Returns true if the RTX is either
+   virtual_stack_vars_rtx or hwasan_frame_base_ptr.  */
+bool
+stack_vars_base_reg_p (rtx base)
+{
+  return base == virtual_stack_vars_rtx || base == hwasan_frame_base_ptr;
+}
+
+/* For stack tagging:
+
+   Emit frame base initialisation.
+   If hwasan_frame_base has been used before here then
+   hwasan_frame_base_init_seq contains the sequence of instructions to
+   initialize it.  This must be put just before the hwasan prologue, so we emit
+   the insns before parm_birth_insn (which will point to the first instruction
+   of the hwasan prologue if it exists).
+
+   We update `parm_birth_insn` to point to the start of this initialisation
+   since that represents the end of the initialisation done by
+   expand_function_{start,end} functions and we want to maintain that.  */
+void
+hwasan_maybe_emit_frame_base_init ()
+{
+  if (! hwasan_frame_base_init_seq)
+    return;
+  emit_insn_before (hwasan_frame_base_init_seq, parm_birth_insn);
+  parm_birth_insn = hwasan_frame_base_init_seq;
+}
+
+/* Record a compile-time constant size stack variable that HWASAN will need to
+   tag.  This record of the range of a stack variable will be used by
+   `hwasan_emit_prologue` to emit the RTL at the start of each frame which will
+   set tags in the shadow memory according to the assigned tag for each object.
+
+   The range that the object spans in stack space should be described by the
+   bounds `untagged_base + nearest_offset` and
+   `untagged_base + farthest_offset`.
+   `tagged_base` is the base address which contains the "base frame tag" for
+   this frame, and from which the value to address this object with will be
+   calculated.
+
+   We record the `untagged_base` since the functions in the hwasan library we
+   use to tag memory take pointers without a tag.  */
+void
+hwasan_record_stack_var (rtx untagged_base, rtx tagged_base,
+			 poly_int64 nearest_offset, poly_int64 farthest_offset)
+{
+  hwasan_stack_var cur_var;
+  cur_var.untagged_base = untagged_base;
+  cur_var.tagged_base = tagged_base;
+  cur_var.nearest_offset = nearest_offset;
+  cur_var.farthest_offset = farthest_offset;
+  cur_var.tag_offset = hwasan_current_frame_tag ();
+
+  hwasan_tagged_stack_vars.safe_push (cur_var);
+}
+
+/* Return the RTX representing the farthest extent of the statically allocated
+   stack objects for this frame.  If hwasan_frame_base_ptr has not been
+   initialized then we are not storing any static variables on the stack in
+   this frame.  In this case we return NULL_RTX to represent that.
+
+   Otherwise simply return virtual_stack_vars_rtx + frame_offset.  */
+rtx
+hwasan_get_frame_extent ()
+{
+  return (hwasan_frame_base_ptr
+	  ? plus_constant (Pmode, virtual_stack_vars_rtx, frame_offset)
+	  : NULL_RTX);
+}
+
+/* For stack tagging:
+
+   Increment the frame tag offset modulo the size a tag can represent.  */
+void
+hwasan_increment_frame_tag ()
+{
+  uint8_t tag_bits = HWASAN_TAG_SIZE;
+  gcc_assert (HWASAN_TAG_SIZE
+	      <= sizeof (hwasan_frame_tag_offset) * CHAR_BIT);
+  hwasan_frame_tag_offset = (hwasan_frame_tag_offset + 1) % (1 << tag_bits);
+  /* The "background tag" of the stack is zero by definition.
+     This is the tag that objects like parameters passed on the stack and
+     spilled registers are given.  It is handy to avoid this tag for objects
+     whose tags we decide ourselves, partly to ensure that buffer overruns
+     can't affect these important variables (e.g. saved link register, saved
+     stack pointer etc) and partly to make debugging easier (everything with a
+     tag of zero is space allocated automatically by the compiler).
+
+     This is not feasible when using random frame tags (the default
+     configuration for hwasan) since the tag for the given frame is randomly
+     chosen at runtime.  In order to avoid any tags matching the stack
+     background we would need to decide tag offsets at runtime instead of
+     compile time (and pay the resulting performance cost).
+
+     When not using random base tags for each frame (i.e. when compiled with
+     `--param hwasan-random-frame-tag=0`) the base tag for each frame is zero.
+     This means the tag that each object gets is equal to the
+     hwasan_frame_tag_offset used in determining it.
+     When this is the case we *can* ensure no object gets the tag of zero by
+     simply ensuring no object has the hwasan_frame_tag_offset of zero.
+
+     There is the extra complication that we only record the
+     hwasan_frame_tag_offset here (which is the offset from the tag stored in
+     the stack pointer).  In the kernel, the tag in the stack pointer is 0xff
+     rather than zero.  This does not cause problems since tags of 0xff are
+     never checked in the kernel.  As mentioned at the beginning of this
+     comment the background tag of the stack is zero by definition, which means
+     that for the kernel we should skip offsets of both 0 and 1 from the stack
+     pointer.  Avoiding the offset of 0 ensures we use a tag which will be
+     checked, avoiding the offset of 1 ensures we use a tag that is not the
+     same as the background.  */
+  if (hwasan_frame_tag_offset == 0 && ! param_hwasan_random_frame_tag)
+    hwasan_frame_tag_offset += 1;
+  if (hwasan_frame_tag_offset == 1 && ! param_hwasan_random_frame_tag
+      && sanitize_flags_p (SANITIZE_KERNEL_HWADDRESS))
+    hwasan_frame_tag_offset += 1;
+}
+
+/* Clear internal state for the next function.
+   This function is called before variables on the stack get expanded, in
+   `init_vars_expansion`.  */
+void
+hwasan_record_frame_init ()
+{
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
+  /* If this isn't the case then some stack variable was recorded *before*
+     hwasan_record_frame_init is called, yet *after* the hwasan prologue for
+     the previous frame was emitted.  Such stack variables would not have
+     their shadow stack filled in.  */
+  gcc_assert (hwasan_tagged_stack_vars.is_empty ());
+  hwasan_frame_base_ptr = NULL_RTX;
+  hwasan_frame_base_init_seq = NULL;
+
+  /* When not using a random frame tag we can avoid the background stack
+     color which gives the user a little better debug output upon a crash.
+     Meanwhile, when using a random frame tag it will be nice to avoid adding
+     tags for the first object since that is unnecessary extra work.
+     Hence set the initial hwasan_frame_tag_offset to be 0 if using a random
+     frame tag and 1 otherwise.
+
+     As described in hwasan_increment_frame_tag, in the kernel the stack
+     pointer has the tag 0xff.  That means that to avoid 0xff and 0 (the tag
+     which the kernel does not check and the background tag respectively) we
+     start with a tag offset of 2.  */
+  hwasan_frame_tag_offset = param_hwasan_random_frame_tag
+    ? 0
+    : sanitize_flags_p (SANITIZE_KERNEL_HWADDRESS) ? 2 : 1;
+}
+
+/* For stack tagging:
+   (Emits HWASAN equivalent of what is emitted by
+   `asan_emit_stack_protection`).
+
+   Emits the extra prologue code to set the shadow stack as required for HWASAN
+   stack instrumentation.
+
+   Uses the vector of recorded stack variables hwasan_tagged_stack_vars.  When
+   this function has completed hwasan_tagged_stack_vars is empty and all
+   objects it had pointed to are deallocated.  */
+void
+hwasan_emit_prologue ()
+{
+  /* We need untagged base pointers since libhwasan only accepts untagged
+    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
+    the base tag for an offset.  */
+
+  if (hwasan_tagged_stack_vars.is_empty ())
+    return;
+
+  poly_int64 bot = 0, top = 0;
+  for (hwasan_stack_var &cur : hwasan_tagged_stack_vars)
+    {
+      poly_int64 nearest = cur.nearest_offset;
+      poly_int64 farthest = cur.farthest_offset;
+
+      if (known_ge (nearest, farthest))
+	{
+	  top = nearest;
+	  bot = farthest;
+	}
+      else
+	{
+	  /* Given how these values are calculated, one must be known greater
+	     than the other.  */
+	  gcc_assert (known_le (nearest, farthest));
+	  top = farthest;
+	  bot = nearest;
+	}
+      poly_int64 size = (top - bot);
+
+      /* Assert the edge of each variable is aligned to the HWASAN tag granule
+	 size.  */
+      gcc_assert (multiple_p (top, HWASAN_TAG_GRANULE_SIZE));
+      gcc_assert (multiple_p (bot, HWASAN_TAG_GRANULE_SIZE));
+      gcc_assert (multiple_p (size, HWASAN_TAG_GRANULE_SIZE));
+
+      rtx fn = init_one_libfunc ("__hwasan_tag_memory");
+      rtx base_tag = targetm.memtag.extract_tag (cur.tagged_base, NULL_RTX);
+      rtx tag = plus_constant (QImode, base_tag, cur.tag_offset);
+      tag = hwasan_truncate_to_tag_size (tag, NULL_RTX);
+
+      rtx bottom = convert_memory_address (ptr_mode,
+					   plus_constant (Pmode,
+							  cur.untagged_base,
+							  bot));
+      emit_library_call (fn, LCT_NORMAL, VOIDmode,
+			 bottom, ptr_mode,
+			 tag, QImode,
+			 gen_int_mode (size, ptr_mode), ptr_mode);
+    }
+  /* Clear the stack vars, we've emitted the prologue for them all now.  */
+  hwasan_tagged_stack_vars.truncate (0);
+}
+
+/* For stack tagging:
+
+   Return RTL insns to clear the tags between DYNAMIC and VARS pointers
+   into the stack.  These instructions should be emitted at the end of
+   every function.
+
+   If `dynamic` is NULL_RTX then no insns are returned.  */
+rtx_insn *
+hwasan_emit_untag_frame (rtx dynamic, rtx vars)
+{
+  if (! dynamic)
+    return NULL;
+
+  start_sequence ();
+
+  dynamic = convert_memory_address (ptr_mode, dynamic);
+  vars = convert_memory_address (ptr_mode, vars);
+
+  rtx top_rtx;
+  rtx bot_rtx;
+  if (FRAME_GROWS_DOWNWARD)
+    {
+      top_rtx = vars;
+      bot_rtx = dynamic;
+    }
+  else
+    {
+      top_rtx = dynamic;
+      bot_rtx = vars;
+    }
+
+  rtx size_rtx = expand_simple_binop (ptr_mode, MINUS, top_rtx, bot_rtx,
+				      NULL_RTX, /* unsignedp = */0,
+				      OPTAB_DIRECT);
+
+  rtx fn = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (fn, LCT_NORMAL, VOIDmode,
+		     bot_rtx, ptr_mode,
+		     HWASAN_STACK_BACKGROUND, QImode,
+		     size_rtx, ptr_mode);
+
+  do_pending_stack_adjust ();
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+/* Needs to be GTY(()), because cgraph_build_static_cdtor may
+   invoke ggc_collect.  */
+static GTY(()) tree hwasan_ctor_statements;
+
+/* Insert module initialization into this TU.  This initialization calls the
+   initialization code for libhwasan.  */
+void
+hwasan_finish_file (void)
+{
+  /* Do not emit constructor initialization for the kernel.
+     (the kernel has its own initialization already).  */
+  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
+    return;
+
+  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
+  flag_sanitize &= ~SANITIZE_HWADDRESS;
+  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
+  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
+  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
+  flag_sanitize |= SANITIZE_HWADDRESS;
+}
+
+/* For stack tagging:
+
+   Truncate `tag` to the number of bits that a tag uses (i.e. to
+   HWASAN_TAG_SIZE).  Store the result in `target` if it's convenient.  */
+rtx
+hwasan_truncate_to_tag_size (rtx tag, rtx target)
+{
+  gcc_assert (GET_MODE (tag) == QImode);
+  if (HWASAN_TAG_SIZE != GET_MODE_PRECISION (QImode))
+    {
+      gcc_assert (GET_MODE_PRECISION (QImode) > HWASAN_TAG_SIZE);
+      rtx mask = gen_int_mode ((HOST_WIDE_INT_1U << HWASAN_TAG_SIZE) - 1,
+			       QImode);
+      tag = expand_simple_binop (QImode, AND, tag, mask, target,
+				 /* unsignedp = */1, OPTAB_WIDEN);
+      gcc_assert (tag);
+    }
+  return tag;
+}
+
 #include "gt-asan.h"
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index 4a82ee421bef42154ccd88e52f7a19f48b340c73..1ad6657da45cc4976532e1b8bc233f67d8da9ccf 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -639,6 +639,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_CONST_SIZE_BOOL,
 		     BT_PTR, BT_PTR, BT_CONST_SIZE, BT_BOOL)
 DEF_FUNCTION_TYPE_3 (BT_FN_PTR_SIZE_SIZE_PTRMODE,
 		     BT_PTR, BT_SIZE, BT_SIZE, BT_PTRMODE)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_PTRMODE, BT_VOID, BT_PTR, BT_UINT8,
+		     BT_PTRMODE)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index b4494c712a1751fbb37378f38cc1411d11a37331..97bb5d0b0aee7fa9ee4c82e2d80eae866fc23829 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -245,6 +245,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
+				| SANITIZE_HWADDRESS \
 				| SANITIZE_UNDEFINED \
 				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 1df6f4bc55a39230c98e58af6c2d765652db8324..231c2ee32362fc3967b1cd7b70bd330ce49648d3 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -376,15 +376,18 @@ align_local_variable (tree decl, bool really_expand)
 	align = GET_MODE_ALIGNMENT (mode);
     }
   else
-    {
-      align = LOCAL_DECL_ALIGNMENT (decl);
-      /* Don't change DECL_ALIGN when called from estimated_stack_frame_size.
-	 That is done before IPA and could bump alignment based on host
-	 backend even for offloaded code which wants different
-	 LOCAL_DECL_ALIGNMENT.  */
-      if (really_expand)
-	SET_DECL_ALIGN (decl, align);
-    }
+    align = LOCAL_DECL_ALIGNMENT (decl);
+
+  if (hwasan_sanitize_stack_p ())
+    align = MAX (align, (unsigned) HWASAN_TAG_GRANULE_SIZE * BITS_PER_UNIT);
+
+  if (TREE_CODE (decl) != SSA_NAME && really_expand)
+    /* Don't change DECL_ALIGN when called from estimated_stack_frame_size.
+       That is done before IPA and could bump alignment based on host
+       backend even for offloaded code which wants different
+       LOCAL_DECL_ALIGNMENT.  */
+    SET_DECL_ALIGN (decl, align);
+
   return align / BITS_PER_UNIT;
 }
 
@@ -428,6 +431,14 @@ alloc_stack_frame_space (poly_int64 size, unsigned HOST_WIDE_INT align)
   return offset;
 }
 
+/* Ensure that the stack is aligned to ALIGN bytes.
+   Return the new frame offset.  */
+static poly_int64
+align_frame_offset (unsigned HOST_WIDE_INT align)
+{
+  return alloc_stack_frame_space (0, align);
+}
+
 /* Accumulate DECL into STACK_VARS.  */
 
 static void
@@ -1004,7 +1015,12 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
   /* If this fails, we've overflowed the stack frame.  Error nicely?  */
   gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
 
-  x = plus_constant (Pmode, base, offset);
+  if (hwasan_sanitize_stack_p ())
+    x = targetm.memtag.add_tag (base, offset,
+				hwasan_current_frame_tag ());
+  else
+    x = plus_constant (Pmode, base, offset);
+
   x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
 		   ? TYPE_MODE (TREE_TYPE (decl))
 		   : DECL_MODE (decl), x);
@@ -1013,7 +1029,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
      If it is we generate stack slots only accidentally so it isn't as
      important, we'll simply set the alignment directly on the MEM.  */
 
-  if (base == virtual_stack_vars_rtx)
+  if (stack_vars_base_reg_p (base))
     offset -= frame_phase;
   align = known_alignment (offset);
   align *= BITS_PER_UNIT;
@@ -1056,13 +1072,13 @@ public:
 /* A subroutine of expand_used_vars.  Give each partition representative
    a unique location within the stack frame.  Update each partition member
    with that location.  */
-
 static void
 expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 {
   size_t si, i, j, n = stack_vars_num;
   poly_uint64 large_size = 0, large_alloc = 0;
   rtx large_base = NULL;
+  rtx large_untagged_base = NULL;
   unsigned large_align = 0;
   bool large_allocation_done = false;
   tree decl;
@@ -1113,7 +1129,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
     {
       rtx base;
       unsigned base_align, alignb;
-      poly_int64 offset;
+      poly_int64 offset = 0;
 
       i = stack_vars_sorted[si];
 
@@ -1134,10 +1150,33 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       if (pred && !pred (i))
 	continue;
 
+      base = (hwasan_sanitize_stack_p ()
+	      ? hwasan_frame_base ()
+	      : virtual_stack_vars_rtx);
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  base = virtual_stack_vars_rtx;
+	  poly_int64 hwasan_orig_offset;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* There must be no tag granule "shared" between different
+		 objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
+		 chunk can have more than one object in it.
+
+		 We ensure this by forcing the end of the last bit of data to
+		 be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
+		 the start of each variable to be aligned to
+		 HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
+
+		 We can't align just one of the start or end, since there are
+		 untagged things stored on the stack which we do not align to
+		 HWASAN_TAG_GRANULE_SIZE bytes.  If we only aligned the start
+		 or the end of tagged objects then untagged objects could end
+		 up sharing the first granule of a tagged object or sharing the
+		 last granule of a tagged object respectively.  */
+	      hwasan_orig_offset = align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+	      gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
+	    }
 	  /* ASAN description strings don't yet have a syntax for expressing
 	     polynomial offsets.  */
 	  HOST_WIDE_INT prev_offset;
@@ -1148,7 +1187,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	    {
 	      if (data->asan_vec.is_empty ())
 		{
-		  alloc_stack_frame_space (0, ASAN_RED_ZONE_SIZE);
+		  align_frame_offset (ASAN_RED_ZONE_SIZE);
 		  prev_offset = frame_offset.to_constant ();
 		}
 	      prev_offset = align_base (prev_offset,
@@ -1216,6 +1255,24 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	    {
 	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
 	      base_align = crtl->max_used_stack_slot_alignment;
+
+	      if (hwasan_sanitize_stack_p ())
+		{
+		  /* Align again since the point of this alignment is to handle
+		     the "end" of the object (i.e. smallest address after the
+		     stack object).  For FRAME_GROWS_DOWNWARD that requires
+		     aligning the stack before allocating, but for a frame that
+		     grows upwards that requires aligning the stack after
+		     allocation.
+
+		     Use `frame_offset` to record the offset value rather than
+		     offset since the `frame_offset` describes the extent
+		     allocated for this particular variable while `offset`
+		     describes the address that this variable starts at.  */
+		  align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+		  hwasan_record_stack_var (virtual_stack_vars_rtx, base,
+					   hwasan_orig_offset, frame_offset);
+		}
 	    }
 	}
       else
@@ -1236,14 +1293,33 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
 	      loffset = alloc_stack_frame_space
 		(rtx_to_poly_int64 (large_allocsize),
 		 PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
-	      large_base = get_dynamic_stack_base (loffset, large_align);
+	      large_base = get_dynamic_stack_base (loffset, large_align, base);
 	      large_allocation_done = true;
 	    }
-	  gcc_assert (large_base != NULL);
 
+	  gcc_assert (large_base != NULL);
 	  large_alloc = aligned_upper_bound (large_alloc, alignb);
 	  offset = large_alloc;
 	  large_alloc += stack_vars[i].size;
+	  if (hwasan_sanitize_stack_p ())
+	    {
+	      /* An object with a large alignment requirement means that the
+		 alignment requirement is greater than the required alignment
+		 for tags.  */
+	      if (!large_untagged_base)
+		large_untagged_base
+		  = targetm.memtag.untagged_pointer (large_base, NULL_RTX);
+	      /* Ensure the end of the variable is also aligned correctly.  */
+	      poly_int64 align_again
+		= aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
+	      /* For large allocations we always allocate a chunk of space
+		 (which is addressed by large_untagged_base/large_base) and
+		 then use positive offsets from that.  Hence the farthest
+		 offset is `align_again` and the nearest offset from the base
+		 is `offset`.  */
+	      hwasan_record_stack_var (large_untagged_base, large_base,
+				       offset, align_again);
+	    }
 
 	  base = large_base;
 	  base_align = large_align;
@@ -1254,9 +1330,10 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
       for (j = i; j != EOC; j = stack_vars[j].next)
 	{
 	  expand_one_stack_var_at (stack_vars[j].decl,
-				   base, base_align,
-				   offset);
+				   base, base_align, offset);
 	}
+      if (hwasan_sanitize_stack_p ())
+	hwasan_increment_frame_tag ();
     }
 
   gcc_assert (known_eq (large_alloc, large_size));
@@ -1347,10 +1424,37 @@ expand_one_stack_var_1 (tree var)
   /* We handle highly aligned variables in expand_stack_vars.  */
   gcc_assert (byte_align * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT);
 
-  offset = alloc_stack_frame_space (size, byte_align);
+  rtx base;
+  if (hwasan_sanitize_stack_p ())
+    {
+      /* Allocate zero bytes to align the stack.  */
+      poly_int64 hwasan_orig_offset
+	= align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+      offset = alloc_stack_frame_space (size, byte_align);
+      align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
+      base = hwasan_frame_base ();
+      /* Use `frame_offset` to automatically account for machines where the
+	 frame grows upwards.
+
+	 `offset` will always point to the "start" of the stack object, which
+	 will be the smallest address, for ! FRAME_GROWS_DOWNWARD this is *not*
+	 the "furthest" offset from the base delimiting the current stack
+	 object.  `frame_offset` will always delimit the extent that the frame.
+	 */
+      hwasan_record_stack_var (virtual_stack_vars_rtx, base,
+			       hwasan_orig_offset, frame_offset);
+    }
+  else
+    {
+      offset = alloc_stack_frame_space (size, byte_align);
+      base = virtual_stack_vars_rtx;
+    }
 
-  expand_one_stack_var_at (var, virtual_stack_vars_rtx,
+  expand_one_stack_var_at (var, base,
 			   crtl->max_used_stack_slot_alignment, offset);
+
+  if (hwasan_sanitize_stack_p ())
+    hwasan_increment_frame_tag ();
 }
 
 /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
@@ -1950,6 +2054,8 @@ init_vars_expansion (void)
   /* Initialize local stack smashing state.  */
   has_protected_decls = false;
   has_short_buffer = false;
+  if (hwasan_sanitize_stack_p ())
+    hwasan_record_frame_init ();
 }
 
 /* Free up stack variable graph data.  */
@@ -2277,10 +2383,26 @@ expand_used_vars (void)
       expand_stack_vars (NULL, &data);
     }
 
+  if (hwasan_sanitize_stack_p ())
+    hwasan_emit_prologue ();
   if (asan_sanitize_allocas_p () && cfun->calls_alloca)
     var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
 					      virtual_stack_vars_rtx,
 					      var_end_seq);
+  else if (hwasan_sanitize_allocas_p () && cfun->calls_alloca)
+    /* When using out-of-line instrumentation we only want to emit one function
+       call for clearing the tags in a region of shadow stack.  When there are
+       alloca calls in this frame we want to emit a call using the
+       virtual_stack_dynamic_rtx, but when not we use the hwasan_frame_extent
+       rtx we created in expand_stack_vars.  */
+    var_end_seq = hwasan_emit_untag_frame (virtual_stack_dynamic_rtx,
+					   virtual_stack_vars_rtx);
+  else if (hwasan_sanitize_stack_p ())
+    /* If no variables were stored on the stack, `hwasan_get_frame_extent`
+       will return NULL_RTX and hence `hwasan_emit_untag_frame` will return
+       NULL (i.e. an empty sequence).  */
+    var_end_seq = hwasan_emit_untag_frame (hwasan_get_frame_extent (),
+					   virtual_stack_vars_rtx);
 
   fini_vars_expansion ();
 
@@ -6641,6 +6763,9 @@ pass_expand::execute (function *fun)
       emit_insn_after (var_ret_seq, after);
     }
 
+  if (hwasan_sanitize_stack_p ())
+    hwasan_maybe_emit_frame_base_init ();
+
   /* Zap the tree EH table.  */
   set_eh_throw_stmt_table (fun, NULL);
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 298fe4b295e2f81d679786f21f499183bc07078f..f06d5e8911241d3fa0f2c7a101a3a2468defd227 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -12230,3 +12230,60 @@ work.
 At preset, this feature does not support address spaces.  It also requires
 @code{Pmode} to be the same as @code{ptr_mode}.
 @end deftypefn
+
+@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_TAG_SIZE ()
+Return the size of a tag (in bits) for this platform.
+
+The default returns 8.
+@end deftypefn
+
+@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_GRANULE_SIZE ()
+Return the size in real memory that each byte in shadow memory refers to.
+I.e. if a variable is @var{X} bytes long in memory, then this hook should
+return the value @var{Y} such that the tag in shadow memory spans
+@var{X}/@var{Y} bytes.
+
+Most variables will need to be aligned to this amount since two variables
+that are neighbors in memory and share a tag granule would need to share
+the same tag.
+
+The default returns 16.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_INSERT_RANDOM_TAG (rtx @var{untagged}, rtx @var{target})
+Return an RTX representing the value of @var{untagged} but with a
+(possibly) random tag in it.
+Put that value into @var{target} if it is convenient to do so.
+This function is used to generate a tagged base for the current stack frame.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADD_TAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
+Return an RTX that represents the result of adding @var{addr_offset} to
+the address in pointer @var{base} and @var{tag_offset} to the tag in pointer
+@var{base}.
+The resulting RTX must either be a valid memory address or be able to get
+put into an operand with @code{force_operand}.
+
+Unlike other memtag hooks, this must return an expression and not emit any
+RTL.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_SET_TAG (rtx @var{untagged_base}, rtx @var{tag}, rtx @var{target})
+Return an RTX representing @var{untagged_base} but with the tag @var{tag}.
+Try and store this in @var{target} if convenient.
+@var{untagged_base} is required to have a zero tag when this hook is called.
+The default of this hook is to set the top byte of @var{untagged_base} to
+@var{tag}.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_EXTRACT_TAG (rtx @var{tagged_pointer}, rtx @var{target})
+Return an RTX representing the tag stored in @var{tagged_pointer}.
+Store the result in @var{target} if it is convenient.
+The default represents the top byte of the original pointer.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_MEMTAG_UNTAGGED_POINTER (rtx @var{tagged_pointer}, rtx @var{target})
+Return an RTX representing @var{tagged_pointer} with its tag set to zero.
+Store the result in @var{target} if convenient.
+The default clears the top byte of the original pointer.
+@end deftypefn
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 8fbd36e2bf31e098f7827ce331fd7059c8a747bc..b08923c8f28455fe77e061625e78ed1bf538e792 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -8186,3 +8186,17 @@ maintainer is familiar with.
 @hook TARGET_RUN_TARGET_SELFTESTS
 
 @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
+
+@hook TARGET_MEMTAG_TAG_SIZE
+
+@hook TARGET_MEMTAG_GRANULE_SIZE
+
+@hook TARGET_MEMTAG_INSERT_RANDOM_TAG
+
+@hook TARGET_MEMTAG_ADD_TAG
+
+@hook TARGET_MEMTAG_SET_TAG
+
+@hook TARGET_MEMTAG_EXTRACT_TAG
+
+@hook TARGET_MEMTAG_UNTAGGED_POINTER
diff --git a/gcc/explow.h b/gcc/explow.h
index 0df8c62b82a8bf1d8d6baf0b6fb658e66361a407..581831cb19fdf9e8fd969bb30139e1358279a34d 100644
--- a/gcc/explow.h
+++ b/gcc/explow.h
@@ -106,7 +106,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
 extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
 
 /* Returns the address of the dynamic stack space without allocating it.  */
-extern rtx get_dynamic_stack_base (poly_int64, unsigned);
+extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
 
 /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
 extern rtx align_dynamic_address (rtx, unsigned);
diff --git a/gcc/explow.c b/gcc/explow.c
index 0fbc6d25b816457a3d13ed45d16b5dd0513cfacd..41c3f6ace49c0e55c080e10b917842b1b21d49eb 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1583,10 +1583,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
    OFFSET is the offset of the area into the virtual stack vars area.
 
    REQUIRED_ALIGN is the alignment (in bits) required for the region
-   of memory.  */
+   of memory.
+
+   BASE is the rtx of the base of this virtual stack vars area.
+   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+   on the stack.  */
 
 rtx
-get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
 {
   rtx target;
 
@@ -1594,7 +1598,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
     crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
 
   target = gen_reg_rtx (Pmode);
-  emit_move_insn (target, virtual_stack_vars_rtx);
+  emit_move_insn (target, base);
   target = expand_binop (Pmode, add_optab, target,
 			 gen_int_mode (offset, Pmode),
 			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index a32715ddb92e69b7ca7be28a8f17a369b891bd76..4f854fb994229fd4ed91d3b5cff7c7acff9a55bc 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
 		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
+/* Hardware Address Sanitizer.  */
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
+		      BT_FN_VOID_PTR_UINT8_PTRMODE, ATTR_NOTHROW_LIST)
+
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/target.def b/gcc/target.def
index 25f0ae228210f926077020082f129fb2e599f062..44807438431488a5a7aa8f8125d256869e152b68 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6874,6 +6874,71 @@ At preset, this feature does not support address spaces.  It also requires\n\
 @code{Pmode} to be the same as @code{ptr_mode}.",
  bool, (), default_memtag_can_tag_addresses)
 
+DEFHOOK
+(tag_size,
+ "Return the size of a tag (in bits) for this platform.\n\
+\n\
+The default returns 8.",
+  uint8_t, (), default_memtag_tag_size)
+
+DEFHOOK
+(granule_size,
+ "Return the size in real memory that each byte in shadow memory refers to.\n\
+I.e. if a variable is @var{X} bytes long in memory, then this hook should\n\
+return the value @var{Y} such that the tag in shadow memory spans\n\
+@var{X}/@var{Y} bytes.\n\
+\n\
+Most variables will need to be aligned to this amount since two variables\n\
+that are neighbors in memory and share a tag granule would need to share\n\
+the same tag.\n\
+\n\
+The default returns 16.",
+  uint8_t, (), default_memtag_granule_size)
+
+DEFHOOK
+(insert_random_tag,
+ "Return an RTX representing the value of @var{untagged} but with a\n\
+(possibly) random tag in it.\n\
+Put that value into @var{target} if it is convenient to do so.\n\
+This function is used to generate a tagged base for the current stack frame.",
+  rtx, (rtx untagged, rtx target), default_memtag_insert_random_tag)
+
+DEFHOOK
+(add_tag,
+ "Return an RTX that represents the result of adding @var{addr_offset} to\n\
+the address in pointer @var{base} and @var{tag_offset} to the tag in pointer\n\
+@var{base}.\n\
+The resulting RTX must either be a valid memory address or be able to get\n\
+put into an operand with @code{force_operand}.\n\
+\n\
+Unlike other memtag hooks, this must return an expression and not emit any\n\
+RTL.",
+  rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
+  default_memtag_add_tag)
+
+DEFHOOK
+(set_tag,
+ "Return an RTX representing @var{untagged_base} but with the tag @var{tag}.\n\
+Try and store this in @var{target} if convenient.\n\
+@var{untagged_base} is required to have a zero tag when this hook is called.\n\
+The default of this hook is to set the top byte of @var{untagged_base} to\n\
+@var{tag}.",
+  rtx, (rtx untagged_base, rtx tag, rtx target), default_memtag_set_tag)
+
+DEFHOOK
+(extract_tag,
+ "Return an RTX representing the tag stored in @var{tagged_pointer}.\n\
+Store the result in @var{target} if it is convenient.\n\
+The default represents the top byte of the original pointer.",
+  rtx, (rtx tagged_pointer, rtx target), default_memtag_extract_tag)
+
+DEFHOOK
+(untagged_pointer,
+ "Return an RTX representing @var{tagged_pointer} with its tag set to zero.\n\
+Store the result in @var{target} if convenient.\n\
+The default clears the top byte of the original pointer.",
+  rtx, (rtx tagged_pointer, rtx target), default_memtag_untagged_pointer)
+
 HOOK_VECTOR_END (memtag)
 #undef HOOK_PREFIX
 #define HOOK_PREFIX "TARGET_"
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 0065c686978d7120978430013c73b1055aaf95c7..68e8688a32f18481ee61f06879aacff20163105b 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -287,4 +287,12 @@ extern bool speculation_safe_value_not_needed (bool);
 extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
 
 extern bool default_memtag_can_tag_addresses ();
+extern uint8_t default_memtag_tag_size ();
+extern uint8_t default_memtag_granule_size ();
+extern rtx default_memtag_insert_random_tag (rtx, rtx);
+extern rtx default_memtag_add_tag (rtx, poly_int64, uint8_t);
+extern rtx default_memtag_set_tag (rtx, rtx, rtx);
+extern rtx default_memtag_extract_tag (rtx, rtx);
+extern rtx default_memtag_untagged_pointer (rtx, rtx);
+
 #endif /* GCC_TARGHOOKS_H */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 46cb536041d396c32fd08042581d6d5cd5ad0395..e634df3f6c6837e422246a7736c0de4471ce1e77 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -73,6 +73,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "flags.h"
 #include "explow.h"
+#include "expmed.h"
 #include "calls.h"
 #include "expr.h"
 #include "output.h"
@@ -86,6 +87,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "sbitmap.h"
 #include "function-abi.h"
+#include "attribs.h"
+#include "asan.h"
+#include "emit-rtl.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -2415,10 +2419,115 @@ default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
   return result;
 }
 
+/* How many bits to shift in order to access the tag bits.
+   The default is to store the tag in the top 8 bits of a 64 bit pointer, hence
+   shifting 56 bits will leave just the tag.  */
+#define HWASAN_SHIFT (GET_MODE_PRECISION (Pmode) - 8)
+#define HWASAN_SHIFT_RTX GEN_INT (HWASAN_SHIFT)
+
 bool
 default_memtag_can_tag_addresses ()
 {
   return false;
 }
 
+uint8_t
+default_memtag_tag_size ()
+{
+  return 8;
+}
+
+uint8_t
+default_memtag_granule_size ()
+{
+  return 16;
+}
+
+/* The default implementation of TARGET_MEMTAG_INSERT_RANDOM_TAG.  */
+rtx
+default_memtag_insert_random_tag (rtx untagged, rtx target)
+{
+  gcc_assert (param_hwasan_instrument_stack);
+  if (param_hwasan_random_frame_tag)
+    {
+      rtx fn = init_one_libfunc ("__hwasan_generate_tag");
+      rtx new_tag = emit_library_call_value (fn, NULL_RTX, LCT_NORMAL, QImode);
+      return targetm.memtag.set_tag (untagged, new_tag, target);
+    }
+  else
+    {
+      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
+	 In the future we may add the option emit random tags with inline
+	 instrumentation instead of function calls.  This would be the same
+	 between the kernel and userland.  */
+      return untagged;
+    }
+}
+
+/* The default implementation of TARGET_MEMTAG_ADD_TAG.  */
+rtx
+default_memtag_add_tag (rtx base, poly_int64 offset, uint8_t tag_offset)
+{
+  /* Need to look into what the most efficient code sequence is.
+     This is a code sequence that would be emitted *many* times, so we
+     want it as small as possible.
+
+     There are two places where tag overflow is a question:
+       - Tagging the shadow stack.
+	  (both tagging and untagging).
+       - Tagging addressable pointers.
+
+     We need to ensure both behaviors are the same (i.e. that the tag that
+     ends up in a pointer after "overflowing" the tag bits with a tag addition
+     is the same that ends up in the shadow space).
+
+     The aim is that the behavior of tag addition should follow modulo
+     wrapping in both instances.
+
+     The libhwasan code doesn't have any path that increments a pointer's tag,
+     which means it has no opinion on what happens when a tag increment
+     overflows (and hence we can choose our own behavior).  */
+
+  offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
+  return plus_constant (Pmode, base, offset);
+}
+
+/* The default implementation of TARGET_MEMTAG_SET_TAG.  */
+rtx
+default_memtag_set_tag (rtx untagged, rtx tag, rtx target)
+{
+  gcc_assert (GET_MODE (untagged) == Pmode && GET_MODE (tag) == QImode);
+  tag = expand_simple_binop (Pmode, ASHIFT, tag, HWASAN_SHIFT_RTX, NULL_RTX,
+			     /* unsignedp = */1, OPTAB_WIDEN);
+  rtx ret = expand_simple_binop (Pmode, IOR, untagged, tag, target,
+				 /* unsignedp = */1, OPTAB_DIRECT);
+  gcc_assert (ret);
+  return ret;
+}
+
+/* The default implementation of TARGET_MEMTAG_EXTRACT_TAG.  */
+rtx
+default_memtag_extract_tag (rtx tagged_pointer, rtx target)
+{
+  rtx tag = expand_simple_binop (Pmode, LSHIFTRT, tagged_pointer,
+				 HWASAN_SHIFT_RTX, target,
+				 /* unsignedp = */0,
+				 OPTAB_DIRECT);
+  rtx ret = gen_lowpart (QImode, tag);
+  gcc_assert (ret);
+  return ret;
+}
+
+/* The default implementation of TARGET_MEMTAG_UNTAGGED_POINTER.  */
+rtx
+default_memtag_untagged_pointer (rtx tagged_pointer, rtx target)
+{
+  rtx tag_mask = gen_int_mode ((HOST_WIDE_INT_1U << HWASAN_SHIFT) - 1, Pmode);
+  rtx untagged_base = expand_simple_binop (Pmode, AND, tagged_pointer,
+					   tag_mask, target, true,
+					   OPTAB_DIRECT);
+  gcc_assert (untagged_base);
+  return untagged_base;
+}
+
 #include "gt-targhooks.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 2a3e7c064a5fbb6913481104975ca85615e49f8e..9938b6afbd4fa22898dbc3c29b92061a71810b08 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -512,6 +512,9 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      if (flag_sanitize & SANITIZE_HWADDRESS)
+	hwasan_finish_file ();
+
       omp_finish_file ();
 
       output_shared_constant_pool ();


[-- Attachment #2: hwasan5-with-doc-moved.patch.gz --]
[-- Type: application/gzip, Size: 14679 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 4/X] libsanitizer: options: Add hwasan flags and argument parsing
  2020-11-18 17:57         ` Richard Sandiford
@ 2020-11-20 18:48           ` Matthew Malcomson
  2020-11-23 20:12             ` Richard Sandiford
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-20 18:48 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: gcc-patches, Richard Earnshaw, Kyrylo Tkachov, Martin Liska

[-- Attachment #1: Type: text/plain, Size: 27324 bytes --]

Hi there,

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

That's the only change, Ok for trunk?

Matthew


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

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

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

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

gcc/ChangeLog:

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



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


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


[-- Attachment #2: hwasan4-with-doc-moved.patch --]
[-- Type: text/plain, Size: 24165 bytes --]

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


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 7/X] libsanitizer:  Add tests
  2020-08-17 14:13     ` [PATCH 7/X] libsanitizer: Add tests Matthew Malcomson
@ 2020-11-20 19:14       ` Richard Sandiford
  2020-11-23 18:08         ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Richard Sandiford @ 2020-11-20 19:14 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, jakub, Richard Earnshaw, kcc, dodji, joseph

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> Adding hwasan tests.
>
> Only interesting thing here is that we have to make sure the tagging mechanism
> is deterministic to avoid flaky tests.

Sorry for not reviewing this one earlier.  TBH I only spot-checked
the tests themselves (they look good).  But on hwasan-dg.exp: I think
we should try to avoid so much cut-&-paste between asan-dg.exp and
hwasan-dg.exp.

For one thing (and obviously not your fault), it seems odd to me that
check_effective_target_fsanitize_address is defined in asan-dg.exp.
I think it and the new check_effective_targets* should be defined
in target-supports.exp instead.  On:

> +proc check_effective_target_hwaddress_exec {} {
> +    if ![check_runtime hwaddress_exec {
> +	int main (void) { return 0; }
> +    }] {
> +	return 0;
> +    }
> +    return 1;
> +
> +    # hwasan doesn't work if there's a ulimit on virtual memory.
> +    if ![is_remote target] {
> +	if [catch {exec sh -c "ulimit -v"} ulimit_v] {
> +	    # failed to get ulimit
> +	} elseif [regexp {^[0-9]+$} $ulimit_v] {
> +	    # ulimit -v gave a numeric limit
> +	    warning "skipping hwasan tests due to ulimit -v"
> +	    return 0;
> +	}
> +    }
> +}

either the “hwasan doesn't work” block or the early “return 1” should
be removed.  (I'm guessing the former.)

> +proc hwasan_include_flags {} {
> +    global srcdir
> +    global TESTING_IN_BUILD_TREE
> +
> +    set flags ""
> +
> +    if { [is_remote host] || ! [info exists TESTING_IN_BUILD_TREE] } {
> +      return "${flags}"
> +    }
> +
> +    set flags "-I$srcdir/../../libsanitizer/include"
> +
> +    return "$flags"
> +}

This is identical to the asan version, but I guess it's small enough
that the cut-&-paste doesn't matter.

> +
> +#
> +# hwasan_link_flags -- compute library path and flags to find libhwasan.
> +# (originally from g++.exp)
> +#
> +
> +proc hwasan_link_flags { paths } {
> +    global srcdir
> +    global ld_library_path
> +    global shlib_ext
> +    global hwasan_saved_library_path
> +
> +    set gccpath ${paths}
> +    set flags ""
> +
> +    set shlib_ext [get_shlib_extension]
> +    set hwasan_saved_library_path $ld_library_path
> +
> +    if { $gccpath != "" } {
> +      if { [file exists "${gccpath}/libsanitizer/hwasan/.libs/libhwasan.a"]
> +	   || [file exists "${gccpath}/libsanitizer/hwasan/.libs/libhwasan.${shlib_ext}"] } {
> +	  append flags " -B${gccpath}/libsanitizer/ "
> +	  append flags " -B${gccpath}/libsanitizer/hwasan/ "
> +	  append flags " -L${gccpath}/libsanitizer/hwasan/.libs "
> +	  append ld_library_path ":${gccpath}/libsanitizer/hwasan/.libs"
> +      }
> +    } else {
> +      global tool_root_dir
> +
> +      set libhwasan [lookfor_file ${tool_root_dir} libhwasan]
> +      if { $libhwasan != "" } {
> +	  append flags "-L${libhwasan} "
> +	  append ld_library_path ":${libhwasan}"
> +      }
> +    }
> +
> +    set_ld_library_path_env_vars
> +
> +    return "$flags"
> +}

Here I'd suggest:

- In asan-dg.exp, have:

    # Compute library path and flags to find libsanitizer library LIB.
    # (originally from g++.exp).
    proc asan_link_flags_1 { paths lib } {
        …body…
    }

    …existing comment…
    proc asan_link_flags { paths } {
        return [asan_link_flags_1 $paths asan]
    }

  where …body… is more or less the current body of asan_link_flags with
  “asan” replaced by ${lib}.  E.g.:

    global ${lib}_saved_library_path
    …
    set ${lib}_saved_library_path $ld_library_path

  is fine.  For local variables like:

      set libasan [lookfor_file ${tool_root_dir} libasan]
      if { $libasan != "" } {
	  append flags "-L${libasan} "
	  append ld_library_path ":${libasan}"
      }

  it would make more sense to use a generic name instead, e.g.:

      set libdir [lookfor_file ${tool_root_dir} lib${lib}]
      if { $libdir != "" } {
	  append flags "-L${libdir} "
	  append ld_library_path ":${libdir}"
      }

- Have hwasan-dg.exp include asan-dg.exp.

- Have:

    proc hwasan_link_flags { paths } {
        return [asan_link_flags_1 $paths hwasan]
    }

> +
> +#
> +# hwasan_init -- called at the start of each subdir of tests
> +#
> +
> +proc hwasan_init { args } {
> +    global TEST_ALWAYS_FLAGS
> +    global ALWAYS_CXXFLAGS
> +    global TOOL_OPTIONS
> +    global hwasan_saved_TEST_ALWAYS_FLAGS
> +    global hwasan_saved_ALWAYS_CXXFLAGS
> +
> +    setenv HWASAN_OPTIONS "random_tags=0"
> +
> +    set link_flags ""
> +    if ![is_remote host] {
> +	if [info exists TOOL_OPTIONS] {
> +	    set link_flags "[hwasan_link_flags [get_multilibs ${TOOL_OPTIONS}]]"
> +	} else {
> +	    set link_flags "[hwasan_link_flags [get_multilibs]]"
> +	}
> +    }
> +
> +    set include_flags "[hwasan_include_flags]"
> +
> +    if [info exists TEST_ALWAYS_FLAGS] {
> +	set hwasan_saved_TEST_ALWAYS_FLAGS $TEST_ALWAYS_FLAGS
> +    }
> +    if [info exists ALWAYS_CXXFLAGS] {
> +	set hwasan_saved_ALWAYS_CXXFLAGS $ALWAYS_CXXFLAGS
> +	set ALWAYS_CXXFLAGS [concat "{ldflags=$link_flags}" $ALWAYS_CXXFLAGS]
> +	set ALWAYS_CXXFLAGS [concat "{additional_flags=-fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags}" $ALWAYS_CXXFLAGS]
> +    } else {
> +	if [info exists TEST_ALWAYS_FLAGS] {
> +	    set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags $TEST_ALWAYS_FLAGS"
> +	} else {
> +	    set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags"
> +	}
> +    }
> +}

I agree this one is different enough from the asan version that it
isn't worth commonising the code.

> +
> +#
> +# hwasan_finish -- called at the start of each subdir of tests
> +#
> +
> +proc hwasan_finish { args } {
> +    global TEST_ALWAYS_FLAGS
> +    global hwasan_saved_TEST_ALWAYS_FLAGS
> +    global hwasan_saved_ALWAYS_CXXFLAGS
> +    global hwasan_saved_library_path
> +    global ld_library_path
> +
> +    unsetenv HWASAN_OPTIONS
> +
> +    if [info exists hwasan_saved_ALWAYS_CXXFLAGS ] {
> +	set ALWAYS_CXXFLAGS $hwasan_saved_ALWAYS_CXXFLAGS
> +    } else {
> +	if [info exists hwasan_saved_TEST_ALWAYS_FLAGS] {
> +	    set TEST_ALWAYS_FLAGS $hwasan_saved_TEST_ALWAYS_FLAGS
> +	} else {
> +	    unset TEST_ALWAYS_FLAGS
> +	}
> +    }
> +    set ld_library_path $hwasan_saved_library_path
> +    set_ld_library_path_env_vars
> +    clear_effective_target_cache
> +}

And I guess then it would be more consistent to keep this separate too,
if the _init function is.

> +
> +# Symbolize lines like
> +#   #2 0xdeadbeef (/some/path/libsanitizer.so.0.0.0+0xbeef)
> +# in $output using addr2line to
> +#   #2 0xdeadbeef in foobar file:123
> +proc hwasan_symbolize { output } {
> +    set addresses [regexp -inline -all -line "^ *#\[0-9\]+ 0x\[0-9a-f\]+ \[(\](\[^)\]+)\[+\](0x\[0-9a-f\]+)\[)\]$" "$output"]
> +    if { [llength $addresses] > 0 } {
> +	set addr2line_name [find_binutils_prog addr2line]
> +	set idx 1
> +	while { $idx < [llength $addresses] } {
> +	    set key [regsub -all "\[\]\[\]" [lindex $addresses $idx] "\\\\&"]
> +	    set val [lindex $addresses [expr $idx + 1]]
> +	    lappend arr($key) $val
> +	    set idx [expr $idx + 3]
> +	}
> +	foreach key [array names arr] {
> +	    set args "-f -e $key $arr($key)"
> +	    set status [remote_exec host "$addr2line_name" "$args"]
> +	    if { [lindex $status 0] > 0 } continue
> +	    regsub -all "\r\n" [lindex $status 1] "\n" addr2line_output
> +	    regsub -all "\[\n\r\]BFD: \[^\n\r\]*" $addr2line_output "" addr2line_output
> +	    regsub -all "^BFD: \[^\n\r\]*\[\n\r\]" $addr2line_output "" addr2line_output
> +	    set addr2line_output [regexp -inline -all -line "^\[^\n\r]*" $addr2line_output]
> +	    set idx 0
> +	    foreach val $arr($key) {
> +		if { [expr $idx + 1] < [llength $addr2line_output] } {
> +		    set fnname [lindex $addr2line_output $idx]
> +		    set fileline [lindex $addr2line_output [expr $idx + 1]]
> +		    if { "$fnname" != "??" } {
> +			set newkey "$key+$val"
> +			set repl($newkey) "$fnname $fileline"
> +		    }
> +		    set idx [expr $idx + 2]
> +		}
> +	    }
> +	}
> +	set idx 0
> +	set new_output ""
> +	while {[regexp -start $idx -indices " #\[0-9\]+ 0x\[0-9a-f\]+ \[(\](\[^)\]+\[+\]0x\[0-9a-f\]+)\[)\]" "$output" -> addr] > 0} {
> +	    set low [lindex $addr 0]
> +	    set high [lindex $addr 1]
> +	    set val [string range "$output" $low $high]
> +	    append new_output [string range "$output" $idx [expr $low - 2]]
> +	    if [info exists repl($val)] {
> +		append new_output "in $repl($val)"
> +	    } else {
> +		append new_output "($val)"
> +	    }
> +	    set idx [expr $high + 2]
> +	}
> +	append new_output [string range "$output" $idx [string length "$output"]]
> +	return "$new_output"
> +    }
> +    return "$output"
> +}
> +
> +# Return a list of gtest tests, printed in the form
> +# DEJAGNU_GTEST_TEST AddressSanitizer_SimpleDeathTest
> +# DEJAGNU_GTEST_TEST AddressSanitizer_VariousMallocsTest
> +proc hwasan_get_gtest_test_list { output } {
> +    set idx 0
> +    set ret ""
> +    while {[regexp -start $idx -indices "DEJAGNU_GTEST_TEST (\[^\n\r\]*)(\r\n|\n|\r)" "$output" -> testname] > 0} {
> +	set low [lindex $testname 0]
> +	set high [lindex $testname 1]
> +	set val [string range "$output" $low $high]
> +	lappend ret $val
> +	set idx [expr $high + 1]
> +    }
> +    return $ret
> +}
> +
> +# Return a list of gtest EXPECT_DEATH tests, printed in the form
> +# DEJAGNU_GTEST_EXPECT_DEATH1 statement DEJAGNU_GTEST_EXPECT_DEATH1 regexp DEJAGNU_GTEST_EXPECT_DEATH1
> +# DEJAGNU_GTEST_EXPECT_DEATH2 other statement DEJAGNU_GTEST_EXPECT_DEATH2 other regexp DEJAGNU_GTEST_EXPECT_DEATH2
> +proc hwasan_get_gtest_expect_death_list { output } {
> +    set idx 0
> +    set ret ""
> +    while {[regexp -start $idx -indices "DEJAGNU_GTEST_EXPECT_DEATH(\[0-9\]*)" "$output" -> id ] > 0} {
> +	set low [lindex $id 0]
> +	set high [lindex $id 1]
> +	set val_id [string range "$output" $low $high]
> +	if {[regexp -start $low -indices "$val_id (.*) DEJAGNU_GTEST_EXPECT_DEATH$val_id (.*) DEJAGNU_GTEST_EXPECT_DEATH$val_id\[\n\r\]" "$output" whole statement regexpr ] == 0} { break }
> +	set low [lindex $statement 0]
> +	set high [lindex $statement 1]
> +	set val_statement [string range "$output" $low $high]
> +	set low [lindex $regexpr 0]
> +	set high [lindex $regexpr 1]
> +	set val_regexpr [string range "$output" $low $high]
> +	lappend ret [list "$val_id" "$val_statement" "$val_regexpr"]
> +	set idx [lindex $whole 1]
> +    }
> +    return $ret
> +}

AFAICT these three functions are identical to the asan-dg.exp versions,
with no asan/hwasan variance.  I think it would be better to reuse the
asan versions for hwasan too.

> +
> +# Replace ${tool}_load with a wrapper so that we can symbolize the output.
> +if { [info procs ${tool}_load] != [list] \
> +      && [info procs saved_hwasan_${tool}_load] == [list] } {
> +    rename ${tool}_load saved_hwasan_${tool}_load
> +
> +    proc ${tool}_load { program args } {
> +	global tool
> +	global hwasan_last_gtest_test_list
> +	global hwasan_last_gtest_expect_death_list
> +	set result [eval [list saved_hwasan_${tool}_load $program] $args]
> +	set output [lindex $result 1]
> +	set symbolized_output [hwasan_symbolize "$output"]
> +	set hwasan_last_gtest_test_list [hwasan_get_gtest_test_list "$output"]
> +	set hwasan_last_gtest_expect_death_list [hwasan_get_gtest_expect_death_list "$output"]
> +	set result [list [lindex $result 0] $symbolized_output]
> +	return $result
> +    }
> +}

Also, I'm not sure we need separate hwasan versions of these globals.

> +
> +# Utility for running gtest hwasan emulation under dejagnu, invoked via dg-final.
> +# Call pass if variable has the desired value, otherwise fail.
> +#
> +# Argument 0 handles expected failures and the like
> +proc hwasan-gtest { args } {
> +    global tool
> +    global hwasan_last_gtest_test_list
> +    global hwasan_last_gtest_expect_death_list
> +
> +    if { ![info exists hwasan_last_gtest_test_list] } { return }
> +    if { [llength $hwasan_last_gtest_test_list] == 0 } { return }
> +    if { ![isnative] || [is_remote target] } { return }
> +
> +    set gtest_test_list $hwasan_last_gtest_test_list
> +    unset hwasan_last_gtest_test_list
> +
> +    if { [llength $args] >= 1 } {
> +	switch [dg-process-target [lindex $args 0]] {
> +	    "S" { }
> +	    "N" { return }
> +	    "F" { setup_xfail "*-*-*" }
> +	    "P" { }
> +	}
> +    }
> +
> +    # This assumes that we are three frames down from dg-test, and that
> +    # it still stores the filename of the testcase in a local variable "name".
> +    # A cleaner solution would require a new DejaGnu release.
> +    upvar 2 name testcase
> +    upvar 2 prog prog
> +
> +    set output_file "[file rootname [file tail $prog]].exe"
> +
> +    foreach gtest $gtest_test_list {
> +	set testname "$testcase $gtest"
> +	set status -1
> +
> +	setenv DEJAGNU_GTEST_ARG "$gtest"
> +	set result [${tool}_load ./$output_file $gtest]
> +	unsetenv DEJAGNU_GTEST_ARG
> +	set status [lindex $result 0]
> +	set output [lindex $result 1]
> +	if { "$status" == "pass" } {
> +	    pass "$testname execution test"
> +	    if { [info exists hwasan_last_gtest_expect_death_list] } {
> +		set gtest_expect_death_list $hwasan_last_gtest_expect_death_list
> +		foreach gtest_death $gtest_expect_death_list {
> +		    set id [lindex $gtest_death 0]
> +		    set testname "$testcase $gtest [lindex $gtest_death 1]"
> +		    set regexpr [lindex $gtest_death 2]
> +		    set status -1
> +
> +		    setenv DEJAGNU_GTEST_ARG "$gtest:$id"
> +		    set result [${tool}_load ./$output_file "$gtest:$id"]
> +		    unsetenv DEJAGNU_GTEST_ARG
> +		    set status [lindex $result 0]
> +		    set output [lindex $result 1]
> +		    if { "$status" == "fail" } {
> +			pass "$testname execution test"
> +			if { ![regexp $regexpr ${output}] } {
> +			    fail "$testname output pattern test"
> +			    send_log "Output should match: $regexpr\n"
> +			} else {
> +			    pass "$testname output pattern test"
> +			}
> +		    } elseif { "$status" == "pass" } {
> +			fail "$testname execution test"
> +		    } else {
> +			$status "$testname execution test"
> +		    }
> +		}
> +	    }
> +	} else {
> +	    $status "$testname execution test"
> +	}
> +	unset hwasan_last_gtest_expect_death_list
> +    }
> +
> +    return
> +}

And if there are no separate versions of those variables, and if
hwasan-dg.exp does include asan-dg.exp, then I think it would be
enough to replace hwasan_symbolize onwards with:

# Utility for running gtest hwasan emulation under dejagnu, invoked via dg-final.
# Call pass if variable has the desired value, otherwise fail.
#
# Argument 0 handles expected failures and the like
proc hwasan-gtest { args } {
    asan-gtest {*}$args
}

Thanks,
Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Document --with-build-config=bootstrap-asan option.
  2020-11-20 18:11           ` Matthew Malcomson
@ 2020-11-22 22:49             ` Gerald Pfeifer
  0 siblings, 0 replies; 150+ messages in thread
From: Gerald Pfeifer @ 2020-11-22 22:49 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, Martin Liska, Richard Earnshaw, Kyrylo Tkachov,
	Joseph S. Myers, Richard Sandiford

On Fri, 20 Nov 2020, Matthew Malcomson wrote:
>>> The patch is okay modulo the question above.
> Apologies for bringing up something from this far back, but I've just 
> noticed I never actually committed this patch.

Actually thank you for doing so!  (And I am known to sometimes bring
up things quite older than this. ;-)

> Did the above line mean approval once the option was moved into the 
> existing table?  Or was it just explaining that the position was the 
> only problem you saw?

Yes - the former.

> In other words: is the patch I proposed below Ok for trunk?

Yes, please.  (I believe there may be an overly long line in the
patch, which will be good to wrap before committing, but please
go ahead in any case.)

(This is also okay to backport to GCC 10.)

Thanks,
Gerald

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [Patch 0/X] HWASAN v4
  2020-11-20 18:42               ` Matthew Malcomson
@ 2020-11-23  8:18                 ` Martin Liška
  2020-11-24 15:46                   ` libsanitizer: Hwasan reporting check for dladdr failing Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Martin Liška @ 2020-11-23  8:18 UTC (permalink / raw)
  To: Matthew Malcomson, gcc-patches
  Cc: jakub, Richard Earnshaw, kcc, dodji, joseph

On 11/20/20 7:42 PM, Matthew Malcomson wrote:
> On 13/11/2020 17:22, Martin Liška wrote:
>> On 11/13/20 5:57 PM, Matthew Malcomson wrote:
>>> Hi there,
>>>
>>> Thanks for the heads-up.
>>> As it turns out the most recent `libhwasan` crashes when displaying an address on the stack in Linux.
>>
>> Hello.
>>
>> What a bad luck.
>>
>>>
>>> I'm currently working on getting it fixed here https://reviews.llvm.org/D91344#2393371 <https://reviews.llvm.org/D91344#2393371> .
>>> If this hwasan patch series gets approved and if that patch goes in would it be feasible to bump the libsanitizer merge to whatever version that would be?
>>>
>>> If not (maybe because stage1 would be finished?) then could/would we end up using the LOCAL_PATCHES approach?
>>
>> Since now, I would prefer doing cherry picks. Hopefully, we'll end just with couple of patches.
> 
> That makes sense, there's just one patch I need 83ac1820.
> 
> As far as I can tell from the history, the process is simply to apply the patch in GCC, commit it with a ChangeLog etc as if it were a GCC patch, and then add the hash into LOCAL_PATCHES as a separate commit.

Hello.

Just add it as we did for e.g. b69f33f477b9ac38af3c39465600ae74a3554878.
You don't need to write ChangeLog entries. Note that libsanitizer is one of the location
where we are not strict about ChangeLog format verification.

And yes, then you add it simply to LOCAL_PATCHES.

> 
> Is that right?
> 
> 
> Given that it looks like the hwasan patch series is nearing going in now, I'd like to make sure I know how the library is getting added.
> 
> Is the plan something like the below?
> 1) You add the libhwasan update (i.e. this patch you posted).
> 2) I add the cherry-pick from LLVM compiler-rt (once it's approved)
>      and a separate commit updating LOCAL_PATCHES.
> 3) I add the hwasan patch series.

Yes.

> 
> Or would it make more sense for me to apply your patch below with `--author` using your details (so it goes in the ChangeLog that way)?

Don't worry with that, just apply it as you want.

Thanks,
Martin

> 
> Thanks!
> Matthew
> 
> 
>>
>> Thanks,
>> Martin
>>
>>>
>>> Thanks,
>>> Matthew
>>> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>>> *From:* Martin Liška <mliska@suse.cz>
>>> *Sent:* 13 November 2020 16:33
>>> *To:* Matthew Malcomson <Matthew.Malcomson@arm.com>; gcc-patches@gcc.gnu.org <gcc-patches@gcc.gnu.org>
>>> *Cc:* jakub@redhat.com <jakub@redhat.com>; Richard Earnshaw <Richard.Earnshaw@arm.com>; kcc@google.com <kcc@google.com>; dodji@redhat.com <dodji@redhat.com>; joseph@codesourcery.com <joseph@codesourcery.com>
>>> *Subject:* Re: [Patch 0/X] HWASAN v4
>>> On 10/16/20 11:03 AM, Martin Li�ka wrote:
>>>> Hello.
>>>>
>>>> I've just merged libsanitizer and there's the corresponding part that includes
>>>> libhwasan.
>>>>
>>>> Martin
>>>
>>> Hey.
>>>
>>> I've just made last merge from upstream, there's corresponding hwasan part.
>>>
>>> Martin
>>
> 


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 7/X] libsanitizer:  Add tests
  2020-11-20 19:14       ` Richard Sandiford
@ 2020-11-23 18:08         ` Matthew Malcomson
  2020-11-23 20:30           ` Richard Sandiford
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-23 18:08 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: gcc-patches, Richard Earnshaw, Kyrylo.Tkachov

[-- Attachment #1: Type: text/plain, Size: 4538 bytes --]


Hello,

Update of the hwasan tests attached.

Ok for trunk?

(NOTE on the state of the entire patch series:
  Currently re-testing everything from scratch to ensure I didn't get tripped up
  from old state carried around.
  Also looking into PR 97941 on hwasan.  As yet don't understand what's going
  on there and having difficulty reproducing.)


------------

Adding hwasan tests.

Only interesting thing here is that we have to make sure the tagging mechanism
is deterministic to avoid flaky tests.

gcc/testsuite/ChangeLog:

	* c-c++-common/ubsan/sanitize-recover-7.c: Update error message format.
	* lib/asan-dg.exp (check_effective_target_fsanitize_address): Move to
	target-supports.exp.
	(asan_link_flags): Implement as a helper function asan_link_flags_1
	which asan_link_flags and hwasan_link_flags use.
	(asan_link_flags_1): Parametrised version of asan_link_flags.
	* lib/target-supports.exp (check_effective_target_fsanitize_address):
	Move from asan-dg.exp.
	(check_effective_target_fsanitize_hwaddress,
	check_effective_target_hwaddress_exec): New.
	* c-c++-common/hwasan/aligned-alloc.c: New test.
	* c-c++-common/hwasan/alloca-array-accessible.c: New test.
	* c-c++-common/hwasan/alloca-base-init.c: New test.
	* c-c++-common/hwasan/alloca-gets-different-tag.c: New test.
	* c-c++-common/hwasan/alloca-outside-caught.c: New test.
	* c-c++-common/hwasan/arguments-1.c: New test.
	* c-c++-common/hwasan/arguments-2.c: New test.
	* c-c++-common/hwasan/arguments-3.c: New test.
	* c-c++-common/hwasan/arguments.c: New test.
	* c-c++-common/hwasan/asan-pr63316.c: New test.
	* c-c++-common/hwasan/asan-pr70541.c: New test.
	* c-c++-common/hwasan/asan-pr78106.c: New test.
	* c-c++-common/hwasan/asan-pr79944.c: New test.
	* c-c++-common/hwasan/asan-rlimit-mmap-test-1.c: New test.
	* c-c++-common/hwasan/bitfield-1.c: New test.
	* c-c++-common/hwasan/bitfield-2.c: New test.
	* c-c++-common/hwasan/builtin-special-handling.c: New test.
	* c-c++-common/hwasan/check-interface.c: New test.
	* c-c++-common/hwasan/halt_on_error-1.c: New test.
	* c-c++-common/hwasan/handles-poly_int-marked-vars.c: New test.
	* c-c++-common/hwasan/heap-overflow.c: New test.
	* c-c++-common/hwasan/hwasan-poison-optimisation.c: New test.
	* c-c++-common/hwasan/hwasan-thread-access-parent.c: New test.
	* c-c++-common/hwasan/hwasan-thread-basic-failure.c: New test.
	* c-c++-common/hwasan/hwasan-thread-clears-stack.c: New test.
	* c-c++-common/hwasan/hwasan-thread-success.c: New test.
	* c-c++-common/hwasan/kernel-defaults.c: New test.
	* c-c++-common/hwasan/large-aligned-0.c: New test.
	* c-c++-common/hwasan/large-aligned-1.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-0.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-1.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-2.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-3.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-4.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-5.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-6.c: New test.
	* c-c++-common/hwasan/large-aligned-untagging-7.c: New test.
	* c-c++-common/hwasan/macro-definition.c: New test.
	* c-c++-common/hwasan/no-sanitize-attribute.c: New test.
	* c-c++-common/hwasan/param-instrument-mem-intrinsics.c: New test.
	* c-c++-common/hwasan/param-instrument-reads-and-writes.c: New test.
	* c-c++-common/hwasan/param-instrument-reads.c: New test.
	* c-c++-common/hwasan/param-instrument-writes.c: New test.
	* c-c++-common/hwasan/random-frame-tag.c: New test.
	* c-c++-common/hwasan/sanity-check-pure-c.c: New test.
	* c-c++-common/hwasan/setjmp-longjmp-0.c: New test.
	* c-c++-common/hwasan/setjmp-longjmp-1.c: New test.
	* c-c++-common/hwasan/stack-tagging-basic-0.c: New test.
	* c-c++-common/hwasan/stack-tagging-basic-1.c: New test.
	* c-c++-common/hwasan/stack-tagging-disable.c: New test.
	* c-c++-common/hwasan/unprotected-allocas-0.c: New test.
	* c-c++-common/hwasan/unprotected-allocas-1.c: New test.
	* c-c++-common/hwasan/use-after-free.c: New test.
	* c-c++-common/hwasan/vararray-outside-caught.c: New test.
	* c-c++-common/hwasan/vararray-stack-restore-correct.c: New test.
	* c-c++-common/hwasan/very-large-objects.c: New test.
	* g++.dg/hwasan/hwasan.exp: New test.
	* g++.dg/hwasan/rvo-handled.C: New test.
	* gcc.dg/hwasan/hwasan.exp: New test.
	* gcc.dg/hwasan/nested-functions-0.c: New test.
	* gcc.dg/hwasan/nested-functions-1.c: New test.
	* gcc.dg/hwasan/nested-functions-2.c: New test.
	* lib/hwasan-dg.exp: New file.

[-- Attachment #2: hwasan-testsuite.patch.gz --]
[-- Type: application/gzip, Size: 15689 bytes --]

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Update: [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN
  2020-11-20 18:46             ` Matthew Malcomson
@ 2020-11-23 20:10               ` Richard Sandiford
  2020-11-23 20:16               ` Richard Sandiford
  2020-11-24 12:30               ` Hongtao Liu
  2 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-11-23 20:10 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: Richard Earnshaw, Kyrylo Tkachov, gcc-patches, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> Hi there,
>
> I was just doing some double-checks and noticed I'd placed the
> documentation in the wrong section of tm.texi.  The `MEMTAG` hooks were
> documented in the `Register Classes` section, so I've now moved it to
> the `Misc` section.
>
> That's the only change, Ok for trunk?

OK, thanks.

Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 4/X] libsanitizer: options: Add hwasan flags and argument parsing
  2020-11-20 18:48           ` Matthew Malcomson
@ 2020-11-23 20:12             ` Richard Sandiford
  0 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-11-23 20:12 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: gcc-patches, Richard Earnshaw, Kyrylo Tkachov, Martin Liska

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> Hi there,
>
> I was just doing some double-checks and noticed I'd placed the
> documentation in the wrong section of tm.texi.  The `MEMTAG` hooks were
> documented in the `Register Classes` section, so I've now moved it to
> the `Misc` section.
>
> That's the only change, Ok for trunk?

OK, thanks.

Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Update: [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN
  2020-11-20 18:46             ` Matthew Malcomson
  2020-11-23 20:10               ` Richard Sandiford
@ 2020-11-23 20:16               ` Richard Sandiford
  2020-11-24 12:30               ` Hongtao Liu
  2 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-11-23 20:16 UTC (permalink / raw)
  To: Matthew Malcomson
  Cc: Richard Earnshaw, Kyrylo Tkachov, gcc-patches, Martin Liska

Sorry for the earlier OK, was replying to the wrong message…

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> @@ -1216,6 +1255,24 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>  	    {
>  	      offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
>  	      base_align = crtl->max_used_stack_slot_alignment;
> +
> +	      if (hwasan_sanitize_stack_p ())
> +		{
> +		  /* Align again since the point of this alignment is to handle
> +		     the "end" of the object (i.e. smallest address after the
> +		     stack object).  For FRAME_GROWS_DOWNWARD that requires
> +		     aligning the stack before allocating, but for a frame that
> +		     grows upwards that requires aligning the stack after
> +		     allocation.
> +
> +		     Use `frame_offset` to record the offset value rather than
> +		     offset since the `frame_offset` describes the extent

What I meant here was to quote “offset”, i.e.:

		     Use `frame_offset` to record the offset value rather than
		     `offset` since `frame_offset` describes the extent

Without the quoting, “the offset value rather than offset” was quite
hard to parse.

OK with that change, thanks.

Richard

> +		     allocated for this particular variable while `offset`
> +		     describes the address that this variable starts at.  */
> +		  align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
> +		  hwasan_record_stack_var (virtual_stack_vars_rtx, base,
> +					   hwasan_orig_offset, frame_offset);
> +		}
>  	    }
>  	}
>        else

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: [PATCH 7/X] libsanitizer:  Add tests
  2020-11-23 18:08         ` Matthew Malcomson
@ 2020-11-23 20:30           ` Richard Sandiford
  0 siblings, 0 replies; 150+ messages in thread
From: Richard Sandiford @ 2020-11-23 20:30 UTC (permalink / raw)
  To: Matthew Malcomson; +Cc: gcc-patches, Richard Earnshaw, Kyrylo.Tkachov

Matthew Malcomson <matthew.malcomson@arm.com> writes:
> new file mode 100644
> index 0000000000000000000000000000000000000000..a6a11866823ae8ba9c20b79ac068e84aa73e053d
> --- /dev/null
> +++ b/gcc/testsuite/lib/hwasan-dg.exp
> @@ -0,0 +1,121 @@
> +# Copyright (C) 2012-2019 Free Software Foundation, Inc.

2012-2020

> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with GCC; see the file COPYING3.  If not see
> +# <http://www.gnu.org/licenses/>.
> +
> +# Return 1 if compilation with -fsanitize=hwaddress is error-free for trivial
> +# code, 0 otherwise.
> +
> +load_lib asan-dg.exp

The “Return 1” comment is now misplaced.  However…

> diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
> index ceee78c26a9379f908659ae3d594bca104227d04..18b8c5a60a089fcac7bf2a41adcc373f995fca0f 100644
> --- a/gcc/testsuite/lib/target-supports.exp
> +++ b/gcc/testsuite/lib/target-supports.exp
> @@ -10633,6 +10633,30 @@ proc check_effective_target_movdir { } {
>      } "-mmovdiri -mmovdir64b" ]
>  }
>  
> +# Return 1 if compilation with -fsanitize=address is error-free for trivial
> +# code, 0 otherwise.
> +
> +proc check_effective_target_fsanitize_address {} {
> +    if ![check_no_compiler_messages fsanitize_address executable {
> +	int main (void) { return 0; }
> +    }] {
> +	return 0;
> +    }
> +
> +    # asan doesn't work if there's a ulimit on virtual memory.
> +    if ![is_remote target] {
> +	if [catch {exec sh -c "ulimit -v"} ulimit_v] {
> +	    # failed to get ulimit
> +	} elseif [regexp {^[0-9]+$} $ulimit_v] {
> +	    # ulimit -v gave a numeric limit
> +	    warning "skipping asan tests due to ulimit -v"
> +	    return 0;
> +	}
> +    }
> +
> +    return 1;
> +}
> +

Looking at this again, I realise I was wrong to say that the
effective_target routines should be here.  I guess this function was in
asan-dg.exp because it doesn't add any flags to ensure that the address
sanitiser is used.  Instead it relies on the harness to add the flags
first.

So could you put this back but add:

#
# NOTE: This function should only be used between calls to asan_init
# and asan_finish.  It is therefore defined here rather than in
# target-supports.exp.

Similarly for the hwasan routines, but with hwasan_init and hwasan_finish.

Sorry for the runaround.

OK with that change, no need for another review round.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Update: [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN
  2020-11-20 18:46             ` Matthew Malcomson
  2020-11-23 20:10               ` Richard Sandiford
  2020-11-23 20:16               ` Richard Sandiford
@ 2020-11-24 12:30               ` Hongtao Liu
  2020-11-24 16:45                 ` Matthew Malcomson
  2 siblings, 1 reply; 150+ messages in thread
From: Hongtao Liu @ 2020-11-24 12:30 UTC (permalink / raw)
  To: Matthew Malcomson; +Cc: Richard Sandiford, Richard Earnshaw, GCC Patches

Hi:
  I'm learning about this patch, and I see one place that might be
slighted improved.

+      poly_int64 size = (top - bot);
+
+      /* Assert the edge of each variable is aligned to the HWASAN tag granule
+        size.  */
+      gcc_assert (multiple_p (top, HWASAN_TAG_GRANULE_SIZE));
+      gcc_assert (multiple_p (bot, HWASAN_TAG_GRANULE_SIZE));
+      gcc_assert (multiple_p (size, HWASAN_TAG_GRANULE_SIZE));
+

The last gcc_assert looks redundant?

On Sat, Nov 21, 2020 at 2:48 AM Matthew Malcomson via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
>
>
> Hi there,
>
> I was just doing some double-checks and noticed I'd placed the
> documentation in the wrong section of tm.texi.  The `MEMTAG` hooks were
> documented in the `Register Classes` section, so I've now moved it to
> the `Misc` section.
>
> That's the only change, Ok for trunk?
>
> Matthew
>
>
> ------------------------------------------------------------
>
>
>
> Handling stack variables has three features.
>
> 1) Ensure HWASAN required alignment for stack variables
>
> When tagging shadow memory, we need to ensure that each tag granule is
> only used by one variable at a time.
>
> This is done by ensuring that each tagged variable is aligned to the tag
> granule representation size and also ensure that the end of each
> object is aligned to ensure the start of any other data stored on the
> stack is in a different granule.
>
> This patch ensures the above by forcing the stack pointer to be aligned
> before and after allocating any stack objects. Since we are forcing
> alignment we also use `align_local_variable` to ensure this new alignment
> is advertised properly through SET_DECL_ALIGN.
>
> 2) Put tags into each stack variable pointer
>
> Make sure that every pointer to a stack variable includes a tag of some
> sort on it.
>
> The way tagging works is:
>   1) For every new stack frame, a random tag is generated.
>   2) A base register is formed from the stack pointer value and this
>      random tag.
>   3) References to stack variables are now formed with RTL describing an
>      offset from this base in both tag and value.
>
> The random tag generation is handled by a backend hook.  This hook
> decides whether to introduce a random tag or use the stack background
> based on the parameter hwasan-random-frame-tag.  Using the stack
> background is necessary for testing and bootstrap.  It is necessary
> during bootstrap to avoid breaking the `configure` test program for
> determining stack direction.
>
> Using the stack background means that every stack frame has the initial
> tag of zero and variables are tagged with incrementing tags from 1,
> which also makes debugging a bit easier.
>
> Backend hooks define the size of a tag, the layout of the HWASAN shadow
> memory, and handle emitting the code that inserts and extracts tags from a
> pointer.
>
> 3) For each stack variable, tag and untag the shadow stack on function
>    prologue and epilogue.
>
> On entry to each function we tag the relevant shadow stack region for
> each stack variable. This stack region is tagged to match the tag added to
> each pointer to that variable.
>
> This is the first patch where we use the HWASAN shadow space, so we need
> to add in the libhwasan initialisation code that creates this shadow
> memory region into the binary we produce.  This instrumentation is done
> in `compile_file`.
>
> When exiting a function we need to ensure the shadow stack for this
> function has no remaining tags.  Without clearing the shadow stack area
> for this stack frame, later function calls could get false positives
> when those later function calls check untagged areas (such as parameters
> passed on the stack) against a shadow stack area with left-over tag.
>
> Hence we ensure that the entire stack frame is cleared on function exit.
>
> config/ChangeLog:
>
>         * bootstrap-hwasan.mk: Disable random frame tags for stack-tagging
>         during bootstrap.
>
> ChangeLog:
>
>         * gcc/asan.c (struct hwasan_stack_var): New.
>         (hwasan_sanitize_p): New.
>         (hwasan_sanitize_stack_p): New.
>         (hwasan_sanitize_allocas_p): New.
>         (initialize_sanitizer_builtins): Define new builtins.
>         (ATTR_NOTHROW_LIST): New macro.
>         (hwasan_current_frame_tag): New.
>         (hwasan_frame_base): New.
>         (stack_vars_base_reg_p): New.
>         (hwasan_maybe_init_frame_base_init): New.
>         (hwasan_record_stack_var): New.
>         (hwasan_get_frame_extent): New.
>         (hwasan_increment_frame_tag): New.
>         (hwasan_record_frame_init): New.
>         (hwasan_emit_prologue): New.
>         (hwasan_emit_untag_frame): New.
>         (hwasan_finish_file): New.
>         (hwasan_truncate_to_tag_size): New.
>         * gcc/asan.h (hwasan_record_frame_init): New declaration.
>         (hwasan_record_stack_var): New declaration.
>         (hwasan_emit_prologue): New declaration.
>         (hwasan_emit_untag_frame): New declaration.
>         (hwasan_get_frame_extent): New declaration.
>         (hwasan_maybe_enit_frame_base_init): New declaration.
>         (hwasan_frame_base): New declaration.
>         (stack_vars_base_reg_p): New declaration.
>         (hwasan_current_frame_tag): New declaration.
>         (hwasan_increment_frame_tag): New declaration.
>         (hwasan_truncate_to_tag_size): New declaration.
>         (hwasan_finish_file): New declaration.
>         (hwasan_sanitize_p): New declaration.
>         (hwasan_sanitize_stack_p): New declaration.
>         (hwasan_sanitize_allocas_p): New declaration.
>         (HWASAN_TAG_SIZE): New macro.
>         (HWASAN_TAG_GRANULE_SIZE): New macro.
>         (HWASAN_STACK_BACKGROUND): New macro.
>         * gcc/builtin-types.def (BT_FN_VOID_PTR_UINT8_PTRMODE): New.
>         * gcc/builtins.def (DEF_SANITIZER_BUILTIN): Enable for HWASAN.
>         * gcc/cfgexpand.c (align_local_variable): When using hwasan ensure
>         alignment to tag granule.
>         (align_frame_offset): New.
>         (expand_one_stack_var_at): For hwasan use tag offset.
>         (expand_stack_vars): Record stack objects for hwasan.
>         (expand_one_stack_var_1): Record stack objects for hwasan.
>         (init_vars_expansion): Initialise hwasan state.
>         (expand_used_vars): Emit hwasan prologue and generate hwasan epilogue.
>         (pass_expand::execute): Emit hwasan base initialization if needed.
>         * gcc/doc/tm.texi (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
>         TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
>         TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
>         TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
>         * gcc/doc/tm.texi.in (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
>         TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
>         TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
>         TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
>         * gcc/explow.c (get_dynamic_stack_base): Take new `base` argument.
>         * gcc/explow.h (get_dynamic_stack_base): Take new `base` argument.
>         * gcc/sanitizer.def (BUILT_IN_HWASAN_INIT): New.
>         (BUILT_IN_HWASAN_TAG_MEM): New.
>         * gcc/target.def (target_memtag_tag_size,target_memtag_granule_size,
>         target_memtag_insert_random_tag,target_memtag_add_tag,
>         target_memtag_set_tag,target_memtag_extract_tag,
>         target_memtag_untagged_pointer): New hooks.
>         * gcc/targhooks.c (HWASAN_SHIFT): New.
>         (HWASAN_SHIFT_RTX): New.
>         (default_memtag_tag_size): New default hook.
>         (default_memtag_granule_size): New default hook.
>         (default_memtag_insert_random_tag): New default hook.
>         (default_memtag_add_tag): New default hook.
>         (default_memtag_set_tag): New default hook.
>         (default_memtag_extract_tag): New default hook.
>         (default_memtag_untagged_pointer): New default hook.
>         * gcc/targhooks.h (default_memtag_tag_size): New default hook.
>         (default_memtag_granule_size): New default hook.
>         (default_memtag_insert_random_tag): New default hook.
>         (default_memtag_add_tag): New default hook.
>         (default_memtag_set_tag): New default hook.
>         (default_memtag_extract_tag): New default hook.
>         (default_memtag_untagged_pointer): New default hook.
>         * gcc/toplev.c (compile_file): Call hwasan_finish_file when finished.
>
>
> ###############     Attachment also inlined for ease of reply    ###############
>
>
> diff --git a/config/bootstrap-hwasan.mk b/config/bootstrap-hwasan.mk
> index 4f60bed3fd6e98b47a3a38aea6eba2a7c320da25..91989f4bb1db6ccff564383777757b896645e541 100644
> --- a/config/bootstrap-hwasan.mk
> +++ b/config/bootstrap-hwasan.mk
> @@ -1,7 +1,11 @@
>  # This option enables -fsanitize=hwaddress for stage2 and stage3.
> +# We need to disable random frame tags for bootstrap since the autoconf check
> +# for which direction the stack is growing has UB that a random frame tag
> +# breaks.  Running with a random frame tag gives approx. 50% chance of
> +# bootstrap comparison diff in libiberty/alloca.c.
>
> -STAGE2_CFLAGS += -fsanitize=hwaddress
> -STAGE3_CFLAGS += -fsanitize=hwaddress
> +STAGE2_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
> +STAGE3_CFLAGS += -fsanitize=hwaddress --param hwasan-random-frame-tag=0
>  POSTSTAGE1_LDFLAGS += -fsanitize=hwaddress -static-libhwasan \
>                       -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \
>                       -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/hwasan/ \
> diff --git a/gcc/asan.h b/gcc/asan.h
> index 114b457ef91c4479d43774bed58c24213196ce12..8d5271e6b575d74da277420798557f3274e966ce 100644
> --- a/gcc/asan.h
> +++ b/gcc/asan.h
> @@ -34,6 +34,22 @@ extern bool asan_expand_mark_ifn (gimple_stmt_iterator *);
>  extern bool asan_expand_poison_ifn (gimple_stmt_iterator *, bool *,
>                                     hash_map<tree, tree> &);
>
> +extern void hwasan_record_frame_init ();
> +extern void hwasan_record_stack_var (rtx, rtx, poly_int64, poly_int64);
> +extern void hwasan_emit_prologue ();
> +extern rtx_insn *hwasan_emit_untag_frame (rtx, rtx);
> +extern rtx hwasan_get_frame_extent ();
> +extern rtx hwasan_frame_base ();
> +extern void hwasan_maybe_emit_frame_base_init (void);
> +extern bool stack_vars_base_reg_p (rtx);
> +extern uint8_t hwasan_current_frame_tag ();
> +extern void hwasan_increment_frame_tag ();
> +extern rtx hwasan_truncate_to_tag_size (rtx, rtx);
> +extern void hwasan_finish_file (void);
> +extern bool hwasan_sanitize_p (void);
> +extern bool hwasan_sanitize_stack_p (void);
> +extern bool hwasan_sanitize_allocas_p (void);
> +
>  extern gimple_stmt_iterator create_cond_insert_point
>       (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
>
> @@ -75,6 +91,26 @@ extern hash_set <tree> *asan_used_labels;
>
>  #define ASAN_USE_AFTER_SCOPE_ATTRIBUTE "use after scope memory"
>
> +/* NOTE: The values below and the hooks under targetm.memtag define an ABI and
> +   are hard-coded to these values in libhwasan, hence they can't be changed
> +   independently here.  */
> +/* How many bits are used to store a tag in a pointer.
> +   The default version uses the entire top byte of a pointer (i.e. 8 bits).  */
> +#define HWASAN_TAG_SIZE targetm.memtag.tag_size ()
> +/* Tag Granule of HWASAN shadow stack.
> +   This is the size in real memory that each byte in the shadow memory refers
> +   to.  I.e. if a variable is X bytes long in memory then its tag in shadow
> +   memory will span X / HWASAN_TAG_GRANULE_SIZE bytes.
> +   Most variables will need to be aligned to this amount since two variables
> +   that are neighbors in memory and share a tag granule would need to share the
> +   same tag (the shared tag granule can only store one tag).  */
> +#define HWASAN_TAG_GRANULE_SIZE targetm.memtag.granule_size ()
> +/* Define the tag for the stack background.
> +   This defines what tag the stack pointer will be and hence what tag all
> +   variables that are not given special tags are (e.g. spilled registers,
> +   and parameters passed on the stack).  */
> +#define HWASAN_STACK_BACKGROUND gen_int_mode (0, QImode)
> +
>  /* Various flags for Asan builtins.  */
>  enum asan_check_flags
>  {
> diff --git a/gcc/asan.c b/gcc/asan.c
> index 0b471afff64ea6a0ffbe0add71333ac688c472c6..d1ede3b62291eba698948e06208c482b6f197be5 100644
> --- a/gcc/asan.c
> +++ b/gcc/asan.c
> @@ -257,6 +257,58 @@ hash_set<tree> *asan_handled_variables = NULL;
>
>  hash_set <tree> *asan_used_labels = NULL;
>
> +/* Global variables for HWASAN stack tagging.  */
> +/* hwasan_frame_tag_offset records the offset from the frame base tag that the
> +   next object should have.  */
> +static uint8_t hwasan_frame_tag_offset = 0;
> +/* hwasan_frame_base_ptr is a pointer with the same address as
> +   `virtual_stack_vars_rtx` for the current frame, and with the frame base tag
> +   stored in it.  N.b. this global RTX does not need to be marked GTY, but is
> +   done so anyway.  The need is not there since all uses are in just one pass
> +   (cfgexpand) and there are no calls to ggc_collect between the uses.  We mark
> +   it GTY(()) anyway to allow the use of the variable later on if needed by
> +   future features.  */
> +static GTY(()) rtx hwasan_frame_base_ptr = NULL_RTX;
> +/* hwasan_frame_base_init_seq is the sequence of RTL insns that will initialize
> +   the hwasan_frame_base_ptr.  When the hwasan_frame_base_ptr is requested, we
> +   generate this sequence but do not emit it.  If the sequence was created it
> +   is emitted once the function body has been expanded.
> +
> +   This delay is because the frame base pointer may be needed anywhere in the
> +   function body, or needed by the expand_used_vars function.  Emitting once in
> +   a known place is simpler than requiring the emission of the instructions to
> +   be know where it should go depending on the first place the hwasan frame
> +   base is needed.  */
> +static GTY(()) rtx_insn *hwasan_frame_base_init_seq = NULL;
> +
> +/* Structure defining the extent of one object on the stack that HWASAN needs
> +   to tag in the corresponding shadow stack space.
> +
> +   The range this object spans on the stack is between `untagged_base +
> +   nearest_offset` and `untagged_base + farthest_offset`.
> +   `tagged_base` is an rtx containing the same value as `untagged_base` but
> +   with a random tag stored in the top byte.  We record both `untagged_base`
> +   and `tagged_base` so that `hwasan_emit_prologue` can use both without having
> +   to emit RTL into the instruction stream to re-calculate one from the other.
> +   (`hwasan_emit_prologue` needs to use both bases since the
> +   __hwasan_tag_memory call it emits uses an untagged value, and it calculates
> +   the tag to store in shadow memory based on the tag_offset plus the tag in
> +   tagged_base).  */
> +struct hwasan_stack_var
> +{
> +  rtx untagged_base;
> +  rtx tagged_base;
> +  poly_int64 nearest_offset;
> +  poly_int64 farthest_offset;
> +  uint8_t tag_offset;
> +};
> +
> +/* Variable recording all stack variables that HWASAN needs to tag.
> +   Does not need to be marked as GTY(()) since every use is in the cfgexpand
> +   pass and gcc_collect is not called in the middle of that pass.  */
> +static vec<hwasan_stack_var> hwasan_tagged_stack_vars;
> +
> +
>  /* Sets shadow offset to value in string VAL.  */
>
>  bool
> @@ -1359,6 +1411,28 @@ asan_redzone_buffer::flush_if_full (void)
>      flush_redzone_payload ();
>  }
>
> +/* Returns whether we are tagging pointers and checking those tags on memory
> +   access.  */
> +bool
> +hwasan_sanitize_p ()
> +{
> +  return sanitize_flags_p (SANITIZE_HWADDRESS);
> +}
> +
> +/* Are we tagging the stack?  */
> +bool
> +hwasan_sanitize_stack_p ()
> +{
> +  return (hwasan_sanitize_p () && param_hwasan_instrument_stack);
> +}
> +
> +/* Are we tagging alloca objects?  */
> +bool
> +hwasan_sanitize_allocas_p (void)
> +{
> +  return (hwasan_sanitize_stack_p () && param_hwasan_instrument_allocas);
> +}
> +
>  /* Insert code to protect stack vars.  The prologue sequence should be emitted
>     directly, epilogue sequence returned.  BASE is the register holding the
>     stack base, against which OFFSETS array offsets are relative to, OFFSETS
> @@ -2908,6 +2982,11 @@ initialize_sanitizer_builtins (void)
>      = build_function_type_list (void_type_node, uint64_type_node,
>                                 ptr_type_node, NULL_TREE);
>
> +  tree BT_FN_VOID_PTR_UINT8_PTRMODE
> +    = build_function_type_list (void_type_node, ptr_type_node,
> +                               unsigned_char_type_node,
> +                               pointer_sized_int_node, NULL_TREE);
> +
>    tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
>    tree BT_FN_IX_CONST_VPTR_INT[5];
>    tree BT_FN_IX_VPTR_IX_INT[5];
> @@ -2958,6 +3037,8 @@ initialize_sanitizer_builtins (void)
>  #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
>  #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
>  #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
> +#undef ATTR_NOTHROW_LIST
> +#define ATTR_NOTHROW_LIST ECF_NOTHROW
>  #undef ATTR_NOTHROW_LEAF_LIST
>  #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
>  #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
> @@ -3709,4 +3790,347 @@ make_pass_asan_O0 (gcc::context *ctxt)
>    return new pass_asan_O0 (ctxt);
>  }
>
> +/* For stack tagging:
> +
> +   Return the offset from the frame base tag that the "next" expanded object
> +   should have.  */
> +uint8_t
> +hwasan_current_frame_tag ()
> +{
> +  return hwasan_frame_tag_offset;
> +}
> +
> +/* For stack tagging:
> +
> +   Return the 'base pointer' for this function.  If that base pointer has not
> +   yet been created then we create a register to hold it and record the insns
> +   to initialize the register in `hwasan_frame_base_init_seq` for later
> +   emission.  */
> +rtx
> +hwasan_frame_base ()
> +{
> +  if (! hwasan_frame_base_ptr)
> +    {
> +      start_sequence ();
> +      hwasan_frame_base_ptr
> +       = force_reg (Pmode,
> +                    targetm.memtag.insert_random_tag (virtual_stack_vars_rtx,
> +                                                      NULL_RTX));
> +      hwasan_frame_base_init_seq = get_insns ();
> +      end_sequence ();
> +    }
> +
> +  return hwasan_frame_base_ptr;
> +}
> +
> +/* For stack tagging:
> +
> +   Check whether this RTX is a standard pointer addressing the base of the
> +   stack variables for this frame.  Returns true if the RTX is either
> +   virtual_stack_vars_rtx or hwasan_frame_base_ptr.  */
> +bool
> +stack_vars_base_reg_p (rtx base)
> +{
> +  return base == virtual_stack_vars_rtx || base == hwasan_frame_base_ptr;
> +}
> +
> +/* For stack tagging:
> +
> +   Emit frame base initialisation.
> +   If hwasan_frame_base has been used before here then
> +   hwasan_frame_base_init_seq contains the sequence of instructions to
> +   initialize it.  This must be put just before the hwasan prologue, so we emit
> +   the insns before parm_birth_insn (which will point to the first instruction
> +   of the hwasan prologue if it exists).
> +
> +   We update `parm_birth_insn` to point to the start of this initialisation
> +   since that represents the end of the initialisation done by
> +   expand_function_{start,end} functions and we want to maintain that.  */
> +void
> +hwasan_maybe_emit_frame_base_init ()
> +{
> +  if (! hwasan_frame_base_init_seq)
> +    return;
> +  emit_insn_before (hwasan_frame_base_init_seq, parm_birth_insn);
> +  parm_birth_insn = hwasan_frame_base_init_seq;
> +}
> +
> +/* Record a compile-time constant size stack variable that HWASAN will need to
> +   tag.  This record of the range of a stack variable will be used by
> +   `hwasan_emit_prologue` to emit the RTL at the start of each frame which will
> +   set tags in the shadow memory according to the assigned tag for each object.
> +
> +   The range that the object spans in stack space should be described by the
> +   bounds `untagged_base + nearest_offset` and
> +   `untagged_base + farthest_offset`.
> +   `tagged_base` is the base address which contains the "base frame tag" for
> +   this frame, and from which the value to address this object with will be
> +   calculated.
> +
> +   We record the `untagged_base` since the functions in the hwasan library we
> +   use to tag memory take pointers without a tag.  */
> +void
> +hwasan_record_stack_var (rtx untagged_base, rtx tagged_base,
> +                        poly_int64 nearest_offset, poly_int64 farthest_offset)
> +{
> +  hwasan_stack_var cur_var;
> +  cur_var.untagged_base = untagged_base;
> +  cur_var.tagged_base = tagged_base;
> +  cur_var.nearest_offset = nearest_offset;
> +  cur_var.farthest_offset = farthest_offset;
> +  cur_var.tag_offset = hwasan_current_frame_tag ();
> +
> +  hwasan_tagged_stack_vars.safe_push (cur_var);
> +}
> +
> +/* Return the RTX representing the farthest extent of the statically allocated
> +   stack objects for this frame.  If hwasan_frame_base_ptr has not been
> +   initialized then we are not storing any static variables on the stack in
> +   this frame.  In this case we return NULL_RTX to represent that.
> +
> +   Otherwise simply return virtual_stack_vars_rtx + frame_offset.  */
> +rtx
> +hwasan_get_frame_extent ()
> +{
> +  return (hwasan_frame_base_ptr
> +         ? plus_constant (Pmode, virtual_stack_vars_rtx, frame_offset)
> +         : NULL_RTX);
> +}
> +
> +/* For stack tagging:
> +
> +   Increment the frame tag offset modulo the size a tag can represent.  */
> +void
> +hwasan_increment_frame_tag ()
> +{
> +  uint8_t tag_bits = HWASAN_TAG_SIZE;
> +  gcc_assert (HWASAN_TAG_SIZE
> +             <= sizeof (hwasan_frame_tag_offset) * CHAR_BIT);
> +  hwasan_frame_tag_offset = (hwasan_frame_tag_offset + 1) % (1 << tag_bits);
> +  /* The "background tag" of the stack is zero by definition.
> +     This is the tag that objects like parameters passed on the stack and
> +     spilled registers are given.  It is handy to avoid this tag for objects
> +     whose tags we decide ourselves, partly to ensure that buffer overruns
> +     can't affect these important variables (e.g. saved link register, saved
> +     stack pointer etc) and partly to make debugging easier (everything with a
> +     tag of zero is space allocated automatically by the compiler).
> +
> +     This is not feasible when using random frame tags (the default
> +     configuration for hwasan) since the tag for the given frame is randomly
> +     chosen at runtime.  In order to avoid any tags matching the stack
> +     background we would need to decide tag offsets at runtime instead of
> +     compile time (and pay the resulting performance cost).
> +
> +     When not using random base tags for each frame (i.e. when compiled with
> +     `--param hwasan-random-frame-tag=0`) the base tag for each frame is zero.
> +     This means the tag that each object gets is equal to the
> +     hwasan_frame_tag_offset used in determining it.
> +     When this is the case we *can* ensure no object gets the tag of zero by
> +     simply ensuring no object has the hwasan_frame_tag_offset of zero.
> +
> +     There is the extra complication that we only record the
> +     hwasan_frame_tag_offset here (which is the offset from the tag stored in
> +     the stack pointer).  In the kernel, the tag in the stack pointer is 0xff
> +     rather than zero.  This does not cause problems since tags of 0xff are
> +     never checked in the kernel.  As mentioned at the beginning of this
> +     comment the background tag of the stack is zero by definition, which means
> +     that for the kernel we should skip offsets of both 0 and 1 from the stack
> +     pointer.  Avoiding the offset of 0 ensures we use a tag which will be
> +     checked, avoiding the offset of 1 ensures we use a tag that is not the
> +     same as the background.  */
> +  if (hwasan_frame_tag_offset == 0 && ! param_hwasan_random_frame_tag)
> +    hwasan_frame_tag_offset += 1;
> +  if (hwasan_frame_tag_offset == 1 && ! param_hwasan_random_frame_tag
> +      && sanitize_flags_p (SANITIZE_KERNEL_HWADDRESS))
> +    hwasan_frame_tag_offset += 1;
> +}
> +
> +/* Clear internal state for the next function.
> +   This function is called before variables on the stack get expanded, in
> +   `init_vars_expansion`.  */
> +void
> +hwasan_record_frame_init ()
> +{
> +  delete asan_used_labels;
> +  asan_used_labels = NULL;
> +
> +  /* If this isn't the case then some stack variable was recorded *before*
> +     hwasan_record_frame_init is called, yet *after* the hwasan prologue for
> +     the previous frame was emitted.  Such stack variables would not have
> +     their shadow stack filled in.  */
> +  gcc_assert (hwasan_tagged_stack_vars.is_empty ());
> +  hwasan_frame_base_ptr = NULL_RTX;
> +  hwasan_frame_base_init_seq = NULL;
> +
> +  /* When not using a random frame tag we can avoid the background stack
> +     color which gives the user a little better debug output upon a crash.
> +     Meanwhile, when using a random frame tag it will be nice to avoid adding
> +     tags for the first object since that is unnecessary extra work.
> +     Hence set the initial hwasan_frame_tag_offset to be 0 if using a random
> +     frame tag and 1 otherwise.
> +
> +     As described in hwasan_increment_frame_tag, in the kernel the stack
> +     pointer has the tag 0xff.  That means that to avoid 0xff and 0 (the tag
> +     which the kernel does not check and the background tag respectively) we
> +     start with a tag offset of 2.  */
> +  hwasan_frame_tag_offset = param_hwasan_random_frame_tag
> +    ? 0
> +    : sanitize_flags_p (SANITIZE_KERNEL_HWADDRESS) ? 2 : 1;
> +}
> +
> +/* For stack tagging:
> +   (Emits HWASAN equivalent of what is emitted by
> +   `asan_emit_stack_protection`).
> +
> +   Emits the extra prologue code to set the shadow stack as required for HWASAN
> +   stack instrumentation.
> +
> +   Uses the vector of recorded stack variables hwasan_tagged_stack_vars.  When
> +   this function has completed hwasan_tagged_stack_vars is empty and all
> +   objects it had pointed to are deallocated.  */
> +void
> +hwasan_emit_prologue ()
> +{
> +  /* We need untagged base pointers since libhwasan only accepts untagged
> +    pointers in __hwasan_tag_memory.  We need the tagged base pointer to obtain
> +    the base tag for an offset.  */
> +
> +  if (hwasan_tagged_stack_vars.is_empty ())
> +    return;
> +
> +  poly_int64 bot = 0, top = 0;
> +  for (hwasan_stack_var &cur : hwasan_tagged_stack_vars)
> +    {
> +      poly_int64 nearest = cur.nearest_offset;
> +      poly_int64 farthest = cur.farthest_offset;
> +
> +      if (known_ge (nearest, farthest))
> +       {
> +         top = nearest;
> +         bot = farthest;
> +       }
> +      else
> +       {
> +         /* Given how these values are calculated, one must be known greater
> +            than the other.  */
> +         gcc_assert (known_le (nearest, farthest));
> +         top = farthest;
> +         bot = nearest;
> +       }
> +      poly_int64 size = (top - bot);
> +
> +      /* Assert the edge of each variable is aligned to the HWASAN tag granule
> +        size.  */
> +      gcc_assert (multiple_p (top, HWASAN_TAG_GRANULE_SIZE));
> +      gcc_assert (multiple_p (bot, HWASAN_TAG_GRANULE_SIZE));
> +      gcc_assert (multiple_p (size, HWASAN_TAG_GRANULE_SIZE));
> +
> +      rtx fn = init_one_libfunc ("__hwasan_tag_memory");
> +      rtx base_tag = targetm.memtag.extract_tag (cur.tagged_base, NULL_RTX);
> +      rtx tag = plus_constant (QImode, base_tag, cur.tag_offset);
> +      tag = hwasan_truncate_to_tag_size (tag, NULL_RTX);
> +
> +      rtx bottom = convert_memory_address (ptr_mode,
> +                                          plus_constant (Pmode,
> +                                                         cur.untagged_base,
> +                                                         bot));
> +      emit_library_call (fn, LCT_NORMAL, VOIDmode,
> +                        bottom, ptr_mode,
> +                        tag, QImode,
> +                        gen_int_mode (size, ptr_mode), ptr_mode);
> +    }
> +  /* Clear the stack vars, we've emitted the prologue for them all now.  */
> +  hwasan_tagged_stack_vars.truncate (0);
> +}
> +
> +/* For stack tagging:
> +
> +   Return RTL insns to clear the tags between DYNAMIC and VARS pointers
> +   into the stack.  These instructions should be emitted at the end of
> +   every function.
> +
> +   If `dynamic` is NULL_RTX then no insns are returned.  */
> +rtx_insn *
> +hwasan_emit_untag_frame (rtx dynamic, rtx vars)
> +{
> +  if (! dynamic)
> +    return NULL;
> +
> +  start_sequence ();
> +
> +  dynamic = convert_memory_address (ptr_mode, dynamic);
> +  vars = convert_memory_address (ptr_mode, vars);
> +
> +  rtx top_rtx;
> +  rtx bot_rtx;
> +  if (FRAME_GROWS_DOWNWARD)
> +    {
> +      top_rtx = vars;
> +      bot_rtx = dynamic;
> +    }
> +  else
> +    {
> +      top_rtx = dynamic;
> +      bot_rtx = vars;
> +    }
> +
> +  rtx size_rtx = expand_simple_binop (ptr_mode, MINUS, top_rtx, bot_rtx,
> +                                     NULL_RTX, /* unsignedp = */0,
> +                                     OPTAB_DIRECT);
> +
> +  rtx fn = init_one_libfunc ("__hwasan_tag_memory");
> +  emit_library_call (fn, LCT_NORMAL, VOIDmode,
> +                    bot_rtx, ptr_mode,
> +                    HWASAN_STACK_BACKGROUND, QImode,
> +                    size_rtx, ptr_mode);
> +
> +  do_pending_stack_adjust ();
> +  rtx_insn *insns = get_insns ();
> +  end_sequence ();
> +  return insns;
> +}
> +
> +/* Needs to be GTY(()), because cgraph_build_static_cdtor may
> +   invoke ggc_collect.  */
> +static GTY(()) tree hwasan_ctor_statements;
> +
> +/* Insert module initialization into this TU.  This initialization calls the
> +   initialization code for libhwasan.  */
> +void
> +hwasan_finish_file (void)
> +{
> +  /* Do not emit constructor initialization for the kernel.
> +     (the kernel has its own initialization already).  */
> +  if (flag_sanitize & SANITIZE_KERNEL_HWADDRESS)
> +    return;
> +
> +  /* Avoid instrumenting code in the hwasan constructors/destructors.  */
> +  flag_sanitize &= ~SANITIZE_HWADDRESS;
> +  int priority = MAX_RESERVED_INIT_PRIORITY - 1;
> +  tree fn = builtin_decl_implicit (BUILT_IN_HWASAN_INIT);
> +  append_to_statement_list (build_call_expr (fn, 0), &hwasan_ctor_statements);
> +  cgraph_build_static_cdtor ('I', hwasan_ctor_statements, priority);
> +  flag_sanitize |= SANITIZE_HWADDRESS;
> +}
> +
> +/* For stack tagging:
> +
> +   Truncate `tag` to the number of bits that a tag uses (i.e. to
> +   HWASAN_TAG_SIZE).  Store the result in `target` if it's convenient.  */
> +rtx
> +hwasan_truncate_to_tag_size (rtx tag, rtx target)
> +{
> +  gcc_assert (GET_MODE (tag) == QImode);
> +  if (HWASAN_TAG_SIZE != GET_MODE_PRECISION (QImode))
> +    {
> +      gcc_assert (GET_MODE_PRECISION (QImode) > HWASAN_TAG_SIZE);
> +      rtx mask = gen_int_mode ((HOST_WIDE_INT_1U << HWASAN_TAG_SIZE) - 1,
> +                              QImode);
> +      tag = expand_simple_binop (QImode, AND, tag, mask, target,
> +                                /* unsignedp = */1, OPTAB_WIDEN);
> +      gcc_assert (tag);
> +    }
> +  return tag;
> +}
> +
>  #include "gt-asan.h"
> diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
> index 4a82ee421bef42154ccd88e52f7a19f48b340c73..1ad6657da45cc4976532e1b8bc233f67d8da9ccf 100644
> --- a/gcc/builtin-types.def
> +++ b/gcc/builtin-types.def
> @@ -639,6 +639,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_CONST_SIZE_BOOL,
>                      BT_PTR, BT_PTR, BT_CONST_SIZE, BT_BOOL)
>  DEF_FUNCTION_TYPE_3 (BT_FN_PTR_SIZE_SIZE_PTRMODE,
>                      BT_PTR, BT_SIZE, BT_SIZE, BT_PTRMODE)
> +DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_UINT8_PTRMODE, BT_VOID, BT_PTR, BT_UINT8,
> +                    BT_PTRMODE)
>
>  DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
>                      BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
> diff --git a/gcc/builtins.def b/gcc/builtins.def
> index b4494c712a1751fbb37378f38cc1411d11a37331..97bb5d0b0aee7fa9ee4c82e2d80eae866fc23829 100644
> --- a/gcc/builtins.def
> +++ b/gcc/builtins.def
> @@ -245,6 +245,7 @@ along with GCC; see the file COPYING3.  If not see
>    DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
>                true, true, true, ATTRS, true, \
>               (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
> +                               | SANITIZE_HWADDRESS \
>                                 | SANITIZE_UNDEFINED \
>                                 | SANITIZE_UNDEFINED_NONDEFAULT) \
>                || flag_sanitize_coverage))
> diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
> index 1df6f4bc55a39230c98e58af6c2d765652db8324..231c2ee32362fc3967b1cd7b70bd330ce49648d3 100644
> --- a/gcc/cfgexpand.c
> +++ b/gcc/cfgexpand.c
> @@ -376,15 +376,18 @@ align_local_variable (tree decl, bool really_expand)
>         align = GET_MODE_ALIGNMENT (mode);
>      }
>    else
> -    {
> -      align = LOCAL_DECL_ALIGNMENT (decl);
> -      /* Don't change DECL_ALIGN when called from estimated_stack_frame_size.
> -        That is done before IPA and could bump alignment based on host
> -        backend even for offloaded code which wants different
> -        LOCAL_DECL_ALIGNMENT.  */
> -      if (really_expand)
> -       SET_DECL_ALIGN (decl, align);
> -    }
> +    align = LOCAL_DECL_ALIGNMENT (decl);
> +
> +  if (hwasan_sanitize_stack_p ())
> +    align = MAX (align, (unsigned) HWASAN_TAG_GRANULE_SIZE * BITS_PER_UNIT);
> +
> +  if (TREE_CODE (decl) != SSA_NAME && really_expand)
> +    /* Don't change DECL_ALIGN when called from estimated_stack_frame_size.
> +       That is done before IPA and could bump alignment based on host
> +       backend even for offloaded code which wants different
> +       LOCAL_DECL_ALIGNMENT.  */
> +    SET_DECL_ALIGN (decl, align);
> +
>    return align / BITS_PER_UNIT;
>  }
>
> @@ -428,6 +431,14 @@ alloc_stack_frame_space (poly_int64 size, unsigned HOST_WIDE_INT align)
>    return offset;
>  }
>
> +/* Ensure that the stack is aligned to ALIGN bytes.
> +   Return the new frame offset.  */
> +static poly_int64
> +align_frame_offset (unsigned HOST_WIDE_INT align)
> +{
> +  return alloc_stack_frame_space (0, align);
> +}
> +
>  /* Accumulate DECL into STACK_VARS.  */
>
>  static void
> @@ -1004,7 +1015,12 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
>    /* If this fails, we've overflowed the stack frame.  Error nicely?  */
>    gcc_assert (known_eq (offset, trunc_int_for_mode (offset, Pmode)));
>
> -  x = plus_constant (Pmode, base, offset);
> +  if (hwasan_sanitize_stack_p ())
> +    x = targetm.memtag.add_tag (base, offset,
> +                               hwasan_current_frame_tag ());
> +  else
> +    x = plus_constant (Pmode, base, offset);
> +
>    x = gen_rtx_MEM (TREE_CODE (decl) == SSA_NAME
>                    ? TYPE_MODE (TREE_TYPE (decl))
>                    : DECL_MODE (decl), x);
> @@ -1013,7 +1029,7 @@ expand_one_stack_var_at (tree decl, rtx base, unsigned base_align,
>       If it is we generate stack slots only accidentally so it isn't as
>       important, we'll simply set the alignment directly on the MEM.  */
>
> -  if (base == virtual_stack_vars_rtx)
> +  if (stack_vars_base_reg_p (base))
>      offset -= frame_phase;
>    align = known_alignment (offset);
>    align *= BITS_PER_UNIT;
> @@ -1056,13 +1072,13 @@ public:
>  /* A subroutine of expand_used_vars.  Give each partition representative
>     a unique location within the stack frame.  Update each partition member
>     with that location.  */
> -
>  static void
>  expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>  {
>    size_t si, i, j, n = stack_vars_num;
>    poly_uint64 large_size = 0, large_alloc = 0;
>    rtx large_base = NULL;
> +  rtx large_untagged_base = NULL;
>    unsigned large_align = 0;
>    bool large_allocation_done = false;
>    tree decl;
> @@ -1113,7 +1129,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>      {
>        rtx base;
>        unsigned base_align, alignb;
> -      poly_int64 offset;
> +      poly_int64 offset = 0;
>
>        i = stack_vars_sorted[si];
>
> @@ -1134,10 +1150,33 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>        if (pred && !pred (i))
>         continue;
>
> +      base = (hwasan_sanitize_stack_p ()
> +             ? hwasan_frame_base ()
> +             : virtual_stack_vars_rtx);
>        alignb = stack_vars[i].alignb;
>        if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
>         {
> -         base = virtual_stack_vars_rtx;
> +         poly_int64 hwasan_orig_offset;
> +         if (hwasan_sanitize_stack_p ())
> +           {
> +             /* There must be no tag granule "shared" between different
> +                objects.  This means that no HWASAN_TAG_GRANULE_SIZE byte
> +                chunk can have more than one object in it.
> +
> +                We ensure this by forcing the end of the last bit of data to
> +                be aligned to HWASAN_TAG_GRANULE_SIZE bytes here, and setting
> +                the start of each variable to be aligned to
> +                HWASAN_TAG_GRANULE_SIZE bytes in `align_local_variable`.
> +
> +                We can't align just one of the start or end, since there are
> +                untagged things stored on the stack which we do not align to
> +                HWASAN_TAG_GRANULE_SIZE bytes.  If we only aligned the start
> +                or the end of tagged objects then untagged objects could end
> +                up sharing the first granule of a tagged object or sharing the
> +                last granule of a tagged object respectively.  */
> +             hwasan_orig_offset = align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
> +             gcc_assert (stack_vars[i].alignb >= HWASAN_TAG_GRANULE_SIZE);
> +           }
>           /* ASAN description strings don't yet have a syntax for expressing
>              polynomial offsets.  */
>           HOST_WIDE_INT prev_offset;
> @@ -1148,7 +1187,7 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>             {
>               if (data->asan_vec.is_empty ())
>                 {
> -                 alloc_stack_frame_space (0, ASAN_RED_ZONE_SIZE);
> +                 align_frame_offset (ASAN_RED_ZONE_SIZE);
>                   prev_offset = frame_offset.to_constant ();
>                 }
>               prev_offset = align_base (prev_offset,
> @@ -1216,6 +1255,24 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>             {
>               offset = alloc_stack_frame_space (stack_vars[i].size, alignb);
>               base_align = crtl->max_used_stack_slot_alignment;
> +
> +             if (hwasan_sanitize_stack_p ())
> +               {
> +                 /* Align again since the point of this alignment is to handle
> +                    the "end" of the object (i.e. smallest address after the
> +                    stack object).  For FRAME_GROWS_DOWNWARD that requires
> +                    aligning the stack before allocating, but for a frame that
> +                    grows upwards that requires aligning the stack after
> +                    allocation.
> +
> +                    Use `frame_offset` to record the offset value rather than
> +                    offset since the `frame_offset` describes the extent
> +                    allocated for this particular variable while `offset`
> +                    describes the address that this variable starts at.  */
> +                 align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
> +                 hwasan_record_stack_var (virtual_stack_vars_rtx, base,
> +                                          hwasan_orig_offset, frame_offset);
> +               }
>             }
>         }
>        else
> @@ -1236,14 +1293,33 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>               loffset = alloc_stack_frame_space
>                 (rtx_to_poly_int64 (large_allocsize),
>                  PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT);
> -             large_base = get_dynamic_stack_base (loffset, large_align);
> +             large_base = get_dynamic_stack_base (loffset, large_align, base);
>               large_allocation_done = true;
>             }
> -         gcc_assert (large_base != NULL);
>
> +         gcc_assert (large_base != NULL);
>           large_alloc = aligned_upper_bound (large_alloc, alignb);
>           offset = large_alloc;
>           large_alloc += stack_vars[i].size;
> +         if (hwasan_sanitize_stack_p ())
> +           {
> +             /* An object with a large alignment requirement means that the
> +                alignment requirement is greater than the required alignment
> +                for tags.  */
> +             if (!large_untagged_base)
> +               large_untagged_base
> +                 = targetm.memtag.untagged_pointer (large_base, NULL_RTX);
> +             /* Ensure the end of the variable is also aligned correctly.  */
> +             poly_int64 align_again
> +               = aligned_upper_bound (large_alloc, HWASAN_TAG_GRANULE_SIZE);
> +             /* For large allocations we always allocate a chunk of space
> +                (which is addressed by large_untagged_base/large_base) and
> +                then use positive offsets from that.  Hence the farthest
> +                offset is `align_again` and the nearest offset from the base
> +                is `offset`.  */
> +             hwasan_record_stack_var (large_untagged_base, large_base,
> +                                      offset, align_again);
> +           }
>
>           base = large_base;
>           base_align = large_align;
> @@ -1254,9 +1330,10 @@ expand_stack_vars (bool (*pred) (size_t), class stack_vars_data *data)
>        for (j = i; j != EOC; j = stack_vars[j].next)
>         {
>           expand_one_stack_var_at (stack_vars[j].decl,
> -                                  base, base_align,
> -                                  offset);
> +                                  base, base_align, offset);
>         }
> +      if (hwasan_sanitize_stack_p ())
> +       hwasan_increment_frame_tag ();
>      }
>
>    gcc_assert (known_eq (large_alloc, large_size));
> @@ -1347,10 +1424,37 @@ expand_one_stack_var_1 (tree var)
>    /* We handle highly aligned variables in expand_stack_vars.  */
>    gcc_assert (byte_align * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT);
>
> -  offset = alloc_stack_frame_space (size, byte_align);
> +  rtx base;
> +  if (hwasan_sanitize_stack_p ())
> +    {
> +      /* Allocate zero bytes to align the stack.  */
> +      poly_int64 hwasan_orig_offset
> +       = align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
> +      offset = alloc_stack_frame_space (size, byte_align);
> +      align_frame_offset (HWASAN_TAG_GRANULE_SIZE);
> +      base = hwasan_frame_base ();
> +      /* Use `frame_offset` to automatically account for machines where the
> +        frame grows upwards.
> +
> +        `offset` will always point to the "start" of the stack object, which
> +        will be the smallest address, for ! FRAME_GROWS_DOWNWARD this is *not*
> +        the "furthest" offset from the base delimiting the current stack
> +        object.  `frame_offset` will always delimit the extent that the frame.
> +        */
> +      hwasan_record_stack_var (virtual_stack_vars_rtx, base,
> +                              hwasan_orig_offset, frame_offset);
> +    }
> +  else
> +    {
> +      offset = alloc_stack_frame_space (size, byte_align);
> +      base = virtual_stack_vars_rtx;
> +    }
>
> -  expand_one_stack_var_at (var, virtual_stack_vars_rtx,
> +  expand_one_stack_var_at (var, base,
>                            crtl->max_used_stack_slot_alignment, offset);
> +
> +  if (hwasan_sanitize_stack_p ())
> +    hwasan_increment_frame_tag ();
>  }
>
>  /* Wrapper for expand_one_stack_var_1 that checks SSA_NAMEs are
> @@ -1950,6 +2054,8 @@ init_vars_expansion (void)
>    /* Initialize local stack smashing state.  */
>    has_protected_decls = false;
>    has_short_buffer = false;
> +  if (hwasan_sanitize_stack_p ())
> +    hwasan_record_frame_init ();
>  }
>
>  /* Free up stack variable graph data.  */
> @@ -2277,10 +2383,26 @@ expand_used_vars (void)
>        expand_stack_vars (NULL, &data);
>      }
>
> +  if (hwasan_sanitize_stack_p ())
> +    hwasan_emit_prologue ();
>    if (asan_sanitize_allocas_p () && cfun->calls_alloca)
>      var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
>                                               virtual_stack_vars_rtx,
>                                               var_end_seq);
> +  else if (hwasan_sanitize_allocas_p () && cfun->calls_alloca)
> +    /* When using out-of-line instrumentation we only want to emit one function
> +       call for clearing the tags in a region of shadow stack.  When there are
> +       alloca calls in this frame we want to emit a call using the
> +       virtual_stack_dynamic_rtx, but when not we use the hwasan_frame_extent
> +       rtx we created in expand_stack_vars.  */
> +    var_end_seq = hwasan_emit_untag_frame (virtual_stack_dynamic_rtx,
> +                                          virtual_stack_vars_rtx);
> +  else if (hwasan_sanitize_stack_p ())
> +    /* If no variables were stored on the stack, `hwasan_get_frame_extent`
> +       will return NULL_RTX and hence `hwasan_emit_untag_frame` will return
> +       NULL (i.e. an empty sequence).  */
> +    var_end_seq = hwasan_emit_untag_frame (hwasan_get_frame_extent (),
> +                                          virtual_stack_vars_rtx);
>
>    fini_vars_expansion ();
>
> @@ -6641,6 +6763,9 @@ pass_expand::execute (function *fun)
>        emit_insn_after (var_ret_seq, after);
>      }
>
> +  if (hwasan_sanitize_stack_p ())
> +    hwasan_maybe_emit_frame_base_init ();
> +
>    /* Zap the tree EH table.  */
>    set_eh_throw_stmt_table (fun, NULL);
>
> diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
> index 298fe4b295e2f81d679786f21f499183bc07078f..f06d5e8911241d3fa0f2c7a101a3a2468defd227 100644
> --- a/gcc/doc/tm.texi
> +++ b/gcc/doc/tm.texi
> @@ -12230,3 +12230,60 @@ work.
>  At preset, this feature does not support address spaces.  It also requires
>  @code{Pmode} to be the same as @code{ptr_mode}.
>  @end deftypefn
> +
> +@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_TAG_SIZE ()
> +Return the size of a tag (in bits) for this platform.
> +
> +The default returns 8.
> +@end deftypefn
> +
> +@deftypefn {Target Hook} uint8_t TARGET_MEMTAG_GRANULE_SIZE ()
> +Return the size in real memory that each byte in shadow memory refers to.
> +I.e. if a variable is @var{X} bytes long in memory, then this hook should
> +return the value @var{Y} such that the tag in shadow memory spans
> +@var{X}/@var{Y} bytes.
> +
> +Most variables will need to be aligned to this amount since two variables
> +that are neighbors in memory and share a tag granule would need to share
> +the same tag.
> +
> +The default returns 16.
> +@end deftypefn
> +
> +@deftypefn {Target Hook} rtx TARGET_MEMTAG_INSERT_RANDOM_TAG (rtx @var{untagged}, rtx @var{target})
> +Return an RTX representing the value of @var{untagged} but with a
> +(possibly) random tag in it.
> +Put that value into @var{target} if it is convenient to do so.
> +This function is used to generate a tagged base for the current stack frame.
> +@end deftypefn
> +
> +@deftypefn {Target Hook} rtx TARGET_MEMTAG_ADD_TAG (rtx @var{base}, poly_int64 @var{addr_offset}, uint8_t @var{tag_offset})
> +Return an RTX that represents the result of adding @var{addr_offset} to
> +the address in pointer @var{base} and @var{tag_offset} to the tag in pointer
> +@var{base}.
> +The resulting RTX must either be a valid memory address or be able to get
> +put into an operand with @code{force_operand}.
> +
> +Unlike other memtag hooks, this must return an expression and not emit any
> +RTL.
> +@end deftypefn
> +
> +@deftypefn {Target Hook} rtx TARGET_MEMTAG_SET_TAG (rtx @var{untagged_base}, rtx @var{tag}, rtx @var{target})
> +Return an RTX representing @var{untagged_base} but with the tag @var{tag}.
> +Try and store this in @var{target} if convenient.
> +@var{untagged_base} is required to have a zero tag when this hook is called.
> +The default of this hook is to set the top byte of @var{untagged_base} to
> +@var{tag}.
> +@end deftypefn
> +
> +@deftypefn {Target Hook} rtx TARGET_MEMTAG_EXTRACT_TAG (rtx @var{tagged_pointer}, rtx @var{target})
> +Return an RTX representing the tag stored in @var{tagged_pointer}.
> +Store the result in @var{target} if it is convenient.
> +The default represents the top byte of the original pointer.
> +@end deftypefn
> +
> +@deftypefn {Target Hook} rtx TARGET_MEMTAG_UNTAGGED_POINTER (rtx @var{tagged_pointer}, rtx @var{target})
> +Return an RTX representing @var{tagged_pointer} with its tag set to zero.
> +Store the result in @var{target} if convenient.
> +The default clears the top byte of the original pointer.
> +@end deftypefn
> diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
> index 8fbd36e2bf31e098f7827ce331fd7059c8a747bc..b08923c8f28455fe77e061625e78ed1bf538e792 100644
> --- a/gcc/doc/tm.texi.in
> +++ b/gcc/doc/tm.texi.in
> @@ -8186,3 +8186,17 @@ maintainer is familiar with.
>  @hook TARGET_RUN_TARGET_SELFTESTS
>
>  @hook TARGET_MEMTAG_CAN_TAG_ADDRESSES
> +
> +@hook TARGET_MEMTAG_TAG_SIZE
> +
> +@hook TARGET_MEMTAG_GRANULE_SIZE
> +
> +@hook TARGET_MEMTAG_INSERT_RANDOM_TAG
> +
> +@hook TARGET_MEMTAG_ADD_TAG
> +
> +@hook TARGET_MEMTAG_SET_TAG
> +
> +@hook TARGET_MEMTAG_EXTRACT_TAG
> +
> +@hook TARGET_MEMTAG_UNTAGGED_POINTER
> diff --git a/gcc/explow.h b/gcc/explow.h
> index 0df8c62b82a8bf1d8d6baf0b6fb658e66361a407..581831cb19fdf9e8fd969bb30139e1358279a34d 100644
> --- a/gcc/explow.h
> +++ b/gcc/explow.h
> @@ -106,7 +106,7 @@ extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
>  extern void get_dynamic_stack_size (rtx *, unsigned, unsigned, HOST_WIDE_INT *);
>
>  /* Returns the address of the dynamic stack space without allocating it.  */
> -extern rtx get_dynamic_stack_base (poly_int64, unsigned);
> +extern rtx get_dynamic_stack_base (poly_int64, unsigned, rtx);
>
>  /* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET.  */
>  extern rtx align_dynamic_address (rtx, unsigned);
> diff --git a/gcc/explow.c b/gcc/explow.c
> index 0fbc6d25b816457a3d13ed45d16b5dd0513cfacd..41c3f6ace49c0e55c080e10b917842b1b21d49eb 100644
> --- a/gcc/explow.c
> +++ b/gcc/explow.c
> @@ -1583,10 +1583,14 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
>     OFFSET is the offset of the area into the virtual stack vars area.
>
>     REQUIRED_ALIGN is the alignment (in bits) required for the region
> -   of memory.  */
> +   of memory.
> +
> +   BASE is the rtx of the base of this virtual stack vars area.
> +   The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
> +   on the stack.  */
>
>  rtx
> -get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
> +get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
>  {
>    rtx target;
>
> @@ -1594,7 +1598,7 @@ get_dynamic_stack_base (poly_int64 offset, unsigned required_align)
>      crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
>
>    target = gen_reg_rtx (Pmode);
> -  emit_move_insn (target, virtual_stack_vars_rtx);
> +  emit_move_insn (target, base);
>    target = expand_binop (Pmode, add_optab, target,
>                          gen_int_mode (offset, Pmode),
>                          NULL_RTX, 1, OPTAB_LIB_WIDEN);
> diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
> index a32715ddb92e69b7ca7be28a8f17a369b891bd76..4f854fb994229fd4ed91d3b5cff7c7acff9a55bc 100644
> --- a/gcc/sanitizer.def
> +++ b/gcc/sanitizer.def
> @@ -180,6 +180,12 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp",
>  DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub",
>                       BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
>
> +/* Hardware Address Sanitizer.  */
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_INIT, "__hwasan_init",
> +                     BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_HWASAN_TAG_MEM, "__hwasan_tag_memory",
> +                     BT_FN_VOID_PTR_UINT8_PTRMODE, ATTR_NOTHROW_LIST)
> +
>  /* Thread Sanitizer */
>  DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init",
>                       BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
> diff --git a/gcc/target.def b/gcc/target.def
> index 25f0ae228210f926077020082f129fb2e599f062..44807438431488a5a7aa8f8125d256869e152b68 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -6874,6 +6874,71 @@ At preset, this feature does not support address spaces.  It also requires\n\
>  @code{Pmode} to be the same as @code{ptr_mode}.",
>   bool, (), default_memtag_can_tag_addresses)
>
> +DEFHOOK
> +(tag_size,
> + "Return the size of a tag (in bits) for this platform.\n\
> +\n\
> +The default returns 8.",
> +  uint8_t, (), default_memtag_tag_size)
> +
> +DEFHOOK
> +(granule_size,
> + "Return the size in real memory that each byte in shadow memory refers to.\n\
> +I.e. if a variable is @var{X} bytes long in memory, then this hook should\n\
> +return the value @var{Y} such that the tag in shadow memory spans\n\
> +@var{X}/@var{Y} bytes.\n\
> +\n\
> +Most variables will need to be aligned to this amount since two variables\n\
> +that are neighbors in memory and share a tag granule would need to share\n\
> +the same tag.\n\
> +\n\
> +The default returns 16.",
> +  uint8_t, (), default_memtag_granule_size)
> +
> +DEFHOOK
> +(insert_random_tag,
> + "Return an RTX representing the value of @var{untagged} but with a\n\
> +(possibly) random tag in it.\n\
> +Put that value into @var{target} if it is convenient to do so.\n\
> +This function is used to generate a tagged base for the current stack frame.",
> +  rtx, (rtx untagged, rtx target), default_memtag_insert_random_tag)
> +
> +DEFHOOK
> +(add_tag,
> + "Return an RTX that represents the result of adding @var{addr_offset} to\n\
> +the address in pointer @var{base} and @var{tag_offset} to the tag in pointer\n\
> +@var{base}.\n\
> +The resulting RTX must either be a valid memory address or be able to get\n\
> +put into an operand with @code{force_operand}.\n\
> +\n\
> +Unlike other memtag hooks, this must return an expression and not emit any\n\
> +RTL.",
> +  rtx, (rtx base, poly_int64 addr_offset, uint8_t tag_offset),
> +  default_memtag_add_tag)
> +
> +DEFHOOK
> +(set_tag,
> + "Return an RTX representing @var{untagged_base} but with the tag @var{tag}.\n\
> +Try and store this in @var{target} if convenient.\n\
> +@var{untagged_base} is required to have a zero tag when this hook is called.\n\
> +The default of this hook is to set the top byte of @var{untagged_base} to\n\
> +@var{tag}.",
> +  rtx, (rtx untagged_base, rtx tag, rtx target), default_memtag_set_tag)
> +
> +DEFHOOK
> +(extract_tag,
> + "Return an RTX representing the tag stored in @var{tagged_pointer}.\n\
> +Store the result in @var{target} if it is convenient.\n\
> +The default represents the top byte of the original pointer.",
> +  rtx, (rtx tagged_pointer, rtx target), default_memtag_extract_tag)
> +
> +DEFHOOK
> +(untagged_pointer,
> + "Return an RTX representing @var{tagged_pointer} with its tag set to zero.\n\
> +Store the result in @var{target} if convenient.\n\
> +The default clears the top byte of the original pointer.",
> +  rtx, (rtx tagged_pointer, rtx target), default_memtag_untagged_pointer)
> +
>  HOOK_VECTOR_END (memtag)
>  #undef HOOK_PREFIX
>  #define HOOK_PREFIX "TARGET_"
> diff --git a/gcc/targhooks.h b/gcc/targhooks.h
> index 0065c686978d7120978430013c73b1055aaf95c7..68e8688a32f18481ee61f06879aacff20163105b 100644
> --- a/gcc/targhooks.h
> +++ b/gcc/targhooks.h
> @@ -287,4 +287,12 @@ extern bool speculation_safe_value_not_needed (bool);
>  extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
>
>  extern bool default_memtag_can_tag_addresses ();
> +extern uint8_t default_memtag_tag_size ();
> +extern uint8_t default_memtag_granule_size ();
> +extern rtx default_memtag_insert_random_tag (rtx, rtx);
> +extern rtx default_memtag_add_tag (rtx, poly_int64, uint8_t);
> +extern rtx default_memtag_set_tag (rtx, rtx, rtx);
> +extern rtx default_memtag_extract_tag (rtx, rtx);
> +extern rtx default_memtag_untagged_pointer (rtx, rtx);
> +
>  #endif /* GCC_TARGHOOKS_H */
> diff --git a/gcc/targhooks.c b/gcc/targhooks.c
> index 46cb536041d396c32fd08042581d6d5cd5ad0395..e634df3f6c6837e422246a7736c0de4471ce1e77 100644
> --- a/gcc/targhooks.c
> +++ b/gcc/targhooks.c
> @@ -73,6 +73,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "varasm.h"
>  #include "flags.h"
>  #include "explow.h"
> +#include "expmed.h"
>  #include "calls.h"
>  #include "expr.h"
>  #include "output.h"
> @@ -86,6 +87,9 @@ along with GCC; see the file COPYING3.  If not see
>  #include "langhooks.h"
>  #include "sbitmap.h"
>  #include "function-abi.h"
> +#include "attribs.h"
> +#include "asan.h"
> +#include "emit-rtl.h"
>
>  bool
>  default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
> @@ -2415,10 +2419,115 @@ default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
>    return result;
>  }
>
> +/* How many bits to shift in order to access the tag bits.
> +   The default is to store the tag in the top 8 bits of a 64 bit pointer, hence
> +   shifting 56 bits will leave just the tag.  */
> +#define HWASAN_SHIFT (GET_MODE_PRECISION (Pmode) - 8)
> +#define HWASAN_SHIFT_RTX GEN_INT (HWASAN_SHIFT)
> +
>  bool
>  default_memtag_can_tag_addresses ()
>  {
>    return false;
>  }
>
> +uint8_t
> +default_memtag_tag_size ()
> +{
> +  return 8;
> +}
> +
> +uint8_t
> +default_memtag_granule_size ()
> +{
> +  return 16;
> +}
> +
> +/* The default implementation of TARGET_MEMTAG_INSERT_RANDOM_TAG.  */
> +rtx
> +default_memtag_insert_random_tag (rtx untagged, rtx target)
> +{
> +  gcc_assert (param_hwasan_instrument_stack);
> +  if (param_hwasan_random_frame_tag)
> +    {
> +      rtx fn = init_one_libfunc ("__hwasan_generate_tag");
> +      rtx new_tag = emit_library_call_value (fn, NULL_RTX, LCT_NORMAL, QImode);
> +      return targetm.memtag.set_tag (untagged, new_tag, target);
> +    }
> +  else
> +    {
> +      /* NOTE: The kernel API does not have __hwasan_generate_tag exposed.
> +        In the future we may add the option emit random tags with inline
> +        instrumentation instead of function calls.  This would be the same
> +        between the kernel and userland.  */
> +      return untagged;
> +    }
> +}
> +
> +/* The default implementation of TARGET_MEMTAG_ADD_TAG.  */
> +rtx
> +default_memtag_add_tag (rtx base, poly_int64 offset, uint8_t tag_offset)
> +{
> +  /* Need to look into what the most efficient code sequence is.
> +     This is a code sequence that would be emitted *many* times, so we
> +     want it as small as possible.
> +
> +     There are two places where tag overflow is a question:
> +       - Tagging the shadow stack.
> +         (both tagging and untagging).
> +       - Tagging addressable pointers.
> +
> +     We need to ensure both behaviors are the same (i.e. that the tag that
> +     ends up in a pointer after "overflowing" the tag bits with a tag addition
> +     is the same that ends up in the shadow space).
> +
> +     The aim is that the behavior of tag addition should follow modulo
> +     wrapping in both instances.
> +
> +     The libhwasan code doesn't have any path that increments a pointer's tag,
> +     which means it has no opinion on what happens when a tag increment
> +     overflows (and hence we can choose our own behavior).  */
> +
> +  offset += ((uint64_t)tag_offset << HWASAN_SHIFT);
> +  return plus_constant (Pmode, base, offset);
> +}
> +
> +/* The default implementation of TARGET_MEMTAG_SET_TAG.  */
> +rtx
> +default_memtag_set_tag (rtx untagged, rtx tag, rtx target)
> +{
> +  gcc_assert (GET_MODE (untagged) == Pmode && GET_MODE (tag) == QImode);
> +  tag = expand_simple_binop (Pmode, ASHIFT, tag, HWASAN_SHIFT_RTX, NULL_RTX,
> +                            /* unsignedp = */1, OPTAB_WIDEN);
> +  rtx ret = expand_simple_binop (Pmode, IOR, untagged, tag, target,
> +                                /* unsignedp = */1, OPTAB_DIRECT);
> +  gcc_assert (ret);
> +  return ret;
> +}
> +
> +/* The default implementation of TARGET_MEMTAG_EXTRACT_TAG.  */
> +rtx
> +default_memtag_extract_tag (rtx tagged_pointer, rtx target)
> +{
> +  rtx tag = expand_simple_binop (Pmode, LSHIFTRT, tagged_pointer,
> +                                HWASAN_SHIFT_RTX, target,
> +                                /* unsignedp = */0,
> +                                OPTAB_DIRECT);
> +  rtx ret = gen_lowpart (QImode, tag);
> +  gcc_assert (ret);
> +  return ret;
> +}
> +
> +/* The default implementation of TARGET_MEMTAG_UNTAGGED_POINTER.  */
> +rtx
> +default_memtag_untagged_pointer (rtx tagged_pointer, rtx target)
> +{
> +  rtx tag_mask = gen_int_mode ((HOST_WIDE_INT_1U << HWASAN_SHIFT) - 1, Pmode);
> +  rtx untagged_base = expand_simple_binop (Pmode, AND, tagged_pointer,
> +                                          tag_mask, target, true,
> +                                          OPTAB_DIRECT);
> +  gcc_assert (untagged_base);
> +  return untagged_base;
> +}
> +
>  #include "gt-targhooks.h"
> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index 2a3e7c064a5fbb6913481104975ca85615e49f8e..9938b6afbd4fa22898dbc3c29b92061a71810b08 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -512,6 +512,9 @@ compile_file (void)
>        if (flag_sanitize & SANITIZE_THREAD)
>         tsan_finish_file ();
>
> +      if (flag_sanitize & SANITIZE_HWADDRESS)
> +       hwasan_finish_file ();
> +
>        omp_finish_file ();
>
>        output_shared_constant_pool ();
>


-- 
BR,
Hongtao

^ permalink raw reply	[flat|nested] 150+ messages in thread

* libsanitizer: Hwasan reporting check for dladdr failing
  2020-11-23  8:18                 ` Martin Liška
@ 2020-11-24 15:46                   ` Matthew Malcomson
  2020-11-24 15:53                     ` Kyrylo Tkachov
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-24 15:46 UTC (permalink / raw)
  To: gcc-patches; +Cc: Martin Liska, Richard Sandiford

[-- Attachment #1: Type: text/plain, Size: 2173 bytes --]

Hello there,

This is the compiler-rt patch I'd like to cherry-pick so that the hwasan tests
pass.

It is in LLVM as commit 83ac18205ec69a00ac2be3b603bc3a61293fbe89.

Ok for trunk?

Also is the libhwasan library merge from the below email OK for trunk?
https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558999.html
(Note I would also add a line to README.gcc mentioning compiler-rt/lib/hwasan
on top of the changes in that patch).

I would guess so, but wasn't certain the OK had ever been said anywhere.

Regards,
Matthew

-------------------------


In `GetGlobalSizeFromDescriptor` we use `dladdr` to get info on the the
current address.  `dladdr` returns 0 if it failed.
During testing on Linux this returned 0 to indicate failure, and
populated the `info` structure with a NULL pointer which was
dereferenced later.

This patch checks for `dladdr` returning 0, and in that case returns 0
from `GetGlobalSizeFromDescriptor` to indicate failure of identifying
the address.

This occurs when `GetModuleNameAndOffsetForPC` succeeds for some address
not in a dynamically loaded library.  One example is when the found
"module" is '[stack]' having come from parsing /proc/self/maps.

Cherry-pick from 83ac18205ec69a00ac2be3b603bc3a61293fbe89.

Differential Revision: https://reviews.llvm.org/D91344


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


diff --git a/libsanitizer/hwasan/hwasan_report.cpp b/libsanitizer/hwasan/hwasan_report.cpp
index 0be7deeaee1a0bd523d9e0fe1dc3b1311b3920e2..894a149775f291bae9cad833b1ac54914212f405 100644
--- a/libsanitizer/hwasan/hwasan_report.cpp
+++ b/libsanitizer/hwasan/hwasan_report.cpp
@@ -254,7 +254,8 @@ static bool TagsEqual(tag_t tag, tag_t *tag_ptr) {
 static uptr GetGlobalSizeFromDescriptor(uptr ptr) {
   // Find the ELF object that this global resides in.
   Dl_info info;
-  dladdr(reinterpret_cast<void *>(ptr), &info);
+  if (dladdr(reinterpret_cast<void *>(ptr), &info) == 0)
+    return 0;
   auto *ehdr = reinterpret_cast<const ElfW(Ehdr) *>(info.dli_fbase);
   auto *phdr_begin = reinterpret_cast<const ElfW(Phdr) *>(
       reinterpret_cast<const u8 *>(ehdr) + ehdr->e_phoff);


[-- Attachment #2: libsanitizer-cherry-pick.patch --]
[-- Type: text/plain, Size: 784 bytes --]

diff --git a/libsanitizer/hwasan/hwasan_report.cpp b/libsanitizer/hwasan/hwasan_report.cpp
index 0be7deeaee1a0bd523d9e0fe1dc3b1311b3920e2..894a149775f291bae9cad833b1ac54914212f405 100644
--- a/libsanitizer/hwasan/hwasan_report.cpp
+++ b/libsanitizer/hwasan/hwasan_report.cpp
@@ -254,7 +254,8 @@ static bool TagsEqual(tag_t tag, tag_t *tag_ptr) {
 static uptr GetGlobalSizeFromDescriptor(uptr ptr) {
   // Find the ELF object that this global resides in.
   Dl_info info;
-  dladdr(reinterpret_cast<void *>(ptr), &info);
+  if (dladdr(reinterpret_cast<void *>(ptr), &info) == 0)
+    return 0;
   auto *ehdr = reinterpret_cast<const ElfW(Ehdr) *>(info.dli_fbase);
   auto *phdr_begin = reinterpret_cast<const ElfW(Phdr) *>(
       reinterpret_cast<const u8 *>(ehdr) + ehdr->e_phoff);


^ permalink raw reply	[flat|nested] 150+ messages in thread

* RE: libsanitizer: Hwasan reporting check for dladdr failing
  2020-11-24 15:46                   ` libsanitizer: Hwasan reporting check for dladdr failing Matthew Malcomson
@ 2020-11-24 15:53                     ` Kyrylo Tkachov
  2020-11-24 16:20                       ` Matthew Malcomson
  0 siblings, 1 reply; 150+ messages in thread
From: Kyrylo Tkachov @ 2020-11-24 15:53 UTC (permalink / raw)
  To: Matthew Malcomson; +Cc: Richard Sandiford, gcc-patches

Hi Matthew,

> -----Original Message-----
> From: Gcc-patches <gcc-patches-bounces@gcc.gnu.org> On Behalf Of
> Matthew Malcomson via Gcc-patches
> Sent: 24 November 2020 15:47
> To: gcc-patches@gcc.gnu.org
> Cc: Richard Sandiford <Richard.Sandiford@arm.com>
> Subject: libsanitizer: Hwasan reporting check for dladdr failing
> 
> Hello there,
> 
> This is the compiler-rt patch I'd like to cherry-pick so that the hwasan tests
> pass.
> 
> It is in LLVM as commit 83ac18205ec69a00ac2be3b603bc3a61293fbe89.
> 
> Ok for trunk?
> 
> Also is the libhwasan library merge from the below email OK for trunk?
> https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558999.html
> (Note I would also add a line to README.gcc mentioning compiler-
> rt/lib/hwasan
> on top of the changes in that patch).
> 
> I would guess so, but wasn't certain the OK had ever been said anywhere.

I believe merges from an upstream are generally considered pre-approved. In any case, I see that merge committed as 98f792ff538109c71d85ab2a61461cd090f3b9f3

Thanks,
Kyrill

> 
> Regards,
> Matthew
> 
> -------------------------
> 
> 
> In `GetGlobalSizeFromDescriptor` we use `dladdr` to get info on the the
> current address.  `dladdr` returns 0 if it failed.
> During testing on Linux this returned 0 to indicate failure, and
> populated the `info` structure with a NULL pointer which was
> dereferenced later.
> 
> This patch checks for `dladdr` returning 0, and in that case returns 0
> from `GetGlobalSizeFromDescriptor` to indicate failure of identifying
> the address.
> 
> This occurs when `GetModuleNameAndOffsetForPC` succeeds for some
> address
> not in a dynamically loaded library.  One example is when the found
> "module" is '[stack]' having come from parsing /proc/self/maps.
> 
> Cherry-pick from 83ac18205ec69a00ac2be3b603bc3a61293fbe89.
> 
> Differential Revision: https://reviews.llvm.org/D91344
> 
> 
> ###############     Attachment also inlined for ease of reply
> ###############
> 
> 
> diff --git a/libsanitizer/hwasan/hwasan_report.cpp
> b/libsanitizer/hwasan/hwasan_report.cpp
> index
> 0be7deeaee1a0bd523d9e0fe1dc3b1311b3920e2..894a149775f291bae9cad8
> 33b1ac54914212f405 100644
> --- a/libsanitizer/hwasan/hwasan_report.cpp
> +++ b/libsanitizer/hwasan/hwasan_report.cpp
> @@ -254,7 +254,8 @@ static bool TagsEqual(tag_t tag, tag_t *tag_ptr) {
>  static uptr GetGlobalSizeFromDescriptor(uptr ptr) {
>    // Find the ELF object that this global resides in.
>    Dl_info info;
> -  dladdr(reinterpret_cast<void *>(ptr), &info);
> +  if (dladdr(reinterpret_cast<void *>(ptr), &info) == 0)
> +    return 0;
>    auto *ehdr = reinterpret_cast<const ElfW(Ehdr) *>(info.dli_fbase);
>    auto *phdr_begin = reinterpret_cast<const ElfW(Phdr) *>(
>        reinterpret_cast<const u8 *>(ehdr) + ehdr->e_phoff);


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: libsanitizer: Hwasan reporting check for dladdr failing
  2020-11-24 15:53                     ` Kyrylo Tkachov
@ 2020-11-24 16:20                       ` Matthew Malcomson
  2020-11-24 16:22                         ` Kyrylo Tkachov
  0 siblings, 1 reply; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-24 16:20 UTC (permalink / raw)
  To: Kyrylo Tkachov; +Cc: Richard Sandiford, gcc-patches

On 24/11/2020 15:53, Kyrylo Tkachov wrote:
> Hi Matthew,
> 
>> -----Original Message-----
>> From: Gcc-patches <gcc-patches-bounces@gcc.gnu.org> On Behalf Of
>> Matthew Malcomson via Gcc-patches
>> Sent: 24 November 2020 15:47
>> To: gcc-patches@gcc.gnu.org
>> Cc: Richard Sandiford <Richard.Sandiford@arm.com>
>> Subject: libsanitizer: Hwasan reporting check for dladdr failing
>>
>> Hello there,
>>
>> This is the compiler-rt patch I'd like to cherry-pick so that the hwasan tests
>> pass.
>>
>> It is in LLVM as commit 83ac18205ec69a00ac2be3b603bc3a61293fbe89.
>>
>> Ok for trunk?
>>
>> Also is the libhwasan library merge from the below email OK for trunk?
>> https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558999.html
>> (Note I would also add a line to README.gcc mentioning compiler-
>> rt/lib/hwasan
>> on top of the changes in that patch).
>>
>> I would guess so, but wasn't certain the OK had ever been said anywhere.
> 
> I believe merges from an upstream are generally considered pre-approved. In any case, I see that merge committed as 98f792ff538109c71d85ab2a61461cd090f3b9f3
> 
> Thanks,
> Kyrill

Thanks Kyrill,

For the record, 98f792ff538109c71d85ab2a61461cd090f3b9f3 is the merge 
without libhwasan part, while I was asking about the libhwasan part.

However, given the standard pre-approved merge from upstream status I 
guess it doesn't matter -- and that's good to know for the future too.

Thanks!
Matthew

> 
>>
>> Regards,
>> Matthew
>>
>> -------------------------
>>
>>
>> In `GetGlobalSizeFromDescriptor` we use `dladdr` to get info on the the
>> current address.  `dladdr` returns 0 if it failed.
>> During testing on Linux this returned 0 to indicate failure, and
>> populated the `info` structure with a NULL pointer which was
>> dereferenced later.
>>
>> This patch checks for `dladdr` returning 0, and in that case returns 0
>> from `GetGlobalSizeFromDescriptor` to indicate failure of identifying
>> the address.
>>
>> This occurs when `GetModuleNameAndOffsetForPC` succeeds for some
>> address
>> not in a dynamically loaded library.  One example is when the found
>> "module" is '[stack]' having come from parsing /proc/self/maps.
>>
>> Cherry-pick from 83ac18205ec69a00ac2be3b603bc3a61293fbe89.
>>
>> Differential Revision: https://reviews.llvm.org/D91344
>>
>>
>> ###############     Attachment also inlined for ease of reply
>> ###############
>>
>>
>> diff --git a/libsanitizer/hwasan/hwasan_report.cpp
>> b/libsanitizer/hwasan/hwasan_report.cpp
>> index
>> 0be7deeaee1a0bd523d9e0fe1dc3b1311b3920e2..894a149775f291bae9cad8
>> 33b1ac54914212f405 100644
>> --- a/libsanitizer/hwasan/hwasan_report.cpp
>> +++ b/libsanitizer/hwasan/hwasan_report.cpp
>> @@ -254,7 +254,8 @@ static bool TagsEqual(tag_t tag, tag_t *tag_ptr) {
>>   static uptr GetGlobalSizeFromDescriptor(uptr ptr) {
>>     // Find the ELF object that this global resides in.
>>     Dl_info info;
>> -  dladdr(reinterpret_cast<void *>(ptr), &info);
>> +  if (dladdr(reinterpret_cast<void *>(ptr), &info) == 0)
>> +    return 0;
>>     auto *ehdr = reinterpret_cast<const ElfW(Ehdr) *>(info.dli_fbase);
>>     auto *phdr_begin = reinterpret_cast<const ElfW(Phdr) *>(
>>         reinterpret_cast<const u8 *>(ehdr) + ehdr->e_phoff);
> 


^ permalink raw reply	[flat|nested] 150+ messages in thread

* RE: libsanitizer: Hwasan reporting check for dladdr failing
  2020-11-24 16:20                       ` Matthew Malcomson
@ 2020-11-24 16:22                         ` Kyrylo Tkachov
  2020-11-25  8:23                           ` Martin Liška
  0 siblings, 1 reply; 150+ messages in thread
From: Kyrylo Tkachov @ 2020-11-24 16:22 UTC (permalink / raw)
  To: Matthew Malcomson; +Cc: Richard Sandiford, gcc-patches, Martin Liska



> -----Original Message-----
> From: Matthew Malcomson <Matthew.Malcomson@arm.com>
> Sent: 24 November 2020 16:20
> To: Kyrylo Tkachov <Kyrylo.Tkachov@arm.com>
> Cc: Richard Sandiford <Richard.Sandiford@arm.com>; gcc-
> patches@gcc.gnu.org
> Subject: Re: libsanitizer: Hwasan reporting check for dladdr failing
> 
> On 24/11/2020 15:53, Kyrylo Tkachov wrote:
> > Hi Matthew,
> >
> >> -----Original Message-----
> >> From: Gcc-patches <gcc-patches-bounces@gcc.gnu.org> On Behalf Of
> >> Matthew Malcomson via Gcc-patches
> >> Sent: 24 November 2020 15:47
> >> To: gcc-patches@gcc.gnu.org
> >> Cc: Richard Sandiford <Richard.Sandiford@arm.com>
> >> Subject: libsanitizer: Hwasan reporting check for dladdr failing
> >>
> >> Hello there,
> >>
> >> This is the compiler-rt patch I'd like to cherry-pick so that the hwasan tests
> >> pass.
> >>
> >> It is in LLVM as commit 83ac18205ec69a00ac2be3b603bc3a61293fbe89.
> >>
> >> Ok for trunk?
> >>
> >> Also is the libhwasan library merge from the below email OK for trunk?
> >> https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558999.html
> >> (Note I would also add a line to README.gcc mentioning compiler-
> >> rt/lib/hwasan
> >> on top of the changes in that patch).
> >>
> >> I would guess so, but wasn't certain the OK had ever been said anywhere.
> >
> > I believe merges from an upstream are generally considered pre-approved.
> In any case, I see that merge committed as
> 98f792ff538109c71d85ab2a61461cd090f3b9f3
> >
> > Thanks,
> > Kyrill
> 
> Thanks Kyrill,
> 
> For the record, 98f792ff538109c71d85ab2a61461cd090f3b9f3 is the merge
> without libhwasan part, while I was asking about the libhwasan part.
> 
> However, given the standard pre-approved merge from upstream status I
> guess it doesn't matter -- and that's good to know for the future too.

Ah, I suppose in the sanitiser case as there are local modifications maybe it's not a straightforward pre-approval, so don't take my word on it 😉
Martin will probably know the rules here.
Thanks,
Kyrill

> 
> Thanks!
> Matthew
> 
> >
> >>
> >> Regards,
> >> Matthew
> >>
> >> -------------------------
> >>
> >>
> >> In `GetGlobalSizeFromDescriptor` we use `dladdr` to get info on the the
> >> current address.  `dladdr` returns 0 if it failed.
> >> During testing on Linux this returned 0 to indicate failure, and
> >> populated the `info` structure with a NULL pointer which was
> >> dereferenced later.
> >>
> >> This patch checks for `dladdr` returning 0, and in that case returns 0
> >> from `GetGlobalSizeFromDescriptor` to indicate failure of identifying
> >> the address.
> >>
> >> This occurs when `GetModuleNameAndOffsetForPC` succeeds for some
> >> address
> >> not in a dynamically loaded library.  One example is when the found
> >> "module" is '[stack]' having come from parsing /proc/self/maps.
> >>
> >> Cherry-pick from 83ac18205ec69a00ac2be3b603bc3a61293fbe89.
> >>
> >> Differential Revision: https://reviews.llvm.org/D91344
> >>
> >>
> >> ###############     Attachment also inlined for ease of reply
> >> ###############
> >>
> >>
> >> diff --git a/libsanitizer/hwasan/hwasan_report.cpp
> >> b/libsanitizer/hwasan/hwasan_report.cpp
> >> index
> >>
> 0be7deeaee1a0bd523d9e0fe1dc3b1311b3920e2..894a149775f291bae9cad8
> >> 33b1ac54914212f405 100644
> >> --- a/libsanitizer/hwasan/hwasan_report.cpp
> >> +++ b/libsanitizer/hwasan/hwasan_report.cpp
> >> @@ -254,7 +254,8 @@ static bool TagsEqual(tag_t tag, tag_t *tag_ptr) {
> >>   static uptr GetGlobalSizeFromDescriptor(uptr ptr) {
> >>     // Find the ELF object that this global resides in.
> >>     Dl_info info;
> >> -  dladdr(reinterpret_cast<void *>(ptr), &info);
> >> +  if (dladdr(reinterpret_cast<void *>(ptr), &info) == 0)
> >> +    return 0;
> >>     auto *ehdr = reinterpret_cast<const ElfW(Ehdr) *>(info.dli_fbase);
> >>     auto *phdr_begin = reinterpret_cast<const ElfW(Phdr) *>(
> >>         reinterpret_cast<const u8 *>(ehdr) + ehdr->e_phoff);
> >


^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: Update: [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN
  2020-11-24 12:30               ` Hongtao Liu
@ 2020-11-24 16:45                 ` Matthew Malcomson
  0 siblings, 0 replies; 150+ messages in thread
From: Matthew Malcomson @ 2020-11-24 16:45 UTC (permalink / raw)
  To: Hongtao Liu; +Cc: Richard Sandiford, Richard Earnshaw, GCC Patches

On 24/11/2020 12:30, Hongtao Liu wrote:
> Hi:
>    I'm learning about this patch, and I see one place that might be
> slighted improved.
> 
> +      poly_int64 size = (top - bot);
> +
> +      /* Assert the edge of each variable is aligned to the HWASAN tag granule
> +        size.  */
> +      gcc_assert (multiple_p (top, HWASAN_TAG_GRANULE_SIZE));
> +      gcc_assert (multiple_p (bot, HWASAN_TAG_GRANULE_SIZE));
> +      gcc_assert (multiple_p (size, HWASAN_TAG_GRANULE_SIZE));
> +
> 
> The last gcc_assert looks redundant?

Hi

I think you're right.

Just FYI I'm planning on making that change as an obvious fix after the 
patchset as it is now goes in.

That way I can say I ran all my tests on the patch series that I applied 
without going through the all the tests again.

Thanks for the catch!

Matthew
> 
> On Sat, Nov 21, 2020 at 2:48 AM Matthew Malcomson via Gcc-patches
> <gcc-patches@gcc.gnu.org> wrote:
>>
>>

^ permalink raw reply	[flat|nested] 150+ messages in thread

* Re: libsanitizer: Hwasan reporting check for dladdr failing
  2020-11-24 16:22                         ` Kyrylo Tkachov
@ 2020-11-25  8:23                           ` Martin Liška
  0 siblings, 0 replies; 150+ messages in thread
From: Martin Liška @ 2020-11-25  8:23 UTC (permalink / raw)
  To: Kyrylo Tkachov, Matthew Malcomson; +Cc: Richard Sandiford, gcc-patches

On 11/24/20 5:22 PM, Kyrylo Tkachov wrote:
> Ah, I suppose in the sanitiser case as there are local modifications maybe it's not a straightforward pre-approval, so don't take my word on it 😉
> Martin will probably know the rules here.
> Thanks,
> Kyrill

Hello.

As I was told by Jeff that libsanitizer merges are pre-approved as long as
one follows all the steps in libsanitizer/HOWTO_MERGE.

About the cherry-picked commit: I would install it also automatically.
As HWASAN is not in master, it can't influence anything existing
in the GCC compiler.

Martin

^ permalink raw reply	[flat|nested] 150+ messages in thread

end of thread, other threads:[~2020-11-25  8:23 UTC | newest]

Thread overview: 150+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-07 18:37 v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC Matthew Malcomson
2019-11-05 11:32 ` Matthew Malcomson
2019-11-05 11:34   ` [PATCH 3/X] [libsanitizer] libhwasan initialisation include kernel syscall ABI relaxation Matthew Malcomson
2019-11-05 11:34   ` [PATCH 2/X] [libsanitizer] Introduce libhwasan to GCC tree Matthew Malcomson
2019-11-05 11:34   ` [PATCH 6/X] [libsanitizer] Add hwasan_exceptions.cpp file Matthew Malcomson
2019-11-05 11:34   ` [PATCH 5/X] [libsanitizer] Remove system allocator fallback Matthew Malcomson
2019-11-05 11:34   ` [PATCH 10/X] [libsanitizer] Tie the hwasan library into our build system Matthew Malcomson
2019-11-05 11:34   ` [PATCH 7/X] [libsanitizer] Add missing SANITIZER_INTERFACE_ATTRIBUTE on __hwasan_personality_wrapper Matthew Malcomson
2019-11-05 11:34   ` [PATCH 1/X][mid-end] Fix declared type of personality functions Matthew Malcomson
2019-11-05 11:41     ` Richard Biener
2019-11-05 11:34   ` [PATCH 11/X] [libsanitizer] Only build libhwasan when targeting AArch64 Matthew Malcomson
2019-11-05 11:34   ` [PATCH 4/X] [libsanitizer] libhwasan add longjmp & setjmp interceptors Matthew Malcomson
2019-11-05 11:34   ` [PATCH 8/X] [libsanitizer] Expose __hwasan_tag_mismatch_stub Matthew Malcomson
2019-11-05 11:34   ` [PATCH 12/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
2019-11-05 11:34   ` [aarch64] Allocate enough space for err_str in aarch64_handle_attr_branch_protection Matthew Malcomson
2019-11-05 11:38     ` Kyrylo Tkachov
2019-11-05 11:35   ` [PATCH 16/X] [libsanitizer] Add tests Matthew Malcomson
2019-11-05 11:35   ` [PATCH 14/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN Matthew Malcomson
2019-11-05 11:35   ` [PATCH 17/X] [libsanitizer] Add hwasan Exception handling Matthew Malcomson
2019-11-05 11:35   ` [PATCH 9/X] [libsanitizer] Remove lazy thread initialisation Matthew Malcomson
2019-11-05 11:35   ` [PATCH 18/X] [libsanitizer] Add in MTE stubs Matthew Malcomson
2019-11-05 11:35   ` [PATCH 13/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
2019-11-05 13:11     ` Andrey Konovalov via gcc-patches
2019-11-07 12:48       ` Matthew Malcomson
2019-11-07 15:51         ` Andrey Konovalov via gcc-patches
2019-11-07 18:01           ` Evgenii Stepanov via gcc-patches
2019-11-08 13:56             ` Andrey Konovalov via gcc-patches
2019-11-05 18:32     ` Joseph Myers
2019-11-05 11:35   ` [PATCH 15/X] [libsanitizer] Add hwasan pass and associated gimple changes Matthew Malcomson
2019-11-05 11:50   ` [PATCH 0/X] Introduce HWASAN sanitizer to GCC Matthew Malcomson
2019-11-05 15:11   ` Martin Liška
2019-11-05 16:11     ` Matthew Malcomson
2019-11-05 17:22       ` Martin Liška
2019-11-05 17:35         ` Matthew Malcomson
2019-11-05 18:15           ` Martin Liška
2019-11-05 18:30   ` Joseph Myers
2019-11-07 18:37   ` [PATCH 4/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
2019-11-20 11:22     ` Martin Liška
2019-11-07 18:37   ` [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
2019-11-11 14:30     ` Martin Liška
2019-11-11 16:13       ` Matthew Malcomson
2019-11-12 12:10         ` Martin Liška
2019-11-13 15:25           ` Matthew Malcomson
2019-11-20 14:38             ` Martin Liška
2019-11-20 15:46               ` Matthew Malcomson
2019-11-21 13:10                 ` Martin Liška
2019-11-26 10:15               ` Martin Liška
2019-11-07 18:37   ` [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN Matthew Malcomson
2019-11-20 14:06     ` Martin Liška
2019-11-20 14:42       ` Matthew Malcomson
2019-11-20 14:48         ` Martin Liška
2019-11-20 15:02           ` Matthew Malcomson
2019-11-20 18:13             ` Joseph Myers
2019-11-07 18:37   ` [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes Matthew Malcomson
2019-11-20 14:11     ` Martin Liška
2019-11-07 18:37   ` [PATCH 1/X] [libsanitizer] Tie the hwasan library into our build system Matthew Malcomson
2019-11-20 11:13     ` Martin Liška
2019-11-07 18:37   ` [PATCH 2/X] [libsanitizer] Only build libhwasan when targeting AArch64 Matthew Malcomson
2019-11-07 18:38   ` [PATCH 7/X] [libsanitizer] Add tests Matthew Malcomson
2019-11-20 14:16     ` Martin Liška
2019-12-12 15:19   ` [Patch 0/X] HWASAN v3 Matthew Malcomson
2019-12-12 15:19     ` [PATCH 3/X] [libsanitizer] Add option to bootstrap using HWASAN Matthew Malcomson
2019-12-12 15:47       ` Matthew Malcomson
2019-12-12 16:58       ` Kyrill Tkachov
2019-12-12 15:19     ` [PATCH 4/X] [libsanitizer][options] Add hwasan flags and argument parsing Matthew Malcomson
2020-01-07 15:04       ` Martin Liška
2019-12-12 15:19     ` [PATCH 6/X] [libsanitizer] Add hwasan pass and associated gimple changes Matthew Malcomson
2020-01-07 15:10       ` Martin Liška
2020-01-13 12:20         ` Matthew Malcomson
2019-12-12 15:19     ` [PATCH 7/X] [libsanitizer] Add tests Matthew Malcomson
2019-12-12 15:19     ` [PATCH 2/X] [libsanitizer] Only build libhwasan when targeting AArch64 Matthew Malcomson
2019-12-12 15:19     ` [PATCH 5/X] [libsanitizer][mid-end] Introduce stack variable handling for HWASAN Matthew Malcomson
2019-12-12 15:19     ` [PATCH 1/X] [libsanitizer] Tie the hwasan library into our build system Matthew Malcomson
2019-12-12 15:37     ` Document --with-build-config=bootstrap-asan option Matthew Malcomson
2020-01-11  7:39       ` Gerald Pfeifer
2020-01-13 10:52         ` Matthew Malcomson
2020-11-20 18:11           ` Matthew Malcomson
2020-11-22 22:49             ` Gerald Pfeifer
2019-12-16 11:47     ` [PATCH 7/X] [libsanitizer] Add tests Matthew Malcomson
2019-12-17 14:32     ` [Patch 0/X] HWASAN v3 Matthew Malcomson
2020-01-06 15:26       ` [PING] " Matthew Malcomson
2020-01-07 15:14     ` Martin Liška
2020-01-08 11:26       ` Matthew Malcomson
2020-01-08 19:30         ` Kostya Serebryany via gcc-patches
2020-01-10 16:17         ` Kyrill Tkachov
2020-08-17 14:12     ` [Patch 0/X] HWASAN v4 Matthew Malcomson
2020-10-16  9:03       ` Martin Liška
2020-11-13 16:33         ` Martin Liška
2020-11-13 16:57           ` Matthew Malcomson
2020-11-13 17:22             ` Martin Liška
2020-11-20 18:42               ` Matthew Malcomson
2020-11-23  8:18                 ` Martin Liška
2020-11-24 15:46                   ` libsanitizer: Hwasan reporting check for dladdr failing Matthew Malcomson
2020-11-24 15:53                     ` Kyrylo Tkachov
2020-11-24 16:20                       ` Matthew Malcomson
2020-11-24 16:22                         ` Kyrylo Tkachov
2020-11-25  8:23                           ` Martin Liška
2020-11-16 15:36       ` Hwasan v5 Matthew Malcomson
2020-11-16 15:37       ` [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system Matthew Malcomson
2020-11-18 17:03         ` Richard Sandiford
2020-11-16 15:37       ` [PATCH 2/X] libsanitizer: Only build libhwasan when targeting AArch64 Matthew Malcomson
2020-11-18 17:06         ` Richard Sandiford
2020-11-16 15:37       ` [PATCH 3/X] libsanitizer: Add option to bootstrap using HWASAN Matthew Malcomson
2020-11-18 17:09         ` Richard Sandiford
2020-11-16 15:37       ` [PATCH 4/X] libsanitizer: options: Add hwasan flags and argument parsing Matthew Malcomson
2020-11-18 17:57         ` Richard Sandiford
2020-11-20 18:48           ` Matthew Malcomson
2020-11-23 20:12             ` Richard Sandiford
2020-11-16 15:37       ` [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN Matthew Malcomson
2020-11-16 15:37       ` [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes Matthew Malcomson
2020-11-16 15:37       ` [PATCH 7/X] libsanitizer: Add tests Matthew Malcomson
2020-11-19 13:01       ` Update [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes Matthew Malcomson
2020-11-19 16:28         ` Richard Sandiford
2020-11-20 17:28           ` Matthew Malcomson
2020-11-20 18:09             ` Richard Sandiford
2020-08-17 14:12     ` [PATCH 1/X] libsanitizer: Tie the hwasan library into our build system Matthew Malcomson
2020-10-13 15:57       ` Richard Sandiford
2020-10-28 13:58         ` Matthew Malcomson
2020-10-28 15:02           ` Richard Sandiford
2020-08-17 14:13     ` [PATCH 2/X] libsanitizer: Only build libhwasan when targeting AArch64 Matthew Malcomson
2020-10-13 16:04       ` Richard Sandiford
2020-08-17 14:13     ` [PATCH 3/X] libsanitizer: Add option to bootstrap using HWASAN Matthew Malcomson
2020-10-14  7:52       ` Richard Sandiford
2020-10-14  8:05         ` Stott Graham
2020-08-17 14:13     ` [PATCH 4/X] libsanitizer: options: Add hwasan flags and argument parsing Matthew Malcomson
2020-10-14  9:42       ` Richard Sandiford
2020-08-17 14:13     ` [PATCH 5/X] libsanitizer: mid-end: Introduce stack variable handling for HWASAN Matthew Malcomson
2020-10-14 16:09       ` Richard Sandiford
2020-11-03 13:00         ` Matthew Malcomson
2020-11-04 15:43           ` Richard Sandiford
2020-11-19 12:57         ` Update: " Matthew Malcomson
2020-11-19 15:28           ` Richard Sandiford
2020-11-20 18:46             ` Matthew Malcomson
2020-11-23 20:10               ` Richard Sandiford
2020-11-23 20:16               ` Richard Sandiford
2020-11-24 12:30               ` Hongtao Liu
2020-11-24 16:45                 ` Matthew Malcomson
2020-08-17 14:13     ` [PATCH 6/X] libsanitizer: Add hwasan pass and associated gimple changes Matthew Malcomson
2020-10-14 18:37       ` Richard Sandiford
2020-08-17 14:13     ` [PATCH 7/X] libsanitizer: Add tests Matthew Malcomson
2020-11-20 19:14       ` Richard Sandiford
2020-11-23 18:08         ` Matthew Malcomson
2020-11-23 20:30           ` Richard Sandiford
2019-11-11 16:16 ` v2 [PATCH 0/X] Introduce HWASAN sanitizer to GCC Matthew Malcomson
2019-11-11 16:21   ` Matthew Malcomson
2019-11-20 14:33 ` Martin Liška
2019-11-20 17:41   ` Matthew Malcomson
2019-11-21 13:15     ` Martin Liška
2019-11-21 15:03       ` Matthew Malcomson
2019-11-25 12:23         ` Martin Liška

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).