mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-15 12:54:27 +00:00
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 MFC after: 3 days
This commit is contained in:
parent
c086d1cbc3
commit
81c5f0fac9
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue