summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortuexen <tuexen@FreeBSD.org>2010-06-18 09:01:44 +0000
committertuexen <tuexen@FreeBSD.org>2010-06-18 09:01:44 +0000
commitde5b3a7add84950b23cc40c7ae54c2a8f2b5a424 (patch)
tree29d0116c5fb26031fd68c59441ab38de9b3c4b88
parent27ff4601c53f980ad40a3bf372d2273a050908b7 (diff)
downloadFreeBSD-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.h1
-rw-r--r--sys/netinet/sctp_usrreq.c19
-rw-r--r--sys/netinet/sctputil.c20
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;
OpenPOWER on IntegriCloud