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:
parent
825fbd087e
commit
636b19ead4
@ -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) ||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user