From: Mark Hatle <mark.hatle@kernel.crashing.org>
To: yocto-patches@lists.yoctoproject.org
Subject: [pseudo][PATCH 4/6] test-seccomp: Check that seccomp is never really enabled
Date: Thu, 12 Nov 2026 10:19:56 -0600 [thread overview]
Message-ID: <1794500398-31706-5-git-send-email-mark.hatle@kernel.crashing.org> (raw)
In-Reply-To: <1794500398-31706-1-git-send-email-mark.hatle@kernel.crashing.org>
From: Mark Hatle <mark.hatle@amd.com>
Ask for seccomp strict and filter mode, by prctl() and by the seccomp()
system call, each in a child process, and check the call succeeds and
that /proc/self/status still reports Seccomp: 0.
AI-Generated: Uses GitHub Copilot
Signed-off-by: Mark Hatle <mark.hatle@amd.com>
---
test/test-seccomp.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++
test/test-seccomp.sh | 12 +++++
2 files changed, 133 insertions(+)
create mode 100644 test/test-seccomp.c
create mode 100755 test/test-seccomp.sh
diff --git a/test/test-seccomp.c b/test/test-seccomp.c
new file mode 100644
index 0000000..b600716
--- /dev/null
+++ b/test/test-seccomp.c
@@ -0,0 +1,121 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * A seccomp sandbox restricts libpseudo along with the program, so pseudo
+ * pretends to install one. Check that every way of asking for one, by
+ * prctl() and by the seccomp() system call, in strict and filter mode,
+ * returns success without leaving the process under seccomp. Strict mode
+ * would kill the process at the next call other than read(), write() or
+ * exit(), so each case runs in a child of its own.
+ */
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+
+/* matches test-openat2-syscall: the shell wrapper turns 77 into "skipped" */
+#define SKIP 77
+
+/* Seccomp: field of /proc/self/status, or -1 if there is none */
+static int seccomp_mode(void) {
+ char line[256];
+ int mode = -1;
+ FILE *f;
+
+ f = fopen("/proc/self/status", "r");
+ if (!f)
+ return -1;
+ while (fgets(line, sizeof(line), f))
+ if (!strncmp(line, "Seccomp:", 8))
+ mode = atoi(line + 8);
+ fclose(f);
+ return mode;
+}
+
+static struct sock_filter allow_all[] = {
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+};
+static struct sock_fprog prog = {
+ .len = sizeof(allow_all) / sizeof(allow_all[0]),
+ .filter = allow_all,
+};
+
+static long request(const char *how) {
+ if (!strcmp(how, "prctl-strict"))
+ return prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, 0, 0, 0);
+ if (!strcmp(how, "prctl-filter"))
+ return prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+ if (!strcmp(how, "syscall-strict"))
+ return syscall(SYS_seccomp, SECCOMP_SET_MODE_STRICT, 0, NULL);
+ if (!strcmp(how, "syscall-filter"))
+ return syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog);
+ errno = EINVAL;
+ return -1;
+}
+
+static int check(const char *how) {
+ int status;
+ pid_t pid;
+
+ fflush(NULL);
+ pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ return 1;
+ }
+ if (pid == 0) {
+ long rc;
+ int mode;
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
+ perror("prctl(PR_SET_NO_NEW_PRIVS)");
+ _exit(1);
+ }
+ rc = request(how);
+ if (rc != 0) {
+ fprintf(stderr, "%s: returned %ld (%s)\n", how, rc,
+ rc == -1 ? strerror(errno) : "?");
+ _exit(1);
+ }
+ mode = seccomp_mode();
+ if (mode != 0) {
+ fprintf(stderr, "%s: Seccomp: %d after the call\n", how, mode);
+ _exit(1);
+ }
+ _exit(0);
+ }
+ if (waitpid(pid, &status, 0) == -1) {
+ perror("waitpid");
+ return 1;
+ }
+ if (WIFSIGNALED(status)) {
+ fprintf(stderr, "%s: killed by signal %d\n", how, WTERMSIG(status));
+ return 1;
+ }
+ return WEXITSTATUS(status) != 0;
+}
+
+int main(void) {
+ static const char *hows[] = {
+ "prctl-strict", "prctl-filter", "syscall-strict", "syscall-filter",
+ };
+ size_t i;
+ int failed = 0;
+
+ if (seccomp_mode() == -1) {
+ fprintf(stderr, "no Seccomp: in /proc/self/status\n");
+ return SKIP;
+ }
+ for (i = 0; i < sizeof(hows) / sizeof(hows[0]); ++i)
+ failed |= check(hows[i]);
+ return failed;
+}
diff --git a/test/test-seccomp.sh b/test/test-seccomp.sh
new file mode 100755
index 0000000..396e314
--- /dev/null
+++ b/test/test-seccomp.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+#
+
+# rc 77 means the kernel doesn't report seccomp status, so we're skipped
+./test/test-seccomp
+case $? in
+0) exit 0 ;;
+77) exit 255 ;;
+*) exit 1 ;;
+esac
--
1.8.3.1
next prev parent reply other threads:[~2026-11-12 16:20 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-12 16:19 [pseudo][PATCH 0/6] Add landlock support, fix seccomp Mark Hatle
2026-11-12 16:19 ` [pseudo][PATCH 1/6] test-landlock: Add a Landlock sandbox test Mark Hatle
2026-11-12 16:19 ` [pseudo][PATCH 2/6] pseudo_client: Connect to the server without changing directory Mark Hatle
2026-11-12 16:19 ` [pseudo][PATCH 3/6] ports/linux/pseudo_wrappers: Report Landlock as unsupported Mark Hatle
2026-11-12 16:19 ` Mark Hatle [this message]
2026-11-12 16:19 ` [pseudo][PATCH 5/6] ports/linux/pseudo_wrappers: Fake seccomp filters set by prctl() Mark Hatle
2026-11-12 16:19 ` [pseudo][PATCH 6/6] ports/linux/pseudo_wrappers: Fake seccomp strict mode in syscall() Mark Hatle
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1794500398-31706-5-git-send-email-mark.hatle@kernel.crashing.org \
--to=mark.hatle@kernel.crashing.org \
--cc=yocto-patches@lists.yoctoproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.