From: Gang Yan <yang...@kylinos.cn>

When testing valkey benchmark tool with MPTCP, the kernel panics in
'mptcp_can_accept_new_subflow' because subflow_req->msk is NULL.

Call trace:

  mptcp_can_accept_new_subflow (./net/mptcp/subflow.c:63 (discriminator 4)) (P)
  subflow_syn_recv_sock (./net/mptcp/subflow.c:854)
  tcp_check_req (./net/ipv4/tcp_minisocks.c:863)
  tcp_v4_rcv (./net/ipv4/tcp_ipv4.c:2268)
  ip_protocol_deliver_rcu (./net/ipv4/ip_input.c:207)
  ip_local_deliver_finish (./net/ipv4/ip_input.c:234)
  ip_local_deliver (./net/ipv4/ip_input.c:254)
  ip_rcv_finish (./net/ipv4/ip_input.c:449)
  ...

According to the debug log, the same req received two SYN-ACK in a very
short time, very likely because the client retransmits the syn ack due
to multiple reasons.

Even if the packets are transmitted with a relevant time interval, they
can be processed by the server on different CPUs concurrently). The
'subflow_req->msk' ownership is transferred to the subflow the first,
and there will be a risk of a null pointer dereference here.

This patch fixes this issue by moving the 'subflow_req->msk' under the
`own_req == true` conditional.

Note that the !msk check in subflow_hmac_valid() can be dropped, because
the same check already exists under the own_req mpj branch where the
code has been moved to.

Fixes: 9466a1ccebbe ("mptcp: enable JOIN requests even if cookies are in use")
Cc: sta...@vger.kernel.org
Suggested-by: Paolo Abeni <pab...@redhat.com>
Signed-off-by: Gang Yan <yang...@kylinos.cn>
Reviewed-by: Matthieu Baerts (NGI0) <matt...@kernel.org>
Signed-off-by: Matthieu Baerts (NGI0) <matt...@kernel.org>
---
 net/mptcp/subflow.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 
efe8d86496dbd06a3c4cae6ffc6462e43e42c959..409bd415ef1d190d5599658d01323ad8c8a9be93
 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -754,8 +754,6 @@ static bool subflow_hmac_valid(const struct request_sock 
*req,
 
        subflow_req = mptcp_subflow_rsk(req);
        msk = subflow_req->msk;
-       if (!msk)
-               return false;
 
        subflow_generate_hmac(READ_ONCE(msk->remote_key),
                              READ_ONCE(msk->local_key),
@@ -850,12 +848,8 @@ static struct sock *subflow_syn_recv_sock(const struct 
sock *sk,
 
        } else if (subflow_req->mp_join) {
                mptcp_get_options(skb, &mp_opt);
-               if (!(mp_opt.suboptions & OPTION_MPTCP_MPJ_ACK) ||
-                   !subflow_hmac_valid(req, &mp_opt) ||
-                   !mptcp_can_accept_new_subflow(subflow_req->msk)) {
-                       SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINACKMAC);
+               if (!(mp_opt.suboptions & OPTION_MPTCP_MPJ_ACK))
                        fallback = true;
-               }
        }
 
 create_child:
@@ -905,6 +899,13 @@ static struct sock *subflow_syn_recv_sock(const struct 
sock *sk,
                                goto dispose_child;
                        }
 
+                       if (!subflow_hmac_valid(req, &mp_opt) ||
+                           !mptcp_can_accept_new_subflow(subflow_req->msk)) {
+                               SUBFLOW_REQ_INC_STATS(req, 
MPTCP_MIB_JOINACKMAC);
+                               subflow_add_reset_reason(skb, 
MPTCP_RST_EPROHIBIT);
+                               goto dispose_child;
+                       }
+
                        /* move the msk reference ownership to the subflow */
                        subflow_req->msk = NULL;
                        ctx->conn = (struct sock *)owner;

-- 
2.48.1


Reply via email to