hctim updated this revision to Diff 199083.
hctim marked an inline comment as done.
hctim added a comment.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

- Updated tests to use unittests rather than expose a public interface and 
exercise that. Minor perf improvements to shouldSample().


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D60593/new/

https://reviews.llvm.org/D60593

Files:
  clang/runtime/CMakeLists.txt
  compiler-rt/cmake/config-ix.cmake
  compiler-rt/lib/gwp_asan/CMakeLists.txt
  compiler-rt/lib/gwp_asan/definitions.h
  compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp
  compiler-rt/lib/gwp_asan/guarded_pool_allocator.h
  compiler-rt/lib/gwp_asan/guarded_pool_allocator_posix.cpp
  compiler-rt/lib/gwp_asan/mutex.h
  compiler-rt/lib/gwp_asan/optional/options_parser.cpp
  compiler-rt/lib/gwp_asan/optional/options_parser.h
  compiler-rt/lib/gwp_asan/options.h
  compiler-rt/lib/gwp_asan/options.inc
  compiler-rt/lib/gwp_asan/random.cpp
  compiler-rt/lib/gwp_asan/random.h
  compiler-rt/lib/gwp_asan/tests/CMakeLists.txt
  compiler-rt/lib/gwp_asan/tests/alignment.cpp
  compiler-rt/lib/gwp_asan/tests/basic.cpp
  compiler-rt/lib/gwp_asan/tests/driver.cpp
  compiler-rt/lib/gwp_asan/tests/harness.h
  compiler-rt/lib/gwp_asan/tests/slot_reuse.cpp
  compiler-rt/lib/gwp_asan/tests/thread_contention.cpp
  compiler-rt/lib/scudo/CMakeLists.txt
  compiler-rt/lib/scudo/scudo_allocator.cpp
  compiler-rt/test/gwp_asan/CMakeLists.txt
  compiler-rt/test/gwp_asan/allocation_methods.cpp
  compiler-rt/test/gwp_asan/allocator_fallback.cpp
  compiler-rt/test/gwp_asan/double_delete.cpp
  compiler-rt/test/gwp_asan/double_deletea.cpp
  compiler-rt/test/gwp_asan/double_free.cpp
  compiler-rt/test/gwp_asan/heap_buffer_overflow.cpp
  compiler-rt/test/gwp_asan/heap_buffer_underflow.cpp
  compiler-rt/test/gwp_asan/invalid_free_left.cpp
  compiler-rt/test/gwp_asan/invalid_free_right.cpp
  compiler-rt/test/gwp_asan/lit.cfg
  compiler-rt/test/gwp_asan/lit.site.cfg.in
  compiler-rt/test/gwp_asan/page_size.h
  compiler-rt/test/gwp_asan/repeated_alloc.cpp
  compiler-rt/test/gwp_asan/unit/lit.site.cfg.in
  compiler-rt/test/gwp_asan/use_after_delete.cpp
  compiler-rt/test/gwp_asan/use_after_deletea.cpp
  compiler-rt/test/gwp_asan/use_after_free.cpp
  compiler-rt/test/lit.common.cfg
  compiler-rt/test/lit.common.configured.in
  compiler-rt/test/scudo/lit.cfg
  llvm/docs/GwpAsan.rst

Index: llvm/docs/GwpAsan.rst
===================================================================
--- /dev/null
+++ llvm/docs/GwpAsan.rst
@@ -0,0 +1,189 @@
+========
+GWP-ASan
+========
+
+.. contents::
+   :local:
+   :depth: 2
+
+Introduction
+============
+
+GWP-ASan is a sampled allocator framework that assists in finding use-after-free
+and heap-buffer-overflow bugs in production environments. It informally is a
+recursive acronym, "**G**\WP-ASan **W**\ill **P**\rovide **A**\llocation
+**SAN**\ity".
+
+GWP-ASan is based on the classic
+`Electric Fence Malloc Debugger <https://linux.die.net/man/3/efence>`_, with a
+key adaptation. Notably, we only choose a very small percentage of allocations
+to sample, and apply guard pages to these sampled allocations only. The sampling
+is small enough to allow us to have very low performance overhead.
+
+There is a small, tunable memory overhead that is fixed for the lifetime of the
+process. This is approximately ~60KiB per process using the default settings,
+depending on the average size of your allocations. Future improvements should
+drastically reduce this amount.
+
+GWP-ASan vs. ASan
+=================
+
+Unlike `AddressSanitizer <https://clang.llvm.org/docs/AddressSanitizer.html>`_,
+GWP-ASan does not induce a significant performance overhead. ASan often requires
+the use of dedicated canaries to be viable in production environments, and as
+such is often impractical.
+
+GWP-ASan is only capable of finding a subset of the memory issues detected by
+ASan. Furthermore, GWP-ASan's bug detection capabilities are only probabilistic.
+As such, we recommend using ASan over GWP-ASan in testing, as well as anywhere
+else that guaranteed error detection is more valuable than the 2x execution
+slowdown/binary size bloat. For the majority of production environments, this
+impact is too high, and GWP-ASan proves extremely useful.
+
+Design
+======
+
+**Please note:** The implementation of GWP-ASan is largely in-flux, and these
+details are subject to change. There are currently other implementations of
+GWP-ASan, such as the implementation featured in
+`Chromium <https://cs.chromium.org/chromium/src/components/gwp_asan/>`_. The
+long-term support goal is to ensure feature-parity where reasonble, and to
+support compiler-rt as the reference implementation.
+
+Allocator Support
+-----------------
+
+GWP-ASan is not a replacement for a traditional allocator. Instead, it works by
+inserting stubs into an existing allocator to redirect allocations when they're
+chosen to be sampled. These stubs are generally implemented in the implementaion
+of ``malloc()``, ``free()`` and ``realloc()``. The stubs are extremely small,
+which makes using GWP-ASan in most allocators fairly trivial. The stubs follow
+the same general pattern (example ``malloc()`` pseudocode below):
+
+.. code:: cpp
+  #ifdef INSTALL_GWP_ASAN_STUBS
+    gwp_asan::GuardedPoolAllocator GWPASanAllocator;
+  #endif
+
+  void* YourAllocator::malloc(..) {
+  #ifdef INSTALL_GWP_ASAN_STUBS
+    if (GWPASanAllocator.shouldSample(..))
+      return GWPASanAllocator.allocate(..);
+  #endif
+
+    // ... the rest of your allocator code here.
+  }
+
+Then, all the supported allocator needs to do is compile with
+``-DINSTALL_GWP_ASAN_STUBS`` and link against the GWP-ASan library!
+
+Guarded Allocation Pool
+-----------------------
+
+The core of GWP-ASan is the guarded allocation pool. Each sampled allocation is
+backed using its own *guarded* slot, which may consist of one or more accessible
+pages. Each guarded slot is surrounded by two *guard* pages, which are mapped as
+inaccessible. We create a contiguous buffer of this ``guard_page | guarded_slot
+| guard_page`` pattern, which we call the *guarded allocation pool*.
+
+Buffer Underflow/Overflow Detection
+-----------------------------------
+
+We gain buffer-overflow and buffer-underflow through these guard pages. When a
+memory access overruns the allocated buffer, it will touch the inaccessible
+guard page, causing memory exception. This exception is caught and handled by
+the internal crash handler. Because each allocation is recorded with metadata
+about where (and by what thread) it was allocated and deallocated, we can
+provide helpful information that will help identify the root cause of the bug.
+
+In order to increase our detection of overflows, we randomly align half of the
+allocations to the right hand side of the guarded slot.
+
+Use after Free Detection
+------------------------
+
+The guarded allocation pool also provide use-after-free detection. Whenever a
+sampled allocation is deallocated, we map the guard page as inaccessible. Any
+memory accesses after deallocation will thus trigger the crash handler, and we
+can provide useful information about the source of the error.
+
+Please note that the use-after-free detection for a sampled allocation is
+transient. We reuse inaccessible slots on-demand as we have a fixed number of
+them, and wish to avoid starving the allocation pool to solely catch
+use-after-frees. We ranomly choose an inaccessible slot to reuse in order to
+provide a chance of detecting long-lived use-after-frees.
+
+Usage
+=====
+
+Currently, the only allocator that supports GWP-ASan is the
+`Scudo Hardened Allocator <https://llvm.org/docs/ScudoHardenedAllocator.html>`_.
+Building compiler-rt will install GWP-ASan into your local version of Scudo, and
+any binary built with Scudo will also have GWP-ASan enabled by default.
+Instructions on using Scudo for your applications can be found on the Scudo wiki
+page.
+
+Options
+-------
+
+GWP-ASan is configured on a per-allocator basis. We provide a default
+implementation of configuration that is used by Scudo. Several aspects of
+GWP-ASan can be configured on a per process basis through the following ways:
+
+- at compile time, by defining ``GWP_ASAN_DEFAULT_OPTIONS`` to the options
+  string you want set by default;
+
+- by defining a ``__gwp_asan_default_options`` function in one's program that
+  returns the options string to be parsed. Said function must have the following
+  prototype: ``extern "C" const char* __gwp_asan_default_options(void)``, with a
+  default visibility. This will override the compile time define;
+
+- through the environment variable ``GWP_ASAN_OPTIONS``, containing the options string
+  to be parsed. Options defined this way will override any definition made
+  through ``__gwp_asan_default_options``.
+
+The options string follows a syntax similar to ASan, where distinct options
+can be assigned in the same string, separated by colons.
+
+For example, using the environment variable:
+
+.. code:: console
+
+  GWP_ASAN_OPTIONS="MaxSimultaneousAllocations=16:SampleRate=5000" ./a.out
+
+Or using the function:
+
+.. code:: cpp
+
+  extern "C" const char *__gwp_asan_default_options() {
+    return "MaxSimultaneousAllocations=16:SampleRate=5000";
+  }
+
+The following options are available:
+
++----------------------------+---------+--------------------------------------------------------------------------------+
+| Option                     | Default | Description                                                                    |
++----------------------------+---------+--------------------------------------------------------------------------------+
+| Enabled                    | true    | Is GWP-ASan enabled?                                                           |
++----------------------------+---------+--------------------------------------------------------------------------------+
+| PerfectlyRightAlign        | false   | When allocations are right-aligned, should we perfectly align them up to the   |
+|                            |         | page boundary? By default (false), we round up allocation size to the nearest  |
+|                            |         | power of two (2, 4, 8, 16) up to a maximum of 16-byte alignment for            |
+|                            |         | performance reasons. Setting this to true can find single byte                 |
+|                            |         | buffer-overflows at the cost of performance, and may be incompatible with      |
+|                            |         | some architectures.                                                            |
++----------------------------+---------+--------------------------------------------------------------------------------+
+| MaxSimultaneousAllocations | 16      | Number of simultaneously-guarded allocations available in the pool.            |
++----------------------------+---------+--------------------------------------------------------------------------------+
+| SampleRate                 | 5000    | The probability (1 / SampleRate) that a page is selected for GWP-ASan          |
+|                            |         | sampling. Sample rates up to 2^63 are supported.                               |
++----------------------------+---------+--------------------------------------------------------------------------------+
+| InstallSignalHandlers      | true    | Install GWP-ASan signal handlers for SIGSEGV. This allows better error reports |
+|                            |         | by providing stack traces for allocation/deallocation when reporting a         |
+|                            |         | memory error. These handlers are generally installed during dynamic loading,   |
+|                            |         | and will ensure that the previously installed signal handlers will be called.  |
+|                            |         | User programs that install SIGSEGV handlers should ensure that any previously  |
+|                            |         | installed signal handlers are also called. Note, if the previously installed   |
+|                            |         | signal handler is SIG_IGN, we terminate the process after dumping the error    |
+|                            |         | report.                                                                        |
++----------------------------+---------+--------------------------------------------------------------------------------+
Index: compiler-rt/test/scudo/lit.cfg
===================================================================
--- compiler-rt/test/scudo/lit.cfg
+++ compiler-rt/test/scudo/lit.cfg
@@ -49,6 +49,13 @@
   # Android defaults to abort_on_error=1, which doesn't work for us.
   default_scudo_opts = 'abort_on_error=0'
 
