⬢ DragonFlyBSD Kernel Audit
DF-2355 / mtw_rig.c
← back to finding ↓ download raw
/*
 * 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;
}