[GWP-ASan] Integrate GWP-ASan into bionc's malloc() (using hooks).

This patch introduces GWP-ASan - a sampled allocator framework that
finds use-after-free and heap-buffer-overflow bugs in production
environments.

GWP-ASan is being introduced in an always-disabled mode. This means that
GWP-ASan will be permanently disabled until a further patch turns on
support. As such, there should be no visible functional change for the
time being.

GWP-ASan requires -fno-emulated-tls wherever it's linked from. We
intentionally link GWP-ASan into libc so that it's part of the initial
set of libraries, and thus has static TLS storage (so we can use
Initial-Exec TLS instead of Global-Dynamic). As a benefit, this reduces
overhead for a sampled process.

GWP-ASan is always initialised via. a call to
mallopt(M_INITIALIZE_GWP_ASAN, which must be done before a process is
multithreaded).

More information about GWP-ASan can be found in the upstream
documentation: http://llvm.org/docs/GwpAsan.html

Bug: 135634846
Test: atest bionic
Change-Id: Ib9bd33337d17dab39ac32f4536bff71bd23498b0
diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp
index 2cb1e8a..79d2521 100644
--- a/libc/bionic/malloc_common_dynamic.cpp
+++ b/libc/bionic/malloc_common_dynamic.cpp
@@ -64,6 +64,7 @@
 
 #include <sys/system_properties.h>
 
+#include "gwp_asan_wrappers.h"
 #include "heap_tagging.h"
 #include "malloc_common.h"
 #include "malloc_common_dynamic.h"
@@ -90,30 +91,6 @@
 
 // =============================================================================
 
-static constexpr MallocDispatch __libc_malloc_default_dispatch
-  __attribute__((unused)) = {
-    Malloc(calloc),
-    Malloc(free),
-    Malloc(mallinfo),
-    Malloc(malloc),
-    Malloc(malloc_usable_size),
-    Malloc(memalign),
-    Malloc(posix_memalign),
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-    Malloc(pvalloc),
-#endif
-    Malloc(realloc),
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-    Malloc(valloc),
-#endif
-    Malloc(malloc_iterate),
-    Malloc(malloc_disable),
-    Malloc(malloc_enable),
-    Malloc(mallopt),
-    Malloc(aligned_alloc),
-    Malloc(malloc_info),
-  };
-
 static constexpr char kHooksSharedLib[] = "libc_malloc_hooks.so";
 static constexpr char kHooksPrefix[] = "hooks";
 static constexpr char kHooksPropertyEnable[] = "libc.debug.hooks.enable";
@@ -261,6 +238,12 @@
   return true;
 }
 
+void SetGlobalFunctions(void* functions[]) {
+  for (size_t i = 0; i < FUNC_LAST; i++) {
+    gFunctions[i] = functions[i];
+  }
+}
+
 static void ClearGlobalFunctions() {
   for (size_t i = 0; i < FUNC_LAST; i++) {
     gFunctions[i] = nullptr;
@@ -344,7 +327,15 @@
 
 bool FinishInstallHooks(libc_globals* globals, const char* options, const char* prefix) {
   init_func_t init_func = reinterpret_cast<init_func_t>(gFunctions[FUNC_INITIALIZE]);
-  if (!init_func(&__libc_malloc_default_dispatch, &gZygoteChild, options)) {
+
+  // If GWP-ASan was initialised, we should use it as the dispatch table for
+  // heapprofd/malloc_debug/malloc_debug.
+  const MallocDispatch* prev_dispatch = GetDefaultDispatchTable();
+  if (prev_dispatch == nullptr) {
+    prev_dispatch = NativeAllocatorDispatch();
+  }
+
+  if (!init_func(prev_dispatch, &gZygoteChild, options)) {
     error_log("%s: failed to enable malloc %s", getprogname(), prefix);
     ClearGlobalFunctions();
     return false;
@@ -388,6 +379,8 @@
   char prop[PROP_VALUE_MAX];
   char* options = prop;
 
+  MaybeInitGwpAsanFromLibc();
+
   // Prefer malloc debug since it existed first and is a more complete
   // malloc interceptor than the hooks.
   bool hook_installed = false;
@@ -531,6 +524,13 @@
   if (opcode == M_SET_HEAP_TAGGING_LEVEL) {
     return SetHeapTaggingLevel(arg, arg_size);
   }
+  if (opcode == M_INITIALIZE_GWP_ASAN) {
+    if (arg == nullptr || arg_size != sizeof(bool)) {
+      errno = EINVAL;
+      return false;
+    }
+    return MaybeInitGwpAsan(*reinterpret_cast<bool*>(arg));
+  }
   // Try heapprofd's mallopt, as it handles options not covered here.
   return HeapprofdMallopt(opcode, arg, arg_size);
 }