Merge branch 'mptcp-misc-fixes-for-v7-3-rc1'

Matthieu Baerts says:

====================
mptcp: misc fixes for v7.3-rc1

Here are various unrelated fixes:

- Patch 1: Do not reschedule the RTX timer for sockets that fell back to
  TCP. A fix for v5.7.

- Patch 2: Avoid copying thmac which will not be used and could be
  uninitialised. A fix for v5.7.

- Patch 3: Re-set the request backup flag when SYN cookies are used. A
  fix for v5.9.

- Patch 4: Drop pending ADD_ADDR when removing ID0, and avoid a WARN. A
  fix for v5.13.

- Patch 5: Handle invalid suboptions where the checksum is requested in
  the MP_CAPABLE 4th ACK with data, but not added in the option. A fix
  for v5.14.

- Patch 6: Prevent a race between mptcp_disconnect() and the retransmit
  timer. A fix for v5.17.

- Patch 7: Fix a use-after-free in the selftests that could lead to
  false positive. A fix for v5.17.

- Patch 8: Limit new addresses with the userspace PM to avoid an address
  ID overflow. A fix for v5.19.

- Patch 9: Reset the ADD_ADDR retransmission counter when the timer is
  reused. A fix for v5.19.

- Patch 10: Remove unneeded and confusing READ_ONCE() annotations. A fix
  for v6.13.

- Patches 11-12: Get nstat counters for the current test, not since the
  creation of the netns. A fix for v6.19.

- Patch 13: Fix an uninit-value in mptcp_write_data_fin for a corner
  case now that only a part of the tcp_out_options struct is reset. A
  fix for v7.1.

- Patches 14-15: Two follow-up patches addressing minor comments
  discovered after the human review. A fix for v7.3-rc1.
====================

Link: https://patch.msgid.link/20260908-net-mptcp-misc-fixes-7-3-rc1-v2-0-df1de70348b6@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-09-09 13:32:58 -07:00
commit 9a1599eeb8
10 changed files with 78 additions and 36 deletions

View File

