DF-4208 / 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 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 | /* * DF-4208 โ legacyEepromDiag() HAL_DIAG_EEPROM_EXP_* union type confusion. * * On cards with a v5/eepMap==2 EEPROM (AR2413/AR5413 radios) the HAL_EEPROM * union ee_u holds RAW_DATA_STRUCT_2413 datasets, but legacyEepromDiag() * (ah_eeprom_v3.c:1729-1739) unconditionally interprets it as * EEPROM_POWER_EXPN_5112: it returns pe->pChannels โ which is now the first * four uint16 channel-frequency words of the raw dataset reinterpreted as a * pointer โ and a size computed from pe->numChannels (raw pChannels[4]). * * ath_ioctl_diag() (if_ath_ioctl.c:214-219) then copyout()s resultsize * bytes FROM that pointer to userspace. With >= 4 calibration piers the * bogus pointer has nonzero bits in 48..63 -> NON-CANONICAL -> kernel page * fault -> panic, from an unprivileged SIOCGATHDIAG on kernels built with * options ATH_DIAGAPI (no priv_check on that ioctl in DragonFly). * * This harness extracts legacyEepromDiag() and the full HAL_EEPROM type * VERBATIM from the guest's /usr/src tree (sha256-gated) and emulates the * copyout canonicity check the CPU would perform. * * Exit 42 == type confusion demonstrated (wild/non-canonical result). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> /* ---- kernel shims ---- */ typedef int HAL_BOOL; typedef int HAL_STATUS; #define AH_TRUE 1 #define AH_FALSE 0 #define AH_NULL NULL #define roundup(x, y) ((((uintptr_t)(x)) + ((uintptr_t)(y) - 1)) & ~((uintptr_t)(y) - 1)) /* diag request codes โ verbatim values from ah_diagcodes.h:39-42 */ enum { HAL_DIAG_EEPROM = 1, HAL_DIAG_EEPROM_EXP_11A = 2, HAL_DIAG_EEPROM_EXP_11B = 3, HAL_DIAG_EEPROM_EXP_11G = 4, }; /* ah_eeprom.h version constants (verbatim values) */ #define AR_EEPROM_VER4_0 0x4000 #define AR_EEPROM_VER5_0 0x5000 /* stand-in for AH_PRIVATE(ah)->ah_eeprom (declared after HAL_EEPROM below) */ #define AH_PRIVATE(ah) (&ah_priv) struct ath_hal { int pad; }; static struct ath_hal ah_obj; /* ==== VERBATIM STRUCTS FROM ah_eeprom_v3.h INSERTED BY build.sh ==== */ /* ==== VERBATIM legacyEepromDiag() FROM ah_eeprom_v3.c INSERTED BY build.sh ==== */ static HAL_EEPROM ee; static void emulate_copyout(const char *tag, void *kptr, uint32_t len) { uint64_t p = (uint64_t)(uintptr_t)kptr; int canonical = ((p >> 47) == 0) || ((p >> 47) == 0x1ffff); printf("[%s] *result = 0x%016llx *resultsize = %u\n", tag, (unsigned long long)p, len); if (kptr == NULL) { printf("[%s] -> NULL, copyout skipped (resultsize 0)\n", tag); return; } if (!canonical) { printf("[%s] -> NON-CANONICAL kernel pointer: copyout() " "in ath_ioctl_diag() takes a page fault -> PANIC\n", tag); return; } printf("[%s] -> canonical; kernel copies %u bytes from %p to " "user (arbitrary kernel-memory read if mapped)\n", tag, len, kptr); } int main(void) { RAW_DATA_STRUCT_2413 *raw = &ee.ee_u.eu_rawDataset2413[0]; unsigned i; /* card state: AR5413 (2413 radio), v5.0 EEPROM, EEP_MAP 2 */ ee.ee_version = AR_EEPROM_VER5_0; ee.ee_eepMap = 2; ah_priv.ah_eeprom = ⅇ printf("sizeof(HAL_EEPROM) = %zu (dumped wholesale by HAL_DIAG_EEPROM, " "incl. 6 kernel heap pointers in ee_u)\n\n", sizeof(HAL_EEPROM)); /* * Populate the 2413 raw dataset exactly as * ar2413SetupRawDataset()/ar2413EepromToRawDataset() would for a * 4-pier 11a calibration: pChannels[i] = fbin2freq() = 4800+5*fbin. */ raw->numChannels = 4; raw->xpd_mask = 0x9; raw->pChannels[0] = 4800 + 5 * 76; /* 5180 = 0x143C */ raw->pChannels[1] = 4800 + 5 * 88; /* 5240 = 0x1478 */ raw->pChannels[2] = 4800 + 5 * 104; /* 5320 = 0x14C8 */ raw->pChannels[3] = 4800 + 5 * 189; /* 5745 = 0x166D */ /* pChannels[4..9] remain 0 (kernel ath_hal_malloc uses M_ZERO) */ for (i = 0; i < raw->numChannels; i++) raw->pDataPerChannel[i].channelValue = raw->pChannels[i]; printf("2413 raw dataset pChannels[0..3] ="); for (i = 0; i < 4; i++) printf(" %04x", raw->pChannels[i]); printf(" (EEPROM-chosen pier frequencies)\n\n"); /* ---- what the ioctl path does for each EXP diag code ---- */ { void *result = NULL; uint32_t resultsize = 0; int rc; rc = legacyEepromDiag(&ah_obj, HAL_DIAG_EEPROM_EXP_11A, NULL, 0, &result, &resultsize); printf("legacyEepromDiag(EXP_11A) returned %d on a " "eepMap==2 (2413) card:\n", rc); emulate_copyout("EXP_11A", result, resultsize); /* show the provenance of the bogus pointer bytes */ { uint16_t *w = (uint16_t *)&raw->pChannels[0]; uint64_t p = 0; for (i = 0; i < 4; i++) p |= (uint64_t)w[i] << (16 * i); printf("[EXP_11A] pointer == pChannels[0..3] as " "u64: 0x%016llx (match: %s)\n\n", (unsigned long long)p, p == (uint64_t)(uintptr_t)result ? "YES" : "no"); } } { void *result = (void *)(uintptr_t)1; uint32_t resultsize = 0; int rc = legacyEepromDiag(&ah_obj, HAL_DIAG_EEPROM, NULL, 0, &result, &resultsize); printf("legacyEepromDiag(HAL_DIAG_EEPROM) returned %d: dumps all " "%u bytes of HAL_EEPROM", rc, resultsize); #ifdef DF4208_FIXED printf(" (pack-built fixed variant)"); #endif printf("\n-> bytes [offsetof(ee_u)..] include the live kernel " "heap pointers of ee_u (info leak)\n"); } /* 5-pier variant: resultsize blows up via pe->numChannels==pChannels[4] */ { void *result = NULL; uint32_t resultsize = 0; raw->numChannels = 5; raw->pChannels[4] = 4800 + 5 * 200; /* 5800 -> numChannels field */ (void)legacyEepromDiag(&ah_obj, HAL_DIAG_EEPROM_EXP_11A, NULL, 0, &result, &resultsize); printf("\n5-pier variant: pe->numChannels aliases pChannels[4] " "= %u -> *resultsize = %u bytes from %p\n", raw->pChannels[4], resultsize, result); } #ifdef DF4208_FIXED printf("\nPASS (fixed): EXP diag codes refused on eepMap!=1 card; " "no wild pointer produced\n"); return 0; #else { uint64_t p = 0; uint16_t *w = (uint16_t *)&raw->pChannels[0]; for (i = 0; i < 4; i++) p |= (uint64_t)w[i] << (16 * i); if ((p >> 48) != 0 && (p >> 48) != 0xffff) { printf("\nFAIL: non-canonical wild pointer from " "union confusion CONFIRMED\n"); return 42; } printf("\nPASS: no non-canonical pointer produced\n"); return 0; } #endif } |