2
0
mirror of https://github.com/checkpoint-restore/criu synced 2025-08-22 09:58:09 +00:00

infect: add SIGTSTP support

Add SIGTSTP signal dump and restore. Add a corresponding field
in the image, save it only if a task is in the stopped state.

Restore task state by sending desired stop signal if it is present
in the image. Fallback to SIGSTOP if it's absent.

Signed-off-by: Yuriy Vasiliev <yuriy.vasiliev@openvz.org>
This commit is contained in:
Yuriy Vasiliev 2022-01-20 17:13:59 +01:00 committed by Andrei Vagin
parent 49caf85b20
commit c7858ba42b
10 changed files with 121 additions and 20 deletions

View File

@ -97,7 +97,10 @@ Following steps are performed to infect the victim process:
- execute system call: *int compel_syscall(ctl, int syscall_nr, long *ret, int arg ...);* - execute system call: *int compel_syscall(ctl, int syscall_nr, long *ret, int arg ...);*
- infect victim: *int compel_infect(ctl, nr_thread, size_of_args_area);* - infect victim: *int compel_infect(ctl, nr_thread, size_of_args_area);*
- cure the victim: *int compel_cure(ctl);* //ctl pointer is freed by this call - cure the victim: *int compel_cure(ctl);* //ctl pointer is freed by this call
- Resume victim: *int compel_resume_task(pid, orig_state, state);* - Resume victim: *int compel_resume_task(pid, orig_state, state)* or
*int compel_resume_task_sig(pid, orig_state, state, stop_signo).*
//compel_resume_task_sig() could be used in case when victim is in stopped state.
stop_signo could be read by calling compel_parse_stop_signo().
*ctl* must be configured with blob information by calling *PREFIX_setup_c_header()*, with ctl as its argument. *ctl* must be configured with blob information by calling *PREFIX_setup_c_header()*, with ctl as its argument.
*PREFIX* is the argument given to *-p* when calling hgen, else it is deduced from file name. *PREFIX* is the argument given to *-p* when calling hgen, else it is deduced from file name.

View File

