Commit 315b0d82 authored by Pavel Emelyanov's avatar Pavel Emelyanov Committed by Andrei Vagin

infect: Move pie tracing code into infect.c

Note -- presumably it's another functionality block inside compel,
so another .c file might be tempting here.
Signed-off-by: 's avatarPavel Emelyanov <xemul@virtuozzo.com>
Signed-off-by: 's avatarAndrei Vagin <avagin@virtuozzo.com>
parent 18248ce4
......@@ -69,6 +69,7 @@
#include "file-lock.h"
#include "action-scripts.h"
#include "shmem.h"
#include "infect.h"
#include "aio.h"
#include "lsm.h"
#include "seccomp.h"
......@@ -1615,7 +1616,8 @@ static int catch_tasks(bool root_seized, enum trace_flags *flag)
return -1;
}
ret = ptrace_stop_pie(pid, rsti(item)->breakpoint, flag);
ret = compel_stop_pie(pid, rsti(item)->breakpoint,
flag, fault_injected(FI_NO_BREAKPOINTS));
if (ret < 0)
return -1;
}
......@@ -1969,7 +1971,7 @@ static int restore_root_task(struct pstree_item *init)
futex_set_and_wake(&task_entries->start, CR_STATE_COMPLETE);
if (ret == 0)
ret = parasite_stop_on_syscall(task_entries->nr_threads,
ret = compel_stop_on_syscall(task_entries->nr_threads,
__NR(rt_sigreturn, 0), __NR(rt_sigreturn, 1), flag);
if (clear_breakpoints())
......
......@@ -58,5 +58,22 @@ extern int compel_run_in_thread(pid_t pid, unsigned int cmd,
struct parasite_ctl *ctl,
struct thread_ctx *octx);
/*
* The PTRACE_SYSCALL will trap task twice -- on
* enter into and on exit from syscall. If we trace
* a single task, we may skip half of all getregs
* calls -- on exit we don't need them.
*/
enum trace_flags {
TRACE_ALL,
TRACE_ENTER,
TRACE_EXIT,
};
extern int compel_stop_on_syscall(int tasks, int sys_nr,
int sys_nr_compat, enum trace_flags trace);
extern int compel_stop_pie(pid_t pid, void *addr, enum trace_flags *tf, bool no_bp);
extern int compel_unmap(struct parasite_ctl *ctl, unsigned long addr);
#endif
......@@ -45,6 +45,7 @@ struct infect_ctx {
#define INFECT_NO_MEMFD 0x1 /* don't use memfd() */
#define INFECT_FAIL_CONNECT 0x2 /* make parasite connect() fail */
#define INFECT_NO_BREAKPOINTS 0x4 /* no breakpoints in pie tracking */
struct parasite_ctl;
......@@ -101,20 +102,4 @@ extern int syscall_seized(struct parasite_ctl *ctl, int nr, unsigned long *ret,
extern bool arch_can_dump_task(struct parasite_ctl *ctl);
extern bool seized_native(struct parasite_ctl *ctl);
/*
* The PTRACE_SYSCALL will trap task twice -- on
* enter into and on exit from syscall. If we trace
* a single task, we may skip half of all getregs
* calls -- on exit we don't need them.
*/
enum trace_flags {
TRACE_ALL,
TRACE_ENTER,
TRACE_EXIT,
};
extern int parasite_stop_on_syscall(int tasks, int sys_nr,
int sys_nr_compat, enum trace_flags trace);
extern int ptrace_stop_pie(pid_t pid, void *addr, enum trace_flags *tf);
#endif /* __CR_PARASITE_SYSCALL_H__ */
......@@ -902,11 +902,12 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
return -1;
/* Go to sigreturn as closer as we can */
ret = ptrace_stop_pie(pid, ctl->sigreturn_addr, &flag);
ret = compel_stop_pie(pid, ctl->sigreturn_addr, &flag,
ctl->ictx.flags & INFECT_NO_BREAKPOINTS);
if (ret < 0)
return ret;
if (parasite_stop_on_syscall(1, __NR(rt_sigreturn, 0),
if (compel_stop_on_syscall(1, __NR(rt_sigreturn, 0),
__NR(rt_sigreturn, 1), flag))
return -1;
......@@ -1052,7 +1053,7 @@ int compel_unmap(struct parasite_ctl *ctl, unsigned long addr)
if (ret)
goto err;
ret = parasite_stop_on_syscall(1, __NR(munmap, 0),
ret = compel_stop_on_syscall(1, __NR(munmap, 0),
__NR(munmap, 1), TRACE_ENTER);
if (restore_thread_ctx(pid, &ctl->orig))
......@@ -1061,3 +1062,149 @@ err:
return ret;
}
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, &regs);
if (ret) {
pr_perror("ptrace");
return -1;
}
if (is_required_syscall(&regs, 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;
}
......@@ -612,117 +612,6 @@ int parasite_get_proc_fd_seized(struct parasite_ctl *ctl)
/* This is officially the 50000'th line in the CRIU source code */
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 parasite_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, &regs);
if (ret) {
pr_perror("ptrace");
return -1;
}
if (is_required_syscall(&regs, 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;
}
int parasite_dump_cgroup(struct parasite_ctl *ctl, struct parasite_dump_cgroup_args *cgroup)
{
int ret;
......@@ -781,6 +670,8 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
ctl->ictx.flags |= INFECT_NO_MEMFD;
if (fault_injected(FI_PARASITE_CONNECT))
ctl->ictx.flags |= INFECT_FAIL_CONNECT;
if (fault_injected(FI_NO_BREAKPOINTS))
ctl->ictx.flags |= INFECT_NO_BREAKPOINTS;
parasite_ensure_args_size(dump_pages_args_size(vma_area_list));
parasite_ensure_args_size(aio_rings_args_size(vma_area_list));
......@@ -797,37 +688,3 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
return ctl;
}
int ptrace_stop_pie(pid_t pid, void *addr, enum trace_flags *tf)
{
int ret;
if (fault_injected(FI_NO_BREAKPOINTS)) {
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;
}
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