DragonFlyBSD Kernel Audit
DF-0940 / trig.c
← back to finding ↓ download raw
/*
 * trig.c - DF-0940 verification trigger.
 *
 * The finding claims an unprivileged user can mmap an >4 GiB MAP_STACK and
 * trigger an integer-truncation DoS in vm_map_growstack(). This trigger
 * exercises that exact code path and demonstrates that:
 *
 *   (a) mmap(MAP_STACK | MAP_ANON, 8 GiB) succeeds (RLIMIT_VMEM = infinity),
 *   (b) the returned region is a NORMAL anon mapping (MAP_STACK was stripped
 *       by vm_mmap.c:429-436), and
 *   (c) deep reads/writes succeed without invoking vm_map_growstack at all —
 *       no panic, no DoS, no kernel memory growth.
 *
 * Build: cc -O0 -o trig trig.c
 * Run:   ./trig
 *
 * Expected output on the vulnerable (and fixed) kernel: prints the mapping
 * details, reads a zero byte, exits 0. Guest stays up.
 */
#define _GNU_SOURCE
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static void dump_map_range(const char *p, size_t stacksz) {
    FILE *f = fopen("/proc/curproc/map", "r");
    if (!f) return;
    char line[256];
    unsigned long lo, hi;
    while (fgets(line, sizeof line, f)) {
        if (sscanf(line, "%lx %lx", &lo, &hi) == 2 &&
            lo >= (unsigned long)p &&
            hi <= (unsigned long)p + stacksz + 0x100000) {
            fprintf(stderr, "  %s", line);
        }
    }
    fclose(f);
}

int
main(void)
{
    size_t stacksz = (size_t)8 << 30;   /* 8 GiB */

    char *p = mmap(NULL, stacksz, PROT_READ | PROT_WRITE,
                   MAP_STACK | MAP_ANON, -1, 0);
    if (p == MAP_FAILED) {
        perror("mmap MAP_STACK");
        return 1;
    }
    fprintf(stderr, "MAP_STACK at %p, size %zu GiB\n", p, stacksz >> 30);
    fprintf(stderr, "--- /proc/curproc/map entries in range ---\n");
    dump_map_range(p, stacksz);

    /* Write to the lowest byte of the reserved range. If MAP_STACK were
     * honored, this would trigger vm_map_growstack with a >4 GiB grow_amount.
     * Because MAP_STACK is stripped (vm_mmap.c:429), this is just a normal
     * anon page write — succeeds trivially. */
    volatile char *bottom = p;
    *bottom = 'x';
    fprintf(stderr, "wrote *bottom = 'x' (no fault, no growstack)\n");

    /* Read 5 GiB "below" the stack top. Again, just a normal anon read. */
    volatile char *deep = p + (stacksz - (5ULL << 30));
    char c = *deep;
    fprintf(stderr, "read *deep (%p) = 0x%02x (no fault, no growstack)\n",
            deep, (unsigned char)c);
    fprintf(stderr, "RESULT: trig exits 0; guest unaffected. Bug NOT triggered.\n");

    return 0;
}