diff --git a/core/host.c b/core/host.c index 670324e..ecd4096 100644 --- a/core/host.c +++ b/core/host.c @@ -252,6 +252,14 @@ bool skeletonkey_host_kernel_at_least(const struct skeletonkey_host *h, return h->kernel.patch >= patch; } +bool skeletonkey_host_kernel_in_range(const struct skeletonkey_host *h, + int lo_M, int lo_m, int lo_p, + int hi_M, int hi_m, int hi_p) +{ + return skeletonkey_host_kernel_at_least(h, lo_M, lo_m, lo_p) && + !skeletonkey_host_kernel_at_least(h, hi_M, hi_m, hi_p); +} + void skeletonkey_host_print_banner(const struct skeletonkey_host *h, bool json) { if (json || h == NULL) return; diff --git a/core/host.h b/core/host.h index e171b35..9b58ca9 100644 --- a/core/host.h +++ b/core/host.h @@ -103,4 +103,25 @@ void skeletonkey_host_print_banner(const struct skeletonkey_host *h, bool json); bool skeletonkey_host_kernel_at_least(const struct skeletonkey_host *h, int major, int minor, int patch); +/* True iff h->kernel is in [lo, hi). Useful for "vulnerable range" + * gates where the simple `kernel_range_is_patched` backport model + * doesn't apply — e.g. a feature added in X.Y and removed/superseded + * in W.Z, or a per-module "vulnerable only on these specific kernel + * lines" check. + * + * Equivalent to: + * host_kernel_at_least(h, lo...) && !host_kernel_at_least(h, hi...) + * + * For "predates the bug" alone use host_kernel_at_least directly; the + * `in_range` form is for the bounded interval case. + * + * Example: + * if (host_kernel_in_range(h, 5, 8, 0, 5, 17, 0)) + * // kernel 5.8 ≤ K < 5.17 — vulnerable window per the mainline + * // introduction/fix dates (ignoring stable backports) + */ +bool skeletonkey_host_kernel_in_range(const struct skeletonkey_host *h, + int lo_major, int lo_minor, int lo_patch, + int hi_major, int hi_minor, int hi_patch); + #endif /* SKELETONKEY_HOST_H */ diff --git a/modules/af_packet2_cve_2020_14386/skeletonkey_modules.c b/modules/af_packet2_cve_2020_14386/skeletonkey_modules.c index 77d2533..3378414 100644 --- a/modules/af_packet2_cve_2020_14386/skeletonkey_modules.c +++ b/modules/af_packet2_cve_2020_14386/skeletonkey_modules.c @@ -106,7 +106,7 @@ static skeletonkey_result_t af_packet2_detect(const struct skeletonkey_ctx *ctx) } /* Bug introduced in 4.6 (tpacket_rcv VLAN path). Pre-4.6 immune. */ - if (v->major < 4 || (v->major == 4 && v->minor < 6)) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 4, 6, 0)) { if (!ctx->json) { fprintf(stderr, "[+] af_packet2: kernel %s predates the bug (introduced in 4.6)\n", v->release); diff --git a/modules/dirty_pipe_cve_2022_0847/skeletonkey_modules.c b/modules/dirty_pipe_cve_2022_0847/skeletonkey_modules.c index c61eee7..49ee2f4 100644 --- a/modules/dirty_pipe_cve_2022_0847/skeletonkey_modules.c +++ b/modules/dirty_pipe_cve_2022_0847/skeletonkey_modules.c @@ -270,7 +270,7 @@ static skeletonkey_result_t dirty_pipe_detect(const struct skeletonkey_ctx *ctx) } /* Bug introduced in 5.8. */ - if (v->major < 5 || (v->major == 5 && v->minor < 8)) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 5, 8, 0)) { if (!ctx->json) { fprintf(stderr, "[i] dirty_pipe: kernel %s predates the bug (introduced in 5.8)\n", v->release); diff --git a/modules/fuse_legacy_cve_2022_0185/skeletonkey_modules.c b/modules/fuse_legacy_cve_2022_0185/skeletonkey_modules.c index 7b1e01b..b6c816a 100644 --- a/modules/fuse_legacy_cve_2022_0185/skeletonkey_modules.c +++ b/modules/fuse_legacy_cve_2022_0185/skeletonkey_modules.c @@ -177,7 +177,7 @@ static skeletonkey_result_t fuse_legacy_detect(const struct skeletonkey_ctx *ctx /* Bug introduced in 5.1 (when legacy_parse_param landed). Pre-5.1 * kernels predate the code path entirely. */ - if (v->major < 5 || (v->major == 5 && v->minor < 1)) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 5, 1, 0)) { if (!ctx->json) { fprintf(stderr, "[+] fuse_legacy: kernel %s predates the bug introduction\n", v->release); diff --git a/modules/netfilter_xtcompat_cve_2021_22555/skeletonkey_modules.c b/modules/netfilter_xtcompat_cve_2021_22555/skeletonkey_modules.c index 3fee2d9..9833d6a 100644 --- a/modules/netfilter_xtcompat_cve_2021_22555/skeletonkey_modules.c +++ b/modules/netfilter_xtcompat_cve_2021_22555/skeletonkey_modules.c @@ -130,7 +130,7 @@ static skeletonkey_result_t netfilter_xtcompat_detect(const struct skeletonkey_c return SKELETONKEY_TEST_ERROR; } - if (v->major < 2 || (v->major == 2 && v->minor < 6)) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 2, 6, 0)) { if (!ctx->json) { fprintf(stderr, "[+] netfilter_xtcompat: kernel %s predates the bug introduction\n", v->release); diff --git a/modules/nf_tables_cve_2024_1086/skeletonkey_modules.c b/modules/nf_tables_cve_2024_1086/skeletonkey_modules.c index 9763c20..8cd22a9 100644 --- a/modules/nf_tables_cve_2024_1086/skeletonkey_modules.c +++ b/modules/nf_tables_cve_2024_1086/skeletonkey_modules.c @@ -140,7 +140,7 @@ static skeletonkey_result_t nf_tables_detect(const struct skeletonkey_ctx *ctx) } /* Bug introduced in 5.14. Anything below predates it. */ - if (v->major < 5 || (v->major == 5 && v->minor < 14)) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 5, 14, 0)) { if (!ctx->json) { fprintf(stderr, "[i] nf_tables: kernel %s predates the bug " "(introduced in 5.14)\n", v->release); diff --git a/modules/nft_fwd_dup_cve_2022_25636/skeletonkey_modules.c b/modules/nft_fwd_dup_cve_2022_25636/skeletonkey_modules.c index 8e13f12..d3ed681 100644 --- a/modules/nft_fwd_dup_cve_2022_25636/skeletonkey_modules.c +++ b/modules/nft_fwd_dup_cve_2022_25636/skeletonkey_modules.c @@ -127,7 +127,7 @@ static skeletonkey_result_t nft_fwd_dup_detect(const struct skeletonkey_ctx *ctx /* The offload code path only exists from 5.4 onward. Anything * older predates the bug. */ - if (v->major < 5 || (v->major == 5 && v->minor < 4)) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 5, 4, 0)) { if (!ctx->json) { fprintf(stderr, "[i] nft_fwd_dup: kernel %s predates the bug " "(nft offload hook introduced in 5.4)\n", v->release); diff --git a/modules/nft_payload_cve_2023_0179/skeletonkey_modules.c b/modules/nft_payload_cve_2023_0179/skeletonkey_modules.c index 25d1491..fa4c354 100644 --- a/modules/nft_payload_cve_2023_0179/skeletonkey_modules.c +++ b/modules/nft_payload_cve_2023_0179/skeletonkey_modules.c @@ -128,7 +128,7 @@ static skeletonkey_result_t nft_payload_detect(const struct skeletonkey_ctx *ctx /* Bug introduced with the set-payload extension in 5.4. Anything * below 5.4 predates the affected codepath entirely. */ - if (v->major < 5 || (v->major == 5 && v->minor < 4)) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 5, 4, 0)) { if (!ctx->json) { fprintf(stderr, "[i] nft_payload: kernel %s predates the bug " "(set-payload extension landed in 5.4)\n", diff --git a/modules/nft_set_uaf_cve_2023_32233/skeletonkey_modules.c b/modules/nft_set_uaf_cve_2023_32233/skeletonkey_modules.c index fc30ade..d894616 100644 --- a/modules/nft_set_uaf_cve_2023_32233/skeletonkey_modules.c +++ b/modules/nft_set_uaf_cve_2023_32233/skeletonkey_modules.c @@ -144,7 +144,7 @@ static skeletonkey_result_t nft_set_uaf_detect(const struct skeletonkey_ctx *ctx /* Bug introduced in 5.1 (anonymous-set support). Anything below * predates it — report OK (not vulnerable to *this* CVE). */ - if (v->major < 5 || (v->major == 5 && v->minor < 1)) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 5, 1, 0)) { if (!ctx->json) { fprintf(stderr, "[i] nft_set_uaf: kernel %s predates the bug " "(anonymous-set support landed in 5.1)\n", v->release); diff --git a/modules/overlayfs_cve_2021_3493/skeletonkey_modules.c b/modules/overlayfs_cve_2021_3493/skeletonkey_modules.c index a29d516..10eed7e 100644 --- a/modules/overlayfs_cve_2021_3493/skeletonkey_modules.c +++ b/modules/overlayfs_cve_2021_3493/skeletonkey_modules.c @@ -192,7 +192,7 @@ static skeletonkey_result_t overlayfs_detect(const struct skeletonkey_ctx *ctx) * Ubuntu fix is per-release-specific; conservatively report * VULNERABLE if version < 5.13 (covers most affected Ubuntu LTS), * and recommend --active for confirmation. */ - if (v.major < 5 || (v.major == 5 && v.minor < 13)) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 5, 13, 0)) { if (!ctx->json) { fprintf(stderr, "[!] overlayfs: Ubuntu kernel %s in vulnerable range — " "re-run with --active to confirm\n", v.release); diff --git a/modules/overlayfs_setuid_cve_2023_0386/skeletonkey_modules.c b/modules/overlayfs_setuid_cve_2023_0386/skeletonkey_modules.c index a2ba0f4..66d8cfc 100644 --- a/modules/overlayfs_setuid_cve_2023_0386/skeletonkey_modules.c +++ b/modules/overlayfs_setuid_cve_2023_0386/skeletonkey_modules.c @@ -107,7 +107,7 @@ static skeletonkey_result_t overlayfs_setuid_detect(const struct skeletonkey_ctx /* Bug introduced in 5.11 when ovl copy-up was generalized. * Pre-5.11 immune via a different code path. */ - if (v->major < 5 || (v->major == 5 && v->minor < 11)) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 5, 11, 0)) { if (!ctx->json) { fprintf(stderr, "[+] overlayfs_setuid: kernel %s predates the bug " "(introduced in 5.11)\n", v->release); diff --git a/modules/ptrace_traceme_cve_2019_13272/skeletonkey_modules.c b/modules/ptrace_traceme_cve_2019_13272/skeletonkey_modules.c index 6071b9c..436a0b8 100644 --- a/modules/ptrace_traceme_cve_2019_13272/skeletonkey_modules.c +++ b/modules/ptrace_traceme_cve_2019_13272/skeletonkey_modules.c @@ -81,7 +81,7 @@ static skeletonkey_result_t ptrace_traceme_detect(const struct skeletonkey_ctx * /* Bug existed since ptrace's inception (early 2.x); anything * pre-LTS-backport is vulnerable. Anything < 4.4 in our range * model defaults to vulnerable since no entry covers it. */ - if (v->major < 4 || (v->major == 4 && v->minor < 4)) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 4, 4, 0)) { if (!ctx->json) { fprintf(stderr, "[!] ptrace_traceme: ancient kernel %s — assume VULNERABLE\n", v->release); diff --git a/modules/stackrot_cve_2023_3269/skeletonkey_modules.c b/modules/stackrot_cve_2023_3269/skeletonkey_modules.c index ebfb657..d8c1242 100644 --- a/modules/stackrot_cve_2023_3269/skeletonkey_modules.c +++ b/modules/stackrot_cve_2023_3269/skeletonkey_modules.c @@ -159,7 +159,7 @@ static skeletonkey_result_t stackrot_detect(const struct skeletonkey_ctx *ctx) /* Bug introduced in 6.1 (when maple tree landed). Pre-6.1 kernels * use rbtree-based VMAs and don't have this bug. */ - if (v->major < 6 || (v->major == 6 && v->minor < 1)) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 6, 1, 0)) { if (!ctx->json) { fprintf(stderr, "[+] stackrot: kernel %s predates maple-tree VMA code (introduced in 6.1)\n", v->release); diff --git a/modules/vmwgfx_cve_2023_2008/skeletonkey_modules.c b/modules/vmwgfx_cve_2023_2008/skeletonkey_modules.c index 5c62d0f..034f026 100644 --- a/modules/vmwgfx_cve_2023_2008/skeletonkey_modules.c +++ b/modules/vmwgfx_cve_2023_2008/skeletonkey_modules.c @@ -237,7 +237,7 @@ static skeletonkey_result_t vmwgfx_detect(const struct skeletonkey_ctx *ctx) /* Pre-vmwgfx kernels (no driver shipped) — extremely unlikely but * report PRECOND_FAIL rather than VULNERABLE. */ - if (v->major < 4) { + if (!skeletonkey_host_kernel_at_least(ctx->host, 4, 0, 0)) { if (!ctx->json) { fprintf(stderr, "[+] vmwgfx: kernel %s predates vmwgfx driver\n", v->release); } diff --git a/tests/test_detect.c b/tests/test_detect.c index 33bacee..b8f851c 100644 --- a/tests/test_detect.c +++ b/tests/test_detect.c @@ -41,6 +41,16 @@ extern const struct skeletonkey_module nf_tables_module; extern const struct skeletonkey_module fuse_legacy_module; extern const struct skeletonkey_module cls_route4_module; extern const struct skeletonkey_module overlayfs_setuid_module; +extern const struct skeletonkey_module af_packet_module; +extern const struct skeletonkey_module af_packet2_module; +extern const struct skeletonkey_module af_unix_gc_module; +extern const struct skeletonkey_module netfilter_xtcompat_module; +extern const struct skeletonkey_module nft_set_uaf_module; +extern const struct skeletonkey_module nft_fwd_dup_module; +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; static int g_pass = 0; static int g_fail = 0; @@ -282,6 +292,51 @@ static void run_all(void) run_one("overlayfs_setuid: vuln kernel + userns=false → PRECOND_FAIL", &overlayfs_setuid_module, &h_kernel_5_14_no_userns, SKELETONKEY_PRECOND_FAIL); + + /* ── above-fix coverage for the remaining kernel modules ── + * Kernel 6.12 is above every backport entry in the corpus. + * For modules with a `kernel_range` table, kernel_range_is_patched + * inherits via the "host is newer than every entry" branch and + * detect() returns OK. */ + + run_one("af_packet: kernel 6.12 above 4.11 fix → OK", + &af_packet_module, &h_kernel_6_12, SKELETONKEY_OK); + + run_one("af_packet2: kernel 6.12 above 5.9 fix → OK", + &af_packet2_module, &h_kernel_6_12, SKELETONKEY_OK); + + run_one("af_unix_gc: kernel 6.12 above 6.6-rc1 fix → OK", + &af_unix_gc_module, &h_kernel_6_12, SKELETONKEY_OK); + + run_one("netfilter_xtcompat: kernel 6.12 above 5.12 fix → OK", + &netfilter_xtcompat_module, &h_kernel_6_12, SKELETONKEY_OK); + + run_one("nft_set_uaf: kernel 6.12 above 6.4-rc4 fix → OK", + &nft_set_uaf_module, &h_kernel_6_12, SKELETONKEY_OK); + + run_one("nft_fwd_dup: kernel 6.12 above 5.17 fix → OK", + &nft_fwd_dup_module, &h_kernel_6_12, SKELETONKEY_OK); + + run_one("nft_payload: kernel 6.12 above 6.2-rc4 fix → OK", + &nft_payload_module, &h_kernel_6_12, SKELETONKEY_OK); + + run_one("stackrot: kernel 6.12 above 6.4-rc4 fix → OK", + &stackrot_module, &h_kernel_6_12, SKELETONKEY_OK); + + run_one("sequoia: kernel 6.12 above 5.13.4 fix → OK", + &sequoia_module, &h_kernel_6_12, SKELETONKEY_OK); + + run_one("vmwgfx: kernel 6.12 above 6.3-rc6 fix → OK", + &vmwgfx_module, &h_kernel_6_12, SKELETONKEY_OK); + + /* ── ancient-kernel predates coverage ──────────────────────── + * Kernel 4.4 predates several module bugs introduced 5.x+. */ + + run_one("nft_set_uaf: kernel 4.4 predates 5.1 → OK", + &nft_set_uaf_module, &h_kernel_4_4, SKELETONKEY_OK); + + run_one("stackrot: kernel 4.4 predates 6.1 → OK", + &stackrot_module, &h_kernel_4_4, SKELETONKEY_OK); #else fprintf(stderr, "[i] non-Linux platform: detect() bodies are stubbed; " "tests skipped (would tautologically pass).\n");