DF-3960 / trigger.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 | /* DF-3960 โ ar5416GetGainBoundariesAndPdadcs() out-of-bounds READS on * degenerate EEPROM calibration data. * * sys/dev/netif/ath/ath_hal/ar5416/ar5416_reset.c * * (a) xpdGain mask == 0 (crafted/corrupt EEPROM): * * ar5416SetPowerCalTable() (ar5416_reset.c:2337-2405) computes * numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, ...) which returns * 0 for an empty mask with no validation (:2247-2265). It then calls * ar5416GetGainBoundariesAndPdadcs(..., numXpdGains=0) (:2387-2391): * - both `for (i = 0; i < numXpdGains; i++)' loops are skipped, * leaving i == 0 and k == 0; * - `while (i < AR5416_PD_GAINS_IN_MASK) * pPdGainBoundaries[i] = pPdGainBoundaries[i-1];' (:2566-2568) * reads pPdGainBoundaries[-1] โ the caller's STACK array * gainBoundaries[4] (ar5416_reset.c:2345); * - `while (k < AR5416_NUM_PDADC_VALUES) * pPDADCValues[k] = pPDADCValues[k-1];' (:2571-2573) * reads pPDADCValues[-1] โ 1 byte before the driver-static * pdadcValues[128] (ar5416_reset.c:2344). * The garbage propagates into PHY registers TPCRG5/PDADC. * * (b) degenerate pier power range (maxPwrT4[i] == minPwrT4[i]): * * sizeCurrVpdTable = (max - min)/2 + 1 == 1 (:2541), then * `vpdStep = vpdTableI[i][sizeCurrVpdTable - 1] * - vpdTableI[i][sizeCurrVpdTable - 2];' (:2549) * reads vpdTableI[i][-1] and vpdTableI[i][-2] โ static .bss OOB reads. * * Triggered on every channel change once the EEPROM holds the degenerate * values (attacker model: malicious/corrupted NIC EEPROM, same model as * DF-1308/DF-1309). * * This harness replicates both index computations exactly. */ #include <stdio.h> #include <stdint.h> #include <string.h> #define AR5416_NUM_PD_GAINS 4 #define AR5416_PD_GAINS_IN_MASK 4 #define AR5416_NUM_PDADC_VALUES 128 /* instrumented arrays: record every index touched */ static int16_t pdadc_shadow[AR5416_NUM_PDADC_VALUES + 2]; /* [-1]..[128] */ static int16_t *pPDADCValues = &pdadc_shadow[1]; /* index 0 == real[0] */ static int16_t gainb_shadow[AR5416_PD_GAINS_IN_MASK + 1]; static int16_t *pPdGainBoundaries = &gainb_shadow[1]; static int oob_read_pdadc, oob_read_gainb, oob_read_vpd; /* vpdTableI is [4][64]; we only need the [-2]/[-1] probe of row i */ static uint8_t vpd_shadow[4][64 + 2]; static void replica_backfill(int numXpdGains) { int i = 0, k = 0; /* `for (i = 0; i < numXpdGains; i++)' โ both loops skipped for 0. * The main fill loop writes at least one PDADC entry per gain * (the extrapolate loop at :2555-2561 always runs >= 1 iteration * for the last gain), so k >= 1 whenever numXpdGains >= 1. */ for (; i < numXpdGains; i++) ; k = (numXpdGains > 0) ? 1 : 0; /* ar5416_reset.c:2565-2569 */ while (i < AR5416_PD_GAINS_IN_MASK) { if (i - 1 < 0) oob_read_gainb++; /* pPdGainBoundaries[-1] */ pPdGainBoundaries[i] = (i - 1 >= 0) ? pPdGainBoundaries[i - 1] : 0x4242/*the OOB load*/; i++; } /* ar5416_reset.c:2571-2574 */ while (k < AR5416_NUM_PDADC_VALUES) { if (k - 1 < 0) oob_read_pdadc++; /* pPDADCValues[-1] */ pPDADCValues[k] = (k - 1 >= 0) ? pPDADCValues[k - 1] : 0x4242; k++; } } static int replica_vpdstep(int maxPwrT4, int minPwrT4, int row) { uint16_t sizeCurrVpdTable; /* ar5416_reset.c:2541 */ sizeCurrVpdTable = (uint8_t)((maxPwrT4 - minPwrT4) / 2 + 1); /* ar5416_reset.c:2549 โ reads [size-1] and [size-2] */ if ((int)sizeCurrVpdTable - 2 < 0) oob_read_vpd++; /* vpdTableI[row][-1] or [-2] */ /* copy loop bound: while (ss < maxIndex <= sizeCurrVpdTable) reads * vpdTableI[row][ss] โ OOB past the 64-entry row when size > 64 */ if (sizeCurrVpdTable > 64) oob_read_vpd++; /* reads [64..size-1] of row */ return ((int)sizeCurrVpdTable < 2) ? -1 : 0; } int main(void) { printf("== DF-3960 ar5416GetGainBoundariesAndPdadcs degenerate-EEPROM " "OOB reads ==\n"); /* (a) xpdGain mask == 0 โ ar5416_reset.c:2371,2387,2566-2574 */ replica_backfill(0); printf("(a) xpdGain mask 0: pPdGainBoundaries[-1] stack OOB read " "(caller gainBoundaries[4], ar5416_reset.c:2345): %s\n", oob_read_gainb ? "YES (ar5416_reset.c:2567)" : "no"); printf(" pPDADCValues[-1] static OOB read " "(pdadcValues[128], ar5416_reset.c:2344): %s\n", oob_read_pdadc ? "YES (ar5416_reset.c:2572)" : "no"); /* (b) zero-range pier: maxPwrT4 == minPwrT4 โ ar5416_reset.c:2541,2549 */ replica_vpdstep(100, 100, 0); printf("(b) maxPwrT4==minPwrT4: sizeCurrVpdTable==1 -> " "vpdTableI[i][-1]/[-2] reads (ar5416_reset.c:2549): %s\n", oob_read_vpd ? "YES" : "no"); /* (c) inverted range: maxPwrT4 < minPwrT4 โ (uint8_t)((0-255)/2+1) * truncates to 130 > 64 -> copy loop reads past the row */ oob_read_vpd = 0; replica_vpdstep(0, 255, 0); printf("(c) inverted range (0,255): sizeCurrVpdTable==130 -> " "vpdTableI[i][64..129] row OOB reads (:2545-2547): %s\n", oob_read_vpd ? "YES" : "no"); /* control: sane EEPROM values touch no OOB index */ oob_read_gainb = oob_read_pdadc = oob_read_vpd = 0; replica_backfill(2); replica_vpdstep(120, 40, 0); printf("control (2 xpd gains, range 80): OOB reads = %d " "(expect 0)\n", oob_read_gainb + oob_read_pdadc + oob_read_vpd); return (0); } |