From: Alexander Bluhm Subject: socket splicing loop counter To: tech@openbsd.org Date: Fri, 20 Feb 2026 18:47:31 +0100 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);