+# Disable GWP-ASan for scudo internal tests.
+if config.gwp_asan:
+  gwp_asan_options = 'Enabled=0'
+  config.environment['GWP_ASAN_OPTIONS'] = gwp_asan_options
+  config.substitutions.append(('%env_gwp_asan_options=',
+                               'env GWP_ASAN_OPTIONS=' + gwp_asan_options))
+
 if default_scudo_opts:
   config.environment['SCUDO_OPTIONS'] = default_scudo_opts
   default_scudo_opts += ':'
Index: compiler-rt/test/lit.common.configured.in
===================================================================
--- compiler-rt/test/lit.common.configured.in
+++ compiler-rt/test/lit.common.configured.in
@@ -41,6 +41,7 @@
 set_default("android_serial", "@ANDROID_SERIAL_FOR_TESTING@")
 set_default("android_files_to_push", [])
 set_default("have_rpc_xdr_h", @HAVE_RPC_XDR_H@)
+set_default("gwp_asan", @COMPILER_RT_HAS_GWP_ASAN_PYBOOL@)
 config.available_features.add('target-is-%s' % config.target_arch)
 
 if config.enable_per_target_runtime_dir:
Index: compiler-rt/test/lit.common.cfg
===================================================================
--- compiler-rt/test/lit.common.cfg
+++ compiler-rt/test/lit.common.cfg
@@ -239,6 +239,9 @@
 if config.can_symbolize:
   config.available_features.add('can-symbolize')
 
+if config.gwp_asan:
+  config.available_features.add('gwp_asan')
+
 lit.util.usePlatformSdkOnDarwin(config, lit_config)
 
 if config.host_os == 'Darwin':
