diff --git a/sys/vfs/ext2fs/ext2_inode.c b/sys/vfs/ext2fs/ext2_inode.c --- a/sys/vfs/ext2fs/ext2_inode.c +++ b/sys/vfs/ext2fs/ext2_inode.c return (EINVAL); ip = VTOI(vp); + /* + * Only regular files, directories and symlinks own filesystem + * blocks described by i_db[]/i_ib[]. For special files those + * slots overlay the device number (i_rdev) or are meaningless; + * running the block-freeing engine over them frees arbitrary + * in-range blocks (panic or silent cross-file aliasing). + * ext2_inactive() also calls us for deleted inodes of any type, + * so gate here, not only in ext2_setattr(). + */ + if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) + return (EINVAL); + if (vp->v_type == VLNK && ip->i_size < vp->v_mount->mnt_maxsymlinklen) { diff --git a/sys/vfs/ext2fs/ext2_vnops.c b/sys/vfs/ext2fs/ext2_vnops.c --- a/sys/vfs/ext2fs/ext2_vnops.c +++ b/sys/vfs/ext2fs/ext2_vnops.c @@ -336,7 +336,16 @@ return (EROFS); break; default: - break; + /* + * Special files (VCHR/VBLK/VFIFO/VSOCK) have no + * truncatable storage; their i_db[] slots overlay + * the device number. Per POSIX the result of + * ftruncate() on other-than-regular files is + * unspecified -- silently ignore, as FreeBSD's + * ufs_setattr() does, instead of running the + * truncate engine over them. + */ + return (0); } if ((error = ext2_truncate(vp, vap->va_size, 0, cred)) != 0) return (error);