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:
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 ...);*
|
||||
- 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
|
||||
- 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.
|
||||
*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 {
|
||||
unsigned long long sigpnd;
|
||||
unsigned long long shdpnd;
|
||||
unsigned long long sigblk;
|
||||
char state;
|
||||
int vpid;
|
||||
int ppid;
|
||||
@ -30,7 +31,9 @@ extern int __must_check compel_wait_task(int pid, int ppid,
|
||||
struct seize_task_status *st, void *data);
|
||||
|
||||
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_sig(pid_t pid, int orig_state, int state, int stop_signo);
|
||||
|
||||
struct parasite_ctl;
|
||||
struct parasite_thread_ctl;
|
||||
|
@ -92,6 +92,12 @@ static int parse_pid_status(int pid, struct seize_task_status *ss, void *data)
|
||||
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(aux, "SigBlk:", 7)) {
|
||||
if (sscanf(aux + 7, "%llx", &ss->sigblk) != 1)
|
||||
goto err_parse;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
@ -186,6 +192,29 @@ static int skip_sigstop(int pid, int nr_signals)
|
||||
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
|
||||
* 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)
|
||||
{
|
||||
siginfo_t si;
|
||||
int status, nr_sigstop;
|
||||
int status, nr_stopsig;
|
||||
int ret = 0, ret2, wait_errno = 0;
|
||||
|
||||
/*
|
||||
@ -291,17 +320,32 @@ try_again:
|
||||
goto err;
|
||||
}
|
||||
|
||||
nr_sigstop = 0;
|
||||
if (ss->sigpnd & (1 << (SIGSTOP - 1)))
|
||||
nr_sigstop++;
|
||||
if (ss->shdpnd & (1 << (SIGSTOP - 1)))
|
||||
nr_sigstop++;
|
||||
if (si.si_signo == SIGSTOP)
|
||||
nr_sigstop++;
|
||||
nr_stopsig = 0;
|
||||
if (SIG_IN_MASK(SIGSTOP, ss->sigpnd))
|
||||
nr_stopsig++;
|
||||
if (SIG_IN_MASK(SIGSTOP, ss->shdpnd))
|
||||
nr_stopsig++;
|
||||
|
||||
if (nr_sigstop) {
|
||||
if (skip_sigstop(pid, nr_sigstop))
|
||||
goto err_stop;
|
||||
if (SIG_IN_MASK(SIGTSTP, ss->sigpnd) && !SIG_IN_MASK(SIGTSTP, ss->sigblk))
|
||||
nr_stopsig++;
|
||||
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;
|
||||
}
|
||||
@ -313,8 +357,6 @@ try_again:
|
||||
goto err;
|
||||
}
|
||||
|
||||
err_stop:
|
||||
kill(pid, SIGSTOP);
|
||||
err:
|
||||
if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
@ -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
|
||||
* detach, so stop it again.
|
||||
*/
|
||||
if (orig_st == COMPEL_TASK_STOPPED)
|
||||
kill(pid, SIGSTOP);
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
pr_err("Unknown final state %d\n", st);
|
||||
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_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);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
@ -1350,6 +1350,9 @@ static inline int fork_with_pid(struct pstree_item *item)
|
||||
item->pid->state = ca.core->tc->task_state;
|
||||
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)) {
|
||||
pr_err("Unknown task state %d\n", item->pid->state);
|
||||
return -1;
|
||||
@ -2104,8 +2107,14 @@ static void finalize_restore(void)
|
||||
|
||||
xfree(ctl);
|
||||
|
||||
if ((item->pid->state == TASK_STOPPED) || (opts.final_state == TASK_STOPPED))
|
||||
if (opts.final_state == TASK_STOPPED)
|
||||
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;
|
||||
|
||||
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
|
||||
|
@ -1027,12 +1027,13 @@ int parse_pid_status(pid_t pid, struct seize_task_status *ss, void *data)
|
||||
|
||||
cr->s.sigpnd = 0;
|
||||
cr->s.shdpnd = 0;
|
||||
cr->s.sigblk = 0;
|
||||
cr->s.seccomp_mode = SECCOMP_MODE_DISABLED;
|
||||
|
||||
if (bfdopenr(&f))
|
||||
return -1;
|
||||
|
||||
while (done < 13) {
|
||||
while (done < 14) {
|
||||
str = breadline(&f);
|
||||
if (str == NULL)
|
||||
break;
|
||||
@ -1143,13 +1144,23 @@ int parse_pid_status(pid_t pid, struct seize_task_status *ss, void *data)
|
||||
goto err_parse;
|
||||
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++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* seccomp and nspids are optional */
|
||||
expected_done = (parsed_seccomp ? 11 : 10);
|
||||
expected_done = (parsed_seccomp ? 12 : 11);
|
||||
if (kdat.has_nspid)
|
||||
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->real = -1;
|
||||
item->pid->state = TASK_UNDEF;
|
||||
item->pid->stop_signo = -1;
|
||||
item->born_sid = -1;
|
||||
item->pid->item = item;
|
||||
futex_init(&item->task_st);
|
||||
|
@ -615,6 +615,9 @@ static int collect_children(struct pstree_item *item)
|
||||
else
|
||||
processes_to_wait--;
|
||||
|
||||
if (ret == TASK_STOPPED)
|
||||
c->pid->stop_signo = compel_parse_stop_signo(pid);
|
||||
|
||||
c->pid->real = pid;
|
||||
c->parent = item;
|
||||
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.
|
||||
*/
|
||||
|
||||
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)
|
||||
return;
|
||||
@ -950,6 +953,9 @@ int collect_pstree(void)
|
||||
else
|
||||
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);
|
||||
root_item->pid->state = ret;
|
||||
|
||||
|
@ -60,6 +60,8 @@ message task_core_entry {
|
||||
// Reserved for container relative start time
|
||||
//optional uint64 start_time = 19;
|
||||
optional uint64 blk_sigset_extended = 20[(criu).hex = true];
|
||||
|
||||
optional uint32 stop_signo = 21;
|
||||
}
|
||||
|
||||
message task_kobj_ids_entry {
|
||||
|
Loading…
x
Reference in New Issue
Block a user