git: b487aca48535 - main - vfs: Avoid integer overflow when faulting

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Sun, 01 Nov 2026 20:25:37 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=b487aca48535e5f04e4913447fcbe33f1bf56c68

commit b487aca48535e5f04e4913447fcbe33f1bf56c68
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2026-11-01 20:09:50 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2026-11-01 20:09:50 +0000

    vfs: Avoid integer overflow when faulting
    
    In vn_io_fault1(), the read / write length can easily exceed INT_MAX.
    Passing it directly to uiomove(), which expects an int, results in a
    negative uio_resid, which to the caller looks like we read more than
    was requested.
    
    PR:             298159
    MFC after:      1 week
    Reviewed by:    olce, kib
    Differential Revision:  https://reviews.freebsd.org/D59412
---
 sys/kern/vfs_vnops.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c
index 96b1c5696251..0bb8bfffec85 100644
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -1531,7 +1531,7 @@ vn_io_fault1(struct vnode *vp, struct uio *uio, struct vn_io_fault_args *args,
 	vm_page_t *prev_td_ma;
 	vm_prot_t prot;
 	vm_offset_t addr, end;
-	size_t len, resid;
+	size_t len;
 	ssize_t adv;
 	int error, cnt, saveheld, prev_td_ma_cnt;
 
@@ -1553,7 +1553,6 @@ vn_io_fault1(struct vnode *vp, struct uio *uio, struct vn_io_fault_args *args,
 	 * point using UIO_NOCOPY below.
 	 */
 	uio_clone = cloneuio(uio);
-	resid = uio->uio_resid;
 
 	short_uio.uio_segflg = UIO_USERSPACE;
 	short_uio.uio_rw = uio->uio_rw;
@@ -1563,9 +1562,15 @@ vn_io_fault1(struct vnode *vp, struct uio *uio, struct vn_io_fault_args *args,
 	if (error != EFAULT)
 		goto out;
 
+	/*
+	 * Advance clone to the redo point.
+	 */
 	atomic_add_long(&vn_io_faults_cnt, 1);
 	uio_clone->uio_segflg = UIO_NOCOPY;
-	uiomove(NULL, resid - uio->uio_resid, uio_clone);
+	while (uio_clone->uio_resid > uio->uio_resid) {
+		len = MIN(uio_clone->uio_resid - uio->uio_resid, INT_MAX);
+		uiomove(NULL, len, uio_clone);
+	}
 	uio_clone->uio_segflg = uio->uio_segflg;
 
 	saveheld = curthread_pflags_set(TDP_UIOHELD);
@@ -1629,6 +1634,7 @@ vn_io_fault1(struct vnode *vp, struct uio *uio, struct vn_io_fault_args *args,
 	curthread_pflags_restore(saveheld);
 out:
 	freeuio(uio_clone);
+	MPASS(uio->uio_resid >= 0);
 	return (error);
 }