[CRIU] [PATCH] dump: block signals with help of ptrace

Andrey Vagin avagin at openvz.org
Fri Apr 19 08:58:48 EDT 2013


Currently crtools blocks signals from a parasite code, but for executing
the parasite code a process has to handle all pending non-blocked signals.

It is not a problem until crtools decide to support stopped tasks.

Signed-off-by: Andrey Vagin <avagin at openvz.org>
---
 cr-dump.c                  |  13 +++--
 include/parasite-syscall.h |  11 +++-
 include/parasite.h         |   1 -
 include/ptrace.h           |   3 +
 parasite-syscall.c         | 138 ++++++++++++++++++++++-----------------------
 pie/parasite.c             |  22 +-------
 6 files changed, 88 insertions(+), 100 deletions(-)

diff --git a/cr-dump.c b/cr-dump.c
index 5743551..9e283ff 100644
--- a/cr-dump.c
+++ b/cr-dump.c
@@ -668,6 +668,7 @@ static int dump_task_core_all(struct parasite_ctl *ctl,
 		const struct proc_pid_stat *stat,
 		const struct parasite_dump_misc *misc,
 		struct vm_area_list *vma_area_list,
+		k_rtsigset_t *sig_blocked,
 		const struct cr_fdset *cr_fdset)
 {
 	int fd_core = fdset_fd(cr_fdset, CR_FD_CORE);
@@ -704,7 +705,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;
@@ -1121,7 +1122,8 @@ static int collect_file_locks(const struct cr_options *opts)
 
 }
 
-static int dump_task_thread(struct parasite_ctl *parasite_ctl, struct pid *tid)
+static int dump_task_thread(struct parasite_ctl *parasite_ctl,
+			    struct pid *tid, k_rtsigset_t *sig_blocked)
 {
 	CoreEntry *core;
 	int ret = -1, fd_core;
@@ -1150,6 +1152,7 @@ static int dump_task_thread(struct parasite_ctl *parasite_ctl, struct pid *tid)
 	}
 
 	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)
@@ -1263,7 +1266,8 @@ static int dump_task_threads(struct parasite_ctl *parasite_ctl,
 		if (item->pid.real == item->threads[i].real)
 			item->threads[i].virt = item->pid.virt;
 		else {
-			if (dump_task_thread(parasite_ctl, &item->threads[i]))
+			if (dump_task_thread(parasite_ctl, &item->threads[i],
+					     &parasite_ctl->threads[i].sig_blocked))
 				return -1;
 		}
 
@@ -1481,7 +1485,8 @@ static int dump_one_task(struct pstree_item *item)
 		goto err_cure;
 	}
 
-	ret = dump_task_core_all(parasite_ctl, &pps_buf, &misc, &vmas, cr_fdset);
+	ret = dump_task_core_all(parasite_ctl, &pps_buf, &misc, &vmas,
+				 &parasite_ctl->thread_leader->sig_blocked, cr_fdset);
 	if (ret) {
 		pr_err("Dump core (pid: %d) failed with %d\n", pid, ret);
 		goto err_cure;
diff --git a/include/parasite-syscall.h b/include/parasite-syscall.h
index 27835f5..bb6f2e4 100644
--- a/include/parasite-syscall.h
+++ b/include/parasite-syscall.h
@@ -5,6 +5,12 @@
 
 #include "pstree.h"
 
+struct parasite_thread_ctl
+{
+	k_rtsigset_t		sig_blocked;
+	bool			use_sig_blocked;
+};
+
 /* parasite control block */
 struct parasite_ctl {
 	struct pid		pid;
@@ -17,12 +23,13 @@ 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;
 	int			tsock;					/* transport socket for transfering fds */
+
+	struct parasite_thread_ctl *threads;
+	struct parasite_thread_ctl *thread_leader;
 };
 
 struct cr_fdset;
diff --git a/include/parasite.h b/include/parasite.h
index 5e4143c..79e44e1 100644
--- a/include/parasite.h
+++ b/include/parasite.h
@@ -90,7 +90,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 8457c62..285ddac 100644
--- a/parasite-syscall.c
+++ b/parasite-syscall.c
@@ -67,7 +67,6 @@ int __parasite_execute(struct parasite_ctl *ctl, pid_t pid, user_regs_struct_t *
 	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->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->regs_orig = r;
-		}
-
-		goto again;
+		pr_err("Unexpected %d task interruption, aborting\n", pid);
+		goto err;
 	}
 
 	/*
@@ -364,7 +299,6 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, struct pid *tid,
 
 	ret = parasite_execute_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);
@@ -588,6 +522,58 @@ int parasite_fini_threads_seized(struct parasite_ctl *ctl, struct pstree_item *i
 	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;
+
+		if (item->pid.real == item->threads[i].real)
+			ctl->thread_leader = ctl->threads + i;
+
+		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 pstree_item *item, struct parasite_ctl *ctl)
+{
+	int ret = 0, i;
+
+	if (ctl->threads == NULL)
+		return 0;
+
+	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_SETSIGMASK, tid, sizeof(*mask), mask);
+		if (ret) {
+			pr_perror("Can't restore sigblockmask of %d\n", tid);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+
 int parasite_cure_seized(struct parasite_ctl *ctl, struct pstree_item *item)
 {
 	int ret = 0;
@@ -595,7 +581,6 @@ int parasite_cure_seized(struct parasite_ctl *ctl, struct pstree_item *item)
 	ctl->tsock = -1;
 
 	if (ctl->parasite_ip) {
-		ctl->signals_blocked = 0;
 		parasite_fini_threads_seized(ctl, item);
 		parasite_execute(PARASITE_CMD_FINI, ctl);
 	}
@@ -625,7 +610,11 @@ int parasite_cure_seized(struct parasite_ctl *ctl, struct pstree_item *item)
 		ret = -1;
 	}
 
-	free(ctl);
+	if (unblock_signals(item, ctl))
+		ret = -1;
+
+	xfree(ctl->threads);
+	xfree(ctl);
 	return ret;
 }
 
@@ -735,6 +724,13 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
 	if (!ctl)
 		return NULL;
 
+	ctl->threads = xzalloc(sizeof(ctl->threads[0]) * item->nr_threads);
+	if (ctl->threads == NULL)
+		goto err_restore;
+
+	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
@@ -761,8 +757,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 29137f0..3fe9135 100644
--- a/pie/parasite.c
+++ b/pie/parasite.c
@@ -18,8 +18,6 @@ static int tsock = -1;
 
 static struct tid_state_s {
 	pid_t		tid;
-	bool		use_sig_blocked;
-	k_rtsigset_t	sig_blocked;
 } *tid_state;
 
 static unsigned int nr_tid_state;
@@ -111,7 +109,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();
@@ -195,14 +192,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();
 
@@ -211,23 +204,14 @@ static int dump_thread(struct parasite_dump_thread *args)
 
 static int init_thread(void)
 {
-	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();
 
 	next_tid_state++;
 
-	return ret;
+	return 0;
 }
 
 static int fini_thread(void)
@@ -238,10 +222,6 @@ static int fini_thread(void)
 	if (!s)
 		return -ENOENT;
 
-	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