diff options
author | Rich Felker <dalias@aerifal.cx> | 2020-11-11 13:37:33 -0500 |
---|---|---|
committer | Rich Felker <dalias@aerifal.cx> | 2020-11-11 15:55:30 -0500 |
commit | 167390f05564e0a4d3fcb4329377fd7743267560 (patch) | |
tree | 1c67cc35fe67d09532df7f23ea8d8a7611cfa00d /src/malloc/oldmalloc | |
parent | 34952fe5de44a833370cbe87b63fb8eec61466d7 (diff) | |
download | musl-167390f05564e0a4d3fcb4329377fd7743267560.tar.gz musl-167390f05564e0a4d3fcb4329377fd7743267560.tar.bz2 musl-167390f05564e0a4d3fcb4329377fd7743267560.tar.xz musl-167390f05564e0a4d3fcb4329377fd7743267560.zip |
lift child restrictions after multi-threaded fork
as the outcome of Austin Group tracker issue #62, future editions of
POSIX have dropped the requirement that fork be AS-safe. this allows
but does not require implementations to synchronize fork with internal
locks and give forked children of multithreaded parents a partly or
fully unrestricted execution environment where they can continue to
use the standard library (per POSIX, they can only portably use
AS-safe functions).
up until recently, taking this allowance did not seem desirable.
however, commit 8ed2bd8bfcb4ea6448afb55a941f4b5b2b0398c0 exposed the
extent to which applications and libraries are depending on the
ability to use malloc and other non-AS-safe interfaces in MT-forked
children, by converting latent very-low-probability catastrophic state
corruption into predictable deadlock. dealing with the fallout has
been a huge burden for users/distros.
while it looks like most of the non-portable usage in applications
could be fixed given sufficient effort, at least some of it seems to
occur in language runtimes which are exposing the ability to run
unrestricted code in the child as part of the contract with the
programmer. any attempt at fixing such contracts is not just a
technical problem but a social one, and is probably not tractable.
this patch extends the fork function to take locks for all libc
singletons in the parent, and release or reset those locks in the
child, so that when the underlying fork operation takes place, the
state protected by these locks is consistent and ready for the child
to use. locking is skipped in the case where the parent is
single-threaded so as not to interfere with legacy AS-safety property
of fork in single-threaded programs. lock order is mostly arbitrary,
but the malloc locks (including bump allocator in case it's used) must
be taken after the locks on any subsystems that might use malloc, and
non-AS-safe locks cannot be taken while the thread list lock is held,
imposing a requirement that it be taken last.
Diffstat (limited to 'src/malloc/oldmalloc')
-rw-r--r-- | src/malloc/oldmalloc/malloc.c | 19 |
1 files changed, 19 insertions, 0 deletions
diff --git a/src/malloc/oldmalloc/malloc.c b/src/malloc/oldmalloc/malloc.c index 0c082bce..53f5f959 100644 --- a/src/malloc/oldmalloc/malloc.c +++ b/src/malloc/oldmalloc/malloc.c @@ -9,6 +9,7 @@ #include "atomic.h" #include "pthread_impl.h" #include "malloc_impl.h" +#include "fork_impl.h" #define malloc __libc_malloc #define realloc __libc_realloc @@ -531,3 +532,21 @@ void __malloc_donate(char *start, char *end) c->csize = n->psize = C_INUSE | (end-start); __bin_chunk(c); } + +void __malloc_atfork(int who) +{ + if (who<0) { + lock(mal.split_merge_lock); + for (int i=0; i<64; i++) + lock(mal.bins[i].lock); + } else if (!who) { + for (int i=0; i<64; i++) + unlock(mal.bins[i].lock); + unlock(mal.split_merge_lock); + } else { + for (int i=0; i<64; i++) + mal.bins[i].lock[0] = mal.bins[i].lock[1] = 0; + mal.split_merge_lock[1] = 0; + mal.split_merge_lock[0] = 0; + } +} |