/* test_mtree.c — unit tests for mtree_low_split (the below-4G split parser). Pure text in, * number out; no QMP/transport. The fragmented fixture reproduces the structural traps the * old heuristic tripped on (Hyper-V synic overlays, smbase/tseg blackhole holes, rom holes) * plus a decoy non-system flatview that carries its OWN GPA-0 stub and a DIFFERENT @offset, * proving the system address space is selected (not "first match in the text"). */ #define _GNU_SOURCE #include "mtree.h" #include #include #include #ifndef FIXTURE_DIR #define FIXTURE_DIR "." #endif static int g_fail = 0; #define CHECK(cond, msg) do { if (!(cond)) { printf(" FAIL: %s\n", (msg)); g_fail = 1; } } while (0) /* Slurp a whole text file into a heap buffer (NUL-terminated). NULL on error. */ static char* slurp(const char* path) { FILE* f = fopen(path, "rb"); if (!f) return NULL; if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; } long sz = ftell(f); if (sz < 0) { fclose(f); return NULL; } rewind(f); char* buf = malloc((size_t)sz + 1); if (!buf) { fclose(f); return NULL; } size_t got = fread(buf, 1, (size_t)sz, f); fclose(f); buf[got] = 0; return buf; } /* Re-encode every '\n' as '\r\n' (QEMU's HMP output is CRLF). Caller frees; NULL on OOM. */ static char* to_crlf(const char* lf) { size_t n = 0, extra = 0; for (const char* p = lf; *p; p++) { n++; if (*p == '\n') extra++; } char* out = malloc(n + extra + 1); if (!out) return NULL; char* o = out; for (const char* p = lf; *p; p++) { if (*p == '\n') *o++ = '\r'; *o++ = *p; } *o = 0; return out; } /* Case B: a minimal, NON-fragmented system flatview — one big GPA-0 ram run plus high-RAM * carrying @. Must not be broken by the new parser. */ static const char* k_happy = "FlatView #0\n" " AS \"memory\", root: system\n" " Root memory region: system\n" " 0000000000000000-000000007fffffff (prio 0, ram): ram0\n" " 0000000080000000-0000000081ffffff (prio 0, i/o): vfio-pci-bar3\n" " 0000000100000000-000000017fffffff (prio 0, ram): ram0 @0000000080000000\n"; /* Case C: text without any system flatview => fail-closed. */ static const char* k_no_system = "FlatView #0\n" " AS \"I/O\", root: io\n" " Root memory region: io\n" " 0000000000000000-0000000000000007 (prio 0, i/o): dma-chan\n"; int main(void) { printf("test_mtree\n"); /* Cases A and E: the fragmented fixture (decoy first, system second). */ char path[1024]; snprintf(path, sizeof path, "%s/mtree_split_fragmented.txt", FIXTURE_DIR); char* frag = slurp(path); CHECK(frag != NULL, "fragmented fixture loaded"); if (frag) { uint64_t low = mtree_low_split(frag); /* A: fragmented low-RAM must NOT yield the GPA-0 stub end (0x18000) — the bug. */ CHECK(low == 0x80000000ull, "A: fragmented split == 0x80000000"); CHECK(low != 0x18000ull, "A: not the GPA-0 stub end (0x18000)"); /* E: the decoy (non-system) flatview comes FIRST and carries @0x40000000; the * function must select the SYSTEM flatview (@0x80000000), not the decoy. */ CHECK(low != 0x40000000ull, "E: decoy flatview @offset rejected (system AS chosen)"); /* F: real QEMU HMP output is CRLF. The parser MUST tolerate '\r' — a synthetic * LF-only fixture hid this, so the shipped parser returned 0 on the real VM mtree * (-> low=0 -> VM never attached). Regression guard, independent of how git stores * the fixture's line endings. */ char* frag_crlf = to_crlf(frag); CHECK(frag_crlf != NULL, "F: CRLF copy allocated"); if (frag_crlf) { CHECK(mtree_low_split(frag_crlf) == 0x80000000ull, "F: CRLF fragmented split == 0x80000000"); free(frag_crlf); } free(frag); } /* Case B: happy path (non-fragmented) still resolves to the high-RAM @offset. */ CHECK(mtree_low_split(k_happy) == 0x80000000ull, "B: non-fragmented happy path == 0x80000000"); /* Case C: no system flatview => 0. */ CHECK(mtree_low_split(k_no_system) == 0, "C: no system flatview => fail-closed 0"); /* Case D: garbage / empty => 0. */ CHECK(mtree_low_split("") == 0, "D: empty text => 0"); CHECK(mtree_low_split("not an mtree at all\n") == 0, "D: junk text => 0"); printf("mtree tests: %s\n", g_fail ? "FAIL" : "PASS"); return g_fail ? 1 : 0; }