From b9b0730cb12e1450b5c2e1e481ba7b037dc72f48 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Thu, 6 Aug 2015 12:37:27 +0300 Subject: [PATCH] ptrace: split task_seize into seize_catch_task and seize_wait_task It's preparation to use a freezer cgroup for freezing tasks. Signed-off-by: Andrey Vagin Signed-off-by: Pavel Emelyanov --- cr-exec.c | 5 +++- include/ptrace.h | 3 ++- ptrace.c | 69 ++++++++++++++++++++++++++++-------------------- seize.c | 16 ++++++++--- 4 files changed, 59 insertions(+), 34 deletions(-) diff --git a/cr-exec.c b/cr-exec.c index f3d55f6d9..8beb80f88 100644 --- a/cr-exec.c +++ b/cr-exec.c @@ -130,7 +130,10 @@ int cr_exec(int pid, char **opt) goto out; } - prev_state = ret = seize_task(pid, -1, &creds); + if (seize_catch_task(pid)) + goto out; + + prev_state = ret = seize_wait_task(pid, -1, &creds); if (ret < 0) { pr_err("Can't seize task %d\n", pid); goto out; diff --git a/include/ptrace.h b/include/ptrace.h index 44b66c9af..079ad632b 100644 --- a/include/ptrace.h +++ b/include/ptrace.h @@ -67,7 +67,8 @@ struct ptrace_peeksiginfo_args { #define SI_EVENT(_si_code) (((_si_code) & 0xFFFF) >> 8) -extern int seize_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds); +extern int seize_catch_task(pid_t pid); +extern int seize_wait_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds); extern int suspend_seccomp(pid_t pid); extern int unseize_task(pid_t pid, int orig_state, int state); extern int ptrace_peek_area(pid_t pid, void *dst, void *addr, long bytes); diff --git a/ptrace.c b/ptrace.c index 905eaece6..a302e56c5 100644 --- a/ptrace.c +++ b/ptrace.c @@ -52,6 +52,39 @@ int suspend_seccomp(pid_t pid) return 0; } +int seize_catch_task(pid) +{ + int ret; + + ret = ptrace(PTRACE_SEIZE, pid, NULL, 0); + if (ret) { + /* + * ptrace API doesn't allow to distinguish + * attaching to zombie from other errors. + * All errors will be handled in seize_wait_task(). + */ + pr_warn("Unable to interrupt task: %d (%s)\n", pid, strerror(errno)); + return ret; + } + + /* + * If we SEIZE-d the task stop it before going + * and reading its stat from proc. Otherwise task + * may die _while_ we're doing it and we'll have + * inconsistent seize/state pair. + * + * If task dies after we seize it but before we + * do this interrupt, we'll notice it via proc. + */ + ret = ptrace(PTRACE_INTERRUPT, pid, NULL, NULL); + if (ret < 0) { + pr_warn("SEIZE %d: can't interrupt task: %s", pid, strerror(errno)); + ptrace(PTRACE_DETACH, pid, NULL, NULL); + } + + return ret; +} + /* * This routine seizes task putting it into a special * state where we can manipulate the task via ptrace @@ -59,12 +92,11 @@ int suspend_seccomp(pid_t pid) * of it so the task would not know if it was saddled * up with someone else. */ - -int seize_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds) +int seize_wait_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds) { siginfo_t si; int status; - int ret, ret2, ptrace_errno, wait_errno = 0; + int ret = 0, ret2, wait_errno = 0; struct proc_status_creds cr; /* @@ -72,26 +104,6 @@ int seize_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds) */ memzero(&cr, sizeof(struct proc_status_creds)); - ret = ptrace(PTRACE_SEIZE, pid, NULL, 0); - ptrace_errno = errno; - if (ret == 0) { - /* - * If we SEIZE-d the task stop it before going - * and reading its stat from proc. Otherwise task - * may die _while_ we're doing it and we'll have - * inconsistent seize/state pair. - * - * If task dies after we seize it but before we - * do this interrupt, we'll notice it via proc. - */ - ret = ptrace(PTRACE_INTERRUPT, pid, NULL, NULL); - if (ret < 0) { - pr_perror("SEIZE %d: can't interrupt task", pid); - ptrace(PTRACE_DETACH, pid, NULL, NULL); - goto err; - } - } - /* * It's ugly, but the ptrace API doesn't allow to distinguish * attaching to zombie from other errors. Thus we have to parse @@ -100,10 +112,9 @@ int seize_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds) */ try_again: - if (!ret) { - ret = wait4(pid, &status, __WALL, NULL); - wait_errno = errno; - } + + ret = wait4(pid, &status, __WALL, NULL); + wait_errno = errno; ret2 = parse_pid_status(pid, &cr); if (ret2) @@ -119,8 +130,8 @@ try_again: if (pid == getpid()) pr_err("The criu itself is within dumped tree.\n"); else - pr_err("Unseizable non-zombie %d found, state %c, err %d/%d/%d\n", - pid, cr.state, ret, ptrace_errno, wait_errno); + pr_err("Unseizable non-zombie %d found, state %c, err %d/%d\n", + pid, cr.state, ret, wait_errno); return -1; } diff --git a/seize.c b/seize.c index e9be332aa..86df3f0fc 100644 --- a/seize.c +++ b/seize.c @@ -58,7 +58,10 @@ static int collect_children(struct pstree_item *item) goto free; } - ret = seize_task(pid, item->pid.real, &dmpi(c)->pi_creds); + /* fails when meets a zombie */ + seize_catch_task(pid); + + ret = seize_wait_task(pid, item->pid.real, &dmpi(c)->pi_creds); if (ret < 0) { /* * Here is a race window between parse_children() and seize(), @@ -207,7 +210,10 @@ static int collect_threads(struct pstree_item *item) pr_info("\tSeizing %d's %d thread\n", item->pid.real, pid); - ret = seize_task(pid, item_ppid(item), &dmpi(item)->pi_creds); + if (seize_catch_task(pid)) + continue; + + ret = seize_wait_task(pid, item_ppid(item), &dmpi(item)->pi_creds); if (ret < 0) { /* * Here is a race window between parse_threads() and seize(), @@ -316,7 +322,11 @@ int collect_pstree(pid_t pid) return -1; root_item->pid.real = pid; - ret = seize_task(pid, -1, &dmpi(root_item)->pi_creds); + + if (seize_catch_task(pid)) + goto err; + + ret = seize_wait_task(pid, -1, &dmpi(root_item)->pi_creds); if (ret < 0) goto err; pr_info("Seized task %d, state %d\n", pid, ret);