Seccomp Filters Reduce Syscall Surface Without Forming a Complete Sandbox
A service can run with a short seccomp allowlist and still retain broad authority through file descriptors, filesystem permissions, network endpoints, and credentials. The filter may sharply reduce the kernel interfaces reachable through system calls, yet the process can remain capable of damaging actions through operations that are explicitly allowed. This is the central boundary of seccomp: it filters syscall attempts; it does not define the full security policy of a process.
Linux kernel documentation describes seccomp filtering as a mechanism for reducing exposed kernel surface and explicitly distinguishes it from a complete sandbox. That distinction becomes operationally important when a deployment treats a passing seccomp profile as proof of containment rather than one layer in a larger isolation design.
The filter sees syscall metadata, not application intent
Seccomp filter mode evaluates a BPF program over struct seccomp_data. The available data includes the syscall number, architecture identifier, instruction pointer, and raw syscall argument values. A filter returns an action that tells the kernel whether to allow the call, reject it, terminate execution, notify another component, or take another supported seccomp action.
The filter does not interpret application-level intent. Allowing write() does not distinguish a harmless log write from modification through an already-open descriptor. Allowing sendmsg() does not establish which application message is acceptable. The kernel evaluates the values exposed at the syscall boundary, not the semantic meaning of the data behind them.
Pointer arguments make that boundary especially clear. Classic seccomp BPF does not dereference userspace pointers. A filter can inspect the numeric pointer value supplied as an argument, but it cannot safely parse the pointed-to pathname, socket structure, or buffer as part of ordinary filter evaluation. This restriction avoids a class of time-of-check/time-of-use races in the filtering mechanism, while also limiting the policy that a filter can express.
A policy that needs pathname authority, object labels, or richer resource semantics therefore belongs in another enforcement mechanism or in a carefully designed supervisor arrangement. Seccomp remains useful precisely because its enforcement point is narrow and mechanically defined.
Architecture checks are part of syscall identity
A syscall number is not globally meaningful without its calling convention. Linux can support multiple syscall ABIs on one machine, and numeric syscall identifiers can differ or overlap across those ABIs. The seccomp input therefore includes an architecture value, and kernel documentation warns that filters should validate it before relying on syscall numbers.
This is not metadata decoration. A filter that assumes one ABI while a process can invoke another can classify a numeric syscall value under the wrong table. Correct policy construction ties the architecture check and syscall-number check together.
The same principle applies to argument interpretation. Raw argument positions and widths are properties of the syscall ABI. Profiles generated for one execution environment should not be treated as portable policy merely because syscall names appear identical at a higher level.
Filter installation has its own privilege boundary
An unprivileged task cannot freely install a filter that persists across execve() and then use that filter to manipulate a more privileged program. Linux addresses that risk by requiring the task to have CAP_SYS_ADMIN in its user namespace or to set no_new_privs before installing a seccomp filter.
Once set, no_new_privs is inherited across fork(), clone(), and execve() and cannot be unset. Its execve() contract prevents that transition from granting privileges that would not otherwise be available, including privilege gains from set-user-ID, set-group-ID, and file capabilities.
The two mechanisms solve different problems. no_new_privs constrains privilege gain across execution transitions. Seccomp constrains syscall handling according to installed filters. Neither mechanism, by itself, revokes file descriptors already held by the process, changes filesystem ownership, removes network reachability, or supplies a mandatory access-control policy.
Additional filters can narrow policy but cannot relax it
Seccomp permits filters to be layered. When multiple filters evaluate a syscall, the kernel selects the action with the highest precedence rather than allowing a later filter to override an earlier restrictive decision with SECCOMP_RET_ALLOW.
This gives filter stacking a monotonic security property at the action level: adding a filter can impose an equally or more restrictive result for a syscall evaluation, but it cannot remove an earlier filter. That property supports processes that reduce their syscall surface as they move through initialization phases.
The operational cost is that policy history matters. A process cannot attach a temporary restrictive filter and later remove it when a phase ends. Designs that require reversible policy transitions need a different architecture, often involving separate processes with distinct lifetimes and privilege boundaries.
Filter stacking also has evaluation cost. Kernel documentation notes that additional filters increase evaluation time. Very large or repeatedly layered policies therefore carry a performance consideration alongside their security effect.
Threaded processes make installation timing visible
A multithreaded process can become inconsistent if only one thread enters a new filter regime while siblings continue with an older set. The SECCOMP_FILTER_FLAG_TSYNC flag exists to synchronize a filter across the calling process’s other threads when the kernel can do so.
Synchronization can fail if a sibling thread is in a state that prevents the requested transition, such as having a seccomp filter ancestry that cannot be reconciled with the caller’s filter tree. A deployment should therefore treat successful filter installation as an event that must be checked, not as an assumption following an attempted call.
Timing also matters before synchronization. Threads created before confinement may execute code during a window in which the intended filter is absent. Installing policy before exposing untrusted inputs or starting unnecessary concurrency reduces the number of transitional states that need to be trusted.
Return actions change failure behavior, not resource authority
A seccomp policy can reject a syscall with SECCOMP_RET_ERRNO, terminate a thread or process, generate SIGSYS with SECCOMP_RET_TRAP, log while allowing execution, or use other actions supported by the running kernel. These choices shape observability and failure semantics.
SECCOMP_RET_ERRNO can make denied calls look like ordinary kernel failures to application code. Termination actions make policy violations fail closed at the cost of process availability. SECCOMP_RET_LOG executes the syscall after logging it, so it is an observation action rather than a denial boundary.
SECCOMP_RET_USER_NOTIF adds a different architecture. Selected calls can be sent to a userspace supervisor through a listener file descriptor. The supervisor can respond to the notification and, with supported operations, inject a file descriptor into the target. This is useful for container managers and similar mediation systems, but it moves part of the decision into a concurrent userspace protocol.
The supervisor model does not turn pointer inspection into a trivial policy operation. If a supervisor reads target memory to interpret pointer arguments, that memory can change concurrently unless the design accounts for the race. Kernel documentation explicitly cautions about time-of-check/time-of-use hazards in this path.
Allowed syscalls can still exercise powerful existing authority
A narrow syscall set can be deceptively capable. A process that already holds a writable descriptor may alter the referenced object using ordinary I/O calls. A connected socket can carry commands to a privileged peer. A memory mapping can expose shared state. Inherited descriptors can cross an execve() boundary unless descriptor flags and process-launch policy close them.
Seccomp does not attach resource-specific permissions to those objects. It decides whether a syscall invocation reaches its normal kernel implementation. Once a call is allowed, the ordinary authorization and object semantics of that subsystem still govern the operation.
This makes descriptor hygiene part of sandbox design. Closing unnecessary descriptors, applying FD_CLOEXEC or creation-time close-on-exec flags, reducing credentials and capabilities, constraining namespaces, and applying an appropriate Linux Security Module policy address authority that a syscall filter does not represent.
The composition matters more than the presence of any single control. A seccomp profile can remove kernel attack surface while an LSM constrains object access and namespaces alter resource visibility. The resulting isolation boundary is the intersection of those mechanisms and the process’s remaining authority.
A syscall allowlist is a kernel-interface budget
Seccomp is strongest when its role is stated narrowly. An allowlist records which kernel entry points a workload is permitted to attempt under a specific ABI and process state. That can materially reduce exposure to kernel code that the workload does not need, and it can convert unexpected syscall use into a controlled failure.
It cannot prove that allowed operations are harmless. It cannot infer data-flow policy from application buffers, revoke ambient authority represented by open objects, or replace authorization in the subsystems reached by permitted calls. Those limits are not gaps around an otherwise complete sandbox; they define the mechanism itself.
A robust confinement design therefore treats the seccomp profile as a kernel-interface budget. The security boundary becomes credible only when that budget is combined with explicit control over credentials, objects, namespaces, descriptors, and any supervisor trusted to mediate exceptional calls.