DragonFlyBSD Kernel Audit
DF-0784 / fix.diff
← back to finding ↓ download raw
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));
 }