mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-07-29 04:50:26 +00:00
creds: dump -- Implement per-thread dump of credentials
This as well as restore requires several steps to reach per-thread support during dump stage - @creds area to be fetched from the parasite is embedded into parasite_dump_structure - when test for task to be dumpable we no longer compare caps because we now allow them to be different (and I renamed proc_status_creds_eq to proc_status_creds_dumpable for this sake) - have to extend dump_thread_common to support dumping of creds (we call for dump_thread_common in several places, in particular when we need to fetch misc params we don't need creds, here @creds option comes into the play) - after this patch no creds-X.img file be generated anymore, I guess we might drop it off with time from descriptors https://jira.sw.ru/browse/PSBM-41416 v2: - In dump_task_creds() don't mangle the call for parasite_dump_creds and collect_lsm_profile - PARASITE_MAX_GROUPS takes parasite_dump_thread into account because dump_thread_common now serves two cases: for plain misc parameters fetching and for creds as well (depending on the context) - when test for dumpable we still require the seccomp filters to match, they can be different and we need to support such configuration too but not in this series v3: - Rip off dump_task_creds completely, together with PARASITE_CMD_DUMP_CREDS, we dump creds unconditionally in dump_thread_common - the group leader thread data is fetched via new parasite_dump_thread_leader_seized helper Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org> Acked-by: Tycho Andersen <tycho.andersen@canonical.com> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
This commit is contained in:
parent
eb5d84428e
commit
e9fc593cde
8 changed files with 150 additions and 101 deletions
40
cr-dump.c
40
cr-dump.c
|
|
@ -511,24 +511,6 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int dump_task_creds(struct parasite_ctl *ctl,
|
||||
const struct cr_imgset *fds)
|
||||
{
|
||||
CredsEntry ce = CREDS_ENTRY__INIT;
|
||||
|
||||
pr_info("\n");
|
||||
pr_info("Dumping creds for %d)\n", ctl->pid.real);
|
||||
pr_info("----------------------------------------\n");
|
||||
|
||||
if (parasite_dump_creds(ctl, &ce) < 0)
|
||||
return -1;
|
||||
|
||||
if (collect_lsm_profile(ctl->pid.real, &ce) < 0)
|
||||
return -1;
|
||||
|
||||
return pb_write_one(img_from_set(fds, CR_FD_CREDS), &ce, PB_CREDS);
|
||||
}
|
||||
|
||||
static int get_task_futex_robust_list(pid_t pid, ThreadCoreEntry *info)
|
||||
{
|
||||
struct robust_list_head *head = NULL;
|
||||
|
|
@ -674,7 +656,9 @@ int dump_thread_core(int pid, CoreEntry *core, const struct parasite_dump_thread
|
|||
int ret;
|
||||
ThreadCoreEntry *tc = core->thread_core;
|
||||
|
||||
ret = get_task_futex_robust_list(pid, tc);
|
||||
ret = collect_lsm_profile(pid, tc->creds);
|
||||
if (!ret)
|
||||
ret = get_task_futex_robust_list(pid, tc);
|
||||
if (!ret)
|
||||
ret = dump_sched_info(pid, tc);
|
||||
if (!ret) {
|
||||
|
|
@ -691,10 +675,10 @@ int dump_thread_core(int pid, CoreEntry *core, const struct parasite_dump_thread
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int dump_task_core_all(struct pstree_item *item,
|
||||
const struct proc_pid_stat *stat,
|
||||
const struct parasite_dump_misc *misc,
|
||||
const struct cr_imgset *cr_imgset)
|
||||
static int dump_task_core_all(struct parasite_ctl *ctl,
|
||||
struct pstree_item *item,
|
||||
const struct proc_pid_stat *stat,
|
||||
const struct cr_imgset *cr_imgset)
|
||||
{
|
||||
struct cr_img *img;
|
||||
CoreEntry *core = item->core[0];
|
||||
|
|
@ -727,7 +711,7 @@ static int dump_task_core_all(struct pstree_item *item,
|
|||
core->tc->task_state = item->state;
|
||||
core->tc->exit_code = 0;
|
||||
|
||||
ret = dump_thread_core(pid, core, &misc->ti);
|
||||
ret = parasite_dump_thread_leader_seized(ctl, pid, core);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
|
@ -1327,18 +1311,12 @@ static int dump_one_task(struct pstree_item *item)
|
|||
goto err_cure;
|
||||
}
|
||||
|
||||
ret = dump_task_core_all(item, &pps_buf, &misc, cr_imgset);
|
||||
ret = dump_task_core_all(parasite_ctl, item, &pps_buf, cr_imgset);
|
||||
if (ret) {
|
||||
pr_err("Dump core (pid: %d) failed with %d\n", pid, ret);
|
||||
goto err_cure;
|
||||
}
|
||||
|
||||
ret = dump_task_creds(parasite_ctl, cr_imgset);
|
||||
if (ret) {
|
||||
pr_err("Dump creds (pid: %d) failed with %d\n", pid, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = parasite_stop_daemon(parasite_ctl);
|
||||
if (ret) {
|
||||
pr_err("Can't cure (pid: %d) from parasite\n", pid);
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ extern int __parasite_wait_daemon_ack(unsigned int cmd,
|
|||
|
||||
extern int parasite_dump_misc_seized(struct parasite_ctl *ctl, struct parasite_dump_misc *misc);
|
||||
extern int parasite_dump_creds(struct parasite_ctl *ctl, struct _CredsEntry *ce);
|
||||
extern int parasite_dump_thread_leader_seized(struct parasite_ctl *ctl, int pid, struct _CoreEntry *core);
|
||||
extern int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
|
||||
struct pid *tid, struct _CoreEntry *core);
|
||||
extern int dump_thread_core(int pid, CoreEntry *core, const struct parasite_dump_thread *dt);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ enum {
|
|||
PARASITE_CMD_DUMP_ITIMERS,
|
||||
PARASITE_CMD_DUMP_POSIX_TIMERS,
|
||||
PARASITE_CMD_DUMP_MISC,
|
||||
PARASITE_CMD_DUMP_CREDS,
|
||||
PARASITE_CMD_DRAIN_FDS,
|
||||
PARASITE_CMD_GET_PROC_FD,
|
||||
PARASITE_CMD_DUMP_TTY,
|
||||
|
|
@ -151,14 +150,6 @@ static inline int posix_timers_dump_size(int timer_n)
|
|||
return sizeof(int) + sizeof(struct posix_timer) * timer_n;
|
||||
}
|
||||
|
||||
struct parasite_dump_thread {
|
||||
unsigned int *tid_addr;
|
||||
pid_t tid;
|
||||
tls_t tls;
|
||||
stack_t sas;
|
||||
int pdeath_sig;
|
||||
};
|
||||
|
||||
/*
|
||||
* Misc sfuff, that is too small for separate file, but cannot
|
||||
* be read w/o using parasite
|
||||
|
|
@ -172,8 +163,6 @@ struct parasite_dump_misc {
|
|||
u32 pgid;
|
||||
u32 umask;
|
||||
|
||||
struct parasite_dump_thread ti;
|
||||
|
||||
int dumpable;
|
||||
};
|
||||
|
||||
|
|
@ -182,9 +171,8 @@ struct parasite_dump_misc {
|
|||
* and still fit the struct in one page
|
||||
*/
|
||||
#define PARASITE_MAX_GROUPS \
|
||||
(PAGE_SIZE - \
|
||||
offsetof(struct parasite_dump_creds, groups) \
|
||||
) / sizeof(unsigned int) /* groups */
|
||||
((PAGE_SIZE - sizeof(struct parasite_dump_thread) - \
|
||||
offsetof(struct parasite_dump_creds, groups)) / sizeof(unsigned int)) /* groups */
|
||||
|
||||
struct parasite_dump_creds {
|
||||
unsigned int cap_last_cap;
|
||||
|
|
@ -214,6 +202,15 @@ struct parasite_dump_creds {
|
|||
unsigned int groups[0];
|
||||
};
|
||||
|
||||
struct parasite_dump_thread {
|
||||
unsigned int *tid_addr;
|
||||
pid_t tid;
|
||||
tls_t tls;
|
||||
stack_t sas;
|
||||
int pdeath_sig;
|
||||
struct parasite_dump_creds creds[0];
|
||||
};
|
||||
|
||||
static inline void copy_sas(ThreadSasEntry *dst, const stack_t *src)
|
||||
{
|
||||
dst->ss_sp = encode_pointer(src->ss_sp);
|
||||
|
|
|
|||
|
|
@ -103,7 +103,8 @@ struct proc_status_creds {
|
|||
u32 cap_bnd[PROC_CAP_SIZE];
|
||||
};
|
||||
|
||||
bool proc_status_creds_eq(struct proc_status_creds *o1, struct proc_status_creds *o2);
|
||||
bool proc_status_creds_dumpable(struct proc_status_creds *parent,
|
||||
struct proc_status_creds *child);
|
||||
|
||||
typedef int (*mount_fn_t)(struct mount_info *mi, const char *src, const
|
||||
char *fstype, unsigned long mountflags);
|
||||
|
|
|
|||
|
|
@ -532,12 +532,74 @@ err:
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int alloc_groups_copy_creds(CredsEntry *ce, struct parasite_dump_creds *c)
|
||||
{
|
||||
BUILD_BUG_ON(sizeof(ce->groups[0]) != sizeof(c->groups[0]));
|
||||
BUILD_BUG_ON(sizeof(ce->cap_inh[0]) != sizeof(c->cap_inh[0]));
|
||||
BUILD_BUG_ON(sizeof(ce->cap_prm[0]) != sizeof(c->cap_prm[0]));
|
||||
BUILD_BUG_ON(sizeof(ce->cap_eff[0]) != sizeof(c->cap_eff[0]));
|
||||
BUILD_BUG_ON(sizeof(ce->cap_bnd[0]) != sizeof(c->cap_bnd[0]));
|
||||
|
||||
BUG_ON(ce->n_cap_inh != CR_CAP_SIZE);
|
||||
BUG_ON(ce->n_cap_prm != CR_CAP_SIZE);
|
||||
BUG_ON(ce->n_cap_eff != CR_CAP_SIZE);
|
||||
BUG_ON(ce->n_cap_bnd != CR_CAP_SIZE);
|
||||
|
||||
memcpy(ce->cap_inh, c->cap_inh, sizeof(c->cap_inh[0]) * CR_CAP_SIZE);
|
||||
memcpy(ce->cap_prm, c->cap_prm, sizeof(c->cap_prm[0]) * CR_CAP_SIZE);
|
||||
memcpy(ce->cap_eff, c->cap_eff, sizeof(c->cap_eff[0]) * CR_CAP_SIZE);
|
||||
memcpy(ce->cap_bnd, c->cap_bnd, sizeof(c->cap_bnd[0]) * CR_CAP_SIZE);
|
||||
|
||||
ce->secbits = c->secbits;
|
||||
ce->n_groups = c->ngroups;
|
||||
|
||||
ce->groups = xmemdup(c->groups, sizeof(c->groups[0]) * c->ngroups);
|
||||
|
||||
ce->uid = c->uids[0];
|
||||
ce->gid = c->gids[0];
|
||||
ce->euid = c->uids[1];
|
||||
ce->egid = c->gids[1];
|
||||
ce->suid = c->uids[2];
|
||||
ce->sgid = c->gids[2];
|
||||
ce->fsuid = c->uids[3];
|
||||
ce->fsgid = c->gids[3];
|
||||
|
||||
return ce->groups ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
int parasite_dump_thread_leader_seized(struct parasite_ctl *ctl, int pid, CoreEntry *core)
|
||||
{
|
||||
ThreadCoreEntry *tc = core->thread_core;
|
||||
struct parasite_dump_thread *args;
|
||||
struct parasite_dump_creds *pc;
|
||||
int ret;
|
||||
|
||||
args = parasite_args(ctl, struct parasite_dump_thread);
|
||||
|
||||
pc = args->creds;
|
||||
pc->cap_last_cap = kdat.last_cap;
|
||||
|
||||
ret = parasite_execute_daemon(PARASITE_CMD_DUMP_THREAD, ctl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = alloc_groups_copy_creds(tc->creds, pc);
|
||||
if (ret) {
|
||||
pr_err("Can't copy creds for thread leader %d\n", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return dump_thread_core(pid, core, args);
|
||||
}
|
||||
|
||||
int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
|
||||
struct pid *tid, CoreEntry *core)
|
||||
{
|
||||
struct parasite_dump_thread *args;
|
||||
pid_t pid = tid->real;
|
||||
ThreadCoreEntry *tc = core->thread_core;
|
||||
CredsEntry *creds = tc->creds;
|
||||
struct parasite_dump_creds *pc;
|
||||
int ret;
|
||||
struct thread_ctx octx;
|
||||
|
||||
|
|
@ -545,6 +607,9 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
|
|||
|
||||
args = parasite_args(ctl, struct parasite_dump_thread);
|
||||
|
||||
pc = args->creds;
|
||||
pc->cap_last_cap = kdat.last_cap;
|
||||
|
||||
ret = get_thread_ctx(pid, &octx);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
|
@ -559,6 +624,12 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
|
|||
return -1;
|
||||
}
|
||||
|
||||
ret = alloc_groups_copy_creds(creds, pc);
|
||||
if (ret) {
|
||||
pr_err("Can't copy creds for thread %d\n", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = get_task_regs(pid, octx.regs, core);
|
||||
if (ret) {
|
||||
pr_err("Can't obtain regs for thread %d\n", pid);
|
||||
|
|
@ -730,51 +801,6 @@ struct parasite_tty_args *parasite_dump_tty(struct parasite_ctl *ctl, int fd, in
|
|||
return p;
|
||||
}
|
||||
|
||||
int parasite_dump_creds(struct parasite_ctl *ctl, CredsEntry *ce)
|
||||
{
|
||||
struct parasite_dump_creds *pc;
|
||||
|
||||
BUILD_BUG_ON(sizeof(*pc) > PAGE_SIZE);
|
||||
|
||||
pc = parasite_args(ctl, struct parasite_dump_creds);
|
||||
pc->cap_last_cap = kdat.last_cap;
|
||||
|
||||
if (parasite_execute_daemon(PARASITE_CMD_DUMP_CREDS, ctl) < 0)
|
||||
return -1;
|
||||
|
||||
ce->n_cap_inh = CR_CAP_SIZE;
|
||||
ce->cap_inh = pc->cap_inh;
|
||||
ce->n_cap_prm = CR_CAP_SIZE;
|
||||
ce->cap_prm = pc->cap_prm;
|
||||
ce->n_cap_eff = CR_CAP_SIZE;
|
||||
ce->cap_eff = pc->cap_eff;
|
||||
ce->n_cap_bnd = CR_CAP_SIZE;
|
||||
ce->cap_bnd = pc->cap_bnd;
|
||||
|
||||
ce->secbits = pc->secbits;
|
||||
ce->n_groups = pc->ngroups;
|
||||
|
||||
/*
|
||||
* Achtung! We leak the parasite args pointer to the caller.
|
||||
* It's not safe in general, but in our case is OK, since the
|
||||
* latter doesn't go to parasite before using the data in it.
|
||||
*/
|
||||
|
||||
BUILD_BUG_ON(sizeof(ce->groups[0]) != sizeof(pc->groups[0]));
|
||||
ce->groups = pc->groups;
|
||||
|
||||
ce->uid = pc->uids[0];
|
||||
ce->gid = pc->gids[0];
|
||||
ce->euid = pc->uids[1];
|
||||
ce->egid = pc->gids[1];
|
||||
ce->suid = pc->uids[2];
|
||||
ce->sgid = pc->gids[2];
|
||||
ce->fsuid = pc->uids[3];
|
||||
ce->fsgid = pc->gids[3];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parasite_drain_fds_seized(struct parasite_ctl *ctl,
|
||||
struct parasite_drain_fd *dfds, int *lfds, struct fd_opts *opts)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ static int dump_posix_timers(struct parasite_dump_posix_timers_args *args)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int dump_creds(struct parasite_dump_creds *args);
|
||||
|
||||
static int dump_thread_common(struct parasite_dump_thread *ti)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -159,6 +161,10 @@ static int dump_thread_common(struct parasite_dump_thread *ti)
|
|||
goto out;
|
||||
|
||||
ret = sys_prctl(PR_GET_PDEATHSIG, (unsigned long)&ti->pdeath_sig, 0, 0, 0);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = dump_creds(ti->creds);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -174,7 +180,7 @@ static int dump_misc(struct parasite_dump_misc *args)
|
|||
sys_umask(args->umask); /* never fails */
|
||||
args->dumpable = sys_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
|
||||
|
||||
return dump_thread_common(&args->ti);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_creds(struct parasite_dump_creds *args)
|
||||
|
|
@ -597,12 +603,12 @@ static noinline __used int noinline parasite_daemon(void *args)
|
|||
case PARASITE_CMD_DUMP_POSIX_TIMERS:
|
||||
ret = dump_posix_timers(args);
|
||||
break;
|
||||
case PARASITE_CMD_DUMP_THREAD:
|
||||
ret = dump_thread(args);
|
||||
break;
|
||||
case PARASITE_CMD_DUMP_MISC:
|
||||
ret = dump_misc(args);
|
||||
break;
|
||||
case PARASITE_CMD_DUMP_CREDS:
|
||||
ret = dump_creds(args);
|
||||
break;
|
||||
case PARASITE_CMD_DRAIN_FDS:
|
||||
ret = drain_fds(args);
|
||||
break;
|
||||
|
|
|
|||
50
proc_parse.c
50
proc_parse.c
|
|
@ -2221,13 +2221,53 @@ int aufs_parse(struct mount_info *new)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool proc_status_creds_eq(struct proc_status_creds *o1, struct proc_status_creds *o2)
|
||||
bool proc_status_creds_dumpable(struct proc_status_creds *parent,
|
||||
struct proc_status_creds *child)
|
||||
{
|
||||
/* FIXME: this is a little too strict, we should do semantic comparison
|
||||
* of seccomp filters instead of forcing them to be exactly identical.
|
||||
* It's not unsafe, though, so let's be lazy for now.
|
||||
const size_t size = sizeof(struct proc_status_creds) -
|
||||
offsetof(struct proc_status_creds, cap_inh);
|
||||
|
||||
/*
|
||||
* The comparision rules are the following
|
||||
*
|
||||
* - CAPs can be different
|
||||
* - seccomp filters should be passed via
|
||||
* semantic comparision (FIXME) but for
|
||||
* now we require them to be exactly
|
||||
* identical
|
||||
* - the rest of members must match
|
||||
*/
|
||||
return memcmp(o1, o2, sizeof(struct proc_status_creds)) == 0;
|
||||
|
||||
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)
|
||||
|
|
|
|||
2
ptrace.c
2
ptrace.c
|
|
@ -247,7 +247,7 @@ try_again:
|
|||
|
||||
**creds = cr;
|
||||
|
||||
} else if (!proc_status_creds_eq(*creds, &cr)) {
|
||||
} else if (!proc_status_creds_dumpable(*creds, &cr)) {
|
||||
pr_err("creds don't match %d %d\n", pid, ppid);
|
||||
goto err;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue