diff --git a/sys/vfs/hammer/hammer_volume.c b/sys/vfs/hammer/hammer_volume.c --- a/sys/vfs/hammer/hammer_volume.c +++ b/sys/vfs/hammer/hammer_volume.c @@ -647,6 +647,21 @@ ondisk->vol_count = root_ondisk->vol_count + 1; /* + * Validate attacker-controlled geometry from the ioctl before letting + * it shape the on-disk volume header. boot_area_size, memory_log_size + * and vol_size are int64 fields supplied by userspace and previously + * only checked via a single signed-<0 test on the resulting + * vol_buf_size; with e.g. boot_area_size = -1 the un-aligned + * vol_buf_beg corrupts subsequent freemap I/O and panics the kernel. + */ + if (ioc->boot_area_size < 0 || ioc->memory_log_size < 0 || + ioc->vol_size <= 0) { + hmkprintf(hmp, "volume %d has non-positive geometry\n", + vol_no); + return(EFTYPE); + } + + /* * Reserve space for (future) header junk. */ vol_alloc = root_ondisk->vol_bot_beg; @@ -667,6 +682,24 @@ return(EFTYPE); } + /* + * The buffer area must fit in HAMMER_OFF_SHORT_MASK (the lower 52 + * bits of a hammer_off_t) and vol_buf_beg must be HAMMER_BUFSIZE + * aligned, otherwise hammer_format_freemap's KKASSERT trips and / + * or its hammer_bread() I/O targets a non-sector-aligned offset + * (both observed as a kernel panic). HAMMER_OFF_SHORT_MASK is + * uint64; cast through uint64_t to avoid signed-comparison noise. + */ + if ((uint64_t)HAMMER_VOL_BUF_SIZE(ondisk) > HAMMER_OFF_SHORT_MASK || + (ondisk->vol_buf_beg & HAMMER_BUFMASK64) != 0) { + hmkprintf(hmp, "volume %d has insane geometry: " + "vol_buf_beg=%jd vol_buf_end=%jd\n", + ondisk->vol_no, + (intmax_t)ondisk->vol_buf_beg, + (intmax_t)ondisk->vol_buf_end); + return(EFTYPE); + } + return(0); }