master
  1#define _GNU_SOURCE
  2#include <spawn.h>
  3#include <sched.h>
  4#include <unistd.h>
  5#include <signal.h>
  6#include <fcntl.h>
  7#include <errno.h>
  8#include <sys/wait.h>
  9#include "syscall.h"
 10#include "lock.h"
 11#include "pthread_impl.h"
 12#include "fdop.h"
 13
 14struct args {
 15	int p[2];
 16	sigset_t oldmask;
 17	const char *path;
 18	const posix_spawn_file_actions_t *fa;
 19	const posix_spawnattr_t *restrict attr;
 20	char *const *argv, *const *envp;
 21};
 22
 23static int __sys_dup2(int old, int new)
 24{
 25#ifdef SYS_dup2
 26	return __syscall(SYS_dup2, old, new);
 27#else
 28	return __syscall(SYS_dup3, old, new, 0);
 29#endif
 30}
 31
 32static int child(void *args_vp)
 33{
 34	int i, ret;
 35	struct sigaction sa = {0};
 36	struct args *args = args_vp;
 37	int p = args->p[1];
 38	const posix_spawn_file_actions_t *fa = args->fa;
 39	const posix_spawnattr_t *restrict attr = args->attr;
 40	sigset_t hset;
 41
 42	close(args->p[0]);
 43
 44	/* All signal dispositions must be either SIG_DFL or SIG_IGN
 45	 * before signals are unblocked. Otherwise a signal handler
 46	 * from the parent might get run in the child while sharing
 47	 * memory, with unpredictable and dangerous results. To
 48	 * reduce overhead, sigaction has tracked for us which signals
 49	 * potentially have a signal handler. */
 50	__get_handler_set(&hset);
 51	for (i=1; i<_NSIG; i++) {
 52		if ((attr->__flags & POSIX_SPAWN_SETSIGDEF)
 53		     && sigismember(&attr->__def, i)) {
 54			sa.sa_handler = SIG_DFL;
 55		} else if (sigismember(&hset, i)) {
 56			if (i-32<3U) {
 57				sa.sa_handler = SIG_IGN;
 58			} else {
 59				__libc_sigaction(i, 0, &sa);
 60				if (sa.sa_handler==SIG_IGN) continue;
 61				sa.sa_handler = SIG_DFL;
 62			}
 63		} else {
 64			continue;
 65		}
 66		__libc_sigaction(i, &sa, 0);
 67	}
 68
 69	if (attr->__flags & POSIX_SPAWN_SETSID)
 70		if ((ret=__syscall(SYS_setsid)) < 0)
 71			goto fail;
 72
 73	if (attr->__flags & POSIX_SPAWN_SETPGROUP)
 74		if ((ret=__syscall(SYS_setpgid, 0, attr->__pgrp)))
 75			goto fail;
 76
 77	/* Use syscalls directly because the library functions attempt
 78	 * to do a multi-threaded synchronized id-change, which would
 79	 * trash the parent's state. */
 80	if (attr->__flags & POSIX_SPAWN_RESETIDS)
 81		if ((ret=__syscall(SYS_setgid, __syscall(SYS_getgid))) ||
 82		    (ret=__syscall(SYS_setuid, __syscall(SYS_getuid))) )
 83			goto fail;
 84
 85	if (fa && fa->__actions) {
 86		struct fdop *op;
 87		int fd;
 88		for (op = fa->__actions; op->next; op = op->next);
 89		for (; op; op = op->prev) {
 90			/* It's possible that a file operation would clobber
 91			 * the pipe fd used for synchronizing with the
 92			 * parent. To avoid that, we dup the pipe onto
 93			 * an unoccupied fd. */
 94			if (op->fd == p) {
 95				ret = __syscall(SYS_dup, p);
 96				if (ret < 0) goto fail;
 97				__syscall(SYS_close, p);
 98				p = ret;
 99			}
100			switch(op->cmd) {
101			case FDOP_CLOSE:
102				__syscall(SYS_close, op->fd);
103				break;
104			case FDOP_DUP2:
105				fd = op->srcfd;
106				if (fd == p) {
107					ret = -EBADF;
108					goto fail;
109				}
110				if (fd != op->fd) {
111					if ((ret=__sys_dup2(fd, op->fd))<0)
112						goto fail;
113				} else {
114					ret = __syscall(SYS_fcntl, fd, F_GETFD);
115					ret = __syscall(SYS_fcntl, fd, F_SETFD,
116					                ret & ~FD_CLOEXEC);
117					if (ret<0)
118						goto fail;
119				}
120				break;
121			case FDOP_OPEN:
122				fd = __sys_open(op->path, op->oflag, op->mode);
123				if ((ret=fd) < 0) goto fail;
124				if (fd != op->fd) {
125					if ((ret=__sys_dup2(fd, op->fd))<0)
126						goto fail;
127					__syscall(SYS_close, fd);
128				}
129				break;
130			case FDOP_CHDIR:
131				ret = __syscall(SYS_chdir, op->path);
132				if (ret<0) goto fail;
133				break;
134			case FDOP_FCHDIR:
135				ret = __syscall(SYS_fchdir, op->fd);
136				if (ret<0) goto fail;
137				break;
138			}
139		}
140	}
141
142	/* Close-on-exec flag may have been lost if we moved the pipe
143	 * to a different fd. We don't use F_DUPFD_CLOEXEC above because
144	 * it would fail on older kernels and atomicity is not needed --
145	 * in this process there are no threads or signal handlers. */
146	__syscall(SYS_fcntl, p, F_SETFD, FD_CLOEXEC);
147
148	pthread_sigmask(SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK)
149		? &attr->__mask : &args->oldmask, 0);
150
151	int (*exec)(const char *, char *const *, char *const *) =
152		attr->__fn ? (int (*)())attr->__fn : execve;
153
154	exec(args->path, args->argv, args->envp);
155	ret = -errno;
156
157fail:
158	/* Since sizeof errno < PIPE_BUF, the write is atomic. */
159	ret = -ret;
160	if (ret) {
161		int r;
162		do r = __syscall(SYS_write, p, &ret, sizeof ret);
163		while (r<0 && r!=-EPIPE);
164	}
165	_exit(127);
166}
167
168
169int posix_spawn(pid_t *restrict res, const char *restrict path,
170	const posix_spawn_file_actions_t *fa,
171	const posix_spawnattr_t *restrict attr,
172	char *const argv[restrict], char *const envp[restrict])
173{
174	pid_t pid;
175	char stack[1024+PATH_MAX];
176	int ec=0, cs;
177	struct args args;
178
179	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
180
181	args.path = path;
182	args.fa = fa;
183	args.attr = attr ? attr : &(const posix_spawnattr_t){0};
184	args.argv = argv;
185	args.envp = envp;
186	pthread_sigmask(SIG_BLOCK, SIGALL_SET, &args.oldmask);
187
188	/* The lock guards both against seeing a SIGABRT disposition change
189	 * by abort and against leaking the pipe fd to fork-without-exec. */
190	LOCK(__abort_lock);
191
192	if (pipe2(args.p, O_CLOEXEC)) {
193		UNLOCK(__abort_lock);
194		ec = errno;
195		goto fail;
196	}
197
198	pid = __clone(child, stack+sizeof stack,
199		CLONE_VM|CLONE_VFORK|SIGCHLD, &args);
200	close(args.p[1]);
201	UNLOCK(__abort_lock);
202
203	if (pid > 0) {
204		if (read(args.p[0], &ec, sizeof ec) != sizeof ec) ec = 0;
205		else waitpid(pid, &(int){0}, 0);
206	} else {
207		ec = -pid;
208	}
209
210	close(args.p[0]);
211
212	if (!ec && res) *res = pid;
213
214fail:
215	pthread_sigmask(SIG_SETMASK, &args.oldmask, 0);
216	pthread_setcancelstate(cs, 0);
217
218	return ec;
219}