Hooks: Protect against reentrancy.
Previously, we made the user deal with this themselves, but that's not good enough; if hooks may allocate, we should test the allocation pathways down hooks. If we're doing that, we might as well actually implement the protection for the user.
This commit is contained in:
committed by
David Goldblatt
parent
0379235f47
commit
a7f749c9af
68
src/hook.c
68
src/hook.c
@@ -99,12 +99,62 @@ for (int for_each_hook_counter = 0; \
|
||||
#define FOR_EACH_HOOK_END \
|
||||
}
|
||||
|
||||
static bool *
|
||||
hook_reentrantp() {
|
||||
/*
|
||||
* We prevent user reentrancy within hooks. This is basically just a
|
||||
* thread-local bool that triggers an early-exit.
|
||||
*
|
||||
* We don't fold in_hook into reentrancy. There are two reasons for
|
||||
* this:
|
||||
* - Right now, we turn on reentrancy during things like extent hook
|
||||
* execution. Allocating during extent hooks is not officially
|
||||
* supported, but we don't want to break it for the time being. These
|
||||
* sorts of allocations should probably still be hooked, though.
|
||||
* - If a hook allocates, we may want it to be relatively fast (after
|
||||
* all, it executes on every allocator operation). Turning on
|
||||
* reentrancy is a fairly heavyweight mode (disabling tcache,
|
||||
* redirecting to arena 0, etc.). It's possible we may one day want
|
||||
* to turn on reentrant mode here, if it proves too difficult to keep
|
||||
* this working. But that's fairly easy for us to see; OTOH, people
|
||||
* not using hooks because they're too slow is easy for us to miss.
|
||||
*
|
||||
* The tricky part is
|
||||
* that this code might get invoked even if we don't have access to tsd.
|
||||
* This function mimics getting a pointer to thread-local data, except
|
||||
* that it might secretly return a pointer to some global data if we
|
||||
* know that the caller will take the early-exit path.
|
||||
* If we return a bool that indicates that we are reentrant, then the
|
||||
* caller will go down the early exit path, leaving the global
|
||||
* untouched.
|
||||
*/
|
||||
static bool in_hook_global = true;
|
||||
tsdn_t *tsdn = tsdn_fetch();
|
||||
bool *in_hook = tsdn_in_hookp_get(tsdn);
|
||||
if (in_hook != NULL) {
|
||||
return in_hook;
|
||||
}
|
||||
return &in_hook_global;
|
||||
}
|
||||
|
||||
#define HOOK_PROLOGUE \
|
||||
if (likely(atomic_load_u(&nhooks, ATOMIC_RELAXED) == 0)) { \
|
||||
return; \
|
||||
} \
|
||||
bool *in_hook = hook_reentrantp(); \
|
||||
if (*in_hook) { \
|
||||
return; \
|
||||
} \
|
||||
*in_hook = true;
|
||||
|
||||
#define HOOK_EPILOGUE \
|
||||
*in_hook = false;
|
||||
|
||||
void
|
||||
hook_invoke_alloc(hook_alloc_t type, void *result, uintptr_t result_raw,
|
||||
uintptr_t args_raw[3]) {
|
||||
if (likely(atomic_load_u(&nhooks, ATOMIC_RELAXED) == 0)) {
|
||||
return;
|
||||
}
|
||||
HOOK_PROLOGUE
|
||||
|
||||
hooks_internal_t hook;
|
||||
FOR_EACH_HOOK_BEGIN(&hook)
|
||||
hook_alloc h = hook.hooks.alloc_hook;
|
||||
@@ -112,13 +162,13 @@ hook_invoke_alloc(hook_alloc_t type, void *result, uintptr_t result_raw,
|
||||
h(hook.hooks.extra, type, result, result_raw, args_raw);
|
||||
}
|
||||
FOR_EACH_HOOK_END
|
||||
|
||||
HOOK_EPILOGUE
|
||||
}
|
||||
|
||||
void
|
||||
hook_invoke_dalloc(hook_dalloc_t type, void *address, uintptr_t args_raw[3]) {
|
||||
if (likely(atomic_load_u(&nhooks, ATOMIC_RELAXED) == 0)) {
|
||||
return;
|
||||
}
|
||||
HOOK_PROLOGUE
|
||||
hooks_internal_t hook;
|
||||
FOR_EACH_HOOK_BEGIN(&hook)
|
||||
hook_dalloc h = hook.hooks.dalloc_hook;
|
||||
@@ -126,14 +176,13 @@ hook_invoke_dalloc(hook_dalloc_t type, void *address, uintptr_t args_raw[3]) {
|
||||
h(hook.hooks.extra, type, address, args_raw);
|
||||
}
|
||||
FOR_EACH_HOOK_END
|
||||
HOOK_EPILOGUE
|
||||
}
|
||||
|
||||
void
|
||||
hook_invoke_expand(hook_expand_t type, void *address, size_t old_usize,
|
||||
size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]) {
|
||||
if (likely(atomic_load_u(&nhooks, ATOMIC_RELAXED) == 0)) {
|
||||
return;
|
||||
}
|
||||
HOOK_PROLOGUE
|
||||
hooks_internal_t hook;
|
||||
FOR_EACH_HOOK_BEGIN(&hook)
|
||||
hook_expand h = hook.hooks.expand_hook;
|
||||
@@ -142,4 +191,5 @@ hook_invoke_expand(hook_expand_t type, void *address, size_t old_usize,
|
||||
result_raw, args_raw);
|
||||
}
|
||||
FOR_EACH_HOOK_END
|
||||
HOOK_EPILOGUE
|
||||
}
|
||||
|
Reference in New Issue
Block a user