POSIX Signals - SIGSYS
SIGSYS ("Bad System Call") is a POSIX-standard signal sent by the operating system kernel to a process when it attempts to execute an invalid, unsupported, or restricted system call.
Technical Overview
| Property | Value / Behavior |
|---|---|
| POSIX Standard | Defined in POSIX.1-2001 and later. |
| Default Action | Core Dump & Terminate Process (Core). |
| Numeric Value | 31 (x86_64 Linux), 12 (macOS/FreeBSD), 12/31 (ARM64 depending on ABI). |
| Type | Synchronous exception signal (generated internally by the kernel in response to CPU execution). |
When Is SIGSYS Triggered?
1. Seccomp Sandboxing (Linux Containers & Security)
The most common source of SIGSYS in modern systems is Linux seccomp-bpf (Secure Computing Mode).
- Security tools, container runtimes (Docker, Podman, Kubernetes), and sandboxed applications (Chrome, gVisor) set seccomp filters to restrict allowed system calls.
- If a process attempts a forbidden system call configured with
SECCOMP_RET_TRAPorSECCOMP_RET_KILL_PROCESS, the kernel blocks execution and immediately deliversSIGSYS.
2. Invalid or Unrecognized Syscall Number
If a process loads an invalid system call number into the architecture register (e.g., RAX on x86_64 or X8 on ARM64) and triggers a kernel interrupt (syscall / svc #0), the kernel recognizes the number as undefined and sends SIGSYS.
3. Architecture / ABI Mismatch
Running binaries across mismatched execution environments (such as executing 32-bit x86 syscalls on a 64-bit kernel without 32-bit compatibility enabled) causes the kernel to fail argument decoding and raise SIGSYS.
Inspecting SIGSYS via siginfo_t
When a signal handler catches SIGSYS, the kernel populates detailed fault metadata inside the siginfo_t structure:
#include <signal.h>
void handle_sigsys(int sig, siginfo_t *info, void *ucontext) {
// info->si_call_addr : PC address where the syscall instruction occurred
// info->si_syscall : Syscall number that triggered the violation
// info->si_arch : Audit architecture ID (e.g., AUDIT_ARCH_X86_64)
}
This metadata allows sandboxes and runtime tracers to log exactly which restricted system call caused the crash.
How gVisor Uses SIGSYS for System Call Interception
gVisor—the application kernel sandbox used in Kubernetes—uses SIGSYS as a primary mechanism to trap, intercept, and virtualize system calls made by untrusted container applications.
Rather than allowing containerized applications to interact directly with the host Linux kernel, gVisor inserts a user-space kernel (called the Sentry) between the application and the host. To force application system calls into the Sentry safely and efficiently, gVisor relies on Linux seccomp-bpf combined with SIGSYS signals.
The Interception Architecture
In gVisor's default platform mode (systrap), execution flows through a precise signal-trapping loop:
+-------------------------------------------------------------------------------+
| Container Application (User Space) |
| |
| 1. Application executes system call (e.g., open(), socket()) |
| | |
| v |
| 2. Linux Kernel Seccomp BPF Filter evaluates syscall |
| |-- Allowed? --> (None - gVisor blocks host access) |
| +-- Blocked! --> Returns SECCOMP_RET_TRAP |
| | |
| v |
| 3. Linux Kernel sends SIGSYS to gVisor Stub / Handler |
+-----------------------------------|-------------------------------------------+
|
v
+-------------------------------------------------------------------------------+
| gVisor Sentry (User-Space Application Kernel) |
| |
| 4. Stub catches SIGSYS on dedicated gsignal/signal stack |
| 5. Reads siginfo_t (si_syscall, si_call_addr) & registers |
| 6. Sentry emulates the system call in user space |
| 7. Rewrites CPU registers with result and resumes application thread |
+-------------------------------------------------------------------------------+
Step-by-Step Breakdown
1. Seccomp Policy Configuration (SECCOMP_RET_TRAP)
When gVisor initializes a sandbox container, it applies a strict seccomp-bpf filter to the application's process tree. Instead of using SECCOMP_RET_ERRNO (which returns an error code) or SECCOMP_RET_KILL (which abruptly terminates the process), gVisor configures rules to return SECCOMP_RET_TRAP for any system call that requires Sentry emulation.
2. Kernel Generates SIGSYS
When the containerized application attempts to execute a restricted system call (such as openat or connect):
- The host Linux kernel's seccomp engine intercepts the instruction before it touches host kernel logic.
- The kernel generates a
SIGSYSsignal and delivers it synchronously to the faulting thread.
3. Signal Metadata Extraction (siginfo_t)
The signal handler in gVisor’s architecture receives a siginfo_t structure containing crucial metadata about the intercepted call:
si_syscall: The exact system call number (e.g.,SYS_openat).si_call_addr: The program counter address where thesyscallinstruction occurred.si_arch: The target CPU architecture (e.g.,AUDIT_ARCH_X86_64orAUDIT_ARCH_AARCH64).
4. User-Space Emulation & Resumption
Instead of passing the system call to the host kernel, the Sentry intercepts control:
- It inspects the application's CPU registers (e.g.,
RDI,RSI,RDXon x86_64) to read arguments. - The Sentry executes its own pure Go implementation of the Linux kernel interface (handling file systems, virtual memory, networking, and process lifecycles entirely in user space).
- The Sentry writes the return value into the application's register context (e.g.,
RAX) and clears the signal frame. - The application thread resumes execution seamlessly, believing the host kernel handled the syscall natively.
Key Advantages for Sandboxed Containers
- Strong Security Boundary: Untrusted application code can never directly invoke arbitrary host kernel syscalls. The host kernel surface exposed to the container is restricted to only what gVisor's Sentry requires.
- No Kernel Module Needed: By using standard POSIX
SIGSYSsignals triggered viaseccomp-bpf, gVisor operates entirely in unprivileged user space without requiring custom host kernel drivers or special root privileges.
