2016-09-27 20:16:07 +03:00
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
2016-09-27 18:15:30 +03:00
|
|
|
#include <errno.h>
|
2016-09-27 20:16:07 +03:00
|
|
|
#include <signal.h>
|
2016-10-27 14:25:46 +03:00
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <stdio.h>
|
2016-10-27 18:07:10 +03:00
|
|
|
#include <fcntl.h>
|
2016-09-27 20:16:07 +03:00
|
|
|
#include <linux/seccomp.h>
|
2016-09-27 18:15:30 +03:00
|
|
|
|
2016-10-31 15:06:48 +03:00
|
|
|
#include "log.h"
|
2016-10-27 14:27:23 +03:00
|
|
|
#include "common/bug.h"
|
|
|
|
#include "common/xmalloc.h"
|
2016-10-28 14:00:55 +03:00
|
|
|
#include "common/lock.h"
|
2016-10-31 15:06:48 +03:00
|
|
|
#include "common/page.h"
|
2016-10-27 14:27:23 +03:00
|
|
|
|
2016-10-27 19:01:21 +03:00
|
|
|
#include <compel/plugins/std/syscall-codes.h>
|
|
|
|
#include <compel/plugins/std/asm/syscall-types.h>
|
2016-10-31 15:06:48 +03:00
|
|
|
#include "uapi/compel/plugins/std/syscall.h"
|
2016-10-28 11:41:28 +03:00
|
|
|
#include "asm/infect-types.h"
|
2016-10-27 14:25:46 +03:00
|
|
|
#include "asm/sigframe.h"
|
2016-09-30 14:34:50 +03:00
|
|
|
#include "infect.h"
|
2017-02-08 06:36:10 -08:00
|
|
|
#include "ptrace.h"
|
2016-09-30 14:34:50 +03:00
|
|
|
#include "infect-rpc.h"
|
2016-09-28 10:47:17 +03:00
|
|
|
#include "infect-priv.h"
|
2016-10-27 12:10:30 +03:00
|
|
|
#include "infect-util.h"
|
2016-11-03 20:29:58 +03:00
|
|
|
#include "rpc-pie-priv.h"
|
2016-11-15 12:42:00 +03:00
|
|
|
#include "infect-util.h"
|
|
|
|
|
|
|
|
#define __sys(foo) foo
|
2017-01-25 12:46:19 +03:00
|
|
|
#define __sys_err(ret) (-errno)
|
2016-11-15 12:42:00 +03:00
|
|
|
#define __memcpy memcpy
|
|
|
|
|
|
|
|
#include "common/scm.h"
|
|
|
|
#include "common/scm-code.c"
|
2016-09-27 18:15:30 +03:00
|
|
|
|
2016-10-27 11:22:42 +03:00
|
|
|
#define UNIX_PATH_MAX (sizeof(struct sockaddr_un) - \
|
|
|
|
(size_t)((struct sockaddr_un *) 0)->sun_path)
|
|
|
|
|
|
|
|
#define PARASITE_STACK_SIZE (16 << 10)
|
|
|
|
|
2016-09-27 20:16:07 +03:00
|
|
|
#ifndef SECCOMP_MODE_DISABLED
|
|
|
|
#define SECCOMP_MODE_DISABLED 0
|
|
|
|
#endif
|
|
|
|
|
2016-11-14 16:05:08 +03:00
|
|
|
static int prepare_thread(int pid, struct thread_ctx *ctx);
|
|
|
|
|
2016-10-27 18:07:10 +03:00
|
|
|
static inline void close_safe(int *pfd)
|
|
|
|
{
|
|
|
|
if (*pfd > -1) {
|
|
|
|
close(*pfd);
|
|
|
|
*pfd = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-21 21:26:19 +03:00
|
|
|
static int parse_pid_status(int pid, struct seize_task_status *ss)
|
|
|
|
{
|
|
|
|
char aux[128];
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
sprintf(aux, "/proc/%d/status", pid);
|
|
|
|
f = fopen(aux, "r");
|
|
|
|
if (!f)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ss->ppid = -1; /* Not needed at this point */
|
|
|
|
ss->seccomp_mode = SECCOMP_MODE_DISABLED;
|
|
|
|
|
|
|
|
while (fgets(aux, sizeof(aux), f)) {
|
|
|
|
if (!strncmp(aux, "State:", 6)) {
|
|
|
|
ss->state = aux[7];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strncmp(aux, "Seccomp:", 8)) {
|
|
|
|
if (sscanf(aux + 9, "%d", &ss->seccomp_mode) != 1)
|
|
|
|
goto err_parse;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strncmp(aux, "ShdPnd:", 7)) {
|
|
|
|
if (sscanf(aux + 7, "%llx", &ss->shdpnd) != 1)
|
|
|
|
goto err_parse;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncmp(aux, "SigPnd:", 7)) {
|
|
|
|
if (sscanf(aux + 7, "%llx", &ss->sigpnd) != 1)
|
|
|
|
goto err_parse;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_parse:
|
|
|
|
fclose(f);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-09-27 18:15:30 +03:00
|
|
|
int compel_stop_task(int pid)
|
2016-11-21 21:26:19 +03:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct seize_task_status ss;
|
|
|
|
|
|
|
|
ret = compel_interrupt_task(pid);
|
|
|
|
if (ret == 0)
|
|
|
|
ret = compel_wait_task(pid, -1, parse_pid_status, &ss);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int compel_interrupt_task(int pid)
|
2016-09-27 18:15:30 +03:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ptrace(PTRACE_SEIZE, pid, NULL, 0);
|
|
|
|
if (ret) {
|
|
|
|
/*
|
|
|
|
* ptrace API doesn't allow to distinguish
|
|
|
|
* attaching to zombie from other errors.
|
2016-09-27 20:16:07 +03:00
|
|
|
* All errors will be handled in compel_wait_task().
|
2016-09-27 18:15:30 +03:00
|
|
|
*/
|
|
|
|
pr_warn("Unable to interrupt task: %d (%s)\n", pid, strerror(errno));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we SEIZE-d the task stop it before going
|
|
|
|
* and reading its stat from proc. Otherwise task
|
|
|
|
* may die _while_ we're doing it and we'll have
|
|
|
|
* inconsistent seize/state pair.
|
|
|
|
*
|
|
|
|
* If task dies after we seize it but before we
|
|
|
|
* do this interrupt, we'll notice it via proc.
|
|
|
|
*/
|
|
|
|
ret = ptrace(PTRACE_INTERRUPT, pid, NULL, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_warn("SEIZE %d: can't interrupt task: %s", pid, strerror(errno));
|
|
|
|
if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
|
|
|
|
pr_perror("Unable to detach from %d", pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-09-27 20:16:07 +03:00
|
|
|
static int skip_sigstop(int pid, int nr_signals)
|
|
|
|
{
|
|
|
|
int i, status, ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 1) SIGSTOP is queued, but isn't handled yet:
|
|
|
|
* SGISTOP can't be blocked, so we need to wait when the kernel
|
|
|
|
* handles this signal.
|
|
|
|
*
|
|
|
|
* Otherwise the process will be stopped immediately after
|
|
|
|
* starting it.
|
|
|
|
*
|
|
|
|
* 2) A seized task was stopped:
|
|
|
|
* PTRACE_SEIZE doesn't affect signal or group stop state.
|
|
|
|
* Currently ptrace reported that task is in stopped state.
|
|
|
|
* We need to start task again, and it will be trapped
|
|
|
|
* immediately, because we sent PTRACE_INTERRUPT to it.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < nr_signals; i++) {
|
|
|
|
ret = ptrace(PTRACE_CONT, pid, 0, 0);
|
|
|
|
if (ret) {
|
|
|
|
pr_perror("Unable to start process");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = wait4(pid, &status, __WALL, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_perror("SEIZE %d: can't wait task", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!WIFSTOPPED(status)) {
|
|
|
|
pr_err("SEIZE %d: task not stopped after seize\n", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This routine seizes task putting it into a special
|
|
|
|
* state where we can manipulate the task via ptrace
|
|
|
|
* interface, and finally we can detach ptrace out of
|
|
|
|
* of it so the task would not know if it was saddled
|
|
|
|
* up with someone else.
|
|
|
|
*/
|
|
|
|
int compel_wait_task(int pid, int ppid,
|
|
|
|
int (*get_status)(int pid, struct seize_task_status *),
|
|
|
|
struct seize_task_status *ss)
|
|
|
|
{
|
|
|
|
siginfo_t si;
|
|
|
|
int status, nr_sigstop;
|
|
|
|
int ret = 0, ret2, wait_errno = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It's ugly, but the ptrace API doesn't allow to distinguish
|
|
|
|
* attaching to zombie from other errors. Thus we have to parse
|
|
|
|
* the target's /proc/pid/stat. Sad, but parse whatever else
|
|
|
|
* we might need at that early point.
|
|
|
|
*/
|
|
|
|
|
|
|
|
try_again:
|
|
|
|
|
|
|
|
ret = wait4(pid, &status, __WALL, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
/*
|
|
|
|
* wait4() can expectedly fail only in a first time
|
|
|
|
* if a task is zombie. If we are here from try_again,
|
|
|
|
* this means that we are tracing this task.
|
|
|
|
*
|
|
|
|
* So here we can be only once in this function.
|
|
|
|
*/
|
|
|
|
wait_errno = errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret2 = get_status(pid, ss);
|
|
|
|
if (ret2)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (ret < 0 || WIFEXITED(status) || WIFSIGNALED(status)) {
|
|
|
|
if (ss->state != 'Z') {
|
|
|
|
if (pid == getpid())
|
|
|
|
pr_err("The criu itself is within dumped tree.\n");
|
|
|
|
else
|
|
|
|
pr_err("Unseizable non-zombie %d found, state %c, err %d/%d\n",
|
|
|
|
pid, ss->state, ret, wait_errno);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
return TASK_ZOMBIE;
|
|
|
|
else
|
|
|
|
return TASK_DEAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ppid != -1) && (ss->ppid != ppid)) {
|
|
|
|
pr_err("Task pid reused while suspending (%d: %d -> %d)\n",
|
|
|
|
pid, ppid, ss->ppid);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!WIFSTOPPED(status)) {
|
|
|
|
pr_err("SEIZE %d: task not stopped after seize\n", pid);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ptrace(PTRACE_GETSIGINFO, pid, NULL, &si);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_perror("SEIZE %d: can't read signfo", pid);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2017-02-08 06:36:10 -08:00
|
|
|
if (PTRACE_SI_EVENT(si.si_code) != PTRACE_EVENT_STOP) {
|
2016-09-27 20:16:07 +03:00
|
|
|
/*
|
|
|
|
* Kernel notifies us about the task being seized received some
|
|
|
|
* event other than the STOP, i.e. -- a signal. Let the task
|
|
|
|
* handle one and repeat.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (ptrace(PTRACE_CONT, pid, NULL,
|
|
|
|
(void *)(unsigned long)si.si_signo)) {
|
|
|
|
pr_perror("Can't continue signal handling, aborting");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
goto try_again;
|
|
|
|
}
|
|
|
|
|
2017-02-08 06:36:10 -08:00
|
|
|
if (ss->seccomp_mode != SECCOMP_MODE_DISABLED && ptrace_suspend_seccomp(pid) < 0)
|
2016-09-27 20:16:07 +03:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
nr_sigstop = 0;
|
|
|
|
if (ss->sigpnd & (1 << (SIGSTOP - 1)))
|
|
|
|
nr_sigstop++;
|
|
|
|
if (ss->shdpnd & (1 << (SIGSTOP - 1)))
|
|
|
|
nr_sigstop++;
|
|
|
|
if (si.si_signo == SIGSTOP)
|
|
|
|
nr_sigstop++;
|
|
|
|
|
|
|
|
if (nr_sigstop) {
|
|
|
|
if (skip_sigstop(pid, nr_sigstop))
|
|
|
|
goto err_stop;
|
|
|
|
|
|
|
|
return TASK_STOPPED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (si.si_signo == SIGTRAP)
|
|
|
|
return TASK_ALIVE;
|
|
|
|
else {
|
|
|
|
pr_err("SEIZE %d: unsupported stop signal %d\n", pid, si.si_signo);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err_stop:
|
|
|
|
kill(pid, SIGSTOP);
|
|
|
|
err:
|
|
|
|
if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
|
|
|
|
pr_perror("Unable to detach from %d", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-11-25 16:20:45 +03:00
|
|
|
int compel_resume_task(pid_t pid, int orig_st, int st)
|
2016-09-29 16:37:34 +03:00
|
|
|
{
|
|
|
|
pr_debug("\tUnseizing %d into %d\n", pid, st);
|
|
|
|
|
|
|
|
if (st == TASK_DEAD) {
|
|
|
|
kill(pid, SIGKILL);
|
|
|
|
return 0;
|
|
|
|
} else if (st == TASK_STOPPED) {
|
|
|
|
/*
|
|
|
|
* Task might have had STOP in queue. We detected such
|
|
|
|
* guy as TASK_STOPPED, but cleared signal to run the
|
|
|
|
* parasite code. hus after detach the task will become
|
|
|
|
* running. That said -- STOP everyone regardless of
|
|
|
|
* the initial state.
|
|
|
|
*/
|
|
|
|
kill(pid, SIGSTOP);
|
|
|
|
} else if (st == TASK_ALIVE) {
|
|
|
|
/*
|
|
|
|
* Same as in the comment above -- there might be a
|
|
|
|
* task with STOP in queue that would get lost after
|
|
|
|
* detach, so stop it again.
|
|
|
|
*/
|
|
|
|
if (orig_st == TASK_STOPPED)
|
|
|
|
kill(pid, SIGSTOP);
|
|
|
|
} else
|
|
|
|
pr_err("Unknown final state %d\n", st);
|
|
|
|
|
|
|
|
if (ptrace(PTRACE_DETACH, pid, NULL, NULL)) {
|
|
|
|
pr_perror("Unable to detach from %d", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-28 11:29:01 +03:00
|
|
|
static int gen_parasite_saddr(struct sockaddr_un *saddr, int key)
|
|
|
|
{
|
|
|
|
int sun_len;
|
|
|
|
|
|
|
|
saddr->sun_family = AF_UNIX;
|
|
|
|
snprintf(saddr->sun_path, UNIX_PATH_MAX,
|
|
|
|
"X/crtools-pr-%d", key);
|
|
|
|
|
|
|
|
sun_len = SUN_LEN(saddr);
|
|
|
|
*saddr->sun_path = '\0';
|
|
|
|
|
|
|
|
return sun_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int prepare_tsock(struct parasite_ctl *ctl, pid_t pid,
|
|
|
|
struct parasite_init_args *args)
|
|
|
|
{
|
2016-11-18 04:23:00 +03:00
|
|
|
int ssock = -1;
|
|
|
|
socklen_t sk_len;
|
|
|
|
struct sockaddr_un addr;
|
2016-09-28 11:29:01 +03:00
|
|
|
|
|
|
|
pr_info("Putting tsock into pid %d\n", pid);
|
|
|
|
args->h_addr_len = gen_parasite_saddr(&args->h_addr, getpid());
|
|
|
|
|
2016-11-18 04:23:00 +03:00
|
|
|
ssock = ctl->ictx.sock;
|
|
|
|
sk_len = sizeof(addr);
|
|
|
|
|
2016-09-28 11:29:01 +03:00
|
|
|
if (ssock == -1) {
|
2016-11-18 04:23:00 +03:00
|
|
|
pr_err("No socket in ictx\n");
|
|
|
|
goto err;
|
|
|
|
}
|
2016-09-28 11:29:01 +03:00
|
|
|
|
2016-11-18 04:23:00 +03:00
|
|
|
if (getsockname(ssock, (struct sockaddr *) &addr, &sk_len) < 0) {
|
|
|
|
pr_perror("Unable to get name for a socket");
|
|
|
|
return -1;
|
|
|
|
}
|
2016-09-28 11:29:01 +03:00
|
|
|
|
2016-11-18 04:23:00 +03:00
|
|
|
if (sk_len == sizeof(addr.sun_family)) {
|
2016-09-28 11:29:01 +03:00
|
|
|
if (bind(ssock, (struct sockaddr *)&args->h_addr, args->h_addr_len) < 0) {
|
|
|
|
pr_perror("Can't bind socket");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (listen(ssock, 1)) {
|
|
|
|
pr_perror("Can't listen on transport socket");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check a case when parasite can't initialize a command socket */
|
|
|
|
if (ctl->ictx.flags & INFECT_FAIL_CONNECT)
|
|
|
|
args->h_addr_len = gen_parasite_saddr(&args->h_addr, getpid() + 1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set to -1 to prevent any accidental misuse. The
|
|
|
|
* only valid user of it is accept_tsock().
|
|
|
|
*/
|
|
|
|
ctl->tsock = -ssock;
|
|
|
|
return 0;
|
|
|
|
err:
|
|
|
|
close_safe(&ssock);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int setup_child_handler(struct parasite_ctl *ctl)
|
|
|
|
{
|
|
|
|
struct sigaction sa = {
|
|
|
|
.sa_sigaction = ctl->ictx.child_handler,
|
|
|
|
.sa_flags = SA_SIGINFO | SA_RESTART,
|
|
|
|
};
|
|
|
|
|
|
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
sigaddset(&sa.sa_mask, SIGCHLD);
|
|
|
|
if (sigaction(SIGCHLD, &sa, NULL)) {
|
|
|
|
pr_perror("Unable to setup SIGCHLD handler");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-21 21:26:17 +03:00
|
|
|
static int restore_child_handler(struct parasite_ctl *ctl)
|
2016-09-28 12:01:01 +03:00
|
|
|
{
|
2016-11-21 21:26:17 +03:00
|
|
|
if (sigaction(SIGCHLD, &ctl->ictx.orig_handler, NULL)) {
|
2016-09-28 12:01:01 +03:00
|
|
|
pr_perror("Unable to setup SIGCHLD handler");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-28 14:07:16 +03:00
|
|
|
static int parasite_run(pid_t pid, int cmd, unsigned long ip, void *stack,
|
2016-09-28 11:29:01 +03:00
|
|
|
user_regs_struct_t *regs, struct thread_ctx *octx)
|
|
|
|
{
|
|
|
|
k_rtsigset_t block;
|
|
|
|
|
|
|
|
ksigfillset(&block);
|
|
|
|
if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &block)) {
|
|
|
|
pr_perror("Can't block signals for %d", pid);
|
|
|
|
goto err_sig;
|
|
|
|
}
|
|
|
|
|
|
|
|
parasite_setup_regs(ip, stack, regs);
|
|
|
|
if (ptrace_set_regs(pid, regs)) {
|
|
|
|
pr_perror("Can't set registers for %d", pid);
|
|
|
|
goto err_regs;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptrace(cmd, pid, NULL, NULL)) {
|
|
|
|
pr_perror("Can't run parasite at %d", pid);
|
|
|
|
goto err_cont;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_cont:
|
|
|
|
if (ptrace_set_regs(pid, &octx->regs))
|
|
|
|
pr_perror("Can't restore regs for %d", pid);
|
|
|
|
err_regs:
|
|
|
|
if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &octx->sigmask))
|
|
|
|
pr_perror("Can't restore sigmask for %d", pid);
|
|
|
|
err_sig:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-09-28 14:09:14 +03:00
|
|
|
static int restore_thread_ctx(int pid, struct thread_ctx *ctx)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (ptrace_set_regs(pid, &ctx->regs)) {
|
|
|
|
pr_perror("Can't restore registers (pid: %d)", pid);
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &ctx->sigmask)) {
|
|
|
|
pr_perror("Can't block signals");
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-09-28 14:08:20 +03:00
|
|
|
|
|
|
|
/* we run at @regs->ip */
|
|
|
|
static int parasite_trap(struct parasite_ctl *ctl, pid_t pid,
|
2016-09-28 13:55:21 +03:00
|
|
|
user_regs_struct_t *regs,
|
2016-09-28 14:08:20 +03:00
|
|
|
struct thread_ctx *octx)
|
|
|
|
{
|
|
|
|
siginfo_t siginfo;
|
|
|
|
int status;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Most ideas are taken from Tejun Heo's parasite thread
|
|
|
|
* https://code.google.com/p/ptrace-parasite/
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (wait4(pid, &status, __WALL, NULL) != pid) {
|
|
|
|
pr_perror("Waited pid mismatch (pid: %d)", pid);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!WIFSTOPPED(status)) {
|
|
|
|
pr_err("Task is still running (pid: %d)\n", pid);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo)) {
|
|
|
|
pr_perror("Can't get siginfo (pid: %d)", pid);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptrace_get_regs(pid, regs)) {
|
|
|
|
pr_perror("Can't obtain registers (pid: %d)", pid);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WSTOPSIG(status) != SIGTRAP || siginfo.si_code != ARCH_SI_TRAP) {
|
|
|
|
pr_debug("** delivering signal %d si_code=%d\n",
|
|
|
|
siginfo.si_signo, siginfo.si_code);
|
|
|
|
|
|
|
|
pr_err("Unexpected %d task interruption, aborting\n", pid);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We've reached this point if int3 is triggered inside our
|
|
|
|
* parasite code. So we're done.
|
|
|
|
*/
|
|
|
|
ret = 0;
|
|
|
|
err:
|
|
|
|
if (restore_thread_ctx(pid, octx))
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-09-28 13:55:21 +03:00
|
|
|
|
|
|
|
int compel_execute_syscall(struct parasite_ctl *ctl,
|
|
|
|
user_regs_struct_t *regs, const char *code_syscall)
|
|
|
|
{
|
|
|
|
pid_t pid = ctl->rpid;
|
|
|
|
int err;
|
2016-10-31 15:06:48 +03:00
|
|
|
uint8_t code_orig[BUILTIN_SYSCALL_SIZE];
|
2016-09-28 13:55:21 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2016-12-06 00:15:00 +03:00
|
|
|
int compel_run_at(struct parasite_ctl *ctl, unsigned long ip, user_regs_struct_t *ret_regs)
|
|
|
|
{
|
|
|
|
user_regs_struct_t regs = ctl->orig.regs;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = parasite_run(ctl->rpid, PTRACE_CONT, ip, 0, ®s, &ctl->orig);
|
|
|
|
if (!ret)
|
|
|
|
ret = parasite_trap(ctl, ctl->rpid, ret_regs ? ret_regs : ®s, &ctl->orig);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-09-28 11:29:01 +03:00
|
|
|
static int accept_tsock(struct parasite_ctl *ctl)
|
|
|
|
{
|
|
|
|
int sock;
|
|
|
|
int ask = -ctl->tsock; /* this '-' is explained above */
|
|
|
|
|
|
|
|
sock = accept(ask, NULL, 0);
|
|
|
|
if (sock < 0) {
|
|
|
|
pr_perror("Can't accept connection to the transport socket");
|
|
|
|
close(ask);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctl->tsock = sock;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parasite_init_daemon(struct parasite_ctl *ctl)
|
|
|
|
{
|
|
|
|
struct parasite_init_args *args;
|
|
|
|
pid_t pid = ctl->rpid;
|
|
|
|
user_regs_struct_t regs;
|
|
|
|
struct ctl_msg m = { };
|
|
|
|
|
|
|
|
*ctl->addr_cmd = PARASITE_CMD_INIT_DAEMON;
|
|
|
|
|
2016-09-28 12:12:09 +03:00
|
|
|
args = compel_parasite_args(ctl, struct parasite_init_args);
|
2016-09-28 11:29:01 +03:00
|
|
|
|
|
|
|
args->sigframe = (uintptr_t)ctl->rsigframe;
|
2016-10-31 15:06:48 +03:00
|
|
|
args->log_level = compel_log_get_loglevel();
|
2016-09-28 11:29:01 +03:00
|
|
|
|
|
|
|
futex_set(&args->daemon_connected, 0);
|
|
|
|
|
|
|
|
if (prepare_tsock(ctl, pid, args))
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* after this we can catch parasite errors in chld handler */
|
|
|
|
if (setup_child_handler(ctl))
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
regs = ctl->orig.regs;
|
|
|
|
if (parasite_run(pid, PTRACE_CONT, ctl->parasite_ip, ctl->rstack, ®s, &ctl->orig))
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
futex_wait_while_eq(&args->daemon_connected, 0);
|
|
|
|
if (futex_get(&args->daemon_connected) != 1) {
|
|
|
|
errno = -(int)futex_get(&args->daemon_connected);
|
|
|
|
pr_perror("Unable to connect a transport socket");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (accept_tsock(ctl) < 0)
|
|
|
|
goto err;
|
|
|
|
|
2016-11-03 02:08:14 +03:00
|
|
|
if (compel_util_send_fd(ctl, ctl->ictx.log_fd))
|
2016-09-28 11:29:01 +03:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
pr_info("Wait for parasite being daemonized...\n");
|
|
|
|
|
|
|
|
if (parasite_wait_ack(ctl->tsock, PARASITE_CMD_INIT_DAEMON, &m)) {
|
|
|
|
pr_err("Can't switch parasite %d to daemon mode %d\n",
|
|
|
|
pid, m.err);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2016-10-27 19:34:17 +03:00
|
|
|
ctl->sigreturn_addr = (void*)(uintptr_t)args->sigreturn_addr;
|
2016-09-28 11:29:01 +03:00
|
|
|
ctl->daemonized = true;
|
|
|
|
pr_info("Parasite %d has been switched to daemon mode\n", pid);
|
|
|
|
return 0;
|
|
|
|
err:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parasite_start_daemon(struct parasite_ctl *ctl)
|
|
|
|
{
|
|
|
|
pid_t pid = ctl->rpid;
|
|
|
|
struct infect_ctx *ictx = &ctl->ictx;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get task registers before going daemon, since the
|
2016-10-19 12:32:44 +03:00
|
|
|
* compel_get_task_regs needs to call ptrace on _stopped_ task,
|
2016-09-28 11:29:01 +03:00
|
|
|
* while in daemon it is not such.
|
|
|
|
*/
|
|
|
|
|
2016-11-14 16:05:22 +03:00
|
|
|
if (get_task_regs(pid, ctl->orig.regs, ictx->save_regs, ictx->regs_arg)) {
|
2016-09-28 11:29:01 +03:00
|
|
|
pr_err("Can't obtain regs for thread %d\n", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ictx->make_sigframe(ictx->regs_arg, ctl->sigframe, ctl->rsigframe, &ctl->orig.sigmask))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (parasite_init_daemon(ctl))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-29 16:22:19 +03:00
|
|
|
static int parasite_mmap_exchange(struct parasite_ctl *ctl, unsigned long size)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
2016-10-19 12:43:00 +03:00
|
|
|
ctl->remote_map = remote_mmap(ctl, NULL, size,
|
2016-09-29 16:22:19 +03:00
|
|
|
PROT_READ | PROT_WRITE | PROT_EXEC,
|
|
|
|
MAP_ANONYMOUS | MAP_SHARED, -1, 0);
|
|
|
|
if (!ctl->remote_map) {
|
|
|
|
pr_err("Can't allocate memory for parasite blob (pid: %d)\n", ctl->rpid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctl->map_length = round_up(size, page_size());
|
|
|
|
|
2016-10-27 18:07:10 +03:00
|
|
|
fd = ctl->ictx.open_proc(ctl->rpid, O_RDWR, "map_files/%p-%p",
|
2016-09-29 16:22:19 +03:00
|
|
|
ctl->remote_map, ctl->remote_map + ctl->map_length);
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ctl->local_map = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
|
|
|
MAP_SHARED | MAP_FILE, fd, 0);
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
if (ctl->local_map == MAP_FAILED) {
|
|
|
|
ctl->local_map = NULL;
|
|
|
|
pr_perror("Can't map remote parasite map");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parasite_memfd_exchange(struct parasite_ctl *ctl, unsigned long size)
|
|
|
|
{
|
|
|
|
void *where = (void *)ctl->ictx.syscall_ip + BUILTIN_SYSCALL_SIZE;
|
2016-10-31 15:06:48 +03:00
|
|
|
uint8_t orig_code[MEMFD_FNAME_SZ] = MEMFD_FNAME;
|
2016-09-29 16:22:19 +03:00
|
|
|
pid_t pid = ctl->rpid;
|
2016-12-17 03:21:56 -08:00
|
|
|
long sret = -ENOSYS;
|
2016-09-29 16:22:19 +03:00
|
|
|
int ret, fd, lfd;
|
2016-09-30 14:56:28 +03:00
|
|
|
bool __maybe_unused compat_task = !compel_mode_native(ctl);
|
2016-09-29 16:22:19 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-10-19 12:43:00 +03:00
|
|
|
ret = compel_syscall(ctl, __NR(memfd_create, compat_task), &sret,
|
2016-09-29 16:22:19 +03:00
|
|
|
(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)
|
2016-10-19 12:43:00 +03:00
|
|
|
compel_syscall(ctl, __NR(close, compat_task), &sret,
|
2016-09-29 16:22:19 +03:00
|
|
|
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)
|
|
|
|
return fd;
|
|
|
|
|
|
|
|
ctl->map_length = round_up(size, page_size());
|
2016-10-27 18:07:10 +03:00
|
|
|
lfd = ctl->ictx.open_proc(ctl->rpid, O_RDWR, "fd/%d", fd);
|
2016-09-29 16:22:19 +03:00
|
|
|
if (lfd < 0)
|
|
|
|
goto err_cure;
|
|
|
|
|
|
|
|
if (ftruncate(lfd, ctl->map_length) < 0) {
|
|
|
|
pr_perror("Fail to truncate memfd for parasite");
|
|
|
|
goto err_cure;
|
|
|
|
}
|
|
|
|
|
2016-10-19 12:43:00 +03:00
|
|
|
ctl->remote_map = remote_mmap(ctl, NULL, size,
|
2016-09-29 16:22:19 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-10-19 12:43:00 +03:00
|
|
|
compel_syscall(ctl, __NR(close, compat_task), &sret, fd, 0, 0, 0, 0, 0);
|
2016-09-29 16:22:19 +03:00
|
|
|
close(lfd);
|
|
|
|
|
|
|
|
pr_info("Set up parasite blob using memfd\n");
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_curef:
|
|
|
|
close(lfd);
|
|
|
|
err_cure:
|
2016-10-19 12:43:00 +03:00
|
|
|
compel_syscall(ctl, __NR(close, compat_task), &sret, fd, 0, 0, 0, 0, 0);
|
2016-09-29 16:22:19 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-11-07 01:20:46 +03:00
|
|
|
void compel_relocs_apply(void *mem, void *vbase, size_t size, compel_reloc_t *elf_relocs, size_t nr_relocs)
|
|
|
|
{
|
|
|
|
size_t i, j;
|
|
|
|
|
|
|
|
for (i = 0, j = 0; i < nr_relocs; i++) {
|
|
|
|
if (elf_relocs[i].type & COMPEL_TYPE_LONG) {
|
|
|
|
long *where = mem + elf_relocs[i].offset;
|
|
|
|
long *p = mem + size;
|
|
|
|
|
|
|
|
if (elf_relocs[i].type & COMPEL_TYPE_GOTPCREL) {
|
|
|
|
int *value = (int *)where;
|
|
|
|
int rel;
|
|
|
|
|
|
|
|
p[j] = (long)vbase + elf_relocs[i].value;
|
|
|
|
rel = (unsigned)((void *)&p[j] - (void *)mem) - elf_relocs[i].offset + elf_relocs[i].addend;
|
|
|
|
|
|
|
|
*value = rel;
|
|
|
|
j++;
|
|
|
|
} else
|
|
|
|
*where = elf_relocs[i].value + elf_relocs[i].addend + (unsigned long)vbase;
|
|
|
|
} else if (elf_relocs[i].type & COMPEL_TYPE_INT) {
|
|
|
|
int *where = (mem + elf_relocs[i].offset);
|
|
|
|
*where = elf_relocs[i].value + elf_relocs[i].addend + (unsigned long)vbase;
|
|
|
|
} else
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-29 16:22:19 +03:00
|
|
|
int compel_map_exchange(struct parasite_ctl *ctl, unsigned long size)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = parasite_memfd_exchange(ctl, size);
|
|
|
|
if (ret == 1) {
|
|
|
|
pr_info("MemFD parasite doesn't work, goto legacy mmap\n");
|
|
|
|
ret = parasite_mmap_exchange(ctl, size);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-06 13:14:13 +03:00
|
|
|
static inline unsigned long total_pie_size(size_t blob_size)
|
2016-11-25 16:21:12 +03:00
|
|
|
{
|
2017-02-06 13:14:13 +03:00
|
|
|
return round_up(blob_size, page_size());
|
2016-11-25 16:21:12 +03:00
|
|
|
}
|
|
|
|
|
2016-09-28 11:29:01 +03:00
|
|
|
int compel_infect(struct parasite_ctl *ctl, unsigned long nr_threads, unsigned long args_size)
|
|
|
|
{
|
|
|
|
int ret;
|
2017-02-06 13:14:12 +03:00
|
|
|
unsigned long p, map_exchange_size, parasite_size = 0;
|
2016-09-28 11:29:01 +03:00
|
|
|
|
2016-11-24 15:21:00 +03:00
|
|
|
if (ctl->pblob.parasite_type != COMPEL_BLOB_CHEADER)
|
|
|
|
goto err;
|
|
|
|
|
2016-11-03 02:08:14 +03:00
|
|
|
if (ctl->ictx.log_fd < 0)
|
|
|
|
goto err;
|
|
|
|
|
2016-09-28 11:29:01 +03:00
|
|
|
if (!arch_can_dump_task(ctl))
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Inject a parasite engine. Ie allocate memory inside alien
|
|
|
|
* space and copy engine code there. Then re-map the engine
|
|
|
|
* locally, so we will get an easy way to access engine memory
|
|
|
|
* without using ptrace at all.
|
|
|
|
*/
|
|
|
|
|
2017-02-06 13:14:13 +03:00
|
|
|
parasite_size = total_pie_size(ctl->pblob.hdr.bsize);
|
2016-09-28 11:29:01 +03:00
|
|
|
|
|
|
|
ctl->args_size = round_up(args_size, PAGE_SIZE);
|
|
|
|
parasite_size += ctl->args_size;
|
|
|
|
|
|
|
|
map_exchange_size = parasite_size;
|
|
|
|
map_exchange_size += RESTORE_STACK_SIGFRAME + PARASITE_STACK_SIZE;
|
|
|
|
if (nr_threads > 1)
|
|
|
|
map_exchange_size += PARASITE_STACK_SIZE;
|
|
|
|
|
2016-09-29 16:22:19 +03:00
|
|
|
ret = compel_map_exchange(ctl, map_exchange_size);
|
2016-09-28 11:29:01 +03:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
pr_info("Putting parasite blob into %p->%p\n", ctl->local_map, ctl->remote_map);
|
|
|
|
|
2016-11-24 15:21:00 +03:00
|
|
|
ctl->parasite_ip = (unsigned long)(ctl->remote_map + ctl->pblob.hdr.parasite_ip_off);
|
|
|
|
ctl->addr_cmd = ctl->local_map + ctl->pblob.hdr.addr_cmd_off;
|
|
|
|
ctl->addr_args = ctl->local_map + ctl->pblob.hdr.addr_arg_off;
|
2016-11-04 00:30:00 +03:00
|
|
|
|
2017-02-06 13:14:12 +03:00
|
|
|
memcpy(ctl->local_map, ctl->pblob.hdr.mem, ctl->pblob.hdr.bsize);
|
2016-11-24 15:21:00 +03:00
|
|
|
if (ctl->pblob.hdr.nr_relocs)
|
|
|
|
compel_relocs_apply(ctl->local_map, ctl->remote_map, ctl->pblob.hdr.bsize,
|
|
|
|
ctl->pblob.hdr.relocs, ctl->pblob.hdr.nr_relocs);
|
2016-09-28 11:29:01 +03:00
|
|
|
|
|
|
|
p = parasite_size;
|
|
|
|
|
|
|
|
ctl->rsigframe = ctl->remote_map + p;
|
|
|
|
ctl->sigframe = ctl->local_map + p;
|
|
|
|
|
|
|
|
p += RESTORE_STACK_SIGFRAME;
|
|
|
|
p += PARASITE_STACK_SIZE;
|
|
|
|
ctl->rstack = ctl->remote_map + p;
|
|
|
|
|
|
|
|
if (nr_threads > 1) {
|
|
|
|
p += PARASITE_STACK_SIZE;
|
|
|
|
ctl->r_thread_stack = ctl->remote_map + p;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parasite_start_daemon(ctl))
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-11-14 16:05:08 +03:00
|
|
|
struct parasite_thread_ctl *compel_prepare_thread(struct parasite_ctl *ctl, int pid)
|
|
|
|
{
|
|
|
|
struct parasite_thread_ctl *tctl;
|
|
|
|
|
|
|
|
tctl = xmalloc(sizeof(*tctl));
|
|
|
|
if (tctl) {
|
|
|
|
if (prepare_thread(pid, &tctl->th)) {
|
|
|
|
xfree(tctl);
|
|
|
|
tctl = NULL;
|
2016-11-14 16:05:49 +03:00
|
|
|
} else {
|
|
|
|
tctl->tid = pid;
|
|
|
|
tctl->ctl = ctl;
|
2016-11-14 16:05:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return tctl;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int prepare_thread(int pid, struct thread_ctx *ctx)
|
2016-09-28 11:50:25 +03:00
|
|
|
{
|
|
|
|
if (ptrace(PTRACE_GETSIGMASK, pid, sizeof(k_rtsigset_t), &ctx->sigmask)) {
|
|
|
|
pr_perror("can't get signal blocking mask for %d", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptrace_get_regs(pid, &ctx->regs)) {
|
|
|
|
pr_perror("Can't obtain registers (pid: %d)", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-14 16:05:08 +03:00
|
|
|
void compel_release_thread(struct parasite_thread_ctl *tctl)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* No stuff to cure in thread here, all routines leave the
|
|
|
|
* guy intact (for now)
|
|
|
|
*/
|
|
|
|
xfree(tctl);
|
|
|
|
}
|
|
|
|
|
2016-11-21 21:26:13 +03:00
|
|
|
struct parasite_ctl *compel_prepare_noctx(int pid)
|
2016-09-28 11:50:25 +03:00
|
|
|
{
|
|
|
|
struct parasite_ctl *ctl = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Control block early setup.
|
|
|
|
*/
|
|
|
|
ctl = xzalloc(sizeof(*ctl));
|
|
|
|
if (!ctl) {
|
|
|
|
pr_err("Parasite control block allocation failed (pid: %d)\n", pid);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctl->tsock = -1;
|
2016-11-03 02:08:14 +03:00
|
|
|
ctl->ictx.log_fd = -1;
|
2016-09-28 11:50:25 +03:00
|
|
|
|
2016-11-14 16:05:08 +03:00
|
|
|
if (prepare_thread(pid, &ctl->orig))
|
2016-09-28 11:50:25 +03:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
ctl->rpid = pid;
|
|
|
|
|
|
|
|
BUILD_BUG_ON(PARASITE_START_AREA_MIN < BUILTIN_SYSCALL_SIZE + MEMFD_FNAME_SZ);
|
|
|
|
|
|
|
|
return ctl;
|
|
|
|
|
|
|
|
err:
|
|
|
|
xfree(ctl);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-11-21 21:26:14 +03:00
|
|
|
/*
|
|
|
|
* Find first executable VMA that would fit the initial
|
|
|
|
* syscall injection.
|
|
|
|
*/
|
|
|
|
static unsigned long find_executable_area(int pid)
|
|
|
|
{
|
|
|
|
char aux[128];
|
|
|
|
FILE *f;
|
|
|
|
unsigned long ret = (unsigned long)MAP_FAILED;
|
|
|
|
|
|
|
|
sprintf(aux, "/proc/%d/maps", pid);
|
|
|
|
f = fopen(aux, "r");
|
|
|
|
if (!f)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
while (fgets(aux, sizeof(aux), f)) {
|
|
|
|
unsigned long start, end;
|
|
|
|
char *f;
|
|
|
|
|
|
|
|
start = strtoul(aux, &f, 16);
|
|
|
|
end = strtoul(f + 1, &f, 16);
|
|
|
|
|
|
|
|
/* f now points at " rwx" (yes, with space) part */
|
|
|
|
if (f[3] == 'x') {
|
|
|
|
BUG_ON(end - start < PARASITE_START_AREA_MIN);
|
|
|
|
ret = start;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-11-21 21:26:16 +03:00
|
|
|
/*
|
|
|
|
* This routine is to create PF_UNIX/SOCK_SEQPACKET socket
|
|
|
|
* in the target net namespace
|
|
|
|
*/
|
|
|
|
static int make_sock_for(int pid)
|
|
|
|
{
|
2016-11-23 18:33:56 +03:00
|
|
|
int ret, mfd, fd, sk = -1;
|
2016-11-21 21:26:16 +03:00
|
|
|
char p[32];
|
|
|
|
|
2016-11-23 18:33:56 +03:00
|
|
|
pr_debug("Preparing seqsk for %d\n", pid);
|
|
|
|
|
2016-11-21 21:26:16 +03:00
|
|
|
sprintf(p, "/proc/%d/ns/net", pid);
|
|
|
|
fd = open(p, O_RDONLY);
|
2016-11-23 18:33:56 +03:00
|
|
|
if (fd < 0) {
|
|
|
|
pr_perror("Can't open %p", p);
|
2016-11-21 21:26:16 +03:00
|
|
|
goto out;
|
2016-11-23 18:33:56 +03:00
|
|
|
}
|
2016-11-21 21:26:16 +03:00
|
|
|
|
|
|
|
mfd = open("/proc/self/ns/net", O_RDONLY);
|
2016-11-23 18:33:56 +03:00
|
|
|
if (mfd < 0) {
|
|
|
|
pr_perror("Can't open self netns");
|
2016-11-21 21:26:16 +03:00
|
|
|
goto out_c;
|
2016-11-23 18:33:56 +03:00
|
|
|
}
|
2016-11-21 21:26:16 +03:00
|
|
|
|
2016-11-23 18:33:56 +03:00
|
|
|
if (setns(fd, CLONE_NEWNET)) {
|
|
|
|
pr_perror("Can't setup target netns");
|
2016-11-21 21:26:16 +03:00
|
|
|
goto out_cm;
|
2016-11-23 18:33:56 +03:00
|
|
|
}
|
2016-11-21 21:26:16 +03:00
|
|
|
|
2016-11-23 18:33:56 +03:00
|
|
|
sk = socket(PF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0);
|
|
|
|
if (sk < 0)
|
|
|
|
pr_perror("Can't create seqsk");
|
2016-11-21 21:26:16 +03:00
|
|
|
|
2016-11-23 18:33:56 +03:00
|
|
|
ret = setns(mfd, CLONE_NEWNET);
|
|
|
|
if (ret) {
|
|
|
|
pr_perror("Can't restore former netns");
|
|
|
|
if (sk >= 0)
|
|
|
|
close(sk);
|
|
|
|
sk = -1;
|
|
|
|
}
|
2016-11-21 21:26:16 +03:00
|
|
|
out_cm:
|
|
|
|
close(mfd);
|
|
|
|
out_c:
|
|
|
|
close(fd);
|
|
|
|
out:
|
2016-11-23 18:33:56 +03:00
|
|
|
return sk;
|
2016-11-21 21:26:16 +03:00
|
|
|
}
|
|
|
|
|
2016-11-21 21:26:15 +03:00
|
|
|
static int simple_open_proc(int pid, int mode, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
int l;
|
|
|
|
char path[128];
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
l = sprintf(path, "/proc/%d/", pid);
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
vsnprintf(path + l, sizeof(path) - l, fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return open(path, mode);
|
|
|
|
}
|
|
|
|
|
2016-11-21 21:26:17 +03:00
|
|
|
static void handle_sigchld(int signal, siginfo_t *siginfo, void *data)
|
|
|
|
{
|
2016-11-23 18:33:56 +03:00
|
|
|
int pid, status;
|
|
|
|
|
|
|
|
pid = waitpid(-1, &status, WNOHANG);
|
|
|
|
if (pid <= 0)
|
|
|
|
return;
|
2016-11-21 21:26:17 +03:00
|
|
|
|
2016-11-23 18:33:56 +03:00
|
|
|
pr_err("si_code=%d si_pid=%d si_status=%d\n",
|
|
|
|
siginfo->si_code, siginfo->si_pid, siginfo->si_status);
|
|
|
|
|
|
|
|
if (WIFEXITED(status))
|
|
|
|
pr_err("%d exited with %d unexpectedly\n", pid, WEXITSTATUS(status));
|
|
|
|
else if (WIFSIGNALED(status))
|
|
|
|
pr_err("%d was killed by %d unexpectedly: %s\n",
|
|
|
|
pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
|
|
|
|
else if (WIFSTOPPED(status))
|
|
|
|
pr_err("%d was stopped by %d unexpectedly\n", pid, WSTOPSIG(status));
|
|
|
|
|
|
|
|
/* FIXME Should we exit? */
|
|
|
|
/* exit(1); */
|
2016-11-21 21:26:17 +03:00
|
|
|
}
|
|
|
|
|
2016-11-21 21:26:18 +03:00
|
|
|
struct plain_regs_struct {
|
|
|
|
user_regs_struct_t regs;
|
|
|
|
user_fpregs_struct_t fpregs;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int save_regs_plain(void *to, user_regs_struct_t *r, user_fpregs_struct_t *f)
|
|
|
|
{
|
|
|
|
struct plain_regs_struct *prs = to;
|
|
|
|
|
|
|
|
prs->regs = *r;
|
|
|
|
prs->fpregs = *f;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef RT_SIGFRAME_UC_SIGMASK
|
|
|
|
#define RT_SIGFRAME_UC_SIGMASK(sigframe) \
|
|
|
|
(k_rtsigset_t*)&RT_SIGFRAME_UC(sigframe)->uc_sigmask
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int make_sigframe_plain(void *from, struct rt_sigframe *f, struct rt_sigframe *rtf, k_rtsigset_t *b)
|
|
|
|
{
|
|
|
|
struct plain_regs_struct *prs = from;
|
|
|
|
k_rtsigset_t *blk_sigset;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure it's zeroified.
|
|
|
|
*/
|
|
|
|
memset(f, 0, sizeof(*f));
|
|
|
|
|
|
|
|
if (sigreturn_prep_regs_plain(f, &prs->regs, &prs->fpregs))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
blk_sigset = RT_SIGFRAME_UC_SIGMASK(f);
|
|
|
|
if (b)
|
|
|
|
memcpy(blk_sigset, b, sizeof(k_rtsigset_t));
|
|
|
|
else
|
|
|
|
memset(blk_sigset, 0, sizeof(k_rtsigset_t));
|
|
|
|
|
|
|
|
if (RT_SIGFRAME_HAS_FPU(f)) {
|
|
|
|
if (sigreturn_prep_fpu_frame_plain(f, rtf))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FIXME What about sas?
|
|
|
|
* setup_sas(sigframe, core->thread_core->sas);
|
|
|
|
*/
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-21 21:26:13 +03:00
|
|
|
struct parasite_ctl *compel_prepare(int pid)
|
|
|
|
{
|
|
|
|
struct parasite_ctl *ctl;
|
|
|
|
struct infect_ctx *ictx;
|
|
|
|
|
|
|
|
ctl = compel_prepare_noctx(pid);
|
|
|
|
if (ctl == NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ictx = &ctl->ictx;
|
|
|
|
ictx->task_size = compel_task_size();
|
2016-11-21 21:26:15 +03:00
|
|
|
ictx->open_proc = simple_open_proc;
|
2016-11-21 21:26:14 +03:00
|
|
|
ictx->syscall_ip = find_executable_area(pid);
|
2016-11-21 21:26:17 +03:00
|
|
|
ictx->child_handler = handle_sigchld;
|
|
|
|
sigaction(SIGCHLD, NULL, &ictx->orig_handler);
|
|
|
|
|
2016-11-21 21:26:18 +03:00
|
|
|
ictx->save_regs = save_regs_plain;
|
|
|
|
ictx->make_sigframe = make_sigframe_plain;
|
|
|
|
ictx->regs_arg = xmalloc(sizeof(struct plain_regs_struct));
|
|
|
|
if (ictx->regs_arg == NULL)
|
|
|
|
goto err;
|
|
|
|
|
2016-11-21 21:26:14 +03:00
|
|
|
if (ictx->syscall_ip == (unsigned long)MAP_FAILED)
|
|
|
|
goto err;
|
2016-11-21 21:26:16 +03:00
|
|
|
ictx->sock = make_sock_for(pid);
|
|
|
|
if (ictx->sock < 0)
|
|
|
|
goto err;
|
2016-11-21 21:26:14 +03:00
|
|
|
|
2016-11-21 21:26:13 +03:00
|
|
|
out:
|
|
|
|
return ctl;
|
2016-11-21 21:26:14 +03:00
|
|
|
|
|
|
|
err:
|
2016-11-23 18:33:56 +03:00
|
|
|
xfree(ictx->regs_arg);
|
|
|
|
xfree(ctl);
|
|
|
|
ctl = NULL;
|
2016-11-21 21:26:14 +03:00
|
|
|
goto out;
|
2016-11-21 21:26:13 +03:00
|
|
|
}
|
|
|
|
|
2016-09-28 12:01:01 +03:00
|
|
|
static bool task_in_parasite(struct parasite_ctl *ctl, user_regs_struct_t *regs)
|
|
|
|
{
|
|
|
|
void *addr = (void *) REG_IP(*regs);
|
|
|
|
return addr >= ctl->remote_map &&
|
|
|
|
addr < ctl->remote_map + ctl->map_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parasite_fini_seized(struct parasite_ctl *ctl)
|
|
|
|
{
|
|
|
|
pid_t pid = ctl->rpid;
|
|
|
|
user_regs_struct_t regs;
|
|
|
|
int status, ret = 0;
|
|
|
|
enum trace_flags flag;
|
|
|
|
|
|
|
|
/* stop getting chld from parasite -- we're about to step-by-step it */
|
2016-11-21 21:26:17 +03:00
|
|
|
if (restore_child_handler(ctl))
|
2016-09-28 12:01:01 +03:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Start to trace syscalls for each thread */
|
|
|
|
if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL)) {
|
|
|
|
pr_perror("Unable to interrupt the process");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("Waiting for %d to trap\n", pid);
|
|
|
|
if (wait4(pid, &status, __WALL, NULL) != pid) {
|
|
|
|
pr_perror("Waited pid mismatch (pid: %d)", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("Daemon %d exited trapping\n", pid);
|
|
|
|
if (!WIFSTOPPED(status)) {
|
|
|
|
pr_err("Task is still running (pid: %d)\n", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ptrace_get_regs(pid, ®s);
|
|
|
|
if (ret) {
|
|
|
|
pr_perror("Unable to get registers");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!task_in_parasite(ctl, ®s)) {
|
|
|
|
pr_err("The task is not in parasite code\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-09-30 14:34:50 +03:00
|
|
|
ret = compel_rpc_call(PARASITE_CMD_FINI, ctl);
|
2016-09-28 12:01:01 +03:00
|
|
|
close_safe(&ctl->tsock);
|
|
|
|
if (ret)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Go to sigreturn as closer as we can */
|
2016-09-29 16:43:29 +03:00
|
|
|
ret = compel_stop_pie(pid, ctl->sigreturn_addr, &flag,
|
|
|
|
ctl->ictx.flags & INFECT_NO_BREAKPOINTS);
|
2016-09-28 12:01:01 +03:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2016-09-29 16:43:29 +03:00
|
|
|
if (compel_stop_on_syscall(1, __NR(rt_sigreturn, 0),
|
2016-09-28 12:01:01 +03:00
|
|
|
__NR(rt_sigreturn, 1), flag))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (ptrace_flush_breakpoints(pid))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All signals are unblocked now. The kernel notifies about leaving
|
|
|
|
* syscall before starting to deliver signals. All parasite code are
|
|
|
|
* executed with blocked signals, so we can sefly unmap a parasite blob.
|
|
|
|
*/
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int compel_stop_daemon(struct parasite_ctl *ctl)
|
|
|
|
{
|
|
|
|
if (ctl->daemonized) {
|
|
|
|
/*
|
|
|
|
* Looks like a previous attempt failed, we should do
|
|
|
|
* nothing in this case. parasite will try to cure itself.
|
|
|
|
*/
|
|
|
|
if (ctl->tsock < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (parasite_fini_seized(ctl)) {
|
|
|
|
close_safe(&ctl->tsock);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ctl->daemonized = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int compel_cure_remote(struct parasite_ctl *ctl)
|
|
|
|
{
|
2016-12-17 03:21:56 -08:00
|
|
|
long ret;
|
compel: kill self-unmap in parasite
Why should we have self-unmapping code in parasite?
It looks like, we can drop this code using simple sys_unmap()
injection (like that I did for `criu exec` action and for cases where we
failed to insert parasite by some reason, but still need to unmap remotes).
It's an RFC, so just a suggestion - maybe I miss something you have in
mind - please, describe that/those things.
My motivation is:
- less code, defined commands for PIE, one BUG() less, one jump to PIE less
- I'm making one 64-bit parasite on x86 instead of two 32 and 64 bit.
It works (branch 32-one-parasite) with long-jump in the beginning to
64-bit code from 32-bit task.
On parasite curing it sig-returns from 64-bit parasite to 32-bit task,
this point we're trapping in CRIU. After that we command parasite to
unmap itself, so it long-jumps again to parasite 64-bit code, unmaps,
we caught task after sys_unmap and the task is with 64-bit CS.
We can't set 32-bit registers after this - kernel checks that
registers set is the same on PTRACE_SETREGSET:
> > static int ptrace_regset(struct task_struct *task, int req, unsigned int type,
> > struct iovec *kiov)
...
> > if (!regset || (kiov->iov_len % regset->size) != 0)
> > return -EINVAL;
So, to return again to 32-bit task I need sigreturn() again or add
long-jump with 32-bit CS.
I've disable that for 32-bit testing with (in compel_cure_remote):
- if (ctl->addr_cmd) {
+ if (ctl->addr_cmd && user_regs_native(&ctl->orig.regs)) {
And it works. It also works for native tasks, so why should we keep it?
travis-ci: success for compel: kill self-unmap in parasite
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: Pavel Emelyanov <xemul@virtuozzo.com>
Cc: Andrei Vagin <avagin@virtuozzo.com>
Signed-off-by: Dmitry Safonov <dsafonov@virtuozzo.com>
Acked-by: Andrei Vagin <avagin@virtuozzo.com>
Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
Signed-off-by: Andrei Vagin <avagin@virtuozzo.com>
2016-11-25 15:51:00 +03:00
|
|
|
|
2016-09-28 12:01:01 +03:00
|
|
|
if (compel_stop_daemon(ctl))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!ctl->remote_map)
|
|
|
|
return 0;
|
|
|
|
|
compel: kill self-unmap in parasite
Why should we have self-unmapping code in parasite?
It looks like, we can drop this code using simple sys_unmap()
injection (like that I did for `criu exec` action and for cases where we
failed to insert parasite by some reason, but still need to unmap remotes).
It's an RFC, so just a suggestion - maybe I miss something you have in
mind - please, describe that/those things.
My motivation is:
- less code, defined commands for PIE, one BUG() less, one jump to PIE less
- I'm making one 64-bit parasite on x86 instead of two 32 and 64 bit.
It works (branch 32-one-parasite) with long-jump in the beginning to
64-bit code from 32-bit task.
On parasite curing it sig-returns from 64-bit parasite to 32-bit task,
this point we're trapping in CRIU. After that we command parasite to
unmap itself, so it long-jumps again to parasite 64-bit code, unmaps,
we caught task after sys_unmap and the task is with 64-bit CS.
We can't set 32-bit registers after this - kernel checks that
registers set is the same on PTRACE_SETREGSET:
> > static int ptrace_regset(struct task_struct *task, int req, unsigned int type,
> > struct iovec *kiov)
...
> > if (!regset || (kiov->iov_len % regset->size) != 0)
> > return -EINVAL;
So, to return again to 32-bit task I need sigreturn() again or add
long-jump with 32-bit CS.
I've disable that for 32-bit testing with (in compel_cure_remote):
- if (ctl->addr_cmd) {
+ if (ctl->addr_cmd && user_regs_native(&ctl->orig.regs)) {
And it works. It also works for native tasks, so why should we keep it?
travis-ci: success for compel: kill self-unmap in parasite
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: Pavel Emelyanov <xemul@virtuozzo.com>
Cc: Andrei Vagin <avagin@virtuozzo.com>
Signed-off-by: Dmitry Safonov <dsafonov@virtuozzo.com>
Acked-by: Andrei Vagin <avagin@virtuozzo.com>
Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
Signed-off-by: Andrei Vagin <avagin@virtuozzo.com>
2016-11-25 15:51:00 +03:00
|
|
|
compel_syscall(ctl, __NR(munmap, !compel_mode_native(ctl)), &ret,
|
|
|
|
(unsigned long)ctl->remote_map, ctl->map_length,
|
|
|
|
0, 0, 0, 0);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("munmap for remote map %p, %lu returned %lu\n",
|
|
|
|
ctl->remote_map, ctl->map_length, ret);
|
|
|
|
return -1;
|
2016-09-28 12:01:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int compel_cure_local(struct parasite_ctl *ctl)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (ctl->local_map) {
|
|
|
|
if (munmap(ctl->local_map, ctl->map_length)) {
|
|
|
|
pr_err("munmap failed (pid: %d)\n", ctl->rpid);
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(ctl);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int compel_cure(struct parasite_ctl *ctl)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = compel_cure_remote(ctl);
|
|
|
|
if (!ret)
|
|
|
|
ret = compel_cure_local(ctl);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-09-28 12:12:09 +03:00
|
|
|
void *compel_parasite_args_p(struct parasite_ctl *ctl)
|
|
|
|
{
|
|
|
|
return ctl->addr_args;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *compel_parasite_args_s(struct parasite_ctl *ctl, int args_size)
|
|
|
|
{
|
|
|
|
BUG_ON(args_size > ctl->args_size);
|
|
|
|
return compel_parasite_args_p(ctl);
|
|
|
|
}
|
|
|
|
|
2016-11-14 16:05:49 +03:00
|
|
|
int compel_run_in_thread(struct parasite_thread_ctl *tctl, unsigned int cmd)
|
2016-09-28 13:58:44 +03:00
|
|
|
{
|
2016-11-14 16:05:49 +03:00
|
|
|
int pid = tctl->tid;
|
|
|
|
struct parasite_ctl *ctl = tctl->ctl;
|
2016-11-14 16:05:08 +03:00
|
|
|
struct thread_ctx *octx = &tctl->th;
|
2016-09-28 13:58:44 +03:00
|
|
|
void *stack = ctl->r_thread_stack;
|
|
|
|
user_regs_struct_t regs = octx->regs;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
*ctl->addr_cmd = cmd;
|
|
|
|
|
|
|
|
ret = parasite_run(pid, PTRACE_CONT, ctl->parasite_ip, stack, ®s, octx);
|
|
|
|
if (ret == 0)
|
|
|
|
ret = parasite_trap(ctl, pid, ®s, octx);
|
|
|
|
if (ret == 0)
|
|
|
|
ret = (int)REG_RES(regs);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
pr_err("Parasite exited with %d\n", ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-09-28 14:07:16 +03:00
|
|
|
/*
|
|
|
|
* compel_unmap() is used for unmapping parasite and restorer blobs.
|
|
|
|
* A blob can contain code for unmapping itself, so the porcess is
|
|
|
|
* trapped on the exit from the munmap syscall.
|
|
|
|
*/
|
|
|
|
int compel_unmap(struct parasite_ctl *ctl, unsigned long addr)
|
|
|
|
{
|
|
|
|
user_regs_struct_t regs = ctl->orig.regs;
|
|
|
|
pid_t pid = ctl->rpid;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
ret = parasite_run(pid, PTRACE_SYSCALL, addr, ctl->rstack, ®s, &ctl->orig);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2016-09-29 16:43:29 +03:00
|
|
|
ret = compel_stop_on_syscall(1, __NR(munmap, 0),
|
2016-09-28 14:07:16 +03:00
|
|
|
__NR(munmap, 1), TRACE_ENTER);
|
|
|
|
|
|
|
|
if (restore_thread_ctx(pid, &ctl->orig))
|
|
|
|
ret = -1;
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-09-29 16:43:29 +03:00
|
|
|
int compel_stop_pie(pid_t pid, void *addr, enum trace_flags *tf, bool no_bp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (no_bp) {
|
|
|
|
pr_debug("Force no-breakpoints restore\n");
|
|
|
|
ret = 0;
|
|
|
|
} else
|
|
|
|
ret = ptrace_set_breakpoint(pid, addr);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (ret > 0) {
|
|
|
|
/*
|
|
|
|
* PIE will stop on a breakpoint, next
|
|
|
|
* stop after that will be syscall enter.
|
|
|
|
*/
|
|
|
|
*tf = TRACE_EXIT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No breakpoints available -- start tracing it
|
|
|
|
* in a per-syscall manner.
|
|
|
|
*/
|
|
|
|
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
|
|
|
if (ret) {
|
|
|
|
pr_perror("Unable to restart the %d process", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*tf = TRACE_ENTER;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool task_is_trapped(int status, pid_t pid)
|
|
|
|
{
|
|
|
|
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
pr_err("Task %d is in unexpected state: %x\n", pid, status);
|
|
|
|
if (WIFEXITED(status))
|
|
|
|
pr_err("Task exited with %d\n", WEXITSTATUS(status));
|
|
|
|
if (WIFSIGNALED(status))
|
|
|
|
pr_err("Task signaled with %d: %s\n",
|
|
|
|
WTERMSIG(status), strsignal(WTERMSIG(status)));
|
|
|
|
if (WIFSTOPPED(status))
|
|
|
|
pr_err("Task stopped with %d: %s\n",
|
|
|
|
WSTOPSIG(status), strsignal(WSTOPSIG(status)));
|
|
|
|
if (WIFCONTINUED(status))
|
|
|
|
pr_err("Task continued\n");
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int is_required_syscall(user_regs_struct_t *regs, pid_t pid,
|
|
|
|
const int sys_nr, const int sys_nr_compat)
|
|
|
|
{
|
|
|
|
const char *mode = user_regs_native(regs) ? "native" : "compat";
|
|
|
|
int req_sysnr = user_regs_native(regs) ? sys_nr : sys_nr_compat;
|
|
|
|
|
|
|
|
pr_debug("%d (%s) is going to execute the syscall %lu, required is %d\n",
|
|
|
|
pid, mode, REG_SYSCALL_NR(*regs), req_sysnr);
|
|
|
|
|
|
|
|
return (REG_SYSCALL_NR(*regs) == req_sysnr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Trap tasks on the exit from the specified syscall
|
|
|
|
*
|
|
|
|
* tasks - number of processes, which should be trapped
|
|
|
|
* sys_nr - the required syscall number
|
|
|
|
* sys_nr_compat - the required compatible syscall number
|
|
|
|
*/
|
|
|
|
int compel_stop_on_syscall(int tasks,
|
|
|
|
const int sys_nr, const int sys_nr_compat,
|
|
|
|
enum trace_flags trace)
|
|
|
|
{
|
|
|
|
user_regs_struct_t regs;
|
|
|
|
int status, ret;
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
if (tasks > 1)
|
|
|
|
trace = TRACE_ALL;
|
|
|
|
|
|
|
|
/* Stop all threads on the enter point in sys_rt_sigreturn */
|
|
|
|
while (tasks) {
|
|
|
|
pid = wait4(-1, &status, __WALL, NULL);
|
|
|
|
if (pid == -1) {
|
|
|
|
pr_perror("wait4 failed");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!task_is_trapped(status, pid))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
pr_debug("%d was trapped\n", pid);
|
|
|
|
|
|
|
|
if (trace == TRACE_EXIT) {
|
|
|
|
trace = TRACE_ENTER;
|
|
|
|
pr_debug("`- Expecting exit\n");
|
|
|
|
goto goon;
|
|
|
|
}
|
|
|
|
if (trace == TRACE_ENTER)
|
|
|
|
trace = TRACE_EXIT;
|
|
|
|
|
|
|
|
ret = ptrace_get_regs(pid, ®s);
|
|
|
|
if (ret) {
|
|
|
|
pr_perror("ptrace");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_required_syscall(®s, pid, sys_nr, sys_nr_compat)) {
|
|
|
|
/*
|
|
|
|
* The process is going to execute the required syscall,
|
|
|
|
* the next stop will be on the exit from this syscall
|
|
|
|
*/
|
|
|
|
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
|
|
|
if (ret) {
|
|
|
|
pr_perror("ptrace");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pid = wait4(pid, &status, __WALL, NULL);
|
|
|
|
if (pid == -1) {
|
|
|
|
pr_perror("wait4 failed");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!task_is_trapped(status, pid))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
pr_debug("%d was stopped\n", pid);
|
|
|
|
tasks--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
goon:
|
|
|
|
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
|
|
|
if (ret) {
|
|
|
|
pr_perror("ptrace");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-09-30 14:56:28 +03:00
|
|
|
|
|
|
|
int compel_mode_native(struct parasite_ctl *ctl)
|
|
|
|
{
|
|
|
|
return user_regs_native(&ctl->orig.regs);
|
|
|
|
}
|
2016-09-30 14:58:29 +03:00
|
|
|
|
2016-11-14 16:05:08 +03:00
|
|
|
static inline k_rtsigset_t *thread_ctx_sigmask(struct thread_ctx *tctx)
|
2016-11-14 16:04:54 +03:00
|
|
|
{
|
|
|
|
return &tctx->sigmask;
|
|
|
|
}
|
|
|
|
|
2016-11-14 16:05:08 +03:00
|
|
|
k_rtsigset_t *compel_thread_sigmask(struct parasite_thread_ctl *tctl)
|
|
|
|
{
|
|
|
|
return thread_ctx_sigmask(&tctl->th);
|
|
|
|
}
|
|
|
|
|
2016-09-30 14:58:29 +03:00
|
|
|
k_rtsigset_t *compel_task_sigmask(struct parasite_ctl *ctl)
|
|
|
|
{
|
2016-11-14 16:05:08 +03:00
|
|
|
return thread_ctx_sigmask(&ctl->orig);
|
2016-09-30 14:58:29 +03:00
|
|
|
}
|
2016-09-30 14:53:49 +03:00
|
|
|
|
2016-11-14 16:05:49 +03:00
|
|
|
int compel_get_thread_regs(struct parasite_thread_ctl *tctl, save_regs_t save, void * arg)
|
2016-11-14 16:05:22 +03:00
|
|
|
{
|
2016-11-14 16:05:49 +03:00
|
|
|
return get_task_regs(tctl->tid, tctl->th.regs, save, arg);
|
2016-11-14 16:05:22 +03:00
|
|
|
}
|
|
|
|
|
2016-09-30 14:53:49 +03:00
|
|
|
struct infect_ctx *compel_infect_ctx(struct parasite_ctl *ctl)
|
|
|
|
{
|
|
|
|
return &ctl->ictx;
|
|
|
|
}
|
2016-11-04 00:30:00 +03:00
|
|
|
|
|
|
|
struct parasite_blob_desc *compel_parasite_blob_desc(struct parasite_ctl *ctl)
|
|
|
|
{
|
|
|
|
return &ctl->pblob;
|
|
|
|
}
|