diff --git a/cr-dump.c b/cr-dump.c index fabf20acd..cbc7676dc 100644 --- a/cr-dump.c +++ b/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); diff --git a/include/parasite-syscall.h b/include/parasite-syscall.h index 64cec51b5..57612df74 100644 --- a/include/parasite-syscall.h +++ b/include/parasite-syscall.h @@ -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); diff --git a/include/parasite.h b/include/parasite.h index f884bb5ba..25bdd74ae 100644 --- a/include/parasite.h +++ b/include/parasite.h @@ -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); diff --git a/include/proc_parse.h b/include/proc_parse.h index f83bb7a3d..2aff4f14d 100644 --- a/include/proc_parse.h +++ b/include/proc_parse.h @@ -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); diff --git a/parasite-syscall.c b/parasite-syscall.c index bcd9922c4..d38ee016e 100644 --- a/parasite-syscall.c +++ b/parasite-syscall.c @@ -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) { diff --git a/pie/parasite.c b/pie/parasite.c index a39c035a7..37936d2b2 100644 --- a/pie/parasite.c +++ b/pie/parasite.c @@ -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; diff --git a/proc_parse.c b/proc_parse.c index 2eef21a8d..648f1aaec 100644 --- a/proc_parse.c +++ b/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) diff --git a/ptrace.c b/ptrace.c index 3f24afb22..510377e77 100644 --- a/ptrace.c +++ b/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; }