posix_spawn.c raw

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