]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
mptcp: plug races between subflow fail and subflow creation
authorPaolo Abeni <pabeni@redhat.com>
Mon, 28 Jul 2025 13:29:22 +0000 (15:29 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 28 Aug 2025 14:26:12 +0000 (16:26 +0200)
commit def5b7b2643ebba696fc60ddf675dca13f073486 upstream.

We have races similar to the one addressed by the previous patch between
subflow failing and additional subflow creation. They are just harder to
trigger.

The solution is similar. Use a separate flag to track the condition
'socket state prevent any additional subflow creation' protected by the
fallback lock.

The socket fallback makes such flag true, and also receiving or sending
an MP_FAIL option.

The field 'allow_infinite_fallback' is now always touched under the
relevant lock, we can drop the ONCE annotation on write.

Fixes: 478d770008b0 ("mptcp: send out MP_FAIL when data checksum fails")
Cc: stable@vger.kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Reviewed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Link: https://patch.msgid.link/20250714-net-mptcp-fallback-races-v1-2-391aff963322@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
[ Conflicts in subflow.c, because commit f1f26512a9bf ("mptcp: use plain
  bool instead of custom binary enum") and commit 46a5d3abedbe
  ("mptcp: fix typos in comments") are not in this version. Both are
  causing conflicts in the context, and the same modifications can still
  be applied. Same in protocol.h with commit b8dc6d6ce931 ("mptcp: fix
  rcv buffer auto-tuning"). Conflicts in protocol.c because commit
  ee2708aedad0 ("mptcp: use get_retrans wrapper") is not in this version
  and refactor the code in __mptcp_retrans(), but the modification can
  still be applied, just not at the same indentation level. ]
Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/mptcp/pm.c
net/mptcp/protocol.c
net/mptcp/protocol.h
net/mptcp/subflow.c

index 34120694ad49bc491c29065a003d0b611ad10c1e..6392973b1fa74a73ef616fe7866312344700533e 100644 (file)
@@ -309,8 +309,14 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
 
        pr_debug("fail_seq=%llu\n", fail_seq);
 
-       if (!READ_ONCE(msk->allow_infinite_fallback))
+       /* After accepting the fail, we can't create any other subflows */
+       spin_lock_bh(&msk->fallback_lock);
+       if (!msk->allow_infinite_fallback) {
+               spin_unlock_bh(&msk->fallback_lock);
                return;
+       }
+       msk->allow_subflows = false;
+       spin_unlock_bh(&msk->fallback_lock);
 
        if (!subflow->fail_tout) {
                pr_debug("send MP_FAIL response and infinite map\n");
index 904a348daa51e7f755d50f1d52c57bd3b6db2e62..73e298f276a848eba4545f82f5ccb3163f41bc5f 100644 (file)
@@ -885,7 +885,7 @@ void mptcp_data_ready(struct sock *sk, struct sock *ssk)
 static void mptcp_subflow_joined(struct mptcp_sock *msk, struct sock *ssk)
 {
        mptcp_subflow_ctx(ssk)->map_seq = READ_ONCE(msk->ack_seq);
-       WRITE_ONCE(msk->allow_infinite_fallback, false);
+       msk->allow_infinite_fallback = false;
        mptcp_event(MPTCP_EVENT_SUB_ESTABLISHED, msk, ssk, GFP_ATOMIC);
 }
 
@@ -897,7 +897,7 @@ static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk)
                return false;
 
        spin_lock_bh(&msk->fallback_lock);
-       if (__mptcp_check_fallback(msk)) {
+       if (!msk->allow_subflows) {
                spin_unlock_bh(&msk->fallback_lock);
                return false;
        }
@@ -2707,7 +2707,7 @@ static void __mptcp_retrans(struct sock *sk)
                dfrag->already_sent = max(dfrag->already_sent, info.sent);
                tcp_push(ssk, 0, info.mss_now, tcp_sk(ssk)->nonagle,
                         info.size_goal);
-               WRITE_ONCE(msk->allow_infinite_fallback, false);
+               msk->allow_infinite_fallback = false;
        }
        spin_unlock_bh(&msk->fallback_lock);
 
@@ -2835,7 +2835,8 @@ static int __mptcp_init_sock(struct sock *sk)
        WRITE_ONCE(msk->first, NULL);
        inet_csk(sk)->icsk_sync_mss = mptcp_sync_mss;
        WRITE_ONCE(msk->csum_enabled, mptcp_is_checksum_enabled(sock_net(sk)));
-       WRITE_ONCE(msk->allow_infinite_fallback, true);
+       msk->allow_infinite_fallback = true;
+       msk->allow_subflows = true;
        msk->recovery = false;
 
        mptcp_pm_data_init(msk);
@@ -3673,7 +3674,7 @@ bool mptcp_finish_join(struct sock *ssk)
        /* active subflow, already present inside the conn_list */
        if (!list_empty(&subflow->node)) {
                spin_lock_bh(&msk->fallback_lock);
-               if (__mptcp_check_fallback(msk)) {
+               if (!msk->allow_subflows) {
                        spin_unlock_bh(&msk->fallback_lock);
                        return false;
                }
index a3acc7042ee977552d5c751f4aebe1b45264e107..e1637443203ebb1d3f391023c73402d8a5635236 100644 (file)
@@ -314,12 +314,14 @@ struct mptcp_sock {
                u64     time;   /* start time of measurement window */
                u64     rtt_us; /* last maximum rtt of subflows */
        } rcvq_space;
+       bool            allow_subflows;
 
        u32 setsockopt_seq;
        char            ca_name[TCP_CA_NAME_MAX];
 
-       spinlock_t      fallback_lock;  /* protects fallback and
-                                        * allow_infinite_fallback
+       spinlock_t      fallback_lock;  /* protects fallback,
+                                        * allow_infinite_fallback and
+                                        * allow_join
                                         */
 };
 
@@ -991,6 +993,7 @@ static inline bool __mptcp_try_fallback(struct mptcp_sock *msk)
                return false;
        }
 
+       msk->allow_subflows = false;
        set_bit(MPTCP_FALLBACK_DONE, &msk->flags);
        spin_unlock_bh(&msk->fallback_lock);
        return true;
index d21109a130ecdcf2bfe285f4f550871fceaac179..cff23281069282114a2746dd50af0ddda0d27168 100644 (file)
@@ -1168,20 +1168,29 @@ static void subflow_sched_work_if_closed(struct mptcp_sock *msk, struct sock *ss
                mptcp_schedule_work(sk);
 }
 
-static void mptcp_subflow_fail(struct mptcp_sock *msk, struct sock *ssk)
+static bool mptcp_subflow_fail(struct mptcp_sock *msk, struct sock *ssk)
 {
        struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
        unsigned long fail_tout;
 
+       /* we are really failing, prevent any later subflow join */
+       spin_lock_bh(&msk->fallback_lock);
+       if (!msk->allow_infinite_fallback) {
+               spin_unlock_bh(&msk->fallback_lock);
+               return false;
+       }
+       msk->allow_subflows = false;
+       spin_unlock_bh(&msk->fallback_lock);
+
        /* greceful failure can happen only on the MPC subflow */
        if (WARN_ON_ONCE(ssk != READ_ONCE(msk->first)))
-               return;
+               return false;
 
        /* since the close timeout take precedence on the fail one,
         * no need to start the latter when the first is already set
         */
        if (sock_flag((struct sock *)msk, SOCK_DEAD))
-               return;
+               return true;
 
        /* we don't need extreme accuracy here, use a zero fail_tout as special
         * value meaning no fail timeout at all;
@@ -1193,6 +1202,7 @@ static void mptcp_subflow_fail(struct mptcp_sock *msk, struct sock *ssk)
        tcp_send_ack(ssk);
 
        mptcp_reset_tout_timer(msk, subflow->fail_tout);
+       return true;
 }
 
 static bool subflow_check_data_avail(struct sock *ssk)
@@ -1261,12 +1271,11 @@ fallback:
                    (subflow->mp_join || subflow->valid_csum_seen)) {
                        subflow->send_mp_fail = 1;
 
-                       if (!READ_ONCE(msk->allow_infinite_fallback)) {
+                       if (!mptcp_subflow_fail(msk, ssk)) {
                                subflow->reset_transient = 0;
                                subflow->reset_reason = MPTCP_RST_EMIDDLEBOX;
                                goto reset;
                        }
-                       mptcp_subflow_fail(msk, ssk);
                        WRITE_ONCE(subflow->data_avail, MPTCP_SUBFLOW_DATA_AVAIL);
                        return true;
                }