Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Feature -> version -> availability matrix (XR-201..XR-210)

Overview

This matrix tracks ten modern kernel features through the chain that decides whether a program can actually use them: kernel feature -> minimum kernel version -> libc availability -> runtime prerequisites. A feature is not usable because a syscall number exists; it is usable when the running kernel has it and is configured for it, when your libc either wraps it or leaves you a supported way to call it raw, and when the runtime gates (capabilities, boot parameters, cgroup versions, seccomp behavior) are satisfied. The rows are XR-201..XR-210 of the coverage ledger (statuses authoritative there: all 10 documented; XR-208/XR-209 upgraded in the 7-d pass).

How to read each row:

  • The kernel version column carries the minimum version in which the feature’s syscall(s) appeared; staged follow-ups are listed in the prose.
  • The glibc and musl columns state what each libc gives you: a wrapper (with the symbol version), a raw syscall()/SYS_* macro path, or nothing.
  • The runtime prerequisites column names the config gate, capability, boot parameter, or filter interaction that still stands between you and the feature on an otherwise-current system.

Provenance and honesty: version claims below were tag-probe verified during this encyclopedia’s research passes – kernel tables and UAPI headers were fetched at consecutive release tags on torvalds/linux and diffed; glibc claims come from the glibc NEWS/Versions files cross-checked with nm -D on the installed glibc 2.41; musl claims come from the musl 1.2.5 tree and WHATSNEW. Claims that could not be double-sourced are flagged in place instead of asserted. Every syscall cites its SC-<name> row in the syscall ledger.

One correction is recorded in place: the ledger’s XR-209 note historically said cachestat landed in 6.1; the tag probe in the memory-syscall pass fixed the value at 6.5 (absent at v6.4, present at v6.5; cachestat.2 HISTORY agrees). This page carries 6.5.

Index of the ten rows (statuses are the ledger’s):

RowFeature headlineStatus
XR-201io_uring (5.1/5.5+)documented
XR-202pidfd API (5.3+)documented
XR-203openat2 (5.6) / landlock (5.13)documented
XR-204clone3 (5.3+)documented
XR-205time64 (5.1, 32-bit)documented
XR-206statx (4.11)documented
XR-207mount API (5.2+)documented
XR-208memfd_secret (5.14)documented
XR-209cachestat (6.5)documented
XR-210fchmodat2 (6.6)documented

XR-201 – io_uring (5.1/5.5+) -> liburing, no glibc wrapper -> kernel config + caps

featurekernel versionglibc supportmusl supportruntime prerequisites
io_uring (SC-io_uring_setup 425, SC-io_uring_enter 426, SC-io_uring_register 427 – numbers uniform across x64/i386/g64/arm)trio since 5.1 (ledger + man pages); the row’s headline “5.1/5.5+” marks the early maturation window – the io_uring man pages carry no VERSIONS sections, so 5.1 rests on the ledger/hubnone: no wrapper and no symbols across glibc releases (NEWS has zero io_uring hits; nm -D on glibc 2.41 finds none); userspace goes through liburing or raw syscall()musl 1.2.5: raw syscall() only (no wrapper)CONFIG_IO_URING (present in the v5.1 Kconfig); sysctls io_uring_disabled/io_uring_group exist since 6.6 (tag-verified absent at v6.5)

The three-call protocol is the interface: io_uring_setup creates a ring, the SQ/CQ rings are mmaped (not read through syscalls), and io_uring_enter submits/completes. The setup flags (IORING_SETUP_IOPOLL, SQPOLL, SQ_AFF, CQSIZE, CLAMP, ATTACH_WQ, R_DISABLED, … – a 1U<<0..1U<<16 block in linux/io_uring.h) and the 66 IORING_OP_* enumerators are the real feature surface: “io_uring supports X” almost always means “this kernel added an op or flag”, which is why the row tracks versions per-feature rather than one number. Registered buffers and files are pinned and bounded by RLIMIT_MEMLOCK (SC-io_uring_register semantics). Because libc never grew a wrapper, availability is decided entirely by kernel config + liburing on the userspace side. Pages: io_uring_setup; kernel side: io_uring async API, io_uring internals, block io_uring.

XR-202 – pidfd API (5.3+) -> glibc 2.30+ era wrappers -> kernel config

