[CRIU] [PATCH] creds: dump -- Implement per-thread dump of credentials

Cyrill Gorcunov gorcunov at gmail.com
Mon Dec 21 09:17:59 PST 2015


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 at openvz.org>
---

Pavel, does v3 address all your comments? Take a look please.

 cr-dump.c                  |  40 ++++------------
 include/parasite-syscall.h |   1 +
 include/parasite.h         |  25 +++++-----
 include/proc_parse.h       |   3 +-
 parasite-syscall.c         | 116 +++++++++++++++++++++++++++------------------
 pie/parasite.c             |  14 ++++--
 proc_parse.c               |  50 +++++++++++++++++--
 ptrace.c                   |   2 +-
 8 files changed, 150 insertions(+), 101 deletions(-)

diff --git a/cr-dump.c b/cr-dump.c
index e76071cb1a1c..1ae36c8af5f1 100644
--- a/cr-dump.c
+++ b/cr-dump.c
@@ -510,24 +510,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;
@@ -673,7 +655,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) {
@@ -690,10 +674,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];
@@ -726,7 +710,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;
 
@@ -1326,18 +1310,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 64cec51b5017..57612df7478e 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 f884bb5baeb4..25bdd74aefea 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 f83bb7a3d0f7..2aff4f14db9f 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 bcd9922c4221..d38ee016ec1e 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 a39c035a7f71..37936d2b2c15 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 2eef21a8d169..648f1aaecc6a 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 3f24afb22736..510377e77e0a 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;
 	}
-- 
2.5.0



More information about the CRIU mailing list