[CRIU] [PATCH 13/22] dump: block signals with help of ptrace
Andrey Vagin
avagin at openvz.org
Wed May 8 09:28:40 EDT 2013
Currently crtools blocks signals from a parasite code, but for executing
the parasite code a process has to handle all pending non-blockedsignals.
It is not a problem until crtools decide to support stopped tasks.
Registers were saved before blocking signals, so a task can enter in a
signal hangler and block a part of signals, than it will be restored out
of signal handler with blocked signals.
Signed-off-by: Andrey Vagin <avagin at openvz.org>
---
cr-dump.c | 10 ++--
include/parasite-syscall.h | 6 ++-
include/parasite.h | 1 -
include/ptrace.h | 3 ++
parasite-syscall.c | 124 +++++++++++++++++++--------------------------
pie/parasite.c | 22 +-------
6 files changed, 68 insertions(+), 98 deletions(-)
diff --git a/cr-dump.c b/cr-dump.c
index 2ce4950..dd39400 100644
--- a/cr-dump.c
+++ b/cr-dump.c
@@ -627,6 +627,7 @@ static int dump_task_core_all(struct parasite_ctl *ctl,
struct vm_area_list *vma_area_list,
const struct cr_fdset *cr_fdset)
{
+ k_rtsigset_t *sig_blocked = &ctl->threads[0].sig_blocked;
int fd_core = fdset_fd(cr_fdset, CR_FD_CORE);
int ret = -1;
pid_t pid = ctl->pid.real;
@@ -652,7 +653,7 @@ static int dump_task_core_all(struct parasite_ctl *ctl,
strncpy((char *)core->tc->comm, stat->comm, TASK_COMM_LEN);
core->tc->flags = stat->flags;
BUILD_BUG_ON(sizeof(core->tc->blk_sigset) != sizeof(k_rtsigset_t));
- memcpy(&core->tc->blk_sigset, &misc->blocked, sizeof(k_rtsigset_t));
+ memcpy(&core->tc->blk_sigset, sig_blocked, sizeof(k_rtsigset_t));
core->tc->task_state = TASK_ALIVE;
core->tc->exit_code = 0;
@@ -1069,7 +1070,8 @@ static int collect_file_locks(const struct cr_options *opts)
}
static int dump_task_thread(struct parasite_ctl *parasite_ctl,
- struct pid *tid, CoreEntry *core)
+ struct pid *tid, CoreEntry *core,
+ k_rtsigset_t *sig_blocked)
{
int ret = -1, fd_core;
pid_t pid = tid->real;
@@ -1089,6 +1091,7 @@ static int dump_task_thread(struct parasite_ctl *parasite_ctl,
}
core->thread_core->has_blk_sigset = true;
+ memcpy(&core->thread_core->blk_sigset, sig_blocked, sizeof(*sig_blocked));
ret = dump_sched_info(pid, core->thread_core);
if (ret)
@@ -1202,7 +1205,8 @@ static int dump_task_threads(struct parasite_ctl *parasite_ctl,
item->threads[i].virt = item->pid.virt;
continue;
}
- if (dump_task_thread(parasite_ctl, &item->threads[i], item->core[i]))
+ if (dump_task_thread(parasite_ctl, &item->threads[i], item->core[i],
+ ¶site_ctl->threads[i].sig_blocked))
return -1;
}
diff --git a/include/parasite-syscall.h b/include/parasite-syscall.h
index 9865014..3906f20 100644
--- a/include/parasite-syscall.h
+++ b/include/parasite-syscall.h
@@ -8,8 +8,12 @@
struct parasite_thread_ctl
{
pid_t tid;
+
user_regs_struct_t regs_orig; /* original registers */
bool daemonized;
+
+ k_rtsigset_t sig_blocked;
+ bool use_sig_blocked;
};
/* parasite control block */
@@ -23,8 +27,6 @@ struct parasite_ctl {
unsigned long syscall_ip; /* entry point of infection */
u8 code_orig[BUILTIN_SYSCALL_SIZE];
- int signals_blocked;
-
unsigned int *addr_cmd; /* addr for command */
void *addr_args; /* address for arguments */
unsigned long args_size;
diff --git a/include/parasite.h b/include/parasite.h
index 421dad3..78797c7 100644
--- a/include/parasite.h
+++ b/include/parasite.h
@@ -114,7 +114,6 @@ struct parasite_dump_itimers_args {
struct parasite_dump_misc {
unsigned long brk;
- k_rtsigset_t blocked;
u32 pid;
u32 sid;
diff --git a/include/ptrace.h b/include/ptrace.h
index 4d1bc92..ae3657a 100644
--- a/include/ptrace.h
+++ b/include/ptrace.h
@@ -23,6 +23,9 @@ struct ptrace_peeksiginfo_args {
__u32 nr; /* how may siginfos to take */
};
+#define PTRACE_GETSIGMASK 0x420a
+#define PTRACE_SETSIGMASK 0x420b
+
/* Read signals from a shared (process wide) queue */
#define PTRACE_PEEKSIGINFO_SHARED (1 << 0)
#endif
diff --git a/parasite-syscall.c b/parasite-syscall.c
index a0cc8a7..bbf17aa 100644
--- a/parasite-syscall.c
+++ b/parasite-syscall.c
@@ -67,7 +67,6 @@ int __parasite_execute_trap(struct parasite_ctl *ctl, pid_t pid, user_regs_struc
int status;
int ret = -1;
-again:
if (ptrace(PTRACE_SETREGS, pid, NULL, regs)) {
pr_perror("Can't set registers (pid: %d)", pid);
goto err;
@@ -104,75 +103,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 (ctl->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, &ctl->threads[0].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;
- }
- ctl->threads[0].regs_orig = r;
- }
-
- goto again;
+ pr_err("Unexpected %d task interruption, aborting\n", pid);
+ goto err;
}
/*
@@ -491,7 +426,6 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, struct pid *tid,
ret = parasite_execute_daemon_by_pid(PARASITE_CMD_DUMP_THREAD, ctl, tid->real);
- memcpy(&core->thread_core->blk_sigset, &args->blocked, sizeof(args->blocked));
CORE_THREAD_ARCH_INFO(core)->clear_tid_addr = encode_pointer(args->tid_addr);
tid->virt = args->tid;
core_put_tls(core, args->tls);
@@ -758,6 +692,51 @@ int parasite_fini_threads_seized(struct parasite_ctl *ctl)
return ret;
}
+static int block_signals(struct pstree_item *item, struct parasite_ctl *ctl)
+{
+ int ret = 0, i;
+ k_rtsigset_t blockall;
+
+ ksigfillset(&blockall);
+
+ for (i = 0; i < item->nr_threads; i++) {
+ k_rtsigset_t *mask = &ctl->threads[i].sig_blocked;
+ pid_t tid = item->threads[i].real;
+
+ ret = ptrace(PTRACE_GETSIGMASK, tid, sizeof(*mask), mask);
+ if (ret) {
+ pr_perror("Can't get sigblockmask of %d\n", tid);
+ break;
+ }
+ ret = ptrace(PTRACE_SETSIGMASK, tid, sizeof(blockall), &blockall);
+ if (ret) {
+ pr_perror("Can't block all signals of %d\n", tid);
+ break;
+ }
+ ctl->threads[i].use_sig_blocked = true;
+ }
+
+ return ret;
+}
+
+static int unblock_signals(struct parasite_ctl *ctl)
+{
+ int ret = 0, i;
+
+ for (i = 0; i < ctl->nr_threads; i++) {
+ k_rtsigset_t *mask = &ctl->threads[i].sig_blocked;
+ pid_t tid = ctl->threads[i].tid;
+
+ ret = ptrace(PTRACE_SETSIGMASK, tid, sizeof(*mask), mask);
+ if (ret) {
+ pr_perror("Can't restore sigblockmask of %d\n", tid);
+ break;
+ }
+ }
+
+ return ret;
+}
+
static int parasite_fini_seized(struct parasite_ctl *ctl)
{
struct parasite_init_args *args;
@@ -787,7 +766,6 @@ int parasite_cure_seized(struct parasite_ctl *ctl)
int ret = 0;
if (ctl->parasite_ip) {
- ctl->signals_blocked = 0;
ret = parasite_fini_threads_seized(ctl);
parasite_fini_seized(ctl);
}
@@ -819,6 +797,9 @@ int parasite_cure_seized(struct parasite_ctl *ctl)
ret = -1;
}
+ if (unblock_signals(ctl))
+ ret = -1;
+
xfree(ctl);
return ret;
}
@@ -935,6 +916,9 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
if (!ctl)
return NULL;
+ if (block_signals(item, ctl))
+ goto err_restore;
+
/*
* Inject a parasite engine. Ie allocate memory inside alien
* space and copy engine code there. Then re-map the engine
@@ -961,8 +945,6 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
goto err_restore;
}
- ctl->signals_blocked = 1;
-
ret = parasite_set_logfd(ctl, pid);
if (ret) {
pr_err("%d: Can't set a logging descriptor\n", pid);
diff --git a/pie/parasite.c b/pie/parasite.c
index 18509ce..e39211a 100644
--- a/pie/parasite.c
+++ b/pie/parasite.c
@@ -25,8 +25,6 @@ static struct tid_state_s {
futex_t ack;
int ret;
- bool use_sig_blocked;
- k_rtsigset_t sig_blocked;
void *next;
unsigned char stack[PARASITE_STACK_SIZE] __aligned(8);
} *tid_state;
@@ -122,7 +120,6 @@ static int dump_itimers(struct parasite_dump_itimers_args *args)
static int dump_misc(struct parasite_dump_misc *args)
{
args->brk = sys_brk(0);
- args->blocked = thread_leader->sig_blocked;
args->pid = sys_getpid();
args->sid = sys_getsid();
@@ -211,14 +208,10 @@ static int dump_thread(struct parasite_dump_thread *args)
if (!s)
return -ENOENT;
- if (!s->use_sig_blocked)
- return -EINVAL;
-
ret = sys_prctl(PR_GET_TID_ADDRESS, (unsigned long) &args->tid_addr, 0, 0, 0);
if (ret)
return ret;
- args->blocked = s->sig_blocked;
args->tid = tid;
args->tls = arch_get_tls();
@@ -227,18 +220,9 @@ static int dump_thread(struct parasite_dump_thread *args)
static int init_thread(struct parasite_init_args *args)
{
- k_rtsigset_t to_block;
- int ret;
-
if (next_tid_state >= nr_tid_state)
return -ENOMEM;
- ksigfillset(&to_block);
- ret = sys_sigprocmask(SIG_SETMASK, &to_block,
- &tid_state[next_tid_state].sig_blocked,
- sizeof(k_rtsigset_t));
- if (ret >= 0)
- tid_state[next_tid_state].use_sig_blocked = true;
tid_state[next_tid_state].tid = sys_gettid();
tid_state[next_tid_state].real = args->real;
@@ -249,15 +233,11 @@ static int init_thread(struct parasite_init_args *args)
next_tid_state++;
- return ret;
+ return 0;
}
static int fini_thread(struct tid_state_s *s)
{
- if (s->use_sig_blocked)
- return sys_sigprocmask(SIG_SETMASK, &s->sig_blocked,
- NULL, sizeof(k_rtsigset_t));
-
return 0;
}
--
1.8.2
More information about the CRIU
mailing list