Hi,

Apparently soreceive_stream() has an issue if it is called to receive data as a
mbuf chain (by supplying an non zero mbuf **mp0) and with MSG_WAITALL set.

I ran into this issue with smbfs, which uses soreceive() exactly in this way
(see netsmb/smb_trantcp.c:nbssn_recv()).

If MSG_WAITALL is set and not all data is received it loops again but on the
next run mb0 is set to sb->sb_mb again loosing all previously received mbufs.
It looks like it should be set to the end of mb0 chain instead. See the
attached path.

Also, in the "copy the remainder" block we reduce uio_resid by m->m_len (the
length of the last mbuf in the chain), but it looks like for the MSG_PEEK case
the remainder may have more than one mbuf in the chain and we have to reduce
by len (the length of the copied chain).

I don't have a test case to check MSG_PEEK issue, but the patch fixes the
issue with smbfs for me.

-- 
Mikolaj Golub

Index: sys/kern/uipc_socket.c
===================================================================
--- sys/kern/uipc_socket.c	(revision 225368)
+++ sys/kern/uipc_socket.c	(working copy)
@@ -2030,7 +2030,11 @@ deliver:
 	if (mp0 != NULL) {
 		/* Dequeue as many mbufs as possible. */
 		if (!(flags & MSG_PEEK) && len >= sb->sb_mb->m_len) {
-			for (*mp0 = m = sb->sb_mb;
+			if (*mp0 == NULL)
+				*mp0 = sb->sb_mb;
+			else
+				n->m_next = sb->sb_mb;
+			for (m = sb->sb_mb;
 			     m != NULL && m->m_len <= len;
 			     m = m->m_next) {
 				len -= m->m_len;
@@ -2052,7 +2056,7 @@ deliver:
 			if (m == NULL)
 				len = 0;	/* Don't flush data from sockbuf. */
 			else
-				uio->uio_resid -= m->m_len;
+				uio->uio_resid -= len;
 			if (*mp0 != NULL)
 				n->m_next = m;
 			else
@@ -2061,6 +2065,9 @@ deliver:
 				error = ENOBUFS;
 				goto out;
 			}
+			/* Update n to point to the last mbuf. */
+			for (; m != NULL; m = m->m_next)
+				n = m;
 		}
 	} else {
 		/* NB: Must unlock socket buffer as uiomove may sleep. */
_______________________________________________
freebsd-net@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "freebsd-net-unsubscr...@freebsd.org"

Reply via email to