[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);
}