Page MenuHomeFreeBSD

D61078.1794686237.diff
No OneTemporary

Authored By
Unknown
Size
6 KB
Referenced Files
None
Subscribers
None

D61078.1794686237.diff

diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -1287,19 +1287,31 @@
return (NULL);
}
- (void)solisten_enqueue(so, connstatus);
+ if (solisten_enqueue(so, connstatus, NULL) != 0) {
+ /* The listener has been closed since we cloned it. */
+ soabort(so);
+ return (NULL);
+ }
return (so);
}
/*
- * Enqueue socket cloned by solisten_clone() to the listen queue of the
+ * Enqueue a socket cloned by solisten_clone() to the listen queue of the
* listener it has been cloned from.
*
- * Return 'true' if socket landed on complete queue, otherwise 'false'.
+ * On success, if 'completep' is non-NULL, it is set to 'true' if the socket
+ * landed on the complete queue, otherwise 'false'.
+ *
+ * If the listener has already been closed, its queues have been drained by
+ * soclose() and won't be pulled from again. Therefore, the socket is
+ * not enqueued and ECONNABORTED is returned. The socket is then left
+ * disassociated from the listener holding a single reference, just as
+ * soclose() leaves the sockets it removes from the queues, and the caller
+ * must soabort() it once it has dropped its protocol locks.
*/
-bool
-solisten_enqueue(struct socket *so, int connstatus)
+int
+solisten_enqueue(struct socket *so, int connstatus, bool *completep)
{
struct socket *head = so->so_listen;
@@ -1307,6 +1319,10 @@
refcount_init(&so->so_count, 1);
SOLISTEN_LOCK(head);
+ if (__predict_false((head->so_state & SS_LISTENCLOSED) != 0)) {
+ SOLISTEN_UNLOCK(head);
+ goto closed;
+ }
if (head->sol_accept_filter != NULL)
connstatus = 0;
so->so_state |= connstatus;
@@ -1316,8 +1332,14 @@
so->so_qstate = SQ_COMP;
head->sol_qlen++;
solisten_wakeup(head); /* unlocks */
- return (true);
+ if (completep != NULL)
+ *completep = true;
+ return (0);
} else {
+ /* Was the listener unlocked since we last checked it was not
+ * in a SS_LISTENCLOSED state? */
+ bool was_unlocked;
+
/*
* Keep removing sockets from the head until there's room for
* us to insert on the tail. In pre-locking revisions, this
@@ -1325,6 +1347,7 @@
* threads and soabort() requires dropping locks, we must
* loop waiting for the condition to be true.
*/
+ was_unlocked = false;
while (head->sol_incqlen > head->sol_qlimit) {
struct socket *sp;
@@ -1335,16 +1358,37 @@
sp->so_qstate = SQ_NONE;
sp->so_listen = NULL;
SOCK_UNLOCK(sp);
+ was_unlocked = true;
sorele_locked(head); /* does SOLISTEN_UNLOCK, head stays */
soabort(sp);
SOLISTEN_LOCK(head);
}
+ /*
+ * If we dropped the lock above, the listener may have been
+ * closed meanwhile. Checking once here rather than once per
+ * loop iteration suffices: closing resets sol_incqlen to 0,
+ * which ends the loop, so we end up doing this check before
+ * any other operation anyway.
+ */
+ if (was_unlocked &&
+ __predict_false((head->so_state & SS_LISTENCLOSED) != 0)) {
+ sorele_locked(head); /* does SOLISTEN_UNLOCK */
+ goto closed;
+ }
TAILQ_INSERT_TAIL(&head->sol_incomp, so, so_list);
so->so_qstate = SQ_INCOMP;
head->sol_incqlen++;
SOLISTEN_UNLOCK(head);
- return (false);
+ if (completep != NULL)
+ *completep = false;
+ return (0);
}
+
+closed:
+ SOCK_LOCK(so);
+ so->so_listen = NULL;
+ SOCK_UNLOCK(so);
+ return (ECONNABORTED);
}
#if defined(SCTP) || defined(SCTP_SUPPORT)
@@ -2020,6 +2064,12 @@
if ((listening = SOLISTENING(so))) {
struct socket *sp;
+ /*
+ * A protocol may still hold a socket cloned from this
+ * listener and not yet enqueued. Make solisten_enqueue()
+ * refuse it, since nothing will drain the queues after this.
+ */
+ so->so_state |= SS_LISTENCLOSED;
TAILQ_INIT(&lqueue);
TAILQ_SWAP(&lqueue, &so->sol_incomp, socket, so_list);
TAILQ_CONCAT(&lqueue, &so->sol_comp, so_list);
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -3295,13 +3295,23 @@
sotounpcb(so2) == unp2,
("%s: unp2 %p so2 %p", __func__, unp2, so2));
unp_connect2(so, so2, connreq);
+ error = 0;
if (connreq)
- (void)solisten_enqueue(so2, SS_ISCONNECTED);
+ error = solisten_enqueue(so2, SS_ISCONNECTED, NULL);
KASSERT((unp->unp_flags & UNP_CONNECTING) != 0,
("%s: unp %p has UNP_CONNECTING clear", __func__, unp));
unp->unp_flags &= ~UNP_CONNECTING;
if (!return_locked)
unp_pcb_unlock_pair(unp, unp2);
+
+ /*
+ * If the listener was closed under us, our new socket gets what
+ * soclose() gives the ones it found on the queue.
+ */
+ if (__predict_false(error != 0)) {
+ MPASS(!return_locked);
+ soabort(so2);
+ }
return (0);
}
diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c
--- a/sys/netinet/tcp_syncache.c
+++ b/sys/netinet/tcp_syncache.c
@@ -772,6 +772,7 @@
struct tcpcb *tp;
int error;
char *s;
+ bool complete;
NET_EPOCH_ASSERT();
@@ -1016,7 +1017,14 @@
TCPSTAT_INC(tcps_accepts);
TCP_PROBE6(state__change, NULL, tp, NULL, tp, NULL, TCPS_LISTEN);
- if (!solisten_enqueue(so, SS_ISCONNECTED))
+ /*
+ * The listener cannot have been closed: we hold its inpcb lock, and
+ * tcp_usr_close() takes it out of LISTEN before soclose() drains the
+ * queues.
+ */
+ error = solisten_enqueue(so, SS_ISCONNECTED, &complete);
+ KASSERT(error == 0, ("%s: listener %p closed", __func__, lso));
+ if (!complete)
tp->t_flags |= TF_SONOTCONN;
/* Can we inherit anything from the listener? */
if (tp->t_fb->tfb_inherit != NULL) {
diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h
--- a/sys/sys/socketvar.h
+++ b/sys/sys/socketvar.h
@@ -240,6 +240,7 @@
#define SS_ASYNC 0x0200 /* async i/o notify */
/* was SS_ISCONFIRMING 0x0400 */
#define SS_ISDISCONNECTED 0x2000 /* socket disconnected from peer */
+#define SS_LISTENCLOSED 0x4000 /* listener closed, accepts no more */
#ifdef _KERNEL
@@ -518,7 +519,7 @@
void solisten_proto(struct socket *so, int backlog);
void solisten_proto_abort(struct socket *so);
int solisten_proto_check(struct socket *so);
-bool solisten_enqueue(struct socket *, int);
+int solisten_enqueue(struct socket *, int, bool *);
int solisten_dequeue(struct socket *, struct socket **, int);
struct socket *
solisten_clone(struct socket *);

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 14, 7:57 PM (13 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41717145
Default Alt Text
D61078.1794686237.diff (6 KB)

Event Timeline