featurekernel versionglibc supportmusl supportruntime prerequisites
pidfd API: SC-pidfd_send_signal (424), SC-pidfd_open (434), SC-pidfd_getfd (438); CLONE_PIDFD on SC-clone/SC-clone3staged: CLONE_PIDFD 5.1 (tag check v5.1), pidfd_send_signal 5.1, pidfd_open 5.3, pidfd_getfd 5.6; clone3 gained the pidfd field 5.3, set_tid 5.5, CLONE_INTO_CGROUP 5.72.36 for the pidfd trio (pidfd_open/pidfd_getfd/pidfd_send_signal @@GLIBC_2.36: NEWS + Versions + nm), pidfd_getpid 2.39; gettid itself 2.30; the ledger headline “glibc 2.30+” covers that eramusl 1.2.5: raw syscall() (no pidfd wrappers)pidfd_getfd requires the usual ptrace-style permission against the target; CLONE_INTO_CGROUP needs a cgroup v2 fd; seccomp profiles that filter by syscall number must pass the new slots

pidfds make PID races disappear by making the kernel hand you an fd instead of a reusable number. The practical availability story is two-sided: kernel staging (5.1..5.7) and the glibc wrapper landing at 2.36 (cached 2021 man pages still say “no wrapper” – stale, both sources named on the per-call pages). Flag-level notes from the header checks: PIDFD_NONBLOCK is O_NONBLOCK, and PIDFD_THREAD (O_EXCL) exists in the installed linux/pidfd.h but predates documentation in the cached man pages – flagged, not asserted. waitid learned the P_PIDFD idtype so the wait family consumes pidfds directly. Pages: pidfd_open, pidfd_getfd, pidfd_send_signal, clone.

XR-203 – openat2 (5.6) / landlock (5.13) -> no glibc wrapper era -> headers and raw syscall()

featurekernel versionglibc supportmusl supportruntime prerequisites
SC-openat2 (437 uniform) + struct open_how/RESOLVE_* flags5.6no wrapper released (man7 wrapper note + nm -D 2.41); glibc 2.43 lists it as pending (NEWS + Versions)musl 1.2.5: syscall(SYS_openat2, ...) via the SYS_openat2 macro (no function)none beyond the kernel version itself – the gate is that your libc has no wrapper, so callers use syscall() with the UAPI header
featurekernel versionglibc supportmusl supportruntime prerequisites
Landlock: SC-landlock_create_ruleset (444), SC-landlock_add_rule (445), SC-landlock_restrict_self (446) – numbers uniform5.13 (ABI v1); ABI v2 5.19, v3 6.2, v4 6.7none (no landlock symbols on glibc 2.41)musl 1.2.5: syscall() (numbers 444-446 present)CONFIG_SECURITY_LANDLOCK (Kconfig at v5.13); the lsm= boot list must include landlock (landlock.7); SC-landlock_restrict_self needs no_new_privs set or CAP_SYS_ADMIN

These two are the faces of the “no-wrapper era”: kernel features that shipped years before any libc blessed them, so correct use means the UAPI headers (linux/openat2.h, linux/landlock.h) plus raw syscall(). For openat2 the extension discipline is the same copy_struct_from_user pattern as clone3: the kernel pins OPEN_HOW_SIZE with build-time checks, and the RESOLVE_* flag block (0x01..0x20: NO_XDEV, NO_MAGICLINKS, NO_SYMLINKS, BENEATH, IN_ROOT, SYMLINK_NOFOLLOW) is what makes path traversal auditable. A master-only OPENAT2_REGULAR flag exists in the UAPI header but not in the 2021 man page – flagged on the openat2 page. For landlock, the three-call fd protocol (create ruleset, add rules, restrict self) is deliberately libc-free. Flag honesty: some Landlock RESTRICT_SELF_* constants are not yet in the installed linux/landlock.h on the reference host. Pages: openat2, landlock_create_ruleset, seccomp; kernel side: seccomp.

XR-204 – clone3 (5.3+) -> glibc 2.34+ posix_spawn routing -> fallback story

