DF-0888 / fix.diff
diff --git a/sys/vfs/ext2fs/ext2_inode.c b/sys/vfs/ext2fs/ext2_inode.c index 0000000..1111111 100644 --- a/sys/vfs/ext2fs/ext2_inode.c +++ b/sys/vfs/ext2fs/ext2_inode.c @@ -138,6 +138,14 @@ last = lastbn; if (lastbn > 0) last /= factor; + /* + * Defense-in-depth: the caller (ext2_ind_truncate) must reject lengths + * beyond the indirect-block scheme's capacity, but never let `last' + * index past bap[NINDR-1] -- otherwise &bap[last+1] and the bzero size + * (NINDR-(last+1))*sizeof would go out of bounds / underflow. + */ + if (last >= NINDIR(fs)) + last = NINDIR(fs) - 1; nblocks = btodb(fs->e2fs_bsize); /* * Get buffer of block pointers, zero those entries corresponding @@ -466,6 +474,25 @@ return (EINVAL); ip = VTOI(vp); + /* + * Reject lengths beyond the maximum size addressable by the inode's + * block-mapping scheme. ext2's e2fs_maxfilesize is set to INT64_MAX + * under HUGE_FILE (sys/vfs/ext2fs/ext2_vfsops.c:703) and does NOT + * reflect the actual capacity of the classic indirect-block scheme + * (12 direct + NINDR + NINDR^2 + NINDR^3). Truncating to a size the + * scheme cannot represent would overflow lastiblock[TRIPLE] in + * ext2_ind_truncate and the index/size math in ext2_indirtrunc, + * causing a heap OOB write in its bzero(). Bound length here. + */ + if (vp->v_type == VREG && (ip->i_flag & IN_E4EXTENTS) == 0) { + struct m_ext2fs *fs = ip->i_e2fs; + off_t nindir = NINDIR(fs); + off_t maxaddr = EXT2_NDADDR + + nindir + nindir * nindir + nindir * nindir * nindir; + off_t maxsize = maxaddr * fs->e2fs_bsize; + if (length > maxsize) + return (EFBIG); + } if (vp->v_type == VLNK && ip->i_size < vp->v_mount->mnt_maxsymlinklen) { #ifdef INVARIANTS |