DF-4758 / df4758_swapon.c
/* * DF-4758 — trigger for vinumsize() OOB read via swapon(2) on raw vinum * subdisk device nodes. * * Root cause (sys/dev/raid/vinum/vinum.c:558-572): * vinumsize() does `vol = &VOL[Volno(dev)]` with no DEVTYPE dispatch and * no bounds check. For raw subdisk devices the minor number is * VINUM_SD(sdno) = (VINUM_RAWSD_TYPE<<28) | (sdno & 0xff) | ... * so Volno(dev) == sdno & 0xff — the *subdisk* number is used to index * the *volume* array. vinum_conf.volume has volumes_allocated entries * (4 initially); any subdisk with sdno >= volumes_allocated reads out of * bounds. Even in-bounds indices return the *volume's* size (and only * when that volume happens to be up) instead of the subdisk's size. * * This program does not itself trigger anything exotic: it just calls * swapon(2) on device paths (the kernel then does VOP_OPEN + dev_dpsize() * -> vinumsize() exactly as sys/vm/vm_swap.c:296-308 does for any swapon * candidate). We do it from C so the exact errno / result is visible. * * Build: cc -O -o df4758_swapon df4758_swapon.c * Run (root, after vinum config exists): * ./df4758_swapon /dev/vinum/sd/s0 # in-bounds wrong object (volume v0 size) * ./df4758_swapon /dev/vinum/sd/s1 # in-bounds, VOL[1] unallocated -> ENXIO * ./df4758_swapon /dev/vinum/sd/s4 # OOB: VOL[4] past 4-entry array * ./df4758_swapon /dev/vinum/sd/s5 # OOB: VOL[5] * then `swapinfo` to see what size was installed. */ #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> int main(int argc, char **argv) { int i; if (argc < 2) { fprintf(stderr, "usage: %s device...\n", argv[0]); return 2; } for (i = 1; i < argc; i++) { errno = 0; printf("== swapon(%s): ", argv[i]); fflush(stdout); if (swapon(argv[i]) == 0) printf("OK\n"); else printf("FAILED errno=%d (%s)\n", errno, strerror(errno)); fflush(stdout); } printf("== swapinfo now:\n"); fflush(stdout); system("swapinfo"); return 0; } |