featurekernel versionglibc supportmusl supportruntime prerequisites
SC-clone3 (435 uniform) + struct clone_args with explicit size5.3; staged extras: set_tid/set_tid_size 5.5, cgroup field (CLONE_INTO_CGROUP) 5.7no public wrapper ever (man page, Versions, nm -D agree); internal use since glibc 2.34: the spawn machinery routes through clone3 with an ENOSYS fallback to clone (commit d8ea0d01 per the process-chapter research)musl 1.2.5: raw syscall()size discipline: size < 64 -> EINVAL, > PAGE_SIZE -> E2BIG, zero-fill trailing bytes required (copy_struct_from_user); seccomp profiles that reject unknown slots shape the fallback story

clone3 is the extensible clone: the old flat flag word becomes a versioned struct whose size is part of the ABI, so future fields do not need a new syscall. The size discipline is pinned by three struct-version constants (CLONE_ARGS_SIZE_VER0/1/2 = 64/80/88), the kernel rejects CSIGNAL-style legacy bits in clone3_args_valid, and unknown trailing bytes must be zero – the extension contract XR-203’s openat2 shares. Its glibc story is the mirror image of pidfd’s: never a public wrapper, but quietly load-bearing since 2.34 – when posix_spawn can use clone3 it does, and when the host kernel (or a seccomp profile) says no, glibc falls back to the legacy clone path. That makes clone3 the rare feature whose libc “support” is invisible from headers and visible only in behavior. Pages: clone (covers clone3), posix_spawn.

XR-205 – time64 (5.1, 32-bit) -> glibc 2.30/2.32/2.34 stages -> runtime fallback

featurekernel versionglibc supportmusl supportruntime prerequisites
the _time64 syscall twins (clock_, timerfd_, signal-time families; 20 slots per the 32-bit table probe)5.1: the v5.0 -> v5.1 syscall_32.tbl diff introduces the time64 rows (tag-diff verified); syscalls(2) omits them – gap recorded in the ledgerstaged: 2.30 dispatches time64 wrappers first with a fallback and documents the seccomp caveat (NEWS); 2.34 makes _TIME_BITS=64 fully supported with kernel >= 5.1 required (NEWS verbatim); the ledger’s “2.32” stage is not further detailed in the sources consultedmusl 1.2.0 switched time_t to 64-bit on all architectures (WHATSNEW; alltypes.h.in TYPEDEF _Int64 time_t) – no twin dispatch neededthe NEWS-documented seccomp hazard: filters that reject unknown 32-bit slots can break the fallback chain on old kernels

time64 is the y2038 fix for 32-bit architectures: new syscall slots whose structs carry 64-bit time fields. The signal-time chapter tables ten such twins (rt_sigtimedwait_time64, clock_gettime_time64 and friends) alongside the timerfd and futex twins; the syscall ledger tracks each slot separately because the numbers only exist on the 32-bit tables (i386/arm, no g64 entries). The glibc stages matter because a 32-bit rootfs built before 2.34 silently runs 32-bit time_t; musl sidestepped the whole matrix by making time_t 64-bit everywhere in 1.2.0. Pages: clock_gettime/clock_getres, clock_nanosleep; libc side: time, time_t and 2038.

XR-206 – statx (4.11) -> glibc statx wrapper 2.28 -> header gates

featurekernel versionglibc supportmusl supportruntime prerequisites
SC-statx (x64 332 / i386 383 / g64 291 / arm 397) + STATX_*/STATX_ATTR_* mask protocol4.11wrapper since 2.28 (statx@@GLIBC_2.28, nm-verified on the reference glibc 2.41)not verified in the sources consulted for this matrixfields are filesystem-dependent: STATX_WRITE_ATOMIC fields exist since man-pages 6.19 with block devices 6.11 and xfs+ext4 files 6.13; stx_atomic_write_unit_max_opt is in the 6.19 man page but not in the installed linux/stat.h

statx is the matrix’s counter-example to the no-wrapper era: a modern stat interface that did get a glibc wrapper quickly (2.28 vs kernel 4.11), with the usual header-gate caveat that newer STATX_* bits lag the installed libc headers (STATX_* masks run 0x1..0x10000 in linux/stat.h, with STATX_ATTR_WRITE_ATOMIC = 0x00400000 header-sourced). Availability of a field is therefore a three-way gate: kernel, libc header, filesystem. Pages: statx, stat family.

