DF-2663 / fix2663.diff
--- a/sys/vfs/hammer2/hammer2_ondisk.c +++ b/sys/vfs/hammer2/hammer2_ondisk.c @@ -281,13 +281,24 @@ } /* check volume size vs block device size */ if (VOP_IOCTL(vol->dev->devvp, DIOCGPART, (void*)&part, 0, - curthread->td_ucred , NULL) == 0) { - if (vol->size > part.media_size) { - hprintf("%s's size 0x%016jx exceeds device size " - "0x%016jx\n", path, (intmax_t)vol->size, - part.media_size); - return EINVAL; - } + curthread->td_ucred , NULL) != 0) { + /* + * The volume's media size could not be determined + * (probe race, d_slice == NULL, raw fallback fail). + * This must not silently disable the containment + * check: a forged volu_size beyond the backing + * device would be accepted and reads past the real + * device end resolve to short reads. + */ + hprintf("%s's media size could not be determined, " + "refusing to mount\n", path); + return EINVAL; + } + if (vol->size > part.media_size) { + hprintf("%s's size 0x%016jx exceeds device size " + "0x%016jx\n", path, (intmax_t)vol->size, + part.media_size); + return EINVAL; } if (vol->size == 0) { hprintf("%s has size of 0\n", path); --- a/sys/vfs/hammer2/hammer2_io.c +++ b/sys/vfs/hammer2/hammer2_io.c @@ -353,6 +353,21 @@ dio->bp->b_flags &= ~B_AGE; /* dio->bp->b_debug_info2 = dio; */ } + + /* + * A short read completes WITHOUT B_ERROR under several media-EOF + * semantics: dscheck() clamps a straddling transfer to the device + * end (reducing b_bcount); a read starting exactly at EOF completes + * with B_INVAL and full b_resid; a vnode-backed device whose + * backing file shrank returns a 0-byte VOP_READ with uio_resid + * untouched. In all those cases part (or all) of the buffer is + * uninitialized kernel memory which must not be consumed as + * filesystem data. + */ + if (error == 0 && dio->bp && + (dio->bp->b_resid != 0 || dio->bp->b_bcount != dio->psize)) { + error = EIO; + } dio->error = error; /* |