[6/7] seccomp: Add engine to checkpoint per-thread seccomp chains

Submitted by Cyrill Gorcunov on April 6, 2018, 7:17 p.m.

Details

Message ID 20180406191708.11325-7-gorcunov@gmail.com
State Rejected
Series "seccomp, v2: Add support for per-thread c/r"
Headers show

Commit Message

Cyrill Gorcunov April 6, 2018, 7:17 p.m.
To checkpoint per-thread seccomp filters we need
a significant rework of a dumping code. The general
idea is the following:

 - Each thread is tracked by its tid inside global
   seccomp rbtree thus we can easily add entries
   there or lookup on demand.

 - When we collect threads into pstree entries we fetch
   its seccomp mode from procfs parsing routine and allocate
   a new entry inside rbtree to remember the seccomp mode.
   Note at this moment we're not dumping real filters yet
   (because filter data image is a single one for all consumers)

 - Once all tids are collected and our tree is complete we call for
   seccomp_collect_dump_filters helper which walks every pstree entry
   and iterate over each tid inside thread group calling
   seccomp_dump_thread, which in turn uses ptrace engine to fetch
   filters and keep this data in memory.

   To optimize data usage we figure out if we can use TSYNC flag
   on restore calling try_use_tsync helper: for TSYNC flag kernel
   automatically propagate filter to all threads, thus we need to
   compare all filters inside thread group for identity since there
   is no other way to figure out if user passed TSYNC flag when
   been creating filters.

  - Finally dump_seccomp_filters is called which does real write
    of seccomp filter data into an image file.

Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
---
 criu/cr-dump.c         |  19 +--
 criu/include/pstree.h  |   6 +-
 criu/include/seccomp.h |  40 ++++-
 criu/seccomp.c         | 392 +++++++++++++++++++++++++++++++++----------------
 criu/seize.c           |  77 +++-------
 5 files changed, 323 insertions(+), 211 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/cr-dump.c b/criu/cr-dump.c
index 4960a7b279d2..7b6f89e4e181 100644
--- a/criu/cr-dump.c
+++ b/criu/cr-dump.c
@@ -733,6 +733,8 @@  int dump_thread_core(int pid, CoreEntry *core, const struct parasite_dump_thread
 			tc->pdeath_sig = ti->pdeath_sig;
 		}
 	}
+	if (!ret)
+		ret = seccomp_dump_thread(pid, tc);
 
 	return ret;
 }
@@ -746,7 +748,6 @@  static int dump_task_core_all(struct parasite_ctl *ctl,
 	CoreEntry *core = item->core[0];
 	pid_t pid = item->pid->real;
 	int ret = -1;
-	struct proc_status_creds *creds;
 	struct parasite_dump_cgroup_args cgroup_args, *info = NULL;
 
 	BUILD_BUG_ON(sizeof(cgroup_args) < PARASITE_ARG_SIZE_MIN);
@@ -759,18 +760,6 @@  static int dump_task_core_all(struct parasite_ctl *ctl,
 	if (ret < 0)
 		goto err;
 
-	creds = dmpi(item)->pi_creds;
-	if (creds->s.seccomp_mode != SECCOMP_MODE_DISABLED) {
-		pr_info("got seccomp mode %d for %d\n", creds->s.seccomp_mode, vpid(item));
-		core->tc->has_old_seccomp_mode = true;
-		core->tc->old_seccomp_mode = creds->s.seccomp_mode;
-
-		if (creds->s.seccomp_mode == SECCOMP_MODE_FILTER) {
-			core->tc->has_old_seccomp_filter = true;
-			core->tc->old_seccomp_filter = creds->last_filter;
-		}
-	}
-
 	strlcpy((char *)core->tc->comm, stat->comm, TASK_COMM_LEN);
 	core->tc->flags = stat->flags;
 	core->tc->task_state = item->pid->state;
@@ -1614,6 +1603,7 @@  static int cr_pre_dump_finish(int ret)
 	}
 
 	free_pstree(root_item);
+	seccomp_free_entries();
 
 	if (irmap_predump_run()) {
 		ret = -1;
@@ -1823,6 +1813,7 @@  static int cr_dump_finish(int ret)
 			    TASK_ALIVE : opts.final_state);
 	timing_stop(TIME_FROZEN);
 	free_pstree(root_item);
+	seccomp_free_entries();
 	free_file_locks();
 	free_link_remaps();
 	free_aufs_branches();
@@ -1939,7 +1930,7 @@  int cr_dump_tasks(pid_t pid)
 	if (!glob_imgset)
 		goto err;
 
-	if (collect_seccomp_filters() < 0)
+	if (seccomp_collect_dump_filters() < 0)
 		goto err;
 
 	/* Errors handled later in detect_pid_reuse */
diff --git a/criu/include/pstree.h b/criu/include/pstree.h
index 08f25384f2c5..5696bc1b8e27 100644
--- a/criu/include/pstree.h
+++ b/criu/include/pstree.h
@@ -4,6 +4,7 @@ 
 #include "common/list.h"
 #include "common/lock.h"
 #include "pid.h"
+#include "xmalloc.h"
 #include "images/core.pb-c.h"
 
 /*
@@ -59,11 +60,6 @@  static inline struct rst_info *rsti(struct pstree_item *i)
 struct ns_id;
 struct dmp_info {
 	struct ns_id *netns;
-	/*
-	 * We keep the creds here so that we can compare creds while seizing
-	 * threads. Dumping tasks with different creds is not supported.
-	 */
-	struct proc_status_creds *pi_creds;
 	struct page_pipe *mem_pp;
 	struct parasite_ctl *parasite_ctl;
 };