@ -93,7 +93,8 @@ static void mptcp_parse_option(const struct sk_buff *skb,
* In other words, the only way for checksums not to be used
* is if both hosts in their SYNs set A=0."
*/
if (flags & MPTCP_CAP_CHECKSUM_REQD)
if ((flags & MPTCP_CAP_CHECKSUM_REQD) &&
opsize < TCPOLEN_MPTCP_MPC_ACK_DATA)
mp_opt->suboptions |= OPTION_MPTCP_CSUMREQD;
mp_opt->deny_join_id0 = !!(flags & MPTCP_CAP_DENY_JOIN_ID0);
@ -529,7 +530,7 @@ static bool mptcp_established_options_mp(struct sock *sk, struct sk_buff *skb,
return false;
/* MPC/MPJ needed only on 3rd ack packet, DATA_FIN and TCP shutdown take precedence */
if (READ_ONCE(subflow->fully_established) || snd_data_fin_enable ||
if (subflow->fully_established || snd_data_fin_enable ||
subflow->snd_isn != TCP_SKB_CB(skb)->seq ||
sk->sk_state != TCP_ESTABLISHED)
return false;
@ -611,6 +612,7 @@ static void mptcp_write_data_fin(struct mptcp_subflow_context *subflow,
ext->data_seq = data_fin_tx_seq;
ext->subflow_seq = 0;
ext->data_len = 1;
ext->csum = 0;
} else if (ext->data_seq + ext->data_len == data_fin_tx_seq) {
/* If there's an existing DSS mapping and it is the
* final mapping, DATA_FIN consumes 1 additional byte of
@ -980,7 +982,7 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk,
/* here we can process OoO, in-window pkts, only in-sequence 4th ack
* will make the subflow fully established
*/
if (likely(READ_ONCE(subflow->fully_established))) {
if (likely(subflow->fully_established)) {
/* on passive sockets, check for 3rd ack retransmission
* note that msk is always set by subflow_syn_recv_sock()
* for mp_join subflows

View File

@ -462,10 +462,10 @@ bool mptcp_pm_announced_alloc(struct mptcp_sock *msk,
add_entry->addr = *addr;
add_entry->sock = msk;
add_entry->retrans_times = 0;
timer_setup(&add_entry->timer, mptcp_pm_add_addr_timer, 0);
reset_timer:
add_entry->retrans_times = 0;
add_entry->timer_done = false;
timeout = mptcp_adjust_add_addr_timeout(msk);
if (timeout)

View File

@ -1137,6 +1137,8 @@ static int mptcp_nl_remove_id_zero_address(struct net *net,
while ((msk = mptcp_token_iter_next(net, &s_slot, &s_num)) != NULL) {
struct sock *sk = (struct sock *)msk;
struct mptcp_addr_info msk_local;
struct mptcp_addr_info anno_addr;
bool announced;
if (list_empty(&msk->conn_list) || mptcp_pm_is_userspace(msk))
goto next;
@ -1146,7 +1148,13 @@ static int mptcp_nl_remove_id_zero_address(struct net *net,
goto next;
lock_sock(sk);
/* Drop a possibly pending ADD_ADDR for this address. */
anno_addr = msk_local;
anno_addr.port = 0;
announced = mptcp_pm_announced_remove(msk, &anno_addr);
spin_lock_bh(&msk->pm.lock);
if (announced)
msk->pm.add_addr_signaled--;
mptcp_pm_remove_addr(msk, &list);
mptcp_pm_rm_subflow(msk, &list);
__mark_subflow_endp_available(msk, 0);

View File

@ -69,6 +69,19 @@ static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk,
}
if (!addr_match && !id_match) {
unsigned int id;
if (!entry->addr.id && needs_id) {
id = find_next_zero_bit(id_bitmap,
MPTCP_PM_MAX_ADDR_ID + 1, 1);
if (id > MPTCP_PM_MAX_ADDR_ID) {
ret = -ENOSPC;
goto append_err;
}
} else {
id = entry->addr.id;
}
/* Memory for the entry is allocated from the
* sock option buffer.
*/
@ -78,10 +91,7 @@ static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk,
goto append_err;
}
if (!e->addr.id && needs_id)
e->addr.id = find_next_zero_bit(id_bitmap,
MPTCP_PM_MAX_ADDR_ID + 1,
1);
e->addr.id = id;
list_add_tail_rcu(&e->list, &msk->pm.userspace_pm_local_addr_list);
msk->pm.local_addr_used++;
ret = e->addr.id;

View File

@ -95,6 +95,7 @@ bool __mptcp_try_fallback(struct mptcp_sock *msk, int fb_mib)
msk->allow_subflows = false;
set_bit(MPTCP_FALLBACK_DONE, &msk->flags);
clear_bit(MPTCP_RTX_ENABLED, &msk->flags);
__MPTCP_INC_STATS(net, fb_mib);
spin_unlock_bh(&msk->fallback_lock);
return true;
@ -288,8 +289,8 @@ static void mptcp_prune_ofo_queue(struct sock *sk,
*/
static bool mptcp_can_ingest(const struct sock *sk)
{
return unlikely(sk_rmem_alloc_get(sk) <= READ_ONCE(sk->sk_rcvbuf)) ||
__mptcp_check_fallback(mptcp_sk(sk));
return likely(sk_rmem_alloc_get(sk) <= READ_ONCE(sk->sk_rcvbuf)) ||
__mptcp_check_fallback(mptcp_sk(sk));
}
static bool mptcp_try_rmem_schedule(struct sock *sk, const struct sk_buff *skb)
@ -312,12 +313,6 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
u64 seq, end_seq, max_seq;
struct sk_buff *skb1;
if (!mptcp_try_rmem_schedule(sk, skb)) {
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED);
mptcp_drop(sk, skb);
return;
}
seq = MPTCP_SKB_CB(skb)->map_seq;
end_seq = MPTCP_SKB_CB(skb)->end_seq;
max_seq = atomic64_read(&msk->rcv_wnd_sent);
@ -334,6 +329,12 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
return;
}
if (!mptcp_try_rmem_schedule(sk, skb)) {
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED);
mptcp_drop(sk, skb);
return;
}
p = &msk->out_of_order_queue.rb_node;
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOQUEUE);
if (RB_EMPTY_ROOT(&msk->out_of_order_queue)) {
@ -1084,13 +1085,14 @@ static bool mptcp_rtx_timer_pending(struct sock *sk)
static void mptcp_reset_rtx_timer(struct sock *sk)
{
struct mptcp_sock *msk = mptcp_sk(sk);
unsigned long tout;
/* prevent rescheduling on close */
if (unlikely(inet_sk_state_load(sk) == TCP_CLOSE))
/* Prevent rescheduling on close and in case of fallback. */
if (!test_bit(MPTCP_RTX_ENABLED, &msk->flags))
return;
tout = mptcp_sk(sk)->timer_ival;
tout = msk->timer_ival;
sk_reset_timer(sk, &sk->mptcp_retransmit_timer, jiffies + tout);
}
@ -3323,6 +3325,9 @@ void mptcp_set_state(struct sock *sk, int state)
* transition from TCP_SYN_RECV to TCP_CLOSE_WAIT.
*/
break;
case TCP_CLOSE:
clear_bit(MPTCP_RTX_ENABLED, &mptcp_sk(sk)->flags);
fallthrough;
default:
if (oldstate == TCP_ESTABLISHED || oldstate == TCP_CLOSE_WAIT)
MPTCP_DEC_STATS(sock_net(sk), MPTCP_MIB_CURRESTAB);
@ -3583,6 +3588,7 @@ static void mptcp_destroy_common(struct mptcp_sock *msk)
static int mptcp_disconnect(struct sock *sk, int flags)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct mptcp_sock *msk = mptcp_sk(sk);
/* We are on the fastopen error path. We can't call straight into the
@ -3595,8 +3601,13 @@ static int mptcp_disconnect(struct sock *sk, int flags)
mptcp_check_listen_stop(sk);
mptcp_set_state(sk, TCP_CLOSE);
mptcp_stop_rtx_timer(sk);
mptcp_stop_tout_timer(sk);
/* The later subflow close can not kick again the tout timer,
* as the msk is already in closed status.
*/
msk->timer_ival = icsk->icsk_rto_min;
sk_stop_timer_sync(sk, &sk->mptcp_retransmit_timer);
icsk->icsk_mtup.probe_timestamp = 0;
sk_stop_timer_sync(sk, &icsk->mptcp_tout_timer);
mptcp_pm_connection_closed(msk);
@ -3875,7 +3886,7 @@ static void schedule_3rdack_retransmission(struct sock *ssk)
struct tcp_sock *tp = tcp_sk(ssk);
unsigned long timeout;
if (READ_ONCE(mptcp_subflow_ctx(ssk)->fully_established))
if (mptcp_subflow_ctx(ssk)->fully_established)
return;
/* reschedule with a timeout above RTT, as we must look only for drop */
@ -4141,6 +4152,7 @@ static int mptcp_connect(struct sock *sk, struct sockaddr_unsized *uaddr,
if (IS_ERR(ssk))
return PTR_ERR(ssk);
set_bit(MPTCP_RTX_ENABLED, &msk->flags);
mptcp_set_state(sk, TCP_SYN_SENT);
subflow = mptcp_subflow_ctx(ssk);
#ifdef CONFIG_TCP_MD5SIG
@ -4288,6 +4300,7 @@ static int mptcp_listen(struct socket *sock, int backlog)
goto unlock;
}
set_bit(MPTCP_RTX_ENABLED, &msk->flags);
mptcp_set_state(sk, TCP_LISTEN);
sock_set_flag(sk, SOCK_RCU_FREE);

View File

@ -116,6 +116,7 @@
#define MPTCP_WORK_RTX 1
#define MPTCP_FALLBACK_DONE 2
#define MPTCP_WORK_CLOSE_SUBFLOW 3
#define MPTCP_RTX_ENABLED 4
/* MPTCP socket release cb flags */
#define MPTCP_PUSH_PENDING 1

View File

@ -2084,7 +2084,6 @@ static void subflow_ulp_clone(const struct request_sock *req,
new_ctx->request_bkup = subflow_req->request_bkup;
WRITE_ONCE(new_ctx->remote_id, subflow_req->remote_id);
new_ctx->token = subflow_req->token;
new_ctx->thmac = subflow_req->thmac;
/* the subflow req id is valid, fetched via subflow_check_req()
* and subflow_token_join_request()

View File

@ -26,7 +26,8 @@ struct join_entry {
u32 local_nonce;
u8 join_id;
u8 local_id;
u8 backup;
u8 backup:1,
request_bkup:1;
u8 valid;
};
@ -63,6 +64,7 @@ static void mptcp_join_store_state(struct join_entry *entry,
entry->remote_nonce = subflow_req->remote_nonce;
entry->local_nonce = subflow_req->local_nonce;
entry->backup = subflow_req->backup;
entry->request_bkup = subflow_req->request_bkup;
entry->join_id = subflow_req->remote_id;
entry->local_id = subflow_req->local_id;
entry->valid = 1;
@ -117,6 +119,7 @@ bool mptcp_token_join_cookie_init_state(struct mptcp_subflow_request_sock *subfl
subflow_req->remote_nonce = e->remote_nonce;
subflow_req->local_nonce = e->local_nonce;
subflow_req->backup = e->backup;
subflow_req->request_bkup = e->request_bkup;
subflow_req->remote_id = e->join_id;
subflow_req->local_id = e->local_id;
subflow_req->token = e->token;

View File

@ -381,6 +381,9 @@ static int sock_connect_mptcp(const char * const remoteaddr,
hints.ai_family = pf;
/* Keep the resolved address alive for the whole execution: it is
* used again when reconnecting, and will be released at exit time.
*/
xgetaddrinfo(remoteaddr, port, &hints, &addr);
for (a = addr; a; a = a->ai_next) {
sock = socket(a->ai_family, a->ai_socktype, proto);
@ -421,7 +424,6 @@ static int sock_connect_mptcp(const char * const remoteaddr,
sock = -1;
}
freeaddrinfo(addr);
if (sock != -1)
SOCK_TEST_TCPULP(sock, proto);
return sock;

View File

@ -108,12 +108,14 @@ mptcp_lib_pr_info() {
mptcp_lib_pr_nstat() {
local ns="${1}"
local hist="/tmp/${ns}.out"
local cache="/tmp/${ns}.out"
local hist="/tmp/${ns}.nstat"
if [ -f "${hist}" ]; then
awk '$2 != 0 { print " "$0 }' "${hist}"
if [ -f "${cache}" ]; then
awk '$2 != 0 { print " "$0 }' "${cache}"
else
ip netns exec "${ns}" nstat -as | grep Tcp
NSTAT_HISTORY="${hist}" ip netns exec "${ns}" nstat -s |
grep Tcp
fi
}
@ -414,19 +416,21 @@ mptcp_lib_nstat_get() {
}
# $1: ns, $2: MIB counter
# Get the counter from the history (mptcp_lib_nstat_{init,get}()) if available.
# If not, get the counter from nstat ignoring any history.
# Get the counter from the cache (mptcp_lib_nstat_{init,get}()) if available.
# If not, get the counter from nstat ignoring any cache, but using the history.
mptcp_lib_get_counter() {
local ns="${1}"
local counter="${2}"
local hist="/tmp/${ns}.out"
local cache="/tmp/${ns}.out"
local hist="/tmp/${ns}.nstat"
local count
if [[ -s "${hist}" && "${counter}" == *"Tcp"* ]]; then
count=$(awk "/^${counter} / {print \$2; exit}" "${hist}")
if [[ -s "${cache}" && "${counter}" == *"Tcp"* ]]; then
count=$(awk "/^${counter} / {print \$2; exit}" "${cache}")
else
count=$(ip netns exec "${ns}" nstat -asz "${counter}" |
awk 'NR==1 {next} {print $2}')
count=$(NSTAT_HISTORY="${hist}" ip netns exec "${ns}" \
nstat -sz "${counter}" |
awk 'NR==1 {next} {print $2}')
fi
if [ -z "${count}" ]; then
mptcp_lib_fail_if_expected_feature "${counter} counter"