Commit 5773ad84 authored by Pavel Emelyanov's avatar Pavel Emelyanov

seize: Simplify creds checkers

Don't do comparisons of creds insize core seizing routine. Instead,
pull the creds on the caller's stack and compare them there if
required.

At the same time more the proc_status_creds_dumpable() to the place
where it's only needed and make it static.
Signed-off-by: 's avatarPavel Emelyanov <xemul@virtuozzo.com>
parent 6a1c0a07
...@@ -119,7 +119,7 @@ int cr_exec(int pid, char **opt) ...@@ -119,7 +119,7 @@ int cr_exec(int pid, char **opt)
struct parasite_ctl *ctl; struct parasite_ctl *ctl;
struct vm_area_list vmas; struct vm_area_list vmas;
int ret, prev_state, exit_code = -1; int ret, prev_state, exit_code = -1;
struct proc_status_creds *creds = NULL; struct proc_status_creds creds;
unsigned long p_start; unsigned long p_start;
if (!sys_name) { if (!sys_name) {
...@@ -141,19 +141,17 @@ int cr_exec(int pid, char **opt) ...@@ -141,19 +141,17 @@ int cr_exec(int pid, char **opt)
if (seize_catch_task(pid)) if (seize_catch_task(pid))
goto out; goto out;
/*
* We don't seize a task's threads here, so there is no reason to
* mess with creds in this use case anyway.
*/
prev_state = ret = seize_wait_task(pid, -1, &creds); prev_state = ret = seize_wait_task(pid, -1, &creds);
if (ret < 0) { if (ret < 0) {
pr_err("Can't seize task %d\n", pid); pr_err("Can't seize task %d\n", pid);
goto out; goto out;
} }
/*
* We don't seize a task's threads here, and there is no reason to
* compare threads' creds in this use case anyway, so let's just free
* the creds.
*/
free(creds);
ret = collect_mappings(pid, &vmas, NULL); ret = collect_mappings(pid, &vmas, NULL);
if (ret) { if (ret) {
pr_err("Can't collect vmas for %d\n", pid); pr_err("Can't collect vmas for %d\n", pid);
......
...@@ -94,9 +94,6 @@ struct proc_status_creds { ...@@ -94,9 +94,6 @@ struct proc_status_creds {
u32 cap_bnd[PROC_CAP_SIZE]; u32 cap_bnd[PROC_CAP_SIZE];
}; };
bool proc_status_creds_dumpable(struct proc_status_creds *parent,
struct proc_status_creds *child);
#define INVALID_UID ((uid_t)-1) #define INVALID_UID ((uid_t)-1)
extern int parse_pid_stat(pid_t pid, struct proc_pid_stat *s); extern int parse_pid_stat(pid_t pid, struct proc_pid_stat *s);
......
...@@ -76,7 +76,7 @@ struct ptrace_peeksiginfo_args { ...@@ -76,7 +76,7 @@ struct ptrace_peeksiginfo_args {
extern int processes_to_wait; extern int processes_to_wait;
extern int seize_catch_task(pid_t pid); extern int seize_catch_task(pid_t pid);
extern int seize_wait_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds); extern int seize_wait_task(pid_t pid, pid_t ppid, struct proc_status_creds *creds);
extern int suspend_seccomp(pid_t pid); extern int suspend_seccomp(pid_t pid);
extern int unseize_task(pid_t pid, int orig_state, int state); extern int unseize_task(pid_t pid, int orig_state, int state);
extern int ptrace_peek_area(pid_t pid, void *dst, void *addr, long bytes); extern int ptrace_peek_area(pid_t pid, void *dst, void *addr, long bytes);
......
...@@ -2511,55 +2511,6 @@ int aufs_parse(struct mount_info *new) ...@@ -2511,55 +2511,6 @@ int aufs_parse(struct mount_info *new)
return ret; return ret;
} }
bool proc_status_creds_dumpable(struct proc_status_creds *parent,
struct proc_status_creds *child)
{
const size_t size = sizeof(struct proc_status_creds) -
offsetof(struct proc_status_creds, cap_inh);
/*
* The comparison rules are the following
*
* - CAPs can be different
* - seccomp filters should be passed via
* semantic comparison (FIXME) but for
* now we require them to be exactly
* identical
* - the rest of members must match
*/
if (memcmp(parent, child, size)) {
if (!pr_quelled(LOG_DEBUG)) {
pr_debug("Creds undumpable (parent:child)\n"
" uids: %d:%d %d:%d %d:%d %d:%d\n"
" gids: %d:%d %d:%d %d:%d %d:%d\n"
" state: %d:%d"
" ppid: %d:%d\n"
" sigpnd: %llu:%llu\n"
" shdpnd: %llu:%llu\n"
" seccomp_mode: %d:%d\n"
" last_filter: %u:%u\n",
parent->uids[0], child->uids[0],
parent->uids[1], child->uids[1],
parent->uids[2], child->uids[2],
parent->uids[3], child->uids[3],
parent->gids[0], child->gids[0],
parent->gids[1], child->gids[1],
parent->gids[2], child->gids[2],
parent->gids[3], child->gids[3],
parent->state, child->state,
parent->ppid, child->ppid,
parent->sigpnd, child->sigpnd,
parent->shdpnd, child->shdpnd,
parent->seccomp_mode, child->seccomp_mode,
parent->last_filter, child->last_filter);
}
return false;
}
return true;
}
int parse_children(pid_t pid, pid_t **_c, int *_n) int parse_children(pid_t pid, pid_t **_c, int *_n)
{ {
pid_t *ch = NULL; pid_t *ch = NULL;
......
...@@ -148,17 +148,11 @@ static int skip_sigstop(int pid, int nr_signals) ...@@ -148,17 +148,11 @@ static int skip_sigstop(int pid, int nr_signals)
* of it so the task would not know if it was saddled * of it so the task would not know if it was saddled
* up with someone else. * up with someone else.
*/ */
int seize_wait_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds) int seize_wait_task(pid_t pid, pid_t ppid, struct proc_status_creds *creds)
{ {
siginfo_t si; siginfo_t si;
int status, nr_sigstop; int status, nr_sigstop;
int ret = 0, ret2, wait_errno = 0; int ret = 0, ret2, wait_errno = 0;
struct proc_status_creds cr;
/*
* For the comparison below, let's zero out any padding.
*/
memzero(&cr, sizeof(struct proc_status_creds));
/* /*
* It's ugly, but the ptrace API doesn't allow to distinguish * It's ugly, but the ptrace API doesn't allow to distinguish
...@@ -184,26 +178,26 @@ try_again: ...@@ -184,26 +178,26 @@ try_again:
wait_errno = errno; wait_errno = errno;
} }
ret2 = parse_pid_status(pid, &cr); ret2 = parse_pid_status(pid, creds);
if (ret2) if (ret2)
goto err; goto err;
if (ret < 0 || WIFEXITED(status) || WIFSIGNALED(status)) { if (ret < 0 || WIFEXITED(status) || WIFSIGNALED(status)) {
if (cr.state != 'Z') { if (creds->state != 'Z') {
if (pid == getpid()) if (pid == getpid())
pr_err("The criu itself is within dumped tree.\n"); pr_err("The criu itself is within dumped tree.\n");
else else
pr_err("Unseizable non-zombie %d found, state %c, err %d/%d\n", pr_err("Unseizable non-zombie %d found, state %c, err %d/%d\n",
pid, cr.state, ret, wait_errno); pid, creds->state, ret, wait_errno);
return -1; return -1;
} }
return TASK_DEAD; return TASK_DEAD;
} }
if ((ppid != -1) && (cr.ppid != ppid)) { if ((ppid != -1) && (creds->ppid != ppid)) {
pr_err("Task pid reused while suspending (%d: %d -> %d)\n", pr_err("Task pid reused while suspending (%d: %d -> %d)\n",
pid, ppid, cr.ppid); pid, ppid, creds->ppid);
goto err; goto err;
} }
...@@ -235,25 +229,13 @@ try_again: ...@@ -235,25 +229,13 @@ try_again:
goto try_again; goto try_again;
} }
if (*creds == NULL) { if (creds->seccomp_mode != SECCOMP_MODE_DISABLED && suspend_seccomp(pid) < 0)
*creds = xzalloc(sizeof(struct proc_status_creds));
if (!*creds)
goto err;
**creds = cr;
} else if (!proc_status_creds_dumpable(*creds, &cr)) {
pr_err("creds don't match %d %d\n", pid, ppid);
goto err;
}
if (cr.seccomp_mode != SECCOMP_MODE_DISABLED && suspend_seccomp(pid) < 0)
goto err; goto err;
nr_sigstop = 0; nr_sigstop = 0;
if (cr.sigpnd & (1 << (SIGSTOP - 1))) if (creds->sigpnd & (1 << (SIGSTOP - 1)))
nr_sigstop++; nr_sigstop++;
if (cr.shdpnd & (1 << (SIGSTOP - 1))) if (creds->shdpnd & (1 << (SIGSTOP - 1)))
nr_sigstop++; nr_sigstop++;
if (si.si_signo == SIGSTOP) if (si.si_signo == SIGSTOP)
nr_sigstop++; nr_sigstop++;
......
...@@ -437,6 +437,7 @@ static int collect_children(struct pstree_item *item) ...@@ -437,6 +437,7 @@ static int collect_children(struct pstree_item *item)
nr_inprogress = 0; nr_inprogress = 0;
for (i = 0; i < nr_children; i++) { for (i = 0; i < nr_children; i++) {
struct pstree_item *c; struct pstree_item *c;
struct proc_status_creds *creds;
pid_t pid = ch[i]; pid_t pid = ch[i];
/* Is it already frozen? */ /* Is it already frozen? */
...@@ -462,7 +463,13 @@ static int collect_children(struct pstree_item *item) ...@@ -462,7 +463,13 @@ static int collect_children(struct pstree_item *item)
/* fails when meets a zombie */ /* fails when meets a zombie */
seize_catch_task(pid); seize_catch_task(pid);
ret = seize_wait_task(pid, item->pid.real, &dmpi(c)->pi_creds); creds = xzalloc(sizeof(*creds));
if (!creds) {
ret = -1;
goto free;
}
ret = seize_wait_task(pid, item->pid.real, creds);
if (ret < 0) { if (ret < 0) {
/* /*
* Here is a race window between parse_children() and seize(), * Here is a race window between parse_children() and seize(),
...@@ -473,9 +480,11 @@ static int collect_children(struct pstree_item *item) ...@@ -473,9 +480,11 @@ static int collect_children(struct pstree_item *item)
*/ */
ret = 0; ret = 0;
xfree(c); xfree(c);
xfree(creds);
continue; continue;
} }
dmpi(c)->pi_creds = creds;
c->pid.real = pid; c->pid.real = pid;
c->parent = item; c->parent = item;
c->pid.state = ret; c->pid.state = ret;
...@@ -591,6 +600,55 @@ static inline bool thread_collected(struct pstree_item *i, pid_t tid) ...@@ -591,6 +600,55 @@ static inline bool thread_collected(struct pstree_item *i, pid_t tid)
return false; return false;
} }
static bool creds_dumpable(struct proc_status_creds *parent,
struct proc_status_creds *child)
{
const size_t size = sizeof(struct proc_status_creds) -
offsetof(struct proc_status_creds, cap_inh);
/*
* The comparison rules are the following
*
* - CAPs can be different
* - seccomp filters should be passed via
* semantic comparison (FIXME) but for
* now we require them to be exactly
* identical
* - the rest of members must match
*/
if (memcmp(parent, child, size)) {
if (!pr_quelled(LOG_DEBUG)) {
pr_debug("Creds undumpable (parent:child)\n"
" uids: %d:%d %d:%d %d:%d %d:%d\n"
" gids: %d:%d %d:%d %d:%d %d:%d\n"
" state: %d:%d"
" ppid: %d:%d\n"
" sigpnd: %llu:%llu\n"
" shdpnd: %llu:%llu\n"
" seccomp_mode: %d:%d\n"
" last_filter: %u:%u\n",
parent->uids[0], child->uids[0],
parent->uids[1], child->uids[1],
parent->uids[2], child->uids[2],
parent->uids[3], child->uids[3],
parent->gids[0], child->gids[0],
parent->gids[1], child->gids[1],
parent->gids[2], child->gids[2],
parent->gids[3], child->gids[3],
parent->state, child->state,
parent->ppid, child->ppid,
parent->sigpnd, child->sigpnd,
parent->shdpnd, child->shdpnd,
parent->seccomp_mode, child->seccomp_mode,
parent->last_filter, child->last_filter);
}
return false;
}
return true;
}
static int collect_threads(struct pstree_item *item) static int collect_threads(struct pstree_item *item)
{ {
struct pid *threads = NULL; struct pid *threads = NULL;
...@@ -618,6 +676,7 @@ static int collect_threads(struct pstree_item *item) ...@@ -618,6 +676,7 @@ static int collect_threads(struct pstree_item *item)
nr_inprogress = 0; nr_inprogress = 0;
for (i = 0; i < nr_threads; i++) { for (i = 0; i < nr_threads; i++) {
pid_t pid = threads[i].real; pid_t pid = threads[i].real;
struct proc_status_creds t_creds = {};
if (thread_collected(item, pid)) if (thread_collected(item, pid))
continue; continue;
...@@ -630,7 +689,7 @@ static int collect_threads(struct pstree_item *item) ...@@ -630,7 +689,7 @@ static int collect_threads(struct pstree_item *item)
if (!opts.freeze_cgroup && seize_catch_task(pid)) if (!opts.freeze_cgroup && seize_catch_task(pid))
continue; continue;
ret = seize_wait_task(pid, item_ppid(item), &dmpi(item)->pi_creds); ret = seize_wait_task(pid, item_ppid(item), &t_creds);
if (ret < 0) { if (ret < 0) {
/* /*
* Here is a race window between parse_threads() and seize(), * Here is a race window between parse_threads() and seize(),
...@@ -651,6 +710,9 @@ static int collect_threads(struct pstree_item *item) ...@@ -651,6 +710,9 @@ static int collect_threads(struct pstree_item *item)
goto err; goto err;
} }
if (!creds_dumpable(dmpi(item)->pi_creds, &t_creds))
goto err;
if (ret == TASK_STOPPED) { if (ret == TASK_STOPPED) {
nr_stopped++; nr_stopped++;
} }
...@@ -737,6 +799,7 @@ int collect_pstree(void) ...@@ -737,6 +799,7 @@ int collect_pstree(void)
{ {
pid_t pid = root_item->pid.real; pid_t pid = root_item->pid.real;
int ret = -1; int ret = -1;
struct proc_status_creds *creds;
timing_start(TIME_FREEZING); timing_start(TIME_FREEZING);
...@@ -755,11 +818,17 @@ int collect_pstree(void) ...@@ -755,11 +818,17 @@ int collect_pstree(void)
goto err; goto err;
} }
ret = seize_wait_task(pid, -1, &dmpi(root_item)->pi_creds); creds = xzalloc(sizeof(*creds));
if (!creds)
goto err;
ret = seize_wait_task(pid, -1, creds);
if (ret < 0) if (ret < 0)
goto err; goto err;
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;
dmpi(root_item)->pi_creds = creds;
ret = collect_task(root_item); ret = collect_task(root_item);
if (ret < 0) if (ret < 0)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment