Process & Thread · Section 2
exit_group(2)
Terminate every thread in the calling thread group at once.
Signature
#include <sys/syscall.h>
void exit_group(int status);- status
- Exit status. Only the low 8 bits are observable by wait4/waitid; conventionally 0 for success, non-zero for failure. Status > 127 is conventionally reserved for signal-derived exit codes.
Description
exit_group() ends all threads in the calling thread group (i.e. the whole process, in POSIX terminology) immediately, with the given exit status. Unlike exit() — which terminates only the calling thread — exit_group() walks the entire TGID and tears down every task. This is what glibc's exit() and _exit() wrappers ultimately call; only thread-local pthread_exit() routes to the per-thread sys_exit. exit_group() never returns. The low 8 bits of status are reported to the parent via wait4/waitid (the upper bits are silently discarded — pass small integers).
Architecture mapping
| Architecture | Number | ABI | Entry point |
|---|---|---|---|
| x86 (i386) | 252 | i386 | sys_exit_group |
| x64 (x86_64) | 231 | common | sys_exit_group |
| ARM64 (aarch64) | 94 | — | sys_exit_group |
Kernel history
Introduced in Linux 2.5.35.
2.5.35
exit_group() was added in 2.5.35 (the NPTL series) so glibc could exit an entire pthread process with a single syscall, instead of looping through every thread and calling exit() on each (the historical _exit() behaviour on Linux until NPTL).
seccomp & containers
Docker default profile
Allowed
Podman default profile
Allowed
exit_group() and exit() must be on every default profile and every custom allow-list. Blocking them produces a zombie process that cannot terminate — the kernel would deliver SIGKILL eventually, but the diagnostic experience is terrible. Always include SCMP_SYS(exit_group), SCMP_SYS(exit), and SCMP_SYS(rt_sigreturn) in the bare-minimum allow-list of any seccomp filter.
libseccomp
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);strace example
$ strace -e exit_group /bin/true
exit_group(0) = ?
+++ exited with 0 +++exit_group() is always the last syscall in any -e exit_group trace; pair it with -f to follow multithreaded teardown. The 'status = ?' annotation is strace's way of saying the kernel doesn't return from this call.
Security & observability
exit_group() is rarely interesting to defenders directly — every process emits exactly one. The eBPF tracepoint sched_process_exit fires with the status; pairing with sched_process_exec gives the full lifecycle. Of more interest: processes that never exit (long-running daemons doing nothing), or processes that exit with status 0 immediately after execve() of a privileged binary (failed exploit attempt). Most monitoring focuses on the exec side, not the exit side.