Commit fa79ffd6 authored by Andrew Vagin's avatar Andrew Vagin Committed by Pavel Emelyanov

ptrace: skip all sigstop (v2)

A task can be stopped and has a queued SIGSTOP, in this case we need
to resume the task twice to skip "both" signals.

v2: detect SIGSTOP in shared and per-process queues
Signed-off-by: 's avatarAndrew Vagin <avagin@virtuozzo.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent fde1116f
...@@ -102,6 +102,45 @@ int seize_catch_task(pid_t pid) ...@@ -102,6 +102,45 @@ int seize_catch_task(pid_t pid)
return ret; return ret;
} }
static int skip_sigstop(int pid, int nr_signals)
{
int i, status, ret;
/*
* 1) SIGSTOP is queued, but isn't handled yet:
* SGISTOP can't be blocked, so we need to wait when the kernel
* handles this signal.
*
* Otherwise the process will be stopped immediatly after
* starting it.
*
* 2) A seized task was stopped:
* PTRACE_SEIZE doesn't affect signal or group stop state.
* Currently ptrace reported that task is in stopped state.
* We need to start task again, and it will be trapped
* immediately, because we sent PTRACE_INTERRUPT to it.
*/
for (i = 0; i < nr_signals; i++) {
ret = ptrace(PTRACE_CONT, pid, 0, 0);
if (ret) {
pr_perror("Unable to start process");
return -1;
}
ret = wait4(pid, &status, __WALL, NULL);
if (ret < 0) {
pr_perror("SEIZE %d: can't wait task", pid);
return -1;
}
if (!WIFSTOPPED(status)) {
pr_err("SEIZE %d: task not stopped after seize\n", pid);
return -1;
}
}
return 0;
}
/* /*
* This routine seizes task putting it into a special * This routine seizes task putting it into a special
* state where we can manipulate the task via ptrace * state where we can manipulate the task via ptrace
...@@ -112,7 +151,7 @@ int seize_catch_task(pid_t pid) ...@@ -112,7 +151,7 @@ int seize_catch_task(pid_t pid)
int seize_wait_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; siginfo_t si;
int status; int status, nr_sigstop;
int ret = 0, ret2, wait_errno = 0; int ret = 0, ret2, wait_errno = 0;
struct proc_status_creds cr; struct proc_status_creds cr;
...@@ -204,42 +243,17 @@ try_again: ...@@ -204,42 +243,17 @@ try_again:
if (cr.seccomp_mode != SECCOMP_MODE_DISABLED && suspend_seccomp(pid) < 0) if (cr.seccomp_mode != SECCOMP_MODE_DISABLED && suspend_seccomp(pid) < 0)
goto err; goto err;
if ((cr.sigpnd | cr.shdpnd) & (1 << (SIGSTOP - 1)) || si.si_signo == SIGSTOP) { nr_sigstop = 0;
/* if (cr.sigpnd & (1 << (SIGSTOP - 1)))
* 1) SIGSTOP is queued, but isn't handled yet: nr_sigstop++;
* SGISTOP can't be blocked, so we need to wait when the kernel if (cr.shdpnd & (1 << (SIGSTOP - 1)))
* handles this signal. nr_sigstop++;
* if (si.si_signo == SIGSTOP)
* Otherwise the process will be stopped immediatly after nr_sigstop++;
* starting it.
*
* 2) A seized task was stopped:
* PTRACE_SEIZE doesn't affect signal or group stop state.
* Currently ptrace reported that task is in stopped state.
* We need to start task again, and it will be trapped
* immediately, because we sent PTRACE_INTERRUPT to it.
*/
ret = ptrace(PTRACE_CONT, pid, 0, 0);
if (ret) {
pr_perror("Unable to start process");
goto err_stop;
}
ret = wait4(pid, &status, __WALL, NULL);
if (ret < 0) {
pr_perror("SEIZE %d: can't wait task", pid);
goto err_stop;
}
if (ret != pid) {
pr_err("SEIZE %d: wrong task attached (%d)\n", pid, ret);
goto err_stop;
}
if (!WIFSTOPPED(status)) { if (nr_sigstop) {
pr_err("SEIZE %d: task not stopped after seize\n", pid); if (skip_sigstop(pid, nr_sigstop))
goto err_stop; goto err_stop;
}
return TASK_STOPPED; return TASK_STOPPED;
} }
......
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