HWASan support in bionic.
* Allow sanitization of libc (excluding existing global sanitizers)
and disallow sanitization of linker. The latter has not been
necessary before because HWASan is the first sanitizer to support
static binaries (with the exception of CFI, which is not used
globally).
* Static binary startup: initialize HWASan shadow very early so that
almost entire libc can be sanitized. The rest of initialization is
done in a global constructor; until that is done sanitized code can
run but can't report errors (will simply crash with SIGTRAP).
* Switch malloc_common from je_* to __sanitizer_*.
* Call hwasan functions when entering and leaving threads. We can not
intercept pthread_create when libc depends on libclang_rt.hwasan.
An alternative to this would be a callback interface like requested
here:
https://sourceware.org/glibc/wiki/ThreadPropertiesAPI
All of the above is behind a compile-time check
__has_feature(hwaddress_sanitizer). This means that HWASan actually
requires libc to be instrumented, and would not work otherwise. It's
an implementation choice that greatly reduces complexity of the tool.
Instrumented libc also guarantees that hwasan is present and
initialized in every process, which allows piecemeal sanitization
(i.e. library w/o main executable, or even individual static
libraries), unlike ASan.
Change-Id: If44c46b79b15049d1745ba46ec910ae4f355d19c
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 9eb574a..55506a3 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -45,6 +45,10 @@
#include "private/bionic_tls.h"
#include "private/KernelArgumentBlock.h"
+#if __has_feature(hwaddress_sanitizer)
+#include <sanitizer/hwasan_interface.h>
+#endif
+
// Leave the variable uninitialized for the sake of the dynamic loader, which
// links in this file. The loader will initialize this variable before
// relocating itself.
@@ -85,11 +89,10 @@
//
// The 'structors' parameter contains pointers to various initializer
// arrays that must be run before the program's 'main' routine is launched.
-
-__noreturn void __libc_init(void* raw_args,
- void (*onexit)(void) __unused,
- int (*slingshot)(int, char**, char**),
- structors_array_t const * const structors) {
+__noreturn static void __real_libc_init(void *raw_args,
+ void (*onexit)(void) __unused,
+ int (*slingshot)(int, char**, char**),
+ structors_array_t const * const structors) {
BIONIC_STOP_UNWIND;
KernelArgumentBlock args(raw_args);
@@ -124,6 +127,20 @@
exit(slingshot(args.argc, args.argv, args.envp));
}
+#if __has_feature(hwaddress_sanitizer)
+__attribute__((no_sanitize("hwaddress")))
+#endif
+__noreturn void __libc_init(void* raw_args,
+ void (*onexit)(void) __unused,
+ int (*slingshot)(int, char**, char**),
+ structors_array_t const * const structors) {
+#if __has_feature(hwaddress_sanitizer)
+ __hwasan_shadow_init();
+#endif
+ __real_libc_init(raw_args, onexit, slingshot, structors);
+}
+
+
static uint32_t g_target_sdk_version{__ANDROID_API__};
extern "C" uint32_t android_get_application_target_sdk_version() {
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index 40a0023..5a5ec76 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -47,8 +47,26 @@
#include <private/bionic_globals.h>
#include <private/bionic_malloc_dispatch.h>
+#if __has_feature(hwaddress_sanitizer)
+// FIXME: implement these in HWASan allocator.
+extern "C" int __sanitizer_iterate(uintptr_t base __unused, size_t size __unused,
+ void (*callback)(uintptr_t base, size_t size, void* arg) __unused,
+ void* arg __unused) {
+ return 0;
+}
+
+extern "C" void __sanitizer_malloc_disable() {
+}
+
+extern "C" void __sanitizer_malloc_enable() {
+}
+#include <sanitizer/hwasan_interface.h>
+#define Malloc(function) __sanitizer_ ## function
+
+#else // __has_feature(hwaddress_sanitizer)
#include "jemalloc.h"
#define Malloc(function) je_ ## function
+#endif
static constexpr MallocDispatch __libc_malloc_default_dispatch
__attribute__((unused)) = {
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index c95d400..98d1726 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -249,6 +249,8 @@
// accesses previously made by the creating thread are visible to us.
thread->startup_handshake_lock.lock();
+ __hwasan_thread_enter();
+
__init_alternate_signal_stack(thread);
void* result = thread->start_routine(thread->start_routine_arg);
diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp
index ac5d429..220f7a0 100644
--- a/libc/bionic/pthread_exit.cpp
+++ b/libc/bionic/pthread_exit.cpp
@@ -126,6 +126,7 @@
// That's one last thing we can do before dropping to assembler.
ScopedSignalBlocker ssb;
__pthread_unmap_tls(thread);
+ __hwasan_thread_exit();
_exit_with_stack_teardown(thread->attr.stack_base, thread->mmap_size);
}
}
@@ -133,5 +134,6 @@
// No need to free mapped space. Either there was no space mapped, or it is left for
// the pthread_join caller to clean up.
__pthread_unmap_tls(thread);
+ __hwasan_thread_exit();
__exit(0);
}
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 18f5aee..1ec201b 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -31,6 +31,13 @@
#include <pthread.h>
#include <stdatomic.h>
+#if __has_feature(hwaddress_sanitizer)
+#include <sanitizer/hwasan_interface.h>
+#else
+#define __hwasan_thread_enter()
+#define __hwasan_thread_exit()
+#endif
+
#include "private/bionic_lock.h"
#include "private/bionic_tls.h"