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 9/9] Documentation: seccomp: describe system call result records
Date: Mon, 19 Oct 2026 17:11:24 -0700 [thread overview]
Message-ID: <20261020001124.974063-10-xiyou.wangcong@gmail.com> (raw)
In-Reply-To: <20261020001124.974063-1-xiyou.wangcong@gmail.com>
From: Cong Wang <cwang@multikernel.io>
Document SECCOMP_USER_NOTIF_FLAG_RESULT and SECCOMP_USER_NOTIF_FLAG_HOLD:
the record they produce, what its return value means after a signal,
what the fork family adds to it, how to open a pidfd for a new task from
it, and how a held result is reported and answered.
Signed-off-by: Cong Wang <cwang@multikernel.io>
---
.../userspace-api/seccomp_filter.rst | 117 +++++++++++++++++-
1 file changed, 116 insertions(+), 1 deletion(-)
diff --git a/Documentation/userspace-api/seccomp_filter.rst b/Documentation/userspace-api/seccomp_filter.rst
index cff0fa7f3175..a4c9fcb4d204 100644
--- a/Documentation/userspace-api/seccomp_filter.rst
+++ b/Documentation/userspace-api/seccomp_filter.rst
@@ -224,7 +224,10 @@ The interface for a seccomp notification fd consists of two structures:
__u64 id;
__u32 pid;
__u32 flags;
- struct seccomp_data data;
+ union {
+ struct seccomp_data data;
+ struct seccomp_notif_result result;
+ };
};
struct seccomp_notif_resp {
@@ -281,6 +284,118 @@ process will ignore non-fatal signals until the response is sent. Signals that
are sent prior to the notification being received by userspace are handled
normally.
+System call results
+-------------------
+
+A supervisor that continues a system call can ask how the call turned out by
+adding ``SECCOMP_USER_NOTIF_FLAG_RESULT`` to
+``SECCOMP_USER_NOTIF_FLAG_CONTINUE`` in its response. The set of supported
+system calls may grow; so far it is ``fork()``, ``vfork()``, ``clone()`` and
+``clone3()``. The flag is refused with ``EINVAL`` for any other system call,
+for system call conventions the kernel cannot classify, and by kernels that
+do not support it, so trying it is a reliable probe.
+
+The outcome is reported once, as a notification with
+``SECCOMP_NOTIF_FLAG_RESULT`` set in ``flags`` and the same ``id`` as the
+continued notification. It is a record, not a request: it is not answered,
+and the task does not wait for it to be read. It is queued by the time the
+task returns to user space, so it arrives before the task's next
+notification. Its ``pid`` is the task that made the call, and ``result``
+replaces ``data``:
+
+.. code-block:: c
+
+ struct seccomp_notif_result {
+ __s32 nr;
+ __u32 arch;
+ __s64 val;
+ __u32 child_pid;
+ __u32 __reserved;
+ __u64 child_ino;
+ };
+
+``nr`` and ``arch`` identify the system call. ``val`` is its return value as
+the task saw it, including any change made at a ptrace syscall exit stop. A
+negative value is an error number:
+
+- One of the kernel internal ``-ERESTART*`` values (-512 to -516) means the
+ call did not complete because of a signal. It is then either restarted,
+ and passes through the filter again as a new notification, or fails with
+ ``EINTR``.
+- If a signal interrupted the notification after the response was sent, the
+ call never ran, and the record reports the error it fails with.
+- If the task exits before the call returns, ``val`` is ``-ESRCH``.
+
+Fork results
+~~~~~~~~~~~~
+
+A successful fork also fills in ``child_pid``, the new task in the listener's
+pid namespace, and ``child_ino``, its pidfs inode number.
+``SECCOMP_NOTIF_FLAG_THREAD`` is set if the new task is a thread. Both fields
+are zero if the fork failed.
+
+The record of a successful fork is queued before the new task first runs, and
+records are received before any pending notification, so the supervisor
+always hears of a task before it hears from it. The new task inherits the
+filter, so its own forks are reported the same way, and so on down the tree.
+
+The task may have run, exited and been reaped by the time the record is read,
+so its pid may already name another task. ``child_ino`` is never reused:
+opening a pidfd by it either yields exactly the new task or fails, in which
+case the task has already exited:
+
+.. code-block:: c
+
+ struct {
+ unsigned int handle_bytes;
+ int handle_type;
+ __u64 ino;
+ } fh = { sizeof(__u64), FILEID_KERNFS /* 0xfe */, rec.result.child_ino };
+
+ pidfd = open_by_handle_at(FD_PIDFS_ROOT, (struct file_handle *)&fh, 0);
+
+This needs no privileges. Together with the pidfd's exit notification it lets
+a supervisor track every task below it, including one that is killed before
+it makes a single notified system call.
+
+Holding a result
+~~~~~~~~~~~~~~~~
+
+A record is one-way: the task does not wait for it to be read. Where the
+supervisor must act on a result before the task goes on, the response can
+carry ``SECCOMP_USER_NOTIF_FLAG_HOLD`` together with
+``SECCOMP_USER_NOTIF_FLAG_RESULT`` and ``SECCOMP_USER_NOTIF_FLAG_CONTINUE``.
+It is supported for the same system calls as ``SECCOMP_USER_NOTIF_FLAG_RESULT``
+and refused with ``EINVAL`` for the others.
+
+The result is then reported as a held notification instead of a record:
+``flags`` has both ``SECCOMP_NOTIF_FLAG_RESULT`` and
+``SECCOMP_NOTIF_FLAG_HELD`` set, ``id`` is that of the continued
+notification, and ``result`` is filled in as above. The task does not return
+to user space until the held notification is answered, whether the call
+succeeded or failed.
+
+For the fork family, a successful fork is held before the new task is woken,
+so neither the parent nor the new task runs until the answer. This is for a
+supervisor that must know a new task before it executes anything at all, for
+example when tasks share memory and one could change another's arguments
+without a system call.
+
+A held notification is answered like any other, except:
+
+- The only accepted response is ``SECCOMP_USER_NOTIF_FLAG_CONTINUE`` with
+ ``val`` and ``error`` zero. Anything else fails with ``EINVAL``; the call
+ has completed and its result cannot be changed.
+- ``SECCOMP_IOCTL_NOTIF_ADDFD`` fails with ``EINVAL``.
+- ``SECCOMP_IOCTL_NOTIF_ID_VALID`` works as usual.
+
+The task waits killable, so non-fatal signals do not end the wait. If the task
+is killed before the supervisor received the held notification, it is turned
+into an ordinary record, so the supervisor still learns the result. A result
+reported because the task exits (``-ESRCH``) is never held. If the listener is
+closed, the task continues. For a fork, the new task is woken once the wait
+ends in every case.
+
It is worth noting that ``struct seccomp_data`` contains the values of register
arguments to the syscall, but does not contain pointers to memory. The task's
memory is accessible to suitably privileged traces via ``ptrace()`` or
--
2.43.0
prev parent 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 [RFC PATCH 0/9] seccomp: report the result of continued system calls Cong Wang
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 ` Cong Wang [this message]
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-10-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®