| From 68cc924729ffcfe90d0383177192030a9aeb2ee4 Mon Sep 17 00:00:00 2001 |
| From: Paolo Abeni <pabeni@redhat.com> |
| Date: Wed, 31 Jul 2024 12:10:15 +0200 |
| Subject: mptcp: fix duplicate data handling |
| |
| From: Paolo Abeni <pabeni@redhat.com> |
| |
| commit 68cc924729ffcfe90d0383177192030a9aeb2ee4 upstream. |
| |
| When a subflow receives and discards duplicate data, the mptcp |
| stack assumes that the consumed offset inside the current skb is |
| zero. |
| |
| With multiple subflows receiving data simultaneously such assertion |
| does not held true. As a result the subflow-level copied_seq will |
| be incorrectly increased and later on the same subflow will observe |
| a bad mapping, leading to subflow reset. |
| |
| Address the issue taking into account the skb consumed offset in |
| mptcp_subflow_discard_data(). |
| |
| Fixes: 04e4cd4f7ca4 ("mptcp: cleanup mptcp_subflow_discard_data()") |
| Cc: stable@vger.kernel.org |
| Link: https://github.com/multipath-tcp/mptcp_net-next/issues/501 |
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> |
| Reviewed-by: Mat Martineau <martineau@kernel.org> |
| Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org> |
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/mptcp/subflow.c | 16 ++++++++++++---- |
| 1 file changed, 12 insertions(+), 4 deletions(-) |
| |
| --- a/net/mptcp/subflow.c |
| +++ b/net/mptcp/subflow.c |
| @@ -863,14 +863,22 @@ static void mptcp_subflow_discard_data(s |
| { |
| struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk); |
| bool fin = TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN; |
| - u32 incr; |
| + struct tcp_sock *tp = tcp_sk(ssk); |
| + u32 offset, incr, avail_len; |
| |
| - incr = limit >= skb->len ? skb->len + fin : limit; |
| + offset = tp->copied_seq - TCP_SKB_CB(skb)->seq; |
| + if (WARN_ON_ONCE(offset > skb->len)) |
| + goto out; |
| |
| - pr_debug("discarding=%d len=%d seq=%d", incr, skb->len, |
| - subflow->map_subflow_seq); |
| + avail_len = skb->len - offset; |
| + incr = limit >= avail_len ? avail_len + fin : limit; |
| + |
| + pr_debug("discarding=%d len=%d offset=%d seq=%d", incr, skb->len, |
| + offset, subflow->map_subflow_seq); |
| MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_DUPDATA); |
| tcp_sk(ssk)->copied_seq += incr; |
| + |
| +out: |
| if (!before(tcp_sk(ssk)->copied_seq, TCP_SKB_CB(skb)->end_seq)) |
| sk_eat_skb(ssk, skb); |
| if (mptcp_subflow_get_map_offset(subflow) >= subflow->map_data_len) |