DF-3324 / df3324.c
/* * DF-3324 harness -- hostap assoc path: Atheros vendor IE accepted with * isatherosoui() (frm[1] > 3) but its fixed 9-byte layout is then read * by ieee80211_parse_ath() with NO length validation. * * VERBATIM kernel code exercised: * ieee80211_hostap.c:2007 else if (isatherosoui(frm)) ath = frm; * ieee80211_hostap.c:2155 if (ath != NULL) { setie(ath_ie, ...); * ieee80211_hostap.c:2160 ieee80211_parse_ath(ni, ni->ni_ies.ath_ie); * ieee80211_input.h:111-115 isatherosoui(frm): * frm[1] > 3 && le32dec(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI) * ieee80211_superg.c:199-207 ieee80211_parse_ath(): * ni->ni_ath_flags = ath->ath_capability; * ni->ni_ath_defkeyix = le16dec(&ath->ath_defkeyix); * struct ieee80211_ath_ie: { ie; len; oui[4]; ath_capability; * ath_defkeyix[2] } -> reads bytes 6..8 * * ieee80211_saveie() copies exactly frm[1]+2 bytes (6 for the minimal * OUI-only IE) into a heap buffer; parse_ath then reads 3 bytes past * that allocation. Guard-page demonstration. */ #include "guard.h" struct ieee80211_ath_ie { uint8_t ie; uint8_t len; uint8_t oui[4]; uint8_t ath_capability; uint16_t ath_defkeyix; } __packed; static uint16_t le16dec_(const void *p) { const uint8_t *b = p; return b[0] | (b[1] << 8); } static uint32_t le32dec_(const void *p) { const uint8_t *b = p; return b[0] | (b[1]<<8) | (b[2]<<16) | ((uint32_t)b[3]<<24); } #define ATH_OUI 0x7d0a00 #define ATH_OUI_TYPE 0x01 /* verbatim from ieee80211_input.h */ static int isatherosoui(const uint8_t *frm) { return frm[1] > 3 && le32dec_(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); } /* verbatim from ieee80211_superg.c:199 */ static void ieee80211_parse_ath(uint16_t *ath_flags, uint16_t *defkeyix, uint8_t *ie) { const struct ieee80211_ath_ie *ath = (const struct ieee80211_ath_ie *) ie; *ath_flags = ath->ath_capability; *defkeyix = le16dec_(&ath->ath_defkeyix); } static uint16_t flags, keyix; static uint8_t *saved; static uint16_t flags, keyix; static uint8_t *saved; static uint8_t ie[8]; int main(int argc, char **argv) { int oob = (argc > 1 && argv[1][0] == 'o'); uint8_t *p; printf("DF-3324: Atheros vendor IE (len=4) accepted by isatherosoui," " parsed by ieee80211_parse_ath (no length check)\n"); ie[0] = 221; ie[1] = 4; ie[2] = ATH_OUI & 0xff; ie[3] = (ATH_OUI>>8)&0xff; ie[4] = (ATH_OUI>>16)&0xff; ie[5] = ATH_OUI_TYPE; if (!isatherosoui(ie)) { printf("OUI check failed?!\n"); return 1; } printf(" isatherosoui = 1; saveie allocates frm[1]+2 = 6 bytes\n"); p = guard_buf(oob ? 6 : 16); /* control: full 9-byte layout */ memcpy(p, ie, 6); saved = p; printf(" parse_ath reads ath_capability (byte 6) + defkeyix (7-8)\n"); fflush(stdout); #ifdef FIXED /* DF-3324 fix: validate the Atheros IE length before parsing */ if (saved[1] < sizeof(struct ieee80211_ath_ie) - 2) { printf("FIXED: Atheros IE too short (len=%u), discarded\n", saved[1]); return 0; } #endif ieee80211_parse_ath(&flags, &keyix, saved); /* OOB when oob */ printf(" ran (in bounds)\n"); return 0; } |