diff options
| -rw-r--r-- | src/internal/fork_impl.h | 2 | ||||
| -rw-r--r-- | src/linux/clone.c | 56 | ||||
| -rw-r--r-- | src/process/_Fork.c | 26 | 
3 files changed, 68 insertions, 16 deletions
| 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..257c1cec 100644 --- a/src/linux/clone.c +++ b/src/linux/clone.c @@ -4,18 +4,62 @@  #include <sched.h>  #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) || !stack) +		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 CLONE_VM is used, it's impossible to give the child a consistent +	 * thread structure. In this case, the best we can do is assume the +	 * caller is content with an extremely restrictive execution context +	 * like the one vfork() would provide. */ +	if (flags & CLONE_VM) return __syscall_ret( +		__clone(func, stack, flags, arg, ptid, tls, ctid)); + +	__block_all_sigs(&csa.sigmask); +	LOCK(__abort_lock); + +	/* Setup the a wrapper start function for the child process to do +	 * mimic _Fork in producing a consistent execution state. */ +	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 e7650863..9c07792d 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_set_tid_address, &__thread_list_lock); @@ -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);  } | 