XR-207 – mount API (5.2+) fsopen/fsmount/move_mount -> glibc 2.36 wrappers -> libmount

featurekernel versionglibc supportmusl supportruntime prerequisites
SC-fsopen (430), SC-fsconfig (431), SC-fsmount (432), SC-fspick (433), SC-move_mount (429), SC-open_tree (428) – all 5.2; SC-mount_setattr (442) 5.125.2 suite (+ mount_setattr 5.12)2.36 added all seven wrappers (NEWS “Version 2.36”: fsopen, fsmount, move_mount, fsconfig, fspick, open_tree AND mount_setattr; @@GLIBC_2.36 nm-verified; declared in sys/mount.h). Recorded disagreement: the cached 2021 mount_setattr.2 still says glibc provides no wrapper – stale; both sources named on the pagemusl 1.2.5: syscall(); __NR_* macros for the family exist since musl 1.1.24 (WHATSNEW)CAP_SYS_ADMIN for the whole family; MOUNT_ATTR_IDMAP additionally needs a userns_fd; OPEN_TREE_NAMESPACE/FSMOUNT_NAMESPACE are master-only UAPI additions (absent through v6.18 – version pin unverified)

The fd-based mount API replaces the flag-soup mount(2) path with an object-per-step protocol: open a filesystem context, configure it, create an attachable mount fd, move it into the tree. The UAPI surface (FSOPEN_*, FSCONFIG_*, FSMOUNT_*, MOVE_MOUNT_*, FSPICK_*, OPEN_TREE_*, MOUNT_ATTR_* plus struct mount_attr) is header-verified, and the mount_setattr wrapper is where the two sources disagree – the fetched 2021 man page predates the 2.36 wrapper, so the page carries both attributions. The ledger row’s “no glibc wrapper -> libmount” headline predates glibc 2.36; the current facts (2.36 wrappers) are carried above, and libmount remains the canonical consumer of the API. Pages: fsopen, fsconfig, fsmount, move_mount, open_tree, fspick, mount_setattr; kernel side: mount API.

XR-208 – memfd_secret (5.14) -> no glibc wrapper -> SECRETMEM config

