From: Cong Wang <xiyou.wangcong@gmail.com>
To: linux-kernel@vger.kernel.org
Cc: Andy Lutomirski <luto@amacapital.net>,
Kees Cook <kees@kernel.org>, Will Drewry <wad@chromium.org>,
Christian Brauner <brauner@kernel.org>
Subject: [RFC PATCH 0/9] seccomp: report the result of continued system calls
Date: Mon, 19 Oct 2026 17:11:15 -0700 [thread overview]
Message-ID: <20261020001124.974063-1-xiyou.wangcong@gmail.com> (raw)
From: Cong Wang <cwang@multikernel.io>
A seccomp user notification supervisor that continues a fork never
learns whether it succeeded or which task it created, so it cannot track
the tasks below it. Today the only way is ptrace fork events, which take
the one tracer slot from every task, may be forbidden by Yama or an LSM,
report nothing for a failed fork, interfere with signals and waitpid(),
and cost several stops per fork.
Sandlock [1] hits exactly this. It is an unprivileged process sandbox
built on seccomp user notification, with no cgroups to count tasks, so
it must see every fork itself and is left with ptrace and its problems.
This series lets the supervisor ask for the result of a continued
system call:
- SECCOMP_USER_NOTIF_FLAG_RESULT, given with CONTINUE, queues a one-way
result record on the listener once the call completes. It is read
with SECCOMP_IOCTL_NOTIF_RECV like any notification, flagged with
SECCOMP_NOTIF_FLAG_RESULT, and carries the id of the continued
notification and the call's return value.
- For the fork family, the record also carries the new task's pid in
the listener's pid namespace and its pidfs inode number, which is
never reused. The supervisor can open a pidfd for exactly that task
with open_by_handle_at(FD_PIDFS_ROOT, ...), unprivileged, or learn
that it has already exited. The record is queued before the new task
first runs and records are received before notifications, so the
supervisor always hears of a task before it hears from it.
- SECCOMP_USER_NOTIF_FLAG_HOLD, given with RESULT, reports the result
as a notification the supervisor must answer, and the task waits
until it does. For a fork, the new task does not run until then
either, so the supervisor can act on it before its first
instruction.
The interface is generic; only the fork family is supported so far.
Results are reported from task work on the way back to user space,
which needs nothing per system call. A successful fork is the one
exception: it is reported in kernel_clone() just before the child is
woken, which is the only point where both ordering and the child's
identity can be guaranteed. Every other system call is refused with
EINVAL, which is also what older kernels return, so the flags are easy
to probe and can be extended to more calls without new uAPI.
struct seccomp_notif keeps its size: the result shares a union with
struct seccomp_data, so the ioctl numbers and SECCOMP_GET_NOTIF_SIZES
are unchanged. The record is allocated by SEND and charged to the
supervisor, so the system call itself never allocates and a record can
never be lost after the point of no return.
Tested on x86_64 in QEMU with lockdep, KASAN and kmemleak: the full
seccomp_bpf selftest, the new tests as an unprivileged user, and fork
result records from an ia32 task.
[1] https://github.com/multikernel/sandlock
---
Cong Wang (9):
seccomp: add result records to the notification listener
seccomp: add __NR_seccomp_* aliases for the fork family
seccomp: report the result of a continued system call
seccomp: report the result of a continued fork
seccomp: factor out the user notification queue and wait
seccomp: add SECCOMP_USER_NOTIF_FLAG_HOLD
selftests/seccomp: test fork result records
selftests/seccomp: test held fork results
Documentation: seccomp: describe system call result records
.../userspace-api/seccomp_filter.rst | 117 +++-
arch/Kconfig | 6 +
arch/x86/include/asm/seccomp.h | 4 +
include/asm-generic/seccomp.h | 26 +
include/linux/seccomp.h | 4 +
include/linux/seccomp_types.h | 6 +
include/uapi/linux/seccomp.h | 57 +-
kernel/fork.c | 3 +
kernel/seccomp.c | 461 ++++++++++++--
tools/testing/selftests/seccomp/seccomp_bpf.c | 579 ++++++++++++++++++
10 files changed, 1218 insertions(+), 45 deletions(-)
base-commit: c03c1a14507dfaf8b856c2608f9ac216021fc222
--
2.43.0
next reply other threads:[~2026-10-20 0:11 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-20 0:11 Cong Wang [this message]
2026-10-20 0:11 ` [RFC PATCH 1/9] seccomp: add result records to the notification listener Cong Wang
2026-10-20 0:11 ` [RFC PATCH 2/9] seccomp: add __NR_seccomp_* aliases for the fork family Cong Wang
2026-10-20 0:11 ` [RFC PATCH 3/9] seccomp: report the result of a continued system call Cong Wang
2026-10-20 0:11 ` [RFC PATCH 4/9] seccomp: report the result of a continued fork Cong Wang
2026-10-20 0:11 ` [RFC PATCH 5/9] seccomp: factor out the user notification queue and wait Cong Wang
2026-10-20 0:11 ` [RFC PATCH 6/9] seccomp: add SECCOMP_USER_NOTIF_FLAG_HOLD Cong Wang
2026-10-20 0:11 ` [RFC PATCH 7/9] selftests/seccomp: test fork result records Cong Wang
2026-10-20 0:11 ` [RFC PATCH 8/9] selftests/seccomp: test held fork results Cong Wang
2026-10-20 0:11 ` [RFC PATCH 9/9] Documentation: seccomp: describe system call result records Cong Wang
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=20261020001124.974063-1-xiyou.wangcong@gmail.com \
--to=xiyou.wangcong@gmail.com \
--cc=brauner@kernel.org \
--cc=kees@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=luto@amacapital.net \
--cc=wad@chromium.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®