diff options
Diffstat (limited to 'src/process')
-rw-r--r-- | src/process/_Fork.c | 43 | ||||
-rw-r--r-- | src/process/aarch64/vfork.s | 9 | ||||
-rw-r--r-- | src/process/fdop.h | 5 | ||||
-rw-r--r-- | src/process/fork.c | 97 | ||||
-rw-r--r-- | src/process/posix_spawn.c | 23 | ||||
-rw-r--r-- | src/process/posix_spawn_file_actions_addclose.c | 1 | ||||
-rw-r--r-- | src/process/posix_spawn_file_actions_adddup2.c | 1 | ||||
-rw-r--r-- | src/process/posix_spawn_file_actions_addfchdir.c | 1 | ||||
-rw-r--r-- | src/process/posix_spawn_file_actions_addopen.c | 1 | ||||
-rw-r--r-- | src/process/riscv64/vfork.s | 12 | ||||
-rw-r--r-- | src/process/waitpid.c | 2 |
11 files changed, 168 insertions, 27 deletions
diff --git a/src/process/_Fork.c b/src/process/_Fork.c new file mode 100644 index 00000000..9c07792d --- /dev/null +++ b/src/process/_Fork.c @@ -0,0 +1,43 @@ +#include <unistd.h> +#include <signal.h> +#include "syscall.h" +#include "libc.h" +#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); + +void __post_Fork(int ret) +{ + if (!ret) { + pthread_t self = __pthread_self(); + self->tid = __syscall(SYS_set_tid_address, &__thread_list_lock); + self->robust_list.off = 0; + self->robust_list.pending = 0; + self->next = self->prev = self; + __thread_list_lock = 0; + libc.threads_minus_1 = 0; + if (libc.need_locks) libc.need_locks = -1; + } + 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); +} diff --git a/src/process/aarch64/vfork.s b/src/process/aarch64/vfork.s new file mode 100644 index 00000000..429bec8c --- /dev/null +++ b/src/process/aarch64/vfork.s @@ -0,0 +1,9 @@ +.global vfork +.type vfork,%function +vfork: + mov x8, 220 // SYS_clone + mov x0, 0x4111 // SIGCHLD | CLONE_VM | CLONE_VFORK + mov x1, 0 + svc 0 + .hidden __syscall_ret + b __syscall_ret diff --git a/src/process/fdop.h b/src/process/fdop.h index 5adf1443..7cf733b2 100644 --- a/src/process/fdop.h +++ b/src/process/fdop.h @@ -10,3 +10,8 @@ struct fdop { mode_t mode; char path[]; }; + +#define malloc __libc_malloc +#define calloc __libc_calloc +#define realloc undef +#define free __libc_free diff --git a/src/process/fork.c b/src/process/fork.c index fb42478a..56f19313 100644 --- a/src/process/fork.c +++ b/src/process/fork.c @@ -1,37 +1,90 @@ #include <unistd.h> -#include <string.h> -#include <signal.h> -#include "syscall.h" +#include <errno.h> #include "libc.h" +#include "lock.h" #include "pthread_impl.h" +#include "fork_impl.h" -static void dummy(int x) -{ -} +static volatile int *const dummy_lockptr = 0; + +weak_alias(dummy_lockptr, __at_quick_exit_lockptr); +weak_alias(dummy_lockptr, __atexit_lockptr); +weak_alias(dummy_lockptr, __gettext_lockptr); +weak_alias(dummy_lockptr, __locale_lockptr); +weak_alias(dummy_lockptr, __random_lockptr); +weak_alias(dummy_lockptr, __sem_open_lockptr); +weak_alias(dummy_lockptr, __stdio_ofl_lockptr); +weak_alias(dummy_lockptr, __syslog_lockptr); +weak_alias(dummy_lockptr, __timezone_lockptr); +weak_alias(dummy_lockptr, __bump_lockptr); + +weak_alias(dummy_lockptr, __vmlock_lockptr); +static volatile int *const *const atfork_locks[] = { + &__at_quick_exit_lockptr, + &__atexit_lockptr, + &__gettext_lockptr, + &__locale_lockptr, + &__random_lockptr, + &__sem_open_lockptr, + &__stdio_ofl_lockptr, + &__syslog_lockptr, + &__timezone_lockptr, + &__bump_lockptr, +}; + +static void dummy(int x) { } weak_alias(dummy, __fork_handler); +weak_alias(dummy, __malloc_atfork); +weak_alias(dummy, __aio_atfork); +weak_alias(dummy, __pthread_key_atfork); +weak_alias(dummy, __ldso_atfork); + +static void dummy_0(void) { } +weak_alias(dummy_0, __tl_lock); +weak_alias(dummy_0, __tl_unlock); pid_t fork(void) { - pid_t ret; sigset_t set; __fork_handler(-1); - __block_all_sigs(&set); -#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); - self->robust_list.off = 0; - self->robust_list.pending = 0; - self->next = self->prev = self; - __thread_list_lock = 0; - libc.threads_minus_1 = 0; + __block_app_sigs(&set); + int need_locks = libc.need_locks > 0; + if (need_locks) { + __ldso_atfork(-1); + __pthread_key_atfork(-1); + __aio_atfork(-1); + __inhibit_ptc(); + for (int i=0; i<sizeof atfork_locks/sizeof *atfork_locks; i++) + if (*atfork_locks[i]) LOCK(*atfork_locks[i]); + __malloc_atfork(-1); + __tl_lock(); + } + pthread_t self=__pthread_self(), next=self->next; + pid_t ret = _Fork(); + int errno_save = errno; + if (need_locks) { + if (!ret) { + for (pthread_t td=next; td!=self; td=td->next) + td->tid = -1; + if (__vmlock_lockptr) { + __vmlock_lockptr[0] = 0; + __vmlock_lockptr[1] = 0; + } + } + __tl_unlock(); + __malloc_atfork(!ret); + for (int i=0; i<sizeof atfork_locks/sizeof *atfork_locks; i++) + if (*atfork_locks[i]) + if (ret) UNLOCK(*atfork_locks[i]); + else **atfork_locks[i] = 0; + __release_ptc(); + if (ret) __aio_atfork(0); + __pthread_key_atfork(!ret); + __ldso_atfork(!ret); } __restore_sigs(&set); __fork_handler(!ret); - return __syscall_ret(ret); + if (ret<0) errno = errno_save; + return ret; } diff --git a/src/process/posix_spawn.c b/src/process/posix_spawn.c index 29652197..8294598b 100644 --- a/src/process/posix_spawn.c +++ b/src/process/posix_spawn.c @@ -4,8 +4,10 @@ #include <unistd.h> #include <signal.h> #include <fcntl.h> +#include <errno.h> #include <sys/wait.h> #include "syscall.h" +#include "lock.h" #include "pthread_impl.h" #include "fdop.h" @@ -155,7 +157,11 @@ static int child(void *args_vp) fail: /* Since sizeof errno < PIPE_BUF, the write is atomic. */ ret = -ret; - if (ret) while (__syscall(SYS_write, p, &ret, sizeof ret) < 0); + if (ret) { + int r; + do r = __syscall(SYS_write, p, &ret, sizeof ret); + while (r<0 && r!=-EPIPE); + } _exit(127); } @@ -170,9 +176,6 @@ int posix_spawn(pid_t *restrict res, const char *restrict path, int ec=0, cs; struct args args; - if (pipe2(args.p, O_CLOEXEC)) - return errno; - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); args.path = path; @@ -182,9 +185,20 @@ int posix_spawn(pid_t *restrict res, const char *restrict path, args.envp = envp; pthread_sigmask(SIG_BLOCK, SIGALL_SET, &args.oldmask); + /* The lock guards both against seeing a SIGABRT disposition change + * by abort and against leaking the pipe fd to fork-without-exec. */ + LOCK(__abort_lock); + + if (pipe2(args.p, O_CLOEXEC)) { + UNLOCK(__abort_lock); + ec = errno; + goto fail; + } + pid = __clone(child, stack+sizeof stack, CLONE_VM|CLONE_VFORK|SIGCHLD, &args); close(args.p[1]); + UNLOCK(__abort_lock); if (pid > 0) { if (read(args.p[0], &ec, sizeof ec) != sizeof ec) ec = 0; @@ -197,6 +211,7 @@ int posix_spawn(pid_t *restrict res, const char *restrict path, if (!ec && res) *res = pid; +fail: pthread_sigmask(SIG_SETMASK, &args.oldmask, 0); pthread_setcancelstate(cs, 0); diff --git a/src/process/posix_spawn_file_actions_addclose.c b/src/process/posix_spawn_file_actions_addclose.c index cdda5979..0c2ef8fa 100644 --- a/src/process/posix_spawn_file_actions_addclose.c +++ b/src/process/posix_spawn_file_actions_addclose.c @@ -5,6 +5,7 @@ int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa, int fd) { + if (fd < 0) return EBADF; struct fdop *op = malloc(sizeof *op); if (!op) return ENOMEM; op->cmd = FDOP_CLOSE; diff --git a/src/process/posix_spawn_file_actions_adddup2.c b/src/process/posix_spawn_file_actions_adddup2.c index 0367498f..addca4d4 100644 --- a/src/process/posix_spawn_file_actions_adddup2.c +++ b/src/process/posix_spawn_file_actions_adddup2.c @@ -5,6 +5,7 @@ int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa, int srcfd, int fd) { + if (srcfd < 0 || fd < 0) return EBADF; struct fdop *op = malloc(sizeof *op); if (!op) return ENOMEM; op->cmd = FDOP_DUP2; diff --git a/src/process/posix_spawn_file_actions_addfchdir.c b/src/process/posix_spawn_file_actions_addfchdir.c index 436c683d..e89ede8c 100644 --- a/src/process/posix_spawn_file_actions_addfchdir.c +++ b/src/process/posix_spawn_file_actions_addfchdir.c @@ -6,6 +6,7 @@ int posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *fa, int fd) { + if (fd < 0) return EBADF; struct fdop *op = malloc(sizeof *op); if (!op) return ENOMEM; op->cmd = FDOP_FCHDIR; diff --git a/src/process/posix_spawn_file_actions_addopen.c b/src/process/posix_spawn_file_actions_addopen.c index 368922c7..82bbcec9 100644 --- a/src/process/posix_spawn_file_actions_addopen.c +++ b/src/process/posix_spawn_file_actions_addopen.c @@ -6,6 +6,7 @@ int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *restrict fa, int fd, const char *restrict path, int flags, mode_t mode) { + if (fd < 0) return EBADF; struct fdop *op = malloc(sizeof *op + strlen(path) + 1); if (!op) return ENOMEM; op->cmd = FDOP_OPEN; diff --git a/src/process/riscv64/vfork.s b/src/process/riscv64/vfork.s new file mode 100644 index 00000000..c93dca23 --- /dev/null +++ b/src/process/riscv64/vfork.s @@ -0,0 +1,12 @@ +.global vfork +.type vfork,@function +vfork: + /* riscv does not have SYS_vfork, so we must use clone instead */ + /* note: riscv's clone = clone(flags, sp, ptidptr, tls, ctidptr) */ + li a7, 220 + li a0, 0x100 | 0x4000 | 17 /* flags = CLONE_VM | CLONE_VFORK | SIGCHLD */ + mv a1, sp + /* the other arguments are ignoreable */ + ecall + .hidden __syscall_ret + j __syscall_ret diff --git a/src/process/waitpid.c b/src/process/waitpid.c index 1b65bf05..80231862 100644 --- a/src/process/waitpid.c +++ b/src/process/waitpid.c @@ -3,5 +3,5 @@ pid_t waitpid(pid_t pid, int *status, int options) { - return syscall_cp(SYS_wait4, pid, status, options, 0); + return sys_wait4_cp(pid, status, options, 0); } |