From kernel 2.6.12 with strict mode in 2005 to seccomp-bpf in kernel 3.5 in 2012, this episode traces the history of seccomp, its adoption by Chromium, Docker, systemd, and libcontainer, and why restricting syscalls is the most effective defense against exploitation.

In episode 0 you set up your environment: a modern Linux kernel, a C compiler, strace, bpftool, and seccomp-tools. Now it's time to answer the most fundamental question: why does the modern world need seccomp so badly?
Seccomp wasn't born overnight. It's the result of a long evolution of Linux security — from a very rigid mechanism in 2005 to the sandboxing foundation used by nearly the entire container industry in 2026. To understand why it's designed the way it is, we first need to understand the problem it solves, then trace its history. This episode gives you two things: the story behind seccomp's birth and the rational reasons why restricting syscalls is the most effective defense.
Imagine a web server application. A healthy web server needs only a few dozen syscalls: reading files, opening sockets, sending data, managing memory, and creating threads. But that process can call hundreds of other syscalls — and it's precisely those unneeded syscalls that attackers exploit.
Here's the practical flow. When an application is successfully exploited (for example, through a buffer overflow bug), the attacker usually runs a chain like this:
execve to run a shell (/bin/sh).openat / read to read sensitive files.socket to establish an outbound connection and steal data.Now imagine if from the start we told the kernel: "this process MUST NOT call execve, MUST NOT create sockets, MUST NOT open files beyond what it needs." What happens when an attacker succeeds in exploiting? They gain execution control — but every exit door is already locked. This is the concept of reducing the attack surface.
Note
An analogy: your house can't be without doors, but you can choose which doors to lock. Seccomp is those locks. A house with a hundred open doors is far easier to rob than a house with a hundred doors where only five are open and the rest are securely locked.
The backdrop: in the early 2000s, user-space sandboxing was still very primitive. The common models were manual privilege separation and chroot — both fragile against overly permissive syscalls. Kernel 2.6.12 (2005) introduced the first mechanism, with a fittingly simple name: seccomp strict mode.
Its rules were extremely rigid: once this mode was enabled, a process could only call four syscalls:
readwrite_exitsigreturnAny other syscall would immediately get the process killed by the kernel. This mechanism was activated via prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT) and was irreversible.
Its strength was predictability — the only way to be safe. But its weakness was fatal: almost no real application can run on just four syscalls. Processes need mmap, need brk, need clone. Strict mode was practically only useful for very narrow code — such as honeypots and specialized security tools. Because it was too narrow, adoption was very limited.
The need for a flexible filter came from Chromium. A browser needs to sandbox untrusted web page code — renderer processes had to run with as few syscalls as possible, while still being able to perform complex browser tasks. The four-syscall mode wasn't enough.
The solution: kernel 3.5 (2012) added seccomp-bpf, the SECCOMP_MODE_FILTER mode. The core idea was clever and simple:
Seccomp-bpf kept strict mode's security (the decision lives in the kernel, not in user-space that can be tampered with) while offering unlimited flexibility. That was the turning point: from a niche mechanism to a universal sandboxing platform.
The birth of seccomp-bpf was immediately followed by mass adoption:
| Year | Event |
|---|---|
| 2005 | Kernel 2.6.12 — strict mode born (4 syscalls) |
| 2012 | Kernel 3.5 — seccomp-bpf / SECCOMP_MODE_FILTER born |
| 2013–2015 | Docker, runc, and libcontainer adopt default seccomp profiles |
| 2015 | systemd adds SystemCallFilter for service units |
| 2019 | Kernel 5.0 — SECCOMP_RET_NOTIFY (user notification) born |
| 2024–2026 | libseccomp 2.6.x — new architectures and security fixes |
Docker carried seccomp to the world at scale. Since Docker 1.10, every container run without special configuration automatically gets the default seccomp profile that blocks dozens of dangerous syscalls — without being asked, without extra configuration:
# This container automatically uses Docker's default seccomp profile
docker run --rm alpine uname -a
Linux 6.8.0-45-generic #1 SMP x86_64 Linux# Replace the default profile with a custom JSON file
docker run --rm --security-opt seccomp=my-profile.json alpine uname -asystemd also integrated seccomp into service units. Administrators can restrict a daemon's syscalls directly without writing any C code:
[Service]
ExecStart=/usr/bin/my-daemon
NoNewPrivileges=yes
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERMUnder the hood, SystemCallFilter is translated by systemd into exactly the same kind of seccomp filter that libseccomp builds. And runc/libcontainer — the OCI runtime that runs containers on your machine — loads the seccomp profile written in config.json every time a container starts. Seccomp is no longer an experimental feature; it has become infrastructure.
The library that abstracts filter creation, libseccomp, keeps evolving. The 2.6.x releases brought new architecture support (including LoongArch and RISC-V 32/64-bit), more precise architecture handling, and a number of security fixes in the BPF generator itself.
Why do updates matter? A wrongly composed seccomp filter can become a new security hole — for example, a bug in the BPF generator that produces a filter that doesn't match what you asked for. For production, always use the latest libseccomp version and watch the changelog for CVE awareness. Verify your version with pkg-config --modversion libseccomp.
After looking at the history, let's crisply summarize four concrete problems that seccomp solves:
1. Closing the attack surface. A process only needs a small subset of the hundreds of available syscalls. Seccomp ensures the rest cannot be called, so an exploit chain is cut off at the earliest step.
2. Preventing privilege escalation and exploitation. Blocking syscalls like execve and socket cuts off an attacker's ability to run payloads, read sensitive files, or establish outbound communication — even if execution control has already been seized.
3. Reducing blast radius. When a component is compromised, seccomp limits the damage to that component alone. A breached service doesn't automatically grant access to the whole machine or internal network.
4. Enforcing "least privilege" policy at the kernel level. Seccomp is one of the few mechanisms where the security decision is executed in the kernel rather than in user-space — so it cannot be bypassed by an already-compromised process.
The key thing to understand: seccomp is not a replacement for other security mechanisms. It's defense in depth — one layer among many. Namespaces limit what a process sees, cgroups limit resources, and seccomp limits the actions a process can take against the kernel. All three complement each other; they don't substitute for each other.
Here's the core of episode 1:
In the next episode 2, we'll dissect the basic concepts and core architecture of seccomp — from the SECCOMP_MODE_STRICT and SECCOMP_MODE_FILTER modes, the role of prctl and the seccomp syscall, kernel components versus libseccomp, to how filters are applied by runc/crun, systemd, and OpenSSH. There you'll see how all that history works technically inside the system. See you then!