[GWP-ASan] Fix non-reentrant libc_globals init behaviour.

The WriteProtected mutator for __libc_globals isn't reentrant.
Previously we were calling __libc_globals.mutate() inside of GWP-ASan's
libc initialisation, which is called inside the __libc_globals.mutate().
This causes problems with malloc_debug and other malloc shims, as they
fail to install when GWP-ASan is sampling their processes.

Bug: 135634846
Test: atest bionic
Change-Id: Iae51faa8d78677eeab6204b6ab4f3ae1b7517ba5
This commit is contained in:
Mitch Phillips 2020-02-11 14:42:14 -08:00
parent d129786e12
commit bba80dcd80
4 changed files with 22 additions and 19 deletions

View File

@ -195,7 +195,7 @@ bool ShouldGwpAsanSampleProcess() {
return false;
}
bool MaybeInitGwpAsanFromLibc() {
bool MaybeInitGwpAsanFromLibc(libc_globals* globals) {
// Never initialize the Zygote here. A Zygote chosen for sampling would also
// have all of its children sampled. Instead, the Zygote child will choose
// whether it samples or not just after the Zygote forks. For
@ -205,14 +205,14 @@ bool MaybeInitGwpAsanFromLibc() {
if (progname && strncmp(progname, "app_process", 11) == 0) {
return false;
}
return MaybeInitGwpAsan(false);
return MaybeInitGwpAsan(globals);
}
static bool GwpAsanInitialized = false;
// Maybe initializes GWP-ASan. Called by android_mallopt() and libc's
// initialisation. This should always be called in a single-threaded context.
bool MaybeInitGwpAsan(bool force_init) {
bool MaybeInitGwpAsan(libc_globals* globals, bool force_init) {
if (GwpAsanInitialized) {
error_log("GWP-ASan was already initialized for this process.");
return false;
@ -239,17 +239,15 @@ bool MaybeInitGwpAsan(bool force_init) {
// GWP-ASan's initialization is always called in a single-threaded context, so
// we can initialize lock-free.
__libc_globals.mutate([](libc_globals* globals) {
// Set GWP-ASan as the malloc dispatch table.
globals->malloc_dispatch_table = gwp_asan_dispatch;
atomic_store(&globals->default_dispatch_table, &gwp_asan_dispatch);
// Set GWP-ASan as the malloc dispatch table.
globals->malloc_dispatch_table = gwp_asan_dispatch;
atomic_store(&globals->default_dispatch_table, &gwp_asan_dispatch);
// If malloc_limit isn't installed, we can skip the default_dispatch_table
// lookup.
if (GetDispatchTable() == nullptr) {
atomic_store(&globals->current_dispatch_table, &gwp_asan_dispatch);
}
});
// If malloc_limit isn't installed, we can skip the default_dispatch_table
// lookup.
if (GetDispatchTable() == nullptr) {
atomic_store(&globals->current_dispatch_table, &gwp_asan_dispatch);
}
#ifndef LIBC_STATIC
SetGlobalFunctions(gwp_asan_gfunctions);

View File

@ -28,11 +28,12 @@
#pragma once
#include <stddef.h>
#include <private/bionic_globals.h>
#include <private/bionic_malloc_dispatch.h>
#include <stddef.h>
// Hooks for libc to possibly install GWP-ASan.
bool MaybeInitGwpAsanFromLibc();
bool MaybeInitGwpAsanFromLibc(libc_globals* globals);
// Maybe initialize GWP-ASan. Set force_init to true to bypass process sampling.
bool MaybeInitGwpAsan(bool force_init = false);
bool MaybeInitGwpAsan(libc_globals* globals, bool force_init = false);

View File

@ -322,7 +322,9 @@ extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) {
errno = EINVAL;
return false;
}
return MaybeInitGwpAsan(*reinterpret_cast<bool*>(arg));
__libc_globals.mutate([&](libc_globals* globals) {
return MaybeInitGwpAsan(globals, *reinterpret_cast<bool*>(arg));
});
}
errno = ENOTSUP;
return false;

View File

@ -379,7 +379,7 @@ static void MallocInitImpl(libc_globals* globals) {
char prop[PROP_VALUE_MAX];
char* options = prop;
MaybeInitGwpAsanFromLibc();
MaybeInitGwpAsanFromLibc(globals);
// Prefer malloc debug since it existed first and is a more complete
// malloc interceptor than the hooks.
@ -529,7 +529,9 @@ extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) {
errno = EINVAL;
return false;
}
return MaybeInitGwpAsan(*reinterpret_cast<bool*>(arg));
__libc_globals.mutate([&](libc_globals* globals) {
return MaybeInitGwpAsan(globals, *reinterpret_cast<bool*>(arg));
});
}
// Try heapprofd's mallopt, as it handles options not covered here.
return HeapprofdMallopt(opcode, arg, arg_size);