DF-5478 / df5478_wrap.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | /* * DF-5478 — arithmetic model of the runtime-map guard and page-fill loop in * efi_create_1t1_map() (sys/platform/pc64/x86_64/efirt.c:249-297). * * The guard at efirt.c:255-263 is reproduced VERBATIM (same types: all * arithmetic in uint64_t / vm_offset_t; EFI_PAGE_SIZE == 4096; * VM_MAX_USER_ADDRESS == UVADDR(255,511,511,0) == 0x00007fffffffe000 on * this platform, per pmap.h NUPDP_USER=256 / vmparam.h). * * The check tests only the *end pointer* of md_phys + md_pages*PAGE. When * md_pages*PAGE_SIZE wraps mod 2^64 to a small value W with * md_phys + W in [md_phys, VM_MAX_USER_ADDRESS), the guard passes while the * fill loop at efirt.c:288-297 still iterates md_pages (up to 2^52+) * times, each iteration doing kmalloc(sizeof(struct vm_page), * M_EFI, M_WAITOK) + vm_page_initfake + vm_page_insert — i.e. an * effectively unbounded driver of kernel allocation until memory (and the * machine, at boot, M_WAITOK under exhaustion) wedges. * * Build: cc -O2 -o df5478_wrap df5478_wrap.c * Run: ./df5478_wrap */ #include <stdio.h> #include <stdint.h> typedef uint64_t u64; #define EFI_PAGE_SIZE 4096UL /* pmap.h:88 NUPDP_USER = NPML4EPG/2 = 256; vmparam.h:139-140 */ #define VM_MAX_USER_ADDRESS 0x00007fffffffe000UL /* efirt.c:255-263, verbatim arithmetic */ static int guard_rejects(u64 md_phys, u64 md_pages) { if (md_phys + md_pages * EFI_PAGE_SIZE < md_phys) return 1; /* wrap -> fail */ if (md_phys + md_pages * EFI_PAGE_SIZE >= VM_MAX_USER_ADDRESS) return 1; /* out of mappable range -> fail */ return 0; /* guard PASSES */ } /* FIXED guard (fix.diff): direct md_pages cap makes product wrap impossible */ static int fixed_guard_rejects(u64 md_phys, u64 md_pages) { if (md_pages >= VM_MAX_USER_ADDRESS / EFI_PAGE_SIZE) return 1; /* pages alone exceed mappable range */ if (md_phys + md_pages * EFI_PAGE_SIZE < md_phys) return 1; if (md_phys + md_pages * EFI_PAGE_SIZE >= VM_MAX_USER_ADDRESS) return 1; return 0; } /* what the fill loop (efirt.c:288-297) would then do per RT descriptor */ static void report_fixed(const char *tag, u64 md_phys, u64 md_pages) { int rejected = fixed_guard_rejects(md_phys, md_pages); printf("%-28s md_phys=%#018lx md_pages=%#018lx -> fixed guard: %s" " (loop %s)\n", tag, md_phys, md_pages, rejected ? "REJECT" : "accept", rejected ? "never starts" : "runs"); } static void report(const char *tag, u64 md_phys, u64 md_pages) { int rejected = guard_rejects(md_phys, md_pages); u64 iter = md_pages; double gib = (double)iter * 88.0 / (1024.0 * 1024.0 * 1024.0); /* ~88B/vm_page */ printf("%-28s md_phys=%#018lx md_pages=%#018lx\n" "%-28s product=%#018lx end=%#018lx guard: %s\n", tag, md_phys, md_pages, "", md_pages * EFI_PAGE_SIZE, md_phys + md_pages * EFI_PAGE_SIZE, rejected ? "REJECT (correct)" : "PASS (BUG: loop runs)"); if (!rejected) printf("%-28s -> fill-loop iterations=%llu (%.3g), " "kmalloc(M_WAITOK) bytes > %.3g GiB\n\n", "", (unsigned long long)iter, (double)iter, gib); else printf("\n"); } int main(void) { printf("VM_MAX_USER_ADDRESS model = %#lx\n\n", VM_MAX_USER_ADDRESS); /* sanity: a normal 256-page RT region is accepted (and should be) */ report("sane 256 pages @16MB", 0x1000000UL, 256); /* md_pages = 2^52: product wraps to exactly 0, end == md_phys */ report("wrap: md_pages = 2^52", 0x1000000UL, 1UL << 52); /* md_pages = 2^52 + 1: product wraps to 4096, end = phys+4096 */ report("wrap: md_pages = 2^52+1", 0x1000000UL, (1UL << 52) + 1); /* high phys base chosen so product == 2^64 exactly */ report("wrap: product == 2^64", 0xF0000000UL, (1UL << 52) - (0xF0000000UL / 4096)); /* control: genuinely huge but non-wrapping extent is still rejected */ report("no-wrap 2^40 pages", 0x1000000UL, 1UL << 40); /* ---- fix validation: same cases under the FIXED guard ---- */ printf("=== under FIXED guard (fix.diff) ===\n\n"); report_fixed("sane 256 pages @16MB", 0x1000000UL, 256); report_fixed("wrap: md_pages = 2^52", 0x1000000UL, 1UL << 52); report_fixed("wrap: md_pages = 2^52+1", 0x1000000UL, (1UL << 52) + 1); report_fixed("no-wrap 2^40 pages", 0x1000000UL, 1UL << 40); return 0; } |