8243817f7e
Three coupled improvements to the test harness:
1. New tests/test_kernel_range.c — 32 pure unit tests covering
kernel_range_is_patched(), skeletonkey_host_kernel_at_least(),
and skeletonkey_host_kernel_in_range(). These are the central
comparison primitives every module routes through; a regression
in any of them silently mis-classifies entire CVE families. Tests
cover exact boundary, one-below, mainline-only, multi-LTS,
between-branch, and NULL-safety cases. Builds and runs
cross-platform (no Linux syscalls).
2. tests/test_detect.c additions:
- mk_host(base, major, minor, patch, release) builder so new
fingerprint-based tests don't duplicate 14-line struct literals
to override one (major, minor, patch) triple.
- Post-run coverage report that iterates the runtime registry and
warns about modules without at least one direct test row. Output
is informational (no CI fail) so coverage grows incrementally.
- 7 new boundary tests for the kernel_patched_from entries added
by tools/refresh-kernel-ranges.py (commit 8de46e2):
- af_unix_gc 6.4.12 → VULNERABLE / 6.4.13 → OK
- vmwgfx 5.10.127 → OK
- nft_set_uaf 5.10.179 → OK / 6.1.27 → OK
- nft_payload 5.10.162 → OK
- nf_tables 5.10.209 → OK
3. core/registry_all.c — extracts the 27-line 'call every
skeletonkey_register_<family>()' enumeration from skeletonkey.c
into a shared helper. skeletonkey.c main() now calls
skeletonkey_register_all_modules() once; the detect-test main()
does the same. Kept in its own translation unit so registry.c
stays standalone for the lean kernel_range unit-test binary
(which links core/ only, no modules).
Makefile: builds two test binaries now —
skeletonkey-test — detect() integration tests (full corpus)
skeletonkey-test-kr — kernel_range unit tests (core/ only)
'make test' runs both.
Verification:
- macOS: 32/32 kernel_range tests pass; detect tests skipped
(non-Linux platform, stubbed bodies).
- Linux (docker gcc:latest): 32/32 kernel_range + 51/51 detect.
Coverage report identifies 2 modules without direct tests
(copy_fail, entrybleed) out of 31 registered.
Test counts: 44 -> 83 (+39).
66 lines
1.7 KiB
C
66 lines
1.7 KiB
C
/*
|
|
* SKELETONKEY — module registry implementation
|
|
*
|
|
* Simple flat array. Resized in chunks of 16. We never expect more
|
|
* than a few dozen modules, so this is fine.
|
|
*
|
|
* The canonical "register every family" enumeration lives in
|
|
* registry_all.c — kept separate so this file links into the
|
|
* standalone kernel_range unit-test binary without pulling in every
|
|
* module's symbol.
|
|
*/
|
|
|
|
#include "registry.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define REGISTRY_CHUNK 16
|
|
|
|
static const struct skeletonkey_module **g_modules = NULL;
|
|
static size_t g_count = 0;
|
|
static size_t g_cap = 0;
|
|
|
|
void skeletonkey_register(const struct skeletonkey_module *m)
|
|
{
|
|
if (m == NULL || m->name == NULL) {
|
|
fprintf(stderr, "[!] skeletonkey_register: NULL module or unnamed module\n");
|
|
return;
|
|
}
|
|
if (g_count == g_cap) {
|
|
size_t new_cap = g_cap + REGISTRY_CHUNK;
|
|
const struct skeletonkey_module **n =
|
|
realloc((void *)g_modules, new_cap * sizeof(*g_modules));
|
|
if (n == NULL) {
|
|
fprintf(stderr, "[!] skeletonkey_register: OOM\n");
|
|
return;
|
|
}
|
|
g_modules = n;
|
|
g_cap = new_cap;
|
|
}
|
|
g_modules[g_count++] = m;
|
|
}
|
|
|
|
size_t skeletonkey_module_count(void)
|
|
{
|
|
return g_count;
|
|
}
|
|
|
|
const struct skeletonkey_module *skeletonkey_module_at(size_t i)
|
|
{
|
|
if (i >= g_count) return NULL;
|
|
return g_modules[i];
|
|
}
|
|
|
|
const struct skeletonkey_module *skeletonkey_module_find(const char *name)
|
|
{
|
|
if (name == NULL) return NULL;
|
|
for (size_t i = 0; i < g_count; i++) {
|
|
if (strcmp(g_modules[i]->name, name) == 0)
|
|
return g_modules[i];
|
|
}
|
|
return NULL;
|
|
}
|