DF-1120 / harness.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 | /* * DF-1120 userspace harness — sym buddy allocator OOB write primitive. * * The kernel bug: * sys/dev/disk/sym/sym_hipd.c:408 m_pool_s.h[] declared * h[MEMO_CLUSTER_SHIFT - MEMO_SHIFT + 1] -> h[9] on x86_64 * (PAGE_SHIFT=12, MEMO_PAGE_ORDER=0, MEMO_SHIFT=4 -> 12-4+1=9 slots, idx 0..8) * sys/dev/disk/sym/sym_hipd.c:476-497 ___sym_mfree merge loop: * while (1) { * b = a ^ s; q = &h[i]; <-- walks h[] for buddy * if found: a = a&b; s <<= 1; ++i; <-- i climbs with no bound * } * MEMO_FREE_UNUSED (#ifdef at :477) is wrapped in `#if 0` at :366-368 so it * does NOT compile in -- the early-exit when s==MEMO_CLUSTER_SIZE is absent, * so two buddy-adjacent 4096-byte frees merge at i=7->8, then again at i=8 * (s becomes 8192), and the next loop top does `q = &h[9]` -> 8-byte OOB * write into whatever follows m_pool_s in kernel heap. * * This harness replicates the allocator bookkeeping with the same layout and * demonstrates the OOB write into a guard canary placed right after h[]. * Uses real mmap'd 4096-aligned buddy addresses so linked-list walks are valid. * * Build: cc -O2 -o harness harness.c * Run: ./harness * Expected: "OOB WRITE at h[9] detected" (canary corrupted). */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <assert.h> #define PAGE_SHIFT 12 #define MEMO_PAGE_ORDER 0 #define MEMO_SHIFT 4 #define MEMO_CLUSTER_SHIFT (PAGE_SHIFT + MEMO_PAGE_ORDER) /* 12 */ #define MEMO_CLUSTER_SIZE (1UL << MEMO_CLUSTER_SHIFT) /* 4096 */ #define NH (MEMO_CLUSTER_SHIFT - MEMO_SHIFT + 1) /* 9 */ typedef struct m_link { struct m_link *next; } m_link_s; /* mimic m_pool_s tail layout: h[] immediately followed by a canary */ typedef struct { m_link_s h[NH]; uint64_t canary; } test_pool; static test_pool pool; int main(void) { memset(&pool, 0, sizeof(pool)); pool.canary = 0xDEADBEEFCAFEBABEull; printf("Layout: h[%d] (idx 0..%d), canary at &h[%d]\n", NH, NH - 1, NH); printf("MEMO_CLUSTER_SIZE = %lu\n", MEMO_CLUSTER_SIZE); /* Allocate two page-aligned buddy-adjacent 4096-byte regions. * A 2-page mmap gives us A at offset 0 and B = A^4096 at offset 4096. */ void *base = NULL; if (posix_memalign(&base, 2 * MEMO_CLUSTER_SIZE, 2 * MEMO_CLUSTER_SIZE) != 0) { perror("posix_memalign"); return 1; } unsigned long A = (unsigned long)base; unsigned long B = A + MEMO_CLUSTER_SIZE; /* buddy: B = A ^ 4096 (A is page-aligned) */ /* for page-aligned A, A ^ 4096 == A + 4096 iff bit 12 of A is 0. * Force it by choosing base appropriately. */ if (A & MEMO_CLUSTER_SIZE) { A += MEMO_CLUSTER_SIZE; B = A ^ MEMO_CLUSTER_SIZE; } assert((A ^ MEMO_CLUSTER_SIZE) == B); printf("A=%p B=%p (B == A ^ 4096 = buddy pair)\n", (void*)A, (void*)B); /* Free A first: walks up to s=4096,i=8; h[8] empty -> push A onto h[8]. */ { int i = 0, s = (1 << MEMO_SHIFT); unsigned long a = A; while (MEMO_CLUSTER_SIZE > (unsigned)s) { s <<= 1; ++i; } ((m_link_s *)a)->next = pool.h[i].next; pool.h[i].next = (m_link_s *)a; printf("freed A: parked on h[%d] (s=%d)\n", i, s); } /* Free B: walks up to s=4096,i=8; buddy A IS on h[8] -> MERGE: * a = A&B, s=8192, i=9. Next loop top accesses h[9] -> OOB. */ { int i = 0, s = (1 << MEMO_SHIFT); unsigned long a = B; while (MEMO_CLUSTER_SIZE > (unsigned)s) { s <<= 1; ++i; } printf("freed B: entering merge loop at i=%d, s=%d\n", i, s); /* EXACT kernel loop body, NO MEMO_FREE_UNUSED guard */ while (1) { m_link_s *q; unsigned long b; if (i >= NH) { printf("\n*** OOB WRITE at h[%d] -- canary corrupted ***\n", i); printf(" (kernel: `q = &h[%d]` then `h[%d].next = (m_link_s*)a`\n", i, i); printf(" = 8-byte write past m_pool_s.h[] into adjacent heap)\n"); pool.h[i].next = (m_link_s *)a; /* the OOB write */ printf(" canary before = 0x%016lx\n", (unsigned long)0xDEADBEEFCAFEBABEull); printf(" canary after = 0x%016lx (OVERWRITTEN)\n", (unsigned long)pool.canary); free(base); return 0; } b = a ^ s; q = &pool.h[i]; while (q->next && q->next != (m_link_s *)b) q = q->next; if (!q->next) { ((m_link_s *)a)->next = pool.h[i].next; pool.h[i].next = (m_link_s *)a; printf(" no buddy at i=%d: parked, break\n", i); break; } q->next = q->next->next; a = a & b; s <<= 1; ++i; printf(" merged at i=%d -> new i=%d, s=%d\n", i-1, i, s); } } printf("no OOB -- unexpected.\n"); free(base); return 1; } |