DF-5497 / c5497_user.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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | /* * DF-5497 userspace model: the PRODUCTION fwcrom.c parser (compiled * unmodified for userland, exactly as fwcontrol(8) links it) is driven * on a 1024-byte Config ROM placed against a PROT_NONE guard page. * * A CSRTYPE_D entry with val=V makes crom_next() load 4 bytes at * rom + 24 + 4*V (fwcrom.c:122 + :130) BEFORE the CROM_END bound check * on the next operand of the && -- so: * subtest A V=300 -> read at rom+1224, 200 bytes into the guard * page (past the 1024-byte ROM). CROM_END * == rom+1023 would have rejected this * directory had the check run first. * subtest B V=0xffffff -> read at rom+24+0x3fffffc (+64 MB): the * full 24-bit reach, unmapped in userland. * subtest C V=100 -> read at rom+424, INSIDE the ROM (control: * walk descends normally, no fault). * * Each subtest runs in a fork()ed child; the SIGSEGV handler prints the * fault address; the child exits 42 iff it faulted exactly at the * predicted OOB address, 0 for a clean walk. */ #include <sys/types.h> #include <sys/mman.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <unistd.h> #include <sys/wait.h> #include <bus/firewire/firewire.h> #include <bus/firewire/iec13213.h> static char *rom; static unsigned long expect_fault; static void segv(int sig, siginfo_t *si, void *uc) { int hit; (void)sig; (void)uc; /* the crc_len bitfield is the high halfword of the quadlet at * 'dir', so the faulting load lands at dir+0..dir+3 (observed * dir+2: movzwl for the 16-bit field). */ hit = ((unsigned long)si->si_addr >= expect_fault && (unsigned long)si->si_addr <= expect_fault + 3); printf(" SIGSEGV: fault address %p, predicted OOB load %p..%p " "(rom+%ld, +%ld past the 1024-byte ROM)\n", si->si_addr, (void *)expect_fault, (void *)(expect_fault + 3), (long)expect_fault - (long)rom, (long)((char *)si->si_addr - (rom + 1024))); fflush(stdout); _exit(hit ? 42 : 43); } static void run_rom(int val, long off) { struct sigaction sa; /* fresh mapping per child: ROM = last 1024 bytes of page 1, * PROT_NONE guard page at rom+1024 .. rom+2048. */ rom = mmap(NULL, 3 * 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (rom == MAP_FAILED) _exit(44); mprotect(rom + 4096, 4096, PROT_NONE); rom += 4096 - 1024; *(u_int32_t *)(rom + 0) = 4 << 24; /* csrhdr: info_len = 4 */ /* Q1..Q4 bus info: zero (MAP_ANON) */ *(u_int32_t *)(rom + 5 * 4) = 1 << 16; /* root dir: crc_len = 1 */ *(u_int32_t *)(rom + 6 * 4) = /* entry 0xd1, value val */ (0xd1u << 24) | (val & 0xffffff); memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = segv; sa.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); expect_fault = (unsigned long)(rom + off); { struct crom_context cc; crom_init_context(&cc, (u_int32_t *)rom); printf(" walk start (depth=%d), dir read expected at rom+%ld\n", cc.depth, off); fflush(stdout); while (cc.depth >= 0) crom_next(&cc); printf(" walk finished cleanly, depth=%d (no fault)\n", cc.depth); fflush(stdout); } _exit(0); } int main(void) { pid_t p; int i, st; static const struct { const char *name; int val, off, want; } t[3] = { { "A: V=300, dir read at rom+1224 (guard page, +200 past ROM)", 300, 24 + 4 * 300, 42 }, { "B: V=0xffffff, dir read at rom+0x4000014 (+64 MB reach)", 0xffffff, 24 + 4 * 0xffffff, 42 }, { "C: V=100, dir read at rom+424 (in-ROM control)", 100, 24 + 4 * 100, 0 }, }; puts("DF-5497 userspace model: production fwcrom.c vs guard-page ROM"); for (i = 0; i < 3; i++) { printf("subtest %s\n", t[i].name); fflush(stdout); p = fork(); if (p == 0) run_rom(t[i].val, t[i].off); waitpid(p, &st, 0); if (WIFEXITED(st)) printf(" -> child exit %d %s\n", WEXITSTATUS(st), WEXITSTATUS(st) == t[i].want ? "(EXPECTED)" : "(UNEXPECTED)"); else printf(" -> child died by signal %d (UNEXPECTED)\n", WTERMSIG(st)); fflush(stdout); } return (0); } |