MRef Prmci6AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { Word *gregs; AVER(NONNEGATIVE(regnum)); AVER(regnum <= 15); gregs = (Word *)&mfc->ucontext->uc_mcontext.gregs; /* .assume.regref */ /* The register numbers (REG_RAX etc.) are defined in <ucontext.h> but only if _GNU_SOURCE is defined: see .feature.li in config.h. */ switch (regnum) { case 0: return &gregs[REG_RAX]; case 1: return &gregs[REG_RCX]; case 2: return &gregs[REG_RDX]; case 3: return &gregs[REG_RBX]; case 4: return &gregs[REG_RSP]; case 5: return &gregs[REG_RBP]; case 6: return &gregs[REG_RSI]; case 7: return &gregs[REG_RDI]; case 8: return &gregs[REG_R8]; case 9: return &gregs[REG_R9]; case 10: return &gregs[REG_R10]; case 11: return &gregs[REG_R11]; case 12: return &gregs[REG_R12]; case 13: return &gregs[REG_R13]; case 14: return &gregs[REG_R14]; case 15: return &gregs[REG_R15]; } NOTREACHED; return (MRef)NULL; /* Avoids compiler warning. */ }
MRef Prmci6AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { AVER(NONNEGATIVE(regnum)); AVER(regnum <= 15); /* .assume.regref */ /* The register numbers (REG_RAX etc.) are defined in <ucontext.h> but only if _XOPEN_SOURCE is defined: see .feature.xc in config.h. */ /* MRef (a Word *) is not compatible with pointers to the register types (actually a __uint64_t). To avoid aliasing optimization problems, The registers are cast through (char *) */ switch (regnum) { case 0: return (MRef)((char *)&mfc->threadState->__rax); case 1: return (MRef)((char *)&mfc->threadState->__rcx); case 2: return (MRef)((char *)&mfc->threadState->__rdx); case 3: return (MRef)((char *)&mfc->threadState->__rbx); case 4: return (MRef)((char *)&mfc->threadState->__rsp); case 5: return (MRef)((char *)&mfc->threadState->__rbp); case 6: return (MRef)((char *)&mfc->threadState->__rsi); case 7: return (MRef)((char *)&mfc->threadState->__rdi); case 8: return (MRef)((char *)&mfc->threadState->__r8); case 9: return (MRef)((char *)&mfc->threadState->__r9); case 10: return (MRef)((char *)&mfc->threadState->__r10); case 11: return (MRef)((char *)&mfc->threadState->__r11); case 12: return (MRef)((char *)&mfc->threadState->__r12); case 13: return (MRef)((char *)&mfc->threadState->__r13); case 14: return (MRef)((char *)&mfc->threadState->__r14); case 15: return (MRef)((char *)&mfc->threadState->__r15); } NOTREACHED; return (MRef)NULL; /* Avoids compiler warning. */ }
MRef Prmci6AddressHoldingReg(MutatorFaultContext context, unsigned int regnum) { PCONTEXT wincont; AVER(NONNEGATIVE(regnum)); AVER(regnum <= 16); wincont = context->ep->ContextRecord; switch (regnum) { case 0: return (MRef)&wincont->Rax; case 1: return (MRef)&wincont->Rcx; case 2: return (MRef)&wincont->Rdx; case 3: return (MRef)&wincont->Rbx; case 4: return (MRef)&wincont->Rsp; case 5: return (MRef)&wincont->Rbp; case 6: return (MRef)&wincont->Rsi; case 7: return (MRef)&wincont->Rdi; case 8: return (MRef)&wincont->R8; case 9: return (MRef)&wincont->R9; case 10: return (MRef)&wincont->R10; case 11: return (MRef)&wincont->R11; case 12: return (MRef)&wincont->R12; case 13: return (MRef)&wincont->R13; case 14: return (MRef)&wincont->R14; case 15: return (MRef)&wincont->R15; default: NOTREACHED; return NULL; /* suppress warning */ } }
MRef Prmci3AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { THREAD_STATE_S *threadState; AVER(mfc != NULL); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 7); AVER(mfc->threadState != NULL); threadState = mfc->threadState; /* .source.i486 */ /* .assume.regref */ /* The register numbers (REG_EAX etc.) are defined in <ucontext.h> but only if _GNU_SOURCE is defined: see .feature.li in config.h. */ /* TODO: The current arrangement of the fix operation (taking a Ref *) forces us to pun these registers (actually `int` on LII3GC). We can suppress the warning by casting through `void *` and this might make it safe, but does it really? RB 2012-09-10 */ switch (regnum) { case 0: return (void *)&threadState->__eax; case 1: return (void *)&threadState->__ecx; case 2: return (void *)&threadState->__edx; case 3: return (void *)&threadState->__ebx; case 4: return (void *)&threadState->__esp; case 5: return (void *)&threadState->__ebp; case 6: return (void *)&threadState->__esi; case 7: return (void *)&threadState->__edi; default: NOTREACHED; return NULL; /* Avoids compiler warning. */ } }
Bool MutatorContextCheck(MutatorContext context) { CHECKS(MutatorContext, context); CHECKL(NONNEGATIVE(context->var)); CHECKL(context->var < MutatorContextLIMIT); return TRUE; }
MRef Prmci3AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { MRef gregs; AVER(mfc != NULL); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 7); AVER(mfc->ucontext != NULL); /* TODO: The current arrangement of the fix operation (taking a Ref *) forces us to pun these registers (actually `int` on LII3GC). We can suppress the warning by casting through `void *` and this might make it safe, but does it really? RB 2012-09-10 */ AVER(sizeof(void *) == sizeof(*mfc->ucontext->uc_mcontext.gregs)); gregs = (void *)mfc->ucontext->uc_mcontext.gregs; /* .source.i486 */ /* .assume.regref */ /* The register numbers (REG_EAX etc.) are defined in <ucontext.h> but only if _GNU_SOURCE is defined: see .feature.li in config.h. */ switch (regnum) { case 0: return &gregs[REG_EAX]; case 1: return &gregs[REG_ECX]; case 2: return &gregs[REG_EDX]; case 3: return &gregs[REG_EBX]; case 4: return &gregs[REG_ESP]; case 5: return &gregs[REG_EBP]; case 6: return &gregs[REG_ESI]; case 7: return &gregs[REG_EDI]; default: NOTREACHED; return NULL; /* Avoids compiler warning. */ } }
MRef Prmci6AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { THREAD_STATE_S *threadState; AVER(mfc != NULL); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 15); AVER(mfc->threadState != NULL); threadState = mfc->threadState; /* .assume.regref */ /* The register numbers (REG_RAX etc.) are defined in <ucontext.h> but only if _XOPEN_SOURCE is defined: see .feature.xc in config.h. */ /* MRef (a Word *) is not compatible with pointers to the register types (actually a __uint64_t). To avoid aliasing optimization problems, the registers are cast through (void *). */ switch (regnum) { case 0: return (void *)&threadState->__rax; case 1: return (void *)&threadState->__rcx; case 2: return (void *)&threadState->__rdx; case 3: return (void *)&threadState->__rbx; case 4: return (void *)&threadState->__rsp; case 5: return (void *)&threadState->__rbp; case 6: return (void *)&threadState->__rsi; case 7: return (void *)&threadState->__rdi; case 8: return (void *)&threadState->__r8; case 9: return (void *)&threadState->__r9; case 10: return (void *)&threadState->__r10; case 11: return (void *)&threadState->__r11; case 12: return (void *)&threadState->__r12; case 13: return (void *)&threadState->__r13; case 14: return (void *)&threadState->__r14; case 15: return (void *)&threadState->__r15; default: NOTREACHED; return NULL; /* Avoids compiler warning. */ } }
static int stdout_handler(sd_event_source *es, int fd, uint32_t revents, void *vorig_fd) { (void)es; (void)revents; int orig_fd = *(int *)vorig_fd; char buf[BUFSIZ]; int n; n = (int)read(fd, buf, sizeof(buf)); if (n < 0) { if (errno == EAGAIN) { return 0; } else { NONNEGATIVE(n); } } else { NONNEGATIVE(write(STDOUT_FILENO, &orig_fd, sizeof(orig_fd))); NONNEGATIVE(write(STDOUT_FILENO, &n, sizeof(n))); NONNEGATIVE(write(STDOUT_FILENO, buf, (size_t)n)); } return 0; }
void EventFlush(EventKind kind) { AVER(eventInited); AVER(NONNEGATIVE(kind)); AVER(kind < EventKindLIMIT); AVER(EventBuffer[kind] <= EventLast[kind]); AVER(EventLast[kind] <= EventWritten[kind]); AVER(EventWritten[kind] <= EventBuffer[kind] + EventBufferSIZE); /* Send all pending events to the event stream. */ EventSync(); /* Flush the in-memory buffer whether or not we send this buffer, so that we can continue to record recent events. */ EventLast[kind] = EventWritten[kind] = EventBuffer[kind] + EventBufferSIZE; }
Bool ChunkCheck(Chunk chunk) { CHECKS(Chunk, chunk); CHECKU(Arena, chunk->arena); CHECKL(chunk->serial < chunk->arena->chunkSerial); /* Can't use CHECKD_NOSIG because TreeEMPTY is NULL. */ CHECKL(TreeCheck(&chunk->chunkTree)); CHECKL(ChunkPagesToSize(chunk, 1) == ChunkPageSize(chunk)); CHECKL(ShiftCheck(ChunkPageShift(chunk))); CHECKL(chunk->base != (Addr)0); CHECKL(chunk->base < chunk->limit); /* check chunk structure is at its own base: see .chunk.at.base. */ CHECKL(chunk->base == (Addr)chunk); CHECKL((Addr)(chunk+1) <= chunk->limit); CHECKL(ChunkSizeToPages(chunk, ChunkSize(chunk)) == chunk->pages); /* check that the tables fit in the chunk */ CHECKL(chunk->allocBase <= chunk->pages); CHECKL(chunk->allocBase >= chunk->pageTablePages); CHECKD_NOSIG(BT, chunk->allocTable); /* check that allocTable is in the chunk overhead */ CHECKL((Addr)chunk->allocTable >= chunk->base); CHECKL(AddrAdd((Addr)chunk->allocTable, BTSize(chunk->pages)) <= PageIndexBase(chunk, chunk->allocBase)); /* check they don't overlap (knowing the order) */ CHECKL(AddrAdd((Addr)chunk->allocTable, BTSize(chunk->pages)) <= (Addr)chunk->pageTable); CHECKL(chunk->pageTable != NULL); CHECKL((Addr)chunk->pageTable >= chunk->base); CHECKL((Addr)&chunk->pageTable[chunk->pageTablePages] <= PageIndexBase(chunk, chunk->allocBase)); CHECKL(NONNEGATIVE(INDEX_OF_ADDR(chunk, (Addr)chunk->pageTable))); /* check there's enough space in the page table */ CHECKL(INDEX_OF_ADDR(chunk, AddrSub(chunk->limit, 1)) < chunk->pages); CHECKL(chunk->pageTablePages < chunk->pages); /* Could check the consistency of the tables, but not O(1). */ return TRUE; }
MRef Prmci3AddressHoldingReg(MutatorFaultContext context, unsigned int regnum) { PCONTEXT wincont; AVER(NONNEGATIVE(regnum)); AVER(regnum <= 7); wincont = context->ep->ContextRecord; switch (regnum) { case 0: return (MRef)&wincont->Eax; case 1: return (MRef)&wincont->Ecx; case 2: return (MRef)&wincont->Edx; case 3: return (MRef)&wincont->Ebx; case 4: return (MRef)&wincont->Esp; case 5: return (MRef)&wincont->Ebp; case 6: return (MRef)&wincont->Esi; case 7: return (MRef)&wincont->Edi; default: NOTREACHED; return NULL; /* suppress warning */ } }
static int sigchld_handler(sd_event_source *es, const struct signalfd_siginfo *si, void *vmpid) { (void)es; pid_t mpid = *(pid_t *)vmpid; if (si->ssi_signo != SIGCHLD) ERROR("parent: unexpected signal"); while (true) { int status; pid_t spid = waitpid(-mpid, &status, WNOHANG | __WALL); NONNEGATIVE(spid); if (!spid) break; if (WIFEXITED(status) && spid == mpid) { FINISH(POE_SUCCESS, WEXITSTATUS(status), NULL); } else if (WIFSIGNALED(status) && spid == mpid) { FINISH(POE_SIGNALED, -1, "Program terminated with signal %d (%s)", WTERMSIG(status), strsignal(WTERMSIG(status))); } else if (WIFSTOPPED(status)) { int e = status >> 16 & 0xff; switch (e) { case PTRACE_EVENT_SECCOMP: handle_syscall(spid); break; case PTRACE_EVENT_CLONE: case PTRACE_EVENT_FORK: case PTRACE_EVENT_VFORK: ptrace(PTRACE_CONT, spid, 0, 0); break; default: ptrace(PTRACE_CONT, spid, 0, WSTOPSIG(status)); break; } } } return 0; }
static void child(const char *root, char *cmd[]) { pid_t pid = (pid_t)syscall(SYS_getpid); assert(pid == 1); // die when parent dies NONNEGATIVE(prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0)); NONNEGATIVE(sethostname(POE_HOSTNAME, strlen(POE_HOSTNAME))); NONNEGATIVE(mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL)); NONNEGATIVE(mount(root, root, "bind", MS_BIND | MS_REC, NULL)); NONNEGATIVE(chroot(root)); // NONNEGATIVE(mount(NULL, "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL)); // NONNEGATIVE(mount(NULL, "/dev", "devtmpfs", MS_NOSUID | MS_NOEXEC, NULL)); // NONNEGATIVE(mount(NULL, "/dev/shm", "tmpfs", MS_NOSUID | MS_NODEV, NULL)); struct passwd *pw = getpwnam(POE_USERNAME); if (!pw) ERROR("getpwnam() failed"); NONNEGATIVE(chdir("/tmp")); NONNEGATIVE(setsid()); NONNEGATIVE(initgroups(POE_USERNAME, pw->pw_gid)); NONNEGATIVE(setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid)); NONNEGATIVE(setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)); char *env[] = { "PATH=/opt/bin:/usr/bin", "USER="******"LOGNAME=" POE_USERNAME, NULL, NULL }; NONNEGATIVE(asprintf(env + 3, "HOME=%s", pw->pw_dir)); // wait parent NONNEGATIVE(kill(pid, SIGSTOP)); NONNEGATIVE(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); poe_init_seccomp(SCMP_ACT_TRACE(0)); NONNEGATIVE(execvpe(cmd[0], cmd, env)); }
int main(int argc, char *argv[]) { if (argc < 5) { ERROR("usage: %s baseroot envroot program cmdl..", program_invocation_short_name); } const char *root = poe_init_playground(argv[1], argv[2]); const char *prog = copy_program(root, argv[3]); char **cmdl = construct_cmdl(argc - 4, argv + 4, prog); int stdout_fd[2], stderr_fd[2]; NONNEGATIVE(pipe2(stdout_fd, O_DIRECT)); NONNEGATIVE(pipe2(stderr_fd, O_DIRECT)); // TODO: CLONE_NEWUSER pid_t pid = (pid_t)syscall(SYS_clone, SIGCHLD | CLONE_NEWIPC | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNET, 0); NONNEGATIVE(pid); if (pid == 0) { dup2(stdout_fd[1], STDOUT_FILENO); close(stdout_fd[0]); close(stdout_fd[1]); dup2(stderr_fd[1], STDERR_FILENO); close(stderr_fd[0]); close(stderr_fd[1]); child(root, cmdl); } else { sd_event *event = NULL; uint64_t now; int stdout_fileno = STDOUT_FILENO; int stderr_fileno = STDERR_FILENO; int fflags; fflags = fcntl(stdout_fd[0], F_GETFL, 0); NONNEGATIVE(fflags); NONNEGATIVE(fcntl(stdout_fd[0], F_SETFL, fflags | O_NONBLOCK)); fflags = fcntl(stderr_fd[0], F_GETFL, 0); NONNEGATIVE(fflags); NONNEGATIVE(fcntl(stderr_fd[0], F_SETFL, fflags | O_NONBLOCK)); sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); sigprocmask(SIG_BLOCK, &mask, NULL); NONNEGATIVE(sd_event_default(&event)); NONNEGATIVE(sd_event_add_signal(event, NULL, SIGCHLD, sigchld_handler, &pid)); NONNEGATIVE(sd_event_add_signal(event, NULL, SIGINT, sigint_handler, &pid)); NONNEGATIVE(sd_event_add_signal(event, NULL, SIGTERM, sigint_handler, &pid)); NONNEGATIVE(sd_event_now(event, CLOCK_MONOTONIC, &now)); NONNEGATIVE(sd_event_add_time(event, NULL, CLOCK_MONOTONIC, now + POE_TIME_LIMIT, 0, timer_handler, &pid)); NONNEGATIVE(sd_event_add_io(event, NULL, stdout_fd[0], EPOLLIN, stdout_handler, &stdout_fileno)); NONNEGATIVE(sd_event_add_io(event, NULL, stderr_fd[0], EPOLLIN, stdout_handler, &stderr_fileno)); NONNEGATIVE(ptrace(PTRACE_SEIZE, pid, NULL, PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACESECCOMP | PTRACE_O_TRACEVFORK)); poe_init_systemd(pid); NONNEGATIVE(sd_event_loop(event)); } ERROR("unreachable"); }
char * poe_init_playground(const char *base, const char *env) { struct stat s; if (stat(POE_TEMPORARY_BASE, &s) == -1) { NONNEGATIVE(mkdir(POE_TEMPORARY_BASE, 0755)); } // setup base (tmpfs) NONNEGATIVE(asprintf(&workbase, POE_TEMPORARY_BASE "/%ld", (long)getpid())); // pid_t is signed, not larger than long if (stat(workbase, &s) != -1) { NONNEGATIVE(rmrf(workbase)); } NONNEGATIVE(mkdir(workbase, 0755)); NONNEGATIVE(mount(NULL, workbase, "tmpfs", MS_NOSUID, "size=32m")); // TODO NONNEGATIVE(asprintf(&workdir, "%s/work", workbase)); NONNEGATIVE(mkdir(workdir, 0755)); NONNEGATIVE(asprintf(&upperdir, "%s/upper", workbase)); NONNEGATIVE(mkdir(upperdir, 0755)); NONNEGATIVE(asprintf(&mergeddir, "%s/merged", workbase)); NONNEGATIVE(mkdir(mergeddir, 0755)); char *opts = NULL; NONNEGATIVE(asprintf(&opts, "lowerdir=%s:%s,upperdir=%s,workdir=%s", env, base, upperdir, workdir)); NONNEGATIVE(mount(NULL, mergeddir, "overlay", MS_NOSUID, opts)); return mergeddir; }