DF-2355 / mtw_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 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | /* * mtw_rig.c -- driver for the VERBATIM mt7601_set_chan() table-walk rig * (DF-2355). * * build.sh machine-extracts from /usr/src (sha256-gated): * mtw_chan.inc : if_mtw.c:279-285 (struct + mt7601_rf_chan[14] with * the VERBATIM MT7601_RF_CHAN initializer from * if_mtwreg.h) and if_mtw.c:3692-3734 * (mt7601_set_chan, verbatim) * * The channel table has exactly 14 entries (channels 1..14). The * verbatim search loop `for (i = 0; mt7601_rf_chan[i].chan != chan; i++)` * has no upper bound; caller mtw_set_chan only rejects chan==0 and 255. * * Scenarios (run in a fork()ed child each): * guard - the table copy is placed at the END of a mapped page with a * PROT_NONE page after it; chan=15 -> the walk leaves the * table and the page -> SIGSEGV (out-of-array read). * sentinel - the word after the table is a crafted entry with * .chan=15 -> the walk STOPS at i=14 (15th element, past the * 14 valid rows) and programs the radio with the sentinel's * garbage r17..r20 + reads txpow1[14] (still in the 54-entry * array, but past the 14 channel rows) -- the "matches * garbage" outcome. * control - chan=6 -> i=5, correct row programmed. */ #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> #define kprintf printf #define DPRINTF(...) do { } while (0) #define DELAY(x) do { } while (0) #define ETIMEDOUT_K 110 #define MTW_TX_ALC_CFG0 0x13b0 /* if_mtwreg.h:189 (shim) */ /* ---------------- kernel environment shims --------------------------- */ /* replica under the production name: only the members the verbatim * mt7601_set_chan() touches */ struct mtw_softc { uint32_t asic_ver; int8_t txpow1[54]; uint8_t lna[4]; void *sc_dev; /* replica placeholder for the fix's printf */ }; #define device_printf(d, ...) printf("mtw: " __VA_ARGS__) #define nitems(x) (sizeof((x)) / sizeof((x)[0])) static struct mtw_softc rig_sc; struct rf_log { int reg; uint8_t val; int bank; }; static struct rf_log rf_writes[256]; static int rf_n; static uint8_t rf_reads_served; static int mtw_rf_write(struct mtw_softc *sc, int bank, int reg, uint8_t val) { (void)sc; if (rf_n < 256) { rf_writes[rf_n].bank = bank; rf_writes[rf_n].reg = reg; rf_writes[rf_n].val = val; rf_n++; } return (0); } static int mtw_rf_read(struct mtw_softc *sc, int bank, int reg, uint8_t *val) { (void)sc; (void)bank; (void)reg; *val = rf_reads_served++; return (0); } static int mtw_read(struct mtw_softc *sc, uint16_t r, uint32_t *v) { (void)sc; (void)r; *v = 0; return (0); } static int mtw_write(struct mtw_softc *sc, uint16_t r, uint32_t v) { (void)sc; (void)r; (void)v; return (0); } static int mtw_bbp_write(struct mtw_softc *sc, uint8_t r, uint8_t v) { (void)sc; (void)r; (void)v; return (0); } static int mtw_bbp_read(struct mtw_softc *sc, uint8_t r, uint8_t *v) { (void)sc; (void)r; *v = 0; return (0); } /* the verbatim function declares `struct mtw_softc *sc`; bind it to the * replica via a typedef */ /* ---------------- VERBATIM production code ---------------------------- */ #include "mtw_chan.inc" /* struct + const mt7601_rf_chan[14] */ #include "mtw_setchan.inc" /* mt7601_set_chan() verbatim */ /* ---------------- 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 -- table walk left " "mt7601_rf_chan[14] and the mapped page (unbounded linear " "search)\n", scen_name, si->si_addr); fflush(stdout); _exit(91); } static void run_child(void (*fn)(void), const char *name) { scen_name = name; pid_t pid; int st; 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(); _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)); } /* verbatim table symbol lives in the TU; the guard scenario needs a COPY * at the page end and the search bound to it: re-run the verbatim loop * shape against the copy by temporarily rebinding -- cleanest faithful * form: mmap page, copy table to its end, then call a small re-bound * driver that repeats the VERBATIM loop text over the copy. */ struct rf_row { /* VERBATIM struct shape (if_mtw.c:280-283) */ u_int chan; uint8_t r17, r18, r19, r20; }; static struct rf_row *table_copy; static void __attribute__((noinline)) setchan_on_copy(u_int chan) { struct mtw_softc *sc = &rig_sc; uint32_t tmp; uint8_t bbp, rf, txpow1; int i; /* VERBATIM loop + body (if_mtw.c:3699-3714) against table_copy */ for (i = 0; table_copy[i].chan != chan; i++) ; mtw_rf_write(sc, 0, 17, table_copy[i].r17); mtw_rf_write(sc, 0, 18, table_copy[i].r18); mtw_rf_write(sc, 0, 19, table_copy[i].r19); mtw_rf_write(sc, 0, 20, table_copy[i].r20); txpow1 = sc->txpow1[i]; mtw_read(sc, 110 /*MTW_TX_ALC_CFG0*/, &tmp); tmp &= ~0x3f3f; tmp |= (txpow1 & 0x3f); mtw_write(sc, 110, tmp); mtw_bbp_write(sc, 62, 0x37 - sc->lna[0]); mtw_bbp_write(sc, 63, 0x37 - sc->lna[0]); mtw_bbp_write(sc, 64, 0x37 - sc->lna[0]); printf(" walk stopped at i=%d (valid rows: 0..13)\n", i); printf(" RF programmed with row r17=%02x r18=%02x r19=%02x " "r20=%02x\n", table_copy[i].r17, table_copy[i].r18, table_copy[i].r19, table_copy[i].r20); printf(" txpow1[%d]=%d used for TX_ALC\n", i, txpow1); } static void scn_guard(void) { uint8_t *page; int i; page = mmap(NULL, 2 * pagesz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); mprotect(page + pagesz, pagesz, PROT_NONE); table_copy = (struct rf_row *)(page + pagesz - 14 * sizeof(struct rf_row)); for (i = 0; i < 14; i++) table_copy[i] = *(const struct rf_row *)&mt7601_rf_chan[i]; printf("SCENARIO guard: chan=15, table at page end, next page " "PROT_NONE\n"); setchan_on_copy(15); printf("SCENARIO guard: NO FAULT (unexpected)\n"); } static void scn_sentinel(void) { static struct rf_row t[15]; int i; for (i = 0; i < 14; i++) t[i] = *(const struct rf_row *)&mt7601_rf_chan[i]; /* garbage word the walk matches at i=14 */ t[14].chan = 15; t[14].r17 = 0xDE; t[14].r18 = 0xAD; t[14].r19 = 0xBE; t[14].r20 = 0xEF; table_copy = t; memset(rig_sc.txpow1, 0x41, sizeof(rig_sc.txpow1)); printf("SCENARIO sentinel: chan=15, crafted 15th row\n"); rf_n = 0; setchan_on_copy(15); printf(" first 4 rf writes:"); for (i = 0; i < 4 && i < rf_n; i++) printf(" r%d=%02x", rf_writes[i].reg, rf_writes[i].val); printf(" <- DEADBEEF garbage row + txpow1[14]=%d ('A')\n", rig_sc.txpow1[14]); } static void scn_walk_real(void) { printf("SCENARIO walk-real: VERBATIM mt7601_set_chan(chan=15) on the " "REAL static table in this process image (.data-adjacent bytes " "are whatever the linker placed -- kernel analogue: adjacent " ".rodata)\n"); rf_n = 0; mt7601_set_chan(&rig_sc, 15); printf(" returned: first 4 rf writes:"); for (int i = 0; i < 4 && i < rf_n; i++) printf(" r%d=%02x", rf_writes[i].reg, rf_writes[i].val); printf("\n -> garbage row matched past the 14 valid entries (radio " "misprogrammed) or faulted above\n"); } static void scn_control(void) { printf("SCENARIO control: chan=6 via the VERBATIM function on the " "REAL static table\n"); rf_n = 0; mt7601_set_chan((struct mtw_softc *)&rig_sc, 6); printf(" first 4 rf writes:"); for (int i = 0; i < 4 && i < rf_n; i++) printf(" r%d=%02x", rf_writes[i].reg, rf_writes[i].val); printf(" <- row 6: 0xec 0xee 0x08 0x51 (correct)\n"); } int main(int argc, char **argv) { const char *scen = argc == 2 ? argv[1] : ""; pagesz = sysconf(_SC_PAGESIZE); memset(&rig_sc, 0, sizeof(rig_sc)); rig_sc.asic_ver = 0x7601; if (strcmp(scen, "guard") == 0) { run_child(scn_guard, "guard"); return 0; } if (strcmp(scen, "sentinel") == 0) { run_child(scn_sentinel, "sentinel"); return 0; } if (strcmp(scen, "control") == 0) { run_child(scn_control, "control"); return 0; } if (strcmp(scen, "walk-real") == 0) { run_child(scn_walk_real, "walk-real"); return 0; } fprintf(stderr, "usage: %s guard|sentinel|control|walk-real\n", argv[0]); return 2; } |