Index: compiler-rt/test/gwp_asan/use_after_free.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/use_after_free.cpp
@@ -0,0 +1,20 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %expect_crash %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Use after free occurred when accessing memory at:
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr = reinterpret_cast<char *>(malloc(10));
+
+  for (unsigned i = 0; i < 10; ++i) {
+    *(Ptr + i) = 0x0;
+  }
+
+  free(Ptr);
+  volatile char x = *Ptr;
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/use_after_deletea.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/use_after_deletea.cpp
@@ -0,0 +1,20 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %expect_crash %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Use after free occurred when accessing memory at:
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr = new char[10];
+
+  for (unsigned i = 0; i < 10; ++i) {
+    *(Ptr + i) = 0x0;
+  }
+
+  delete[] Ptr;
+  volatile char x = *Ptr;
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/use_after_delete.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/use_after_delete.cpp
@@ -0,0 +1,18 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %expect_crash %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Use after free occurred when accessing memory at:
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr = new char;
+
+  *Ptr = 0x0;
+
+  delete Ptr;
+  volatile char x = *Ptr;
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/unit/lit.site.cfg.in
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/unit/lit.site.cfg.in
@@ -0,0 +1,9 @@
+@LIT_SITE_CFG_IN_HEADER@
+
+config.name = "GwpAsan-Unittest"
+# Load common config for all compiler-rt unit tests.
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured")
+
+config.test_exec_root = os.path.join("@COMPILER_RT_BINARY_DIR@",
+                                     "lib", "gwp_asan", "tests")
+config.test_source_root = config.test_exec_root
Index: compiler-rt/test/gwp_asan/repeated_alloc.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/repeated_alloc.cpp
@@ -0,0 +1,19 @@
+// REQUIRES: gwp_asan
+// This test ensures that normal allocation/memory access/deallocation works
+// as expected.
+
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %run %t
+
+int main() {
+  for (unsigned i = 1; i < 0x100000; i <<= 1) {
+    char *Ptr = new char[i];
+
+    for (unsigned j = 0; j < i; ++j) {
+      *(Ptr + j) = 0x0;
+    }
+
+    delete[] Ptr;
+  }
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/page_size.h
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/page_size.h
@@ -0,0 +1,11 @@
+#ifndef PAGE_SIZE_
+#define PAGE_SIZE_
+
+#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
+# include <unistd.h>
+unsigned pageSize() {
+  return sysconf(_SC_PAGESIZE);
+}
+#endif
+
+#endif // PAGE_SIZE_
Index: compiler-rt/test/gwp_asan/lit.site.cfg.in
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/lit.site.cfg.in
@@ -0,0 +1,11 @@
+@LIT_SITE_CFG_IN_HEADER@
+
+config.name_suffix = "@GWP_ASAN_TEST_CONFIG_SUFFIX@"
+config.target_arch = "@GWP_ASAN_TEST_TARGET_ARCH@"
+config.target_cflags = "@GWP_ASAN_TEST_TARGET_CFLAGS@"
+
+# Load common config for all compiler-rt lit tests.
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
+
+# Load tool-specific config that would do the real work.
+lit_config.load_config(config, "@GWP_ASAN_LIT_SOURCE_DIR@/lit.cfg")
Index: compiler-rt/test/gwp_asan/lit.cfg
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/lit.cfg
@@ -0,0 +1,51 @@
+# -*- Python -*-
+
+import os
+
+# Setup config name.
+config.name = 'GWP-ASan' + config.name_suffix
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+# Test suffixes.
+config.suffixes = ['.c', '.cc', '.cpp', '.test']
+
+# C & CXX flags.
+c_flags = ([config.target_cflags])
+
+# Android doesn't want -lrt.
+if not config.android:
+  c_flags += ["-lrt"]
+
+cxx_flags = (c_flags + config.cxx_mode_flags + ["-std=c++11"])
+
+gwp_asan_flags = ["-fsanitize=scudo"]
+
+def build_invocation(compile_flags):
+  return " " + " ".join([config.clang] + compile_flags) + " "
+
+# Add substitutions.
+config.substitutions.append(("%clang ", build_invocation(c_flags)))
+config.substitutions.append(("%clang_gwp_asan ", build_invocation(c_flags + gwp_asan_flags)))
+config.substitutions.append(("%clangxx_gwp_asan ", build_invocation(cxx_flags + gwp_asan_flags)))
+
+# Platform-specific default GWP_ASAN for lit tests. Ensure that GWP-ASan is
+# enabled and that it samples every allocation.
+default_gwp_asan_options = 'Enabled=1:SampleRate=1'
+
+config.environment['GWP_ASAN_OPTIONS'] = default_gwp_asan_options
+default_gwp_asan_options += ':'
+config.substitutions.append(('%env_gwp_asan_options=',
+                             'env GWP_ASAN_OPTIONS=' + default_gwp_asan_options))
+
+# Add a configuration that defaults to an almost-zero chance of sampling (1 in
+# a quintillion). If you ever spuriously fail a GWP-ASan unit test due to this,
+# buy a lottery ticket.
+no_sample_rate_options = default_gwp_asan_options + 'SampleRate=1000000000000000000:'
+config.substitutions.append(('%env_no_sample_options=',
+                             'env GWP_ASAN_OPTIONS=' + no_sample_rate_options))
+
+# GWP-ASan tests are currently supported on Linux only.
+if config.host_os not in ['Linux']:
+   config.unsupported = True
Index: compiler-rt/test/gwp_asan/invalid_free_right.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/invalid_free_right.cpp
@@ -0,0 +1,16 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Invalid (wild) free occurred when trying to free memory at:
+// CHECK: is located {{[0-9]+}} bytes to the right
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr =
+      reinterpret_cast<char *>(malloc(1));
+  free(Ptr + 1);
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/invalid_free_left.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/invalid_free_left.cpp
@@ -0,0 +1,16 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Invalid (wild) free occurred when trying to free memory at:
+// CHECK: is located 1 bytes to the left of
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr =
+      reinterpret_cast<char *>(malloc(1));
+  free(Ptr - 1);
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/heap_buffer_underflow.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/heap_buffer_underflow.cpp
@@ -0,0 +1,18 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %expect_crash %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Buffer underflow occurred when accessing memory at:
+// CHECK: is located 1 bytes to the left
+
+#include <cstdlib>
+
+#include "page_size.h"
+
+int main() {
+  char *Ptr =
+      reinterpret_cast<char *>(malloc(pageSize()));
+  volatile char x = *(Ptr - 1);
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/heap_buffer_overflow.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/heap_buffer_overflow.cpp
@@ -0,0 +1,18 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %expect_crash %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Buffer overflow occurred when accessing memory at:
+// CHECK: is located {{[0-9]+}} bytes to the right
+
+#include <cstdlib>
+
+#include "page_size.h"
+
+int main() {
+  char *Ptr =
+      reinterpret_cast<char *>(malloc(pageSize()));
+  volatile char x = *(Ptr + pageSize());
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/double_free.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/double_free.cpp
@@ -0,0 +1,15 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Double free occurred when trying to free memory at:
+
+#include <cstdlib>
+
+int main() {
+  void *Ptr = malloc(10);
+  free(Ptr);
+  free(Ptr);
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/double_deletea.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/double_deletea.cpp
@@ -0,0 +1,15 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Double free occurred when trying to free memory at:
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr = new char[50];
+  delete[] Ptr;
+  delete[] Ptr;
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/double_delete.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/double_delete.cpp
@@ -0,0 +1,15 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Double free occurred when trying to free memory at:
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr = new char;
+  delete Ptr;
+  delete Ptr;
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/allocator_fallback.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/allocator_fallback.cpp
@@ -0,0 +1,28 @@
+// REQUIRES: gwp_asan
+// This test ensures that normal allocation/memory access/deallocation works
+// as expected and we didn't accidentally break the supporting allocator.
+
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=1 %run %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=2 %run %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=11 %run %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=12 %run %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=13 %run %t
+
+#include <cstdlib>
+
+int main() {
+  void* Pointers[16];
+  for (unsigned i = 0; i < 16; ++i) {
+    char *Ptr = reinterpret_cast<char*>(malloc(1 << i));
+    Pointers[i] = Ptr;
+    *Ptr = 0;
+    Ptr[(1 << i) - 1] = 0;
+  }
+
+  for (unsigned i = 0; i < 16; ++i) {
+    free(Pointers[i]);
+  }
+
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/allocation_methods.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/allocation_methods.cpp
@@ -0,0 +1,29 @@
+// REQUIRES: gwp_asan
+// Ensure we didn't accidentally break the backing allocator, when GWP-ASan is
+// both enabled and disabled.
+
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %run %t
+// RUN: %env_no_sample_options= %run %t
+
+#include <cstdlib>
+
+int main() {
+  void *Ptr = calloc(1, sizeof(char));
+  free(Ptr);
+
+  Ptr = malloc(1);
+  free(Ptr);
+
+  char *CPtr = new char;
+  delete CPtr;
+
+  char *CPtr2 = new char[2];
+  delete[] CPtr2;
+
+  Ptr = malloc(1);
+  Ptr = realloc(Ptr, 2);
+  free(Ptr);
+
+  return 0;
+}
Index: compiler-rt/test/gwp_asan/CMakeLists.txt
===================================================================
--- /dev/null
+++ compiler-rt/test/gwp_asan/CMakeLists.txt
@@ -0,0 +1,46 @@
+set(GWP_ASAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+set(GWP_ASAN_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
+
+set(GWP_ASAN_TESTSUITES)
+
+set(GWP_ASAN_UNITTEST_DEPS)
+set(GWP_ASAN_TEST_DEPS
+  ${SANITIZER_COMMON_LIT_TEST_DEPS}
+  gwp_asan
+  scudo)
+
+if (COMPILER_RT_INCLUDE_TESTS)
+  list(APPEND GWP_ASAN_TEST_DEPS GwpAsanUnitTests)
+  configure_lit_site_cfg(
+    ${CMAKE_CURRENT_SOURCE_DIR}/unit/lit.site.cfg.in
+    ${CMAKE_CURRENT_BINARY_DIR}/unit/lit.site.cfg)
+  add_lit_testsuite(check-gwp_asan-unit "Running GWP-ASan unit tests"
+    ${CMAKE_CURRENT_BINARY_DIR}/unit
+    DEPENDS ${GWP_ASAN_TEST_DEPS})
+  set_target_properties(check-gwp_asan-unit PROPERTIES FOLDER
+    "Compiler-RT Tests")
+    list(APPEND GWP_ASAN_TEST_DEPS check-gwp_asan-unit)
+endif()
+
+configure_lit_site_cfg(
+  ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+  ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+  )
+
+foreach(arch ${GWP_ASAN_SUPPORTED_ARCH})
+  set(GWP_ASAN_TEST_TARGET_ARCH ${arch})
+  string(TOLOWER "-${arch}" GWP_ASAN_TEST_CONFIG_SUFFIX)
+  get_test_cc_for_arch(${arch} GWP_ASAN_TEST_TARGET_CC GWP_ASAN_TEST_TARGET_CFLAGS)
+  string(TOUPPER ${arch} ARCH_UPPER_CASE)
+  set(CONFIG_NAME ${ARCH_UPPER_CASE}${OS_NAME}Config)
+
+  configure_lit_site_cfg(
+    ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+    ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg)
+  list(APPEND GWP_ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
+endforeach()
+
+add_lit_testsuite(check-gwp_asan "Running the GWP-ASan tests"
+  ${GWP_ASAN_TESTSUITES}
+  DEPENDS ${GWP_ASAN_TEST_DEPS})
+set_target_properties(check-gwp_asan PROPERTIES FOLDER "Compiler-RT Misc")
Index: compiler-rt/lib/scudo/scudo_allocator.cpp
===================================================================
--- compiler-rt/lib/scudo/scudo_allocator.cpp
+++ compiler-rt/lib/scudo/scudo_allocator.cpp
@@ -25,6 +25,11 @@
 #include "sanitizer_common/sanitizer_allocator_interface.h"
 #include "sanitizer_common/sanitizer_quarantine.h"
 
+#ifdef GWP_ASAN_HOOKS
+# include "gwp_asan/guarded_pool_allocator.h"
+# include "gwp_asan/optional/options_parser.h"
+#endif // GWP_ASAN_HOOKS
+
 #include <errno.h>
 #include <string.h>
 
@@ -213,6 +218,10 @@
   return reinterpret_cast<QuarantineCacheT *>(TSD->QuarantineCachePlaceHolder);
 }
 
+#ifdef GWP_ASAN_HOOKS
+static gwp_asan::GuardedPoolAllocator GuardedAlloc;
+#endif // GWP_ASAN_HOOKS
+
 struct Allocator {
   static const uptr MaxAllowedMallocSize =
       FIRST_32_SECOND_64(2UL << 30, 1ULL << 40);
@@ -291,6 +300,14 @@
   void *allocate(uptr Size, uptr Alignment, AllocType Type,
                  bool ForceZeroContents = false) {
     initThreadMaybe();
+
+#ifdef GWP_ASAN_HOOKS
+    if (UNLIKELY(GuardedAlloc.shouldSample())) {
+      if (void *Ptr = GuardedAlloc.allocate(Size))
+        return Ptr;
+    }
+#endif // GWP_ASAN_HOOKS
+
     if (UNLIKELY(Alignment > MaxAlignment)) {
       if (AllocatorMayReturnNull())
         return nullptr;
@@ -434,6 +451,14 @@
       __sanitizer_free_hook(Ptr);
     if (UNLIKELY(!Ptr))
       return;
+
+#ifdef GWP_ASAN_HOOKS
+    if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr))) {
+      GuardedAlloc.deallocate(Ptr);
+      return;
+    }
+#endif // GWP_ASAN_HOOKS
+
     if (UNLIKELY(!Chunk::isAligned(Ptr)))
       dieWithMessage("misaligned pointer when deallocating address %p\n", Ptr);
     UnpackedHeader Header;
@@ -463,6 +488,18 @@
   // size still fits in the chunk.
   void *reallocate(void *OldPtr, uptr NewSize) {
     initThreadMaybe();
+
+#ifdef GWP_ASAN_HOOKS
+    if (UNLIKELY(GuardedAlloc.pointerIsMine(OldPtr))) {
+      size_t OldSize = GuardedAlloc.getSize(OldPtr);
+      void *NewPtr = allocate(NewSize, MinAlignment, FromMalloc);
+      if (NewPtr)
+        memcpy(NewPtr, OldPtr, (NewSize < OldSize) ? NewSize : OldSize);
+      GuardedAlloc.deallocate(OldPtr);
+      return NewPtr;
+    }
+#endif // GWP_ASAN_HOOKS
+
     if (UNLIKELY(!Chunk::isAligned(OldPtr)))
       dieWithMessage("misaligned address when reallocating address %p\n",
                      OldPtr);
@@ -504,6 +541,12 @@
     initThreadMaybe();
     if (UNLIKELY(!Ptr))
       return 0;
+
+#ifdef GWP_ASAN_HOOKS
+    if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr)))
+      return GuardedAlloc.getSize(Ptr);
+#endif // GWP_ASAN_HOOKS
+
     UnpackedHeader Header;
     Chunk::loadHeader(Ptr, &Header);
     // Getting the usable size of a chunk only makes sense if it's allocated.
@@ -626,6 +669,10 @@
 
 void initScudo() {
   Instance.init();
+#ifdef GWP_ASAN_HOOKS
+  gwp_asan::options::initOptions();
+  GuardedAlloc.init(*gwp_asan::options::getOptions());
+#endif // GWP_ASAN_HOOKS
 }
 
 void ScudoTSD::init() {
Index: compiler-rt/lib/scudo/CMakeLists.txt
===================================================================
--- compiler-rt/lib/scudo/CMakeLists.txt
+++ compiler-rt/lib/scudo/CMakeLists.txt
@@ -30,6 +30,15 @@
   RTSanitizerCommonNoTermination
   RTSanitizerCommonLibc
   RTInterception)
+
+if (COMPILER_RT_HAS_GWP_ASAN)
+  # Currently, Scudo uses the GwpAsan flag parser. This backs onto the flag
+  # parsing mechanism of sanitizer_common. Once Scudo has its own flag parsing,
+  # and parses GwpAsan options, you can remove this dependency.
+  list(APPEND SCUDO_MINIMAL_OBJECT_LIBS RTGwpAsan RTGwpAsanOptionsParser)
+  list(APPEND SCUDO_CFLAGS -DGWP_ASAN_HOOKS)
+endif()
+
 set(SCUDO_OBJECT_LIBS ${SCUDO_MINIMAL_OBJECT_LIBS})
 set(SCUDO_DYNAMIC_LIBS ${SCUDO_MINIMAL_DYNAMIC_LIBS})
 
Index: compiler-rt/lib/gwp_asan/tests/thread_contention.cpp
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/tests/thread_contention.cpp
@@ -0,0 +1,82 @@
+//===-- thread_contention.cc ------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/tests/harness.h"
+
+// Note: Compilation of <atomic> and <thread> are extremely expensive for
+// non-opt builds of clang.
+#include <atomic>
+#include <cstdlib>
+#include <thread>
+#include <vector>
+
+void asyncTask(gwp_asan::GuardedPoolAllocator *GPA,
+               std::atomic<bool> *StartingGun, unsigned NumIterations) {
+  while (!StartingGun->load()) {
+  }
+
+  // Get ourselves a new allocation.
+  for (unsigned i = 0; i < NumIterations; ++i) {
+    volatile char *Ptr = reinterpret_cast<volatile char *>(
+        GPA->allocate(GPA->maximumAllocationSize()));
+    // Do any other threads have access to this page?
+    EXPECT_EQ(*Ptr, 0);
+
+    // Mark the page as from malloc. Wait to see if another thread also takes
+    // this page.
+    *Ptr = 'A';
+    std::this_thread::sleep_for(std::chrono::nanoseconds(10000));
+
+    // Check we still own the page.
+    EXPECT_EQ(*Ptr, 'A');
+
+    // And now release it.
+    *Ptr = 0;
+    GPA->deallocate(const_cast<char *>(Ptr));
+  }
+}
+
+void runThreadContentionTest(unsigned NumThreads, unsigned NumIterations,
+                             gwp_asan::GuardedPoolAllocator *GPA) {
+
+  std::atomic<bool> StartingGun{false};
+  std::vector<std::thread> Threads;
+  if (std::thread::hardware_concurrency() < NumThreads) {
+    NumThreads = std::thread::hardware_concurrency();
+  }
+
+  for (unsigned i = 0; i < NumThreads; ++i) {
+    Threads.emplace_back(asyncTask, GPA, &StartingGun, NumIterations);
+  }
+
+  StartingGun.store(true);
+
+  for (auto &T : Threads)
+    T.join();
+}
+
+TEST_F(CustomGuardedPoolAllocator, ThreadContention) {
+  unsigned NumThreads = 4;
+  unsigned NumIterations = 10000;
+  InitNumSlots(NumThreads);
+  runThreadContentionTest(NumThreads, NumIterations, &GPA);
+}
+
+TEST_F(CustomGuardedPoolAllocator,
+       ThreadContentionWithPreviouslyExhaustedPool) {
+  unsigned NumThreads = 4;
+  unsigned NumIterations = 10000;
+  InitNumSlots(NumThreads);
+
+  for (unsigned i = 0; i < NumThreads; ++i) {
+    void *Ptr = GPA.allocate(1);
+    GPA.deallocate(Ptr);
+  }
+
+  runThreadContentionTest(NumThreads, NumIterations, &GPA);
+}
Index: compiler-rt/lib/gwp_asan/tests/slot_reuse.cpp
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/tests/slot_reuse.cpp
@@ -0,0 +1,72 @@
+//===-- slot_reuse.cc -------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/tests/harness.h"
+
+void singleByteGoodAllocDealloc(gwp_asan::GuardedPoolAllocator *GPA) {
+  void *Ptr = GPA->allocate(1);
+  EXPECT_NE(nullptr, Ptr);
+  EXPECT_TRUE(GPA->pointerIsMine(Ptr));
+  EXPECT_EQ(1u, GPA->getSize(Ptr));
+  GPA->deallocate(Ptr);
+}
+
+TEST_F(CustomGuardedPoolAllocator, EnsureReuseOfQuarantine1) {
+  InitNumSlots(1);
+  for (unsigned i = 0; i < 128; ++i)
+    singleByteGoodAllocDealloc(&GPA);
+}
+
+TEST_F(CustomGuardedPoolAllocator, EnsureReuseOfQuarantine2) {
+  InitNumSlots(2);
+  for (unsigned i = 0; i < 128; ++i)
+    singleByteGoodAllocDealloc(&GPA);
+}
+
+TEST_F(CustomGuardedPoolAllocator, EnsureReuseOfQuarantine127) {
+  InitNumSlots(127);
+  for (unsigned i = 0; i < 128; ++i)
+    singleByteGoodAllocDealloc(&GPA);
+}
+
+// This test ensures that our slots are not reused ahead of time. We increase
+// the use-after-free detection by not reusing slots until all of them have been
+// allocated. This is done by always using the slots from left-to-right in the
+// pool before we used each slot once, at which point random selection takes
+// over.
+void runNoReuseBeforeNecessary(gwp_asan::GuardedPoolAllocator *GPA,
+                               unsigned PoolSize) {
+  std::set<void *> Ptrs;
+  for (unsigned i = 0; i < PoolSize; ++i) {
+    void *Ptr = GPA->allocate(1);
+
+    EXPECT_TRUE(GPA->pointerIsMine(Ptr));
+    EXPECT_EQ(0u, Ptrs.count(Ptr));
+
+    Ptrs.insert(Ptr);
+    GPA->deallocate(Ptr);
+  }
+}
+
+TEST_F(CustomGuardedPoolAllocator, NoReuseBeforeNecessary2) {
+  constexpr unsigned kPoolSize = 2;
+  InitNumSlots(kPoolSize);
+  runNoReuseBeforeNecessary(&GPA, kPoolSize);
+}
+
+TEST_F(CustomGuardedPoolAllocator, NoReuseBeforeNecessary128) {
+  constexpr unsigned kPoolSize = 128;
+  InitNumSlots(kPoolSize);
+  runNoReuseBeforeNecessary(&GPA, kPoolSize);
+}
+
+TEST_F(CustomGuardedPoolAllocator, NoReuseBeforeNecessary129) {
+  constexpr unsigned kPoolSize = 129;
+  InitNumSlots(kPoolSize);
+  runNoReuseBeforeNecessary(&GPA, kPoolSize);
+}
Index: compiler-rt/lib/gwp_asan/tests/harness.h
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/tests/harness.h
@@ -0,0 +1,61 @@
+//===-- harness.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GWP_ASAN_TESTS_HARNESS_H_
+#define GWP_ASAN_TESTS_HARNESS_H_
+
+#include "gtest/gtest.h"
+
+// Include sanitizer_common first as gwp_asan/guarded_pool_allocator.h
+// transiently includes definitions.h, which overwrites some of the definitions
+// in sanitizer_common.
+#include "sanitizer_common/sanitizer_common.h"
+
+#include "gwp_asan/guarded_pool_allocator.h"
+#include "gwp_asan/options.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+class DefaultGuardedPoolAllocator : public ::testing::Test {
+public:
+  DefaultGuardedPoolAllocator() {
+    gwp_asan::options::Options Opts;
+    Opts.setDefaults();
+    MaxSimultaneousAllocations = Opts.MaxSimultaneousAllocations;
+
+    Opts.Printf = __sanitizer::Printf;
+    GPA.init(Opts);
+  }
+
+protected:
+  gwp_asan::GuardedPoolAllocator GPA;
+  decltype(gwp_asan::options::Options::MaxSimultaneousAllocations)
+      MaxSimultaneousAllocations;
+};
+
+class CustomGuardedPoolAllocator : public ::testing::Test {
+public:
+  void
+  InitNumSlots(decltype(gwp_asan::options::Options::MaxSimultaneousAllocations)
+                   MaxSimultaneousAllocationsArg) {
+    gwp_asan::options::Options Opts;
+    Opts.setDefaults();
+
+    Opts.MaxSimultaneousAllocations = MaxSimultaneousAllocationsArg;
+    MaxSimultaneousAllocations = MaxSimultaneousAllocationsArg;
+
+    Opts.Printf = __sanitizer::Printf;
+    GPA.init(Opts);
+  }
+
+protected:
+  gwp_asan::GuardedPoolAllocator GPA;
+  decltype(gwp_asan::options::Options::MaxSimultaneousAllocations)
+      MaxSimultaneousAllocations;
+};
+
+#endif // GWP_ASAN_TESTS_HARNESS_H_
Index: compiler-rt/lib/gwp_asan/tests/driver.cpp
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/tests/driver.cpp
@@ -0,0 +1,14 @@
+//===-- driver.cc -----------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+
+int main(int argc, char **argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
Index: compiler-rt/lib/gwp_asan/tests/basic.cpp
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/tests/basic.cpp
@@ -0,0 +1,73 @@
+//===-- basic.cc ------------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/tests/harness.h"
+
+TEST_F(CustomGuardedPoolAllocator, BasicAllocation) {
+  InitNumSlots(1);
+  void *Ptr = GPA.allocate(1);
+  EXPECT_NE(nullptr, Ptr);
+  EXPECT_TRUE(GPA.pointerIsMine(Ptr));
+  EXPECT_EQ(1u, GPA.getSize(Ptr));
+  GPA.deallocate(Ptr);
+}
+
+TEST_F(DefaultGuardedPoolAllocator, NullptrIsNotMine) {
+  EXPECT_FALSE(GPA.pointerIsMine(nullptr));
+}
+
+TEST_F(CustomGuardedPoolAllocator, SizedAllocations) {
+  InitNumSlots(1);
+
+  std::size_t MaxAllocSize = GPA.maximumAllocationSize();
+  EXPECT_TRUE(MaxAllocSize > 0);
+
+  for (unsigned AllocSize = 1; AllocSize <= MaxAllocSize; AllocSize <<= 1) {
+    void *Ptr = GPA.allocate(AllocSize);
+    EXPECT_NE(nullptr, Ptr);
+    EXPECT_TRUE(GPA.pointerIsMine(Ptr));
+    EXPECT_EQ(AllocSize, GPA.getSize(Ptr));
+    GPA.deallocate(Ptr);
+  }
+}
+
+TEST_F(DefaultGuardedPoolAllocator, TooLargeAllocation) {
+  EXPECT_EQ(nullptr, GPA.allocate(GPA.maximumAllocationSize() + 1));
+}
+
+TEST_F(CustomGuardedPoolAllocator, AllocAllSlots) {
+  constexpr unsigned kNumSlots = 128;
+  InitNumSlots(kNumSlots);
+  void *Ptrs[kNumSlots];
+  for (unsigned i = 0; i < kNumSlots; ++i) {
+    Ptrs[i] = GPA.allocate(1);
+    EXPECT_NE(nullptr, Ptrs[i]);
+    EXPECT_TRUE(GPA.pointerIsMine(Ptrs[i]));
+  }
+  for (unsigned i = 0; i < kNumSlots; ++i)
+    GPA.deallocate(Ptrs[i]);
+}
+
+TEST_F(CustomGuardedPoolAllocator, AllocTooManySlots) {
+  constexpr unsigned kNumSlots = 128;
+  InitNumSlots(kNumSlots);
+  void *Ptrs[kNumSlots];
+  for (unsigned i = 0; i < kNumSlots; ++i) {
+    Ptrs[i] = GPA.allocate(1);
+    EXPECT_NE(nullptr, Ptrs[i]);
+    EXPECT_TRUE(GPA.pointerIsMine(Ptrs[i]));
+  }
+
+  // This allocation should fail as all the slots are used.
+  void *Ptr = GPA.allocate(1);
+  EXPECT_EQ(nullptr, Ptr);
+  EXPECT_FALSE(GPA.pointerIsMine(nullptr));
+
+  for (unsigned i = 0; i < kNumSlots; ++i)
+    GPA.deallocate(Ptrs[i]);
+}
Index: compiler-rt/lib/gwp_asan/tests/alignment.cpp
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/tests/alignment.cpp
@@ -0,0 +1,27 @@
+//===-- alignment.cc --------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/tests/harness.h"
+
+TEST_F(DefaultGuardedPoolAllocator, BasicAllocation) {
+  std::vector<std::pair<int, int>> AllocSizeToAlignment = {
+      {1, 1},   {2, 2},   {3, 4},       {4, 4},       {5, 8},   {7, 8},
+      {8, 8},   {9, 16},  {15, 16},     {16, 16},     {17, 16}, {31, 16},
+      {32, 16}, {33, 16}, {4095, 4096}, {4096, 4096},
+  };
+
+  for (const auto &KV : AllocSizeToAlignment) {
+    void *Ptr = GPA.allocate(KV.first);
+    EXPECT_NE(nullptr, Ptr);
+
+    // Check the alignment of the pointer is as expected.
+    EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(Ptr) % KV.second);
+
+    GPA.deallocate(Ptr);
+  }
+}
Index: compiler-rt/lib/gwp_asan/tests/CMakeLists.txt
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/tests/CMakeLists.txt
@@ -0,0 +1,53 @@
+include(CompilerRTCompile)
+
+set(GWP_ASAN_UNITTEST_CFLAGS
+  ${COMPILER_RT_UNITTEST_CFLAGS}
+  ${COMPILER_RT_GTEST_CFLAGS}
+  -I${COMPILER_RT_SOURCE_DIR}/lib/
+  -O2)
+
+file(GLOB GWP_ASAN_HEADERS ../*.h)
+file(GLOB GWP_ASAN_UNITTESTS *.cpp)
+set(GWP_ASAN_UNIT_TEST_HEADERS
+  ${GWP_ASAN_HEADERS}
+  harness.h)
+
+add_custom_target(GwpAsanUnitTests)
+set_target_properties(GwpAsanUnitTests PROPERTIES FOLDER "Compiler-RT Tests")
+
+set(GWP_ASAN_UNITTEST_LINK_FLAGS ${COMPILER_RT_UNITTEST_LINK_FLAGS})
+list(APPEND GWP_ASAN_UNITTEST_LINK_FLAGS --driver-mode=g++)
+if(NOT WIN32)
+  list(APPEND GWP_ASAN_UNITTEST_LINK_FLAGS -lpthread)
+endif()
+
+if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST GWP_ASAN_SUPPORTED_ARCH)
+  # GWP-ASan unit tests are only run on the host machine.
+  set(arch ${COMPILER_RT_DEFAULT_TARGET_ARCH})
+
+  set(GWP_ASAN_TEST_RUNTIME RTGwpAsanTest.${arch})
+
+  # RTSanitizerCommonNoTermination(NoLibc) required for __sanitizer::Printf.
+  set(GWP_ASAN_TEST_RUNTIME_OBJECTS
+    $<TARGET_OBJECTS:RTGwpAsan.${arch}>
+    $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+    $<TARGET_OBJECTS:RTSanitizerCommonNoLibc.${arch}>)
+
+  add_library(${GWP_ASAN_TEST_RUNTIME} STATIC
+    ${GWP_ASAN_TEST_RUNTIME_OBJECTS})
+
+  set_target_properties(${GWP_ASAN_TEST_RUNTIME} PROPERTIES
+    ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+    FOLDER "Compiler-RT Runtime tests")
+
+  set(GwpAsanTestObjects)
+  generate_compiler_rt_tests(GwpAsanTestObjects
+    GwpAsanUnitTests "GwpAsan-${arch}-Test" ${arch}
+    SOURCES ${GWP_ASAN_UNITTESTS} ${COMPILER_RT_GTEST_SOURCE}
+    RUNTIME ${GWP_ASAN_TEST_RUNTIME}
+    DEPS gtest ${GWP_ASAN_UNIT_TEST_HEADERS}
+    CFLAGS ${GWP_ASAN_UNITTEST_CFLAGS}
+    LINK_FLAGS ${GWP_ASAN_UNITTEST_LINK_FLAGS})
+  set_target_properties(GwpAsanUnitTests PROPERTIES
+    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+endif()
Index: compiler-rt/lib/gwp_asan/random.h
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/random.h
@@ -0,0 +1,28 @@
+//===-- random.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GWP_ASAN_RANDOM_H_
+#define GWP_ASAN_RANDOM_H_
+
+#include <cstdint>
+
+namespace gwp_asan {
+namespace random {
+// TODO(hctim): This may have significant overhead for platforms where
+// 64-bit arithmetic is emulated. Do we need less than a 2^32 chance of
+// sampling?
+// xorshift128+ (64-bit output). Avoids multiplication.
+uint64_t getRandomUnsigned64();
+
+// xorshift* (64-bit output). This is primarily used to seed the xorshift128+
+// generator.
+uint64_t xorShiftStar64();
+} // namespace random
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_RANDOM_H_
Index: compiler-rt/lib/gwp_asan/random.cpp
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/random.cpp
@@ -0,0 +1,43 @@
+//===-- random.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/random.h"
+
+#include <ctime>
+
+namespace gwp_asan {
+namespace random {
+uint64_t getRandomUnsigned64() {
+  static thread_local uint64_t RandomStateA =
+      static_cast<uint64_t>(time(nullptr));
+  static thread_local uint64_t RandomStateB = xorShiftStar64();
+
+  uint64_t A = RandomStateA;
+  const uint64_t B = RandomStateB;
+  RandomStateA = B;
+
+  A ^= A << 23;
+  A ^= A >> 17;
+  A ^= B ^ (B >> 26);
+
+  RandomStateB = A;
+  return A + B;
+}
+
+uint64_t xorShiftStar64() {
+  static thread_local uint64_t RandomState =
+      static_cast<uint64_t>(time(nullptr));
+  uint64_t A = RandomState;
+  A ^= A >> 12;
+  A ^= A << 25;
+  A ^= A >> 27;
+  RandomState = A;
+  return A * 0x2545F4914F6CDD1D;
+}
+} // namespace random
+} // namespace gwp_asan
Index: compiler-rt/lib/gwp_asan/options.inc
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/options.inc
@@ -0,0 +1,50 @@
+//===-- options.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
+//
+//===----------------------------------------------------------------------===//
+
+// Please ensure that this file is kept up to date with llvm/docs/GwpAsan.rst.
+
+#ifndef GWP_ASAN_OPTION
+#error "Define GWP_ASAN_OPTION prior to including this file!"
+#endif
+
+GWP_ASAN_OPTION(bool, Enabled, true, "Is GWP-ASan enabled? Defaults to true.")
+
+GWP_ASAN_OPTION(
+    bool, PerfectlyRightAlign, false,
+    "When allocations are right-aligned, should we perfectly align them up to "
+    "the page boundary? By default (false), we round up allocation size to the "
+    "nearest power of two (2, 4, 8, 16) up to a maximum of 16-byte alignment "
+    "for performance reasons. Setting this to true can find single byte "
+    "buffer-overflows at the cost of performance, and may be incompatible with "
+    "some architectures.")
+
+GWP_ASAN_OPTION(
+    int, MaxSimultaneousAllocations, 16,
+    "Number of usable guarded slots in the allocation pool. Defaults to 16.")
+
+// Note that this is intentionally a signed long long due to sanitizer_common
+// implementation details. The __sanitizer::FlagHandler<>::Parse() calls
+// __sanitizer::internal_simple_strtoll() to do the string->integer conversion,
+// and supports a signed, 64-bit number as its maximum.
+GWP_ASAN_OPTION(
+    long long, SampleRate, 5000,
+    "The probability (1 / SampleRate) that an allocation is selected for "
+    "GWP-ASan sampling. Default is 5000. Sample rates up to 2^63 are "
+    "supported.")
+
+GWP_ASAN_OPTION(
+    bool, InstallSignalHandlers, true,
+    "Install GWP-ASan signal handlers for SIGSEGV. This allows "
+    "better error reports by providing stack traces for "
+    "allocation/deallocation when reporting a memory error. These handlers "
+    "are generally installed during dynamic loading, and will ensure that the "
+    "previously installed signal handlers will be called. User programs that "
+    "install SIGSEGV handlers should ensure that any previously installed "
+    "signal handlers are also called. Note, if the previously installed signal "
+    "handler is SIG_IGN, we terminate the process after dumping the error "
+    "report.")
Index: compiler-rt/lib/gwp_asan/options.h
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/options.h
@@ -0,0 +1,41 @@
+//===-- options.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GWP_ASAN_OPTIONS_H_
+#define GWP_ASAN_OPTIONS_H_
+
+namespace gwp_asan {
+namespace options {
+// The function pointer type for printf(). Follows the standard format from the
+// sanitizers library. If the supported allocator exposes printing via a
+// different function signature, please provide a wrapper which has this
+// printf() signature, and pass the wrapper instead.
+typedef void (*Printf_t)(const char *Format, ...);
+
+struct Options {
+  Printf_t Printf = nullptr;
+
+  // Read the options from the included definitions file.
+#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description)                 \
+  Type Name = DefaultValue;
+#include "gwp_asan/options.inc"
+#undef GWP_ASAN_OPTION
+
+  void setDefaults() {
+#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description)                 \
+  Name = DefaultValue;
+#include "gwp_asan/options.inc"
+#undef GWP_ASAN_OPTION
+
+    Printf = nullptr;
+  }
+};
+} // namespace options
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_OPTIONS_H_
Index: compiler-rt/lib/gwp_asan/optional/options_parser.h
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/optional/options_parser.h
@@ -0,0 +1,32 @@
+//===-- options_parser.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_
+#define GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_
+
+#include "gwp_asan/options.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+namespace gwp_asan {
+namespace options {
+
+// Parse the options from the GWP_ASAN_FLAGS environment variable.
+void initOptions();
+// Returns a pointer to the initialised options. Call initOptions() prior to
+// calling this function.
+Options *getOptions();
+
+} // namespace options
+} // namespace gwp_asan
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char *
+__gwp_asan_default_options();
+}
+
+#endif // GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_
Index: compiler-rt/lib/gwp_asan/optional/options_parser.cpp
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/optional/options_parser.cpp
@@ -0,0 +1,86 @@
+//===-- options_parser.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/optional/options_parser.h"
+
+#include <cstdarg>
+#include <cstdlib>
+#include <cstring>
+
+#include "gwp_asan/options.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_flags.h"
+
+namespace gwp_asan {
+namespace options {
+void registerGwpAsanFlags(__sanitizer::FlagParser *parser, Options *o) {
+#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description)                 \
+  RegisterFlag(parser, #Name, Description, &o->Name);
+#include "gwp_asan/options.inc"
+#undef GWP_ASAN_OPTION
+}
+
+const char *getCompileDefinitionGwpAsanDefaultOptions() {
+#ifdef GWP_ASAN_DEFAULT_OPTIONS
+  return SANITIZER_STRINGIFY(GWP_ASAN_DEFAULT_OPTIONS);
+#else
+  return "";
+#endif
+}
+
+const char *getGwpAsanDefaultOptions() {
+  return (__gwp_asan_default_options) ? __gwp_asan_default_options() : "";
+}
+
+void initOptions() {
+  Options *o = getOptions();
+  o->setDefaults();
+
+  __sanitizer::FlagParser Parser;
+  registerGwpAsanFlags(&Parser, o);
+
+  // Override from compile definition.
+  Parser.ParseString(getCompileDefinitionGwpAsanDefaultOptions());
+
+  // Override from user-specified string.
+  Parser.ParseString(getGwpAsanDefaultOptions());
+
+  // Override from environment.
+  Parser.ParseString(__sanitizer::GetEnv("GWP_ASAN_OPTIONS"));
+
+  __sanitizer::InitializeCommonFlags();
+
+  // Sanity checks and default settings for the parameters.
+  if (o->Enabled && o->MaxSimultaneousAllocations <= 0) {
+    __sanitizer::Printf(
+        "GWP-ASan ERROR: MaxSimultaneousAllocations must be >= 0 when "
+        "GWP-ASan is enabled.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  if (o->Enabled && o->SampleRate < 1) {
+    __sanitizer::Printf("GWP-ASan ERROR: SampleRate must be >= 1 when "
+                        "GWP-ASan is enabled.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  o->Printf = __sanitizer::Printf;
+}
+
+Options *getOptions() {
+  static Options GwpAsanFlags;
+  return &GwpAsanFlags;
+}
+
+} // namespace options
+} // namespace gwp_asan
+
+SANITIZER_INTERFACE_WEAK_DEF(const char *, __gwp_asan_default_options, void) {
+  return "";
+}
Index: compiler-rt/lib/gwp_asan/mutex.h
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/mutex.h
@@ -0,0 +1,94 @@
+//===-- mutex.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
+//
+//===----------------------------------------------------------------------===//
+
+// TODO(hctim): Move this implementation and Scudo's implementation into a
+// unified base implementation. We shouldn't need to have separate
+// implementations for this.
+
+#ifndef GWP_ASAN_MUTEX_H_
+#define GWP_ASAN_MUTEX_H_
+
+#include "gwp_asan/definitions.h"
+
+#include <cstdint>
+
+#ifdef __unix__
+#include <sched.h>
+#endif // defined(__unix__)
+
+namespace gwp_asan {
+class Mutex {
+public:
+  void lock();
+  bool tryLock();
+  void unlock();
+
+private:
+  void yieldProcessor(uint8_t Count);
+  void yieldPlatform();
+
+  void lockSlow();
+
+  bool Locked = false;
+};
+
+class ScopedLock {
+public:
+  explicit ScopedLock(Mutex *Mx) : Mu(Mx) { Mu->lock(); }
+  ~ScopedLock() { Mu->unlock(); }
+
+private:
+  Mutex *Mu;
+};
+
+ALWAYS_INLINE void Mutex::lock() {
+  if (tryLock())
+    return;
+  lockSlow();
+}
+
+ALWAYS_INLINE bool Mutex::tryLock() {
+  return !__atomic_exchange_n(&Locked, true, __ATOMIC_ACQUIRE);
+}
+
+ALWAYS_INLINE void Mutex::unlock() {
+  __atomic_store_n(&Locked, false, __ATOMIC_RELEASE);
+}
+
+ALWAYS_INLINE void Mutex::yieldProcessor(uint8_t Count) {
+#if defined(__i386__) || defined(__x86_64__)
+  asm volatile("" ::: "memory");
+  for (uint8_t i = 0; i < Count; ++i)
+    asm volatile("pause");
+#elif defined(__aarch64__) || defined(__arm__)
+  asm volatile("" ::: "memory");
+  for (uint8_t i = 0; I < Count; ++i)
+    asm volatile("yield");
+#endif
+  asm volatile("" ::: "memory");
+}
+
+ALWAYS_INLINE void Mutex::lockSlow() {
+  for (uint32_t i = 0;; ++i) {
+    if (i < 10)
+      yieldProcessor(10);
+    else
+      yieldPlatform();
+
+    if (!__atomic_load_n(&Locked, __ATOMIC_RELAXED) && tryLock())
+      return;
+  }
+}
+
+#ifdef __unix__
+ALWAYS_INLINE void Mutex::yieldPlatform() { sched_yield(); }
+#endif // defined(__unix__)
+
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_MUTEX_H_
Index: compiler-rt/lib/gwp_asan/guarded_pool_allocator_posix.cpp
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/guarded_pool_allocator_posix.cpp
@@ -0,0 +1,96 @@
+//===-- guarded_pool_allocator_posix.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/guarded_pool_allocator.h"
+
+#include <cstdlib>
+#include <errno.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace gwp_asan {
+
+void *GuardedPoolAllocator::mapMemory(size_t Size) const {
+  void *Ptr =
+      mmap(nullptr, Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+
+  if (Ptr == MAP_FAILED) {
+    Printf("Failed to map guarded pool allocator memory, errno: %d\n", errno);
+    Printf("  mmap(nullptr, %zu, ...) failed.\n", Size);
+    exit(EXIT_FAILURE);
+  }
+  return Ptr;
+}
+
+void GuardedPoolAllocator::markReadWrite(void *Ptr, size_t Size) const {
+  if (mprotect(Ptr, Size, PROT_READ | PROT_WRITE) != 0) {
+    Printf("Failed to set guarded pool allocator memory at as RW, errno: %d\n",
+           errno);
+    Printf("  mprotect(%p, %zu, RW) failed.\n", Ptr, Size);
+    exit(EXIT_FAILURE);
+  }
+}
+
+void GuardedPoolAllocator::markInaccessible(void *Ptr, size_t Size) const {
+  // mmap() a PROT_NONE page over the address to release it to the system, if
+  // we used mprotect() here the system would count pages in the quarantine
+  // against the RSS.
+  if (mmap(Ptr, Size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1,
+           0) == MAP_FAILED) {
+    Printf("Failed to set guarded pool allocator memory as inaccessible, "
+           "errno: %d\n",
+           errno);
+    Printf("  mmap(%p, %zu, NONE, ...) failed.\n", Ptr, Size);
+    exit(EXIT_FAILURE);
+  }
+}
+
+std::size_t GuardedPoolAllocator::getPlatformPageSize() {
+  return sysconf(_SC_PAGESIZE);
+}
+
+struct sigaction PreviousHandler;
+
+static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
+  gwp_asan::GuardedPoolAllocator::reportError(
+      reinterpret_cast<uintptr_t>(info->si_addr));
+
+  // Process any previous handlers.
+  if (PreviousHandler.sa_flags & SA_SIGINFO) {
+    PreviousHandler.sa_sigaction(sig, info, ucontext);
+  } else if (PreviousHandler.sa_handler == SIG_IGN ||
+             PreviousHandler.sa_handler == SIG_DFL) {
+    // If the previous handler was the default handler, or was ignoring this
+    // signal, install the default handler and re-raise the signal in order to
+    // get a core dump and terminate this process.
+    signal(SIGSEGV, SIG_DFL);
+    raise(SIGSEGV);
+  } else {
+    PreviousHandler.sa_handler(sig);
+  }
+}
+
+void GuardedPoolAllocator::installSignalHandlers() {
+  struct sigaction Action;
+  Action.sa_sigaction = sigSegvHandler;
+  Action.sa_flags = SA_SIGINFO;
+  sigaction(SIGSEGV, &Action, &PreviousHandler);
+}
+
+uint64_t GuardedPoolAllocator::getThreadID() {
+#ifdef SYS_gettid
+  return syscall(SYS_gettid);
+#else
+  return kInvalidThreadID;
+#endif
+}
+
+} // namespace gwp_asan
Index: compiler-rt/lib/gwp_asan/guarded_pool_allocator.h
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/guarded_pool_allocator.h
@@ -0,0 +1,262 @@
+//===-- guarded_pool_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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_
+#define GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_
+
+#include "gwp_asan/definitions.h"
+#include "gwp_asan/mutex.h"
+#include "gwp_asan/options.h"
+#include "gwp_asan/random.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+
+namespace gwp_asan {
+// This class is the primary implementation of the allocator portion of GWP-
+// ASan. It is the sole owner of the pool of sequentially allocated guarded
+// slots. It should always be treated as a singleton.
+
+// Functions in the public interface of this class are thread-compatible until
+// init() is called, at which point they become thread-safe (unless specified
+// otherwise).
+class GuardedPoolAllocator {
+public:
+  static constexpr uint64_t kInvalidThreadID = UINT64_MAX;
+
+  enum class Error {
+    UNKNOWN,
+    USE_AFTER_FREE,
+    DOUBLE_FREE,
+    INVALID_FREE,
+    BUFFER_OVERFLOW,
+    BUFFER_UNDERFLOW
+  };
+
+  struct AllocationMetadata {
+    // Maximum number of stack trace frames to collect for allocations + frees.
+    // TODO(hctim): Implement stack frame compression, a-la Chromium.
+    // Currently the maximum stack frames is one, as we don't collect traces.
+    static constexpr std::size_t kMaximumStackFrames = 1;
+
+    // Records the given allocation metadata into this struct. In the future,
+    // this will collect the allocation trace as well.
+    void RecordAllocation(uintptr_t Addr, std::size_t Size);
+
+    // Record that this allocation is now deallocated. In future, this will
+    // collect the deallocation trace as well.
+    void RecordDeallocation();
+
+    struct CallSiteInfo {
+      // The backtrace to the allocation/deallocation. If the first value is
+      // zero, we did not collect a trace.
+      uintptr_t Trace[kMaximumStackFrames] = {};
+      // The thread ID for this trace, or kInvalidThreadID if not available.
+      uint64_t ThreadID = kInvalidThreadID;
+    };
+
+    // The address of this allocation.
+    uintptr_t Addr = 0;
+    // Represents the actual size of the allocation.
+    std::size_t Size = 0;
+
+    CallSiteInfo AllocationTrace;
+    CallSiteInfo DeallocationTrace;
+
+    // Whether this allocation has been deallocated yet.
+    bool IsDeallocated = false;
+  };
+
+  // During program startup, we must ensure that memory allocations do not land
+  // in this allocation pool if the allocator decides to runtime-disable
+  // GWP-ASan. The constructor value-initialises the class such that if no
+  // further initialisation takes place, calls to shouldSample() and
+  // pointerIsMine() will return false.
+  constexpr GuardedPoolAllocator(){};
+  GuardedPoolAllocator(const GuardedPoolAllocator &) = delete;
+  GuardedPoolAllocator &operator=(const GuardedPoolAllocator &) = delete;
+
+  // Note: This class is expected to be a singleton for the lifetime of the
+  // program. If this object is initialised, it will leak the guarded page pool
+  // and metadata allocations during destruction. We can't clean up these areas
+  // as this may cause a use-after-free on shutdown.
+  ~GuardedPoolAllocator() = default;
+
+  // Initialise the rest of the members of this class. Create the allocation
+  // pool using the provided options. See options.inc for runtime configuration
+  // options.
+  void init(const options::Options &Opts);
+
+  // Return whether the allocation should be randomly chosen for sampling.
+  ALWAYS_INLINE bool shouldSample() {
+    // NextSampleCounter == 0 means we "should regenerate the counter".
+    //                   == 1 means we "should sample this allocation".
+    if (UNLIKELY(NextSampleCounter == 0)) {
+      // GuardedPagePoolEnd == 0 if GWP-ASan is disabled.
+      if (UNLIKELY(GuardedPagePoolEnd == 0))
+        return false;
+      NextSampleCounter =
+          (random::getRandomUnsigned64() % AdjustedSampleRate) + 1;
+    }
+
+    return UNLIKELY(--NextSampleCounter == 0);
+  }
+
+
+  // Returns whether the provided pointer is a current sampled allocation that
+  // is owned by this pool.
+  ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const {
+    uintptr_t P = reinterpret_cast<uintptr_t>(Ptr);
+    return GuardedPagePool <= P && P < GuardedPagePoolEnd;
+  }
+
+  // Allocate memory in a guarded slot, and return a pointer to the new
+  // allocation. Returns nullptr if the pool is empty, the requested size is too
+  // large for this pool to handle, or the requested size is zero.
+  void *allocate(std::size_t Size);
+
+  // Deallocate memory in a guarded slot. The provided pointer must have been
+  // allocated using this pool. This will set the guarded slot as inaccessible.
+  void deallocate(void *Ptr);
+
+  // Returns the size of the allocation at Ptr.
+  std::size_t getSize(const void *Ptr);
+
+  // Returns the largest allocation that is supported by this pool. Any
+  // allocations larger than this should go to the regular system allocator.
+  std::size_t maximumAllocationSize() const;
+
+  // Dumps an error report (including allocation and deallocation stack traces).
+  // An optional error may be provided if the caller knows what the error is
+  // ahead of time. This is primarily a helper function to locate the static
+  // singleton pointer and call the internal version of this function. This
+  // method is never thread safe, and should only be called when fatal errors
+  // occur.
+  static void reportError(uintptr_t AccessPtr, Error Error = Error::UNKNOWN);
+
+private:
+  static constexpr std::size_t kInvalidSlotID = SIZE_MAX;
+
+  // These functions anonymously map memory or change the permissions of mapped
+  // memory into this process in a platform- specific way. Pointer and size
+  // arguments are expected to be page-aligned. These functions will never
+  // return on error, instead electing to kill the calling process on failure.
+  // Note that memory is initially mapped inaccessible. In order for RW
+  // mappings, call mapMemory() followed by markReadWrite() on the returned
+  // pointer.
+  void *mapMemory(std::size_t Size) const;
+  void markReadWrite(void *Ptr, std::size_t Size) const;
+  void markInaccessible(void *Ptr, std::size_t Size) const;
+
+  // Get the current thread ID, or kInvalidThreadID if failure. Note: This
+  // implementation is platform-specific.
+  static uint64_t getThreadID();
+
+  // Get the page size from the platform-specific implementation. Only needs to
+  // be called once, and the result should be cached in PageSize in this class.
+  static std::size_t getPlatformPageSize();
+
+  // Install the SIGSEGV crash handler for printing use-after-free and heap-
+  // buffer-{under|over}flow exceptions. This is platform specific as even
+  // though POSIX and Windows both support registering handlers through
+  // signal(), we have to use platform-specific signal handlers to obtain the
+  // address that caused the SIGSEGV exception.
+  static void installSignalHandlers();
+
+  // Returns the index of the slot that this pointer resides in. If the pointer
+  // is not owned by this pool, the result is undefined.
+  std::size_t addrToSlot(uintptr_t Ptr) const;
+
+  // Returns the address of to the N-th guarded slot.
+  uintptr_t slotToAddr(std::size_t N) const;
+
+  // Returns a pointer to the metadata for the owned pointer. If the pointer is
+  // not owned by this pool, the result is undefined.
+  AllocationMetadata *addrToMetadata(uintptr_t Ptr) const;
+
+  // Returns the address of the page that this pointer resides in.
+  uintptr_t getPageAddr(uintptr_t Ptr) const;
+
+  // Gets the nearest slot to the provided address.
+  std::size_t getNearestSlot(uintptr_t Ptr) const;
+
+  // Returns whether the provided pointer is a guard page or not. The pointer
+  // must be within memory owned by this pool, else the result is undefined.
+  bool isGuardPage(uintptr_t Ptr) const;
+
+  // Reserve a slot for a new guarded allocation. Returns kInvalidSlotID if no
+  // slot is available to be reserved.
+  std::size_t reserveSlot();
+
+  // Unreserve the guarded slot.
+  void freeSlot(std::size_t SlotIndex);
+
+  // Returns the offset (in bytes) between the start of a guarded slot and where
+  // the start of the allocation should take place. Determined using the size of
+  // the allocation and the options provided at init-time.
+  uintptr_t allocationSlotOffset(std::size_t AllocationSize);
+
+  // Returns the diagnosis for an unknown error. If the diagnosis is not
+  // Error::INVALID_FREE or Error::UNKNOWN, the metadata for the slot
+  // responsible for the error is placed in *Meta.
+  Error diagnoseUnknownError(uintptr_t AccessPtr, AllocationMetadata **Meta);
+
+  void reportErrorInternal(uintptr_t AccessPtr, Error Error);
+
+  // Cached page size for this system in bytes.
+  std::size_t PageSize = 0;
+
+  // A mutex to protect the guarded slot and metadata pool for this class.
+  Mutex PoolMutex;
+  // The number of guarded slots that this pool holds.
+  std::size_t MaxSimultaneousAllocations = 0;
+  // Record the number allocations that we've sampled. We store this amount so
+  // that we don't randomly choose to recycle a slot that previously had an
+  // allocation before all the slots have been utilised.
+  std::size_t NumSampledAllocations = 0;
+  // Pointer to the pool of guarded slots. Note that this points to the start of
+  // the pool (which is a guard page), not a pointer to the first guarded page.
+  uintptr_t GuardedPagePool = UINTPTR_MAX;
+  uintptr_t GuardedPagePoolEnd = 0;
+  // Pointer to the allocation metadata (allocation/deallocation stack traces),
+  // if any.
+  AllocationMetadata *Metadata = nullptr;
+
+  // Pointer to an array of free slot indexes.
+  std::size_t *FreeSlots = nullptr;
+  // The current length of the list of free slots.
+  std::size_t FreeSlotsLength = 0;
+
+  // See options.{h, inc} for more information.
+  bool PerfectlyRightAlign = false;
+
+  // Printf function supplied by the implementing allocator. We can't (in
+  // general) use printf() from the cstdlib as it may malloc(), causing infinite
+  // recursion.
+  options::Printf_t Printf = nullptr;
+
+  // The adjusted sample rate for allocation sampling. Default *must* be
+  // nonzero, as dynamic initialisation may call malloc (e.g. from libstdc++)
+  // before GPA::init() is called. This would cause an error in shouldSample(),
+  // where we would calculate modulo zero. This value is set UINT64_MAX, as when
+  // GWP-ASan is disabled, we wish to never spend wasted cycles recalculating
+  // the sample rate. Manually aligned here as on a 32-bit system, the 8-bit
+  // integer is not guaranteed to have 8-byte alignment, leading to decreased
+  // performance for hot functions.
+  ALIGNED(8) uint64_t AdjustedSampleRate = UINT64_MAX;
+  // Thread-local decrementing counter that indicates that a given allocation
+  // should be sampled when it reaches zero.
+  static TLS_INITIAL_EXEC uint64_t NextSampleCounter;
+};
+
+static_assert(std::is_trivially_destructible<GuardedPoolAllocator>::value,
+              "GuardedPoolAllocator must be trivially destructible.");
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_
Index: compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp
@@ -0,0 +1,415 @@
+//===-- guarded_pool_allocator.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/guarded_pool_allocator.h"
+
+#include "gwp_asan/options.h"
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <new>
+
+using AllocationMetadata = gwp_asan::GuardedPoolAllocator::AllocationMetadata;
+using Error = gwp_asan::GuardedPoolAllocator::Error;
+
+namespace gwp_asan {
+namespace {
+// Forward declare the pointer to the singleton version of this class.
+// Instantiated during initialisation, this allows the signal handler
+// to find this class in order to deduce the root cause of failures. Must not be
+// referenced by users outside this translation unit, in order to avoid
+// init-order-fiasco.
+GuardedPoolAllocator *SingletonPtr = nullptr;
+} // anonymous namespace
+
+// Gets the singleton implementation of this class. Thread-compatible until
+// init() is called, thread-safe afterwards.
+GuardedPoolAllocator *getSingleton() { return SingletonPtr; }
+
+void GuardedPoolAllocator::AllocationMetadata::RecordAllocation(
+    uintptr_t AllocAddr, std::size_t AllocSize) {
+  Addr = AllocAddr;
+  Size = AllocSize;
+  IsDeallocated = false;
+
+  // TODO(hctim): Implement stack trace collection.
+  AllocationTrace.ThreadID = getThreadID();
+  DeallocationTrace.ThreadID = kInvalidThreadID;
+  AllocationTrace.Trace[0] = 0;
+  DeallocationTrace.Trace[0] = 0;
+}
+
+void GuardedPoolAllocator::AllocationMetadata::RecordDeallocation() {
+  IsDeallocated = true;
+  // TODO(hctim): Implement stack trace collection.
+  DeallocationTrace.ThreadID = getThreadID();
+}
+
+void GuardedPoolAllocator::init(const options::Options &Opts) {
+  // Note: We return from the constructor here if GWP-ASan is not available.
+  // This will stop heap-allocation of class members, as well as mmap() of the
+  // guarded slots.
+  if (!Opts.Enabled || Opts.SampleRate == 0 ||
+      Opts.MaxSimultaneousAllocations == 0)
+    return;
+
+  // TODO(hctim): Add a death unit test for this.
+  if (SingletonPtr) {
+    (*SingletonPtr->Printf)(
+        "GWP-ASan Error: init() has already been called.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  if (Opts.SampleRate < 0) {
+    Opts.Printf("GWP-ASan Error: SampleRate is < 0.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  if (Opts.MaxSimultaneousAllocations < 0) {
+    Opts.Printf("GWP-ASan Error: MaxSimultaneousAllocations is < 0.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  SingletonPtr = this;
+
+  MaxSimultaneousAllocations = Opts.MaxSimultaneousAllocations;
+
+  PageSize = getPlatformPageSize();
+
+  PerfectlyRightAlign = Opts.PerfectlyRightAlign;
+  Printf = Opts.Printf;
+
+  std::size_t PoolBytesRequired =
+      PageSize * (1 + MaxSimultaneousAllocations) +
+      MaxSimultaneousAllocations * maximumAllocationSize();
+  void *GuardedPoolMemory = mapMemory(PoolBytesRequired);
+
+  std::size_t BytesRequired = MaxSimultaneousAllocations * sizeof(*Metadata);
+  Metadata = reinterpret_cast<AllocationMetadata *>(mapMemory(BytesRequired));
+  markReadWrite(Metadata, BytesRequired);
+
+  // Allocate memory and set up the free pages queue.
+  BytesRequired = MaxSimultaneousAllocations * sizeof(*FreeSlots);
+  FreeSlots = reinterpret_cast<std::size_t *>(mapMemory(BytesRequired));
+  markReadWrite(FreeSlots, BytesRequired);
+
+  // Multiply the sample rate by 2 to give a good, fast approximation for (1 /
+  // SampleRate) chance of sampling.
+  if (Opts.SampleRate != 1)
+    AdjustedSampleRate = Opts.SampleRate * 2;
+  else
+    AdjustedSampleRate = 1;
+
+  GuardedPagePool = reinterpret_cast<uintptr_t>(GuardedPoolMemory);
+  GuardedPagePoolEnd =
+      reinterpret_cast<uintptr_t>(GuardedPoolMemory) + PoolBytesRequired;
+
+  // Ensure that signal handlers are installed as late as possible, as the class
+  // is not thread-safe until init() is finished, and thus a SIGSEGV may cause a
+  // race to members if recieved during init().
+  if (Opts.InstallSignalHandlers)
+    installSignalHandlers();
+}
+
+void *GuardedPoolAllocator::allocate(std::size_t Size) {
+  if (Size == 0 || Size > maximumAllocationSize())
+    return nullptr;
+
+  ScopedLock L(&PoolMutex);
+
+  size_t Index = reserveSlot();
+  if (Index == kInvalidSlotID)
+    return nullptr;
+
+  uintptr_t Ptr = slotToAddr(Index);
+  Ptr += allocationSlotOffset(Size);
+  AllocationMetadata *Meta = addrToMetadata(Ptr);
+
+  // If a slot is multiple pages in size, and the allocation takes up a single
+  // page, we can improve overflow detection by leaving the unused pages as
+  // unmapped.
+  markReadWrite(reinterpret_cast<void *>(getPageAddr(Ptr)), Size);
+
+  Meta->RecordAllocation(Ptr, Size);
+
+  return reinterpret_cast<void *>(Ptr);
+}
+
+void GuardedPoolAllocator::deallocate(void *Ptr) {
+  assert(pointerIsMine(Ptr) && "Pointer is not mine!");
+  uintptr_t UPtr = reinterpret_cast<uintptr_t>(Ptr);
+  uintptr_t SlotStart = slotToAddr(addrToSlot(UPtr));
+  AllocationMetadata *Meta = addrToMetadata(UPtr);
+  if (Meta->Addr != UPtr) {
+    reportError(UPtr, Error::INVALID_FREE);
+    exit(EXIT_FAILURE);
+  }
+
+  ScopedLock L(&PoolMutex);
+
+  if (Meta->IsDeallocated) {
+    reportError(UPtr, Error::DOUBLE_FREE);
+    exit(EXIT_FAILURE);
+  }
+
+  Meta->RecordDeallocation();
+
+  markInaccessible(reinterpret_cast<void *>(SlotStart),
+                   maximumAllocationSize());
+  freeSlot(addrToSlot(UPtr));
+}
+
+std::size_t GuardedPoolAllocator::getSize(const void *Ptr) {
+  assert(pointerIsMine(Ptr));
+  ScopedLock L(&PoolMutex);
+  AllocationMetadata *Meta = addrToMetadata(reinterpret_cast<uintptr_t>(Ptr));
+  assert(Meta->Addr == reinterpret_cast<uintptr_t>(Ptr));
+  return Meta->Size;
+}
+
+std::size_t GuardedPoolAllocator::maximumAllocationSize() const {
+  return PageSize;
+}
+
+AllocationMetadata *GuardedPoolAllocator::addrToMetadata(uintptr_t Ptr) const {
+  assert(pointerIsMine(reinterpret_cast<void *>(Ptr)));
+  return &Metadata[addrToSlot(Ptr)];
+}
+
+std::size_t GuardedPoolAllocator::addrToSlot(uintptr_t Ptr) const {
+  std::size_t ByteOffsetFromPoolStart = Ptr - GuardedPagePool;
+  return ByteOffsetFromPoolStart / (maximumAllocationSize() + PageSize);
+}
+
+uintptr_t GuardedPoolAllocator::slotToAddr(std::size_t N) const {
+  return GuardedPagePool + (PageSize * (1 + N)) + (maximumAllocationSize() * N);
+}
+
+uintptr_t GuardedPoolAllocator::getPageAddr(uintptr_t Ptr) const {
+  return Ptr & ~(static_cast<uintptr_t>(PageSize) - 1);
+}
+
+bool GuardedPoolAllocator::isGuardPage(uintptr_t Ptr) const {
+  std::size_t PageOffsetFromPoolStart = (Ptr - GuardedPagePool) / PageSize;
+  std::size_t PagesPerSlot = maximumAllocationSize() / PageSize;
+  return (PageOffsetFromPoolStart % (PagesPerSlot + 1)) == 0;
+}
+
+std::size_t GuardedPoolAllocator::reserveSlot() {
+  // Avoid potential reuse of a slot before we have made at least a single
+  // allocation in each slot. Helps with our use-after-free detection.
+  if (NumSampledAllocations < MaxSimultaneousAllocations)
+    return NumSampledAllocations++;
+
+  if (FreeSlotsLength == 0)
+    return kInvalidSlotID;
+
+  std::size_t ReservedIndex = random::getRandomUnsigned64() % FreeSlotsLength;
+  std::size_t SlotIndex = FreeSlots[ReservedIndex];
+  FreeSlots[ReservedIndex] = FreeSlots[--FreeSlotsLength];
+  return SlotIndex;
+}
+
+void GuardedPoolAllocator::freeSlot(std::size_t SlotIndex) {
+  assert(FreeSlotsLength < MaxSimultaneousAllocations);
+  FreeSlots[FreeSlotsLength++] = SlotIndex;
+}
+
+uintptr_t GuardedPoolAllocator::allocationSlotOffset(std::size_t Size) {
+  assert(Size > 0);
+
+  bool ShouldRightAlign = random::getRandomUnsigned64() % 2 == 0;
+  if (!ShouldRightAlign)
+    return 0;
+
+  uintptr_t Offset = maximumAllocationSize();
+  if (!PerfectlyRightAlign) {
+    if (Size == 3)
+      Size = 4;
+    else if (Size > 4 && Size <= 8)
+      Size = 8;
+    else if (Size > 8 && (Size % 16) != 0)
+      Size += 16 - (Size % 16);
+  }
+  Offset -= Size;
+  return Offset;
+}
+
+void GuardedPoolAllocator::reportError(uintptr_t AccessPtr, Error Error) {
+  if (SingletonPtr)
+    SingletonPtr->reportErrorInternal(AccessPtr, Error);
+}
+
+std::size_t GuardedPoolAllocator::getNearestSlot(uintptr_t Ptr) const {
+  if (Ptr <= GuardedPagePool + PageSize)
+    return 0;
+  if (Ptr > GuardedPagePoolEnd - PageSize)
+    return MaxSimultaneousAllocations - 1;
+
+  if (!isGuardPage(Ptr))
+    return addrToSlot(Ptr);
+
+  if (Ptr % PageSize <= PageSize / 2)
+    return addrToSlot(Ptr - PageSize); // Round down.
+  return addrToSlot(Ptr + PageSize);   // Round up.
+}
+
+Error GuardedPoolAllocator::diagnoseUnknownError(uintptr_t AccessPtr,
+                                                 AllocationMetadata **Meta) {
+  // Let's try and figure out what the source of this error is.
+  if (isGuardPage(AccessPtr)) {
+    std::size_t Slot = getNearestSlot(AccessPtr);
+    AllocationMetadata *SlotMeta = addrToMetadata(slotToAddr(Slot));
+
+    // Ensure that this slot was allocated once upon a time.
+    if (!SlotMeta->Addr)
+      return Error::UNKNOWN;
+    *Meta = SlotMeta;
+
+    if (SlotMeta->Addr < AccessPtr)
+      return Error::BUFFER_OVERFLOW;
+    return Error::BUFFER_UNDERFLOW;
+  }
+
+  // Access wasn't a guard page, check for use-after-free.
+  AllocationMetadata *SlotMeta = addrToMetadata(AccessPtr);
+  if (SlotMeta->IsDeallocated) {
+    *Meta = SlotMeta;
+    return Error::USE_AFTER_FREE;
+  }
+
+  // If we have reached here, the error is still unknown. There is no metadata
+  // available.
+  return Error::UNKNOWN;
+}
+
+// Prints the provided error and metadata information. Returns true if there is
+// additional context that can be provided, false otherwise (i.e. returns false
+// if Error == {UNKNOWN, INVALID_FREE without metadata}).
+bool printErrorType(Error Error, uintptr_t AccessPtr, AllocationMetadata *Meta,
+                    options::Printf_t Printf) {
+  switch (Error) {
+  case Error::UNKNOWN:
+    Printf("GWP-ASan couldn't automatically determine the source of the "
+           "memory error when accessing 0x%zx. It was likely caused by a wild "
+           "memory access into the GWP-ASan pool.\n",
+           AccessPtr);
+    return false;
+  case Error::USE_AFTER_FREE:
+    Printf("Use after free occurred when accessing memory at: 0x%zx\n",
+           AccessPtr);
+    break;
+  case Error::DOUBLE_FREE:
+    Printf("Double free occurred when trying to free memory at: 0x%zx\n",
+           AccessPtr);
+    break;
+  case Error::INVALID_FREE:
+    Printf(
+        "Invalid (wild) free occurred when trying to free memory at: 0x%zx\n",
+        AccessPtr);
+    // It's possible for an invalid free to fall onto a slot that has never been
+    // allocated. If this is the case, there is no valid metadata.
+    if (Meta == nullptr)
+      return false;
+    break;
+  case Error::BUFFER_OVERFLOW:
+    Printf("Buffer overflow occurred when accessing memory at: 0x%zx\n",
+           AccessPtr);
+    break;
+  case Error::BUFFER_UNDERFLOW:
+    Printf("Buffer underflow occurred when accessing memory at: 0x%zx\n",
+           AccessPtr);
+    break;
+  }
+
+  Printf("0x%zx is ", AccessPtr);
+  if (AccessPtr < Meta->Addr)
+    Printf("located %zu bytes to the left of a %zu-byte allocation located at "
+           "0x%zx\n",
+           Meta->Addr - AccessPtr, Meta->Size, Meta->Addr);
+  else if (AccessPtr > Meta->Addr)
+    Printf("located %zu bytes to the right of a %zu-byte allocation located at "
+           "0x%zx\n",
+           AccessPtr - Meta->Addr, Meta->Size, Meta->Addr);
+  else
+    Printf("a %zu-byte allocation\n", Meta->Size);
+  return true;
+}
+
+void printThreadInformation(Error Error, uintptr_t AccessPtr,
+                            AllocationMetadata *Meta,
+                            options::Printf_t Printf) {
+  Printf("0x%zx was allocated by thread ", AccessPtr);
+  if (Meta->AllocationTrace.ThreadID == UINT64_MAX)
+    Printf("UNKNOWN.\n");
+  else
+    Printf("%zu.\n", Meta->AllocationTrace.ThreadID);
+
+  if (Error == Error::USE_AFTER_FREE || Error == Error::DOUBLE_FREE) {
+    Printf("0x%zx was freed by thread ", AccessPtr);
+    if (Meta->AllocationTrace.ThreadID == UINT64_MAX)
+      Printf("UNKNOWN.\n");
+    else
+      Printf("%zu.\n", Meta->AllocationTrace.ThreadID);
+  }
+}
+
+struct ScopedEndOfReportDecorator {
+  ScopedEndOfReportDecorator(options::Printf_t Printf) : Printf(Printf) {}
+  ~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report ***\n"); }
+  options::Printf_t Printf;
+};
+
+void GuardedPoolAllocator::reportErrorInternal(uintptr_t AccessPtr,
+                                               Error Error) {
+  if (!pointerIsMine(reinterpret_cast<void *>(AccessPtr))) {
+    return;
+  }
+
+  // Attempt to prevent races to re-use the same slot that triggered this error.
+  // This does not guarantee that there are no races, because another thread can
+  // take the locks during the time that the signal handler is being called.
+  PoolMutex.tryLock();
+
+  Printf("*** GWP-ASan detected a memory error ***\n");
+  ScopedEndOfReportDecorator Decorator(Printf);
+
+  AllocationMetadata *Meta = nullptr;
+
+  if (Error == Error::UNKNOWN) {
+    Error = diagnoseUnknownError(AccessPtr, &Meta);
+  } else {
+    std::size_t Slot = getNearestSlot(AccessPtr);
+    Meta = addrToMetadata(slotToAddr(Slot));
+    // Ensure that this slot has been previously allocated.
+    if (!Meta->Addr)
+      Meta = nullptr;
+  }
+
+  // Print the error information, and if there is no valid metadata, stop here.
+  if (!printErrorType(Error, AccessPtr, Meta, Printf)) {
+    return;
+  }
+
+  // Ensure that we have a valid metadata pointer from this point forward.
+  if (Meta == nullptr) {
+    Printf("GWP-ASan internal unreachable error. Metadata is not null.\n");
+    return;
+  }
+
+  printThreadInformation(Error, AccessPtr, Meta, Printf);
+  // TODO(hctim): Implement stack unwinding here. Ask the caller to provide us
+  // with the base pointer, and we unwind the stack to give a stack trace for
+  // the access.
+  // TODO(hctim): Implement dumping here of allocation/deallocation traces.
+}
+
+TLS_INITIAL_EXEC uint64_t GuardedPoolAllocator::NextSampleCounter = 0;
+} // namespace gwp_asan
Index: compiler-rt/lib/gwp_asan/definitions.h
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/definitions.h
@@ -0,0 +1,34 @@
+//===-- gwp_asan_definitions.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GWP_ASAN_DEFINITIONS_H_
+#define GWP_ASAN_DEFINITIONS_H_
+
+#define TLS_INITIAL_EXEC __thread __attribute__((tls_model("initial-exec")))
+
+#ifdef LIKELY
+# undef LIKELY
+#endif // defined(LIKELY)
+#define LIKELY(X) __builtin_expect(!!(X), 1)
+
+#ifdef UNLIKELY
+# undef UNLIKELY
+#endif // defined(UNLIKELY)
+#define UNLIKELY(X) __builtin_expect(!!(X), 0)
+
+#ifdef ALWAYS_INLINE
+# undef ALWAYS_INLINE
+#endif // defined(ALWAYS_INLINE)
+#define ALWAYS_INLINE inline __attribute__((always_inline))
+
+#ifdef ALIGNED
+# undef ALIGNED
+#endif // defined(ALIGNED)
+#define ALIGNED(X) __attribute__((aligned(X)))
+
+#endif // GWP_ASAN_DEFINITIONS_H_
Index: compiler-rt/lib/gwp_asan/CMakeLists.txt
===================================================================
--- /dev/null
+++ compiler-rt/lib/gwp_asan/CMakeLists.txt
@@ -0,0 +1,70 @@
+add_compiler_rt_component(gwp_asan)
+
+include_directories(..)
+
+set(GWP_ASAN_SOURCES
+  guarded_pool_allocator.cpp
+  guarded_pool_allocator_posix.cpp
+  random.cpp
+)
+
+set(GWP_ASAN_HEADERS
+  guarded_pool_allocator.h
+  mutex.h
+  options.h
+  options.inc
+  random.h
+)
+
+# Disable RTTI and exception support, as we want these libraries to be
+# C-compatible. Regular C source files can be linked against the generated
+# GwpAsan libraries using the Clang C compiler.
+set(GWP_ASAN_CFLAGS -fno-rtti -fno-exceptions)
+
+# Options parsing support is optional. GwpAsan is totally independent of
+# sanitizer_common, the options parser is not. This is an optional library
+# that can be used by an allocator to automatically parse GwpAsan options from
+# the environment variable GWP_ASAN_FLAGS, but the allocator can choose to
+# implement its own options parsing and populate the Options struct itself.
+set(GWP_ASAN_OPTIONS_PARSER_SOURCES
+  optional/options_parser.cpp
+)
+set(GWP_ASAN_OPTIONS_PARSER_HEADERS
+  optional/options_parser.h
+)
+set(GWP_ASAN_OPTIONS_PARSER_CFLAGS
+    ${GWP_ASAN_CFLAGS}
+    ${SANITIZER_COMMON_CFLAGS})
+
+if (COMPILER_RT_HAS_GWP_ASAN)
+  foreach(arch ${GWP_ASAN_SUPPORTED_ARCH})
+    add_compiler_rt_runtime(
+      clang_rt.gwp_asan
+      STATIC
+      ARCHS ${arch}
+      SOURCES ${GWP_ASAN_SOURCES}
+      ADDITIONAL_HEADERS ${GWP_ASAN_HEADERS}
+      CFLAGS ${GWP_ASAN_CFLAGS}
+      PARENT_TARGET gwp_asan
+    )
+  endforeach()
+
+  add_compiler_rt_object_libraries(RTGwpAsan
+      ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
+      SOURCES ${GWP_ASAN_SOURCES}
+      ADDITIONAL_HEADERS ${GWP_ASAN_HEADERS}
+      CFLAGS ${GWP_ASAN_CFLAGS})
+
+  # Note: If you choose to add this as an object library, ensure you also
+  # include the sanitizer_common flag parsing object lib
+  # 'RTSanitizerCommonNoTermination'.
+  add_compiler_rt_object_libraries(RTGwpAsanOptionsParser
+      ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
+      SOURCES ${GWP_ASAN_OPTIONS_PARSER_SOURCES}
+      ADDITIONAL_HEADERS ${GWP_ASAN_OPTIONS_PARSER_HEADERS}
+      CFLAGS ${GWP_ASAN_OPTIONS_PARSER_CFLAGS})
+endif()
+
+if(COMPILER_RT_INCLUDE_TESTS)
+  add_subdirectory(tests)
+endif()
Index: compiler-rt/cmake/config-ix.cmake
===================================================================
--- compiler-rt/cmake/config-ix.cmake
+++ compiler-rt/cmake/config-ix.cmake
@@ -238,6 +238,7 @@
   set(ALL_FUZZER_SUPPORTED_ARCH ${X86_64} ${ARM64})
 endif()
 
+set(ALL_GWP_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM64} ${ARM32})
 if(APPLE)
   set(ALL_LSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${MIPS64} ${ARM64})
 else()
@@ -437,6 +438,9 @@
   list_intersect(DFSAN_SUPPORTED_ARCH
     ALL_DFSAN_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
+  list_intersect(GWP_ASAN_SUPPORTED_ARCH
+    ALL_GWP_ASAN_SUPPORTED_ARCH
+    SANITIZER_COMMON_SUPPORTED_ARCH)
   list_intersect(LSAN_SUPPORTED_ARCH
     ALL_LSAN_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
@@ -505,6 +509,7 @@
   filter_available_targets(XRAY_SUPPORTED_ARCH ${ALL_XRAY_SUPPORTED_ARCH})
   filter_available_targets(SHADOWCALLSTACK_SUPPORTED_ARCH
     ${ALL_SHADOWCALLSTACK_SUPPORTED_ARCH})
+  filter_available_targets(GWP_ASAN_SUPPORTED_ARCH ${ALL_GWP_ASAN_SUPPORTED_ARCH})
 endif()
 
 if (MSVC)
@@ -532,7 +537,7 @@
   set(OS_NAME "${CMAKE_SYSTEM_NAME}")
 endif()
 
-set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;cfi;scudo;ubsan_minimal)
+set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;cfi;scudo;ubsan_minimal;gwp_asan)
 set(COMPILER_RT_SANITIZERS_TO_BUILD all CACHE STRING
     "sanitizers to build if supported on the target (all;${ALL_SANITIZERS})")
 list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}")
@@ -676,3 +681,13 @@
 else()
   set(COMPILER_RT_HAS_SHADOWCALLSTACK FALSE)
 endif()
+
+# Note: Fuchsia and Windows are not currently supported by GWP-ASan. Support
+# is planned for these platforms. Darwin is also not supported due to TLS
+# calling malloc on first use.
+if (GWP_ASAN_SUPPORTED_ARCH AND OS_NAME MATCHES "Android|Linux")
+  set(COMPILER_RT_HAS_GWP_ASAN TRUE)
+else()
+  set(COMPILER_RT_HAS_GWP_ASAN FALSE)
+endif()
+pythonize_bool(COMPILER_RT_HAS_GWP_ASAN)
Index: clang/runtime/CMakeLists.txt
===================================================================
--- clang/runtime/CMakeLists.txt
+++ clang/runtime/CMakeLists.txt
@@ -132,7 +132,7 @@
     # Add top-level targets for various compiler-rt test suites.
     set(COMPILER_RT_TEST_SUITES check-fuzzer check-asan check-hwasan check-asan-dynamic check-dfsan
       check-lsan check-msan check-sanitizer check-tsan check-ubsan check-ubsan-minimal
-      check-profile check-cfi check-cfi-and-supported check-safestack)
+      check-profile check-cfi check-cfi-and-supported check-safestack check-gwp_asan)
     foreach(test_suite ${COMPILER_RT_TEST_SUITES})
       get_ext_project_build_command(run_test_suite ${test_suite})
       add_custom_target(${test_suite}
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to