diff mbox series

mptcp: fix close of larval mptcp sockets

Message ID 20210429120049.8703-1-fw@strlen.de
State Superseded, archived
Headers show
Series mptcp: fix close of larval mptcp sockets | expand

Commit Message

Florian Westphal April 29, 2021, noon UTC
If userspace exits before calling accept() on a listener that had at
least one new connection ready, we get:

  Attempt to release TCP socket in state 8

This happens because the mptcp socket gets cloned when the TCP
connection is ready, but the socket is never exposed to userspace.

Tag the cloned socket until userspace calls accept().

In case a subflow context is destroyed, check the associated mptcp
socket.  If its still in larval state, it has not been exposed to
userspace, so it needs the DEAD flag and its state set to TCP_CLOSE.

Fixes: 58b09919626bf ("mptcp: create msk early")
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/185
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 Please don't apply, this needs to be reviewd by Paolo.
 The extra release hack/check in subflow_ulp_release gives
 me unpleasant headache, i.e. I have no idea what I am doing.

 net/mptcp/protocol.c                          |  1 +
 net/mptcp/protocol.h                          |  1 +
 net/mptcp/subflow.c                           |  5 ++++
 .../net/mptcp/regressions/exit_unaccept.c     | 29 +++++++++++++++++++
 4 files changed, 36 insertions(+)
 create mode 100644 tools/testing/selftests/net/mptcp/regressions/exit_unaccept.c

Comments

Paolo Abeni May 3, 2021, 8:47 a.m. UTC | #1
On Thu, 2021-04-29 at 14:00 +0200, Florian Westphal wrote:
> If userspace exits before calling accept() on a listener that had at
> least one new connection ready, we get:
> 
>   Attempt to release TCP socket in state 8
> 
> This happens because the mptcp socket gets cloned when the TCP
> connection is ready, but the socket is never exposed to userspace.

The above happens if the client additionally sends a DATA_FIN, while
the msk socket is still unaccepted, right? If so I think the fixes tag
should point to the DATA_FIN state machine introduction.

What about something like the following istead ?

Thanks!

Paolo
---
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 15620bafc544..2aa7fc72a99a 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -547,7 +547,7 @@ static void mptcp_sock_destruct(struct sock *sk)
 	 * Both result in warnings from inet_sock_destruct.
 	 */
 
-	if (sk->sk_state == TCP_ESTABLISHED) {
+	if (((1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) {
 		sk->sk_state = TCP_CLOSE;
 		WARN_ON_ONCE(sk->sk_socket);
 		sock_orphan(sk);
diff mbox series

Patch

diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index aec8e77b18e4..69a64401ecef 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -2832,6 +2832,7 @@  static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
 		/* acquire the 2nd reference for the owning socket */
 		sock_hold(new_mptcp_sock);
 		newsk = new_mptcp_sock;
+		mptcp_sk(newsk)->is_larval = 0;
 		MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPCAPABLEPASSIVEACK);
 	} else {
 		MPTCP_INC_STATS(sock_net(sk),
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index d230a75af631..839837b5957b 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -257,6 +257,7 @@  struct mptcp_sock {
 		u64	rtt_us; /* last maximum rtt of subflows */
 	} rcvq_space;
 
+	u8 is_larval:1;		/* incoming mptcp sk cloned but not yet acccepted */
 	u32 setsockopt_seq;
 };
 
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 15620bafc544..00264c6f6479 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -649,6 +649,7 @@  static struct sock *subflow_syn_recv_sock(const struct sock *sk,
 		new_msk = mptcp_sk_clone(listener->conn, &mp_opt, req);
 		if (!new_msk)
 			fallback = true;
+		mptcp_sk(new_msk)->is_larval = 1;
 	} else if (subflow_req->mp_join) {
 		mptcp_get_options(skb, &mp_opt);
 		if (!mp_opt.mp_join || !subflow_hmac_valid(req, &mp_opt) ||
@@ -1570,6 +1571,10 @@  static void subflow_ulp_release(struct sock *ssk)
 		 * when the subflow is still unaccepted
 		 */
 		release = ctx->disposable || list_empty(&ctx->node);
+		if (mptcp_sk(sk)->is_larval) {
+			sk->sk_state = TCP_CLOSE;
+			sock_orphan(sk);
+		}
 		sock_put(sk);
 	}