mirror of
https://github.com/checkpoint-restore/criu
synced 2025-09-04 16:25:31 +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 "file-lock.h"
|
||||||
#include "action-scripts.h"
|
#include "action-scripts.h"
|
||||||
#include "shmem.h"
|
#include "shmem.h"
|
||||||
|
#include "infect.h"
|
||||||
#include "aio.h"
|
#include "aio.h"
|
||||||
#include "lsm.h"
|
#include "lsm.h"
|
||||||
#include "seccomp.h"
|
#include "seccomp.h"
|
||||||
@@ -1615,7 +1616,8 @@ static int catch_tasks(bool root_seized, enum trace_flags *flag)
|
|||||||
return -1;
|
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)
|
if (ret < 0)
|
||||||
return -1;
|
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);
|
futex_set_and_wake(&task_entries->start, CR_STATE_COMPLETE);
|
||||||
|
|
||||||
if (ret == 0)
|
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);
|
__NR(rt_sigreturn, 0), __NR(rt_sigreturn, 1), flag);
|
||||||
|
|
||||||
if (clear_breakpoints())
|
if (clear_breakpoints())
|
||||||
|
@@ -58,5 +58,22 @@ extern int compel_run_in_thread(pid_t pid, unsigned int cmd,
|
|||||||
struct parasite_ctl *ctl,
|
struct parasite_ctl *ctl,
|
||||||
struct thread_ctx *octx);
|
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);
|
extern int compel_unmap(struct parasite_ctl *ctl, unsigned long addr);
|
||||||
#endif
|
#endif
|
||||||
|
@@ -45,6 +45,7 @@ struct infect_ctx {
|
|||||||
|
|
||||||
#define INFECT_NO_MEMFD 0x1 /* don't use memfd() */
|
#define INFECT_NO_MEMFD 0x1 /* don't use memfd() */
|
||||||
#define INFECT_FAIL_CONNECT 0x2 /* make parasite connect() fail */
|
#define INFECT_FAIL_CONNECT 0x2 /* make parasite connect() fail */
|
||||||
|
#define INFECT_NO_BREAKPOINTS 0x4 /* no breakpoints in pie tracking */
|
||||||
|
|
||||||
struct parasite_ctl;
|
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 arch_can_dump_task(struct parasite_ctl *ctl);
|
||||||
extern bool seized_native(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__ */
|
#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;
|
return -1;
|
||||||
|
|
||||||
/* Go to sigreturn as closer as we can */
|
/* 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)
|
if (ret < 0)
|
||||||
return ret;
|
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))
|
__NR(rt_sigreturn, 1), flag))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@@ -1052,7 +1053,7 @@ int compel_unmap(struct parasite_ctl *ctl, unsigned long addr)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
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);
|
__NR(munmap, 1), TRACE_ENTER);
|
||||||
|
|
||||||
if (restore_thread_ctx(pid, &ctl->orig))
|
if (restore_thread_ctx(pid, &ctl->orig))
|
||||||
@@ -1061,3 +1062,149 @@ err:
|
|||||||
return ret;
|
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 */
|
/* 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 parasite_dump_cgroup(struct parasite_ctl *ctl, struct parasite_dump_cgroup_args *cgroup)
|
||||||
{
|
{
|
||||||
int ret;
|
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;
|
ctl->ictx.flags |= INFECT_NO_MEMFD;
|
||||||
if (fault_injected(FI_PARASITE_CONNECT))
|
if (fault_injected(FI_PARASITE_CONNECT))
|
||||||
ctl->ictx.flags |= INFECT_FAIL_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(dump_pages_args_size(vma_area_list));
|
||||||
parasite_ensure_args_size(aio_rings_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;
|
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