From 2db92dee22af99b36fd33e3390201de08cc56c84 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Thu, 11 Jul 2013 13:46:51 +0400 Subject: [PATCH] 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 Signed-off-by: Pavel Emelyanov --- arch/arm/crtools.c | 2 +- arch/x86/crtools.c | 2 +- include/parasite-syscall.h | 3 +- parasite-syscall.c | 137 ++++++++++++++----------------------- 4 files changed, 54 insertions(+), 90 deletions(-) diff --git a/arch/arm/crtools.c b/arch/arm/crtools.c index 2ca59d11f..bbc5cf904 100644 --- a/arch/arm/crtools.c +++ b/arch/arm/crtools.c @@ -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; diff --git a/arch/x86/crtools.c b/arch/x86/crtools.c index 9b7cca503..c8c94336b 100644 --- a/arch/x86/crtools.c +++ b/arch/x86/crtools.c @@ -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; diff --git a/include/parasite-syscall.h b/include/parasite-syscall.h index 3157e2719..fb1236dda 100644 --- a/include/parasite-syscall.h +++ b/include/parasite-syscall.h @@ -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, diff --git a/parasite-syscall.c b/parasite-syscall.c index 48b4328da..2b9877c29 100644 --- a/parasite-syscall.c +++ b/parasite-syscall.c @@ -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;