DragonFlyBSD Kernel Audit
DF-0799 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/nfs/nfs_bio.c b/sys/vfs/nfs/nfs_bio.c
--- a/sys/vfs/nfs/nfs_bio.c
+++ b/sys/vfs/nfs/nfs_bio.c
@@ -256,8 +256,24 @@
 		n = biosize - boff;
 		if (n > uio->uio_resid)
 			n = uio->uio_resid;
-		if (loffset + boff + n > np->n_size)
-			n = np->n_size - loffset - boff;
+		/*
+		 * Re-read np->n_size into a local.  A concurrent
+		 * ftruncate()/stat() may have called nfs_meta_setsize()
+		 * to reduce np->n_size while nfs_doio() above slept in
+		 * the READ RPC.  Guard the unsigned subtraction against
+		 * underflow when n_size < loffset + boff, which would
+		 * cause uiomovebp() to copy far past the biosize buffer
+		 * into adjacent kernel memory (TOCTOU / OOB heap read).
+		 */
+		{
+			u_quad_t cur_size = np->n_size;
+			if (loffset + boff + n > cur_size) {
+				if (cur_size > (u_quad_t)(loffset + boff))
+					n = (size_t)(cur_size - loffset - boff);
+				else
+					n = 0;
+			}
+		}
 		break;
 	    case VLNK:
 		biosize = min(NFS_MAXPATHLEN, np->n_size);