/*
 * 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;
}
