Commit dcb1cbfb authored by Cyrill Gorcunov's avatar Cyrill Gorcunov

Rework parasite code

 - make control block to keep all information
   needed to run injected syscall and parasite
   blobs

 - add ptrace_swap_area helper

 - handle both parasite engine calls and injected
   syscalls by single __parasite_execute function

 - drop jerr() usage

 - bring back handling of inflight signals from
   original program inside parasite code
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
parent 47ee1213
...@@ -1225,7 +1225,7 @@ static int dump_one_task(struct pstree_item *item, struct cr_fdset *cr_fdset) ...@@ -1225,7 +1225,7 @@ static int dump_one_task(struct pstree_item *item, struct cr_fdset *cr_fdset)
goto err; goto err;
} }
ret = parasite_cure_seized(parasite_ctl, &vma_area_list); ret = parasite_cure_seized(parasite_ctl);
if (ret) { if (ret) {
pr_err("Can't cure (pid: %d) from parasite\n", pid); pr_err("Can't cure (pid: %d) from parasite\n", pid);
goto err; goto err;
......
...@@ -13,26 +13,31 @@ ...@@ -13,26 +13,31 @@
/* parasite control block */ /* parasite control block */
struct parasite_ctl { struct parasite_ctl {
pid_t pid; /* process pid where we live in */ pid_t pid; /* process pid where we live in */
void * remote_map; void * remote_map;
void * local_map; void * local_map;
unsigned long map_length; unsigned long map_length;
unsigned long parasite_ip; /* service routine start ip */
void * addr_cmd; /* addr for command */
void * addr_args; /* address for arguments */
};
extern int can_run_syscall(unsigned long ip, unsigned long start, unsigned long end); unsigned long parasite_ip; /* service routine start ip */
user_regs_struct_t regs_orig; /* original registers */
unsigned long syscall_ip; /* entry point of infection */
u8 code_orig[BUILTIN_SYSCALL_SIZE];
unsigned long status;
void * addr_cmd; /* addr for command */
void * addr_args; /* address for arguments */
};
extern int parasite_dump_pages_seized(struct parasite_ctl *ctl, struct list_head *vma_area_list,
struct cr_fdset *cr_fdset);
extern int parasite_dump_sigacts_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset); extern int parasite_dump_sigacts_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset);
extern int parasite_dump_itimers_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset); extern int parasite_dump_itimers_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset);
struct parasite_dump_misc; struct parasite_dump_misc;
extern int parasite_dump_misc_seized(struct parasite_ctl *ctl, struct parasite_dump_misc *); extern int parasite_dump_misc_seized(struct parasite_ctl *ctl, struct parasite_dump_misc *misc);
extern int parasite_dump_pages_seized(struct parasite_ctl *ctl,
extern struct parasite_ctl *parasite_infect_seized(pid_t pid, int pid_dir, struct list_head *vma_area_list); struct list_head *vma_area_list,
extern int parasite_cure_seized(struct parasite_ctl *ctl, struct list_head *vma_area_list); struct cr_fdset *cr_fdset);
extern int parasite_cure_seized(struct parasite_ctl *ctl);
extern struct parasite_ctl *parasite_infect_seized(pid_t pid, int pid_dir,
struct list_head *vma_area_list);
#endif /* PARASITE_SYSCALL_H_ */ #endif /* PARASITE_SYSCALL_H_ */
...@@ -37,6 +37,7 @@ extern int seize_task(pid_t pid); ...@@ -37,6 +37,7 @@ extern int seize_task(pid_t pid);
extern int unseize_task(pid_t pid, enum cr_task_state st); extern int unseize_task(pid_t pid, enum cr_task_state st);
extern int ptrace_peek_area(pid_t pid, void *dst, void *addr, long bytes); extern int ptrace_peek_area(pid_t pid, void *dst, void *addr, long bytes);
extern int ptrace_poke_area(pid_t pid, void *src, void *addr, long bytes); extern int ptrace_poke_area(pid_t pid, void *src, void *addr, long bytes);
extern int ptrace_swap_area(pid_t pid, void *dst, void *src, long bytes);
extern int ptrace_show_area(pid_t pid, void *addr, long bytes); extern int ptrace_show_area(pid_t pid, void *addr, long bytes);
extern int ptrace_show_area_r(pid_t pid, void *addr, long bytes); extern int ptrace_show_area_r(pid_t pid, void *addr, long bytes);
......
...@@ -36,6 +36,11 @@ static const char code_syscall[] = {0x0f, 0x05, 0xcc, 0xcc, ...@@ -36,6 +36,11 @@ static const char code_syscall[] = {0x0f, 0x05, 0xcc, 0xcc,
#define code_syscall_size (round_up(sizeof(code_syscall), sizeof(long))) #define code_syscall_size (round_up(sizeof(code_syscall), sizeof(long)))
#define parasite_size (round_up(sizeof(parasite_blob), sizeof(long))) #define parasite_size (round_up(sizeof(parasite_blob), sizeof(long)))
static int can_run_syscall(unsigned long ip, unsigned long start, unsigned long end)
{
return ip >= start && ip < (end - code_syscall_size);
}
static int syscall_fits_vma_area(struct vma_area *vma_area) static int syscall_fits_vma_area(struct vma_area *vma_area)
{ {
return can_run_syscall((unsigned long)vma_area->vma.start, return can_run_syscall((unsigned long)vma_area->vma.start,
...@@ -43,9 +48,20 @@ static int syscall_fits_vma_area(struct vma_area *vma_area) ...@@ -43,9 +48,20 @@ static int syscall_fits_vma_area(struct vma_area *vma_area)
(unsigned long)vma_area->vma.end); (unsigned long)vma_area->vma.end);
} }
int can_run_syscall(unsigned long ip, unsigned long start, unsigned long end) static struct vma_area *get_vma_by_ip(struct list_head *vma_area_list, unsigned long ip)
{ {
return ip >= start && ip < (end - code_syscall_size); struct vma_area *vma_area;
list_for_each_entry(vma_area, vma_area_list, list) {
if (!in_vma_area(vma_area, ip))
continue;
if (!(vma_area->vma.prot & PROT_EXEC))
continue;
if (syscall_fits_vma_area(vma_area))
return vma_area;
}
return NULL;
} }
/* Note it's destructive on @regs */ /* Note it's destructive on @regs */
...@@ -60,218 +76,220 @@ static void parasite_setup_regs(unsigned long new_ip, user_regs_struct_t *regs) ...@@ -60,218 +76,220 @@ static void parasite_setup_regs(unsigned long new_ip, user_regs_struct_t *regs)
regs->flags &= ~(X86_EFLAGS_TF | X86_EFLAGS_DF | X86_EFLAGS_IF); regs->flags &= ~(X86_EFLAGS_TF | X86_EFLAGS_DF | X86_EFLAGS_IF);
} }
/* @regs must already have been tuned up for parasite execution */ /* we run at @regs->ip */
static int syscall_seized(pid_t pid, user_regs_struct_t *regs) static int __parasite_execute(struct parasite_ctl *ctl, user_regs_struct_t *regs)
{ {
unsigned long start_ip; pid_t pid = ctl->pid;
char saved[sizeof(code_syscall)];
siginfo_t siginfo; siginfo_t siginfo;
int status; int status;
int ret = -1; int ret = -1;
BUILD_BUG_ON(sizeof(code_syscall) != BUILTIN_SYSCALL_SIZE);
BUILD_BUG_ON(!is_log2(sizeof(code_syscall)));
start_ip = (unsigned long)regs->ip;
jerr(ptrace_peek_area(pid, (void *)saved, (void *)start_ip, code_syscall_size), err);
jerr(ptrace_poke_area(pid, (void *)code_syscall, (void *)start_ip, code_syscall_size), err_restore);
again: again:
jerr(ptrace(PTRACE_SETREGS, pid, NULL, regs), err_restore); if (ptrace(PTRACE_SETREGS, pid, NULL, regs)) {
pr_err("Can't set registers (pid: %d)\n", pid);
goto err;
}
/* /*
* Most ideas are taken from Tejun Heo's parasite thread * Most ideas are taken from Tejun Heo's parasite thread
* https://code.google.com/p/ptrace-parasite/ * https://code.google.com/p/ptrace-parasite/
*/ */
/* if (ptrace(PTRACE_CONT, pid, NULL, NULL)) {
* Run the parasite code, at the completion it'll trigger pr_err("Can't continue (pid: %d)\n", pid);
* int3 and inform us that all is done. goto err;
*/ }
if (wait4(pid, &status, __WALL, NULL) != pid) {
pr_err("Waited pid mismatch (pid: %d)\n", 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_err("Can't get siginfo (pid: %d)\n", pid);
goto err;
}
jerr(ptrace(PTRACE_CONT, pid, NULL, NULL), err_restore); if (ptrace(PTRACE_GETREGS, pid, NULL, regs)) {
jerr(wait4(pid, &status, __WALL, NULL) != pid, err_restore); pr_err("Can't obtain registers (pid: %d)\n", pid);
jerr(!WIFSTOPPED(status), err_restore); goto err;
jerr(ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo),err_restore); }
if (WSTOPSIG(status) != SIGTRAP || siginfo.si_code != SI_KERNEL) { if (WSTOPSIG(status) != SIGTRAP || siginfo.si_code != SI_KERNEL) {
retry_signal: retry_signal:
/* pr_debug("** delivering signal %d si_code=%d\n", pr_debug("** delivering signal %d si_code=%d\n",
siginfo.si_signo, siginfo.si_code); */ siginfo.si_signo, siginfo.si_code);
/* FIXME: jerr(siginfo.si_code > 0, err_restore); */ /* FIXME: jerr(siginfo.si_code > 0, err_restore); */
jerr(ptrace(PTRACE_INTERRUPT, pid, NULL, NULL), err_restore);
jerr(ptrace(PTRACE_CONT, pid, NULL, (void *)(unsigned long)siginfo.si_signo), err_restore);
jerr(wait4(pid, &status, __WALL, NULL) != pid, err_restore); /*
jerr(!WIFSTOPPED(status), err_restore); * This requires some explanation. If a signal from original
jerr(ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo), err_restore); * program delivered while we're trying to execute our
* injected blob -- we need to setup original registers back
* so the kernel would make sigframe for us and update the
* former registers.
*
* Then we should swap registers back to our modified copy
* and retry.
*/
if (ptrace(PTRACE_SETREGS, pid, NULL, &ctl->regs_orig)) {
pr_panic("Can't set registers (pid: %d)\n", pid);
goto err;
}
if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL)) {
pr_panic("Can't interrupt (pid: %d)\n", pid);
goto err;
}
if (ptrace(PTRACE_CONT, pid, NULL, (void *)(unsigned long)siginfo.si_signo)) {
pr_err("Can't continue (pid: %d)\n", pid);
goto err;
}
if (wait4(pid, &status, __WALL, NULL) != pid) {
pr_err("Waited pid mismatch (pid: %d)\n", 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_err("Can't get siginfo (pid: %d)\n", pid);
goto err;
}
if (siginfo.si_code >> 8 != PTRACE_EVENT_STOP) if (siginfo.si_code >> 8 != PTRACE_EVENT_STOP)
goto retry_signal; goto retry_signal;
/*
* Signal is delivered, so we should update
* original registers.
*/
{
user_regs_struct_t r;
if (ptrace(PTRACE_GETREGS, pid, NULL, &r)) {
pr_err("Can't obtain registers (pid: %d)\n", pid);
goto err;
}
ctl->regs_orig = r;
}
goto again; goto again;
} }
ret = 0;
/* /*
* Our code is done. * Our code is done.
*/ */
jerr(ptrace(PTRACE_INTERRUPT, pid, NULL, NULL), err_restore); if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL)) {
jerr(ptrace(PTRACE_CONT, pid, NULL, NULL), err_restore); pr_panic("Can't interrupt (pid: %d)\n", pid);
goto err;
}
jerr(wait4(pid, &status, __WALL, NULL) != pid, err_restore); if (ptrace(PTRACE_CONT, pid, NULL, NULL)) {
jerr(!WIFSTOPPED(status), err_restore); pr_err("Can't continue (pid: %d)\n", pid);
jerr(ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo), err_restore); goto err;
}
jerr((siginfo.si_code >> 8 != PTRACE_EVENT_STOP), err_restore); if (wait4(pid, &status, __WALL, NULL) != pid) {
pr_err("Waited pid mismatch (pid: %d)\n", pid);
goto err;
}
jerr(ptrace(PTRACE_GETREGS, pid, NULL, regs), err_restore); if (!WIFSTOPPED(status)) {
pr_err("Task is still running (pid: %d)\n", pid);
goto err;
}
ret = 0; if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo)) {
pr_err("Can't get siginfo (pid: %d)\n", pid);
goto err;
}
err_restore: if (siginfo.si_code >> 8 != PTRACE_EVENT_STOP) {
if (ptrace_poke_area(pid, (void *)saved, (void *)start_ip, code_syscall_size)) { pr_err("si_code doesn't match (pid: %d si_code: %d)\n",
pr_panic("Crap... Can't restore data (pid: %d)\n", pid); pid, siginfo.si_code);
ret = -1; goto err;
} }
ret = 0;
err: err:
return ret; return ret;
} }
static void *mmap_seized(pid_t pid, user_regs_struct_t *regs, static int parasite_execute(unsigned long cmd, struct parasite_ctl *ctl,
void *addr, size_t length, int prot, parasite_status_t *args, int args_size)
int flags, int fd, off_t offset)
{ {
void *mmaped = NULL;
int ret; int ret;
regs->ax = (unsigned long)__NR_mmap; /* mmap */ user_regs_struct_t regs = ctl->regs_orig;
regs->di = (unsigned long)addr; /* @addr */
regs->si = (unsigned long)length; /* @length */
regs->dx = (unsigned long)prot; /* @prot */
regs->r10 = (unsigned long)flags; /* @flags */
regs->r8 = (unsigned long)fd; /* @fd */
regs->r9 = (unsigned long)offset; /* @offset */
ret = syscall_seized(pid, regs);
if (ret)
goto err;
mmaped = (void *)regs->ax;
/* error code from the kernel space */ memcpy(ctl->addr_cmd, &cmd, sizeof(cmd));
if ((long)mmaped < 0) memcpy(ctl->addr_args, args, args_size);
mmaped = NULL;
err:
return mmaped;
}
static int munmap_seized(pid_t pid, user_regs_struct_t *regs, parasite_setup_regs(ctl->parasite_ip, &regs);
void *addr, size_t length)
{
int ret;
regs->ax = (unsigned long)__NR_munmap; /* mmap */ ret = __parasite_execute(ctl, &regs);
regs->di = (unsigned long)addr; /* @addr */
regs->si = (unsigned long)length; /* @length */
ret = syscall_seized(pid, regs); memcpy(args, ctl->addr_args, args_size);
if (!ret) if (!ret)
ret = (int)regs->ax; ret = args->ret;
return ret; return ret;
} }
static struct vma_area *get_vma_by_ip(struct list_head *vma_area_list, unsigned long ip) static void *mmap_seized(struct parasite_ctl *ctl,
{ void *addr, size_t length, int prot,
struct vma_area *vma_area; int flags, int fd, off_t offset)
list_for_each_entry(vma_area, vma_area_list, list) {
if (!in_vma_area(vma_area, ip))
continue;
if (!(vma_area->vma.prot & PROT_EXEC))
continue;
if (syscall_fits_vma_area(vma_area))
return vma_area;
}
return NULL;
}
static int parasite_execute(unsigned long cmd, struct parasite_ctl *ctl,
parasite_status_t *args, int args_size)
{ {
user_regs_struct_t regs, regs_orig; user_regs_struct_t regs = ctl->regs_orig;
int status, ret = -1; void *map = NULL;
siginfo_t siginfo; int ret;
jerr(ptrace(PTRACE_GETREGS, ctl->pid, NULL, &regs_orig), err);
/*
* Pass the command first, it's immutable.
*/
memcpy(ctl->addr_cmd, &cmd, sizeof(cmd));
again:
regs = regs_orig;
parasite_setup_regs(ctl->parasite_ip, &regs);
jerr(ptrace(PTRACE_SETREGS, ctl->pid, NULL, &regs), err_restore);
memcpy(ctl->addr_args, args, args_size);
jerr(ptrace(PTRACE_CONT, (long)ctl->pid, NULL, NULL), err_restore);
jerr(wait4((long)ctl->pid, &status, __WALL, NULL) != (long)ctl->pid, err_restore);
jerr(!WIFSTOPPED(status), err_restore);
jerr(ptrace(PTRACE_GETSIGINFO, (long)ctl->pid, NULL, &siginfo), err_restore);
if (WSTOPSIG(status) != SIGTRAP || siginfo.si_code != SI_KERNEL) {
retry_signal:
/* pr_debug("** delivering signal %d si_code=%d\n",
siginfo.si_signo, siginfo.si_code); */
/* FIXME: jerr(siginfo.si_code > 0, err_restore_full); */
jerr(ptrace(PTRACE_SETREGS, (long)ctl->pid, NULL, (void *)&regs_orig), err_restore);
jerr(ptrace(PTRACE_INTERRUPT, (long)ctl->pid, NULL, NULL), err_restore);
jerr(ptrace(PTRACE_CONT, (long)ctl->pid, NULL, (void *)(unsigned long)siginfo.si_signo), err_restore);
jerr(wait4((long)ctl->pid, &status, __WALL, NULL) != (long)ctl->pid, err_restore); regs.ax = (unsigned long)__NR_mmap; /* mmap */
jerr(!WIFSTOPPED(status), err_restore); regs.di = (unsigned long)addr; /* @addr */
jerr(ptrace(PTRACE_GETSIGINFO, (long)ctl->pid, NULL, &siginfo), err_restore); regs.si = (unsigned long)length; /* @length */
regs.dx = (unsigned long)prot; /* @prot */
regs.r10= (unsigned long)flags; /* @flags */
regs.r8 = (unsigned long)fd; /* @fd */
regs.r9 = (unsigned long)offset; /* @offset */
if (siginfo.si_code >> 8 != PTRACE_EVENT_STOP) parasite_setup_regs(ctl->syscall_ip, &regs);
goto retry_signal;
goto again; ret = __parasite_execute(ctl, &regs);
} if (ret)
goto err;
/* if ((long)regs.ax > 0)
* Check if error happened during dumping. map = (void *)regs.ax;
*/ err:
memcpy(args, ctl->addr_args, args_size); return map;
if (args->ret) { }
pr_panic("Dumping sigactions failed with %li (%li) at %li\n",
args->ret, args->sys_ret, args->line);
goto err_restore;
}
/* static int munmap_seized(struct parasite_ctl *ctl, void *addr, size_t length)
* Our code is done. {
*/ user_regs_struct_t regs = ctl->regs_orig;
jerr(ptrace(PTRACE_INTERRUPT, (long)ctl->pid, NULL, NULL), err_restore); int ret;
jerr(ptrace(PTRACE_CONT, (long)ctl->pid, NULL, NULL), err_restore);
jerr(wait4((long)ctl->pid, &status, __WALL, NULL) != (long)ctl->pid, err_restore); regs.ax = (unsigned long)__NR_munmap; /* mmap */
jerr(!WIFSTOPPED(status), err_restore); regs.di = (unsigned long)addr; /* @addr */
jerr(ptrace(PTRACE_GETSIGINFO, (long)ctl->pid, NULL, &siginfo), err_restore); regs.si = (unsigned long)length; /* @length */
jerr((siginfo.si_code >> 8 != PTRACE_EVENT_STOP), err_restore); parasite_setup_regs(ctl->syscall_ip, &regs);
ret = 0; ret = __parasite_execute(ctl, &regs);
if (!ret)
ret = (int)regs.ax;
err_restore:
if (ptrace(PTRACE_SETREGS, (long)ctl->pid, NULL, &regs_orig)) {
pr_panic("Can't restore registers (pid: %d)\n", ctl->pid);
ret = -1;
}
err:
return ret; return ret;
} }
...@@ -313,8 +331,8 @@ out: ...@@ -313,8 +331,8 @@ out:
return ret; return ret;
} }
static int parasite_prep_file(int type, static int parasite_prep_file(int type, struct parasite_ctl *ctl,
struct parasite_ctl *ctl, struct cr_fdset *fdset) struct cr_fdset *fdset)
{ {
int ret; int ret;
...@@ -330,24 +348,23 @@ static int parasite_prep_file(int type, ...@@ -330,24 +348,23 @@ static int parasite_prep_file(int type,
return 0; return 0;
} }
static int parasite_file_cmd(int cmd, int type, static int parasite_file_cmd(char *what, int cmd, int type,
struct parasite_ctl *ctl, struct cr_fdset *cr_fdset) struct parasite_ctl *ctl,
struct cr_fdset *cr_fdset)
{ {
parasite_status_t args = { }; parasite_status_t args = { };
int status, ret = -1; int ret = -1;
pr_info("\n"); pr_info("\n");
pr_info("Dumping sigactions (pid: %d)\n", ctl->pid); pr_info("Dumping %s (pid: %d)\n", what, ctl->pid);
pr_info("----------------------------------------\n"); pr_info("----------------------------------------\n");
ret = parasite_prep_file(type, ctl, cr_fdset); ret = parasite_prep_file(type, ctl, cr_fdset);
if (ret < 0) if (ret < 0)
goto out; goto out;
ret = parasite_execute(cmd, ctl, ret = parasite_execute(cmd, ctl, (parasite_status_t *)&args, sizeof(args));
(parasite_status_t *)&args, sizeof(args));
err:
fchmod(cr_fdset->fds[type], CR_FD_PERM); fchmod(cr_fdset->fds[type], CR_FD_PERM);
out: out:
pr_info("----------------------------------------\n"); pr_info("----------------------------------------\n");
...@@ -358,13 +375,11 @@ out: ...@@ -358,13 +375,11 @@ out:
static int parasite_init(struct parasite_ctl *ctl, pid_t pid) static int parasite_init(struct parasite_ctl *ctl, pid_t pid)
{ {
struct parasite_init_args args = { }; struct parasite_init_args args = { };
int ret;
args.sun_len = get_socket_name(&args.saddr, pid); args.sun_len = get_socket_name(&args.saddr, pid);
ret = parasite_execute(PARASITE_CMD_INIT, ctl, return parasite_execute(PARASITE_CMD_INIT, ctl,
(parasite_status_t *)&args, sizeof(args)); (parasite_status_t *)&args, sizeof(args));
return ret;
} }
static int parasite_set_logfd(struct parasite_ctl *ctl, pid_t pid) static int parasite_set_logfd(struct parasite_ctl *ctl, pid_t pid)
...@@ -376,8 +391,7 @@ static int parasite_set_logfd(struct parasite_ctl *ctl, pid_t pid) ...@@ -376,8 +391,7 @@ static int parasite_set_logfd(struct parasite_ctl *ctl, pid_t pid)
if (ret) if (ret)
return ret; return ret;
ret = parasite_execute(PARASITE_CMD_SET_LOGFD, ctl, ret = parasite_execute(PARASITE_CMD_SET_LOGFD, ctl, &args, sizeof(args));
&args, sizeof(args));
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -386,18 +400,21 @@ static int parasite_set_logfd(struct parasite_ctl *ctl, pid_t pid) ...@@ -386,18 +400,21 @@ static int parasite_set_logfd(struct parasite_ctl *ctl, pid_t pid)
int parasite_dump_sigacts_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset) int parasite_dump_sigacts_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset)
{ {
return parasite_file_cmd(PARASITE_CMD_DUMP_SIGACTS, CR_FD_SIGACT, ctl, cr_fdset); return parasite_file_cmd("sigactions", PARASITE_CMD_DUMP_SIGACTS,
CR_FD_SIGACT, ctl, cr_fdset);
} }
int parasite_dump_itimers_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset) int parasite_dump_itimers_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset)
{ {
return parasite_file_cmd(PARASITE_CMD_DUMP_ITIMERS, CR_FD_ITIMERS, ctl, cr_fdset); return parasite_file_cmd("timers", PARASITE_CMD_DUMP_ITIMERS,
CR_FD_ITIMERS, ctl, cr_fdset);
} }
int parasite_dump_misc_seized(struct parasite_ctl *ctl, struct parasite_dump_misc *misc) int parasite_dump_misc_seized(struct parasite_ctl *ctl, struct parasite_dump_misc *misc)
{ {
return parasite_execute(PARASITE_CMD_DUMP_MISC, ctl, return parasite_execute(PARASITE_CMD_DUMP_MISC, ctl,
(parasite_status_t *)misc, sizeof(struct parasite_dump_misc)); (parasite_status_t *)misc,
sizeof(struct parasite_dump_misc));
} }
/* /*
...@@ -409,11 +426,9 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl, struct list_head *vma_a ...@@ -409,11 +426,9 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl, struct list_head *vma_a
{ {
struct parasite_dump_pages_args parasite_dumppages = { }; struct parasite_dump_pages_args parasite_dumppages = { };
parasite_status_t *st = &parasite_dumppages.status; parasite_status_t *st = &parasite_dumppages.status;
user_regs_struct_t regs, regs_orig;
unsigned long nrpages_dumped = 0; unsigned long nrpages_dumped = 0;
struct vma_area *vma_area; struct vma_area *vma_area;
siginfo_t siginfo; int ret = -1;
int status, ret = -1;
pr_info("\n"); pr_info("\n");
pr_info("Dumping pages (type: %d pid: %d)\n", CR_FD_PAGES, ctl->pid); pr_info("Dumping pages (type: %d pid: %d)\n", CR_FD_PAGES, ctl->pid);
...@@ -466,8 +481,8 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl, struct list_head *vma_a ...@@ -466,8 +481,8 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl, struct list_head *vma_a
} }
ret = parasite_execute(PARASITE_CMD_DUMPPAGES, ctl, ret = parasite_execute(PARASITE_CMD_DUMPPAGES, ctl,
(parasite_status_t *) &parasite_dumppages, (parasite_status_t *) &parasite_dumppages,
sizeof(parasite_dumppages)); sizeof(parasite_dumppages));
if (ret) { if (ret) {
pr_panic("Dumping pages failed with %li (%li) at %li\n", pr_panic("Dumping pages failed with %li (%li) at %li\n",
parasite_dumppages.status.ret, parasite_dumppages.status.ret,
...@@ -500,90 +515,114 @@ out: ...@@ -500,90 +515,114 @@ out:
return ret; return ret;
} }
int parasite_cure_seized(struct parasite_ctl *ctl, struct list_head *vma_area_list) int parasite_cure_seized(struct parasite_ctl *ctl)
{ {
user_regs_struct_t regs, regs_orig;
struct vma_area *vma_area;
int ret = -1;
parasite_status_t args = { }; parasite_status_t args = { };
int ret = 0;
ret = parasite_execute(PARASITE_CMD_FINI, ctl, if (ctl->parasite_ip) {
&args, sizeof(args)); if (parasite_execute(PARASITE_CMD_FINI, ctl, &args, sizeof(args))) {
if (ret) { pr_err("Can't finalize parasite (pid: %d) task\n", ctl->pid);
pr_err("Can't finalize parasite (pid: %d) task\n", ctl->pid); ret = -1;
goto err; }
} }
jerr(ptrace(PTRACE_GETREGS, ctl->pid, NULL, &regs), err); if (ctl->remote_map) {
if (munmap_seized(ctl, (void *)ctl->remote_map, ctl->map_length)) {
regs_orig = regs; pr_panic("munmap_seized failed (pid: %d)\n", ctl->pid);
ret = -1;
vma_area = get_vma_by_ip(vma_area_list, regs.ip); }
if (!vma_area) {
pr_err("No suitable VMA found to run cure (pid: %d)\n", ctl->pid);
goto err;
} }
parasite_setup_regs(vma_area->vma.start, &regs); if (ctl->local_map) {
if (munmap(ctl->local_map, parasite_size)) {
pr_panic("munmap failed (pid: %d)\n", ctl->pid);
ret = -1;
}
}
ret = munmap_seized(ctl->pid, &regs, ctl->remote_map, ctl->map_length); if (ptrace_poke_area(ctl->pid, (void *)ctl->code_orig,
if (ret) (void *)ctl->syscall_ip, sizeof(ctl->code_orig))) {
pr_err("munmap_seized failed (pid: %d)\n", ctl->pid); pr_panic("Can't restore syscall blob (pid: %d)\n", ctl->pid);
ret = -1;
}
if (ptrace(PTRACE_SETREGS, ctl->pid, NULL, &regs_orig)) { if (ptrace(PTRACE_SETREGS, ctl->pid, NULL, &ctl->regs_orig)) {
pr_panic("PTRACE_SETREGS failed (pid: %d)\n", ctl->pid); pr_panic("Can't restore registers (pid: %d)\n", ctl->pid);
ret = -1; ret = -1;
} }
free(ctl); free(ctl);
err:
return ret; return ret;
} }
struct parasite_ctl *parasite_infect_seized(pid_t pid, int pid_dir, struct list_head *vma_area_list) struct parasite_ctl *parasite_infect_seized(pid_t pid, int pid_dir, struct list_head *vma_area_list)
{ {
parasite_status_t args = { };
user_regs_struct_t regs, regs_orig;
struct parasite_ctl *ctl = NULL; struct parasite_ctl *ctl = NULL;
struct vma_area *vma_area; struct vma_area *vma_area;
char fname[128]; char fname[128];
int ret, fd; int ret, fd;
/*
* Control block early setup.
*/
ctl = xzalloc(sizeof(*ctl)); ctl = xzalloc(sizeof(*ctl));
if (!ctl) { if (!ctl) {
pr_err("Parasite control block allocation failed (pid: %d)\n", pid); pr_err("Parasite control block allocation failed (pid: %d)\n", pid);
goto err; goto err;
} }
if (ptrace(PTRACE_GETREGS, pid, NULL, &regs)) if (ptrace(PTRACE_GETREGS, pid, NULL, &ctl->regs_orig)) {
pr_err_jmp(err); pr_err("Can't obtain registers (pid: %d)\n", pid);
goto err;
}
vma_area = get_vma_by_ip(vma_area_list, regs.ip); vma_area = get_vma_by_ip(vma_area_list, ctl->regs_orig.ip);
if (!vma_area) { if (!vma_area) {
pr_err("No suitable VMA found to run parasite " pr_err("No suitable VMA found to run parasite "
"bootstrap code (pid: %d)\n", pid); "bootstrap code (pid: %d)\n", pid);
goto err; goto err;
} }
regs_orig = regs; ctl->pid = pid;
parasite_setup_regs(vma_area->vma.start, &regs); ctl->syscall_ip = vma_area->vma.start;
/*
* Inject syscall instruction and remember original code,
* we will need it to restore original program content.
*/
BUILD_BUG_ON(sizeof(code_syscall) != sizeof(ctl->code_orig));
BUILD_BUG_ON(!is_log2(sizeof(code_syscall)));
memcpy(ctl->code_orig, code_syscall, sizeof(ctl->code_orig));
if (ptrace_swap_area(ctl->pid, (void *)ctl->syscall_ip,
(void *)ctl->code_orig, sizeof(ctl->code_orig))) {
pr_err("Can't inject syscall blob (pid: %d)\n", pid);
goto err;
}
ctl->remote_map = mmap_seized(pid, &regs, NULL, (size_t)parasite_size, /*
* 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.
*/
ctl->remote_map = mmap_seized(ctl, NULL, (size_t)parasite_size,
PROT_READ | PROT_WRITE | PROT_EXEC, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANONYMOUS | MAP_SHARED, -1, 0); MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (!ctl->remote_map) { if (!ctl->remote_map) {
pr_err("Can't allocate memory for parasite blob (pid: %d)\n", pid); pr_err("Can't allocate memory for parasite blob (pid: %d)\n", pid);
goto err_restore_regs; goto err_restore;
} }
ctl->map_length = round_up(parasite_size, PAGE_SIZE); ctl->map_length = round_up(parasite_size, PAGE_SIZE);
snprintf(fname, sizeof(fname), "map_files/%p-%p", snprintf(fname, sizeof(fname), "map_files/%p-%p",
ctl->remote_map, ctl->remote_map + ctl->map_length); ctl->remote_map, ctl->remote_map + ctl->map_length);
fd = openat(pid_dir, fname, O_RDWR); fd = openat(pid_dir, fname, O_RDWR);
if (fd < 0) { if (fd < 0) {
pr_perror("Can't open remote parasite map"); pr_perror("Can't open remote parasite map");
goto err_munmap_restore; goto err_restore;
} }
ctl->local_map = mmap(NULL, parasite_size, PROT_READ | PROT_WRITE, ctl->local_map = mmap(NULL, parasite_size, PROT_READ | PROT_WRITE,
...@@ -591,17 +630,15 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, int pid_dir, struct list_ ...@@ -591,17 +630,15 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, int pid_dir, struct list_
close(fd); close(fd);
if (ctl->local_map == MAP_FAILED) { if (ctl->local_map == MAP_FAILED) {
ctl->local_map = NULL;
pr_perror("Can't map remote parasite map"); pr_perror("Can't map remote parasite map");
goto err_munmap_restore; goto err_restore;
} }
pr_info("Putting parasite blob into %p->%p\n", ctl->local_map, ctl->remote_map); pr_info("Putting parasite blob into %p->%p\n", ctl->local_map, ctl->remote_map);
memcpy(ctl->local_map, parasite_blob, sizeof(parasite_blob)); memcpy(ctl->local_map, parasite_blob, sizeof(parasite_blob));
jerr(ptrace(PTRACE_SETREGS, pid, NULL, &regs_orig), err_munmap_restore); /* Setup the rest of a control block */
/* Setup control block */
ctl->pid = pid;
ctl->parasite_ip = PARASITE_HEAD_ADDR((unsigned long)ctl->remote_map); ctl->parasite_ip = PARASITE_HEAD_ADDR((unsigned long)ctl->remote_map);
ctl->addr_cmd = (void *)PARASITE_CMD_ADDR((unsigned long)ctl->local_map); ctl->addr_cmd = (void *)PARASITE_CMD_ADDR((unsigned long)ctl->local_map);
ctl->addr_args = (void *)PARASITE_ARGS_ADDR((unsigned long)ctl->local_map); ctl->addr_args = (void *)PARASITE_ARGS_ADDR((unsigned long)ctl->local_map);
...@@ -609,32 +646,19 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, int pid_dir, struct list_ ...@@ -609,32 +646,19 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, int pid_dir, struct list_
ret = parasite_init(ctl, pid); ret = parasite_init(ctl, pid);
if (ret) { if (ret) {
pr_err("%d: Can't create a transport socket\n", pid); pr_err("%d: Can't create a transport socket\n", pid);
goto err_munmap_restore; goto err_restore;
} }
ret = parasite_set_logfd(ctl, pid); ret = parasite_set_logfd(ctl, pid);
if (ret) { if (ret) {
pr_err("%d: Can't set a logging descriptor\n", pid); pr_err("%d: Can't set a logging descriptor\n", pid);
goto err_fini; goto err_restore;
} }
return ctl; return ctl;
err_fini: err_restore:
ret = parasite_execute(PARASITE_CMD_FINI, ctl, parasite_cure_seized(ctl);
&args, sizeof(args));
if (ret)
pr_panic("Can't finalize parasite (pid: %d) task\n", ctl->pid);
err_munmap_restore:
regs = regs_orig;
parasite_setup_regs(vma_area->vma.start, &regs);
if (munmap_seized(pid, &regs, ctl->remote_map, ctl->map_length))
pr_panic("mmap_seized failed (pid: %d)\n", pid);
err_restore_regs:
if (ptrace(PTRACE_SETREGS, pid, NULL, &regs_orig))
pr_panic("PTRACE_SETREGS failed (pid: %d)\n", pid);
err: err:
xfree(ctl); xfree(ctl);
......
...@@ -175,3 +175,21 @@ err: ...@@ -175,3 +175,21 @@ err:
return -2; return -2;
} }
/* 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment