/*
 * DF-4024 trigger.c - re_rxeof device-fed length geometry model.
 *
 * Hardware precondition (cannot be met on this guest - virtio-net only):
 * a faulty or malicious RTL8169SC/8168/8101E/8125 DMAs a completion
 * descriptor into sc->re_ldata.re_rx_list whose 14-bit GFRAGLEN field
 * (RE_RDESC_STAT_GFRAGLEN 0x3FFF, if_rereg.h:139; read by RE_RXBYTES,
 * if_revar.h:54) lies about the received frame length.  The driver
 * (if_re.c re_rxeof) applies that length to an MCLBYTES (2048) cluster
 * with no bound:
 *
 *   single-fragment EOF:   if_re.c:1228-1229
 *       m->m_pkthdr.len = m->m_len = total_len - ETHER_CRC_LEN;
 *     -> m_len up to 16379 on a 2048-byte cluster; every consumer that
 *        trusts m_len (BPF tap at ether_input, m_copydata pulls, MAC/IPv4
 *        header reads past the first 2KB) reads adjacent kernel heap.
 *
 *   multi-fragment EOF:    if_re.c:1209/1226
 *       m_pkthdr.len = total_len - 4 may EXCEED the accumulated chain
 *       (k * 2048 + last-frag bytes); m_copydata then walks m_next past
 *       NULL -> KASSERT panic (INVARIANTS) or NULL+0x58 page fault
 *       (GENERIC, KASSERT compiled out) - sys/kern/uipc_mbuf.c m_copydata.
 *
 *   total_len < 4 sub-case is SAFE: negative m_len is dropped by
 *       ether_input's `m->m_len < sizeof(struct ether_header)' guard
 *       (sys/net/if_ethersubr.c, ether_input) - mirroring ale's
 *       m_devget tot<=0 behavior noted in DF-3989.
 *
 * This program prints the exact OOB extents implied by the masks/geometry.
 */
#include <stdio.h>

#define RE_RDESC_STAT_GFRAGLEN	0x00003fff	/* if_rereg.h:139 */
#define MCLBYTES		2048
#define ETHER_CRC_LEN		4
#define ETHER_HDR_LEN		14

int main(void)
{
	int max_len = RE_RDESC_STAT_GFRAGLEN;		/* device-fed */
	int max_mlen = max_len - ETHER_CRC_LEN;		/* if_re.c:1228 */

	printf("DF-4024 re_rxeof length-trust geometry model\n");
	printf("descriptor GFRAGLEN field       : 14 bits, max %d\n", max_len);
	printf("RX cluster (re_newbuf_std)     : %d bytes (MCLBYTES)\n",
	    MCLBYTES);
	printf("single-frag EOF m_len lie      : %d - %d = %d\n",
	    max_len, ETHER_CRC_LEN, max_mlen);
	printf("  -> heap OOB read past cluster: %d bytes\n",
	    max_mlen - MCLBYTES);

	/* multi-frag: chain of k SOF frags, EOF claims total_len */
	for (int k = 1; k <= 7; k++) {
		int chain = k * MCLBYTES;
		int excess = max_mlen - chain;
		if (excess > 0)
			printf("multi-frag k=%d: pkthdr.len %d > chain %d "
			    "-> m_copydata walks m_next past NULL (+%d)\n",
			    k, max_mlen, chain, excess);
	}

	printf("total_len<4 sub-case           : negative m_len, dropped by "
	    "ether_input m_len<ETHER_HDR_LEN guard (SAFE)\n");
	printf("precondition                   : device-DMA'd descriptor lie "
	    "(faulty/malicious NIC); guest has no re(4) device\n");
	return 0;
}
