Cider Isn't Darwin Emulation, Really
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358#include "posix_spawn.h"#include "execve.h"#include "../base.h"#include "../errno.h"#include <linux-syscalls/linux.h>#include "../fcntl/open.h"#include "../unistd/read.h"#include "../unistd/write.h"#include "../unistd/exit.h"#include "../unistd/close.h"#include "../unistd/pipe.h"#include "../unistd/dup2.h"#include "../unistd/dup.h"#include "../unistd/getuid.h"#include "../unistd/getgid.h"#include "../unistd/setuid.h"#include "../unistd/setgid.h"#include "../unistd/setpgid.h"#include "../signal/sigprocmask.h"#include "../mach/lkm.h"#include "../simple.h"#include "lkm/api.h"#include "fork.h"#include <stddef.h>#include <stdint.h>#include <sys/spawn.h>#include "../unistd/chdir.h"#include "../unistd/fchdir.h"#include "../fcntl/fcntl.h"#include "../dirent/getdirentries.h"
// for debugging only; remove before committing#include "../signal/kill.h"#include "../unistd/getpid.h"
#ifndef _POSIX_SPAWN_DISABLE_ASLR#define _POSIX_SPAWN_DISABLE_ASLR 0x0100#endif
#define LINUX_ADDR_NO_RANDOMIZE 0x40000
long sys_posix_spawn(int* pid, const char* path, const struct _posix_spawn_args_desc* desc, char** argvp, char** envp){ int ret, my_pid; int pipe[2] = { -1, -1 };
if (desc && desc->attrp && desc->attrp->psa_flags & POSIX_SPAWN_SETEXEC) goto no_fork;
ret = LINUX_SYSCALL(__NR_pipe2, pipe, LINUX_O_CLOEXEC); if (ret < 0) return ret;
if ((my_pid = sys_fork()) == 0) { mach_driver_init(NULL);
// child // close the reading side close_internal(pipe[0]);
no_fork: if (desc && desc->attrp) { if (desc->attrp->psa_flags & POSIX_SPAWN_RESETIDS) { sys_setgid(sys_getgid()); sys_setuid(sys_getuid()); } if (desc->attrp->psa_flags & POSIX_SPAWN_SETPGROUP) { ret = sys_setpgid(0, desc->attrp->psa_pgroup); if (ret != 0) goto fail; } if (desc->attrp->psa_flags & POSIX_SPAWN_SETSIGMASK) { ret = sys_sigprocmask(3 /* SIG_SETMASK */, &desc->attrp->psa_sigmask, NULL); if (ret != 0) goto fail; } if (desc->attrp->psa_flags & _POSIX_SPAWN_DISABLE_ASLR) { unsigned int pers = LINUX_SYSCALL(__NR_personality, 0xffffffff); if (!(pers & LINUX_ADDR_NO_RANDOMIZE)) { pers |= LINUX_ADDR_NO_RANDOMIZE; LINUX_SYSCALL(__NR_personality, pers); } } if (desc->attrp->psa_flags & POSIX_SPAWN_START_SUSPENDED) { lkm_call(NR_stop_after_exec, NULL); }
if (desc->attrp->psa_flags & POSIX_SPAWN_CLOEXEC_DEFAULT) { // set O_CLOEXEC on everything int dir = sys_open("/proc/self/fd", BSD_O_RDONLY, 0); char buf[4096]; int len = 0;
//__simple_kprintf("asked for cloexec default\n");
if (dir < 0) { ret = dir; goto fail; }
while ((len = LINUX_SYSCALL(__NR_getdents64, dir, buf, sizeof(buf))) > 0) { struct linux_dirent64* dirent = (struct linux_dirent64*)&buf[0]; for (int i = 0; i < len; i += dirent->d_reclen, dirent = (struct linux_dirent64*)&buf[i]) { int fd = -1;
//__simple_kprintf("dentry name: %s\n", dirent->d_name); if (dirent->d_name[0] == '.') { continue; }
fd = __simple_atoi(dirent->d_name, NULL);
// FIXME: pretty sure this should not be hardcoded if (fd == 1023) { // special commpage fd continue; }
if (desc && desc->factp) { // if this FD is one that we're creating, don't set O_CLOEXEC on it for (size_t j = 0; j < desc->factp->psfa_act_count; j++) { const struct _psfa_action* act = &desc->factp->psfa_act_acts[j]; int maybe_fd = act->psfaa_filedes;
switch (act->psfaa_type) { case PSFA_DUP2: case PSFA_FILEPORT_DUP2: maybe_fd = act->psfaa_dup2args.psfad_newfiledes; // fall through
case PSFA_OPEN: case PSFA_INHERIT: if (fd == maybe_fd) { maybe_fd = -1; } break;
default: break; }
// maybe_fd was set to -1, indicating the descriptors matched if (maybe_fd == -1) { fd = -1; break; } } }
// fd was set to -1, indicating we should skip it if (fd == -1) { continue; }
//__simple_kprintf("setting cloexec on %d\n", fd);
ret = sys_fcntl(fd, F_GETFD, 0); if (ret < 0) { close_internal(dir); goto fail; }
ret = sys_fcntl(fd, F_SETFD, ret | FD_CLOEXEC); if (ret < 0) { close_internal(dir); goto fail; } } }
close_internal(dir);
if (len < 0) { ret = len; goto fail; } }
// TODO: other attributes } if (desc && desc->factp) { int i;
//__simple_kprintf("act count: %d\n", desc->factp->psfa_act_count);
//sys_kill(sys_getpid(), SIGSTOP, 0);
for (i = 0; i < desc->factp->psfa_act_count; i++) { const struct _psfa_action* act;
act = &desc->factp->psfa_act_acts[i];
//__simple_kprintf("act count (on iter %d): %d\n", i, desc->factp->psfa_act_count);
if (act->psfaa_filedes == pipe[1] || (act->psfaa_type == PSFA_DUP2 && act->psfaa_dup2args.psfad_newfiledes == pipe[1])) { ret = sys_dup(pipe[1]); if (ret < 0) goto fail;
close_internal(pipe[1]); pipe[1] = ret; }
switch (act->psfaa_type) { case PSFA_CLOSE: //__simple_kprintf("closing %d\n", act->psfaa_filedes); ret = close_internal(act->psfaa_filedes); if (ret != 0) goto fail; break;
case PSFA_DUP2: //__simple_kprintf("duping %d to %d\n", act->psfaa_filedes, act->psfaa_dup2args.psfad_newfiledes); ret = sys_dup2(act->psfaa_filedes, act->psfaa_dup2args.psfad_newfiledes); if (ret < 0) goto fail; break;
case PSFA_OPEN: { //__simple_kprintf("opening %s to %d\n", act->psfaa_openargs.psfao_path, act->psfaa_filedes); ret = sys_open(act->psfaa_openargs.psfao_path, act->psfaa_openargs.psfao_oflag, act->psfaa_openargs.psfao_mode); if (ret < 0) goto fail;
if (ret != act->psfaa_filedes) { int fd = ret; ret = sys_dup2(ret, act->psfaa_filedes);
if (ret < 0) { close_internal(fd); goto fail; } } break; }
case PSFA_CHDIR: { //__simple_kprintf("chdiring to %s\n", act->psfaa_chdirargs.psfac_path); ret = sys_chdir(act->psfaa_chdirargs.psfac_path); if (ret < 0) { goto fail; } } break;
case PSFA_FCHDIR: { //__simple_kprintf("fchdiring to %d\n", act->psfaa_filedes); ret = sys_fchdir(act->psfaa_filedes); if (ret < 0) { goto fail; } } break;
// unset CLOEXEC on this fd case PSFA_INHERIT: { //__simple_kprintf("inheriting %d\n", act->psfaa_filedes); ret = sys_fcntl(act->psfaa_filedes, F_GETFD, 0); if (ret < 0) { goto fail; } ret = sys_fcntl(act->psfaa_filedes, F_SETFD, ret & ~FD_CLOEXEC); if (ret < 0) { goto fail; } } break;
default: //__simple_kprintf("unknown PSFA type %d\n", act->psfaa_type); ; } }
//__simple_kprintf("act count (on finish): %d\n", desc->factp->psfa_act_count); }
char binprefs[64]; if (desc && desc->attrp && desc->attrp->psa_binprefs[0]) { char** new_envp; int i, env_len = 0;
while (envp[env_len]) env_len++; env_len++; // terminating NULL
// +1 for our new entry new_envp = (char**) __builtin_alloca((env_len + 1) * sizeof(char*));
__simple_sprintf(binprefs, "__mldr_bprefs=%x,%x,%x,%x", desc->attrp->psa_binprefs[0], desc->attrp->psa_binprefs[1], desc->attrp->psa_binprefs[2], desc->attrp->psa_binprefs[3]);
new_envp[0] = binprefs; for (i = 0; i < env_len; i++) new_envp[i+1] = envp[i]; envp = new_envp; }
ret = sys_execve((char*) path, argvp, envp);fail: //__simple_kprintf("posix_spawn is failing with %d\n", ret); if (desc && desc->attrp && desc->attrp->psa_flags & POSIX_SPAWN_SETEXEC) { // no_fork case ret = errno_linux_to_bsd(ret); return ret; } else { sys_write(pipe[1], &ret, sizeof(ret)); sys_exit(127); __builtin_unreachable(); } } else { // parent int err;
// close the writing side close_internal(pipe[1]);
if (my_pid < 0) { ret = my_pid; goto out; }
if (pid != NULL) *pid = my_pid;
if (sys_read(pipe[0], &ret, sizeof(ret)) != sizeof(ret)) ret = 0; close_internal(pipe[0]); }
out: return ret;}