1
0
mirror of https://github.com/openbsd/src.git synced 2026-06-17 23:03:29 +02:00

Fix race during socket unsplicing.

Problem was that splicing holds the socket lock when it writes
so_sp, but unsplicing does not when it reads so_sp.  So it may get
the new pointer, but PR_ZERO is not visible due to reordering.  Then
so->so_sp->ssp_socket is garbage.  Crash happend on octeon/mips64
during regress/sys/netinet/udpthread test run-unsplice.
When creating a splice from socket 1 to socket 2, kernel holds
socket buffer lock on so1->so_rcv and so2->so_snd and socket lock
on both while installing so_sp on so1 and so2.  Concurrent sosplice()
on socket 2 has the opposite order, we hold sblock on so2->so_rcv,
sblock on so1->so_snd and solock on both sockets.
The unsplice thread of the source socket did hold sblock on so->so_rcv
only.  So we did lockless so_sp check while concurrent sosplice()
thread installs so_sp on the same socket as drain, holding sblock
on so->so_snd.
Grabbing sblock on both so->so_srv and so->so_snd fixes the crash.

with and OK mvs@
This commit is contained in:
bluhm
2026-06-11 19:21:51 +00:00
parent 889c2a510d
commit 10fabdd075
+6 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: uipc_socket.c,v 1.388 2026/02/22 21:30:58 bluhm Exp $ */
/* $OpenBSD: uipc_socket.c,v 1.389 2026/06/11 19:21:51 bluhm Exp $ */
/* $NetBSD: uipc_socket.c,v 1.21 1996/02/04 02:17:52 christos Exp $ */
/*
@@ -1323,10 +1323,15 @@ sosplice(struct socket *so, int fd, off_t max, struct timeval *tv)
if (fd < 0) {
if ((error = sblock(&so->so_rcv, SBL_WAIT)) != 0)
return (error);
if ((error = sblock(&so->so_snd, SBL_WAIT)) != 0) {
sbunlock(&so->so_rcv);
return (error);
}
if (so->so_sp && so->so_sp->ssp_socket)
sounsplice(so, so->so_sp->ssp_socket, 0);
else
error = EPROTO;
sbunlock(&so->so_snd);
sbunlock(&so->so_rcv);
return (error);
}