Three coordinated changes that build on the host_kernel_at_least
landed in 1571b88:
1. core/host gains skeletonkey_host_kernel_in_range(h, lo..., hi...)
— a [lo, hi) bounded-interval check for modules that want the
'vulnerable window' semantics directly. Implemented in terms of
host_kernel_at_least (so the comparison logic stays in one place).
No module uses it yet; available for new modules that want it.
2. 13 modules migrated off the manual
if (v->major < X || (v->major == X && v->minor < Y)) { ... }
pattern onto
if (!skeletonkey_host_kernel_at_least(ctx->host, X, Y, 0)) { ... }
One-line replacements, mechanical, no behavior change.
Migrated: af_packet2, dirty_pipe, fuse_legacy, netfilter_xtcompat,
nf_tables, nft_fwd_dup, nft_payload, nft_set_uaf, overlayfs,
overlayfs_setuid, ptrace_traceme, stackrot, vmwgfx. The repo now
has zero manual 'v->major < X' patterns — every predates-check
reads the same way.
3. tests/test_detect.c expanded from 17 to 29 cases. Adds:
Above-fix coverage on h_kernel_6_12 (10 modules previously
untested): af_packet, af_packet2, af_unix_gc, netfilter_xtcompat,
nft_set_uaf, nft_fwd_dup, nft_payload, stackrot, sequoia, vmwgfx.
Ancient-kernel predates coverage on h_kernel_4_4 (2 more cases):
nft_set_uaf (introduced 5.1), stackrot (introduced 6.1).
Detect-path test coverage now spans most of the corpus that
has a testable host-fingerprint gate. Untested modules from
here on are either userspace bugs whose detect() doesn't gate
on host fields (pwnkit, sudo_samedit, sudoedit_editor),
entrybleed (sysfs-direct, no host gate), or the copy_fail_family
bridge (no ctx->host integration yet).
Verification: Linux (docker gcc:latest, non-root user): 29/29 pass.
macOS (local): 31-module build clean, suite reports 'skipped —
Linux-only' as designed.
Completes the host-fingerprint refactor that started in c00c3b4. Every
module now consults the shared ctx->host (populated once at startup
by core/host.c) instead of re-doing uname / geteuid / /etc/os-release
parsing / fork+unshare(CLONE_NEWUSER) probes per detect().
Migrations applied per module (mechanical, no exploit logic touched):
1. #include "../../core/host.h" inside each module's #ifdef __linux__.
2. kernel_version_current(&v) -> ctx->host->kernel (with the
v -> v-> arrow-vs-dot fix for all later usage). Drops ~20 redundant
uname() calls across the corpus.
3. geteuid() == 0 (the 'already root, nothing to escalate' gate) ->
bool is_root = ctx->host ? ctx->host->is_root : (geteuid() == 0);
This is the key change that lets the unit test suite construct
non-root fingerprints regardless of the test process's actual euid.
4. Per-detect fork+unshare(CLONE_NEWUSER) probe helpers (named
can_unshare_userns / can_unshare_userns_mount across the corpus)
are removed wholesale; their call sites now consult
ctx->host->unprivileged_userns_allowed, which was probed once at
startup. Removes ~10 per-scan fork()s.
Modules touched by this commit (22):
Batch A (7): dirty_pipe, dirty_cow, ptrace_traceme, pwnkit,
cgroup_release_agent, overlayfs_setuid, and entrybleed
(no migration target — KPTI gate stays as direct sysfs
read; documented as 'no applicable pattern').
Batch B (7): nf_tables, cls_route4, netfilter_xtcompat, af_packet,
af_packet2, af_unix_gc, fuse_legacy.
Batch C (8): stackrot, nft_set_uaf, nft_fwd_dup, nft_payload,
sudo_samedit, sequoia, sudoedit_editor, vmwgfx.
Combined with the 4 modules already migrated (dirtydecrypt, fragnesia,
pack2theroot, overlayfs) and the 5-module copy_fail_family bridge,
the entire registered corpus now goes through ctx->host. The 4
'fork+unshare per detect()' helpers that existed across nf_tables,
cls_route4, netfilter_xtcompat, af_packet, af_packet2, fuse_legacy,
nft_set_uaf, nft_fwd_dup, nft_payload, sequoia,
cgroup_release_agent, and overlayfs_setuid are now gone — replaced by
the single startup probe in core/host.c.
Verification:
- Linux (docker gcc:latest + libglib2.0-dev): full clean build links
31 modules; tests/test_detect.c: 8/8 pass.
- macOS (local): full clean build links 31 modules (Mach-O, 172KB);
test suite reports skipped as designed on non-Linux.
Subsequent commits can add more EXPECT_DETECT cases in
tests/test_detect.c — the host-fingerprint paths in every module are
now uniformly testable via synthetic struct skeletonkey_host instances.
Every kernel-LPE module that uses Linux-only headers (splice, posix_fadvise,
linux/netlink.h, sys/ptrace.h, etc.) now follows the same #ifdef __linux__
pattern the new modules already used: Linux body in the ifdef, stub
detect/exploit/cleanup returning SKELETONKEY_PRECOND_FAIL on non-Linux,
platform-neutral rule strings + module struct + register fn left outside.
14 modules wrapped:
dirty_pipe (already done above), af_packet, af_packet2,
cgroup_release_agent, cls_route4, dirty_cow, fuse_legacy,
netfilter_xtcompat, nf_tables, nft_fwd_dup, nft_payload,
overlayfs, overlayfs_setuid, ptrace_traceme.
Several modules previously had ad-hoc partial stubs (af_packet2 faked
SIOCSIFFLAGS/MAP_LOCKED, netfilter_xtcompat faked sysv-msg syscalls,
the nft_* modules had 3 partial __linux__ islands each, fuse_legacy /
nf_tables had inner-only ifdef blocks) — all replaced with the uniform
outer-wrap shape from dirty_pipe / dirtydecrypt / fragnesia / pack2theroot.
Where a module includes core/kernel_range.h, core/finisher.h, or
core/offsets.h, those are now inside the ifdef block as well — silences
clangd's "unused-includes" LSP warning on macOS while keeping them
present for the real Linux build.
No exploit logic, constant, struct, shellcode byte, or rule string was
modified — only include placement and ifdef markers.
Build verification:
macOS (local): make clean && make → Mach-O x86_64, 31 modules
registered, --scan reports each Linux-only module as
"Linux-only module — not applicable here".
Linux (docker gcc:latest + libglib2.0-dev): make clean && make →
ELF 64-bit, 31 modules. Exploit code paths unchanged.