2011-09-23 12:00:45 +04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <sys/ptrace.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/user.h>
|
|
|
|
#include <sys/wait.h>
|
2012-02-01 16:23:50 +03:00
|
|
|
#include <sys/socket.h>
|
2011-09-23 12:00:45 +04:00
|
|
|
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
#include "crtools.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
#include "compiler.h"
|
|
|
|
#include "syscall.h"
|
|
|
|
#include "types.h"
|
2011-12-19 21:57:59 +04:00
|
|
|
#include "ptrace.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
#include "util.h"
|
2012-02-01 16:23:50 +03:00
|
|
|
#include "util-net.h"
|
|
|
|
#include "log.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
#include "parasite-syscall.h"
|
|
|
|
#include "parasite-blob.h"
|
|
|
|
#include "parasite.h"
|
|
|
|
|
|
|
|
#ifdef CONFIG_X86_64
|
|
|
|
static const char code_syscall[] = {0x0f, 0x05, 0xcc, 0xcc,
|
|
|
|
0xcc, 0xcc, 0xcc, 0xcc};
|
|
|
|
|
|
|
|
#define code_syscall_size (round_up(sizeof(code_syscall), sizeof(long)))
|
|
|
|
#define parasite_size (round_up(sizeof(parasite_blob), sizeof(long)))
|
|
|
|
|
|
|
|
static int syscall_fits_vma_area(struct vma_area *vma_area)
|
|
|
|
{
|
|
|
|
return can_run_syscall((unsigned long)vma_area->vma.start,
|
|
|
|
(unsigned long)vma_area->vma.start,
|
|
|
|
(unsigned long)vma_area->vma.end);
|
|
|
|
}
|
|
|
|
|
|
|
|
int can_run_syscall(unsigned long ip, unsigned long start, unsigned long end)
|
|
|
|
{
|
|
|
|
return ip >= start && ip < (end - code_syscall_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *mmap_seized(pid_t pid, user_regs_struct_t *regs,
|
|
|
|
void *addr, size_t length, int prot,
|
|
|
|
int flags, int fd, off_t offset)
|
|
|
|
{
|
|
|
|
user_regs_struct_t params = *regs;
|
|
|
|
void *mmaped = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
params.ax = (unsigned long)__NR_mmap; /* mmap */
|
|
|
|
params.di = (unsigned long)addr; /* @addr */
|
|
|
|
params.si = (unsigned long)length; /* @length */
|
|
|
|
params.dx = (unsigned long)prot; /* @prot */
|
|
|
|
params.r10 = (unsigned long)flags; /* @flags */
|
|
|
|
params.r8 = (unsigned long)fd; /* @fd */
|
|
|
|
params.r9 = (unsigned long)offset; /* @offset */
|
|
|
|
|
|
|
|
ret = syscall_seized(pid, regs, ¶ms, ¶ms);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
mmaped = (void *)params.ax;
|
|
|
|
|
|
|
|
/* error code from the kernel space */
|
|
|
|
if ((long)mmaped < 0)
|
|
|
|
mmaped = NULL;
|
|
|
|
err:
|
|
|
|
return mmaped;
|
|
|
|
}
|
|
|
|
|
|
|
|
int munmap_seized(pid_t pid, user_regs_struct_t *regs,
|
|
|
|
void *addr, size_t length)
|
|
|
|
{
|
|
|
|
user_regs_struct_t params = *regs;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
params.ax = (unsigned long)__NR_munmap; /* mmap */
|
|
|
|
params.di = (unsigned long)addr; /* @addr */
|
|
|
|
params.si = (unsigned long)length; /* @length */
|
|
|
|
|
|
|
|
ret = syscall_seized(pid, regs, ¶ms, ¶ms);
|
|
|
|
if (!ret)
|
|
|
|
ret = (int)params.ax;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int kill_seized(pid_t pid, user_regs_struct_t *where)
|
|
|
|
{
|
|
|
|
user_regs_struct_t params = *where;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
params.ax = (unsigned long)__NR_exit; /* exit */
|
|
|
|
params.di = (unsigned long)-1; /* @error-code */
|
|
|
|
|
|
|
|
ret = syscall_seized(pid, where, ¶ms, ¶ms);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-10-12 09:40:02 +04:00
|
|
|
unsigned long brk_seized(pid_t pid, unsigned long addr)
|
|
|
|
{
|
|
|
|
user_regs_struct_t params, regs_orig;
|
|
|
|
unsigned long ret = -1UL;
|
|
|
|
|
|
|
|
jerr(ptrace(PTRACE_GETREGS, pid, NULL, ®s_orig), err);
|
|
|
|
params = regs_orig;
|
|
|
|
|
|
|
|
params.ax = (unsigned long)__NR_brk; /* brk */
|
|
|
|
params.di = (unsigned long)addr; /* @addr */
|
|
|
|
|
|
|
|
ret = syscall_seized(pid, ®s_orig, ¶ms, ¶ms);
|
|
|
|
if (!ret)
|
|
|
|
ret = (unsigned long)params.ax;
|
|
|
|
else
|
|
|
|
ret = -1UL;
|
|
|
|
|
2012-02-08 14:11:54 +04:00
|
|
|
if (ptrace(PTRACE_SETREGS, pid, NULL, ®s_orig)) {
|
2011-10-12 09:40:02 +04:00
|
|
|
pr_panic("Can't restore registers (pid: %d)\n", pid);
|
2012-02-08 14:11:54 +04:00
|
|
|
ret = -1UL;
|
|
|
|
}
|
2011-10-12 09:40:02 +04:00
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
int syscall_seized(pid_t pid,
|
|
|
|
user_regs_struct_t *where,
|
|
|
|
user_regs_struct_t *params,
|
|
|
|
user_regs_struct_t *result)
|
|
|
|
{
|
|
|
|
user_regs_struct_t regs_orig, regs;
|
|
|
|
unsigned long start_ip;
|
|
|
|
char saved[sizeof(code_syscall)];
|
|
|
|
siginfo_t siginfo;
|
|
|
|
int status;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
BUILD_BUG_ON(sizeof(code_syscall) != BUILTIN_SYSCALL_SIZE);
|
|
|
|
BUILD_BUG_ON(!is_log2(sizeof(code_syscall)));
|
|
|
|
|
|
|
|
start_ip = (unsigned long)where->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);
|
|
|
|
|
|
|
|
again:
|
|
|
|
jerr(ptrace(PTRACE_GETREGS, pid, NULL, ®s), err);
|
|
|
|
regs_orig = regs;
|
|
|
|
|
|
|
|
regs.ip = start_ip;
|
|
|
|
regs.ax = params->ax;
|
|
|
|
regs.di = params->di;
|
|
|
|
regs.si = params->si;
|
|
|
|
regs.dx = params->dx;
|
|
|
|
regs.r10 = params->r10;
|
|
|
|
regs.r8 = params->r8;
|
|
|
|
regs.r9 = params->r9;
|
|
|
|
regs.orig_ax = -1; /* avoid end-of-syscall processing */
|
|
|
|
|
|
|
|
jerr(ptrace(PTRACE_SETREGS, pid, NULL, ®s), err_restore);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Most ideas are taken from Tejun Heo's parasite thread
|
|
|
|
* https://code.google.com/p/ptrace-parasite/
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Run the parasite code, at the completion it'll trigger
|
|
|
|
* int3 and inform us that all is done.
|
|
|
|
*/
|
|
|
|
|
|
|
|
jerr(ptrace(PTRACE_CONT, pid, NULL, NULL), err_restore_full);
|
|
|
|
jerr(wait4(pid, &status, __WALL, NULL) != pid, err_restore_full);
|
|
|
|
jerr(!WIFSTOPPED(status), err_restore_full);
|
|
|
|
jerr(ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo),err_restore_full);
|
|
|
|
|
|
|
|
jerr(ptrace(PTRACE_GETREGS, pid, NULL, ®s), err_restore_full);
|
|
|
|
|
|
|
|
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, pid, NULL, (void *)®s_orig), err_restore_full);
|
|
|
|
jerr(ptrace(PTRACE_INTERRUPT, pid, NULL, NULL), err_restore_full);
|
|
|
|
jerr(ptrace(PTRACE_CONT, pid, NULL, (void *)(unsigned long)siginfo.si_signo), err_restore_full);
|
|
|
|
|
|
|
|
jerr(wait4(pid, &status, __WALL, NULL) != pid, err_restore_full);
|
|
|
|
jerr(!WIFSTOPPED(status), err_restore_full);
|
|
|
|
jerr(ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo), err_restore_full);
|
|
|
|
|
|
|
|
if (siginfo.si_code >> 8 != PTRACE_EVENT_STOP)
|
|
|
|
goto retry_signal;
|
|
|
|
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
/*
|
2012-01-30 21:18:37 +04:00
|
|
|
* Our code is done.
|
2011-09-23 12:00:45 +04:00
|
|
|
*/
|
|
|
|
jerr(ptrace(PTRACE_INTERRUPT, pid, NULL, NULL), err_restore_full);
|
|
|
|
jerr(ptrace(PTRACE_CONT, pid, NULL, NULL), err_restore_full);
|
|
|
|
|
|
|
|
jerr(wait4(pid, &status, __WALL, NULL) != pid, err_restore_full);
|
|
|
|
jerr(!WIFSTOPPED(status), err_restore_full);
|
|
|
|
jerr(ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo), err_restore_full);
|
|
|
|
|
|
|
|
jerr((siginfo.si_code >> 8 != PTRACE_EVENT_STOP), err_restore_full);
|
|
|
|
|
|
|
|
jerr(ptrace(PTRACE_GETREGS, pid, NULL, ®s), err_restore_full);
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
*result = regs;
|
|
|
|
|
|
|
|
err_restore_full:
|
2012-02-08 14:11:54 +04:00
|
|
|
if (ptrace(PTRACE_SETREGS, pid, NULL, ®s_orig)) {
|
2011-09-23 12:00:45 +04:00
|
|
|
pr_panic("Can't restore registers (pid: %d)\n", pid);
|
2012-02-08 14:11:54 +04:00
|
|
|
ret = -1;
|
|
|
|
}
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
err_restore:
|
2012-02-08 14:11:54 +04:00
|
|
|
if (ptrace_poke_area(pid, (void *)saved, (void *)start_ip, code_syscall_size)) {
|
2011-09-23 12:00:45 +04:00
|
|
|
pr_panic("Crap... Can't restore data (pid: %d)\n", pid);
|
2012-02-08 14:11:54 +04:00
|
|
|
ret = -1;
|
|
|
|
}
|
2011-09-23 12:00:45 +04:00
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct vma_area *get_vma_by_ip(struct list_head *vma_area_list, unsigned long ip)
|
|
|
|
{
|
|
|
|
struct vma_area *vma_area;
|
|
|
|
|
|
|
|
list_for_each_entry(vma_area, vma_area_list, list) {
|
|
|
|
if (in_vma_area(vma_area, ip)) {
|
|
|
|
if (vma_area->vma.prot & PROT_EXEC) {
|
|
|
|
if (syscall_fits_vma_area(vma_area))
|
|
|
|
return vma_area;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-11-29 13:33:59 +03:00
|
|
|
int parasite_execute(unsigned long cmd, struct parasite_ctl *ctl,
|
|
|
|
parasite_status_t *args, int args_size)
|
|
|
|
{
|
|
|
|
parasite_args_t parasite_arg = { };
|
|
|
|
user_regs_struct_t regs, regs_orig;
|
|
|
|
int status, ret = -1;
|
|
|
|
siginfo_t siginfo;
|
|
|
|
|
|
|
|
jerr(ptrace(PTRACE_GETREGS, ctl->pid, NULL, ®s_orig), err);
|
|
|
|
|
|
|
|
parasite_arg.command = cmd;
|
|
|
|
parasite_arg.args_size = args_size;
|
|
|
|
parasite_arg.args = args;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pass the command first, it's immutable.
|
|
|
|
*/
|
|
|
|
jerr(ptrace_poke_area((long)ctl->pid, (void *)¶site_arg.command,
|
|
|
|
(void *)ctl->addr_cmd, sizeof(parasite_arg.command)),
|
|
|
|
err_restore);
|
|
|
|
|
|
|
|
again:
|
|
|
|
jerr(ptrace(PTRACE_GETREGS, ctl->pid, NULL, ®s), err_restore);
|
|
|
|
regs.ip = ctl->parasite_ip;
|
|
|
|
jerr(ptrace(PTRACE_SETREGS, ctl->pid, NULL, ®s), err_restore);
|
|
|
|
|
|
|
|
if (ptrace_poke_area((long)ctl->pid, (void *)parasite_arg.args,
|
|
|
|
(void *)ctl->addr_args, parasite_arg.args_size)) {
|
|
|
|
pr_err("Can't setup parasite arguments (pid: %d)\n", ctl->pid);
|
|
|
|
goto err_restore;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 *)®s_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);
|
|
|
|
jerr(!WIFSTOPPED(status), err_restore);
|
|
|
|
jerr(ptrace(PTRACE_GETSIGINFO, (long)ctl->pid, NULL, &siginfo), err_restore);
|
|
|
|
|
|
|
|
if (siginfo.si_code >> 8 != PTRACE_EVENT_STOP)
|
|
|
|
goto retry_signal;
|
|
|
|
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if error happened during dumping.
|
|
|
|
*/
|
|
|
|
if (ptrace_peek_area((long)ctl->pid,
|
|
|
|
(void *)args,
|
|
|
|
(void *)(ctl->addr_args),
|
|
|
|
args_size)) {
|
|
|
|
pr_err("Can't get dumper ret code (pid: %d)\n", ctl->pid);
|
|
|
|
goto err_restore;
|
|
|
|
}
|
|
|
|
if (args->ret) {
|
|
|
|
pr_panic("Dumping sigactions failed with %li (%li) at %li\n",
|
|
|
|
args->ret,
|
|
|
|
args->sys_ret,
|
|
|
|
args->line);
|
|
|
|
|
|
|
|
goto err_restore;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Our code is done.
|
|
|
|
*/
|
|
|
|
jerr(ptrace(PTRACE_INTERRUPT, (long)ctl->pid, NULL, NULL), err_restore);
|
|
|
|
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);
|
|
|
|
|
|
|
|
jerr((siginfo.si_code >> 8 != PTRACE_EVENT_STOP), err_restore);
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
err_restore:
|
|
|
|
if (ptrace(PTRACE_SETREGS, (long)ctl->pid, NULL, ®s_orig)) {
|
|
|
|
pr_panic("Can't restore registers (pid: %d)\n", ctl->pid);
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-02-01 16:23:50 +03:00
|
|
|
static int get_socket_name(struct sockaddr_un *saddr, pid_t pid)
|
|
|
|
{
|
|
|
|
int sun_len;
|
|
|
|
|
|
|
|
saddr->sun_family = AF_UNIX;
|
|
|
|
snprintf(saddr->sun_path, UNIX_PATH_MAX,
|
|
|
|
"X/crtools-pr-%d", pid);
|
|
|
|
|
|
|
|
sun_len = SUN_LEN(saddr);
|
|
|
|
*saddr->sun_path = '\0';
|
|
|
|
|
|
|
|
return sun_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parasite_send_fd(struct parasite_ctl *ctl, int fd)
|
|
|
|
{
|
|
|
|
struct sockaddr_un saddr;
|
|
|
|
int sun_len, ret = -1;
|
|
|
|
int sock;
|
|
|
|
|
|
|
|
sun_len = get_socket_name(&saddr, ctl->pid);
|
|
|
|
|
|
|
|
sock = socket(PF_UNIX, SOCK_DGRAM, 0);
|
|
|
|
if (sock < 0) {
|
|
|
|
pr_perror("Can't create socket");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (send_fd(sock, &saddr, sun_len, fd) < 0) {
|
|
|
|
pr_perror("Can't send file descriptor");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
close(sock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-02-01 13:00:51 +03:00
|
|
|
static int parasite_prep_file(int type,
|
2012-01-24 16:40:55 +04:00
|
|
|
struct parasite_ctl *ctl, struct cr_fdset *fdset)
|
|
|
|
{
|
2012-02-01 13:00:51 +03:00
|
|
|
int ret;
|
2012-01-24 16:40:55 +04:00
|
|
|
|
|
|
|
if (fchmod(fdset->fds[type], CR_FD_PERM_DUMP)) {
|
2012-01-31 15:13:05 +04:00
|
|
|
pr_perror("Can't change permissions on %d file", type);
|
2012-01-24 16:40:55 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-02-01 13:00:51 +03:00
|
|
|
ret = parasite_send_fd(ctl, fdset->fds[type]);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2012-01-24 16:40:55 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-01-24 16:45:19 +04:00
|
|
|
static int parasite_file_cmd(int cmd, int type,
|
|
|
|
struct parasite_ctl *ctl, struct cr_fdset *cr_fdset)
|
2011-11-29 15:12:25 +03:00
|
|
|
{
|
2012-02-01 13:00:51 +03:00
|
|
|
parasite_status_t args = { };
|
2012-01-12 15:17:51 +04:00
|
|
|
int status, ret = -1;
|
2011-11-29 15:12:25 +03:00
|
|
|
|
|
|
|
pr_info("\n");
|
|
|
|
pr_info("Dumping sigactions (pid: %d)\n", ctl->pid);
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
2012-02-01 13:00:51 +03:00
|
|
|
ret = parasite_prep_file(type, ctl, cr_fdset);
|
2012-01-24 16:40:55 +04:00
|
|
|
if (ret < 0)
|
2011-11-29 15:12:25 +03:00
|
|
|
goto out;
|
|
|
|
|
2012-01-24 16:45:19 +04:00
|
|
|
ret = parasite_execute(cmd, ctl,
|
|
|
|
(parasite_status_t *)&args, sizeof(args));
|
2011-11-29 15:12:25 +03:00
|
|
|
|
|
|
|
err:
|
2012-01-24 16:45:19 +04:00
|
|
|
fchmod(cr_fdset->fds[type], CR_FD_PERM);
|
2011-11-29 15:12:25 +03:00
|
|
|
out:
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-02-01 16:23:50 +03:00
|
|
|
static int parasite_init(struct parasite_ctl *ctl, pid_t pid)
|
|
|
|
{
|
|
|
|
struct parasite_init_args args = { };
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
args.sun_len = get_socket_name(&args.saddr, pid);
|
|
|
|
|
|
|
|
ret = parasite_execute(PARASITE_CMD_INIT, ctl,
|
|
|
|
(parasite_status_t *)&args, sizeof(args));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parasite_set_logfd(struct parasite_ctl *ctl, pid_t pid)
|
|
|
|
{
|
|
|
|
parasite_status_t args = { };
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = parasite_send_fd(ctl, get_logfd());
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = parasite_execute(PARASITE_CMD_SET_LOGFD, ctl,
|
|
|
|
&args, sizeof(args));
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-01-24 16:45:19 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-01-27 21:35:59 +04:00
|
|
|
int parasite_dump_misc_seized(struct parasite_ctl *ctl, struct parasite_dump_misc *misc)
|
|
|
|
{
|
|
|
|
return parasite_execute(PARASITE_CMD_DUMP_MISC, ctl,
|
|
|
|
(parasite_status_t *)misc, sizeof(struct parasite_dump_misc));
|
|
|
|
}
|
|
|
|
|
2011-10-13 16:36:50 +04:00
|
|
|
/*
|
|
|
|
* This routine drives parasite code (been previously injected into a victim
|
|
|
|
* process) and tells it to dump pages into the file.
|
|
|
|
*/
|
2011-09-23 12:00:45 +04:00
|
|
|
int parasite_dump_pages_seized(struct parasite_ctl *ctl, struct list_head *vma_area_list,
|
2012-01-11 13:30:38 +04:00
|
|
|
struct cr_fdset *cr_fdset)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
2012-01-24 16:39:56 +04:00
|
|
|
struct parasite_dump_pages_args parasite_dumppages = { };
|
2011-09-23 12:00:45 +04:00
|
|
|
user_regs_struct_t regs, regs_orig;
|
|
|
|
unsigned long nrpages_dumped = 0;
|
|
|
|
struct vma_area *vma_area;
|
|
|
|
siginfo_t siginfo;
|
2012-01-12 15:17:51 +04:00
|
|
|
int status, ret = -1;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
pr_info("\n");
|
2012-01-11 13:30:38 +04:00
|
|
|
pr_info("Dumping pages (type: %d pid: %d)\n", CR_FD_PAGES, ctl->pid);
|
2011-09-23 12:00:45 +04:00
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
2012-02-01 13:00:51 +03:00
|
|
|
ret = parasite_prep_file(CR_FD_PAGES, ctl, cr_fdset);
|
2012-01-24 16:40:55 +04:00
|
|
|
if (ret < 0)
|
2011-11-23 13:08:28 +04:00
|
|
|
goto out;
|
2011-10-03 11:52:13 +04:00
|
|
|
|
2011-10-13 13:41:13 +04:00
|
|
|
/*
|
|
|
|
* Make sure the data is on disk since we will re-open
|
|
|
|
* it in another process.
|
|
|
|
*/
|
2012-01-12 15:17:51 +04:00
|
|
|
fsync(cr_fdset->fds[CR_FD_PAGES]);
|
2012-01-24 16:39:56 +04:00
|
|
|
parasite_dumppages.fd = -1UL;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
list_for_each_entry(vma_area, vma_area_list, list) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The special areas are not dumped.
|
|
|
|
*/
|
|
|
|
if (!(vma_area->vma.status & VMA_AREA_REGULAR))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* No dumps for file-shared mappings */
|
|
|
|
if (vma_area->vma.status & VMA_FILE_SHARED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
pr_info_vma(vma_area);
|
|
|
|
parasite_dumppages.vma_entry = vma_area->vma;
|
|
|
|
|
2011-11-29 13:33:59 +03:00
|
|
|
ret = parasite_execute(PARASITE_CMD_DUMPPAGES, ctl,
|
|
|
|
(parasite_status_t *) ¶site_dumppages,
|
|
|
|
sizeof(parasite_dumppages));
|
|
|
|
if (ret) {
|
2011-11-22 20:07:20 +04:00
|
|
|
pr_panic("Dumping pages failed with %li (%li) at %li\n",
|
2012-02-01 13:00:51 +03:00
|
|
|
parasite_dumppages.status.ret,
|
|
|
|
parasite_dumppages.status.sys_ret,
|
|
|
|
parasite_dumppages.status.line);
|
2011-11-22 20:07:20 +04:00
|
|
|
|
|
|
|
goto err_restore;
|
|
|
|
}
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
pr_info(" (dumped: %16li pages)\n", parasite_dumppages.nrpages_dumped);
|
|
|
|
nrpages_dumped += parasite_dumppages.nrpages_dumped;
|
|
|
|
}
|
|
|
|
|
2012-02-08 14:11:54 +04:00
|
|
|
if (ptrace(PTRACE_GETREGS, (long)ctl->pid, NULL, ®s_orig)) {
|
|
|
|
pr_err("Can't get registers (pid: %d)\n", ctl->pid);
|
|
|
|
goto err_restore;
|
|
|
|
}
|
2011-11-29 13:33:59 +03:00
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
/* Finally close the descriptor the parasite has opened */
|
|
|
|
if (parasite_dumppages.fd != -1UL) {
|
|
|
|
regs = regs_orig;
|
|
|
|
regs.ax = __NR_close; /* close */
|
|
|
|
regs.di = parasite_dumppages.fd; /* @fd */
|
|
|
|
ret = syscall_seized(ctl->pid, ®s_orig, ®s, ®s);
|
|
|
|
}
|
|
|
|
|
2011-11-29 13:33:59 +03:00
|
|
|
if (ptrace(PTRACE_SETREGS, (long)ctl->pid, NULL, ®s_orig)) {
|
|
|
|
pr_panic("Can't restore registers (pid: %d)\n", ctl->pid);
|
2012-02-08 14:11:54 +04:00
|
|
|
goto err_restore;
|
2011-11-29 13:33:59 +03:00
|
|
|
}
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
/*
|
|
|
|
* We don't know the position in file since it's updated
|
|
|
|
* outside of our process.
|
|
|
|
*/
|
2012-01-12 15:17:51 +04:00
|
|
|
lseek(cr_fdset->fds[CR_FD_PAGES], 0, SEEK_END);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
/* Ending page */
|
2012-01-22 20:20:40 +04:00
|
|
|
if (write_img(cr_fdset->fds[CR_FD_PAGES], &zero_page_entry))
|
|
|
|
goto err_restore;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
pr_info("\n");
|
|
|
|
pr_info("Summary: %16li pages dumped\n", nrpages_dumped);
|
2012-02-08 14:11:54 +04:00
|
|
|
ret = 0;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
err_restore:
|
2012-01-24 16:40:55 +04:00
|
|
|
fchmod(cr_fdset->fds[CR_FD_PAGES], CR_FD_PERM);
|
2011-11-23 13:08:28 +04:00
|
|
|
out:
|
2011-09-23 12:00:45 +04:00
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-01-11 13:32:43 +04:00
|
|
|
int parasite_cure_seized(struct parasite_ctl *ctl, struct list_head *vma_area_list)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
|
|
|
user_regs_struct_t regs, regs_orig;
|
|
|
|
struct vma_area *vma_area;
|
|
|
|
int ret = -1;
|
2012-02-01 16:23:50 +03:00
|
|
|
parasite_status_t args = { };
|
|
|
|
|
|
|
|
ret = parasite_execute(PARASITE_CMD_FINI, ctl,
|
|
|
|
&args, sizeof(args));
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't finalize parasite (pid: %d) task\n", ctl->pid);
|
|
|
|
goto err;
|
|
|
|
}
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
jerr(ptrace(PTRACE_GETREGS, ctl->pid, NULL, ®s), err);
|
|
|
|
|
|
|
|
regs_orig = regs;
|
|
|
|
|
|
|
|
vma_area = get_vma_by_ip(vma_area_list, regs.ip);
|
|
|
|
if (!vma_area) {
|
2011-09-30 14:37:12 +04:00
|
|
|
pr_err("No suitable VMA found to run cure (pid: %d)\n", ctl->pid);
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
regs.ip = vma_area->vma.start;
|
|
|
|
|
|
|
|
ret = munmap_seized(ctl->pid, ®s,
|
2012-01-13 17:20:57 +04:00
|
|
|
(void *)ctl->vma_area.vma.start,
|
|
|
|
(size_t)vma_entry_len(&ctl->vma_area.vma));
|
2011-09-23 12:00:45 +04:00
|
|
|
if (ret)
|
2011-09-30 14:37:12 +04:00
|
|
|
pr_err("munmap_seized failed (pid: %d)\n", ctl->pid);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
if (ptrace(PTRACE_SETREGS, ctl->pid, NULL, ®s_orig)) {
|
|
|
|
pr_panic("PTRACE_SETREGS failed (pid: %d)\n", ctl->pid);
|
2012-02-08 14:11:54 +04:00
|
|
|
ret = -1;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2012-01-11 13:32:43 +04:00
|
|
|
free(ctl);
|
2011-09-23 12:00:45 +04:00
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-01-11 13:31:30 +04:00
|
|
|
struct parasite_ctl *parasite_infect_seized(pid_t pid, struct list_head *vma_area_list)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
2012-02-01 16:23:50 +03:00
|
|
|
parasite_status_t args = { };
|
2011-09-23 12:00:45 +04:00
|
|
|
user_regs_struct_t regs, regs_orig;
|
|
|
|
struct parasite_ctl *ctl = NULL;
|
|
|
|
struct vma_area *vma_area;
|
|
|
|
void *mmaped;
|
2012-02-01 16:23:50 +03:00
|
|
|
int ret;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-01-13 17:20:57 +04:00
|
|
|
ctl = xzalloc(sizeof(*ctl));
|
2011-09-23 12:00:45 +04:00
|
|
|
if (!ctl) {
|
2011-09-30 14:37:12 +04:00
|
|
|
pr_err("Parasite control block allocation failed (pid: %d)\n", pid);
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup control block */
|
2012-01-13 17:20:57 +04:00
|
|
|
ctl->pid = pid;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
if (ptrace(PTRACE_GETREGS, pid, NULL, ®s))
|
2011-09-30 14:37:12 +04:00
|
|
|
pr_err_jmp(err_free);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
vma_area = get_vma_by_ip(vma_area_list, regs.ip);
|
|
|
|
if (!vma_area) {
|
2011-09-30 14:37:12 +04:00
|
|
|
pr_err("No suitable VMA found to run parasite "
|
2011-09-23 12:00:45 +04:00
|
|
|
"bootstrap code (pid: %d)\n", pid);
|
|
|
|
goto err_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
regs_orig = regs;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prepare for in-process syscall.
|
|
|
|
*/
|
2012-01-13 17:20:57 +04:00
|
|
|
ctl->vma_area.vma.prot = PROT_READ | PROT_WRITE | PROT_EXEC;
|
|
|
|
ctl->vma_area.vma.flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
regs.ip = vma_area->vma.start;
|
|
|
|
|
2012-01-11 13:31:30 +04:00
|
|
|
mmaped = mmap_seized(pid, ®s, NULL, (size_t)parasite_size,
|
2012-01-13 17:20:57 +04:00
|
|
|
(int)ctl->vma_area.vma.prot,
|
|
|
|
(int)ctl->vma_area.vma.flags,
|
2011-09-23 12:00:45 +04:00
|
|
|
(int)-1, (off_t)0);
|
|
|
|
|
|
|
|
if (!mmaped || (long)mmaped < 0) {
|
2011-09-30 14:37:12 +04:00
|
|
|
pr_err("Can't allocate memory for parasite blob (pid: %d)\n", pid);
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err_restore_regs;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctl->parasite_ip = PARASITE_HEAD_ADDR((unsigned long)mmaped);
|
|
|
|
ctl->addr_cmd = PARASITE_CMD_ADDR((unsigned long)mmaped);
|
|
|
|
ctl->addr_args = PARASITE_ARGS_ADDR((unsigned long)mmaped);
|
|
|
|
|
2012-01-13 17:20:57 +04:00
|
|
|
ctl->vma_area.vma.start = (u64)mmaped;
|
|
|
|
ctl->vma_area.vma.end = (u64)(mmaped + parasite_size);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
if (ptrace_poke_area(pid, parasite_blob, mmaped, parasite_size)) {
|
2011-09-30 14:37:12 +04:00
|
|
|
pr_err("Can't inject parasite blob (pid: %d)\n", pid);
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err_munmap_restore;
|
|
|
|
}
|
|
|
|
|
|
|
|
jerr(ptrace(PTRACE_SETREGS, pid, NULL, ®s_orig), err_munmap_restore);
|
|
|
|
|
2012-02-01 16:23:50 +03:00
|
|
|
ret = parasite_init(ctl, pid);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("%d: Can't create a transport socket\n", pid);
|
|
|
|
goto err_munmap_restore;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = parasite_set_logfd(ctl, pid);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("%d: Can't set a logging descriptor\n", pid);
|
|
|
|
goto err_munmap_restore;
|
|
|
|
}
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
return ctl;
|
|
|
|
|
2012-02-01 16:23:50 +03:00
|
|
|
err_fini:
|
|
|
|
ret = parasite_execute(PARASITE_CMD_FINI, ctl,
|
|
|
|
&args, sizeof(args));
|
|
|
|
if (ret)
|
|
|
|
pr_panic("Can't finalize parasite (pid: %d) task\n", ctl->pid);
|
2011-09-23 12:00:45 +04:00
|
|
|
err_munmap_restore:
|
|
|
|
regs = regs_orig, regs.ip = vma_area->vma.start;
|
|
|
|
if (munmap_seized(pid, ®s, mmaped, parasite_size))
|
|
|
|
pr_panic("mmap_seized failed (pid: %d)\n", pid);
|
|
|
|
err_restore_regs:
|
|
|
|
if (ptrace(PTRACE_SETREGS, pid, NULL, ®s_orig))
|
|
|
|
pr_panic("PTRACE_SETREGS failed (pid: %d)\n", pid);
|
|
|
|
err_free:
|
|
|
|
if (ctl)
|
|
|
|
free(ctl);
|
|
|
|
err:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* CONFIG_X86_64 */
|
|
|
|
# error x86-32 is not yet implemented
|
|
|
|
#endif /* CONFIG_X86_64 */
|