⬢ DragonFlyBSD Kernel Audit
DF-3373 / fix.diff
← back to finding ↓ download raw
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -452,6 +452,50 @@
 
 	COMMON_START(so, inp, 0);
 
+	/*
+	 * Already a listener: this is just a backlog update.  Checked
+	 * before the state guard so a healthy listener is never
+	 * rejected -- and never has SO_ACCEPTCONN undone.
+	 */
+	if (tp->t_flags & TF_LISTEN)
+		goto out;
+
+	/*
+	 * Reject listen(2) on a socket that is connecting or connected.
+	 *
+	 * solisten()'s SS_ISCONNECTED/SS_ISCONNECTING test is a plain
+	 * check-then-act: with the default async connect path the flag
+	 * is only stored by tcp_usr_preconnect() which runs after
+	 * soconnect()'s checks, and a connect(2) message still in flight
+	 * (possibly mid-migration between netisrs) leaves the inpcb in
+	 * states this handler must not turn into a listener (otherwise
+	 * the same inpcb ends up on both the wildcard hash and the
+	 * connection hash).  Handler-side checks are race-free because
+	 * pru messages are serialized per socket.
+	 *
+	 * NOTE: A PRUL_RELINK re-entry (our own migration to netisr0)
+	 * passed this guard before the forward and must not be rejected
+	 * for the then-unlinked inp_pcbinfo == NULL.
+	 */
+	if ((msg->listen.nm_flags & PRUL_RELINK) == 0 &&
+	    (tp->t_state != TCPS_CLOSED ||
+	     (so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING)) ||
+	     inp->inp_fport != 0 || inp->inp_pcbinfo == NULL)) {
+		/*
+		 * solisten() sets SO_ACCEPTCONN before dispatching this
+		 * message; undo it, otherwise tcp_input()'s SO_ACCEPTCONN
+		 * branch and soclose() treat this still-connecting socket
+		 * as a listener.  Race-free: segments and further pru
+		 * messages are serialized behind this handler.
+		 */
+		lwkt_gettoken(&so->so_rcv.ssb_token);
+		if (TAILQ_EMPTY(&so->so_comp) && TAILQ_EMPTY(&so->so_incomp))
+			so->so_options &= ~SO_ACCEPTCONN;
+		lwkt_reltoken(&so->so_rcv.ssb_token);
+		error = EINVAL;
+		goto out;
+	}
+
 	if (&curthread->td_msgport != port0) {
 		lwkt_msg_t lmsg = &msg->listen.base.lmsg;
 
@@ -531,6 +575,23 @@
 	if (tp->t_flags & TF_LISTEN)
 		goto out;
 
+	/*
+	 * Same race as tcp_usr_listen(): reject sockets that are
+	 * connecting or connected (handler-side, race-free), and undo
+	 * solisten()'s SO_ACCEPTCONN so the socket stack does not treat
+	 * a connecting socket as a listener.
+	 */
+	if (tp->t_state != TCPS_CLOSED ||
+	    (so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING)) ||
+	    inp->inp_fport != 0 || inp->inp_pcbinfo == NULL) {
+		lwkt_gettoken(&so->so_rcv.ssb_token);
+		if (TAILQ_EMPTY(&so->so_comp) && TAILQ_EMPTY(&so->so_incomp))
+			so->so_options &= ~SO_ACCEPTCONN;
+		lwkt_reltoken(&so->so_rcv.ssb_token);
+		error = EINVAL;
+		goto out;
+	}
+
 	if (inp->inp_lport == 0) {
 		error = in6_pcbbind(inp, NULL, td);
 		if (error)
@@ -1141,6 +1202,30 @@
 	COMMON_START(so, inp, 0);
 
 	/*
+	 * Reject connect(2) on a socket that is (or has become) a
+	 * listener.  soconnect()'s SO_ACCEPTCONN test races
+	 * solisten()'s SO_ACCEPTCONN store, so by the time this
+	 * message is processed (including the PRUC_RECONNECT
+	 * re-entry) the inpcb may already be linked into the wildcard
+	 * hash; continuing would link it into the connection hash
+	 * as well.
+	 */
+	if (tp->t_flags & TF_LISTEN) {
+		if (msg->connect.nm_flags & PRUC_ASYNC) {
+			/*
+			 * Report the error to the connect(2) caller, but
+			 * do NOT soisdisconnected() the listener (the
+			 * generic error path would): sonewconn() children
+			 * inherit the parent's so_state.
+			 */
+			so->so_error = EOPNOTSUPP;
+			msg->connect.nm_flags &= ~PRUC_ASYNC;
+		}
+		error = EOPNOTSUPP;
+		goto out;
+	}
+
+	/*
 	 * Reconnect our pcb if we have to
 	 */
 	if (msg->connect.nm_flags & PRUC_RECONNECT) {
@@ -1323,6 +1408,15 @@
 	COMMON_START(so, inp, 0);
 
 	/*
+	 * Reject connect(2) on a socket that is (or has become) a
+	 * listener (see tcp_connect()).
+	 */
+	if (tp->t_flags & TF_LISTEN) {
+		error = EOPNOTSUPP;
+		goto out;
+	}
+
+	/*
 	 * Reconnect our pcb if we have to
 	 */
 	if (msg->connect.nm_flags & PRUC_RECONNECT) {