← All articles

Seccomp

What seccomp filters can inspect, how their actions differ, and a runnable Linux example that rejects one syscall without terminating the process.

A compromised process can only attack kernel interfaces it can reach. Seccomp lets a process restrict the system calls available to it, reducing part of that exposed interface. It is useful precisely because it operates below application code: the same filter still applies when a request handler has been subverted.

The boundary needs a precise description. Seccomp filters system-call entry; it does not replace object permissions, application authorisation or a complete sandbox. A good profile expresses a workload’s requirements and makes the consequences of denying a call explicit.

How Seccomp Works

Linux provides strict mode and filter mode. Strict mode exposes a very small, predefined interface. Filter mode evaluates a classic BPF program over metadata including the system-call number, architecture and argument values. The filter returns an action that determines what happens to the call.

Filter mode can express either a restrictive default with selected allowances, or an allowing default with selected denials. Neither arrangement is implied by the word “filter”. When reviewing a profile, read the default action before reading individual rules.

A process enters a system call, seccomp evaluates its ABI and arguments, and the kernel applies the returned action.
SELinux and other object-access controls still govern access to resources.

The result is not always process termination. Important actions include allowing the call, returning an error, raising a signal and killing a thread or process. Linux also supports tracing and userspace notification mechanisms with their own requirements. Their existence should not be confused with a simple allow/deny filter. The kernel guide and seccomp manual document the details.

Implementing Seccomp Filters

A filter sees argument values, not arbitrary application memory. If a system call receives a pointer to a pathname, ordinary seccomp-BPF cannot follow that pointer and compare the pathname string. A rule that purports to allow open() only for /etc/passwd by comparing the pointer argument is not a pathname policy.

This is where SELinux and other object-access controls matter. They address permissions on resources while seccomp restricts the available kernel interface. Argument filtering remains useful for scalar values such as selected flags or socket families, provided the ABI and argument representation are handled correctly.

Architecture handling is essential. System-call numbers differ across ABIs; on x86-64 the x32 namespace also needs attention. A number comparison without a suitable architecture check can implement a different policy from the one intended.

The following small demonstration denies getppid() with EPERM and allows other native calls. It deliberately demonstrates one filter action; it is not a production confinement profile. It supports Linux x86-64 and AArch64 and requires the kernel’s seccomp-filter and SECCOMP_RET_KILL_PROCESS support.

/* Educational example: deny one harmless syscall; allow other native calls.
 * This demonstrates filter behaviour, not a complete confinement policy.
 * Linux x86-64 or AArch64; kernel support for SECCOMP_RET_KILL_PROCESS required.
 */
#define _GNU_SOURCE
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>

#if defined(__x86_64__)
#define EXPECTED_ARCH AUDIT_ARCH_X86_64
#elif defined(__aarch64__)
#define EXPECTED_ARCH AUDIT_ARCH_AARCH64
#else
#error "This example supports only Linux x86-64 and AArch64"
#endif

int main(void)
{
    struct sock_filter instructions[] = {
        BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)),
        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, EXPECTED_ARCH, 1, 0),
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
        BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
#if defined(__x86_64__)
        /* AUDIT_ARCH_X86_64 also covers x32; reject that syscall namespace. */
        BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0x40000000U, 0, 1),
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
#endif
        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SYS_getppid, 0, 1),
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EPERM),
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
    };
    struct sock_fprog program = {
        .len = (unsigned short)(sizeof instructions / sizeof instructions[0]),
        .filter = instructions,
    };
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
        perror("PR_SET_NO_NEW_PRIVS");
        return 1;
    }
    if (syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &program) == -1) {
        perror("seccomp");
        return 1;
    }
    errno = 0;
    if (syscall(SYS_getppid) != -1 || errno != EPERM) {
        fputs("Unexpected result from filtered getppid\n", stderr);
        return 1;
    }
    puts("getppid blocked with EPERM; the process is still running.");
    return 0;
}

Download seccomp-demo.c, then compile and run it as an ordinary user in a local test directory:

cc -O2 -Wall -Wextra -Werror seccomp-demo.c -o seccomp-demo
./seccomp-demo

Expected output:

getppid blocked with EPERM; the process is still running.

The explicit syscall() exercises the filtered kernel interface. Error handling checks both filter installation and the result of the blocked call. The example sets no_new_privs before installing the filter, as required for this unprivileged use. The kernel documentation explains that flag’s inheritance and limits.

Common Seccomp Filters

With libseccomp, seccomp_init() selects the default action and seccomp_rule_add() adds matching exceptions. For example, an allowing default plus an error action for one call is a deny rule. A restrictive default plus selected allow rules is a different policy. The libseccomp API reference describes those action semantics.

A syscall name also needs an operational interpretation:

Example callQuestion to resolve before restricting it
execveDoes the workload launch helpers, and how are their executable files controlled?
ptraceIs debugging or process inspection an explicit operational requirement?
mountDoes the workload itself need mount operations, or is that the runtime’s responsibility?
socketWhich socket families are needed, and which other controls restrict destinations?

Choosing the restrictive form is only the start of the engineering work. The process also needs calls for startup, memory management, thread creation, logging, shutdown and error recovery. A profile inferred from one successful request may miss a rare but legitimate path.

Tracing can help discover observed calls, but observation does not establish a complete specification. Exercise maintenance operations and failure cases, review why each permission exists, and repeat the checks when the application, runtime or libraries change.

Seccomp in Containers

Container runtimes can install a profile before starting the workload. The effective profile depends on the runtime, its configuration, the host kernel and the selected options. A JSON profile copied into an image does not apply itself.

For a container, review seccomp together with capabilities, user identity, mounted resources and SELinux policy. Giving a process access to a sensitive mounted file can still be harmful through perfectly ordinary permitted calls. Conversely, denying a necessary syscall can break a service even when its file permissions are correct.

The companion article on SELinux and seccomp in containers develops that division of responsibilities using a Podman-oriented example.

Conclusion

Seccomp is a way to reduce the kernel interface exposed to a workload. Its usefulness depends on the actual filter, the execution environment and the behaviour tested. Read the default action, understand each exception, check ABI handling and verify the failure paths. Then combine the filter with controls that govern the resources the process may use.

References

← Back to all articles

Contact Info

Send us a message

Tell us about your application, infrastructure or security requirements. All fields below are required.

Protected by our own security check.