diff options
author | tuexen <tuexen@FreeBSD.org> | 2010-06-18 09:01:44 +0000 |
---|---|---|
committer | tuexen <tuexen@FreeBSD.org> | 2010-06-18 09:01:44 +0000 |
commit | de5b3a7add84950b23cc40c7ae54c2a8f2b5a424 (patch) | |
tree | 29d0116c5fb26031fd68c59441ab38de9b3c4b88 | |
parent | 27ff4601c53f980ad40a3bf372d2273a050908b7 (diff) | |
download | FreeBSD-src-de5b3a7add84950b23cc40c7ae54c2a8f2b5a424.zip FreeBSD-src-de5b3a7add84950b23cc40c7ae54c2a8f2b5a424.tar.gz |
Fix a rece condition in the shutdown handling.
The race condition resulted in a panic.
MFC after: 3 days
-rw-r--r-- | sys/netinet/sctp.h | 1 | ||||
-rw-r--r-- | sys/netinet/sctp_usrreq.c | 19 | ||||
-rw-r--r-- | sys/netinet/sctputil.c | 20 |
3 files changed, 40 insertions, 0 deletions
diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index f4d5d6c..ce5ac0a 100644 --- a/sys/netinet/sctp.h +++ b/sys/netinet/sctp.h @@ -442,6 +442,7 @@ struct sctp_error_unrecognized_chunk { #define SCTP_PCB_FLAGS_BLOCKING_IO 0x08000000 #define SCTP_PCB_FLAGS_SOCKET_GONE 0x10000000 #define SCTP_PCB_FLAGS_SOCKET_ALLGONE 0x20000000 +#define SCTP_PCB_FLAGS_SOCKET_CANT_READ 0x40000000 /* flags to copy to new PCB */ #define SCTP_PCB_COPY_FLAGS (SCTP_PCB_FLAGS_BOUNDALL|\ SCTP_PCB_FLAGS_WAKEINPUT|\ diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index 2492d9e..46cfa3d 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -947,11 +947,30 @@ sctp_flush(struct socket *so, int how) * they will not be able to read the data, the socket will block * that from happening. */ + struct sctp_inpcb *inp; + + 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 */ + if (inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) { + SCTP_INP_RUNLOCK(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); + SCTP_INP_WUNLOCK(inp); so->so_rcv.sb_cc = 0; so->so_rcv.sb_mbcnt = 0; so->so_rcv.sb_mb = NULL; diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index aff0204..9a8f32f 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -3185,6 +3185,9 @@ sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb, uint32_t error, /* event not enabled */ return; } + if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_CANT_READ) { + return; + } m_notify = sctp_get_mbuf_for_msg(sizeof(struct sctp_pdapi_event), 0, M_DONTWAIT, 1, MT_DATA); if (m_notify == NULL) /* no space left */ @@ -4365,6 +4368,17 @@ sctp_add_to_readq(struct sctp_inpcb *inp, } if (inp_read_lock_held == 0) SCTP_INP_READ_LOCK(inp); + if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_CANT_READ) { + sctp_free_remote_addr(control->whoFrom); + if (control->data) { + sctp_m_freem(control->data); + control->data = NULL; + } + SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_readq), control); + if (inp_read_lock_held == 0) + SCTP_INP_READ_UNLOCK(inp); + return; + } if (!(control->spec_flags & M_NOTIFICATION)) { atomic_add_int(&inp->total_recvs, 1); if (!control->do_not_ref_stcb) { @@ -4405,6 +4419,8 @@ sctp_add_to_readq(struct sctp_inpcb *inp, control->tail_mbuf = prev; } else { /* Everything got collapsed out?? */ + sctp_free_remote_addr(control->whoFrom); + SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_readq), control); if (inp_read_lock_held == 0) SCTP_INP_READ_UNLOCK(inp); return; @@ -4477,6 +4493,10 @@ get_out: } return (-1); } + if (inp && (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_CANT_READ)) { + SCTP_INP_READ_UNLOCK(inp); + return 0; + } if (control->end_added) { /* huh this one is complete? */ goto get_out; |