Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177548959
D61078.1794686237.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
6 KB
Referenced Files
None
Subscribers
None
D61078.1794686237.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D61078: socket: Refuse to enqueue onto a closed listener
Attached
Detach File
Event Timeline