How Linux Implements Seccomp to Restrict System Calls
This article provides an overview of how the Linux kernel implements Secure Computing Mode (seccomp) to restrict the system calls a process can execute. It covers the evolution from strict seccomp to flexible Berkeley Packet Filter (BPF) filtering, the internal kernel mechanics that intercept system calls, the execution of filter programs, and how process inheritance and privilege controls ensure that sandboxing policies cannot be bypassed.
The Two Modes of Seccomp
Linux provides two distinct operational modes for seccomp, configured
primarily through the seccomp() system call or legacy
prctl() operations:
- Strict Mode (
SECCOMP_SET_MODE_STRICT): The original, hardcoded implementation introduced in Linux 2.6.12. In this mode, a process is restricted strictly to four system calls:read(),write(),_exit(), andsigreturn(). Attempting any other system call causes the kernel to immediately terminate the process with aSIGKILLsignal. - Filter Mode (
SECCOMP_SET_MODE_FILTER): Introduced in Linux 3.5, this mode allows developers to define fine-grained, programmable allowlists and blocklists using classic Berkeley Packet Filter (cBPF) bytecode.
System Call Inspection with cBPF
Under filter mode, the kernel treats incoming system calls similarly
to network packets. Before a system call executes, the kernel constructs
a read-only metadata structure named struct seccomp_data
and feeds it to the user-supplied BPF program:
struct seccomp_data {
int nr; /* System call number */
__u32 arch; /* Architecture ABI (e.g., AUDIT_ARCH_X86_64) */
__u64 instruction_pointer; /* CPU instruction pointer */
__u64 args[6]; /* System call arguments */
};The filter evaluates this data and returns a 32-bit status code that dictates the kernel's response:
SECCOMP_RET_ALLOW: The system call is executed normally.SECCOMP_RET_KILL_PROCESS/SECCOMP_RET_KILL_THREAD: Terminates the process or calling thread immediately.SECCOMP_RET_ERRNO: Skips the system call and returns a specificerrnovalue directly to user space without executing the kernel routine.SECCOMP_RET_TRAP: Leaves the system call unexecuted and raises aSIGSYSsignal, allowing the process to handle the violation.SECCOMP_RET_TRACE: Notifies aptrace-attached debugger, allowing external inspection or modification.SECCOMP_RET_LOG: Executes the call but logs the event to the system audit framework.
Kernel Interception and Execution Flow
The enforcement of seccomp is integrated directly into the kernel's low-level system call entry path:
- Thread Flag Verification: When a process installs a
seccomp filter, the kernel sets the
_TIF_SECCOMPthread information flag in the task'sthread_infostructure. - Entry Point Interception: Whenever the thread
issues a software interrupt or executes a dedicated instruction (such as
syscallon x86-64), the architecture-specific assembly entry handler checks for active thread flags. - Filter Evaluation: If
_TIF_SECCOMPis set, control diverts to__secure_computing(). This invokesseccomp_run_filters(), which steps through all installed BPF programs attached to the process'sstruct seccompcontainer. - Action Resolution: If multiple filters exist, the kernel runs all of them in reverse chronological order and selects the return value with the highest precedence (the most restrictive action).
- Dispatch or Abort: If the resolved action is
SECCOMP_RET_ALLOW, the kernel passes execution to the target system call handler. Otherwise, the kernel aborts execution, sets register return values, or raises signals as instructed.
Security Guarantees and Filter Inheritance
To prevent unprivileged processes from subverting restrictions, Linux enforces two critical behavioral models:
- Privilege Restriction (
no_new_privs): Before an unprivileged process can load a seccomp filter, it must enable theno_new_privsbit viaprctl(PR_SET_NO_NEW_PRIVS, 1, ...). This bit guarantees that operations likeexecve()cannot elevate privileges through set-user-ID (SUID) or set-group-ID (SGID) binaries, preventing a restricted process from imposing an altered environment onto a privileged executable. - Filter Stacking and Inheritance: Seccomp filters cannot be removed or modified once installed. When a process forks, clones, or executes a new binary, child processes inherit all existing filters. New filters can only be appended to the chain, ensuring that security boundaries can only become progressively more restrictive over a program's lifecycle.