A confined process can reach a syscall that the kernel would reject under its current credentials, while a separate supervisor has enough privilege to perform an equivalent operation safely on its behalf. Linux seccomp user notification creates a mediation channel for that arrangement: a filter can stop the calling thread, emit a notification to a listener, and wait for a userspace response.

That mechanism is more precise than treating the supervisor as a general syscall proxy. The notification carries register-level syscall data and an identifier tied to the pending request. The supervisor can synthesize a return value, inject a file descriptor, or in selected cases tell the kernel to continue the original syscall. Each option places the trust boundary in a different location.

USER_NOTIF suspends execution rather than granting authority

A seccomp filter that returns SECCOMP_RET_USER_NOTIF does not execute the intercepted syscall. With a listener created through SECCOMP_FILTER_FLAG_NEW_LISTENER, the kernel queues a request on the notification file descriptor and blocks the triggering thread while that request remains pending.

The listener receives a struct seccomp_notif with an id, a task identifier as visible from the listener’s PID namespace, and struct seccomp_data. The latter contains the syscall number, architecture, instruction pointer, and raw argument values.

Those values are not a snapshot of memory referenced by pointer arguments. A pathname argument, for example, appears as an address. If a supervisor reads bytes from the target’s address space, those bytes remain target-controlled memory unless the design establishes another invariant.

This distinction sets the central boundary: seccomp captures register arguments at interception, but it does not freeze arbitrary userspace memory reachable from those registers.

Notification IDs bind replies to pending requests

The id field is not merely an application correlation number. The kernel associates it with the pending notification. A supervisor can use SECCOMP_IOCTL_NOTIF_ID_VALID to test whether a request remains valid before performing work whose result should be attached to that request.

A target can terminate or its blocked syscall can be interrupted before the supervisor replies. Code that treats a PID alone as durable request identity can also collide with process lifecycle changes. The notification ID gives the protocol a request-scoped identity that is separate from numeric PID reuse.

A successful validity check does not freeze the target’s memory or guarantee that the request will remain pending indefinitely. It narrows one race: whether the notification still names a live pending syscall at the time of the check.

Emulation keeps the original syscall from running

The supervisor can answer with SECCOMP_IOCTL_NOTIF_SEND and provide a result or an error. In that mode, the original syscall is not executed by the target. The kernel returns the supervisor-supplied outcome to the blocked thread.

This is useful when the privileged side can perform an operation on behalf of the target under a deliberately constrained policy. The supervisor’s own credentials, namespaces, open descriptors, and filesystem view may differ from the target’s, so equivalence is not automatic. Emulation code must define which context supplies each security-relevant property.

For operations that produce file descriptors, returning only an integer is insufficient because descriptor numbers are local to a descriptor table. SECCOMP_IOCTL_NOTIF_ADDFD lets the supervisor install a descriptor into the target and associate that transfer with the pending notification ID. O_CLOEXEC can be requested for the installed descriptor, and the API also supports selecting a descriptor number under defined flags.

The security property is therefore object transfer, not numeric imitation. The target receives a reference to a kernel object selected by the supervisor.

CONTINUE moves enforcement back into the original syscall path

SECCOMP_USER_NOTIF_FLAG_CONTINUE changes the model. Instead of synthesizing the syscall result, the supervisor asks the kernel to resume the target’s original syscall. Normal kernel checks then apply to that execution.

This option carries a documented time-of-check/time-of-use hazard. If the supervisor inspected target memory referenced by a pointer argument before sending CONTINUE, another thread in the target can modify that memory before the resumed syscall dereferences it. A policy decision based on the earlier bytes can therefore diverge from the bytes consumed by the kernel.

For that reason, user notification with CONTINUE is not a sound place to implement a security decision over mutable pointer data. The mechanism is intended for delegation scenarios in which a more privileged supervisor assists a less privileged target and another enforcement layer still constrains the continued syscall.

Copying target memory into supervisor-owned storage can stabilize data used for an emulated operation. It cannot make the target’s original pointer refer to that copy when CONTINUE resumes the syscall.

Filter precedence is part of the mediation boundary

Seccomp filters can be stacked, and action precedence affects which behavior reaches the target. A design that depends on user notification must account for other filters rather than assuming that every matching syscall reaches the listener.

The listener itself is also a capability. Possession of the notification file descriptor permits receiving pending requests and sending responses accepted for that filter. Its lifecycle, inheritance, and transfer therefore belong to the security architecture, not merely to event-loop plumbing.

If no listener is attached for a SECCOMP_RET_USER_NOTIF result, the kernel cannot delegate the request in the intended way. Deployment code must treat listener creation and ownership as part of establishing the confinement boundary.

Delegation is narrower than a userspace reference monitor

Seccomp user notification provides a controlled crossing between a blocked syscall and a supervisor. It can support privileged emulation, request-scoped descriptor injection, and selective continuation while keeping the target confined by other kernel mechanisms.

Its limits are equally structural. Register arguments do not freeze pointed-to memory, request validity is not a lock over target state, and CONTINUE re-enters the original syscall with mutable userspace arguments. The supervisor may also operate in a different namespace and credential context from the target.

The strongest architecture keeps those facts explicit: use notification IDs to bind work to pending requests, copy mutable data before making emulation decisions, transfer kernel objects through the dedicated descriptor interface, and reserve continuation for cases where downstream kernel enforcement remains sufficient. The resulting boundary is delegation under stated assumptions, not a general security policy engine in userspace.