Merge "Add the recoverable GWP-ASan feature."
diff --git a/libc/Android.bp b/libc/Android.bp
index fb4825d..67da126 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1444,6 +1444,7 @@
whole_static_libs: [
"gwp_asan",
+ "gwp_asan_crash_handler",
"libarm-optimized-routines-string",
"libasync_safe",
"libc_bionic_ndk",
@@ -1686,6 +1687,7 @@
cflags: ["-DLIBC_STATIC"],
whole_static_libs: [
"gwp_asan",
+ "gwp_asan_crash_handler",
"libc_init_static",
"libc_common_static",
"libc_unwind_static",
@@ -1695,6 +1697,7 @@
srcs: [ ":libc_sources_shared" ],
whole_static_libs: [
"gwp_asan",
+ "gwp_asan_crash_handler",
"libc_init_dynamic",
"libc_common_shared",
"libunwind-exported",
diff --git a/libc/bionic/gwp_asan_wrappers.cpp b/libc/bionic/gwp_asan_wrappers.cpp
index 3ccaf9b..251633d 100644
--- a/libc/bionic/gwp_asan_wrappers.cpp
+++ b/libc/bionic/gwp_asan_wrappers.cpp
@@ -36,6 +36,7 @@
#include <string.h>
#include <sys/types.h>
+#include "gwp_asan/crash_handler.h"
#include "gwp_asan/guarded_pool_allocator.h"
#include "gwp_asan/options.h"
#include "gwp_asan_wrappers.h"
@@ -189,6 +190,7 @@
}
bool GwpAsanInitialized = false;
+bool GwpAsanRecoverable = false;
// The probability (1 / SampleRate) that an allocation gets chosen to be put
// into the special GWP-ASan pool.
@@ -222,8 +224,32 @@
static const char* kMaxAllocsTargetedSyspropPrefix = "libc.debug.gwp_asan.max_allocs.";
static const char* kMaxAllocsEnvVar = "GWP_ASAN_MAX_ALLOCS";
+static const char* kRecoverableSystemSysprop = "libc.debug.gwp_asan.recoverable.system_default";
+static const char* kRecoverableAppSysprop = "libc.debug.gwp_asan.recoverable.app_default";
+static const char* kRecoverableTargetedSyspropPrefix = "libc.debug.gwp_asan.recoverable.";
+static const char* kRecoverableEnvVar = "GWP_ASAN_RECOVERABLE";
+
static const char kPersistPrefix[] = "persist.";
+bool NeedsGwpAsanRecovery(void* fault_ptr) {
+ fault_ptr = untag_address(fault_ptr);
+ return GwpAsanInitialized && GwpAsanRecoverable &&
+ __gwp_asan_error_is_mine(GuardedAlloc.getAllocatorState(),
+ reinterpret_cast<uintptr_t>(fault_ptr));
+}
+
+void GwpAsanPreCrashHandler(void* fault_ptr) {
+ fault_ptr = untag_address(fault_ptr);
+ if (!NeedsGwpAsanRecovery(fault_ptr)) return;
+ GuardedAlloc.preCrashReport(fault_ptr);
+}
+
+void GwpAsanPostCrashHandler(void* fault_ptr) {
+ fault_ptr = untag_address(fault_ptr);
+ if (!NeedsGwpAsanRecovery(fault_ptr)) return;
+ GuardedAlloc.postCrashReportRecoverableOnly(fault_ptr);
+}
+
void SetDefaultGwpAsanOptions(Options* options, unsigned* process_sample_rate,
const android_mallopt_gwp_asan_options_t& mallopt_options) {
options->Enabled = true;
@@ -243,11 +269,10 @@
}
}
-bool GetGwpAsanOption(unsigned long long* result,
- const android_mallopt_gwp_asan_options_t& mallopt_options,
- const char* system_sysprop, const char* app_sysprop,
- const char* targeted_sysprop_prefix, const char* env_var,
- const char* descriptive_name) {
+bool GetGwpAsanOptionImpl(char* value_out,
+ const android_mallopt_gwp_asan_options_t& mallopt_options,
+ const char* system_sysprop, const char* app_sysprop,
+ const char* targeted_sysprop_prefix, const char* env_var) {
const char* basename = "";
if (mallopt_options.program_name) basename = __gnu_basename(mallopt_options.program_name);
@@ -282,17 +307,25 @@
sysprop_names[3] = persist_default_sysprop;
}
- char settings_buf[PROP_VALUE_MAX];
- if (!get_config_from_env_or_sysprops(env_var, sysprop_names, arraysize(sysprop_names),
- settings_buf, PROP_VALUE_MAX)) {
+ return get_config_from_env_or_sysprops(env_var, sysprop_names, arraysize(sysprop_names),
+ value_out, PROP_VALUE_MAX);
+}
+
+bool GetGwpAsanIntegerOption(unsigned long long* result,
+ const android_mallopt_gwp_asan_options_t& mallopt_options,
+ const char* system_sysprop, const char* app_sysprop,
+ const char* targeted_sysprop_prefix, const char* env_var,
+ const char* descriptive_name) {
+ char buffer[PROP_VALUE_MAX];
+ if (!GetGwpAsanOptionImpl(buffer, mallopt_options, system_sysprop, app_sysprop,
+ targeted_sysprop_prefix, env_var)) {
return false;
}
-
char* end;
- unsigned long long value = strtoull(settings_buf, &end, 10);
+ unsigned long long value = strtoull(buffer, &end, 10);
if (value == ULLONG_MAX || *end != '\0') {
warning_log("Invalid GWP-ASan %s: \"%s\". Using default value instead.", descriptive_name,
- settings_buf);
+ buffer);
return false;
}
@@ -300,6 +333,33 @@
return true;
}
+bool GetGwpAsanBoolOption(bool* result, const android_mallopt_gwp_asan_options_t& mallopt_options,
+ const char* system_sysprop, const char* app_sysprop,
+ const char* targeted_sysprop_prefix, const char* env_var,
+ const char* descriptive_name) {
+ char buffer[PROP_VALUE_MAX] = {};
+ if (!GetGwpAsanOptionImpl(buffer, mallopt_options, system_sysprop, app_sysprop,
+ targeted_sysprop_prefix, env_var)) {
+ return false;
+ }
+
+ if (strncasecmp(buffer, "1", PROP_VALUE_MAX) == 0 ||
+ strncasecmp(buffer, "true", PROP_VALUE_MAX) == 0) {
+ *result = true;
+ return true;
+ } else if (strncasecmp(buffer, "0", PROP_VALUE_MAX) == 0 ||
+ strncasecmp(buffer, "false", PROP_VALUE_MAX) == 0) {
+ *result = false;
+ return true;
+ }
+
+ warning_log(
+ "Invalid GWP-ASan %s: \"%s\". Using default value \"%s\" instead. Valid values are \"true\", "
+ "\"1\", \"false\", or \"0\".",
+ descriptive_name, buffer, *result ? "true" : "false");
+ return false;
+}
+
// Initialize the GWP-ASan options structure in *options, taking into account whether someone has
// asked for specific GWP-ASan settings. The order of priority is:
// 1. Environment variables.
@@ -314,22 +374,23 @@
bool had_overrides = false;
unsigned long long buf;
- if (GetGwpAsanOption(&buf, mallopt_options, kSampleRateSystemSysprop, kSampleRateAppSysprop,
- kSampleRateTargetedSyspropPrefix, kSampleRateEnvVar, "sample rate")) {
+ if (GetGwpAsanIntegerOption(&buf, mallopt_options, kSampleRateSystemSysprop,
+ kSampleRateAppSysprop, kSampleRateTargetedSyspropPrefix,
+ kSampleRateEnvVar, "sample rate")) {
options->SampleRate = buf;
had_overrides = true;
}
- if (GetGwpAsanOption(&buf, mallopt_options, kProcessSamplingSystemSysprop,
- kProcessSamplingAppSysprop, kProcessSamplingTargetedSyspropPrefix,
- kProcessSamplingEnvVar, "process sampling rate")) {
+ if (GetGwpAsanIntegerOption(&buf, mallopt_options, kProcessSamplingSystemSysprop,
+ kProcessSamplingAppSysprop, kProcessSamplingTargetedSyspropPrefix,
+ kProcessSamplingEnvVar, "process sampling rate")) {
*process_sample_rate = buf;
had_overrides = true;
}
- if (GetGwpAsanOption(&buf, mallopt_options, kMaxAllocsSystemSysprop, kMaxAllocsAppSysprop,
- kMaxAllocsTargetedSyspropPrefix, kMaxAllocsEnvVar,
- "maximum simultaneous allocations")) {
+ if (GetGwpAsanIntegerOption(&buf, mallopt_options, kMaxAllocsSystemSysprop, kMaxAllocsAppSysprop,
+ kMaxAllocsTargetedSyspropPrefix, kMaxAllocsEnvVar,
+ "maximum simultaneous allocations")) {
options->MaxSimultaneousAllocations = buf;
had_overrides = true;
} else if (had_overrides) {
@@ -341,6 +402,16 @@
options->MaxSimultaneousAllocations =
/* default */ kDefaultMaxAllocs / frequency_multiplier;
}
+
+ bool recoverable = false;
+ if (GetGwpAsanBoolOption(&recoverable, mallopt_options, kRecoverableSystemSysprop,
+ kRecoverableAppSysprop, kRecoverableTargetedSyspropPrefix,
+ kRecoverableEnvVar, "recoverable")) {
+ options->Recoverable = recoverable;
+ GwpAsanRecoverable = recoverable;
+ had_overrides = true;
+ }
+
return had_overrides;
}
@@ -400,6 +471,9 @@
__libc_shared_globals()->gwp_asan_state = GuardedAlloc.getAllocatorState();
__libc_shared_globals()->gwp_asan_metadata = GuardedAlloc.getMetadataRegion();
+ __libc_shared_globals()->debuggerd_needs_gwp_asan_recovery = NeedsGwpAsanRecovery;
+ __libc_shared_globals()->debuggerd_gwp_asan_pre_crash_report = GwpAsanPreCrashHandler;
+ __libc_shared_globals()->debuggerd_gwp_asan_post_crash_report = GwpAsanPostCrashHandler;
return true;
}
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index 520e50f..c375cc4 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -107,6 +107,9 @@
const gwp_asan::AllocatorState* gwp_asan_state = nullptr;
const gwp_asan::AllocationMetadata* gwp_asan_metadata = nullptr;
+ bool (*debuggerd_needs_gwp_asan_recovery)(void* fault_addr) = nullptr;
+ void (*debuggerd_gwp_asan_pre_crash_report)(void* fault_addr) = nullptr;
+ void (*debuggerd_gwp_asan_post_crash_report)(void* fault_addr) = nullptr;
const char* scudo_stack_depot = nullptr;
const char* scudo_region_info = nullptr;
diff --git a/libdl/libdl.cpp b/libdl/libdl.cpp
index a56a5ab..20f08d9 100644
--- a/libdl/libdl.cpp
+++ b/libdl/libdl.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
+#include <android/dlext.h>
#include <dlfcn.h>
#include <link.h>
+#include <signal.h>
#include <stdlib.h>
-#include <android/dlext.h>
// These functions are exported by the loader
// TODO(dimitry): replace these with reference to libc.so
@@ -72,6 +73,9 @@
__attribute__((__weak__, visibility("default")))
int __loader_android_get_application_target_sdk_version();
+__attribute__((__weak__, visibility("default"))) bool __loader_android_handle_signal(
+ int signal_number, siginfo_t* info, void* context);
+
// Proxy calls to bionic loader
__attribute__((__weak__))
void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) {
@@ -138,4 +142,14 @@
return __loader_android_get_application_target_sdk_version();
}
+// Returns true if this function handled the signal, false if the caller should handle the signal
+// itself. This function returns true if the sigchain handler should immediately return, which
+// happens when the signal came from GWP-ASan, and we've dumped a debuggerd report and patched up
+// the GWP-ASan allocator to recover from the fault, and regular execution of the program can
+// continue.
+__attribute__((__weak__)) bool android_handle_signal(int signal_number, siginfo_t* info,
+ void* context) {
+ return __loader_android_handle_signal(signal_number, info, context);
+}
+
} // extern "C"
diff --git a/libdl/libdl.map.txt b/libdl/libdl.map.txt
index 473bdf2..043ec53 100644
--- a/libdl/libdl.map.txt
+++ b/libdl/libdl.map.txt
@@ -45,4 +45,5 @@
global:
android_get_LD_LIBRARY_PATH;
__cfi_init;
+ android_handle_signal;
} LIBC_OMR1;
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index af05027..a3f5246 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -28,8 +28,9 @@
#include "linker.h"
#include "linker_cfi.h"
-#include "linker_globals.h"
+#include "linker_debuggerd.h"
#include "linker_dlwarning.h"
+#include "linker_globals.h"
#include <link.h>
#include <pthread.h>
@@ -92,6 +93,8 @@
#if defined(__arm__)
_Unwind_Ptr __loader_dl_unwind_find_exidx(_Unwind_Ptr pc, int* pcount) __LINKER_PUBLIC__;
#endif
+bool __loader_android_handle_signal(int signal_number, siginfo_t* info,
+ void* context) __LINKER_PUBLIC__;
}
static pthread_mutex_t g_dl_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
@@ -302,6 +305,10 @@
return __libc_shared_globals();
}
+bool __loader_android_handle_signal(int signal_number, siginfo_t* info, void* context) {
+ return debuggerd_handle_signal(signal_number, info, context);
+}
+
static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8)));
static soinfo* __libdl_info = nullptr;
diff --git a/linker/ld_android.cpp b/linker/ld_android.cpp
index 0239c30..1c03106 100644
--- a/linker/ld_android.cpp
+++ b/linker/ld_android.cpp
@@ -44,6 +44,7 @@
__strong_alias(__loader_android_set_application_target_sdk_version, __internal_linker_error);
__strong_alias(__loader_android_update_LD_LIBRARY_PATH, __internal_linker_error);
__strong_alias(__loader_cfi_fail, __internal_linker_error);
+__strong_alias(__loader_android_handle_signal, __internal_linker_error);
__strong_alias(__loader_dl_iterate_phdr, __internal_linker_error);
__strong_alias(__loader_dladdr, __internal_linker_error);
__strong_alias(__loader_dlclose, __internal_linker_error);
diff --git a/linker/linker.arm.map b/linker/linker.arm.map
index be438ca..b805cd6 100644
--- a/linker/linker.arm.map
+++ b/linker/linker.arm.map
@@ -24,6 +24,7 @@
__loader_remove_thread_local_dtor;
__loader_shared_globals;
rtld_db_dlactivity;
+ __loader_android_handle_signal;
local:
*;
};
diff --git a/linker/linker.generic.map b/linker/linker.generic.map
index f3c01c0..4d7f236 100644
--- a/linker/linker.generic.map
+++ b/linker/linker.generic.map
@@ -23,6 +23,7 @@
__loader_remove_thread_local_dtor;
__loader_shared_globals;
rtld_db_dlactivity;
+ __loader_android_handle_signal;
local:
*;
};
diff --git a/linker/linker_debuggerd.h b/linker/linker_debuggerd.h
index d701879..95f99e7 100644
--- a/linker/linker_debuggerd.h
+++ b/linker/linker_debuggerd.h
@@ -28,4 +28,7 @@
#pragma once
+#include <signal.h>
+
void linker_debuggerd_init();
+extern "C" bool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context);
diff --git a/linker/linker_debuggerd_android.cpp b/linker/linker_debuggerd_android.cpp
index 3d64628..ab6fc30 100644
--- a/linker/linker_debuggerd_android.cpp
+++ b/linker/linker_debuggerd_android.cpp
@@ -46,6 +46,17 @@
.scudo_ring_buffer_size = __libc_shared_globals()->scudo_ring_buffer_size,
};
}
+
+static gwp_asan_callbacks_t get_gwp_asan_callbacks() {
+ return {
+ .debuggerd_needs_gwp_asan_recovery =
+ __libc_shared_globals()->debuggerd_needs_gwp_asan_recovery,
+ .debuggerd_gwp_asan_pre_crash_report =
+ __libc_shared_globals()->debuggerd_gwp_asan_pre_crash_report,
+ .debuggerd_gwp_asan_post_crash_report =
+ __libc_shared_globals()->debuggerd_gwp_asan_post_crash_report,
+ };
+}
#endif
void linker_debuggerd_init() {
@@ -53,9 +64,10 @@
// so don't pass in any process info from the bootstrap linker.
debuggerd_callbacks_t callbacks = {
#if defined(__ANDROID_APEX__)
- .get_process_info = get_process_info,
+ .get_process_info = get_process_info,
+ .get_gwp_asan_callbacks = get_gwp_asan_callbacks,
#endif
- .post_dump = notify_gdb_of_libraries,
+ .post_dump = notify_gdb_of_libraries,
};
debuggerd_init(&callbacks);
}
diff --git a/linker/linker_debuggerd_stub.cpp b/linker/linker_debuggerd_stub.cpp
index 631e6e4..c671dd9 100644
--- a/linker/linker_debuggerd_stub.cpp
+++ b/linker/linker_debuggerd_stub.cpp
@@ -28,5 +28,11 @@
#include "linker_debuggerd.h"
+#include <signal.h>
+
void linker_debuggerd_init() {
}
+extern "C" bool debuggerd_handle_signal(int /* signal_number */, siginfo_t* /* info */,
+ void* /* context */) {
+ return false;
+}