/*
 * DF-4067 trigger/geometry model (runs on the audit guest; userland).
 *
 * Bug modeled (source-certain, hardware-gated):
 *   ti_rxeof() sets the delivered mbuf's length from the NIC-DMA'd
 *   return-descriptor field ti_len (uint16_t, if_tireg.h:710) with no
 *   bound against the buffer actually backing the descriptor:
 *
 *     m->m_pkthdr.len = m->m_len = cur_rx->ti_len;   if_ti.c:1690
 *
 *   Buffers per ring:
 *     std   : m_getcl cluster, MCLBYTES = 2048 (param.h:497), then
 *             m_adj(m, ETHER_ALIGN=2) -> usable 2046 (if_ti.c:694-705);
 *             firmware-legit max is rcb->ti_max_len = TI_FRAMELEN = 1518
 *             (if_ti.c:1279, tireg.h:821).
 *     mini  : MGETHDR mbuf, data area MHLEN (<= ~480), m_adj(2)
 *             (if_ti.c:730-741); rcb max_len = MHLEN - ETHER_ALIGN
 *             (if_ti.c:1305).
 *     jumbo : driver jumbo pool slot TI_JLEN = roundup(TI_JUMBO_FRAMELEN
 *             + 2, 8) = 9024, data at +2 -> usable 9022 (if_ti.c:799-802,
 *             tireg.h:1048-1050,822).
 *
 *   A descriptor claiming ti_len > usable makes the mbuf claim more bytes
 *   than its cluster holds; every consumer trusting m_len/m_pkthdr.len
 *   (BPF tap, ether_input, protocol m_copydata/pullup, socket receive)
 *   reads PAST the buffer: kernel-heap disclosure into delivered frames
 *   (DF-3604 bce / DF-3989 ale / DF-4024 re / DF-4045 stge / DF-4055 et
 *   family) or a page fault on unmapped tail.  The std path alone allows
 *   m_len up to 65535 on a 2048-byte cluster = 63489 bytes of OOB read.
 *
 *   This program prints the per-ring OOB read extents for the full 16-bit
 *   ti_len range.  It does NOT touch kernel memory: the real path needs a
 *   Tigon NIC (faulty/malicious firmware or DMA tampering) that writes a
 *   lying ti_len into the return ring.
 */

#include <stdio.h>

#define MCLBYTES        2048    /* sys/sys/param.h:497 */
#define ETHER_ALIGN     2       /* sys/net/ethernet.h:41 */
#define TI_FRAMELEN     1518    /* if_tireg.h:821 */
#define TI_JUMBO_FRAMELEN 9018  /* if_tireg.h:822 */
#define TI_JRAWLEN      (TI_JUMBO_FRAMELEN + ETHER_ALIGN)      /* 9020 */
#define TI_JLEN         ((TI_JRAWLEN + 7) & ~7)                /* 9024 */
#define MHLEN_GUEST     224     /* conservative lower bound; DFly MSIZE=512,
                                   sizeof(m_hdr)+sizeof(pkthdr) ~= 288 ->
                                   MHLEN ~= 224 (exact value boot-dependent) */

struct ringinfo {
	const char *ring;
	unsigned    usable;     /* bytes actually addressable from m_data */
	unsigned    legit_max;  /* firmware-legit max (rcb->ti_max_len) */
	unsigned    claimed_max;/* NIC-claimable (u16) */
};

static struct ringinfo rings[] = {
	{ "std   (MCLBYTES cluster)", MCLBYTES - ETHER_ALIGN, TI_FRAMELEN, 65535 },
	{ "mini  (MHLEN mbuf hdr area)", MHLEN_GUEST - ETHER_ALIGN, MHLEN_GUEST - ETHER_ALIGN, 65535 },
	{ "jumbo (TI_JLEN slot)", TI_JLEN - ETHER_ALIGN, TI_JUMBO_FRAMELEN, 65535 },
};

int main(void)
{
	unsigned i;

	printf("DF-4067 model: ti_rxeof trusts NIC-DMA'd u16 ti_len "
	       "(if_ti.c:1690) with no bound vs buffer size\n\n");
	for (i = 0; i < sizeof(rings)/sizeof(rings[0]); i++) {
		printf("%-28s usable=%-5u firmware-legit max=%-5u "
		       "claim max=%u -> OOB read up to %u bytes past buffer\n",
		       rings[i].ring, rings[i].usable, rings[i].legit_max,
		       rings[i].claimed_max,
		       rings[i].claimed_max - rings[i].usable);
	}
	printf("\nstd ring detail: m_len=65535 on 2048B cluster -> "
	       "m_copydata/BPF read 65535 bytes starting at data(+2): "
	       "%u bytes OOB (16x the cluster)\n",
	       65535u - (MCLBYTES - ETHER_ALIGN));
	printf("jumbo ring detail: m_len=65535 on 9024B slot -> %u bytes OOB; "
	       "slot pool block = TI_JSLOTS*TI_JLEN = %u bytes, so claims beyond "
	       "the block read past the contigmalloc region\n",
	       65535u - (TI_JLEN - ETHER_ALIGN), 384u * TI_JLEN);
	printf("\nRC_DF4067_MODEL=0\n");
	return 0;
}
