mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-31 06:15:24 +00:00
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: Pavel Emelyanov <xemul@virtuozzo.com> Signed-off-by: Andrei Vagin <avagin@virtuozzo.com>
This commit is contained in:
committed by
Andrei Vagin
parent
18248ce4bd
commit
315b0d82aa
@@ -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__ */
|
||||
|
153
criu/infect.c
153
criu/infect.c
@@ -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, ®s);
|
||||
if (ret) {
|
||||
pr_perror("ptrace");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_required_syscall(®s, pid, sys_nr, sys_nr_compat)) {
|
||||
/*
|
||||
* The process is going to execute the required syscall,
|
||||
* the next stop will be on the exit from this syscall
|
||||
*/
|
||||
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
||||
if (ret) {
|
||||
pr_perror("ptrace");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid = wait4(pid, &status, __WALL, NULL);
|
||||
if (pid == -1) {
|
||||
pr_perror("wait4 failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!task_is_trapped(status, pid))
|
||||
return -1;
|
||||
|
||||
pr_debug("%d was stopped\n", pid);
|
||||
tasks--;
|
||||
continue;
|
||||
}
|
||||
goon:
|
||||
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
||||
if (ret) {
|
||||
pr_perror("ptrace");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -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, ®s);
|
||||
if (ret) {
|
||||
pr_perror("ptrace");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_required_syscall(®s, pid, sys_nr, sys_nr_compat)) {
|
||||
/*
|
||||
* The process is going to execute the required syscall,
|
||||
* the next stop will be on the exit from this syscall
|
||||
*/
|
||||
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
||||
if (ret) {
|
||||
pr_perror("ptrace");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid = wait4(pid, &status, __WALL, NULL);
|
||||
if (pid == -1) {
|
||||
pr_perror("wait4 failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!task_is_trapped(status, pid))
|
||||
return -1;
|
||||
|
||||
pr_debug("%d was stopped\n", pid);
|
||||
tasks--;
|
||||
continue;
|
||||
}
|
||||
goon:
|
||||
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
||||
if (ret) {
|
||||
pr_perror("ptrace");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user