Skip to content

Choosing sandbox profiles

Every task runs under a seccomp sandbox profile that decides which system calls it may make. Pick it per task with sandbox_profile; tasks that do not set one get the server’s default (normally compile_v2).

ProfileKindProcesses and threadsSocketsUnlisted syscalls
compile_v2AllowlistAllowedUnix, IPv4, IPv6, route netlink (all confined to the empty network namespace)Fail with ENOSYS
native_v2AllowlistDeniedDeniedFail with ENOSYS
compile_v1DenylistAllowedAs compile_v2Allowed
native_v1DenylistDeniedDeniedAllowed

Every profile denies the interfaces a sandboxed program has no business using: namespace creation and joining (unshare, setns, clone with namespace flags), mounting (mount, pivot_root, the new mount API), kernel modules, kexec, reboot, swap, keyrings, bpf, perf_event_open, ptrace, process_vm_readv/writev, kcmp, userfaultfd, pidfd_getfd, handle-based opens, acct, quotactl and syslog. clone3 and the io_uring calls are not denied but fail with ENOSYS under every profile, so that runtimes fall back to clone and epoll.

A denied syscall kills the process that made it with SIGSYS, which it cannot catch. When that is the task’s own process, the result shows outcome: policy_violation, termination_signal: 31 and exit_code: 159.

An unlisted syscall under a v2 profile is not fatal: it returns ENOSYS (“Function not implemented”), as on a kernel too old to have it. Most programs fall back or report an error.

WorkloadProfile
Compilers, build tools, make, shellscompile_v2
Interpreters and managed runtimes (Python, Node.js, Java, .NET, …)compile_v2: most start threads or helper processes
Go programscompile_v2: the Go runtime always starts threads
Single-threaded native programs (typical C, C++, Rust submissions)native_v2
Multithreaded native programscompile_v2

Use the narrowest profile that works. For a compiled submission, native_v2 means the program cannot fork, spawn threads, run a shell or open a socket, which removes most of what a hostile program would try.

A program that starts a thread or a process under a native profile is killed with policy_violation. If your problems allow threads, run submissions under compile_v2.

The operator decides which profiles requests may use:

VariableDefault
DEFAULT_SANDBOX_PROFILEcompile_v2
ALLOWED_SANDBOX_PROFILEScompile_v2,native_v2

Asking for a profile outside ALLOWED_SANDBOX_PROFILES fails the request with 422 and code profile_not_allowed. GET /info lists the default and the allowed profiles:

"sandbox_profiles": { "default": "compile_v2", "allowed": ["compile_v2", "native_v2"] }

policy_violation. The task made a denied syscall. Either it is doing something the sandbox forbids on purpose, or it started a thread or process under a native profile. Switch to compile_v2 for the latter; the former cannot be allowed.

Strange failures, ENOSYS, “Function not implemented”. A toolchain used a syscall that is not on the v2 allowlist. To confirm, run the same command under compile_v1 on an instance that allows it: if it works there, the allowlist is missing a syscall. Find it with strace -f outside Faber and report it. Operators can allow the v1 profiles as a stopgap; they run anything that is not explicitly dangerous, which is weaker.

Before relying on a new image, run every language you serve through the default profile. The toolchain test script does that for each language in the Dockerfile builder catalog.

Seccomp profiles: how the filters stack, the full denylist and argument rules, how the allowlists were traced, and how x86_64 and ARM64 differ.