Commit 1da9760d authored by Andrew Vagin's avatar Andrew Vagin Committed by Pavel Emelyanov

ptrace: start tracing processes before restoring creds

In order to restore seccomp correctly, we need to do it before
restore_creds() in the restorer blob. But, if the seccomp policy forbids
e.g. prctl, if the task doesn't have SUSPEND_SECCOMP set it will die when
trying to restore creds. To solve this, we break attach_to_tasks up into
two parts: 1. we attach and set SUSPEND_SECCOMP (but let the tasks continue
normally), and then after the RESTORE_CREDS stage we 2. attach to the tasks
and stop them on the final sigreturn.
Signed-off-by: 's avatarAndrew Vagin <avagin@virtuozzo.com>
Signed-off-by: 's avatarTycho Andersen <tycho.andersen@canonical.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 4ed47b76
...@@ -1706,13 +1706,13 @@ static int restore_switch_stage(int next_stage) ...@@ -1706,13 +1706,13 @@ static int restore_switch_stage(int next_stage)
return restore_wait_inprogress_tasks(); return restore_wait_inprogress_tasks();
} }
static int attach_to_tasks(bool root_seized, enum trace_flags *flag) static int attach_to_tasks(bool root_seized)
{ {
struct pstree_item *item; struct pstree_item *item;
for_each_pstree_item(item) { for_each_pstree_item(item) {
pid_t pid = item->pid.real; pid_t pid = item->pid.real;
int status, i, ret; int status, i;
if (!task_alive(item)) if (!task_alive(item))
continue; continue;
...@@ -1724,21 +1724,16 @@ static int attach_to_tasks(bool root_seized, enum trace_flags *flag) ...@@ -1724,21 +1724,16 @@ static int attach_to_tasks(bool root_seized, enum trace_flags *flag)
pid = item->threads[i].real; pid = item->threads[i].real;
if (item != root_item || !root_seized || i != 0) { if (item != root_item || !root_seized || i != 0) {
if (ptrace(PTRACE_ATTACH, pid, 0, 0)) { if (ptrace(PTRACE_SEIZE, pid, 0, 0)) {
pr_perror("Can't attach to %d", pid); pr_perror("Can't attach to %d", pid);
return -1; return -1;
} }
} else {
/*
* Root item is SEIZE-d, so we only need
* to stop one (INTERRUPT) to make wait4
* and SYSCALL below work.
*/
if (ptrace(PTRACE_INTERRUPT, pid, 0, 0)) {
pr_perror("Can't interrupt the %d task", pid);
return -1;
}
} }
if (ptrace(PTRACE_INTERRUPT, pid, 0, 0)) {
pr_perror("Can't interrupt the %d task", pid);
return -1;
}
if (wait4(pid, &status, __WALL, NULL) != pid) { if (wait4(pid, &status, __WALL, NULL) != pid) {
pr_perror("waitpid(%d) failed", pid); pr_perror("waitpid(%d) failed", pid);
...@@ -1755,6 +1750,43 @@ static int attach_to_tasks(bool root_seized, enum trace_flags *flag) ...@@ -1755,6 +1750,43 @@ static int attach_to_tasks(bool root_seized, enum trace_flags *flag)
if (rsti(item)->has_seccomp && suspend_seccomp(pid) < 0) if (rsti(item)->has_seccomp && suspend_seccomp(pid) < 0)
pr_err("failed to suspend seccomp, restore will probably fail...\n"); pr_err("failed to suspend seccomp, restore will probably fail...\n");
if (ptrace(PTRACE_CONT, pid, NULL, NULL) ) {
pr_perror("Unable to resume %d", pid);
return -1;
}
}
}
return 0;
}
static int catch_tasks(bool root_seized, enum trace_flags *flag)
{
struct pstree_item *item;
for_each_pstree_item(item) {
pid_t pid = item->pid.real;
int status, i, ret;
if (!task_alive(item))
continue;
if (parse_threads(item->pid.real, &item->threads, &item->nr_threads))
return -1;
for (i = 0; i < item->nr_threads; i++) {
pid = item->threads[i].real;
if (ptrace(PTRACE_INTERRUPT, pid, 0, 0)) {
pr_perror("Can't interrupt the %d task", pid);
return -1;
}
if (wait4(pid, &status, __WALL, NULL) != pid) {
pr_perror("waitpid(%d) failed", pid);
return -1;
}
ret = ptrace_stop_pie(pid, rsti(item)->breakpoint, flag); ret = ptrace_stop_pie(pid, rsti(item)->breakpoint, flag);
if (ret < 0) if (ret < 0)
return -1; return -1;
...@@ -2037,13 +2069,14 @@ static int restore_root_task(struct pstree_item *init) ...@@ -2037,13 +2069,14 @@ static int restore_root_task(struct pstree_item *init)
* ------------------------------------------------------------- * -------------------------------------------------------------
* Below this line nothing should fail, because network is unlocked * Below this line nothing should fail, because network is unlocked
*/ */
attach_to_tasks(root_as_sibling);
ret = restore_switch_stage(CR_STATE_RESTORE_CREDS); ret = restore_switch_stage(CR_STATE_RESTORE_CREDS);
BUG_ON(ret); BUG_ON(ret);
timing_stop(TIME_RESTORE); timing_stop(TIME_RESTORE);
ret = attach_to_tasks(root_as_sibling, &flag); ret = catch_tasks(root_as_sibling, &flag);
pr_info("Restore finished successfully. Resuming tasks.\n"); pr_info("Restore finished successfully. Resuming tasks.\n");
futex_set_and_wake(&task_entries->start, CR_STATE_COMPLETE); futex_set_and_wake(&task_entries->start, CR_STATE_COMPLETE);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment