Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/criu-dev' into unprivileged
Browse files Browse the repository at this point in the history
  • Loading branch information
ymanton committed Jul 25, 2022
2 parents ef9d3a0 + ebe9db9 commit a38fc9a
Show file tree
Hide file tree
Showing 32 changed files with 577 additions and 54 deletions.
5 changes: 4 additions & 1 deletion Documentation/compel.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions Documentation/criu.txt
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,9 @@ The 'mode' may be one of the following:
build-ID cannot be obtained, 'chksm-first' method will be
used. This is the default if mode is unspecified.

*--skip-file-rwx-check*::
Skip checking file permissions (r/w/x for u/g/o) on restore.

*check*
~~~~~~~
Checks whether the kernel supports the features needed by *criu* to
Expand Down
3 changes: 2 additions & 1 deletion Makefile.config
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ export DEFINES += $(FEATURE_DEFINES)
export CFLAGS += $(FEATURE_DEFINES)

FEATURES_LIST := TCP_REPAIR STRLCPY STRLCAT PTRACE_PEEKSIGINFO \
SETPROCTITLE_INIT MEMFD TCP_REPAIR_WINDOW FSCONFIG MEMFD_CREATE OPENAT2
SETPROCTITLE_INIT MEMFD TCP_REPAIR_WINDOW FSCONFIG MEMFD_CREATE \
OPENAT2 NO_LIBC_RSEQ_DEFS

# $1 - config name
define gen-feature-test
Expand Down
26 changes: 15 additions & 11 deletions compel/arch/x86/src/lib/infect.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,19 @@ static int get_task_fpregs(pid_t pid, user_fpregs_struct_t *xsave)
return 0;
}

static inline void fixup_mxcsr(struct xsave_struct *xsave)
{
/*
* Right now xsave->i387.mxcsr filled with the random garbage,
* let's make it valid by applying mask which allows all
* features, except the denormals-are-zero feature bit.
*
* See also fpu__init_system_mxcsr function:
* https://github.com/torvalds/linux/blob/8cb1ae19/arch/x86/kernel/fpu/init.c#L117
*/
xsave->i387.mxcsr &= 0x0000ffbf;
}

/* See arch/x86/kernel/fpu/xstate.c */
static void validate_random_xstate(struct xsave_struct *xsave)
{
Expand Down Expand Up @@ -272,17 +285,6 @@ static void validate_random_xstate(struct xsave_struct *xsave)

/* No reserved bits may be set */
memset(&hdr->reserved, 0, sizeof(hdr->reserved));

/*
* While using PTRACE_SETREGSET the kernel checks that
* "Reserved bits in MXCSR must be zero."
* if (mxcsr[0] & ~mxcsr_feature_mask)
* return -EINVAL;
*
* As the mxcsr_feature_mask depends on the CPU the easiest solution for
* this error injection test is to set mxcsr just to zero.
*/
xsave->i387.mxcsr = 0;
}

/*
Expand All @@ -309,6 +311,8 @@ static int corrupt_extregs(pid_t pid)
*/
pr_err("Corrupting %s for %d, seed %u\n", use_xsave ? "xsave" : "fpuregs", pid, init_seed);

fixup_mxcsr(&ext_regs);

