DF-3178 / fix.diff
--- a/sys/vfs/hpfs/hpfs_vfsops.c +++ b/sys/vfs/hpfs/hpfs_vfsops.c @@ -31,6 +31,7 @@ #include <sys/systm.h> #include <sys/nlookup.h> #include <sys/conf.h> +#include <sys/device.h> #include <sys/proc.h> #include <sys/kernel.h> #include <sys/vnode.h> @@ -290,6 +291,42 @@ goto failed; } + /* + * DF-3178: validate the volume census BEFORE any of it is used as + * a size or index. Previously only the magic numbers were checked: + * an on-disk su_btotal then flowed unvalidated into hpfs_bminit(), + * where dbnum = (su_btotal + 0x3FFF) / 0x4000 drives both the + * bitmap/bmind kmalloc sizes and a single bmind-list bread of + * ((dbnum + 0x7F) & ~0x7F) << 2 bytes. A crafted su_btotal + * >= 0x10000001 made that bread exceed MAXBSIZE and panicked the + * kernel at mount time; smaller lies wired attacker-chosen kernel + * heap (dbnum * 2048 bytes) and were echoed verbatim into statfs + * f_blocks. (su_btotal >= 0xFFFFC001 additionally wraps the u32 + * addition -- DF-0862's undersize regime.) + * + * Bounds: + * - non-zero, and no larger than the backing device + * (dev_dpsize() reports DEV_BSIZE-scale blocks); + * - at most 0x4000 data bands, which keeps hpfs_bminit's single + * bitmap-index bread within MAXBSIZE (volumes > 128 GiB in + * 512-byte sectors are beyond what this driver's bminit can + * read in one buffer; reject instead of panicking); + * - su_rootfno must lie inside the volume. + */ + { + int64_t devsize = dev_dpsize(dev); + u_int64_t dbnum = ((u_int64_t)sup->su_btotal + 0x3FFF) / 0x4000; + + if (sup->su_btotal == 0 || + (devsize > 0 && (u_int64_t)sup->su_btotal > (u_int64_t)devsize) || + dbnum > 0x4000 || sup->su_rootfno >= sup->su_btotal) { + kprintf("hpfs_mountfs: implausible su_btotal %u or " + "su_rootfno %u\n", sup->su_btotal, sup->su_rootfno); + error = EINVAL; + goto failed; + } + } + mp->mnt_data = (qaddr_t)hpmp; hpmp->hpm_devvp = devvp; hpmp->hpm_dev = dev; |