โฌข DragonFlyBSD Kernel Audit
DF-4141 / df4141_oob.c
โ† back to finding โ†“ download raw
/*
 * DF-4141 โ€” proof-of-primitive harness (userspace, no hardware required)
 *
 * File audited: sys/dev/netif/ath/ath_hal/ar5211/ar5211_reset.c
 * Function:     ar5211GetLowerUpperValues() (lines 1813-1867) โ€” VERBATIM copy
 *
 * Defect: when invoked with listSize == 0 the function executes
 *
 *      const uint16_t listEndValue = *(pList + listSize - 1);   // :1818
 *
 * i.e. loads 2 bytes BELOW the caller's array (pList[-1]), and then reads
 * the uninitialized *pList (tempChannelList[0]) at :1829.
 *
 * Kernel reachability (ar5211_reset.c:1528-1551): ar5211SetRateTable()
 * copies a CTL's RD_EDGES_POWER row into `uint16_t tempChannelList[NUM_EDGES]`
 * stopping at the first zero rdEdge; a matched CTL (ee_ctl[i] == cfgCtl != 0)
 * whose first edge is blanked (rdEdge == 0, twice_rdEdgePower == 0 โ€” a
 * degenerate/corrupt/crafted EEPROM image) leaves numEdges == 0 and the
 * stack array untouched, then calls
 *
 *      ar5211GetLowerUpperValues(freq, tempChannelList, numEdges, ...)
 *
 * at :1537 -> pList[-1] OOB stack read + uninitialized stack read; the
 * resulting garbage lower/upper channel feeds twiceMaxEdgePower selection
 * (regulatory edge-power misprogramming).
 *
 * This harness places the array at the start of an mmap'd page whose
 * predecessor page is PROT_NONE, so the pList[-1] load faults.  SUCCESS
 * CRITERION: the process dies exactly at the :1818 load (marker printed
 * by the SIGSEGV handler) โ€” proving the below-array read is real and
 * unconditional for listSize == 0.
 */
#include <sys/mman.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

typedef unsigned short  uint16_t;
typedef unsigned int    uint32_t;
typedef int             int32_t;

#define EEP_SCALE   100
#define EEP_DELTA   10

/* ---- verbatim from ar5211_reset.c:1813-1867 ---- */
static void
ar5211GetLowerUpperValues(uint16_t value,
	const uint16_t *pList, uint16_t listSize,
	uint16_t *pLowerValue, uint16_t *pUpperValue)
{
	const uint16_t listEndValue = *(pList + listSize - 1);	/* :1818 */
	uint32_t target = value * EEP_SCALE;
	int i;

	*pLowerValue = 0;	/* avoid gcc warnings */
	*pUpperValue = 0;	/* avoid gcc warnings */

	if (target < (uint32_t)(*pList * EEP_SCALE - EEP_DELTA)) {
		*pLowerValue = *pList;
		*pUpperValue = *pList;
		return;
	}

	if (target > (uint32_t)(listEndValue * EEP_SCALE + EEP_DELTA)) {
		*pLowerValue = listEndValue;
		*pUpperValue = listEndValue;
		return;
	}

	for (i = 0; i < listSize; i++) {
		if (abs(pList[i] * EEP_SCALE - (int32_t) target) < EEP_DELTA) {
			*pLowerValue = pList[i];
			*pUpperValue = pList[i];
			return;
		}
		if (target < (uint32_t)(pList[i + 1] * EEP_SCALE - EEP_DELTA)) {
			*pLowerValue = pList[i];
			*pUpperValue = pList[i + 1];
			return;
		}
	}
}
/* ---- end verbatim ---- */

static const char *stage = "not-yet";

static void
fault_handler(int sig)
{
	printf("SIGSEGV (%s) โ€” OOB load below the array confirmed\n", stage);
	printf("RESULT: FAULTED at %s : primitive PROVEN\n", stage);
	fflush(stdout);
	_exit(33);	/* distinctive exit code = proof */
}

int
main(void)
{
	uint16_t *array;
	uint16_t lo, hi;
	void *base;
	long pg = sysconf(_SC_PAGESIZE);

	signal(SIGSEGV, fault_handler);
	signal(SIGBUS, fault_handler);

	/* two adjacent pages; first becomes PROT_NONE */
	base = mmap(NULL, 2 * pg, PROT_READ|PROT_WRITE,
	    MAP_PRIVATE|MAP_ANON, -1, 0);
	if (base == MAP_FAILED) { perror("mmap"); return 1; }
	if (mprotect(base, pg, PROT_NONE) != 0) { perror("mprotect"); return 1; }

	/* array sits exactly at the start of the second page */
	array = (uint16_t *)((char *)base + pg);
	memset(array, 0xAA, 64);	/* whatever: must NOT be read below */

	/* simulates ar5211SetRateTable:1530-1537 with a blanked CTL edge row */
	stage = ":1818 *(pList + listSize - 1) with listSize=0 (numEdges=0)";
	printf("calling ar5211GetLowerUpperValues(listSize=0) "
	    "with array at page start, predecessor PROT_NONE...\n");
	fflush(stdout);

	ar5211GetLowerUpperValues(2437 /* freq */, array, 0, &lo, &hi);

	/*
	 * If the OOB load somehow did not fault (e.g. mapping granularity),
	 * the uninitialized-array read at :1829 still produced garbage:
	 */
	printf("no fault: lower=%u upper=%u (from untouched stack)\n", lo, hi);
	printf("RESULT: NOT-FAULTED (weaker evidence of uninit read only)\n");
	return 1;
}