tcp: Disallow re-connection of a connected socket

soconnectat() tries to ensure that one cannot connect a connected
socket.  However, the check is racy and does not really prevent two
threads from attempting to connect the same TCP socket.

Modify tcp_connect() and tcp6_connect() to perform the check again, this
time synchronized by the inpcb lock, under which we call
soisconnecting().

Reported by:	syzkaller
Reviewed by:	glebius
MFC after:	2 weeks
Sponsored by:	Klara, Inc.
Sponsored by:	Modirum MDPay
Differential Revision:	https://reviews.freebsd.org/D38507
This commit is contained in:
Mark Johnston 2023-02-14 09:27:47 -05:00
parent 825fbd087e
commit 636b19ead4
2 changed files with 14 additions and 1 deletions

View File

@ -1345,10 +1345,14 @@ soconnectat(int fd, struct socket *so, struct sockaddr *nam, struct thread *td)
int error;
CURVNET_SET(so->so_vnet);
/*
* If protocol is connection-based, can only connect once.
* Otherwise, if connected, try to disconnect first. This allows
* user to disconnect by connecting to, e.g., a null address.
*
* Note, this check is racy and may need to be re-evaluated at the
* protocol layer.
*/
if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) &&
((so->so_proto->pr_flags & PR_CONNREQUIRED) ||

View File

@ -1401,6 +1401,10 @@ tcp_connect(struct tcpcb *tp, struct sockaddr_in *sin, struct thread *td)
NET_EPOCH_ASSERT();
INP_WLOCK_ASSERT(inp);
if (__predict_false((so->so_state &
(SS_ISCONNECTING | SS_ISCONNECTED)) != 0))
return (EISCONN);
INP_HASH_WLOCK(&V_tcbinfo);
error = in_pcbconnect(inp, sin, td->td_ucred, true);
INP_HASH_WUNLOCK(&V_tcbinfo);
@ -1433,11 +1437,16 @@ static int
tcp6_connect(struct tcpcb *tp, struct sockaddr_in6 *sin6, struct thread *td)
{
struct inpcb *inp = tptoinpcb(tp);
struct socket *so = tptosocket(tp);
int error;
NET_EPOCH_ASSERT();
INP_WLOCK_ASSERT(inp);
if (__predict_false((so->so_state &
(SS_ISCONNECTING | SS_ISCONNECTED)) != 0))
return (EISCONN);
INP_HASH_WLOCK(&V_tcbinfo);
error = in6_pcbconnect(inp, sin6, td->td_ucred, true);
INP_HASH_WUNLOCK(&V_tcbinfo);
@ -1449,7 +1458,7 @@ tcp6_connect(struct tcpcb *tp, struct sockaddr_in6 *sin6, struct thread *td)
(TCP_MAXWIN << tp->request_r_scale) < sb_max)
tp->request_r_scale++;
soisconnecting(inp->inp_socket);
soisconnecting(so);
TCPSTAT_INC(tcps_connattempt);
tcp_state_change(tp, TCPS_SYN_SENT);
tp->iss = tcp_new_isn(&inp->inp_inc);