DF-5538 / df5538.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 | /* * DF-5538 PoC โ vkernel64 fuwordadd32()/swapu32() page-tail straddle guard * over-rejects: sys/platform/vkernel64/platform/copyio.c:229 (fuwordadd32) * and :165 (swapu32) test `PAGE_SIZE - (p & PAGE_MASK) < sizeof(uint64_t)` * for 32-bit operations. A 4-byte word at page offset 0xFFC has exactly * 4 bytes of room (fits entirely in-page), but 4 < 8 makes both functions * bail out with the fault sentinel -1 even though the page is mapped. * * Live caller: kern_umtx.c umtx_sleep() reads the user word with * `fuwordadd32(uptr, 0)` (sys/kern/kern_umtx.c:146). On native pc64 the * word at 0xFFC is read with a real `lock xaddl` + onfault and works. * In a vkernel the kernel's view of that word is permanently -1: * * B) umtx_sleep(&w_at_0xFFC, real_value, t) -> EBUSY immediately, * even when the word *equals* real_value (should sleep ~t, rc=0) * C) umtx_sleep(&w_at_0xFFC, -1, t) with word=0x41 -> sleeps the full * timeout and returns 0 even though the word is NOT -1 (should be * instant EBUSY) * * Run the same binary on the native host kernel (correct reference) and * inside a vkernel (broken). No privileges required. */ #include <sys/param.h> #include <unistd.h> #include <sys/mman.h> #include <sys/time.h> #include <errno.h> #include <stdio.h> #include <stdint.h> #include <string.h> static double now(void) { struct timeval tv; gettimeofday(&tv, NULL); return ((double)tv.tv_sec + (double)tv.tv_usec / 1e6); } /* * DF umtx_sleep(2) returns -1/EAGAIN after sleeping out its timeout, and * -1/EBUSY instantly when the word mismatches. The semantic under test is * whether the kernel actually saw the user word, i.e. whether it slept. */ static int probe(int *w, int value, int timeout_us, int *rcp, int *errp, double *dp) { double t0, t1; int rc, saved; errno = 0; t0 = now(); rc = umtx_sleep(w, value, timeout_us); saved = errno; t1 = now(); *rcp = rc; *errp = saved; *dp = t1 - t0; return ((t1 - t0) > (double)timeout_us / 1000000.0 * 0.6); } int main(void) { char *pages; int *w0, *wt; int rc, err, slept, broken = 0; double d; pages = mmap(NULL, 3 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (pages == MAP_FAILED) { perror("mmap"); return (2); } memset(pages, 0, 3 * PAGE_SIZE); w0 = (int *)(pages + PAGE_SIZE); /* page start */ wt = (int *)(pages + 2 * PAGE_SIZE - 4); /* page 0xFFC */ printf("DF5538 pid=%d page=%p w0=%p(off 0x000) wt=%p(off 0x%03lx)\n", getpid(), pages + PAGE_SIZE, (void *)w0, (void *)wt, (unsigned long)((char *)wt - (pages + PAGE_SIZE))); *w0 = 0x41; *wt = 0x41; /* A: control at page offset 0x000 โ must sleep the full timeout */ slept = probe(w0, 0x41, 1500000, &rc, &err, &d); printf("A control off0x000 umtx_sleep(0x41,1.5s): rc=%d errno=%d(%s) " "elapsed=%.3f -> %s\n", rc, err, strerror(err), d, slept ? "OK(slept)" : "UNEXPECTED"); if (!slept) broken |= 4; /* control broken too: environment problem */ /* B: word at page offset 0xFFC, matching value โ must sleep */ slept = probe(wt, 0x41, 1500000, &rc, &err, &d); printf("B tail off0xFFC umtx_sleep(0x41,1.5s): rc=%d errno=%d(%s) " "elapsed=%.3f -> %s\n", rc, err, strerror(err), d, slept ? "OK(slept)" : "BROKEN(kernel never sees the word)"); if (!slept) broken |= 1; /* C: word at 0xFFC holds 0x41 but ask for -1 โ must be instant EBUSY */ slept = probe(wt, -1, 300000, &rc, &err, &d); printf("C tail off0xFFC umtx_sleep(-1,0.3s): rc=%d errno=%d(%s) " "elapsed=%.3f -> %s\n", rc, err, strerror(err), d, slept ? "BROKEN(slept on phantom -1)" : "OK(instant EBUSY)"); if (slept) broken |= 2; printf("DF5538 RESULT: %s (mask=%d)\n", broken ? "BROKEN" : "OK", broken); return (broken ? 1 : 0); } |