Index | Thread | Search

From:
Alexander Bluhm <bluhm@openbsd.org>
Subject:
socket splicing loop counter
To:
tech@openbsd.org
Date:
Fri, 20 Feb 2026 18:47:31 +0100

Download raw body.

Thread
  • Alexander Bluhm:

    socket splicing loop counter

Hi,

To avoid endless splicing loops, the mbuf's lifetime is limited by
the ph_loopcnt counter.  Each time somove() compares the value to
a maximum and increments.

After that, in the unlikely case that the drain buffer is full, the
mbuf stays in the source buffer.  Then it is incremented again when
more data arrives and somove() is called.  Eventually the maxiumm
could be reached and splicing dissolved with ELOOP.  This was not
intended for mbufs which never moved, we want to count the moves.

Avoid the race and increment just before sending the mbuf out to
the new buffer.

ok?

bluhm

Index: kern/uipc_socket.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/kern/uipc_socket.c,v
diff -u -p -r1.387 uipc_socket.c
--- kern/uipc_socket.c	21 Jan 2026 10:18:20 -0000	1.387
+++ kern/uipc_socket.c	20 Feb 2026 15:40:27 -0000
@@ -1592,12 +1592,9 @@ somove(struct socket *so, int wait)
 	/*
 	 * By splicing sockets connected to localhost, userland might create a
 	 * loop.  Dissolve splicing with error if loop is detected by counter.
-	 *
-	 * If we deal with looped broadcast/multicast packet we bail out with
-	 * no error to suppress splice termination.
 	 */
 	if ((m->m_flags & M_PKTHDR) &&
-	    ((m->m_pkthdr.ph_loopcnt++ >= M_MAXLOOP) ||
+	    ((m->m_pkthdr.ph_loopcnt >= M_MAXLOOP) ||
 	    ((m->m_flags & M_LOOP) && (m->m_flags & (M_BCAST|M_MCAST))))) {
 		error = ELOOP;
 		goto release;
@@ -1737,6 +1734,8 @@ somove(struct socket *so, int wait)
 		} else if (oobmark) {
 			o = m_split(m, oobmark, wait);
 			if (o) {
+				if (m->m_flags & M_PKTHDR)
+					m->m_pkthdr.ph_loopcnt++;
 				solock_shared(sosp);
 				error = pru_send(sosp, m, NULL, NULL);
 				sounlock_shared(sosp);
@@ -1793,6 +1792,8 @@ somove(struct socket *so, int wait)
 
 	mtx_leave(&sosp->so_snd.sb_mtx);
 	mtx_leave(&so->so_rcv.sb_mtx);
+	if (m->m_flags & M_PKTHDR)
+		m->m_pkthdr.ph_loopcnt++;
 	solock_shared(sosp);
 	error = pru_send(sosp, m, NULL, NULL);
 	sounlock_shared(sosp);