core/host: add meltdown_mitigation passthrough + migrate entrybleed

The kpti_enabled bool in struct skeletonkey_host flattens three
distinct sysfs states into one bit:

  /sys/devices/system/cpu/vulnerabilities/meltdown content:
    - 'Not affected'      → CPU is Meltdown-immune; KPTI off; EntryBleed
                            doesn't apply (verdict: OK)
    - 'Mitigation: PTI'   → KPTI on (verdict: VULNERABLE)
    - 'Vulnerable'        → KPTI off but CPU not hardened (rare;
                            verdict: VULNERABLE conservatively)
    - file unreadable     → unknown (verdict: VULNERABLE conservatively)

kpti_enabled=true only captures 'Mitigation: PTI'; kpti_enabled=false
collapses 'Not affected', 'Vulnerable', and 'unreadable' into one
indistinguishable case. That meant entrybleed_detect() had to
re-open the sysfs file to recover the raw string.

Fix by also stashing the raw first line in
ctx->host->meltdown_mitigation[64]. kpti_enabled stays for callers
that only need the simple bool; new code that needs the nuance reads
the string. populate happens once at startup, like every other host
field.

entrybleed migration:
  - reads ctx->host->meltdown_mitigation instead of opening sysfs
  - removes the file-local read_first_line() helper (now dead code)
  - same three-way verdict logic, but driven by a const char *
    instead of a fresh fopen() each detect()

Test coverage:
  - 3 new test rows on x86_64 fingerprints:
      empty mitigation       → VULNERABLE (conservative)
      'Not affected'         → OK
      'Mitigation: PTI'      → VULNERABLE
  - 1 stub-path test row on non-x86_64 fingerprints (PRECOND_FAIL)
  - registry coverage report: 30/31 modules now have direct tests
    (up from 29/31; copy_fail is the only remaining untested module)

Verification:
  - macOS: 33 kernel_range + 1 entrybleed-stub = 34 passes, 0 fails
  - Linux (docker gcc:latest): 33 kernel_range + 54 detect = 87
    passes, 0 fails. Up from 83 last commit.
This commit is contained in:
2026-05-23 01:14:38 -04:00
parent e2fef41667
commit 60d22eb4f6
4 changed files with 68 additions and 23 deletions
+36
View File
@@ -34,6 +34,7 @@ extern const struct skeletonkey_module dirtydecrypt_module;
extern const struct skeletonkey_module fragnesia_module;
extern const struct skeletonkey_module pack2theroot_module;
extern const struct skeletonkey_module overlayfs_module;
extern const struct skeletonkey_module entrybleed_module;
extern const struct skeletonkey_module dirty_pipe_module;
extern const struct skeletonkey_module dirty_cow_module;
extern const struct skeletonkey_module ptrace_traceme_module;
@@ -594,6 +595,41 @@ static void run_all(void)
&nf_tables_module, &h_nf_tables_5_10_209,
SKELETONKEY_OK);
/* ── entrybleed: meltdown_mitigation passthrough ────────────────
* entrybleed reads ctx->host->meltdown_mitigation (raw sysfs line)
* instead of re-opening /sys/.../meltdown. Test the three branches:
* - empty string ("probe failed") → conservative VULNERABLE
* - "Not affected" (Meltdown-immune CPU) → OK
* - "Mitigation: PTI" (KPTI on, vulnerable) → VULNERABLE
* The module is x86_64-only; on other arches the stub returns
* PRECOND_FAIL regardless of meltdown status. We test the x86_64
* branch via the synthetic host's `arch` field. */
#if defined(__x86_64__) || defined(__amd64__)
struct skeletonkey_host h_entry_no_data = h_kernel_6_12;
h_entry_no_data.meltdown_mitigation[0] = '\0';
run_one("entrybleed: meltdown probe unread → conservative VULNERABLE",
&entrybleed_module, &h_entry_no_data,
SKELETONKEY_VULNERABLE);
struct skeletonkey_host h_entry_immune = h_kernel_6_12;
strcpy(h_entry_immune.meltdown_mitigation, "Not affected");
run_one("entrybleed: meltdown=Not affected (immune CPU) → OK",
&entrybleed_module, &h_entry_immune,
SKELETONKEY_OK);
struct skeletonkey_host h_entry_kpti = h_kernel_6_12;
strcpy(h_entry_kpti.meltdown_mitigation, "Mitigation: PTI");
run_one("entrybleed: meltdown=Mitigation: PTI → VULNERABLE",
&entrybleed_module, &h_entry_kpti,
SKELETONKEY_VULNERABLE);
#else
/* On non-x86_64 dev / CI containers, the stubbed detect() returns
* PRECOND_FAIL regardless of meltdown_mitigation contents. */
run_one("entrybleed: non-x86_64 arch → PRECOND_FAIL (stub)",
&entrybleed_module, &h_kernel_6_12,
SKELETONKEY_PRECOND_FAIL);
#endif
/* ── coverage report ─────────────────────────────────────────
* Iterate the runtime registry (populated by skeletonkey_register_*
* calls in main()) and warn for any module that was not touched