featurekernel versionglibc supportmusl supportruntime prerequisites
SC-memfd_secret (x64 447, i386 447; no g64 slot, no arm slot)5.14 (x86-first: the arm32 table carries only a reservation comment; no asm-generic slot)none (nm -D on glibc 2.41 finds no memfd_secret symbol; no declaration anywhere under /usr/include except the SYS_memfd_secret macro in bits/syscall.h, guarded by #ifdef __NR_memfd_secret)musl 1.2.5: syscall() path noted for x86_64CONFIG_SECRETMEM (Kconfig: default y behind EXPERT, depends on ARCH_HAS_SET_DIRECT_MAP); secretmem.enable boot/module parameter – off by default at v5.14, flipped to on at v6.5 (tag bisect v6.4 off / v6.5 on; kernel-source-sourced, still no doc file states it – flagged, not asserted); ENOSYS when the gate is closed

memfd_secret carves memory out of the kernel’s direct map so it is not reachable by the kernel’s linear mapping; creation returns an fd whose pages are __GFP_ZERO-allocated and accounted against RLIMIT_MEMLOCK, and the descriptor is deliberately excluded from fork/exec sharing per the man page. The row upgraded to documented when the syscall encyclopedia grew the full memfd_secret page (man page in the cached tree plus mm/secretmem.c/mm/Kconfig), and the header sweep pinned the libc columns: there is no memfd_secret declaration anywhere in the installed headers – /usr/include/x86_64-linux-gnu/sys/random.h declares only getrandom/getentropy, and the existing /usr/include/linux/memfd.h (linux-libc-dev) carries only the MFD_* flags of memfd_create – so the sole userspace surface is the SYS_memfd_secret macro (bits/syscall.h, #ifdef __NR_memfd_secret) and raw syscall(). The one flagged sub-claim is the v6.4/v6.5 secretmem.enable default flip, which rests on the tag bisect (kernel-source-sourced, no doc file). Sibling: memfd_create; kernel side: memfd.

XR-209 – cachestat (6.5) -> no glibc wrapper -> structs in linux/mman.h, raw syscall()

featurekernel versionglibc supportmusl supportruntime prerequisites
SC-cachestat (451 uniform across x64/i386/g64/arm)6.5 – tag-probe verified (absent at v6.4, present at v6.5); cachestat.2 HISTORY says 6.5. Correction: the ledger note historically read 6.1; 6.5 is the verified value and this page carries itnone (no wrapper; NEWS has no cachestat item; nm -D on glibc 2.41 finds no symbol; the SYS_cachestat macro sits in bits/syscall.h under #ifdef __NR_cachestat)musl 1.2.5: the SYS_cachestat macro only – no functionno CONFIG_CACHESTAT_SYSCALL prerequisite at the tool level; one kernel-source fact not yet in the 6.19 man page: EPERM can come from the kernel’s cachestat permission check

cachestat answers “which pages of this file are in the page cache” – a query page-cache operators previously approximated with mincore tricks. The row upgraded to documented when the syscall encyclopedia grew the full cachestat page and the header sweep pinned the struct story: there is no /usr/include/linux/cachestat.h on the reference host, but the structs are shipped anyway – linux-libc-dev’s /usr/include/linux/mman.h defines struct cachestat_range (the off/len pair) and struct cachestat (the five __u64 counters), matching the UAPI – so the real use pattern is #include <linux/mman.h> plus syscall(SYS_cachestat, ...) with the SYS_cachestat macro from bits/syscall.h. (The sibling syscall page’s “declare the structs locally” note reflects a glibc-header check; the kernel UAPI header does ship them on this host – both attributions stand.) The EPERM gate stays kernel-source-sourced per the linked page. Sibling: mincore; kernel side: page cache; performance view of cachestat.

XR-210 – fchmodat2 (6.6) -> glibc 2.40 fchmodat routing -> runtime prereq

featurekernel versionglibc supportmusl supportruntime prerequisites
SC-fchmodat2 (452 uniform)6.6 – present in include/uapi/asm-generic/unistd.h at v6.6, absent at v6.5 (tag probe); the syscall ledger’s Since column still reads “–” and its row is source-limited (no man page in the cached tree; a fetched copy exists in the research cache)no exported fchmodat2 symbol on glibc 2.41 (nm -D); SYS_fchmodat2 is in the installed bits/syscall.h; the “glibc >= 2.40 routes fchmodat through fchmodat2 when flags require” claim is version-note sourced but not observable on the 2.41 reference host – kept flagged, not assertednot verified in the sources consulted for this matrixnone beyond the kernel version; the glibc routing question is the row’s open verification item

fchmodat2 is the newest row in the matrix and also the least settled at the libc layer: the kernel-side story is tag-probe solid (6.6, slot 452 on every tabulated arch), while the glibc-side routing claim (2.40) cannot be confirmed on the 2.41 reference host because there is no exported symbol to observe and no cached NEWS section naming it. That is exactly the kind of claim this encyclopedia marks uncertain instead of smoothing over. The syscall ledger keeps SC-fchmodat2 itself source-limited for the same reason (no man page in the cached tree), so the row and the ledger agree on where the boundary of verified knowledge sits. Page: chmod (covers fchmodat2 notes).


Status summary

RowsStatusNote
XR-201 .. XR-210documented (10)versions tag-probe or NEWS/nm double-sourced; open sub-claims flagged in place (XR-208’s boot-param default flip remains tag-bisect sourced; XR-209’s EPERM gate kernel-source sourced)

Interview Questions

  1. Why can’t you #include <linux/io_uring.h> and call io_uring from plain libc code? You can – but only via raw syscall(): glibc never grew wrappers (XR-201), so liburing exists precisely to own the ring mmap and submission plumbing.
  2. Your seccomp profile rejects unknown syscalls. Which matrix rows break? The fallback-dependent ones: glibc’s clone3-internal posix_spawn (XR-204) and the time64 fallback chain on 32-bit (XR-205) both assume a conventional ENOSYS-style rejection.
  3. Which row changed version during this encyclopedia’s research, and why? XR-209: cachestat was noted as 6.1 in the ledger, but the v6.4/v6.5 tag probe and cachestat.2 HISTORY fix it at 6.5 (correction recorded above).

Cross-References