DF-0784 / fix.diff
diff --git a/sys/vfs/ext2fs/ext2_vnops.c b/sys/vfs/ext2fs/ext2_vnops.c index 0000000..1111111 100644 --- a/sys/vfs/ext2fs/ext2_vnops.c +++ b/sys/vfs/ext2fs/ext2_vnops.c @@ -1347,13 +1347,30 @@ { struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); - int isize; + uint64_t isize; isize = ip->i_size; - if (isize < vp->v_mount->mnt_maxsymlinklen) { - uiomove((char *)ip->i_shortlink, isize, ap->a_uio); + /* + * Fast-symlink path: target stored inline in i_db (i_shortlink). + * Valid only when isize fits in the inline area AND no data blocks + * have been allocated. isize must be a 64-bit compare to avoid an + * integer-truncation primitive: previously `int isize` silently + * narrowed a 64-bit i_size, so a corrupted i_size with bit 31 set + * truncated to a negative int, passed the < maxsymlinklen test, and + * was sign-extended back to a huge size_t in uiomove(), driving an + * unbounded kernel-heap read (DF-0784). + * + * A corrupted inode with isize > maxsymlinklen but i_blocks == 0 has + * neither inline data nor backing blocks; return EINVAL rather than + * fall through to VOP_READ on a vnode without a VM object (which + * would panic in getblk). + */ + if (isize <= vp->v_mount->mnt_maxsymlinklen && ip->i_blocks == 0) { + uiomove((char *)ip->i_shortlink, (size_t)isize, ap->a_uio); return (0); } + if (ip->i_blocks == 0) + return (EINVAL); return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); } |