DF-5719 / bcd_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 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | /* * bcd_rig.c -- DF-5719 userland verbatim-algorithm rig. * * Embeds VERBATIM the tables from sys/libkern/bcd.c (bcd2bin_data, 154 * entries; bin2bcd_data, 100 entries; hex2ascii_data, 37 bytes) and the * VERBATIM indexing macros from sys/sys/libkern.h:59-61, then proves: * * MODE=1 SIZE : actual object sizes (bin2bcd_data == 100 == the largest * in-domain index + 1; anything above is out of bounds). * MODE=2 GUARD : the tables are placed so each object ENDS exactly at a * PROT_NONE guard page; the verbatim macros indexed with * the unvalidated u_char ioctl bytes (scsi_cd.c:2045,2161 * receive args->start_track/args->track straight from * userspace, 0..255) SIGSEGV -- formal proof the macro * reads past the end of the object. * MODE=3 DIFF : exhaustive semantics check -- table macro vs true BCD * arithmetic over the whole valid domain (bin 0..99, * bcd 0x00..0x99 with both nibbles <= 9), plus the fix * masks are identity on the valid domain. * MODE=4 FIXED : guard-page placement + the FIXED macros from fix.diff * (index masked into range) -- same probes, no fault. * * Build: cc -O2 -DMODE=n -o rig bcd_rig.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <sys/mman.h> #include <sys/wait.h> /* ---- VERBATIM from sys/libkern/bcd.c:9-36 ---- */ static const unsigned char v_bcd2bin_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 0, 0, 0, 0, 0, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0, 0, 0, 0, 0, 0, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0, 0, 0, 0, 0, 0, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0, 0, 0, 0, 0, 0, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0, 0, 0, 0, 0, 0, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; static const unsigned char v_bin2bcd_data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99 }; static const char v_hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz"; #define B2B_N (sizeof(v_bin2bcd_data)) /* expect 100 */ #define B2D_N (sizeof(v_bcd2bin_data)) /* expect 154 */ /* ---- VERBATIM macro text from sys/sys/libkern.h:59-61 (array names * redirected to the placement pointers so we control adjacency) ---- */ static const unsigned char *bin2bcd_data; static const unsigned char *bcd2bin_data; #define bcd2bin(bcd) (bcd2bin_data[bcd]) #define bin2bcd(bin) (bin2bcd_data[bin]) #define hex2ascii(hex) (hex2ascii_data[hex]) static const char *hex2ascii_data; /* ---- fix.diff treatment (index wrapped into the object; identity on * the valid domain because every valid index < the modulus) ---- */ #define bcd2bin_fixed(bcd) (bcd2bin_data[(bcd) % (int)B2D_N]) #define bin2bcd_fixed(bin) (bin2bcd_data[(u_char)(bin) % B2B_N]) static void * place_at_guard(const void *obj, size_t n) { /* map 2 pages, guard the 2nd, copy obj so it ENDS at the boundary */ unsigned char *m = mmap(NULL, 2 * 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (m == MAP_FAILED) { perror("mmap"); exit(2); } if (mprotect(m + 4096, 4096, PROT_NONE) != 0) { perror("mprotect"); exit(2); } memcpy(m + 4096 - n, obj, n); return (m + 4096 - n); } static int probe(void (*fn)(void)) { pid_t p = fork(); int st; if (p == 0) { fn(); _exit(0); } waitpid(p, &st, 0); if (WIFSIGNALED(st) && WTERMSIG(st) == SIGSEGV) return (1); /* read crossed the object boundary */ if (WIFEXITED(st) && WEXITSTATUS(st) == 0) return (0); return (-1); } static volatile unsigned char sink; static void pb_200(void) { sink = bin2bcd(200); } /* CDIOCPLAYTRACKS */ static void pb_255(void) { sink = bin2bcd(255); } /* max u_char */ static void pb_100(void) { sink = bin2bcd(100); } /* first OOB */ static void pb_ok(void) { sink = bin2bcd(99); } /* last in-bounds */ static void pd_0x9a(void){ sink = bcd2bin(0x9a); } /* first OOB bcd */ static void pd_0xff(void){ sink = bcd2bin(0xff); } /* garbage CMOS */ static void pd_ok(void) { sink = bcd2bin(0x99); } /* last in-bounds */ static void pf_200(void) { sink = bin2bcd_fixed(200); } static void pf_255(void) { sink = bin2bcd_fixed(255); } static void pdf_ff(void) { sink = bcd2bin_fixed(0xff); } int main(void) { int r, bad = 0; setvbuf(stdout, NULL, _IONBF, 0); #if MODE == 1 printf("sizeof(bcd2bin_data) = %zu (max valid BCD index 0x99=%d)\n", B2D_N, 0x99); printf("sizeof(bin2bcd_data) = %zu (max valid bin index 99)\n", B2B_N); printf("sizeof(hex2ascii_data) = %zu\n", sizeof(v_hex2ascii_data)); printf("=> any macro index above %zu/%d is out of bounds\n", B2B_N, 0x99); #elif MODE == 2 bin2bcd_data = place_at_guard(v_bin2bcd_data, B2B_N); bcd2bin_data = place_at_guard(v_bcd2bin_data, B2D_N); hex2ascii_data = place_at_guard(v_hex2ascii_data, sizeof(v_hex2ascii_data)); printf("guard-page rig (verbatim kernel macros, objects end at " "PROT_NONE boundary)\n"); r = probe(pb_ok); printf("bin2bcd(99) (last in-bounds) : %s\n", r == 0 ? "OK" : "FAULT"); bad += !!r; r = probe(pb_100); printf("bin2bcd(100) (first OOB) : %s\n", r == 1 ? "SIGSEGV" : "no fault?"); bad += r != 1; r = probe(pb_200); printf("bin2bcd(200) (ioctl byte) : %s\n", r == 1 ? "SIGSEGV" : "no fault?"); bad += r != 1; r = probe(pb_255); printf("bin2bcd(255) (max u_char) : %s\n", r == 1 ? "SIGSEGV" : "no fault?"); bad += r != 1; r = probe(pd_ok); printf("bcd2bin(0x99) (last in-bounds) : %s\n", r == 0 ? "OK" : "FAULT"); bad += !!r; r = probe(pd_0x9a);printf("bcd2bin(0x9a) (first OOB) : %s\n", r == 1 ? "SIGSEGV" : "no fault?"); bad += r != 1; r = probe(pd_0xff);printf("bcd2bin(0xff) (garbage CMOS) : %s\n", r == 1 ? "SIGSEGV" : "no fault?"); bad += r != 1; printf("=> verbatim macros index past the end of the objects\n"); return (bad ? 0 : 1); #elif MODE == 3 bin2bcd_data = v_bin2bcd_data; bcd2bin_data = v_bcd2bin_data; hex2ascii_data = v_hex2ascii_data; { int i, e = 0; for (i = 0; i < 100; i++) { unsigned char want = (unsigned char) (((i / 10) << 4) | (i % 10)); if (bin2bcd(i) != want) e++; if (bin2bcd_fixed(i) != bin2bcd(i)) e++; } printf("bin2bcd vs arithmetic + fix-mask identity, 0..99: " "%s (%d errors)\n", e ? "FAIL" : "MATCH", e); bad += e; e = 0; for (i = 0; i <= 0x99; i++) { int hi = i >> 4, lo = i & 0xf; if (hi <= 9 && lo <= 9) { /* valid BCD */ if (bcd2bin(i) != hi * 10 + lo) e++; if (bcd2bin_fixed(i) != bcd2bin(i)) e++; } /* holes: table stores 0s -- fix mask preserves * nothing to compare there; only check fixed stays * in-domain numerically (mask keeps index <= 0x99) */ } printf("bcd2bin vs arithmetic + fix-mask identity over valid " "BCD 0x00..0x99: %s (%d errors)\n", e ? "FAIL" : "MATCH", e); bad += e; e = 0; for (i = 0; i < 36; i++) if (hex2ascii(i) != "0123456789abcdefghijklmnopqrstuvwxyz"[i]) e++; printf("hex2ascii radix[2..36] domain (0..35): %s (%d errors)\n", e ? "FAIL" : "MATCH", e); bad += e; } return (bad ? 1 : 0); #elif MODE == 4 bin2bcd_data = place_at_guard(v_bin2bcd_data, B2B_N); bcd2bin_data = place_at_guard(v_bcd2bin_data, B2D_N); hex2ascii_data = place_at_guard(v_hex2ascii_data, sizeof(v_hex2ascii_data)); printf("fix.diff treatment under the same guard-page placement:\n"); r = probe(pf_200); printf("bin2bcd_fixed(200) : %s (value=%u)\n", r == 0 ? "in-bounds" : "SIGSEGV", (unsigned)v_bin2bcd_data[200 % 100]); bad += !!r; r = probe(pf_255); printf("bin2bcd_fixed(255) : %s (value=%u)\n", r == 0 ? "in-bounds" : "SIGSEGV", (unsigned)v_bin2bcd_data[255 % 100]); bad += !!r; r = probe(pdf_ff); printf("bcd2bin_fixed(0xff): %s (value=%u)\n", r == 0 ? "in-bounds" : "SIGSEGV", (unsigned)v_bcd2bin_data[0xff % B2D_N]); bad += !!r; printf("=> masked indices never leave the objects\n"); return (bad ? 1 : 0); #else #error "compile with -DMODE=1|2|3|4" #endif } |