int compel_execute_syscall(struct parasite_ctl *ctl, user_regs_struct_t *regs, const char *code_syscall) { pid_t pid = ctl->rpid; int err; uint8_t code_orig[BUILTIN_SYSCALL_SIZE]; /* * Inject syscall instruction and remember original code, * we will need it to restore original program content. */ memcpy(code_orig, code_syscall, sizeof(code_orig)); if (ptrace_swap_area(pid, (void *)ctl->ictx.syscall_ip, (void *)code_orig, sizeof(code_orig))) { pr_err("Can't inject syscall blob (pid: %d)\n", pid); return -1; } err = parasite_run(pid, PTRACE_CONT, ctl->ictx.syscall_ip, 0, regs, &ctl->orig); if (!err) err = parasite_trap(ctl, pid, regs, &ctl->orig); if (ptrace_poke_area(pid, (void *)code_orig, (void *)ctl->ictx.syscall_ip, sizeof(code_orig))) { pr_err("Can't restore syscall blob (pid: %d)\n", ctl->rpid); err = -1; } return err; }
int parasite_cure_remote(struct parasite_ctl *ctl, struct pstree_item *item) { int ret = 0; ctl->tsock = -1; if (ctl->parasite_ip) { ctl->signals_blocked = 0; parasite_fini_threads_seized(ctl, item); parasite_execute(PARASITE_CMD_FINI, ctl); } if (ctl->remote_map) { if (munmap_seized(ctl, (void *)ctl->remote_map, ctl->map_length)) { pr_err("munmap_seized failed (pid: %d)\n", ctl->pid.real); ret = -1; } } if (ptrace_poke_area(ctl->pid.real, (void *)ctl->code_orig, (void *)ctl->syscall_ip, sizeof(ctl->code_orig))) { pr_err("Can't restore syscall blob (pid: %d)\n", ctl->pid.real); ret = -1; } if (ptrace(PTRACE_SETREGS, ctl->pid.real, NULL, &ctl->regs_orig)) { pr_err("Can't restore registers (pid: %d)\n", ctl->pid.real); ret = -1; } return ret; }
/* don't swap big space, it might overflow the stack */ int ptrace_swap_area(pid_t pid, void *dst, void *src, long bytes) { void *t = alloca(bytes); if (ptrace_peek_area(pid, t, dst, bytes)) return -1; if (ptrace_poke_area(pid, src, dst, bytes)) { if (ptrace_poke_area(pid, t, dst, bytes)) return -2; return -1; } memcpy(src, t, bytes); return 0; }
static int parasite_memfd_exchange(struct parasite_ctl *ctl, unsigned long size) { void *where = (void *)ctl->ictx.syscall_ip + BUILTIN_SYSCALL_SIZE; uint8_t orig_code[MEMFD_FNAME_SZ] = MEMFD_FNAME; pid_t pid = ctl->rpid; long sret = -ENOSYS; int ret, fd, lfd; bool __maybe_unused compat_task = !compel_mode_native(ctl); if (ctl->ictx.flags & INFECT_NO_MEMFD) return 1; BUILD_BUG_ON(sizeof(orig_code) < sizeof(long)); if (ptrace_swap_area(pid, where, (void *)orig_code, sizeof(orig_code))) { pr_err("Can't inject memfd args (pid: %d)\n", pid); return -1; } ret = compel_syscall(ctl, __NR(memfd_create, compat_task), &sret, (unsigned long)where, 0, 0, 0, 0, 0); if (ptrace_poke_area(pid, orig_code, where, sizeof(orig_code))) { fd = (int)(long)sret; if (fd >= 0) compel_syscall(ctl, __NR(close, compat_task), &sret, fd, 0, 0, 0, 0, 0); pr_err("Can't restore memfd args (pid: %d)\n", pid); return -1; } if (ret < 0) return ret; fd = (int)(long)sret; if (fd == -ENOSYS) return 1; if (fd < 0) { errno = -fd; pr_perror("Can't create memfd in victim"); return fd; } ctl->map_length = round_up(size, page_size()); lfd = ctl->ictx.open_proc(ctl->rpid, O_RDWR, "fd/%d", fd); if (lfd < 0) goto err_cure; if (ftruncate(lfd, ctl->map_length) < 0) { pr_perror("Fail to truncate memfd for parasite"); goto err_cure; } ctl->remote_map = remote_mmap(ctl, NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FILE | MAP_SHARED, fd, 0); if (!ctl->remote_map) { pr_err("Can't rmap memfd for parasite blob\n"); goto err_curef; } ctl->local_map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, lfd, 0); if (ctl->local_map == MAP_FAILED) { ctl->local_map = NULL; pr_perror("Can't lmap memfd for parasite blob"); goto err_curef; } compel_syscall(ctl, __NR(close, compat_task), &sret, fd, 0, 0, 0, 0, 0); close(lfd); pr_info("Set up parasite blob using memfd\n"); return 0; err_curef: close(lfd); err_cure: compel_syscall(ctl, __NR(close, compat_task), &sret, fd, 0, 0, 0, 0, 0); return -1; }