DF-3747 / pun_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-3747 harness: models the sccngetch() static-layout type-pun store * (sys/dev/misc/syscons/syscons.c:2004-2059) and the unclamped bcopy. * * The kernel code: * static struct fkeytab fkey; // 17 bytes * static int fkeycp; // 4 bytes * ... * p = kbd_get_fkeystr(scp->sc->kbd, KEYCHAR(c), (size_t *)&fkeycp); * fkey.len = fkeycp; * if ((p != NULL) && (fkey.len > 0)) * bcopy(p, fkey.str, fkey.len); * * kbd_get_fkeystr -> genkbd_get_fkeystr() (sys/dev/misc/kbd/kbd.c:1123) * performs *len = kbd->kb_fkeytab[fkey].len; -- a size_t (8-byte) store. * On the pointer to the 4-byte static int fkeycp that store is 4 bytes OOB. * * Real kernel image layout (guest, nm -n /boot/kernel/kernel): * fkeycp.13185 @ 0xffffffff817a6da0 * fkey.13184 @ 0xffffffff817a6db0 (next symbol is +0x10) * so the 8-byte store at &fkeycp spans [da0,da8): the int plus 4 bytes of * the inter-symbol alignment gap. * * This harness reproduces both stores verbatim against a canary-guarded * model of that layout and reports exactly which bytes are modified. */ #include <stdio.h> #include <string.h> #include <stddef.h> struct fkeytab { unsigned char str[16]; unsigned char len; }; /* Layout model, mirroring the kernel image order/sizes: * [guard 32][fkeycp int @32][gap 12][fkey 17 @48][guard 32] * (kernel: fkeycp @+0x00, gap, fkey @+0x10; guards stand in for the * surrounding .bss objects) */ static struct { unsigned char guard0[32]; int fkeycp; unsigned char gap[12]; struct fkeytab fkey; unsigned char guard1[32]; } M; static void dump_changed(const char *what, unsigned char *base, size_t off, size_t n) { size_t i; int any = 0; for (i = 0; i < n; i++) { if (base[i] != 0xAA) { if (!any) { printf(" %s modified bytes:", what); any = 1; } printf(" [%zu]=%02x", off + i, base[i]); } } if (!any) printf(" %s: no bytes modified", what); printf("\n"); } int main(void) { unsigned char src[256]; size_t i; printf("sizeof(size_t)=%zu sizeof(int)=%zu sizeof(struct fkeytab)=%zu\n", sizeof(size_t), sizeof(int), sizeof(struct fkeytab)); printf("model: fkeycp @+%zu, fkey @+%zu, guard1 @+%zu\n", offsetof(typeof(M), fkeycp), offsetof(typeof(M), fkey), offsetof(typeof(M), guard1)); /* ---- 1. the type-pun store (syscons.c:2054 via kbd.c:1123) ---- */ memset(&M, 0xAA, sizeof(M)); M.fkeycp = 0; /* .bss zero-init */ /* exactly what genkbd_get_fkeystr does through (size_t *)&fkeycp, * with a legitimate SETFKEY-clamped length of 5. * volatile => the 8-byte store is not optimized down to 32 bits * (in the kernel the pun is cross-TU: genkbd_get_fkeystr kbd.c:1123 * compiles to a real 64-bit "mov %rax,(%r12)" at *len == &fkeycp) */ *(volatile size_t *)(void *)&M.fkeycp = 5; printf("[1] 8-byte size_t store at &fkeycp (len=5):\n"); printf(" fkeycp=%d (low 4 bytes: the length)\n", M.fkeycp); dump_changed("OOB bytes [fkeycp+4 .. fkeycp+8)", (unsigned char *)&M.fkeycp + 4, 4, 4); printf(" => bytes 4..7 of the store land OUTSIDE the 4-byte int" " (in the real kernel: the alignment gap before fkey.13184)\n"); /* ---- 2. unclamped bcopy into fkey.str[16] with a corrupt len ---- * DF-1503 (kbd.c SETFKEY imin(-1,MAXFK) wrap) shows fkeytab[].len is * corruptible to arbitrary u_char values; sccngetch then does * fkey.len = fkeycp; bcopy(p, fkey.str, fkey.len); * without any clamp to sizeof(fkey.str) == 16. */ memset(&M, 0xAA, sizeof(M)); M.fkeycp = 0; for (i = 0; i < sizeof(src); i++) src[i] = (unsigned char)i; *(volatile size_t *)(void *)&M.fkeycp = 255; /* corrupt fkeytab len */ { int fkeycp = (int)M.fkeycp; /* syscons.c:2005 type */ unsigned char *p = src; /* fkeytab[].str */ M.fkey.len = (unsigned char)fkeycp; /* syscons.c:2055 */ if ((p != NULL) && (M.fkey.len > 0)) memcpy(M.fkey.str, p, M.fkey.len); /* syscons.c:2057 */ } printf("[2] bcopy with corrupted len=255 into fkey.str[16]:\n"); dump_changed("bytes after fkey.str[16] (kernel: .bss objects after" " fkey.13184)", M.fkey.str + 16, offsetof(typeof(M), fkey) + 16, 17 - 16 + 32); printf(" => %d bytes written past the end of the 17-byte static" " fkey object\n", 255 - 16); return 0; } |