Skip to content

Commit

Permalink
mptcp: avoid race on msk state changes
Browse files Browse the repository at this point in the history
The msk socket state is currently updated in a few spots without
owning the msk socket lock itself.

Some of such operations are safe, as they happens before exposing
the msk socket to user-space and can't race with other changes.

A couple of them, at connect time, can actually race with close()
or shutdown(), leaving breaking the socket state machine.

This change addresses the issue moving such update under the msk
socket lock with the usual:

<acquire spinlock>
<check sk lock onwers>
<ev defer to release_cb>

scheme.

Closes: #56
Fixes: 8fd7380 ("mptcp: fallback in case of simultaneous connect")
Fixes: c3c123d ("net: mptcp: don't hang in mptcp_sendmsg() after TCP fallback")
Reviewed-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
  • Loading branch information
Paolo Abeni authored and jenkins-tessares committed Jun 25, 2021
1 parent 403d4a6 commit 1cec5c6
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 8 deletions.
5 changes: 5 additions & 0 deletions net/mptcp/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -2896,6 +2896,11 @@ static void mptcp_release_cb(struct sock *sk)
spin_lock_bh(&sk->sk_lock.slock);
}

/* be sure to set the current sk state before tacking actions
* depending on sk_state
*/
if (test_and_clear_bit(MPTCP_CONNECTED, &mptcp_sk(sk)->flags))
__mptcp_set_connected(sk);
if (test_and_clear_bit(MPTCP_CLEAN_UNA, &mptcp_sk(sk)->flags))
__mptcp_clean_una_wakeup(sk);
if (test_and_clear_bit(MPTCP_ERROR_REPORT, &mptcp_sk(sk)->flags))
Expand Down
2 changes: 2 additions & 0 deletions net/mptcp/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
#define MPTCP_ERROR_REPORT 8
#define MPTCP_RETRANSMIT 9
#define MPTCP_WORK_SYNC_SETSOCKOPT 10
#define MPTCP_CONNECTED 11

static inline bool before64(__u64 seq1, __u64 seq2)
{
Expand Down Expand Up @@ -600,6 +601,7 @@ void mptcp_get_options(const struct sock *sk,
struct mptcp_options_received *mp_opt);

void mptcp_finish_connect(struct sock *sk);
void __mptcp_set_connected(struct sock *sk);
static inline bool mptcp_is_fully_established(struct sock *sk)
{
return inet_sk_state_load(sk) == TCP_ESTABLISHED &&
Expand Down
30 changes: 22 additions & 8 deletions net/mptcp/subflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,24 @@ static bool subflow_use_different_dport(struct mptcp_sock *msk, const struct soc
return inet_sk(sk)->inet_dport != inet_sk((struct sock *)msk)->inet_dport;
}

void __mptcp_set_connected(struct sock *sk)
{
if (sk->sk_state == TCP_SYN_SENT) {
inet_sk_state_store(sk, TCP_ESTABLISHED);
sk->sk_state_change(sk);
}
}

static void mptcp_set_connected(struct sock *sk)
{
mptcp_data_lock(sk);
if (!sock_owned_by_user(sk))
__mptcp_set_connected(sk);
else
set_bit(MPTCP_CONNECTED, &mptcp_sk(sk)->flags);
mptcp_data_unlock(sk);
}

static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
Expand All @@ -381,10 +399,6 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)

subflow->icsk_af_ops->sk_rx_dst_set(sk, skb);

if (inet_sk_state_load(parent) == TCP_SYN_SENT) {
inet_sk_state_store(parent, TCP_ESTABLISHED);
parent->sk_state_change(parent);
}

/* be sure no special action on any packet other than syn-ack */
if (subflow->conn_finished)
Expand Down Expand Up @@ -417,6 +431,7 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
subflow->remote_key);
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPCAPABLEACTIVEACK);
mptcp_finish_connect(sk);
mptcp_set_connected(parent);
} else if (subflow->request_join) {
u8 hmac[SHA256_DIGEST_SIZE];

Expand Down Expand Up @@ -457,6 +472,7 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
} else if (mptcp_check_fallback(sk)) {
fallback:
mptcp_rcv_space_init(mptcp_sk(parent), sk);
mptcp_set_connected(parent);
}
return;

Expand Down Expand Up @@ -564,6 +580,7 @@ static void mptcp_sock_destruct(struct sock *sk)

static void mptcp_force_close(struct sock *sk)
{
/* the msk is not yet exposed to user-space */
inet_sk_state_store(sk, TCP_CLOSE);
sk_common_release(sk);
}
Expand Down Expand Up @@ -1577,10 +1594,7 @@ static void subflow_state_change(struct sock *sk)
mptcp_rcv_space_init(mptcp_sk(parent), sk);
pr_fallback(mptcp_sk(parent));
subflow->conn_finished = 1;
if (inet_sk_state_load(parent) == TCP_SYN_SENT) {
inet_sk_state_store(parent, TCP_ESTABLISHED);
parent->sk_state_change(parent);
}
mptcp_set_connected(parent);
}

/* as recvmsg() does not acquire the subflow socket for ssk selection
Expand Down

0 comments on commit 1cec5c6

Please sign in to comment.