[PATCH 6/6] [DEBUG] seccomp: Report bitmap coverage ranges

From: Kees Cook
Date: Wed Sep 23 2020 - 19:30:09 EST


This is what I've been using to explore actual bitmap results for
real-world filters...

Signed-off-by: Kees Cook <keescook@xxxxxxxxxxxx>
---
kernel/seccomp.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)

diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 9921f6f39d12..1a0595d7f8ef 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -835,6 +835,85 @@ static void seccomp_update_bitmap(struct seccomp_filter *filter,
}
}

+static void __report_bitmap(const char *arch, u32 ret, int start, int finish)
+{
+ int gap;
+ char *name;
+
+ if (finish == -1)
+ return;
+
+ switch (ret) {
+ case UINT_MAX:
+ name = "filter";
+ break;
+ case SECCOMP_RET_ALLOW:
+ name = "SECCOMP_RET_ALLOW";
+ break;
+ case SECCOMP_RET_KILL_PROCESS:
+ name = "SECCOMP_RET_KILL_PROCESS";
+ break;
+ case SECCOMP_RET_KILL_THREAD:
+ name = "SECCOMP_RET_KILL_THREAD";
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ name = "unknown";
+ break;
+ }
+
+ gap = 0;
+ if (start < 100)
+ gap++;
+ if (start < 10)
+ gap++;
+ if (finish < 100)
+ gap++;
+ if (finish < 10)
+ gap++;
+
+ if (start == finish)
+ pr_info("%s %3d: %s\n", arch, start, name);
+ else if (start + 1 == finish)
+ pr_info("%s %*s%d,%d: %s\n", arch, gap, "", start, finish, name);
+ else
+ pr_info("%s %*s%d-%d: %s\n", arch, gap, "", start, finish, name);
+}
+
+static void report_bitmap(struct seccomp_bitmaps *bitmaps, const char *arch)
+{
+ u32 nr;
+ int start = 0, finish = -1;
+ u32 ret = UINT_MAX;
+ struct report_states {
+ unsigned long *bitmap;
+ u32 ret;
+ } states[] = {
+ { .bitmap = bitmaps->allow, .ret = SECCOMP_RET_ALLOW, },
+ { .bitmap = bitmaps->kill_process, .ret = SECCOMP_RET_KILL_PROCESS, },
+ { .bitmap = bitmaps->kill_thread, .ret = SECCOMP_RET_KILL_THREAD, },
+ { .bitmap = NULL, .ret = UINT_MAX, },
+ };
+
+ for (nr = 0; nr < NR_syscalls; nr++) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(states); i++) {
+ if (!states[i].bitmap || test_bit(nr, states[i].bitmap)) {
+ if (ret != states[i].ret) {
+ __report_bitmap(arch, ret, start, finish);
+ ret = states[i].ret;
+ start = nr;
+ }
+ finish = nr;
+ break;
+ }
+ }
+ }
+ if (start != nr)
+ __report_bitmap(arch, ret, start, finish);
+}
+
static void seccomp_update_bitmaps(struct seccomp_filter *filter,
void *pagepair)
{
@@ -849,6 +928,23 @@ static void seccomp_update_bitmaps(struct seccomp_filter *filter,
SECCOMP_MULTIPLEXED_SYSCALL_TABLE_MASK,
&current->seccomp.multiplex);
#endif
+ if (strncmp(current->comm, "test-", 5) == 0 ||
+ strcmp(current->comm, "seccomp_bpf") == 0 ||
+ /*
+ * Why are systemd's process names head-truncated to 8 bytes
+ * and wrapped in parens!?
+ */
+ (current->comm[0] == '(' && strrchr(current->comm, ')') != NULL)) {
+ pr_info("reporting syscall bitmap usage for %d (%s):\n",
+ task_pid_nr(current), current->comm);
+ report_bitmap(&current->seccomp.native, "native");
+#ifdef CONFIG_COMPAT
+ report_bitmap(&current->seccomp.compat, "compat");
+#endif
+#ifdef SECCOMP_MULTIPLEXED_SYSCALL_TABLE_ARCH
+ report_bitmap(&current->seccomp.multiplex, "multiplex");
+#endif
+ }
}
#else
static void seccomp_update_bitmaps(struct seccomp_filter *filter,
@@ -908,6 +1004,10 @@ static long seccomp_attach_filter(unsigned int flags,
filter->prev = current->seccomp.filter;
current->seccomp.filter = filter;
atomic_inc(&current->seccomp.filter_count);
+ if (atomic_read(&current->seccomp.filter_count) > 10)
+ pr_info("%d filters: %d (%s)\n",
+ atomic_read(&current->seccomp.filter_count),
+ task_pid_nr(current), current->comm);

/* Evaluate filter for new known-outcome syscalls */
seccomp_update_bitmaps(filter, pagepair);
@@ -2419,6 +2519,21 @@ static int __init seccomp_sysctl_init(void)
pr_warn("sysctl registration failed\n");
else
kmemleak_not_leak(hdr);
+#ifndef SECCOMP_ARCH
+ pr_info("arch lacks support for constant action bitmaps\n");
+#else
+ pr_info("NR_syscalls: %d\n", NR_syscalls);
+ pr_info("arch: 0x%x\n", SECCOMP_ARCH);
+#ifdef CONFIG_COMPAT
+ pr_info("compat arch: 0x%x\n", SECCOMP_ARCH_COMPAT);
+#endif
+#ifdef SECCOMP_MULTIPLEXED_SYSCALL_TABLE_ARCH
+ pr_info("multiplex arch: 0x%x (mask: 0x%x)\n",
+ SECCOMP_MULTIPLEXED_SYSCALL_TABLE_ARCH,
+ SECCOMP_MULTIPLEXED_SYSCALL_TABLE_MASK);
+#endif
+#endif
+ pr_info("sizeof(struct seccomp_bitmaps): %zu\n", sizeof(struct seccomp_bitmaps));

return 0;
}
--
2.25.1