mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-22 18:07:57 +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:
parent
49caf85b20
commit
c7858ba42b
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user