diff options
| author | Mark Johnston <markj@FreeBSD.org> | 2025-04-15 00:55:00 +0000 |
|---|---|---|
| committer | Mark Johnston <markj@FreeBSD.org> | 2025-04-29 00:44:04 +0000 |
| commit | c0c5d01e53745f97a1fa132d47c27a8c300ab4f8 (patch) | |
| tree | b9653ebebaa9836eb552d495820f90ae7ed3a056 | |
| parent | dfbda7c53d673999aeb743f77c30c157c067dd8b (diff) | |
so_splice: Synchronize so_unsplice() with so_splice()
so_unsplice() assumed that if SB_SPLICED is set in the receive buffer of
the first socket, then the splice is fully initialized. However, that's
not true, and it's possible for so_unsplice() to race ahead of
so_splice().
Modify so_unsplice() to simply bail if the splice state is embryonic.
Reported by: syzkaller
Reviewed by: gallatin
Fixes: a1da7dc1cdad ("socket: Implement SO_SPLICE")
MFC after: 2 weeks
Differential Revision: https://reviews.freebsd.org/D49814
(cherry picked from commit 992b18a9ec9f21cdf15b70b2f510fa2a69844385)
| -rw-r--r-- | sys/kern/uipc_socket.c | 15 |
1 files changed, 14 insertions, 1 deletions
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index fffb1d5b9af4..6468b3001b3e 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -1721,6 +1721,8 @@ so_splice(struct socket *so, struct socket *so2, struct splice *splice) /* * Transfer any data already present in the socket buffer. */ + KASSERT(sp->state == SPLICE_INIT, + ("so_splice: splice %p state %d", sp, sp->state)); sp->state = SPLICE_QUEUED; so_splice_xfer(sp); return (0); @@ -1749,8 +1751,19 @@ so_unsplice(struct socket *so, bool timeout) SOCK_UNLOCK(so); return (ENOTCONN); } - so->so_rcv.sb_flags &= ~SB_SPLICED; sp = so->so_splice; + mtx_lock(&sp->mtx); + if (sp->state == SPLICE_INIT) { + /* + * A splice is in the middle of being set up. + */ + mtx_unlock(&sp->mtx); + SOCK_RECVBUF_UNLOCK(so); + SOCK_UNLOCK(so); + return (ENOTCONN); + } + mtx_unlock(&sp->mtx); + so->so_rcv.sb_flags &= ~SB_SPLICED; so->so_splice = NULL; SOCK_RECVBUF_UNLOCK(so); SOCK_UNLOCK(so); |
