⬢ DragonFlyBSD Kernel Audit
DF-3325 / dfpredispatch.c
← back to finding ↓ download raw
/*
 * DF-3325 / DF-3342 / DF-3416 harness (shared shape, one binary per
 * finding compiled with -DHOSTAP / -DSTA / -DADHOC) -- pre-dispatch
 * rx-sequence block reads i_seq (offset 22) and i_qos (offset 24) of
 * TODS/DSTODS frames although only frame_ack (10 bytes) is guaranteed.
 *
 * VERBATIM kernel code (hostap.c:580-590 == sta.c:641-649 ==
 * adhoc.c:410-419 modulo the addr2 precondition):
 *
 *	if (IEEE80211_HAS_SEQ(type, subtype)) {
 *		uint8_t tid = ieee80211_gettid(wh);
 *		if (IEEE80211_QOS_HAS_SEQ(wh) &&
 *		    TID_TO_WME_AC(tid) >= WME_AC_VI)
 *			ic->ic_wme.wme_hipri_traffic++;
 *		if (! ieee80211_check_rxseq(ni, wh, bssid))
 *			goto out;
 *	}
 *
 * The >= 24-byte length check only guards the NODS bssid path
 * (hostap.c:557-566); a TODS QoS data frame of 10 bytes passes:
 *   dir = TODS -> bssid = wh->i_addr1 (bytes 4..10, in range)
 *   IEEE80211_HAS_SEQ(DATA) = true
 *   ieee80211_gettid -> IEEE80211_QOS_HAS_SEQ(wh) (reads i_fc[0]) ->
 *       ((qosframe*)wh)->i_qos[0]  = byte 24 of a 10-byte frame.
 * ieee80211_check_rxseq() then reads wh->i_seq (bytes 22-23).
 */
#include "guard.h"

/* frame layout */
struct ieee80211_frame {
	uint8_t i_fc[2], i_dur[2];
	uint8_t i_addr1[6], i_addr2[6], i_addr3[6];
	uint16_t i_seq;
} __packed;
struct ieee80211_qosframe {
	struct ieee80211_frame wh;
	uint16_t i_qos[2];
} __packed;

/* verbatim macros (ieee80211.h / _ieee80211.h) */
#define	IEEE80211_FC0_TYPE_MASK		0x0c
#define	IEEE80211_FC0_SUBTYPE_QOS	0x80
#define	IEEE80211_FC0_TYPE_DATA		0x08
#define	IEEE80211_FC1_DIR_MASK		0x03
#define	IEEE80211_FC1_DIR_TODS		0x01
#define	IEEE80211_FC1_DIR_NODS		0x00
#define	IEEE80211_FC0_TYPE_CTL		0x04
#define	IEEE80211_QOS_TID		0x0f
#define	IEEE80211_HAS_SEQ(t, s) \
	(((t) == IEEE80211_FC0_TYPE_DATA || \
	  !((s) & 0x40) /* !CF-Ack,CF-Poll */) && \
	 (t) != IEEE80211_FC0_TYPE_CTL)
#define	IEEE80211_QOS_HAS_SEQ(wh) \
	(((wh)->i_fc[0] & \
	  (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \
	  (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS))

/* verbatim ieee80211_proto.h:337-350 */
static uint8_t
ieee80211_gettid(const struct ieee80211_frame *wh)
{
	uint8_t tid;
	if (IEEE80211_QOS_HAS_SEQ(wh)) {
		tid = ((const struct ieee80211_qosframe *)wh)->i_qos[0];
		tid &= IEEE80211_QOS_TID;
	} else
		tid = 7;	/* IEEE80211_NONQOS_TID */
	return tid;
}

static int hipri;

/* the pre-dispatch block, verbatim shape */
static int
predispatch(struct ieee80211_frame *wh, uint8_t *bssid)
{
	int type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
	int subtype = wh->i_fc[0] & 0xf0;
	int dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;

	/* only the NODS path is length-checked in the kernel */
	if (dir != IEEE80211_FC1_DIR_NODS)
		bssid = wh->i_addr1;		/* bytes 4..10 -- in range */
	else
		return 0;			/* (NODS path is guarded) */

	if (IEEE80211_HAS_SEQ(type, subtype)) {
		uint8_t tid = ieee80211_gettid(wh);	/* reads byte 24 */
		if (IEEE80211_QOS_HAS_SEQ(wh) && (tid & 7) >= 5)
			hipri++;
		/* ieee80211_check_rxseq(ni, wh, bssid) reads wh->i_seq */
		volatile uint16_t seq __unused = wh->i_seq;	/* bytes 22..23 */
	}
	return 0;
}

static struct ieee80211_frame *wh;

static struct ieee80211_frame *wh;

int df_frame_len;

int
main(int argc, char **argv)
{
	int oob = (argc > 1 && argv[1][0] == 'o');

#if defined(HOSTAP)
	printf("DF-3325 (hostap_input pre-dispatch, hostap.c:580-590)\n");
#elif defined(STA)
	printf("DF-3342 (sta_input pre-dispatch, sta.c:641-649)\n");
#else
	printf("DF-3416 (adhoc_input pre-dispatch, adhoc.c:410-419)\n");
#endif
	wh = (struct ieee80211_frame *)guard_buf(oob ? 10 : 30);
	df_frame_len = oob ? 10 : 30;
	wh->i_fc[0] = IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS;
	wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
	memset((void *)wh->i_addr1, 0xff, 6);
	printf("  frame: %d bytes, TODS, QoS data; i_qos at offset 24, i_seq at 22\n",
	    oob ? 10 : 30);
	printf("  pre-dispatch block: gettid reads i_qos[0]; check_rxseq reads i_seq\n");
	fflush(stdout);
#if defined(FIXED) && defined(HOSTAP)
	/* DF-3325 fix: require a full header for any sequenced frame */
	if (((struct { char b[30]; } *)0, 0)) {}
#endif
#if defined(FIXED) && (defined(STA) || defined(ADHOC) || defined(HOSTAP))
	{
		/* pre-dispatch length gate: sequenced/QoS frames need the
		 * full qosframe header present (>= 26 bytes TODS QoS) */
		extern int df_frame_len;
		if (df_frame_len < 26) {
			printf("FIXED: frame too short for seq/QoS pre-dispatch"
			    " (%d bytes), rejected\n", df_frame_len);
			return 0;
		}
	}
#endif
	predispatch(wh, NULL);
	printf("  ran (frame long enough)\n");
	return 0;
}