git: c4e047ab6eb1 - stable/14 - sctp: improve shutting down the read side of a socket
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 15 Sep 2023 17:47:32 UTC
The branch stable/14 has been updated by tuexen: URL: https://cgit.FreeBSD.org/src/commit/?id=c4e047ab6eb15d27f95f46ccf12d55c46d30debd commit c4e047ab6eb15d27f95f46ccf12d55c46d30debd Author: Michael Tuexen <tuexen@FreeBSD.org> AuthorDate: 2023-09-12 23:33:54 +0000 Commit: Michael Tuexen <tuexen@FreeBSD.org> CommitDate: 2023-09-15 17:45:42 +0000 sctp: improve shutting down the read side of a socket When shutdown(..., SHUT_RD) or shutdown(..., SHUT_RDWR) is called, really clean up the read queue and issue an ungraceful shutdown if user messages are affected. Reported by: syzbot+d4e1d30d578891245f59@syzkaller.appspotmail.com --- sys/netinet/sctp_usrreq.c | 90 ++++++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index 52b4faf48574..02bb87578528 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -777,52 +777,76 @@ sctp_disconnect(struct socket *so) int sctp_flush(struct socket *so, int how) { - /* - * We will just clear out the values and let subsequent close clear - * out the data, if any. Note if the user did a shutdown(SHUT_RD) - * they will not be able to read the data, the socket will block - * that from happening. - */ + struct epoch_tracker et; + struct sctp_tcb *stcb; + struct sctp_queued_to_read *control, *ncontrol; struct sctp_inpcb *inp; + struct mbuf *m, *op_err; + bool need_to_abort = false; + /* + * For 1-to-1 style sockets, flush the read queue and trigger an + * ungraceful shutdown of the association, if and only if user + * messages are lost. Loosing notifications does not need to be + * signalled to the peer. + */ + if (how == PRU_FLUSH_WR) { + /* This function is only relevant for the read directions. */ + return (0); + } inp = (struct sctp_inpcb *)so->so_pcb; if (inp == NULL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); return (EINVAL); } - SCTP_INP_RLOCK(inp); - /* For the 1 to many model this does nothing */ + SCTP_INP_WLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) { - SCTP_INP_RUNLOCK(inp); + /* For 1-to-many style sockets this function does nothing. */ + SCTP_INP_WUNLOCK(inp); return (0); } - SCTP_INP_RUNLOCK(inp); - if ((how == PRU_FLUSH_RD) || (how == PRU_FLUSH_RDWR)) { - /* - * First make sure the sb will be happy, we don't use these - * except maybe the count - */ - SCTP_INP_WLOCK(inp); - SCTP_INP_READ_LOCK(inp); - inp->sctp_flags |= SCTP_PCB_FLAGS_SOCKET_CANT_READ; - SCTP_INP_READ_UNLOCK(inp); + stcb = LIST_FIRST(&inp->sctp_asoc_list); + if (stcb == NULL) { SCTP_INP_WUNLOCK(inp); - SOCK_LOCK(so); - KASSERT(!SOLISTENING(so), - ("sctp_flush: called on listening socket %p", so)); - SCTP_SB_CLEAR(so->so_rcv); - SOCK_UNLOCK(so); + return (ENOTCONN); } - if ((how == PRU_FLUSH_WR) || (how == PRU_FLUSH_RDWR)) { - /* - * First make sure the sb will be happy, we don't use these - * except maybe the count - */ - SOCK_LOCK(so); - KASSERT(!SOLISTENING(so), - ("sctp_flush: called on listening socket %p", so)); - SOCK_UNLOCK(so); + SCTP_TCB_LOCK(stcb); + SCTP_INP_READ_LOCK(inp); + inp->sctp_flags |= SCTP_PCB_FLAGS_SOCKET_CANT_READ; + SOCK_LOCK(so); + TAILQ_FOREACH_SAFE(control, &inp->read_queue, next, ncontrol) { + if ((control->spec_flags & M_NOTIFICATION) == 0) { + need_to_abort = true; + } + TAILQ_REMOVE(&inp->read_queue, control, next); + control->on_read_q = 0; + for (m = control->data; m; m = SCTP_BUF_NEXT(m)) { + sctp_sbfree(control, control->stcb, &so->so_rcv, m); + } + if (control->on_strm_q == 0) { + sctp_free_remote_addr(control->whoFrom); + if (control->data) { + sctp_m_freem(control->data); + control->data = NULL; + } + sctp_free_a_readq(stcb, control); + } else { + stcb->asoc.size_on_all_streams += control->length; + } } + SOCK_UNLOCK(so); + SCTP_INP_READ_UNLOCK(inp); + if (need_to_abort) { + inp->last_abort_code = SCTP_FROM_SCTP_USRREQ + SCTP_LOC_6; + SCTP_INP_WUNLOCK(inp); + op_err = sctp_generate_cause(SCTP_CAUSE_OUT_OF_RESC, ""); + NET_EPOCH_ENTER(et); + sctp_abort_an_association(inp, stcb, op_err, false, SCTP_SO_LOCKED); + NET_EPOCH_EXIT(et); + return (ECONNABORTED); + } + SCTP_TCB_UNLOCK(stcb); + SCTP_INP_WUNLOCK(inp); return (0); }