mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-30 05:48:05 +00:00
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: Andrew Vagin <avagin@virtuozzo.com> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
This commit is contained in:
parent
fde1116fee
commit
fa79ffd6ed
84
ptrace.c
84
ptrace.c
@ -102,6 +102,45 @@ int seize_catch_task(pid_t pid)
|
||||
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
|
||||
* state where we can manipulate the task via ptrace
|
||||
@ -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)
|
||||
{
|
||||
siginfo_t si;
|
||||
int status;
|
||||
int status, nr_sigstop;
|
||||
int ret = 0, ret2, wait_errno = 0;
|
||||
struct proc_status_creds cr;
|
||||
|
||||
@ -204,42 +243,17 @@ try_again:
|
||||
if (cr.seccomp_mode != SECCOMP_MODE_DISABLED && suspend_seccomp(pid) < 0)
|
||||
goto err;
|
||||
|
||||
if ((cr.sigpnd | cr.shdpnd) & (1 << (SIGSTOP - 1)) || si.si_signo == SIGSTOP) {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
ret = ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
if (ret) {
|
||||
pr_perror("Unable to start process");
|
||||
goto err_stop;
|
||||
}
|
||||
nr_sigstop = 0;
|
||||
if (cr.sigpnd & (1 << (SIGSTOP - 1)))
|
||||
nr_sigstop++;
|
||||
if (cr.shdpnd & (1 << (SIGSTOP - 1)))
|
||||
nr_sigstop++;
|
||||
if (si.si_signo == SIGSTOP)
|
||||
nr_sigstop++;
|
||||
|
||||
ret = wait4(pid, &status, __WALL, NULL);
|
||||
if (ret < 0) {
|
||||
pr_perror("SEIZE %d: can't wait task", pid);
|
||||
if (nr_sigstop) {
|
||||
if (skip_sigstop(pid, nr_sigstop))
|
||||
goto err_stop;
|
||||
}
|
||||
|
||||
if (ret != pid) {
|
||||
pr_err("SEIZE %d: wrong task attached (%d)\n", pid, ret);
|
||||
goto err_stop;
|
||||
}
|
||||
|
||||
if (!WIFSTOPPED(status)) {
|
||||
pr_err("SEIZE %d: task not stopped after seize\n", pid);
|
||||
goto err_stop;
|
||||
}
|
||||
|
||||
return TASK_STOPPED;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user