@ -18,6 +18,7 @@ extern int __must_check compel_interrupt_task(int pid);
struct seize_task_status { struct seize_task_status {
unsigned long long sigpnd; unsigned long long sigpnd;
unsigned long long shdpnd; unsigned long long shdpnd;
unsigned long long sigblk;
char state; char state;
int vpid; int vpid;
int ppid; int ppid;
@ -30,7 +31,9 @@ extern int __must_check compel_wait_task(int pid, int ppid,
struct seize_task_status *st, void *data); struct seize_task_status *st, void *data);
extern int __must_check compel_stop_task(int pid); extern int __must_check compel_stop_task(int pid);
extern int __must_check compel_parse_stop_signo(int pid);
extern int compel_resume_task(pid_t pid, int orig_state, int state); extern int compel_resume_task(pid_t pid, int orig_state, int state);
extern int compel_resume_task_sig(pid_t pid, int orig_state, int state, int stop_signo);
struct parasite_ctl; struct parasite_ctl;
struct parasite_thread_ctl; struct parasite_thread_ctl;

View File

@ -92,6 +92,12 @@ static int parse_pid_status(int pid, struct seize_task_status *ss, void *data)
continue; continue;
} }
if (!strncmp(aux, "SigBlk:", 7)) {
if (sscanf(aux + 7, "%llx", &ss->sigblk) != 1)
goto err_parse;
continue;
}
} }
fclose(f); fclose(f);
@ -186,6 +192,29 @@ static int skip_sigstop(int pid, int nr_signals)
return 0; return 0;
} }
#define SIG_MASK(sig) (1ULL << ((sig)-1))
#define SIG_IN_MASK(sig, mask) ((sig) > 0 && (sig) <= SIGMAX && (SIG_MASK(sig) & (mask)))
#define SUPPORTED_STOP_MASK ((1ULL << (SIGSTOP - 1)) | (1ULL << (SIGTSTP - 1)))
static inline int sig_stop(int sig)
{
return SIG_IN_MASK(sig, SUPPORTED_STOP_MASK);
}
int compel_parse_stop_signo(int pid)
{
siginfo_t si;
if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si) < 0) {
pr_perror("SEIZE %d: can't parse stopped siginfo", pid);
return -1;
}
return si.si_signo;
}
/* /*
* 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
@ -198,7 +227,7 @@ int compel_wait_task(int pid, int ppid, int (*get_status)(int pid, struct seize_
void *data) void *data)
{ {
siginfo_t si; siginfo_t si;
int status, nr_sigstop; int status, nr_stopsig;
int ret = 0, ret2, wait_errno = 0; int ret = 0, ret2, wait_errno = 0;
/* /*
@ -291,17 +320,32 @@ try_again:
goto err; goto err;
} }
nr_sigstop = 0; nr_stopsig = 0;
if (ss->sigpnd & (1 << (SIGSTOP - 1))) if (SIG_IN_MASK(SIGSTOP, ss->sigpnd))
nr_sigstop++; nr_stopsig++;
if (ss->shdpnd & (1 << (SIGSTOP - 1))) if (SIG_IN_MASK(SIGSTOP, ss->shdpnd))
nr_sigstop++; nr_stopsig++;
if (si.si_signo == SIGSTOP)
nr_sigstop++;
if (nr_sigstop) { if (SIG_IN_MASK(SIGTSTP, ss->sigpnd) && !SIG_IN_MASK(SIGTSTP, ss->sigblk))
if (skip_sigstop(pid, nr_sigstop)) nr_stopsig++;
goto err_stop; if (SIG_IN_MASK(SIGTSTP, ss->shdpnd) && !SIG_IN_MASK(SIGTSTP, ss->sigblk))
nr_stopsig++;
if (sig_stop(si.si_signo))
nr_stopsig++;
if (nr_stopsig) {
if (skip_sigstop(pid, nr_stopsig)) {
/*
* Make sure that the task is stopped by a supported stop signal and
* send it again to restore task state before criu intervention.
*/
if (sig_stop(si.si_signo))
kill(pid, si.si_signo);
else
kill(pid, SIGSTOP);
goto err;
}
return COMPEL_TASK_STOPPED; return COMPEL_TASK_STOPPED;
} }
@ -313,8 +357,6 @@ try_again:
goto err; goto err;
} }
err_stop:
kill(pid, SIGSTOP);
err: err:
if (ptrace(PTRACE_DETACH, pid, NULL, NULL)) if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
pr_perror("Unable to detach from %d", pid); pr_perror("Unable to detach from %d", pid);
@ -322,6 +364,11 @@ err:
} }
int compel_resume_task(pid_t pid, int orig_st, int st) int compel_resume_task(pid_t pid, int orig_st, int st)
{
return compel_resume_task_sig(pid, orig_st, st, SIGSTOP);
}
int compel_resume_task_sig(pid_t pid, int orig_st, int st, int stop_signo)
{ {
int ret = 0; int ret = 0;
@ -345,8 +392,18 @@ int compel_resume_task(pid_t pid, int orig_st, int st)
* task with STOP in queue that would get lost after * task with STOP in queue that would get lost after
* detach, so stop it again. * detach, so stop it again.
*/ */
if (orig_st == COMPEL_TASK_STOPPED) if (orig_st == COMPEL_TASK_STOPPED) {
/*
* Check that stop_signo contain supported stop signal.
* If it isn't, then send SIGSTOP. It makes sense in the case
* when we get COMPEL_TASK_STOPPED from old image,
* where stop_signo was not yet supported.
*/
if (sig_stop(stop_signo))
kill(pid, stop_signo);
else
kill(pid, SIGSTOP); kill(pid, SIGSTOP);
}
} else { } else {
pr_err("Unknown final state %d\n", st); pr_err("Unknown final state %d\n", st);
ret = -1; ret = -1;

View File

@ -781,6 +781,11 @@ static int dump_task_core_all(struct parasite_ctl *ctl, struct pstree_item *item
core->thread_core->creds->lsm_profile = dmpi(item)->thread_lsms[0]->profile; core->thread_core->creds->lsm_profile = dmpi(item)->thread_lsms[0]->profile;
core->thread_core->creds->lsm_sockcreate = dmpi(item)->thread_lsms[0]->sockcreate; core->thread_core->creds->lsm_sockcreate = dmpi(item)->thread_lsms[0]->sockcreate;
if (core->tc->task_state == TASK_STOPPED) {
core->tc->has_stop_signo = true;
core->tc->stop_signo = item->pid->stop_signo;
}
ret = parasite_dump_thread_leader_seized(ctl, pid, core); ret = parasite_dump_thread_leader_seized(ctl, pid, core);
if (ret) if (ret)
goto err; goto err;

View File

@ -1350,6 +1350,9 @@ static inline int fork_with_pid(struct pstree_item *item)
item->pid->state = ca.core->tc->task_state; item->pid->state = ca.core->tc->task_state;
rsti(item)->cg_set = ca.core->tc->cg_set; rsti(item)->cg_set = ca.core->tc->cg_set;
if (ca.core->tc->has_stop_signo)
item->pid->stop_signo = ca.core->tc->stop_signo;
if (item->pid->state != TASK_DEAD && !task_alive(item)) { if (item->pid->state != TASK_DEAD && !task_alive(item)) {
pr_err("Unknown task state %d\n", item->pid->state); pr_err("Unknown task state %d\n", item->pid->state);
return -1; return -1;
@ -2104,8 +2107,14 @@ static void finalize_restore(void)
xfree(ctl); xfree(ctl);
if ((item->pid->state == TASK_STOPPED) || (opts.final_state == TASK_STOPPED)) if (opts.final_state == TASK_STOPPED)
kill(item->pid->real, SIGSTOP); kill(item->pid->real, SIGSTOP);
else if (item->pid->state == TASK_STOPPED) {
if (item->pid->stop_signo > 0)
kill(item->pid->real, item->pid->stop_signo);
else
kill(item->pid->real, SIGSTOP);
}
} }
} }

View File

@ -31,6 +31,10 @@ struct pid {
pid_t real; pid_t real;
int state; /* TASK_XXX constants */ int state; /* TASK_XXX constants */
/* If an item is in stopped state it has a signal number
* that caused task to stop.
*/
int stop_signo;
/* /*
* The @virt pid is one which used in the image itself and keeps * The @virt pid is one which used in the image itself and keeps

View File

@ -1027,12 +1027,13 @@ int parse_pid_status(pid_t pid, struct seize_task_status *ss, void *data)
cr->s.sigpnd = 0; cr->s.sigpnd = 0;
cr->s.shdpnd = 0; cr->s.shdpnd = 0;
cr->s.sigblk = 0;
cr->s.seccomp_mode = SECCOMP_MODE_DISABLED; cr->s.seccomp_mode = SECCOMP_MODE_DISABLED;
if (bfdopenr(&f)) if (bfdopenr(&f))
return -1; return -1;
while (done < 13) { while (done < 14) {
str = breadline(&f); str = breadline(&f);
if (str == NULL) if (str == NULL)
break; break;
@ -1143,13 +1144,23 @@ int parse_pid_status(pid_t pid, struct seize_task_status *ss, void *data)
goto err_parse; goto err_parse;
cr->s.sigpnd |= sigpnd; cr->s.sigpnd |= sigpnd;
done++;
continue;
}
if (!strncmp(str, "SigBlk:", 7)) {
unsigned long long sigblk = 0;
if (sscanf(str + 7, "%llx", &sigblk) != 1)
goto err_parse;
cr->s.sigblk |= sigblk;
done++; done++;
continue; continue;
} }
} }
/* seccomp and nspids are optional */ /* seccomp and nspids are optional */
expected_done = (parsed_seccomp ? 11 : 10); expected_done = (parsed_seccomp ? 12 : 11);
if (kdat.has_nspid) if (kdat.has_nspid)
expected_done++; expected_done++;
if (done == expected_done) if (done == expected_done)

View File

@ -222,6 +222,7 @@ struct pstree_item *__alloc_pstree_item(bool rst)
item->pid->ns[0].virt = -1; item->pid->ns[0].virt = -1;
item->pid->real = -1; item->pid->real = -1;
item->pid->state = TASK_UNDEF; item->pid->state = TASK_UNDEF;
item->pid->stop_signo = -1;
item->born_sid = -1; item->born_sid = -1;
item->pid->item = item; item->pid->item = item;
futex_init(&item->task_st); futex_init(&item->task_st);

View File

@ -615,6 +615,9 @@ static int collect_children(struct pstree_item *item)
else else
processes_to_wait--; processes_to_wait--;
if (ret == TASK_STOPPED)
c->pid->stop_signo = compel_parse_stop_signo(pid);
c->pid->real = pid; c->pid->real = pid;
c->parent = item; c->parent = item;
c->pid->state = ret; c->pid->state = ret;
@ -646,7 +649,7 @@ static void unseize_task_and_threads(const struct pstree_item *item, int st)
* the item->state is the state task was in when we seized one. * the item->state is the state task was in when we seized one.
*/ */
compel_resume_task(item->pid->real, item->pid->state, st); compel_resume_task_sig(item->pid->real, item->pid->state, st, item->pid->stop_signo);
if (st == TASK_DEAD) if (st == TASK_DEAD)
return; return;
@ -950,6 +953,9 @@ int collect_pstree(void)
else else
processes_to_wait--; processes_to_wait--;
if (ret == TASK_STOPPED)
root_item->pid->stop_signo = compel_parse_stop_signo(pid);
pr_info("Seized task %d, state %d\n", pid, ret); pr_info("Seized task %d, state %d\n", pid, ret);
root_item->pid->state = ret; root_item->pid->state = ret;

View File

@ -60,6 +60,8 @@ message task_core_entry {
// Reserved for container relative start time // Reserved for container relative start time
//optional uint64 start_time = 19; //optional uint64 start_time = 19;
optional uint64 blk_sigset_extended = 20[(criu).hex = true]; optional uint64 blk_sigset_extended = 20[(criu).hex = true];
optional uint32 stop_signo = 21;
} }
message task_kobj_ids_entry { message task_kobj_ids_entry {