diff --git a/src/internal/fork_impl.h b/src/internal/fork_impl.h index 354e733b..f995fce2 100644 --- a/src/internal/fork_impl.h +++ b/src/internal/fork_impl.h @@ -17,3 +17,5 @@ extern hidden volatile int *const __vmlock_lockptr; hidden void __malloc_atfork(int); hidden void __ldso_atfork(int); hidden void __pthread_key_atfork(int); + +hidden void __post_Fork(int); diff --git a/src/linux/clone.c b/src/linux/clone.c index 8c1af7d3..e8f76bbc 100644 --- a/src/linux/clone.c +++ b/src/linux/clone.c @@ -4,18 +4,55 @@ #include #include "pthread_impl.h" #include "syscall.h" +#include "lock.h" +#include "fork_impl.h" + +struct clone_start_args { + int (*func)(void *); + void *arg; + sigset_t sigmask; +}; + +static int clone_start(void *arg) +{ + struct clone_start_args *csa = arg; + __post_Fork(0); + __restore_sigs(&csa->sigmask); + return csa->func(csa->arg); +} int clone(int (*func)(void *), void *stack, int flags, void *arg, ...) { + struct clone_start_args csa; va_list ap; - pid_t *ptid, *ctid; - void *tls; + pid_t *ptid = 0, *ctid = 0; + void *tls = 0; + + /* Flags that produce an invalid thread/TLS state are disallowed. */ + int badflags = CLONE_THREAD | CLONE_SETTLS | CLONE_CHILD_CLEARTID; + if (flags & badflags) + return __syscall_ret(-EINVAL); va_start(ap, arg); - ptid = va_arg(ap, pid_t *); - tls = va_arg(ap, void *); - ctid = va_arg(ap, pid_t *); + if (flags & (CLONE_PIDFD | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID)) + ptid = va_arg(ap, pid_t *); + if (flags & CLONE_CHILD_SETTID) { + tls = va_arg(ap, void *); + ctid = va_arg(ap, pid_t *); + } va_end(ap); - return __syscall_ret(__clone(func, stack, flags, arg, ptid, tls, ctid)); + if (flags & CLONE_VM) + return __syscall_ret(__clone(func, stack, flags, arg, ptid, tls, ctid)); + + __block_all_sigs(&csa.sigmask); + LOCK(__abort_lock); + + csa.func = func; + csa.arg = arg; + int ret = __clone(clone_start, stack, flags, &csa, ptid, tls, ctid); + + __post_Fork(ret); + __restore_sigs(&csa.sigmask); + return __syscall_ret(ret); } diff --git a/src/process/_Fork.c b/src/process/_Fork.c index fb0fdc2c..94906103 100644 --- a/src/process/_Fork.c +++ b/src/process/_Fork.c @@ -5,21 +5,13 @@ #include "lock.h" #include "pthread_impl.h" #include "aio_impl.h" +#include "fork_impl.h" static void dummy(int x) { } weak_alias(dummy, __aio_atfork); -pid_t _Fork(void) +void __post_Fork(int ret) { - pid_t ret; - sigset_t set; - __block_all_sigs(&set); - LOCK(__abort_lock); -#ifdef SYS_fork - ret = __syscall(SYS_fork); -#else - ret = __syscall(SYS_clone, SIGCHLD, 0); -#endif if (!ret) { pthread_t self = __pthread_self(); self->tid = __syscall(SYS_gettid); @@ -32,6 +24,20 @@ pid_t _Fork(void) } UNLOCK(__abort_lock); if (!ret) __aio_atfork(1); +} + +pid_t _Fork(void) +{ + pid_t ret; + sigset_t set; + __block_all_sigs(&set); + LOCK(__abort_lock); +#ifdef SYS_fork + ret = __syscall(SYS_fork); +#else + ret = __syscall(SYS_clone, SIGCHLD, 0); +#endif + __post_Fork(ret); __restore_sigs(&set); return __syscall_ret(ret); }