git: 872c36cb6f2d - main - cuse: Fix hang on readv(2) and writev(2) with multiple iovecs

From: Christos Margiolis <christos_at_FreeBSD.org>
Date: Wed, 30 Sep 2026 09:51:22 UTC
The branch main has been updated by christos:

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

commit 872c36cb6f2de17278c559a300c2163d8b39b3c6
Author:     Christos Margiolis <christos@FreeBSD.org>
AuthorDate: 2026-09-30 09:49:35 +0000
Commit:     Christos Margiolis <christos@FreeBSD.org>
CommitDate: 2026-09-30 09:49:35 +0000

    cuse: Fix hang on readv(2) and writev(2) with multiple iovecs
    
    uiomove() leaves an iovec it has just emptied as the current one, so
    cuse_client_read() and cuse_client_write() picked it up again on the
    next iteration, sent the server a zero-length command, and got zero
    bytes back. That left the residual count unchanged, so the loop never
    terminated and the call never returned.
    
    Step past empty iovecs at the start of every iteration. This also covers
    caller-supplied zero-length iovecs, which hung in the same way
    
    PR:             293489
    MFC after:      1 week
    Sponsored by:   The FreeBSD Foundation
    Reviewed by:    kib, markj
    Differential Revision:  https://reviews.freebsd.org/D59822
---
 sys/fs/cuse/cuse.c | 28 ++++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/sys/fs/cuse/cuse.c b/sys/fs/cuse/cuse.c
index ef786d125c15..c6107b977dac 100644
--- a/sys/fs/cuse/cuse.c
+++ b/sys/fs/cuse/cuse.c
@@ -1666,11 +1666,21 @@ cuse_client_read(struct cdev *dev, struct uio *uio, int ioflag)
 	cuse_cmd_lock(pccmd);
 
 	while (uio->uio_resid != 0) {
-		if (uio->uio_iov->iov_len > CUSE_LENGTH_MAX) {
+		len = uio->uio_iov->iov_len;
+		/*
+		 * The uiomove() below does not step past an iovec it has
+		 * just emptied, so do it here, to avoid an infinite loop
+		 * where we are requesting zero-byte transfers.
+		 */
+		if (len == 0) {
+			uio->uio_iov++;
+			uio->uio_iovcnt--;
+			continue;
+		}
+		if (len > CUSE_LENGTH_MAX) {
 			error = ENOMEM;
 			break;
 		}
-		len = uio->uio_iov->iov_len;
 
 		cuse_server_lock(pcs);
 		if (len <= CUSE_COPY_BUFFER_MAX) {
@@ -1754,11 +1764,21 @@ cuse_client_write(struct cdev *dev, struct uio *uio, int ioflag)
 	cuse_cmd_lock(pccmd);
 
 	while (uio->uio_resid != 0) {
-		if (uio->uio_iov->iov_len > CUSE_LENGTH_MAX) {
+		len = uio->uio_iov->iov_len;
+		/*
+		 * The uiomove() below does not step past an iovec it has
+		 * just emptied, so do it here, to avoid an infinite loop
+		 * where we are requesting zero-byte transfers.
+		 */
+		if (len == 0) {
+			uio->uio_iov++;
+			uio->uio_iovcnt--;
+			continue;
+		}
+		if (len > CUSE_LENGTH_MAX) {
 			error = ENOMEM;
 			break;
 		}
-		len = uio->uio_iov->iov_len;
 
 		if (len <= CUSE_COPY_BUFFER_MAX) {
 			error = copyin(uio->uio_iov->iov_base,