copy_fail_family: bridge-level userns gate + 4 new tests (33 total)

The 4 dirty_frag siblings + the GCM variant all gate on unprivileged
user-namespace creation (the XFRM-ESP / AF_RXRPC paths are
unreachable without it). The inner DIRTYFAIL detect functions
already check this, but the check happened deep inside the legacy
code — invisible to the test harness, and the bridge wrappers would
delegate first and only short-circuit afterwards.

Move the check up to the bridge: a single cff_check_userns() helper
inspects ctx->host->unprivileged_userns_allowed and returns
PRECOND_FAIL (with a host-fingerprint-annotated message) BEFORE
calling the inner detect. The inner check stays in place as belt-
and-suspenders.

copy_fail itself uses AF_ALG (no userns needed) and bypasses the
gate — its inner detect still confirms the primitive empirically
via the active probe.

modules/copy_fail_family/skeletonkey_modules.c:
- #include "../../core/host.h" alongside the existing includes.
- new static cff_check_userns(modname, ctx) helper.
- copy_fail_gcm_detect_wrap, dirty_frag_esp_detect_wrap,
  dirty_frag_esp6_detect_wrap, dirty_frag_rxrpc_detect_wrap all
  call cff_check_userns before delegating.
- copy_fail_detect_wrap is intentionally untouched.

tests/test_detect.c: 4 new EXPECT_DETECT cases assert that all 4
gated bridge wrappers return PRECOND_FAIL when
unprivileged_userns_allowed=false, using the existing
h_kernel_5_14_no_userns fingerprint.

29 → 33 tests, all pass on Linux.
This commit is contained in:
2026-05-23 00:02:23 -04:00
parent 2b1e96336e
commit 0d87cbc71c
2 changed files with 60 additions and 0 deletions
+28
View File
@@ -51,6 +51,10 @@ extern const struct skeletonkey_module nft_payload_module;
extern const struct skeletonkey_module stackrot_module;
extern const struct skeletonkey_module sequoia_module;
extern const struct skeletonkey_module vmwgfx_module;
extern const struct skeletonkey_module copy_fail_gcm_module;
extern const struct skeletonkey_module dirty_frag_esp_module;
extern const struct skeletonkey_module dirty_frag_esp6_module;
extern const struct skeletonkey_module dirty_frag_rxrpc_module;
static int g_pass = 0;
static int g_fail = 0;
@@ -337,6 +341,30 @@ static void run_all(void)
run_one("stackrot: kernel 4.4 predates 6.1 → OK",
&stackrot_module, &h_kernel_4_4, SKELETONKEY_OK);
/* ── copy_fail_family bridge userns gate ─────────────────────
* The 4 dirty_frag siblings + the GCM variant all reach the
* bug via XFRM-ESP / AF_RXRPC paths gated on unprivileged
* user-namespace creation. Bridge-layer precondition fires
* before delegating to the inner DIRTYFAIL detect. copy_fail
* itself uses AF_ALG (no userns needed) and bypasses the
* gate — its detect would proceed to the inner active probe. */
run_one("copy_fail_gcm: userns_allowed=false → PRECOND_FAIL",
&copy_fail_gcm_module, &h_kernel_5_14_no_userns,
SKELETONKEY_PRECOND_FAIL);
run_one("dirty_frag_esp: userns_allowed=false → PRECOND_FAIL",
&dirty_frag_esp_module, &h_kernel_5_14_no_userns,
SKELETONKEY_PRECOND_FAIL);
run_one("dirty_frag_esp6: userns_allowed=false → PRECOND_FAIL",
&dirty_frag_esp6_module, &h_kernel_5_14_no_userns,
SKELETONKEY_PRECOND_FAIL);
run_one("dirty_frag_rxrpc: userns_allowed=false → PRECOND_FAIL",
&dirty_frag_rxrpc_module, &h_kernel_5_14_no_userns,
SKELETONKEY_PRECOND_FAIL);
#else
fprintf(stderr, "[i] non-Linux platform: detect() bodies are stubbed; "
"tests skipped (would tautologically pass).\n");