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);
|
|
|
|
}
|
|
|
|
|
2012-02-12 00:36:45 +04:00
|
|
|
static int syscall_seized(pid_t pid, user_regs_struct_t *regs)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
|
|
|
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)));
|
|
|
|
|
2012-02-12 00:36:45 +04:00
|
|
|
start_ip = (unsigned long)regs->ip;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2012-02-12 00:36:45 +04:00
|
|
|
regs->orig_ax = -1; /* avoid end-of-syscall processing */
|
2011-09-23 12:00:45 +04:00
|
|
|
again:
|
2012-02-12 00:36:45 +04:00
|
|
|
jerr(ptrace(PTRACE_SETREGS, pid, NULL, regs), err_restore);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
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_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);
|
|
|
|
|
2012-02-12 00:36:45 +04:00
|
|
|
jerr(ptrace(PTRACE_GETREGS, pid, NULL, regs), err_restore_full);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
err_restore_full:
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-02-12 00:34:12 +04:00
|
|
|
static void *mmap_seized(pid_t pid, user_regs_struct_t *regs,
|
2012-02-12 00:32:32 +04:00
|
|
|
void *addr, size_t length, int prot,
|
|
|
|
int flags, int fd, off_t offset)
|
|
|
|
{
|
|
|
|
void *mmaped = NULL;
|
|
|
|
int ret;
|
|
|
|
|
2012-02-12 00:36:45 +04:00
|
|
|
regs->ax = (unsigned long)__NR_mmap; /* mmap */
|
|
|
|
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 */
|
2012-02-12 00:32:32 +04:00
|
|
|
|
2012-02-12 00:36:45 +04:00
|
|
|
ret = syscall_seized(pid, regs);
|
2012-02-12 00:32:32 +04:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2012-02-12 00:36:45 +04:00
|
|
|
mmaped = (void *)regs->ax;
|
2012-02-12 00:32:32 +04:00
|
|
|
|
|
|
|
/* error code from the kernel space */
|
|
|
|
if ((long)mmaped < 0)
|
|
|
|
mmaped = NULL;
|
|
|
|
err:
|
|
|
|
return mmaped;
|
|
|
|
}
|
|
|
|
|
2012-02-12 00:34:12 +04:00
|
|
|
static int munmap_seized(pid_t pid, user_regs_struct_t *regs,
|
2012-02-12 00:32:32 +04:00
|
|
|
void *addr, size_t length)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2012-02-12 00:36:45 +04:00
|
|
|
regs->ax = (unsigned long)__NR_munmap; /* mmap */
|
|
|
|
regs->di = (unsigned long)addr; /* @addr */
|
|
|
|
regs->si = (unsigned long)length; /* @length */
|
2012-02-12 00:32:32 +04:00
|
|
|
|
2012-02-12 00:36:45 +04:00
|
|
|
ret = syscall_seized(pid, regs);
|
2012-02-12 00:32:32 +04:00
|
|
|
if (!ret)
|
2012-02-12 00:36:45 +04:00
|
|
|
ret = (int)regs->ax;
|
2012-02-12 00:32:32 +04:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
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) {
|
2012-02-08 20:36:59 +04:00
|
|
|
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;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-02-12 14:52:14 +04:00
|
|
|
static int parasite_execute(unsigned long cmd, struct parasite_ctl *ctl,
|
2011-11-29 13:33:59 +03:00
|
|
|
parasite_status_t *args, int args_size)
|
|
|
|
{
|
|
|
|
user_regs_struct_t regs, regs_orig;
|
|
|
|
int status, ret = -1;
|
|
|
|
siginfo_t siginfo;
|
|
|
|
|
|
|
|
jerr(ptrace(PTRACE_GETREGS, ctl->pid, NULL, ®s_orig), err);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pass the command first, it's immutable.
|
|
|
|
*/
|
2012-02-12 14:51:38 +04:00
|
|
|
memcpy(ctl->addr_cmd, &cmd, sizeof(cmd));
|
2011-11-29 13:33:59 +03:00
|
|
|
again:
|
2012-02-12 00:33:30 +04:00
|
|
|
regs = regs_orig;
|
2011-11-29 13:33:59 +03:00
|
|
|
regs.ip = ctl->parasite_ip;
|
|
|
|
jerr(ptrace(PTRACE_SETREGS, ctl->pid, NULL, ®s), err_restore);
|
|
|
|
|
2012-02-12 14:51:38 +04:00
|
|
|
memcpy(ctl->addr_args, args, args_size);
|
2011-11-29 13:33:59 +03:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2012-02-12 14:51:38 +04:00
|
|
|
|
|
|
|
memcpy(args, ctl->addr_args, args_size);
|
2011-11-29 13:33:59 +03:00
|
|
|
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 = { };
|
2012-02-12 00:26:54 +04:00
|
|
|
parasite_status_t *st = ¶site_dumppages.status;
|
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
|
|
|
|
2012-02-12 00:26:54 +04:00
|
|
|
ret = parasite_prep_file(CR_FD_PAGES_SHMEM, ctl, cr_fdset);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = parasite_execute(PARASITE_CMD_DUMPPAGES_INIT, ctl, st, sizeof(*st));
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_panic("Dumping pages failed with %li (%li) at %li\n",
|
|
|
|
parasite_dumppages.status.ret,
|
|
|
|
parasite_dumppages.status.sys_ret,
|
|
|
|
parasite_dumppages.status.line);
|
|
|
|
goto out;
|
|
|
|
}
|
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;
|
|
|
|
|
2012-02-12 00:26:54 +04:00
|
|
|
if (vma_area_is(vma_area, VMA_ANON_PRIVATE) ||
|
|
|
|
vma_area_is(vma_area, VMA_FILE_PRIVATE))
|
|
|
|
parasite_dumppages.fd_type = PG_PRIV;
|
|
|
|
else if (vma_area_is(vma_area, VMA_ANON_SHARED))
|
|
|
|
parasite_dumppages.fd_type = PG_SHARED;
|
|
|
|
else {
|
|
|
|
pr_warning("Unexpected VMA area found\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2012-02-12 00:26:54 +04:00
|
|
|
goto out;
|
2011-11-22 20:07:20 +04:00
|
|
|
}
|
|
|
|
|
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-12 00:26:54 +04:00
|
|
|
parasite_execute(PARASITE_CMD_DUMPPAGES_FINI, ctl, st, sizeof(*st));
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-01-22 20:20:40 +04:00
|
|
|
if (write_img(cr_fdset->fds[CR_FD_PAGES], &zero_page_entry))
|
2012-02-12 00:26:54 +04:00
|
|
|
goto out;
|
|
|
|
if (write_img(cr_fdset->fds[CR_FD_PAGES_SHMEM], &zero_page_entry))
|
|
|
|
goto out;
|
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
|
|
|
|
2011-11-23 13:08:28 +04:00
|
|
|
out:
|
2012-02-12 00:26:54 +04:00
|
|
|
fchmod(cr_fdset->fds[CR_FD_PAGES], CR_FD_PERM);
|
|
|
|
fchmod(cr_fdset->fds[CR_FD_PAGES_SHMEM], CR_FD_PERM);
|
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;
|
|
|
|
|
2012-02-12 14:50:34 +04:00
|
|
|
ret = munmap_seized(ctl->pid, ®s, ctl->remote_map, ctl->map_length);
|
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-02-12 14:50:34 +04:00
|
|
|
struct parasite_ctl *parasite_infect_seized(pid_t pid, int pid_dir, 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;
|
2012-02-12 14:50:34 +04:00
|
|
|
char fname[128];
|
|
|
|
int ret, fd;
|
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;
|
|
|
|
regs.ip = vma_area->vma.start;
|
|
|
|
|
2012-02-12 14:50:34 +04:00
|
|
|
ctl->remote_map = mmap_seized(pid, ®s, NULL, (size_t)parasite_size,
|
|
|
|
PROT_READ | PROT_WRITE | PROT_EXEC,
|
|
|
|
MAP_ANONYMOUS | MAP_SHARED, -1, 0);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-02-12 14:50:34 +04:00
|
|
|
if (!ctl->remote_map || (long)ctl->remote_map < 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;
|
|
|
|
}
|
|
|
|
|
2012-02-12 14:50:34 +04:00
|
|
|
ctl->map_length = round_up(parasite_size, PAGE_SIZE);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-02-12 14:50:34 +04:00
|
|
|
ctl->parasite_ip = PARASITE_HEAD_ADDR((unsigned long)ctl->remote_map);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-02-12 14:50:34 +04:00
|
|
|
snprintf(fname, sizeof(fname), "map_files/%p-%p",
|
|
|
|
ctl->remote_map, ctl->remote_map + ctl->map_length);
|
|
|
|
fd = openat(pid_dir, fname, O_RDWR);
|
|
|
|
if (fd < 0) {
|
|
|
|
pr_perror("Can't open remote parasite map");
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err_munmap_restore;
|
|
|
|
}
|
|
|
|
|
2012-02-12 14:50:34 +04:00
|
|
|
ctl->local_map = mmap(NULL, parasite_size, PROT_READ | PROT_WRITE,
|
|
|
|
MAP_SHARED | MAP_FILE, fd, 0);
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
if (ctl->local_map == MAP_FAILED) {
|
|
|
|
pr_perror("Can't map remote parasite map");
|
|
|
|
goto err_munmap_restore;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("Putting parasite blob into %p->%p\n", ctl->local_map, ctl->remote_map);
|
|
|
|
memcpy(ctl->local_map, parasite_blob, parasite_size);
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
jerr(ptrace(PTRACE_SETREGS, pid, NULL, ®s_orig), err_munmap_restore);
|
|
|
|
|
2012-02-12 14:51:38 +04:00
|
|
|
ctl->addr_cmd = (void *)PARASITE_CMD_ADDR((unsigned long)ctl->local_map);
|
|
|
|
ctl->addr_args = (void *)PARASITE_ARGS_ADDR((unsigned long)ctl->local_map);
|
|
|
|
|
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;
|
2012-02-12 14:50:34 +04:00
|
|
|
if (munmap_seized(pid, ®s, ctl->remote_map, ctl->map_length))
|
2011-09-23 12:00:45 +04:00
|
|
|
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 */
|