A confined process issues a system call that its ordinary seccomp policy cannot safely reduce to a static allow-or-deny decision. The arguments may refer to mutable process memory, or the operation may need privileged work performed outside the confined process. Returning a fixed errno is too restrictive, while permitting the call directly gives the target more authority than the deployment intends.
Linux seccomp user notification creates a mediation path for this case. A filter can return SECCOMP_RET_USER_NOTIF, causing the kernel to block the triggering task and emit a request on a listener file descriptor. A userspace supervisor receives the request and later supplies a result. This mechanism changes where a selected syscall decision is made, but it does not turn seccomp into a general reference monitor without additional policy and race controls.
The listener belongs to a filter, not one task
A process installs a seccomp filter with SECCOMP_FILTER_FLAG_NEW_LISTENER. On success, seccomp() returns a listener file descriptor associated with that filter. The descriptor can be transferred to another process, including through Unix-domain descriptor passing.
When the filter selects SECCOMP_RET_USER_NOTIF, a matching syscall is not executed immediately. The task blocks while the request is pending. The supervisor receives a struct seccomp_notif with SECCOMP_IOCTL_NOTIF_RECV and responds with SECCOMP_IOCTL_NOTIF_SEND.
The listener is tied to the filter rather than to a single task. Descendants that share the installed filter can therefore generate notifications on the same listener. That property suits container managers and process supervisors, but it also means policy state cannot assume that one listener maps to one caller identity.
The notification carries an identifier, a task identifier when visible in the listener’s PID namespace, flags, and struct seccomp_data. The syscall number, architecture value, instruction pointer, and raw argument register values are available through that data structure. The notification does not copy arbitrary memory referenced by pointer arguments.
Raw pointer arguments remain mutable state
For syscalls whose arguments include pointers, the register value only identifies an address in the target. A supervisor that reads strings or structures from target memory is observing state that can change independently of the notification metadata.
This creates a time-of-check to time-of-use boundary. If policy checks one representation and later performs work using data read again from mutable target memory, another thread in the target can potentially alter the bytes between those operations. Kernel documentation calls out this race and recommends copying relevant target memory into supervisor-owned memory before making policy decisions.
Even a single copy does not make every emulation pattern atomic. The supervisor must define which copied bytes form the authorized request and ensure later privileged operations use that captured representation rather than a fresh target-controlled value. Syscalls with complex pointer graphs or process-relative semantics can require additional care.
SECCOMP_IOCTL_NOTIF_ID_VALID addresses a different race. It lets the supervisor test whether a received notification identifier still refers to a live pending request. This is relevant when the target exits, a signal interrupts the blocked syscall, or numeric task identifiers can be reused. Validating the notification identifier does not validate the contents of target memory.
A synthetic return value is not syscall execution
A supervisor can answer a notification with a return value or an error. In that case, the kernel returns the supplied result to the blocked task without executing the intercepted syscall itself.
This distinction matters for operations with kernel side effects. Returning a value that resembles success does not create those side effects. If an intercepted operation is supposed to produce a file descriptor, mapping, filesystem change, or another kernel object, the supervisor needs a mechanism that actually establishes the corresponding state.
The response model therefore supports emulation only to the extent that the supervisor can reproduce the relevant externally visible effects. A policy that fabricates success without reproducing required state can break application invariants even when no privilege boundary is crossed.
The supervisor can also set SECCOMP_USER_NOTIF_FLAG_CONTINUE, instructing the kernel to execute the original syscall. This is not equivalent to a stable authorization transaction over pointer arguments. Target memory may change after supervisor inspection and before the kernel consumes it. Kernel documentation explicitly warns that the continue operation is subject to this class of race and should not be treated as a security policy primitive for mutable arguments.
ADDFD can install supervisor-held authority in the target
SECCOMP_IOCTL_NOTIF_ADDFD gives the supervisor a more concrete tool for intercepted operations that produce file descriptors. It can install a duplicate of a supervisor-held descriptor into the target’s descriptor table and return the resulting descriptor number.
This crosses an authority boundary. The target receives access to the same underlying open file description or other descriptor-backed kernel object represented by the injected descriptor, subject to the object’s semantics. The supervisor therefore must treat descriptor selection as a policy decision, not merely as response formatting.
newfd_flags can request O_CLOEXEC for the descriptor installed in the target. SECCOMP_ADDFD_FLAG_SETFD can request a specific descriptor number; if that number is already open in the target, the documented operation replaces it. Such replacement semantics make descriptor-number assumptions part of the mediation design.
SECCOMP_ADDFD_FLAG_SEND can combine descriptor installation with the notification response. This atomic form avoids a gap in which a descriptor is inserted but a separate response cannot be delivered because the notification ceased to be valid. It narrows one lifecycle race; it does not make the selected descriptor safe for the target by itself.
Signals can invalidate or restart pending mediation
A notification exists while the target is blocked on the intercepted syscall. Target lifecycle and signal delivery can alter that state before the supervisor responds. A response to an invalidated request can fail with ENOENT.
Signal interruption can also cause the syscall to restart and generate another notification. A supervisor must therefore avoid assuming that each observed notification corresponds to a permanently unique high-level application action. Side effects performed outside the kernel’s notification transaction need idempotence or another mechanism that prevents duplicate privileged work.
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV changes signal handling after a notification has been received by the supervisor: nonfatal signals are deferred while the target waits for the response, while fatal signals remain effective. This can reduce disruption for long-running mediation, but it is deployment behavior rather than a blanket guarantee that a request will complete.
Delegation enlarges the supervisor’s security role
Static seccomp filtering keeps the decision inside kernel evaluation of a BPF program. User notification deliberately adds a userspace component whose credentials, namespaces, descriptor inventory, memory reads, and policy logic can influence the result.
That supervisor may carry authority unavailable to the target. The design can therefore support privilege separation: a narrow request crosses from a constrained process to a component able to perform a controlled operation. The same arrangement also concentrates risk. A supervisor that accepts ambiguous caller identity, trusts mutable pointer data, injects overly broad descriptors, or performs irreversible work before request validity is settled can weaken the intended boundary.
Seccomp user notification is most precise when treated as a protocol between a blocked syscall context and a policy process. The kernel supplies request identity, blocking, response transport, and specific descriptor-transfer machinery. The deployment still owns semantic validation, race handling, authority minimization, and consistency between the synthetic syscall result and any side effects performed on the target’s behalf.