A system call selected by a seccomp filter can stop before kernel execution and appear as a request on a notification file descriptor. With SECCOMP_RET_USER_NOTIF, Linux turns that call into a coordination point between the blocked target and a userspace supervisor. The supervisor can emulate a result, inject a file descriptor for suitable operations, or permit the kernel to continue the original call.
This mechanism is deliberately narrower than a general userspace security policy engine. Its strongest boundary is the kernel-mediated suspension and response protocol. Data reached through target-memory pointers can still change around a supervisor’s inspection, and a response that continues the original call re-enters ordinary kernel execution with that race still relevant.
The listener belongs to a filter
A filter installed with SECCOMP_FILTER_FLAG_NEW_LISTENER yields a notification file descriptor. Filter rules that return SECCOMP_RET_USER_NOTIF cause matching calls to generate requests on that descriptor.
The descriptor corresponds to the filter rather than one specific task. Descendants that share the filter can therefore produce notifications on the same listener. This makes the descriptor a supervision endpoint for a filter domain, not a private mailbox tied to a single thread.
The listener can also cross a process boundary through normal descriptor-transfer mechanisms such as SCM_RIGHTS. That property permits a container manager or another supervisor to remain separate from the workload whose calls are intercepted.
A notification represents a blocked call, not completed work
SECCOMP_IOCTL_NOTIF_RECV returns a struct seccomp_notif containing an identifier, target thread information, and struct seccomp_data. At this point the intercepted call has not completed normally. The target is waiting for a response unless the request is interrupted or otherwise becomes invalid.
The supervisor replies with SECCOMP_IOCTL_NOTIF_SEND. A response can provide a return value or error without executing the original system call. This permits emulation where the supervisor performs equivalent or substituted work under its own authority.
SECCOMP_USER_NOTIF_FLAG_CONTINUE has different semantics. It tells the kernel to continue the target’s original call. The supervisor is no longer substituting a result; it is releasing the target back into the normal syscall path.
That distinction matters because emulation and continuation expose different state transitions. A supervisor that performs an operation itself acts with its own credentials, namespaces, descriptors, and other process context unless it deliberately reconstructs relevant target state.
Register arguments are snapshots, pointed-to memory is not
struct seccomp_data carries the syscall number and raw argument register values. For pointer arguments, those values are addresses. The notification does not copy the target memory referenced by those addresses into an immutable request.
A target with another runnable thread can modify memory while the intercepted thread is blocked. If a supervisor reads a pathname, socket address, or another pointed-to object and later permits the original syscall to continue, the kernel may observe bytes different from those previously inspected by the supervisor.
This is a time-of-check/time-of-use boundary inherent in the continuation model. Kernel documentation explicitly cautions against treating userspace notification as a policy mechanism for calls whose safety decision depends on dereferenced target pointers.
When a supervisor must inspect target memory for an emulated operation, copying all relevant input into supervisor-owned memory before making the decision gives that decision a stable local snapshot. It does not make a later continued syscall consume that snapshot.
Notification IDs guard request lifetime
A received notification includes an ID used by later operations. The target can disappear or its blocked call can be interrupted before the supervisor finishes processing the request.
SECCOMP_IOCTL_NOTIF_ID_VALID checks whether an ID still refers to a live blocked request. This check is especially important before acting through interfaces keyed by the reported target ID, because numeric process or thread IDs can be reused after exit.
ID validation addresses notification lifetime. It does not freeze target memory or create a transaction spanning arbitrary supervisor actions. A design still needs to separate “this request remains current” from “all external state observed for this request remains unchanged.”
ADDFD transfers an open file description into the target
Some emulated system calls naturally return file descriptors. SECCOMP_IOCTL_NOTIF_ADDFD lets a supervisor install a descriptor into the target’s file descriptor table while processing a valid notification.
Without SECCOMP_ADDFD_FLAG_SETFD, the kernel chooses the lowest available target descriptor number. With SECCOMP_ADDFD_FLAG_SETFD, the supervisor requests a specific number subject to the interface rules. newfd_flags can request O_CLOEXEC.
SECCOMP_ADDFD_FLAG_SEND can combine descriptor installation with the notification response. This avoids a separate response step between creating the target descriptor and releasing the blocked call.
The transferred descriptor still represents a kernel object opened or obtained by the supervisor. Descriptor injection therefore crosses an authority boundary: the supervisor is granting the target access to an object selected under supervisor control.
Readiness exposes protocol state
The notification descriptor integrates with poll(), select(), and epoll. Pending notifications make it readable. After a notification has been received, the descriptor can indicate writability for a response.
This lets the mechanism participate in an event loop without a dedicated blocking receive thread. Readiness still describes the notification protocol, not target progress. A busy event loop can delay responses and keep intercepted threads blocked.
Supervisor availability is consequently part of workload liveness. A filter that routes essential calls to a listener creates a dependency on the process servicing that listener. Failure handling has to account for that dependency rather than treating notification as passive observability.
Filter precedence remains part of the boundary
Seccomp filters can be stacked, and action precedence affects which behavior wins. A userspace notifier is not an external reference monitor that automatically dominates every later filter decision.
The filter set installed on the target therefore remains part of the security model. If target policy permits installation of additional filters or related control operations, the resulting action precedence must be considered alongside the notifier’s intended role.
This is another reason the notification interface fits privilege mediation better than standalone policy enforcement. The kernel’s existing permission checks, namespace rules, capabilities, LSM decisions, and seccomp filter semantics remain authoritative around the delegated operation.
Delegation creates explicit authority and liveness edges
Seccomp user notification converts selected syscall attempts into a request-response protocol. The target pauses, the kernel supplies a request identity and register-level call data, and the supervisor chooses an allowed response form.
The protocol can support container compatibility and privileged mediation without moving an entire subsystem into the target. Its boundaries are equally concrete: pointed-to memory is mutable, notification identity has a lifetime, continuation retains pointer races, injected descriptors transfer real authority, and supervisor stalls become target stalls.
Those properties make the mechanism useful precisely when delegation is intentional and the surrounding kernel security controls remain in force. They also keep it distinct from a complete userspace replacement for syscall security policy.