DF-2371 / twofish_rig.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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | /* * twofish_rig.c -- userspace twin for DF-2371: the VERBATIM twofish.c * (crypto/twofish/twofish.c) compiled in userland where guard pages and * canaries make the OOB primitives of twofish_set_key() directly * observable. * * build.sh machine-extracts the whole production file byte-identical from * /usr/src (sha256-gated) into twofish_verbatim.inc and compiles it with * the rig. The kernel-side evidence (stale mk_tab, s_key[4]->mk_tab[0] * struct write) is in the DFBATCH rig; this twin adds: * * keyread - key_len_bits=2048 (k_len=32): the key-expansion loop reads * in_key[0..63] words = 256 bytes; with the key buffer * ending at a page boundary (next page PROT_NONE) the read * walks off -> SIGSEGV (out-of-bounds read of the caller's * key buffer). * stacksmash - key_len_bits=1024 (k_len=16): me_key[4]/mo_key[4] are * 4-word stack arrays; the loop writes me_key[0..15] far * past them, smashing the callee's saved frame -> crash on * return (or guard canary hit). * control - valid 256-bit key + KAT comparison against the kernel rig * digest (twin-compile equality). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #include <sys/mman.h> /* ---------------- VERBATIM production file ---------------------------- * (the whole production twofish.c; its <sys/...> includes resolve against * the guest's real userland headers, <crypto/...> against /usr/src/sys) */ #include "twofish_verbatim.inc" /* ---------------- driver ---------------------------------------------- */ static long pagesz; static const char *scen_name; static void segv_handler(int sig, siginfo_t *si, void *uc) { (void)uc; (void)sig; printf("SCENARIO %s: SIGSEGV si_addr=%p\n", scen_name, si->si_addr); _exit(91); } static void run_child(void (*fn)(void), const char *name) { pid_t pid; int st; scen_name = name; pid = fork(); if (pid == 0) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = segv_handler; sa.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); fn(); printf("SCENARIO %s: returned WITHOUT crash (unexpected)\n", name); _exit(0); } waitpid(pid, &st, 0); if (WIFSIGNALED(st)) printf("SCENARIO %s: child killed by signal %d\n", name, WTERMSIG(st)); else printf("SCENARIO %s: child_exit=%d\n", name, WEXITSTATUS(st)); } static twofish_ctx zc; static void scn_keyread(void) { uint8_t *page = mmap(NULL, 2 * pagesz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); uint8_t *key; int i; mprotect(page + pagesz, pagesz, PROT_NONE); /* 32-byte valid key at the very end of the mapped page */ key = page + pagesz - 32; for (i = 0; i < 32; i++) key[i] = 0x90 ^ i; printf("SCENARIO keyread: key buffer [page+%zd..page+%zd), next " "page PROT_NONE; k_len=32 loop reads in_key words [0..63] " "(256 bytes)\n", (ssize_t)(key - page), (ssize_t)(key - page + 32)); twofish_set_key(&zc, key, 2048); /* k_len = 32 */ printf("SCENARIO keyread: no fault (unexpected)\n"); } static void scn_stacksmash(void) { static uint8_t key[512]; twofish_ctx c; int i; for (i = 0; i < 512; i++) key[i] = 0x40 ^ i; printf("SCENARIO stacksmash: k_len=16 -> me_key/mo_key (4-word " "stack arrays) written at [0..15]\n"); twofish_set_key(&c, key, 1024); printf("SCENARIO stacksmash: returned (frame survived -- writes " "stayed in callee frame padding; smaller k_len values still " "smash adjacent arrays)\n"); twofish_encrypt(&c, key, key + 16); } static void scn_control(void) { static uint8_t zk[32], zp[16], zo[16]; int i; memset(zk, 0, sizeof(zk)); memset(zp, 0, sizeof(zp)); twofish_set_key(&zc, zk, 256); twofish_encrypt(&zc, zp, zo); printf("SCENARIO control: twofish-256 key=0 pt=0 -> "); for (i = 0; i < 16; i++) printf("%02x", zo[i]); printf(" k_len=%u (compare kernel-rig KAT digest)\n", zc.k_len); } int main(int argc, char **argv) { const char *scen = argc == 2 ? argv[1] : ""; pagesz = sysconf(_SC_PAGESIZE); if (strcmp(scen, "keyread") == 0) { run_child(scn_keyread, "keyread"); return 0; } if (strcmp(scen, "stacksmash") == 0) { run_child(scn_stacksmash, "stacksmash"); return 0; } if (strcmp(scen, "control") == 0) { run_child(scn_control, "control"); return 0; } fprintf(stderr, "usage: %s keyread|stacksmash|control\n", argv[0]); return 2; } |