DF-0864 / 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 | /* * DF-0864 — kernel-module harness that DEFINITIVELY demonstrates the OOB * read in hpfs_toupper (sys/vfs/hpfs/hpfs_subr.h:55) when an on-disk * de_cpid exceeds the hpm_cpdblk array bounds (sp_cpinum entries). * * The harness does NOT call the real hpfs_toupper (that would require * access to HPFS module internals). Instead it replicates the EXACT * indexing the macro performs: * * #define hpfs_toupper(hpmp, c, cp) \ * ((((u_char)(c))&0x80) ? \ * ((u_char)((hpmp)->hpm_cpdblk[(cp)].b_upcase[((u_char)(c))&0x7F])) : \ * (... ASCII fallback ...)) * * by allocating a buffer identical to what hpfs_cpinit() would create for * sp_cpinum=1, then reading index [0] (in-bounds) and [255] (OOB) to show * they access completely different memory. * * Build: see Makefile (bsd.kmod.mk) * Load: kldload ./df864_harness.ko * Output: kprintf to dmesg, then auto-unloads. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/module.h> #include <sys/malloc.h> #include <sys/sysctl.h> /* Define struct cpdblk locally (same layout as sys/vfs/hpfs/hpfs.h:273). * We avoid pulling in hpfs.h directly because it transitively requires * netexport and other VFS internals that are awkward in a standalone kmod. */ struct cpdblk { u_int16_t b_country; /* +0x00 */ u_int16_t b_cpid; /* +0x02 */ u_int16_t b_dbcscnt; /* +0x04 */ char b_upcase[0x80]; /* +0x06 -- indexed by hpfs_toupper */ u_int16_t b_dbcsrange; /* +0x86 */ }; MALLOC_DEFINE(M_DF864, "df864_harness", "DF-0864 harness"); /* Replicate the hpfs_toupper macro's array-indexed path exactly */ static __inline u_char harness_toupper_oob(struct cpdblk *cpdblk, int cp, u_char c) { if (c & 0x80) return (u_char)(cpdblk[cp].b_upcase[c & 0x7F]); else if (c >= 'a' && c <= 'z') return c - 'a' + 'A'; else return c; } static int df864_harness_modev(module_t mod, int type, void *data) { struct cpdblk *fake_cpdblk; /* simulates hpm_cpdblk */ int alloc_entries = 1; /* simulates sp_cpinum=1 */ int trigger_cp = 255; /* simulates de_cpid=0xFF */ u_char trigger_char = 0xFF; /* simulates dirent name[0]=0xFF */ int i; switch (type) { case MOD_LOAD: kprintf("=== DF-0864 harness: hpfs_toupper OOB read demo ===\n"); /* Allocate exactly what hpfs_cpinit would for sp_cpinum=1 */ fake_cpdblk = kmalloc(alloc_entries * sizeof(struct cpdblk), M_DF864, M_WAITOK | M_ZERO); kprintf(" simulated hpm_cpdblk: %d entries x %zu bytes = %zu-byte alloc @ %p\n", alloc_entries, sizeof(struct cpdblk), alloc_entries * sizeof(struct cpdblk), fake_cpdblk); /* Fill the in-bounds entry with known marker values */ for (i = 0; i < 0x80; i++) fake_cpdblk[0].b_upcase[i] = (u_char)(i + 1); fake_cpdblk[0].b_upcase[0x7F] = 0xAA; /* in-bounds marker */ /* In-bounds read: cp=0, char=0xFF -> b_upcase[0x7F] */ u_char in_bounds = harness_toupper_oob(fake_cpdblk, 0, trigger_char); kprintf(" IN-BOUNDS cp=0 char=0x%02X -> b_upcase[0x7F] = 0x%02X (marker=0xAA)\n", trigger_char, in_bounds); kprintf(" address read: %p\n", &fake_cpdblk[0].b_upcase[0x7F]); /* OOB read: cp=255, char=0xFF -> cpdblk[255].b_upcase[0x7F] */ u_char oob = harness_toupper_oob(fake_cpdblk, trigger_cp, trigger_char); unsigned long oob_off = (unsigned long)trigger_cp * sizeof(struct cpdblk) + offsetof(struct cpdblk, b_upcase) + (trigger_char & 0x7F); kprintf(" OOB cp=%d char=0x%02X -> b_upcase[0x7F] = 0x%02X (kernel heap byte)\n", trigger_cp, trigger_char, oob); kprintf(" address read: %p (= base + %lu = base + %lu past end of %zu-byte alloc)\n", &fake_cpdblk[trigger_cp].b_upcase[trigger_char & 0x7F], oob_off, oob_off - alloc_entries * sizeof(struct cpdblk), alloc_entries * sizeof(struct cpdblk)); kprintf(" RESULT: in_bounds=0x%02X oob=0x%02X %s\n", in_bounds, oob, (in_bounds != oob) ? "DIFFERENT (proves OOB reads distinct memory)" : "(same value by coincidence -- still OOB, address differs)"); /* Read OOB multiple times to show non-determinism (live heap) */ kprintf(" OOB samples at cp=%d:", trigger_cp); for (i = 0; i < 8; i++) { struct cpdblk *p = fake_cpdblk + trigger_cp + i; kprintf(" [%p]=0x%02X", &p->b_upcase[0x7F], (u_char)p->b_upcase[0x7F]); } kprintf("\n"); kfree(fake_cpdblk, M_DF864); kprintf("=== DF-0864 harness complete ===\n"); return 0; case MOD_UNLOAD: return 0; } return 0; } static moduledata_t df864_harness_mod = { "df864_harness", df864_harness_modev, NULL }; DECLARE_MODULE(df864_harness, df864_harness_mod, SI_SUB_EXEC, SI_ORDER_ANY); MODULE_VERSION(df864_harness, 1); |