/*
 * DF-1410 harness — xe(4) RX packet-length overflow
 *
 * sys/dev/netif/xe/if_xe.c:752  len = XE_INW(XE_RBC) - ETHER_CRC_LEN;
 *                              ^ u_int16_t, no mask vs XE_RBC_BYTE_COUNT (0x1fff),
 *                                no upper-bound vs MCLBYTES(2048)
 * sys/dev/netif/xe/if_xe.c:826  bus_space_read_multi_2(bst, bsh, XE_EDP,
 *                                                       (u_int16_t*)ehp,
 *                                                       (len + 1) >> 1);
 *                              ^ copies 2*ceil(len/2) bytes into a 2KB mbuf
 *                                cluster window — overflow into adjacent heap.
 *
 * The guest has no Xircom CE PCMCIA hardware (no PCMCIA bridge in QEMU).
 * This harness extracts the buggy arithmetic and demonstrates that the OOB
 * length is real and unbounded: a 8KB packet (or a wrapped 0xFFFC RBC) yields
 * a many-KB overflow of the 2KB cluster. It uses the genuine register layout
 * from if_xereg.h:250-273 and the genuine len-arithmetic from if_xe.c:752,
 * 775-827.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define ETHER_CRC_LEN 4
#define XE_RBC_BYTE_COUNT 0x1fff       /* if_xereg.h:269 */
#define MCLBYTES 2048                  /* 2 KiB cluster */
#define MHLEN   160                    /* mbuf header data area (approx) */

/* Simulate XE_INW: returns the full 16-bit register (incl. status bits).
 * Silicon can legitimately present byte counts up to 8184 for long packets. */
static uint16_t XE_INW_RBC(uint16_t raw) { return raw; }

/* The buggy computation from if_xe.c:750,752. */
static uint16_t buggy_len(uint16_t rbc)
{
    uint16_t len = XE_INW_RBC(rbc) - ETHER_CRC_LEN;     /* line 752 */
    return len;
}

/* The fixed computation: mask the count bits, sanity-range it. */
static int fixed_len(uint16_t rbc)
{
    int raw = rbc & XE_RBC_BYTE_COUNT;                  /* mask the 13 count bits */
    int len = raw - ETHER_CRC_LEN;                       /* signed */
    if (len <= 0) return 0;
    if (len > MCLBYTES - 2 - 1) return -1;              /* too big */
    return len;
}

/* Words copied = (len+1)>>1, bytes = words*2.  Compare to MCLBYTES-2. */
static int overflow_bytes(int len)
{
    int bytes = ((len + 1) >> 1) * 2;                   /* line 826 word count */
    /* m_data is bumped +2 in line 784, so usable window is MCLBYTES - 2. */
    int window = MCLBYTES - 2;
    return bytes - window;
}

int main(void)
{
    int bad = 0, fixed_ok = 0;
    struct case_t { const char *name; uint16_t rbc; } cases[] = {
        { "normal 1500B packet",        1500 + ETHER_CRC_LEN },
        { "max legal 1518B packet",     1518 + ETHER_CRC_LEN },
        { "silicon long 8184B packet",  8184 },               /* legal per silicon */
        { "RBC=0  (wrap to 0xFFFC)",    0 },
        { "RBC=1  (wrap to 0xFFFD)",    1 },
        { "RBC=3  (wrap to 0xFFFF)",    3 },
        { "RBC=4  (len=0)",             4 },
    };
    size_t n = sizeof(cases)/sizeof(cases[0]);
    printf("%-32s %6s %8s %8s %8s %8s\n",
           "case","RBC","bug_len","oob_B","fix_len","oob_B");
    for (size_t i=0;i<n;i++) {
        uint16_t r = cases[i].rbc;
        int bl = buggy_len(r);
        int bo = overflow_bytes(bl);
        int fl = fixed_len(r);
        int fo = fl > 0 ? overflow_bytes(fl) : 0;
        printf("%-32s %6u %8d %8d %8d %8d\n",
               cases[i].name, (unsigned)r,
               (int)bl, bo, fl, fo);
        if (bo > 0) bad++;
        if (fl >= 0 && fo == 0) fixed_ok++;
    }
    printf("\nBuggy driver: %d/%zu cases overflow the 2KiB mbuf cluster\n", bad, n);
    printf("Fixed driver : %d/%zu cases accept & fit, rest rejected\n", fixed_ok, n);

    if (bad >= 4) {
        printf("\nCONFIRMED: missing upper bound on XE_RBC lets oversized packets "
               "(or RBC<4 unsigned wrap) overflow the 2048-byte mbuf cluster by "
               "thousands of bytes — heap corruption on every received long/"
               "runted packet, triggerable by an unauthenticated sender on the "
               "same L2 segment.\n");
        return 0;
    }
    fprintf(stderr, "NOT CONFIRMED\n");
    return 1;
}