if (!use_xsave) {
if (ptrace(PTRACE_SETFPREGS, pid, NULL, &ext_regs)) {
pr_perror("Can't set FPU registers for %d", pid);
Expand Down
3 changes: 3 additions & 0 deletions compel/include/uapi/infect.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
87 changes: 72 additions & 15 deletions compel/src/lib/infect.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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;

/*
Expand Down Expand Up @@ -291,17 +320,32 @@ int compel_wait_task(int pid, int ppid, int (*get_status)(int pid, struct seize_
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 (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_sigstop) {
if (skip_sigstop(pid, nr_sigstop))
goto err_stop;
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;
}
Expand All @@ -313,15 +357,18 @@ int compel_wait_task(int pid, int ppid, int (*get_status)(int pid, struct seize_
goto err;
}

err_stop:
kill(pid, SIGSTOP);
err:
if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
pr_perror("Unable to detach from %d", pid);
return -1;
}

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;

Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions criu/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,7 @@ int parse_options(int argc, char **argv, bool *usage_error, bool *has_exec_cmd,
{ "cgroup-yard", required_argument, 0, 1096 },
{ "pre-dump-mode", required_argument, 0, 1097 },
{ "file-validation", required_argument, 0, 1098 },
BOOL_OPT("skip-file-rwx-check", &opts.skip_file_rwx_check),
{ "lsm-mount-context", required_argument, 0, 1099 },
{ "network-lock", required_argument, 0, 1100 },
BOOL_OPT("mntns-compat-mode", &opts.mntns_compat_mode),
Expand Down
20 changes: 13 additions & 7 deletions criu/cr-dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1034,7 +1039,7 @@ static int dump_task_signals(pid_t pid, struct pstree_item *item)
return 0;
}

static int read_rseq_cs(pid_t tid, struct __ptrace_rseq_configuration *rseqc, struct rseq_cs *rseq_cs,
static int read_rseq_cs(pid_t tid, struct __ptrace_rseq_configuration *rseqc, struct criu_rseq_cs *rseq_cs,
struct criu_rseq *rseq)
{
int ret;
Expand Down Expand Up @@ -1065,10 +1070,11 @@ static int read_rseq_cs(pid_t tid, struct __ptrace_rseq_configuration *rseqc, st
if (!rseq->rseq_cs)
return 0;

ret = ptrace_peek_area(tid, rseq_cs, decode_pointer(rseq->rseq_cs), sizeof(struct rseq_cs));
ret = ptrace_peek_area(tid, rseq_cs, decode_pointer(rseq->rseq_cs), sizeof(struct criu_rseq_cs));
if (ret) {
pr_err("ptrace_peek_area(%d, %lx, %lx, %lx): fail to read rseq_cs struct\n", tid,
(unsigned long)rseq_cs, (unsigned long)rseq->rseq_cs, (unsigned long)sizeof(struct rseq_cs));
(unsigned long)rseq_cs, (unsigned long)rseq->rseq_cs,
(unsigned long)sizeof(struct criu_rseq_cs));
return -1;
}

Expand All @@ -1083,7 +1089,7 @@ static int dump_thread_rseq(struct pstree_item *item, int i)
CoreEntry *core = item->core[i];
RseqEntry **rseqep = &core->thread_core->rseq_entry;
struct criu_rseq rseq = {};
struct rseq_cs *rseq_cs = &dmpi(item)->thread_rseq_cs[i];
struct criu_rseq_cs *rseq_cs = &dmpi(item)->thread_rseq_cs[i];
pid_t tid = item->threads[i].real;

/*
Expand Down Expand Up @@ -1149,7 +1155,7 @@ static int dump_thread_rseq(struct pstree_item *item, int i)
static int dump_task_rseq(pid_t pid, struct pstree_item *item)
{
int i;
struct rseq_cs *thread_rseq_cs;
struct criu_rseq_cs *thread_rseq_cs;

/* if rseq() syscall isn't supported then nothing to dump */
if (!kdat.has_rseq)
Expand All @@ -1174,15 +1180,15 @@ static int dump_task_rseq(pid_t pid, struct pstree_item *item)
return -1;
}

static bool task_in_rseq(struct rseq_cs *rseq_cs, uint64_t addr)
static bool task_in_rseq(struct criu_rseq_cs *rseq_cs, uint64_t addr)
{
return addr >= rseq_cs->start_ip && addr < rseq_cs->start_ip + rseq_cs->post_commit_offset;
}

static int fixup_thread_rseq(struct pstree_item *item, int i)
{
CoreEntry *core = item->core[i];
struct rseq_cs *rseq_cs = &dmpi(item)->thread_rseq_cs[i];
struct criu_rseq_cs *rseq_cs = &dmpi(item)->thread_rseq_cs[i];
pid_t tid = item->threads[i].real;

/* equivalent to (struct rseq)->rseq_cs is NULL */
Expand Down
11 changes: 10 additions & 1 deletion criu/cr-restore.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -2107,8 +2110,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);
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions criu/cr-service.c
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,9 @@ static int setup_opts_from_req(int sk, CriuOpts *req)
if (req->has_shell_job)
opts.shell_job = req->shell_job;

if (req->has_skip_file_rwx_check)
opts.skip_file_rwx_check = req->skip_file_rwx_check;

if (req->has_file_locks)
opts.handle_file_locks = req->file_locks;

Expand Down
3 changes: 3 additions & 0 deletions criu/crtools.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,9 @@ int main(int argc, char *argv[], char *envp[])
" --file-validation METHOD\n"
" pass the validation method to be used; argument\n"
" can be 'filesize' or 'buildid' (default).\n"
" --skip-file-rwx-check\n"
" Skip checking file permissions\n"
" (r/w/x for u/g/o) on restore.\n"
"\n"
"Check options:\n"
" Without options, \"criu check\" checks availability of absolutely required\n"
Expand Down
18 changes: 15 additions & 3 deletions criu/files-reg.c
Original file line number Diff line number Diff line change
Expand Up @@ -2199,9 +2199,21 @@ int open_path(struct file_desc *d, int (*open_cb)(int mntns_root, struct reg_fil
if (!validate_file(tmp, &st, rfi))
goto err;

if (rfi->rfe->has_mode && (st.st_mode != rfi->rfe->mode)) {
pr_err("File %s has bad mode 0%o (expect 0%o)\n", rfi->path, (int)st.st_mode, rfi->rfe->mode);
goto err;
if (rfi->rfe->has_mode) {
mode_t curr_mode = st.st_mode;
mode_t saved_mode = rfi->rfe->mode;

if (opts.skip_file_rwx_check) {
curr_mode &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
saved_mode &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
}

if (curr_mode != saved_mode) {
pr_err("File %s has bad mode 0%o (expect 0%o)\n"
"File r/w/x checks can be skipped with the --skip-file-rwx-check option\n",
rfi->path, (int)curr_mode, saved_mode);
goto err;
}
}

/*
Expand Down
1 change: 1 addition & 0 deletions criu/include/cr_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ struct cr_options {
bool lazy_pages;
char *work_dir;
int network_lock_method;
int skip_file_rwx_check;

/*
* When we scheduler for removal some functionality we first
Expand Down
Loading

0 comments on commit a38fc9a

Please sign in to comment.