mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-30 05:48:05 +00:00
parasite: block signals for each parasite command
Pending signals should be saved, so signals should be blocked. Signals are blocked for EACH command, because a chance of destroying a process state should be a small as possible. At the end there will only two "trapped" commands -- to dump thread and to start daemon in parasite, so this doesn't add significant overheads. If crtools is killed between two commands, a dumped process will run continue. Signed-off-by: Andrey Vagin <avagin@openvz.org> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
This commit is contained in:
parent
ea93b23783
commit
2db92dee22
@ -75,7 +75,7 @@ int syscall_seized(struct parasite_ctl *ctl, int nr, unsigned long *ret,
|
||||
|
||||
parasite_setup_regs(ctl->syscall_ip, 0, ®s);
|
||||
err = __parasite_execute_trap(ctl, ctl->pid.real, ®s,
|
||||
&ctl->regs_orig, 0);
|
||||
&ctl->regs_orig, &ctl->sig_blocked);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -102,7 +102,7 @@ int syscall_seized(struct parasite_ctl *ctl, int nr, unsigned long *ret,
|
||||
|
||||
parasite_setup_regs(ctl->syscall_ip, 0, ®s);
|
||||
err = __parasite_execute_trap(ctl, ctl->pid.real, ®s,
|
||||
&ctl->regs_orig, 0);
|
||||
&ctl->regs_orig, &ctl->sig_blocked);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -29,7 +29,6 @@ struct parasite_ctl {
|
||||
user_regs_struct_t regs_orig; /* original registers */
|
||||
|
||||
k_rtsigset_t sig_blocked;
|
||||
bool use_sig_blocked;
|
||||
|
||||
void *rstack; /* thread leader stack*/
|
||||
struct rt_sigframe *sigframe;
|
||||
@ -105,7 +104,7 @@ extern int syscall_seized(struct parasite_ctl *ctl, int nr, unsigned long *ret,
|
||||
extern int __parasite_execute_trap(struct parasite_ctl *ctl, pid_t pid,
|
||||
user_regs_struct_t *regs,
|
||||
user_regs_struct_t *regs_orig,
|
||||
bool signals_blocked);
|
||||
k_rtsigset_t *sigmask);
|
||||
extern bool arch_can_dump_task(pid_t pid);
|
||||
|
||||
extern int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid,
|
||||
|
@ -69,13 +69,19 @@ static struct vma_area *get_vma_by_ip(struct list_head *vma_area_list, unsigned
|
||||
int __parasite_execute_trap(struct parasite_ctl *ctl, pid_t pid,
|
||||
user_regs_struct_t *regs,
|
||||
user_regs_struct_t *regs_orig,
|
||||
bool signals_blocked)
|
||||
k_rtsigset_t *sigmask)
|
||||
{
|
||||
k_rtsigset_t blockall;
|
||||
siginfo_t siginfo;
|
||||
int status;
|
||||
int ret = -1;
|
||||
|
||||
again:
|
||||
ksigfillset(&blockall);
|
||||
if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &blockall)) {
|
||||
pr_perror("Can't block signals");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_SETREGS, pid, NULL, regs)) {
|
||||
pr_perror("Can't set registers (pid: %d)", pid);
|
||||
goto err;
|
||||
@ -112,75 +118,11 @@ again:
|
||||
}
|
||||
|
||||
if (WSTOPSIG(status) != SIGTRAP || siginfo.si_code != ARCH_SI_TRAP) {
|
||||
retry_signal:
|
||||
pr_debug("** delivering signal %d si_code=%d\n",
|
||||
siginfo.si_signo, siginfo.si_code);
|
||||
|
||||
if (signals_blocked) {
|
||||
pr_err("Unexpected %d task interruption, aborting\n", pid);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* FIXME: jerr(siginfo.si_code > 0, err_restore); */
|
||||
|
||||
/*
|
||||
* This requires some explanation. If a signal from original
|
||||
* program delivered while we're trying to execute our
|
||||
* injected blob -- we need to setup original registers back
|
||||
* so the kernel would make sigframe for us and update the
|
||||
* former registers.
|
||||
*
|
||||
* Then we should swap registers back to our modified copy
|
||||
* and retry.
|
||||
*/
|
||||
|
||||
if (ptrace(PTRACE_SETREGS, pid, NULL, regs_orig)) {
|
||||
pr_perror("Can't set registers (pid: %d)", pid);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL)) {
|
||||
pr_perror("Can't interrupt (pid: %d)", pid);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_CONT, pid, NULL, (void *)(unsigned long)siginfo.si_signo)) {
|
||||
pr_perror("Can't continue (pid: %d)", pid);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (wait4(pid, &status, __WALL, NULL) != pid) {
|
||||
pr_perror("Waited pid mismatch (pid: %d)", pid);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!WIFSTOPPED(status)) {
|
||||
pr_err("Task is still running (pid: %d)\n", pid);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo)) {
|
||||
pr_perror("Can't get siginfo (pid: %d)", pid);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (SI_EVENT(siginfo.si_code) != PTRACE_EVENT_STOP)
|
||||
goto retry_signal;
|
||||
|
||||
/*
|
||||
* Signal is delivered, so we should update
|
||||
* original registers.
|
||||
*/
|
||||
{
|
||||
user_regs_struct_t r;
|
||||
if (ptrace(PTRACE_GETREGS, pid, NULL, &r)) {
|
||||
pr_perror("Can't obtain registers (pid: %d)", pid);
|
||||
goto err;
|
||||
}
|
||||
*regs_orig = r;
|
||||
}
|
||||
|
||||
goto again;
|
||||
pr_err("Unexpected %d task interruption, aborting\n", pid);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -189,6 +131,10 @@ retry_signal:
|
||||
*/
|
||||
ret = 0;
|
||||
err:
|
||||
if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &sigmask)) {
|
||||
pr_perror("Can't block signals");
|
||||
ret = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -201,7 +147,8 @@ void *parasite_args_s(struct parasite_ctl *ctl, int args_size)
|
||||
static int parasite_execute_trap_by_pid(unsigned int cmd,
|
||||
struct parasite_ctl *ctl, pid_t pid,
|
||||
user_regs_struct_t *regs_orig,
|
||||
void *stack, bool use_sig_blocked)
|
||||
void *stack,
|
||||
k_rtsigset_t *sigmask)
|
||||
{
|
||||
user_regs_struct_t regs = *regs_orig;
|
||||
int ret;
|
||||
@ -210,7 +157,7 @@ static int parasite_execute_trap_by_pid(unsigned int cmd,
|
||||
|
||||
parasite_setup_regs(ctl->parasite_ip, stack, ®s);
|
||||
|
||||
ret = __parasite_execute_trap(ctl, pid, ®s, regs_orig, use_sig_blocked);
|
||||
ret = __parasite_execute_trap(ctl, pid, ®s, regs_orig, sigmask);
|
||||
if (ret == 0)
|
||||
ret = (int)REG_RES(regs);
|
||||
|
||||
@ -228,8 +175,9 @@ static int parasite_execute_trap_by_pid(unsigned int cmd,
|
||||
|
||||
static int parasite_execute_trap(unsigned int cmd, struct parasite_ctl *ctl)
|
||||
{
|
||||
return parasite_execute_trap_by_pid(cmd, ctl, ctl->pid.real, &ctl->regs_orig,
|
||||
ctl->rstack, ctl->use_sig_blocked);
|
||||
return parasite_execute_trap_by_pid(cmd, ctl, ctl->pid.real,
|
||||
&ctl->regs_orig, ctl->rstack,
|
||||
&ctl->sig_blocked);
|
||||
}
|
||||
|
||||
static int __parasite_send_cmd(int sockfd, struct ctl_msg *m)
|
||||
@ -444,9 +392,6 @@ static int parasite_init(struct parasite_ctl *ctl, pid_t pid, struct pstree_item
|
||||
goto err;
|
||||
}
|
||||
|
||||
ctl->sig_blocked = args->sig_blocked;
|
||||
ctl->use_sig_blocked = true;
|
||||
|
||||
sock = accept_tsock();
|
||||
if (sock < 0)
|
||||
goto err;
|
||||
@ -462,6 +407,9 @@ static int parasite_daemonize(struct parasite_ctl *ctl)
|
||||
pid_t pid = ctl->pid.real;
|
||||
user_regs_struct_t regs;
|
||||
struct ctl_msg m = { };
|
||||
k_rtsigset_t blockall;
|
||||
|
||||
ksigfillset(&blockall);
|
||||
|
||||
*ctl->addr_cmd = PARASITE_CMD_DAEMONIZE;
|
||||
|
||||
@ -473,6 +421,11 @@ static int parasite_daemonize(struct parasite_ctl *ctl)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &blockall)) {
|
||||
pr_perror("Can't block signals");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_CONT, pid, NULL, NULL)) {
|
||||
pr_perror("Can't continue (pid: %d)\n", pid);
|
||||
ptrace(PTRACE_SETREGS, pid, NULL, ctl->regs_orig);
|
||||
@ -492,6 +445,9 @@ static int parasite_daemonize(struct parasite_ctl *ctl)
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), ctl->sig_blocked))
|
||||
pr_perror("Can't block signals");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -507,6 +463,14 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
|
||||
|
||||
args = parasite_args(ctl, struct parasite_dump_thread);
|
||||
|
||||
ret = ptrace(PTRACE_GETSIGMASK, pid, sizeof(k_rtsigset_t),
|
||||
&core->thread_core->blk_sigset);
|
||||
if (ret) {
|
||||
pr_perror("ptrace can't get signal blocking mask for %d", pid);
|
||||
return -1;
|
||||
}
|
||||
core->thread_core->has_blk_sigset = true;
|
||||
|
||||
ret = ptrace(PTRACE_GETREGS, pid, NULL, ®s_orig);
|
||||
if (ret) {
|
||||
pr_perror("Can't obtain registers (pid: %d)", pid);
|
||||
@ -515,7 +479,8 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
|
||||
|
||||
ret = parasite_execute_trap_by_pid(PARASITE_CMD_INIT_THREAD, ctl,
|
||||
pid, ®s_orig,
|
||||
ctl->r_thread_stack, false);
|
||||
ctl->r_thread_stack,
|
||||
(k_rtsigset_t *) &core->thread_core->blk_sigset);
|
||||
if (ret) {
|
||||
pr_err("Can't init thread in parasite %d\n", pid);
|
||||
return -1;
|
||||
@ -527,17 +492,14 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
|
||||
|
||||
if (parasite_execute_trap_by_pid(PARASITE_CMD_FINI_THREAD, ctl,
|
||||
pid, ®s_orig,
|
||||
ctl->r_thread_stack, true)) {
|
||||
ctl->r_thread_stack,
|
||||
(k_rtsigset_t *) &core->thread_core->blk_sigset)) {
|
||||
pr_err("Can't init thread in parasite %d\n", pid);
|
||||
return -1;
|
||||
}
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
memcpy(&core->thread_core->blk_sigset,
|
||||
&args->blocked, sizeof(k_rtsigset_t));
|
||||
core->thread_core->has_blk_sigset = true;
|
||||
|
||||
BUG_ON(!core->thread_core->sas);
|
||||
copy_sas(core->thread_core->sas, &args->sas);
|
||||
|
||||
@ -859,8 +821,6 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
|
||||
}
|
||||
}
|
||||
|
||||
ctl->use_sig_blocked = false;
|
||||
|
||||
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
||||
if (ret) {
|
||||
pr_perror("ptrace");
|
||||
@ -957,6 +917,11 @@ struct parasite_ctl *parasite_prep_ctl(pid_t pid, struct vm_area_list *vma_area_
|
||||
|
||||
ctl->tsock = -1;
|
||||
|
||||
if (ptrace(PTRACE_GETSIGMASK, pid, sizeof(k_rtsigset_t), &ctl->sig_blocked)) {
|
||||
pr_perror("ptrace doesn't support PTRACE_GETSIGMASK\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_GETREGS, pid, NULL, &ctl->regs_orig)) {
|
||||
pr_err("Can't obtain registers (pid: %d)\n", pid);
|
||||
goto err;
|
||||
@ -1065,6 +1030,9 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
|
||||
map_exchange_size += RESTORE_STACK_SIGFRAME + PARASITE_STACK_SIZE;
|
||||
if (item->nr_threads > 1)
|
||||
map_exchange_size += PARASITE_STACK_SIZE;
|
||||
|
||||
memcpy(&item->core[0]->tc->blk_sigset, &ctl->sig_blocked, sizeof(k_rtsigset_t));
|
||||
|
||||
ret = parasite_map_exchange(ctl, map_exchange_size);
|
||||
if (ret)
|
||||
goto err_restore;
|
||||
@ -1110,9 +1078,6 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
|
||||
goto err_restore;
|
||||
}
|
||||
|
||||
memcpy(&item->core[0]->tc->blk_sigset,
|
||||
&ctl->sig_blocked, sizeof(k_rtsigset_t));
|
||||
|
||||
if (construct_sigframe(ctl->sigframe, ctl->rsigframe, item->core[0]))
|
||||
goto err_restore;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user