diff --git a/criu/include/seccomp.h b/criu/include/seccomp.h
index 0791597fefd6..8e200124e1f8 100644
--- a/criu/include/seccomp.h
+++ b/criu/include/seccomp.h
@@ -29,15 +29,43 @@ 
 
 struct thread_restore_args;
 struct task_restore_args;
+struct pstree_item;
+struct rb_node;
 
-struct seccomp_info {
-	struct seccomp_info	*prev;
-	int			id;
-	SeccompFilter		filter;
+/*
+ * seccomp filters are bound to @current->seccomp.filter
+ * in the kernel, ie they are per thread structures.
+ *
+ * If filter is assigned then every subsequent call
+ * to fork() makes a copy of this @current->seccomp.filter
+ * pointer into child process.
+ *
+ * The thread group can share a filter if the filter
+ * is assigned with SECCOMP_FILTER_FLAG_TSYNC on group
+ * which has no filters yet.
+ */
+struct seccomp_filter_chain {
+	struct seccomp_filter_chain	*prev;
+	SeccompFilter			filter;
 };
 
-extern int collect_seccomp_filters(void);
-extern int prepare_seccomp_filters(void);
+struct seccomp_entry {
+	struct rb_node			node;
+	struct seccomp_entry		*next;
+	pid_t				tid_real;
+	size_t				img_filter_pos;
+	unsigned int			mode;
+
+	struct seccomp_filter_chain	*chain;
+	size_t				nr_chains;
+};
+
+extern struct seccomp_entry *seccomp_lookup(pid_t tid_real, bool create, bool mandatory);
+#define seccomp_find_entry(tid_real) seccomp_lookup(tid_real, false, true)
+extern int seccomp_collect_entry(pid_t tid_real, unsigned int mode);
+extern void seccomp_free_entries(void);
+extern int seccomp_dump_thread(pid_t tid_real, ThreadCoreEntry *thread_core);
+extern int seccomp_collect_dump_filters(void);
 
 extern int seccomp_read_image(void);
 extern int seccomp_prepare_threads(struct pstree_item *item, struct task_restore_args *ta);
diff --git a/criu/seccomp.c b/criu/seccomp.c
index 8da5a2932e83..96a5a62314d8 100644
--- a/criu/seccomp.c
+++ b/criu/seccomp.c
@@ -21,67 +21,133 @@ 
 #undef	LOG_PREFIX
 #define LOG_PREFIX "seccomp: "
 
+static struct rb_root seccomp_tid_rb_root = RB_ROOT;
+static struct seccomp_entry *seccomp_tid_entry_root;
+
 static SeccompEntry *seccomp_img_entry;
 
-/* populated on dump during collect_seccomp_filters() */
-static int next_filter_id = 0;
-static struct seccomp_info **filters = NULL;
+struct seccomp_entry *seccomp_lookup(pid_t tid_real, bool create, bool mandatory)
+{
+	struct seccomp_entry *entry = NULL;
+
+	struct rb_node *node = seccomp_tid_rb_root.rb_node;
+	struct rb_node **new = &seccomp_tid_rb_root.rb_node;
+	struct rb_node *parent = NULL;
+
+	while (node) {
+		struct seccomp_entry *this = rb_entry(node, struct seccomp_entry, node);
+
+		parent = *new;
+		if (tid_real < this->tid_real)
+			node = node->rb_left, new = &((*new)->rb_left);
+		else if (tid_real > this->tid_real)
+			node = node->rb_right, new = &((*new)->rb_right);
+		else
+			return this;
+	}
+
+	if (create) {
+		entry = xzalloc(sizeof(*entry));
+		if (!entry)
+			return NULL;
+		rb_init_node(&entry->node);
+		entry->tid_real	= tid_real;
+
+		entry->next = seccomp_tid_entry_root, seccomp_tid_entry_root = entry;
+		rb_link_and_balance(&seccomp_tid_rb_root, &entry->node, parent, new);
+	} else {
+		if (mandatory)
+			pr_err("Can't find entry on tid_real %d\n", tid_real);
+	}
+
+	return entry;
+}
 
-static struct seccomp_info *find_inherited(struct pstree_item *parent,
-					   struct sock_filter *filter,
-					   int len, struct seccomp_metadata *meta)
+int seccomp_collect_entry(pid_t tid_real, unsigned int mode)
 {
-	struct seccomp_info *info;
+	struct seccomp_entry *entry;
 
-	/* if we have no filters yet, this one has no parent */
-	if (!filters)
-		return NULL;
+	entry = seccomp_lookup(tid_real, true, false);
+	if (!entry) {
+		pr_err("Can't create entry on tid_real %d\n", tid_real);
+		return -1;
+	}
+	entry->mode = mode;
 
-	for (info = filters[dmpi(parent)->pi_creds->last_filter]; info; info = info->prev) {
+	pr_debug("Collected tid_real %d mode %#x\n", tid_real, mode);
+	return 0;
+}
 
-		if (len != info->filter.filter.len)
-			continue;
-		if (!!meta ^ !!info->filter.has_flags)
-			continue;
-		if (info->filter.has_flags && meta) {
-			if (info->filter.flags != meta->flags)
-				continue;
+static void seccomp_free_chain(struct seccomp_entry *entry)
+{
+	struct seccomp_filter_chain *chain, *prev;
+
+	for (chain = entry->chain; chain; chain = prev) {
+		prev = chain->prev;
+
+		xfree(chain->filter.filter.data);
+		xfree(chain);
+	}
+
+	entry->nr_chains = 0;
+	entry->chain = NULL;
+}
+
+void seccomp_free_entries(void)
+{
+	struct seccomp_entry *entry, *next;
+
+	for (entry = seccomp_tid_entry_root; entry; entry = next) {
+		next = entry->next;
+		seccomp_free_chain(entry);
+		xfree(entry);
+	}
+
+	seccomp_tid_rb_root = RB_ROOT;
+	seccomp_tid_entry_root = NULL;
+}
+
+int seccomp_dump_thread(pid_t tid_real, ThreadCoreEntry *thread_core)
+{
+	struct seccomp_entry *entry = seccomp_find_entry(tid_real);
+	if (!entry) {
+		pr_err("Can't dump thread core on tid_real %d\n", tid_real);
+		return -1;
+	}
+
+	if (entry->mode != SECCOMP_MODE_DISABLED) {
+		thread_core->has_seccomp_mode = true;
+		thread_core->seccomp_mode = entry->mode;
+
+		if (entry->mode == SECCOMP_MODE_FILTER) {
+			thread_core->has_seccomp_filter = true;
+			thread_core->seccomp_filter = entry->img_filter_pos;
 		}
-		if (!memcmp(filter, info->filter.filter.data, len))
-			return info;
 	}
 
-	return NULL;
+	return 0;
 }
 
-static int collect_filter_for_pstree(struct pstree_item *item)
+static int collect_filter(struct seccomp_entry *entry)
 {
 	struct seccomp_metadata meta_buf, *meta = &meta_buf;
-	struct seccomp_info *infos = NULL, *cursor;
-	int info_count, i, ret = -1;
+	struct seccomp_filter_chain *chain, *prev;
 	struct sock_filter buf[BPF_MAXINSNS];
-	void *m;
+	size_t i;
+	int len;
 
-	if (item->pid->state == TASK_DEAD ||
-	    dmpi(item)->pi_creds->s.seccomp_mode != SECCOMP_MODE_FILTER)
+	if (entry->mode != SECCOMP_MODE_FILTER)
 		return 0;
 
 	for (i = 0; true; i++) {
-		int len;
-		struct seccomp_info *info, *inherited = NULL;
-
-		len = ptrace(PTRACE_SECCOMP_GET_FILTER, item->pid->real, i, buf);
+		len = ptrace(PTRACE_SECCOMP_GET_FILTER, entry->tid_real, i, buf);
 		if (len < 0) {
 			if (errno == ENOENT) {
-				/* end of the search */
-				BUG_ON(i == 0);
-				goto save_infos;
-			} else if (errno == EINVAL) {
-				pr_err("dumping seccomp infos not supported\n");
-				goto out;
+				break;
 			} else {
-				pr_perror("couldn't dump seccomp filter");
-				goto out;
+				pr_perror("Can't fetch filter on tid_real %d i %zu",
+					  entry->tid_real, i);
+				return -1;
 			}
 		}
 
@@ -91,142 +157,212 @@  static int collect_filter_for_pstree(struct pstree_item *item)
 		meta->flags = 0;
 		meta->filter_off = i;
 
-		if (ptrace(PTRACE_SECCOMP_GET_METADATA, item->pid->real, sizeof(meta), meta) < 0) {
+		if (ptrace(PTRACE_SECCOMP_GET_METADATA, entry->tid_real, sizeof(meta), meta) < 0) {
 			if (errno == EIO) {
-				/*
-				 * No PTRACE_SECCOMP_GET_METADATA support in
-				 * kernel detected, thus simply ignore. Moving
-				 * it into kerndat is preferred but not
-				 * required.
-				 */
 				meta = NULL;
 			} else {
-				pr_perror("couldn't fetch seccomp metadata: pid %d pos %d",
-					  item->pid->real, i);
-				goto out;
+				pr_perror("Can't fetch seccomp metadataon tid_real %d pos %zu",
+					  entry->tid_real, i);
+				return -1;
 			}
 		}
 
-		inherited = find_inherited(item->parent, buf, len, meta);
-		if (inherited) {
-			bool found = false;
-
-			/* Small sanity check: if infos is already populated,
-			 * we should have inherited that filter too. */
-			for (cursor = infos; cursor; cursor = cursor->prev) {
-				if (inherited->prev== cursor) {
-					found = true;
-					break;
-				}
-			}
+		chain = xzalloc(sizeof(*chain));
+		if (!chain)
+			return -1;
 
-			BUG_ON(!found);
+		seccomp_filter__init(&chain->filter);
 
-			infos = inherited;
-			continue;
+		chain->filter.has_flags = true;
+		chain->filter.flags = 0;
+
+		chain->filter.filter.len = len * sizeof(struct sock_filter);
+		chain->filter.filter.data = xmalloc(chain->filter.filter.len);
+		if (!chain->filter.filter.data) {
+			xfree(chain);
+			return -1;
 		}
 
-		info = xmalloc(sizeof(*info));
-		if (!info)
-			goto out;
-		seccomp_filter__init(&info->filter);
+		memcpy(chain->filter.filter.data, buf, chain->filter.filter.len);
 
-		if (meta) {
-			info->filter.has_flags = true;
-			info->filter.flags = meta->flags;
-		}
+		if (meta)
+			chain->filter.flags |= meta->flags;
 
-		info->filter.filter.len = len * sizeof(struct sock_filter);
-		info->filter.filter.data = xmalloc(info->filter.filter.len);
-		if (!info->filter.filter.data) {
-			xfree(info);
-			goto out;
-		}
+		prev = entry->chain, entry->chain = chain, chain->prev = prev;
+		entry->nr_chains++;
+	}
 
-		memcpy(info->filter.filter.data, buf, info->filter.filter.len);
+	return 0;
+}
 
-		info->prev = infos;
-		infos = info;
+/*
+ * When filter is being set up with SECCOMP_FILTER_FLAG_TSYNC then all
+ * threads share same filters chain. Still without kernel support we
+ * don't know if the chains are indeed were propagated by the flag above
+ * or application installed identical chains manually.
+ *
+ * Thus we do a trick: if all threads are sharing chains we just drop
+ * all ones except on a leader and assign SECCOMP_FILTER_FLAG_TSYNC there.
+ * The rationale is simple: if application is using tsync it always can
+ * assign new not-tsync filters after, but in reverse if we don't provide
+ * tsync on restore the further calls with tsync will fail later.
+ *
+ * Proper fix needs some support from kernel side (presumably kcmp mode).
+ */
+static void try_use_tsync(struct seccomp_entry *leader, struct pstree_item *item)
+{
+	struct seccomp_filter_chain *chain_a, *chain_b;
+	struct seccomp_entry *entry;
+	size_t i, j;
+
+	if (leader->mode != SECCOMP_MODE_FILTER)
+		return;
+
+	for (i = 0; i < item->nr_threads; i++) {
+		entry = seccomp_find_entry(item->threads[i]->real);
+		BUG_ON(!entry);
+
+		if (entry == leader)
+			continue;
+
+		if (entry->mode != leader->mode ||
+		    entry->nr_chains != leader->nr_chains)
+			return;
+
+		chain_a = leader->chain;
+		chain_b = entry->chain;
+
+		for (j = 0; j < leader->nr_chains; j++) {
+			BUG_ON((!chain_a || !chain_b));
+
+			if (chain_a->filter.filter.len !=
+			    chain_b->filter.filter.len)
+				return;
+
+			if (memcmp(chain_a->filter.filter.data,
+				   chain_b->filter.filter.data,
+				   chain_a->filter.filter.len))
+				return;
+
+			chain_a = chain_a->prev;
+			chain_b = chain_b->prev;
+		}
 	}
 
-save_infos:
-	info_count = i;
+	/* OK, so threads can be restored with tsync */
+	pr_debug("Use SECCOMP_FILTER_FLAG_TSYNC for tid_real %d\n",
+		 leader->tid_real);
+
+	for (chain_a = leader->chain; chain_a; chain_a = chain_a->prev)
+		chain_a->filter.flags |= SECCOMP_FILTER_FLAG_TSYNC;
+
+	for (i = 0; i < item->nr_threads; i++) {
+		entry = seccomp_find_entry(item->threads[i]->real);
+		BUG_ON(!entry);
+
+		if (entry == leader)
+			continue;
 
-	m = xrealloc(filters, sizeof(*filters) * (next_filter_id + info_count));
-	if (!m)
-		goto out;
-	filters = m;
+		pr_debug("\t Disable filter on tid_rea %d, will be propagated\n",
+			 entry->tid_real);
 
-	for (cursor = infos, i = info_count + next_filter_id - 1;
-	     i >= next_filter_id; i--) {
-		BUG_ON(!cursor);
-		cursor->id = i;
-		filters[i] = cursor;
-		cursor = cursor->prev;
+		entry->mode = SECCOMP_MODE_DISABLED;
+		seccomp_free_chain(entry);
 	}
+}
 
-	next_filter_id += info_count;
+static int collect_filters(struct pstree_item *item)
+{
+	struct seccomp_entry *leader, *entry;
+	size_t i;
 
-	dmpi(item)->pi_creds->last_filter = infos->id;
+	if (item->pid->state == TASK_DEAD)
+		return 0;
 
-	/* Don't free the part of the tree we just successfully acquired */
-	infos = NULL;
-	ret = 0;
-out:
-	while (infos) {
-		struct seccomp_info *freeme = infos;
-		infos = infos->prev;
-		xfree(freeme->filter.filter.data);
-		xfree(freeme);
+	leader = seccomp_find_entry(item->pid->real);
+	if (!leader) {
+		pr_err("Can't collect filter on leader tid_real %d\n",
+		       item->pid->real);
+		return -1;
 	}
 
-	return ret;
+	for (i = 0; i < item->nr_threads; i++) {
+		entry = seccomp_find_entry(item->threads[i]->real);
+		if (!leader) {
+			pr_err("Can't collect filter on tid_real %d\n",
+			       item->pid->real);
+			return -1;
+		}
+
+		if (collect_filter(entry))
+			return -1;
+	}
+
+	try_use_tsync(leader, item);
+	return 0;
 }
 
 static int dump_seccomp_filters(void)
 {
 	SeccompEntry se = SECCOMP_ENTRY__INIT;
-	int ret = -1, i;
+	struct seccomp_filter_chain *chain;
+	struct seccomp_entry *entry;
+	size_t img_filter_pos = 0, nr_chains = 0;
+	struct rb_node *node;
+	int ret;
 
-	/* If we didn't collect any filters, don't create a seccomp image at all. */
-	if (next_filter_id == 0)
-		return 0;
+	for (node = rb_first(&seccomp_tid_rb_root); node; node = rb_next(node)) {
+		entry = rb_entry(node, struct seccomp_entry, node);
+		nr_chains += entry->nr_chains;
+	}
 
-	se.seccomp_filters = xzalloc(sizeof(*se.seccomp_filters) * next_filter_id);
-	if (!se.seccomp_filters)
-		return -1;
+	se.n_seccomp_filters = nr_chains;
+	if (nr_chains) {
+		se.seccomp_filters = xmalloc(sizeof(*se.seccomp_filters) * nr_chains);
+		if (!se.seccomp_filters)
+			return -1;
+	}
 
-	se.n_seccomp_filters = next_filter_id;
+	for (node = rb_first(&seccomp_tid_rb_root); node; node = rb_next(node)) {
+		entry = rb_entry(node, struct seccomp_entry, node);
 
-	for (i = 0; i < next_filter_id; i++) {
-		SeccompFilter *sf;
-		struct seccomp_info *cur = filters[i];
+		if (!entry->nr_chains)
+			continue;
 
-		sf = se.seccomp_filters[cur->id] = &cur->filter;
-		if (cur->prev) {
-			sf->has_prev = true;
-			sf->prev = cur->prev->id;
+		for (chain = entry->chain; chain; chain = chain->prev) {
+			if (img_filter_pos >= nr_chains) {
+				pr_err("Unexpected position %zu > %zu\n",
+				       img_filter_pos, nr_chains);
+				xfree(se.seccomp_filters);
+				return -1;
+			}
+
+			se.seccomp_filters[img_filter_pos] = &chain->filter;
+			if (chain != entry->chain) {
+				chain->filter.has_prev = true;
+				chain->filter.prev = img_filter_pos - 1;
+			}
+			img_filter_pos++;
 		}
+
+		entry->img_filter_pos = img_filter_pos - 1;
 	}
 
 	ret = pb_write_one(img_from_set(glob_imgset, CR_FD_SECCOMP), &se, PB_SECCOMP);
 
 	xfree(se.seccomp_filters);
 
-	for (i = 0; i < next_filter_id; i++) {
-		struct seccomp_info *freeme = filters[i];
-
-		xfree(freeme->filter.filter.data);
-		xfree(freeme);
+	for (node = rb_first(&seccomp_tid_rb_root); node; node = rb_next(node)) {
+		entry = rb_entry(node, struct seccomp_entry, node);
+		seccomp_free_chain(entry);
 	}
-	xfree(filters);
 
 	return ret;
 }
 
-int collect_seccomp_filters(void)
+int seccomp_collect_dump_filters(void)
 {
-	if (preorder_pstree_traversal(root_item, collect_filter_for_pstree) < 0)
+	if (preorder_pstree_traversal(root_item, collect_filters) < 0)
 		return -1;
 
 	if (dump_seccomp_filters())
diff --git a/criu/seize.c b/criu/seize.c
index e1780c7ec063..38f9419022be 100644
--- a/criu/seize.c
+++ b/criu/seize.c
@@ -17,6 +17,7 @@ 
 #include "criu-log.h"
 #include <compel/ptrace.h>
 #include "proc_parse.h"
+#include "seccomp.h"
 #include "seize.h"
 #include "stats.h"
 #include "xmalloc.h"
@@ -463,7 +464,7 @@  static int collect_children(struct pstree_item *item)
 	nr_inprogress = 0;
 	for (i = 0; i < nr_children; i++) {
 		struct pstree_item *c;
-		struct proc_status_creds *creds;
+		struct proc_status_creds creds;
 		pid_t pid = ch[i];
 
 		/* Is it already frozen? */
@@ -489,13 +490,7 @@  static int collect_children(struct pstree_item *item)
 			/* fails when meets a zombie */
 			compel_interrupt_task(pid);
 
-		creds = xzalloc(sizeof(*creds));
-		if (!creds) {
-			ret = -1;
-			goto free;
-		}
-
-		ret = compel_wait_task(pid, item->pid->real, parse_task_status, NULL, &creds->s, c);
+		ret = compel_wait_task(pid, item->pid->real, parse_task_status, NULL, &creds.s, c);
 		if (ret < 0) {
 			/*
 			 * Here is a race window between parse_children() and seize(),
@@ -506,7 +501,6 @@  static int collect_children(struct pstree_item *item)
 			 */
 			ret = 0;
 			free_pstree_item(c);
-			xfree(creds);
 			continue;
 		}
 
@@ -515,12 +509,15 @@  static int collect_children(struct pstree_item *item)
 		else
 			processes_to_wait--;
 
-		dmpi(c)->pi_creds = creds;
 		c->pid->real = pid;
 		c->parent = item;
 		c->pid->state = ret;
 		list_add_tail(&c->sibling, &item->children);
 
+		ret = seccomp_collect_entry(pid, creds.s.seccomp_mode);
+		if (ret < 0)
+			goto free;
+
 		/* Here is a recursive call (Depth-first search) */
 		ret = collect_task(c);
 		if (ret < 0)
@@ -631,46 +628,6 @@  static inline bool thread_collected(struct pstree_item *i, pid_t tid)
 	return false;
 }
 
-static bool creds_dumpable(struct proc_status_creds *parent,
-				struct proc_status_creds *child)
-{
-	/*
-	 *  - seccomp filters should be passed via
-	 *    semantic comparison (FIXME) but for
-	 *    now we require them to be exactly
-	 *    identical
-	 */
-	if (parent->s.seccomp_mode != child->s.seccomp_mode ||
-	    parent->last_filter != child->last_filter) {
-		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"
-				 "  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->s.state, child->s.state,
-				 parent->s.ppid, child->s.ppid,
-				 parent->s.shdpnd, child->s.shdpnd,
-				 parent->s.seccomp_mode, child->s.seccomp_mode,
-				 parent->last_filter, child->last_filter);
-		}
-		return false;
-	}
-
-	return true;
-}
-
 static int parse_thread_status(int pid, struct seize_task_status *ss, void *thread)
 {
 	return parse_pid_status(pid, ss, NULL, thread);
@@ -678,10 +635,15 @@  static int parse_thread_status(int pid, struct seize_task_status *ss, void *thre
 
 static int collect_threads(struct pstree_item *item)
 {
+	struct seccomp_entry *task_seccomp_entry;
 	struct pid **threads = NULL;
 	int nr_threads = 0, i = 0, j, ret, nr_inprogress, nr_stopped = 0;
 	int level = item->pid->level, id;
 
+	task_seccomp_entry = seccomp_find_entry(item->pid->real);
+	if (!task_seccomp_entry)
+		goto err;
+
 	ret = parse_threads(item->pid->real, &threads, &nr_threads);
 	if (ret < 0)
 		goto err;
@@ -762,7 +724,7 @@  static int collect_threads(struct pstree_item *item)
 			goto err;
 		}
 
-		if (!creds_dumpable(dmpi(item)->pi_creds, &t_creds))
+		if (seccomp_collect_entry(pid, t_creds.s.seccomp_mode))
 			goto err;
 
 		if (ret == TASK_STOPPED) {
@@ -855,7 +817,7 @@  int collect_pstree(void)
 {
 	pid_t pid = root_item->pid->real;
 	int ret = -1;
-	struct proc_status_creds *creds;
+	struct proc_status_creds creds;
 
 	timing_start(TIME_FREEZING);
 
@@ -874,11 +836,7 @@  int collect_pstree(void)
 		goto err;
 	}
 
-	creds = xzalloc(sizeof(*creds));
-	if (!creds)
-		goto err;
-
-	ret = compel_wait_task(pid, -1, parse_task_status, NULL, &creds->s, root_item);
+	ret = compel_wait_task(pid, -1, parse_task_status, NULL, &creds.s, root_item);
 	if (ret < 0)
 		goto err;
 
@@ -889,7 +847,10 @@  int collect_pstree(void)
 
 	pr_info("Seized task %d, state %d\n", pid, ret);
 	root_item->pid->state = ret;
-	dmpi(root_item)->pi_creds = creds;
+
+	ret = seccomp_collect_entry(pid, creds.s.seccomp_mode);
+	if (ret < 0)
+		goto err;
 
 	ret = collect_task(root_item);
 	if (ret < 0)