about summary refs log tree commit diff
path: root/src/process
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2023-05-31 12:04:06 -0400
committerRich Felker <dalias@aerifal.cx>2023-06-01 16:15:38 -0400
commitfa4a8abd06a401822cc8ba4e352a219544c0118d (patch)
tree064926e9f147c7d20e3294dd869b3c959567f817 /src/process
parent0c277ff156749628c678257f878d3a6edb5868de (diff)
downloadmusl-fa4a8abd06a401822cc8ba4e352a219544c0118d.tar.gz
musl-fa4a8abd06a401822cc8ba4e352a219544c0118d.tar.xz
musl-fa4a8abd06a401822cc8ba4e352a219544c0118d.zip
fix public clone function to be safe and usable by applications
the clone() function has been effectively unusable since it was added,
due to producing a child process with inconsistent state. in
particular, the child process's thread structure still contains the
tid, thread list pointers, thread count, and robust list for the
parent. this will cause malfunction in interfaces that attempt to use
the tid or thread list, some of which are specified to be
async-signal-safe.

this patch attempts to make clone() consistent in a _Fork-like sense.
as in _Fork, when the parent process is multi-threaded, the child
process inherits an async-signal context where it cannot call
AS-unsafe functions, but its context is now intended to be safe for
calling AS-safe functions. making clone fork-like would also be a
future option, if it turns out that this is what makes sense to
applications, but it's not done at this time because the changes would
be more invasive.

in the case where the CLONE_VM flag is used, clone is only vfork-like,
not _Fork-like. in particular, the child will see itself as having the
parent's tid, and cannot safely call any libc functions but one of the
exec family or _exit.

handling of flags and variadic arguments is also changed so that
arguments are only consumed with flags that indicate their presence,
and so that flags which produce an inconsistent state are disallowed
(reported as EINVAL). in particular, all libc functions carry a
contract that they are only callable with ABI requirements met, which
includes having a valid thread pointer to a thread structure that's
unique within the process, and whose contents are opaque and only able
to be setup internally by the implementation. the only way for an
application to use flags that violate these requirements without
executing any libc code is to perform the syscall from
application-provided asm.
Diffstat (limited to 'src/process')
-rw-r--r--src/process/_Fork.c26
1 files changed, 16 insertions, 10 deletions
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);
 }