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 ...);*
- 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.

View File

@ -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;

View File

@ -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)
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;

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_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;

View File

@ -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);
}
}
}

View File

@ -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

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.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)

View File

@ -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);

View File

@ -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;

View File

@ -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 {