DragonFlyBSD Kernel Audit
DF-0778 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/ufs/ufs_vnops.c b/sys/vfs/ufs/ufs_vnops.c
--- a/sys/vfs/ufs/ufs_vnops.c
+++ b/sys/vfs/ufs/ufs_vnops.c
@@ -1737,12 +1737,22 @@
 {
 	struct vnode *vp = ap->a_vp;
 	struct inode *ip = VTOI(vp);
-	int isize;
+	int64_t isize;	/* was: int -- ip->i_size is uint64_t */
 
 	isize = ip->i_size;
-	if ((isize < vp->v_mount->mnt_maxsymlinklen) ||
-	    (ip->i_din.di_blocks == 0)) {   /* XXX - for old fastlink support */
-		uiomove((char *)ip->i_shortlink, isize, ap->a_uio);
+	/*
+	 * Only trust the inline i_shortlink buffer when isize fits within it
+	 * (i_shortlink overlays di_db, UFS1_MAXSYMLINKLEN bytes).  A crafted
+	 * or corrupted inode with di_blocks==0 but a huge or negative i_size
+	 * must NOT be passed to uiomove() with the inline buffer: previously
+	 * i_size was truncated to a signed int and then sign-extended to a
+	 * size_t, causing either an out-of-bounds read past the inode
+	 * (kernel heap info leak) or a vm_fault panic (DF-0778).
+	 */
+	if (isize >= 0 && isize <= UFS1_MAXSYMLINKLEN &&
+	    ((isize < vp->v_mount->mnt_maxsymlinklen) ||
+	     (ip->i_din.di_blocks == 0))) {   /* XXX - for old fastlink support */
+		uiomove((char *)ip->i_shortlink, (size_t)isize, ap->a_uio);
 		return (0);
 	}