| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/07 16:21 | flow | patch-triage |
12mArgs:null Results: {
"EnableConfigs": [
"MPTCP",
"MPTCP_IPV6"
],
"FocusSymbols": [
"mptcp_incoming_options",
"mptcp_pm_chk_stale",
"mptcp_subflow_get_retrans",
"mptcp_data_ready",
"mptcp_recvmsg",
"mptcp_sendmsg"
],
"KMSANReasoning": "The patch series focuses on MPTCP memory accounting, packet dropping under memory pressure, and retransmission logic. It introduces new MIB counters, adds memory limit checks (`mptcp_over_limit`, `mptcp_try_rmem_schedule`), implements out-of-order queue pruning (`mptcp_prune_ofo_queue`), and refactors the retransmission loop (`__mptcp_retrans`). None of these changes involve allocating new complex structures, exposing kernel memory to user space, or relying on potentially uninitialized memory for control flow. Any potential bugs introduced by these changes, such as use-after-free or out-of-bounds accesses during packet dropping or queue manipulation, would be effectively caught by standard KASAN and other default bug detectors. There is no risk of uninitialized memory usage or info-leaks that would necessitate a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies the MPTCP protocol implementation to handle memory limits, backlog drops, and retransmission logic. It adds new MIB counters and modifies core MPTCP functions like `mptcp_incoming_options`, `mptcp_pm_chk_stale`, and `__mptcp_retrans`. These are reachable from standard MPTCP socket operations and network receive paths, making them highly relevant for fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/07 16:21 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit b606a7bc46510b8a98488ab5264b73fb863fa89a\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Aug 7 16:21:12 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/net/mptcp/mib.c b/net/mptcp/mib.c\nindex f23fda0c55a72..2569385bab7ca 100644\n--- a/net/mptcp/mib.c\n+++ b/net/mptcp/mib.c\n@@ -85,6 +85,9 @@ static const struct snmp_mib mptcp_snmp_list[] = {\n \tSNMP_MIB_ITEM(\"SimultConnectFallback\", MPTCP_MIB_SIMULTCONNFALLBACK),\n \tSNMP_MIB_ITEM(\"FallbackFailed\", MPTCP_MIB_FALLBACKFAILED),\n \tSNMP_MIB_ITEM(\"WinProbe\", MPTCP_MIB_WINPROBE),\n+\tSNMP_MIB_ITEM(\"BacklogDrop\", MPTCP_MIB_BACKLOGDROP),\n+\tSNMP_MIB_ITEM(\"RcvPruned\", MPTCP_MIB_RCVPRUNED),\n+\tSNMP_MIB_ITEM(\"OFOPruned\", MPTCP_MIB_OFOPRUNED),\n };\n \n /* mptcp_mib_alloc - allocate percpu mib counters\ndiff --git a/net/mptcp/mib.h b/net/mptcp/mib.h\nindex 812218b5ed2bf..3a3425e258a76 100644\n--- a/net/mptcp/mib.h\n+++ b/net/mptcp/mib.h\n@@ -88,6 +88,9 @@ enum linux_mptcp_mib_field {\n \tMPTCP_MIB_SIMULTCONNFALLBACK,\t/* Simultaneous connect */\n \tMPTCP_MIB_FALLBACKFAILED,\t/* Can't fallback due to msk status */\n \tMPTCP_MIB_WINPROBE,\t\t/* MPTCP-level zero window probe */\n+\tMPTCP_MIB_BACKLOGDROP,\t\t/* Backlog over memory limit */\n+\tMPTCP_MIB_RCVPRUNED,\t\t/* Dropped due to memory constraints */\n+\tMPTCP_MIB_OFOPRUNED,\t\t/* MPTCP-level OoO queue pruned */\n \t__MPTCP_MIB_MAX\n };\n \ndiff --git a/net/mptcp/options.c b/net/mptcp/options.c\nindex 1057d500577b0..b8318e0301389 100644\n--- a/net/mptcp/options.c\n+++ b/net/mptcp/options.c\n@@ -1190,8 +1190,34 @@ static bool add_addr_hmac_valid(struct mptcp_sock *msk,\n \treturn hmac == mp_opt-\u003eahmac;\n }\n \n-/* Return false in case of error (or subflow has been reset),\n- * else return true.\n+static bool mptcp_over_limit(struct sock *sk, struct sock *ssk,\n+\t\t\t const struct sk_buff *skb)\n+{\n+\tstruct mptcp_sock *msk = mptcp_sk(sk);\n+\tu32 rcvbuf = READ_ONCE(sk-\u003esk_rcvbuf);\n+\n+\tif (likely((u32)sk_rmem_alloc_get(sk) \u003c= rcvbuf \u0026\u0026\n+\t\t READ_ONCE(msk-\u003ebacklog_len) \u003c= rcvbuf))\n+\t\treturn false;\n+\n+\t/* Avoid silently dropping pure acks, fin, rst or already-acked segm. */\n+\tif (TCP_SKB_CB(skb)-\u003eseq == TCP_SKB_CB(skb)-\u003eend_seq ||\n+\t TCP_SKB_CB(skb)-\u003etcp_flags \u0026 (TCPHDR_FIN | TCPHDR_RST) ||\n+\t !after(TCP_SKB_CB(skb)-\u003eend_seq, tcp_sk(ssk)-\u003ercv_nxt))\n+\t\treturn false;\n+\n+\t/* Dropped due to memory constraints, schedule an ack. */\n+\tinet_csk(ssk)-\u003eicsk_ack.pending |= ICSK_ACK_NOMEM | ICSK_ACK_NOW;\n+\tinet_csk_schedule_ack(ssk);\n+\n+\t/* Plain TCP (fallback) and skb is dropped before the TCP recv queue. */\n+\tNET_INC_STATS(sock_net(sk), LINUX_MIB_TCPRCVQDROP);\n+\n+\treturn true;\n+}\n+\n+/* Return false when the caller must drop the packet, i.e. in case of error,\n+ * subflow has been reset, or over memory limits.\n */\n bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)\n {\n@@ -1217,7 +1243,7 @@ bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)\n \n \t\t__mptcp_data_acked(subflow-\u003econn);\n \t\tmptcp_data_unlock(subflow-\u003econn);\n-\t\treturn true;\n+\t\treturn !mptcp_over_limit(subflow-\u003econn, sk, skb);\n \t}\n \n \tmptcp_get_options(skb, \u0026mp_opt);\ndiff --git a/net/mptcp/pm.c b/net/mptcp/pm.c\nindex 64a1236aabee9..d1f73c3e39fa3 100644\n--- a/net/mptcp/pm.c\n+++ b/net/mptcp/pm.c\n@@ -1065,7 +1065,8 @@ bool mptcp_pm_is_backup(struct mptcp_sock *msk, struct sock_common *skc)\n \treturn mptcp_pm_nl_is_backup(msk, \u0026skc_local);\n }\n \n-static void mptcp_pm_subflows_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)\n+static void\n+mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)\n {\n \tstruct mptcp_subflow_context *iter, *subflow = mptcp_subflow_ctx(ssk);\n \tstruct sock *sk = (struct sock *)msk;\n@@ -1102,22 +1103,34 @@ static void mptcp_pm_subflows_chk_stale(const struct mptcp_sock *msk, struct soc\n \t}\n }\n \n-void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)\n+void mptcp_pm_chk_stale(const struct mptcp_sock *msk)\n {\n-\tstruct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);\n-\tu32 rcv_tstamp = READ_ONCE(tcp_sk(ssk)-\u003ercv_tstamp);\n-\n-\t/* keep track of rtx periods with no progress */\n-\tif (!subflow-\u003estale_count) {\n-\t\tsubflow-\u003estale_rcv_tstamp = rcv_tstamp;\n-\t\tsubflow-\u003estale_count++;\n-\t} else if (subflow-\u003estale_rcv_tstamp == rcv_tstamp) {\n-\t\tif (subflow-\u003estale_count \u003c U8_MAX)\n+\tstruct mptcp_subflow_context *subflow;\n+\n+\tmptcp_for_each_subflow(msk, subflow) {\n+\t\tstruct sock *ssk = mptcp_subflow_tcp_sock(subflow);\n+\t\tu32 rcv_tstamp;\n+\n+\t\tif (!__mptcp_subflow_active(subflow))\n+\t\t\tcontinue;\n+\n+\t\t/* No data outstanding at TCP level? not stale */\n+\t\tif (tcp_rtx_and_write_queues_empty(ssk))\n+\t\t\tcontinue;\n+\n+\t\t/* keep track of rtx periods with no progress */\n+\t\trcv_tstamp = READ_ONCE(tcp_sk(ssk)-\u003ercv_tstamp);\n+\t\tif (!subflow-\u003estale_count) {\n+\t\t\tsubflow-\u003estale_rcv_tstamp = rcv_tstamp;\n \t\t\tsubflow-\u003estale_count++;\n-\t\tmptcp_pm_subflows_chk_stale(msk, ssk);\n-\t} else {\n-\t\tsubflow-\u003estale_count = 0;\n-\t\tmptcp_subflow_set_active(subflow);\n+\t\t} else if (subflow-\u003estale_rcv_tstamp == rcv_tstamp) {\n+\t\t\tif (subflow-\u003estale_count \u003c U8_MAX)\n+\t\t\t\tsubflow-\u003estale_count++;\n+\t\t\tmptcp_pm_subflow_chk_stale(msk, ssk);\n+\t\t} else {\n+\t\t\tsubflow-\u003estale_count = 0;\n+\t\t\tmptcp_subflow_set_active(subflow);\n+\t\t}\n \t}\n }\n \ndiff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c\nindex 7c8180d8d5eff..ec874d2ead6a4 100644\n--- a/net/mptcp/protocol.c\n+++ b/net/mptcp/protocol.c\n@@ -242,6 +242,65 @@ static bool mptcp_rcvbuf_grow(struct sock *sk, u32 newval)\n \treturn false;\n }\n \n+/* \"Inspired\" from the TCP version; main difference: stop as soon as the MPTCP\n+ * socket is under memory limit.\n+ */\n+static void mptcp_prune_ofo_queue(struct sock *sk,\n+\t\t\t\t const struct sk_buff *in_skb)\n+{\n+\tstruct mptcp_sock *msk = mptcp_sk(sk);\n+\tstruct rb_node *node, *prev;\n+\tbool pruned = false;\n+\tu64 mem;\n+\n+\tif (RB_EMPTY_ROOT(\u0026msk-\u003eout_of_order_queue))\n+\t\treturn;\n+\n+\tnode = \u0026msk-\u003eooo_last_skb-\u003erbnode;\n+\n+\tdo {\n+\t\tstruct sk_buff *skb = rb_to_skb(node);\n+\n+\t\t/* Stop pruning if the incoming skb would land in OoO tail. */\n+\t\tif (after64(MPTCP_SKB_CB(in_skb)-\u003emap_seq,\n+\t\t\t MPTCP_SKB_CB(skb)-\u003emap_seq))\n+\t\t\tbreak;\n+\n+\t\tpruned = true;\n+\t\tprev = rb_prev(node);\n+\t\trb_erase(node, \u0026msk-\u003eout_of_order_queue);\n+\t\tmptcp_drop(sk, skb);\n+\t\tmsk-\u003eooo_last_skb = rb_to_skb(prev);\n+\n+\t\tmem = (unsigned int)sk_rmem_alloc_get(sk);\n+\t\tif (mem \u003c= sk-\u003esk_rcvbuf)\n+\t\t\tbreak;\n+\n+\t\tnode = prev;\n+\t} while (node);\n+\n+\tif (pruned)\n+\t\tMPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOPRUNED);\n+}\n+\n+/* The stack can't drop packets for fallback socket at the msk level, or the\n+ * stream will break.\n+ */\n+static bool mptcp_can_ingest(const struct sock *sk)\n+{\n+\treturn unlikely(sk_rmem_alloc_get(sk) \u003c= READ_ONCE(sk-\u003esk_rcvbuf)) ||\n+\t\t\t__mptcp_check_fallback(mptcp_sk(sk));\n+}\n+\n+static bool mptcp_try_rmem_schedule(struct sock *sk, const struct sk_buff *skb)\n+{\n+\tif (!mptcp_can_ingest(sk)) {\n+\t\tmptcp_prune_ofo_queue(sk, skb);\n+\t\treturn mptcp_can_ingest(sk);\n+\t}\n+\treturn true;\n+}\n+\n /* \"inspired\" by tcp_data_queue_ofo(), main differences:\n * - use mptcp seqs\n * - don't cope with sacks\n@@ -253,6 +312,12 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)\n \tu64 seq, end_seq, max_seq;\n \tstruct sk_buff *skb1;\n \n+\tif (!mptcp_try_rmem_schedule(sk, skb)) {\n+\t\tMPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED);\n+\t\tmptcp_drop(sk, skb);\n+\t\treturn;\n+\t}\n+\n \tseq = MPTCP_SKB_CB(skb)-\u003emap_seq;\n \tend_seq = MPTCP_SKB_CB(skb)-\u003eend_seq;\n \tmax_seq = atomic64_read(\u0026msk-\u003ercv_wnd_sent);\n@@ -389,6 +454,13 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)\n \n \tif (MPTCP_SKB_CB(skb)-\u003emap_seq == msk-\u003eack_seq) {\n \t\t/* in sequence */\n+insert:\n+\t\tif (!mptcp_try_rmem_schedule(sk, skb)) {\n+\t\t\tMPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED);\n+\t\t\tmptcp_drop(sk, skb);\n+\t\t\treturn false;\n+\t\t}\n+\n \t\tmsk-\u003ebytes_received += copy_len;\n \t\tWRITE_ONCE(msk-\u003eack_seq, msk-\u003eack_seq + copy_len);\n \t\ttail = skb_peek_tail(\u0026sk-\u003esk_receive_queue);\n@@ -403,26 +475,20 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)\n \t\treturn false;\n \t}\n \n-\t/* Completely old data? */\n-\tif (!after64(MPTCP_SKB_CB(skb)-\u003eend_seq, msk-\u003eack_seq)) {\n-\t\tMPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);\n-\t\tmptcp_drop(sk, skb);\n-\t\treturn false;\n+\t/* Partial packet */\n+\tif (after64(MPTCP_SKB_CB(skb)-\u003eend_seq, msk-\u003eack_seq)) {\n+\t\tcopy_len = MPTCP_SKB_CB(skb)-\u003eend_seq - msk-\u003eack_seq;\n+\t\tMPTCP_SKB_CB(skb)-\u003eoffset += msk-\u003eack_seq -\n+\t\t\t\t\t MPTCP_SKB_CB(skb)-\u003emap_seq;\n+\t\tMPTCP_SKB_CB(skb)-\u003emap_seq += msk-\u003eack_seq -\n+\t\t\t\t\t MPTCP_SKB_CB(skb)-\u003emap_seq;\n+\t\tgoto insert;\n \t}\n \n-\t/* Partial packet: map_seq \u003c ack_seq \u003c end_seq.\n-\t * Skip the already-acked bytes and enqueue the new data.\n-\t */\n-\tcopy_len = MPTCP_SKB_CB(skb)-\u003eend_seq - msk-\u003eack_seq;\n-\tMPTCP_SKB_CB(skb)-\u003eoffset += msk-\u003eack_seq - MPTCP_SKB_CB(skb)-\u003emap_seq;\n-\tMPTCP_SKB_CB(skb)-\u003emap_seq += msk-\u003eack_seq -\n-\t\t\t\t MPTCP_SKB_CB(skb)-\u003emap_seq;\n-\tmsk-\u003ebytes_received += copy_len;\n-\tWRITE_ONCE(msk-\u003eack_seq, msk-\u003eack_seq + copy_len);\n-\n-\tskb_set_owner_r(skb, sk);\n-\t__skb_queue_tail(\u0026sk-\u003esk_receive_queue, skb);\n-\treturn true;\n+\t/* Completely old data */\n+\tMPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);\n+\tmptcp_drop(sk, skb);\n+\treturn false;\n }\n \n static void mptcp_stop_rtx_timer(struct sock *sk)\n@@ -681,6 +747,7 @@ static void __mptcp_add_backlog(struct sock *sk,\n \tstruct sk_buff *tail = NULL;\n \tstruct sock *ssk = skb-\u003esk;\n \tbool fragstolen;\n+\tu64 limit;\n \tint delta;\n \n \tif (unlikely(sk-\u003esk_state == TCP_CLOSE)) {\n@@ -688,6 +755,16 @@ static void __mptcp_add_backlog(struct sock *sk,\n \t\treturn;\n \t}\n \n+\t/* Similar additional allowance as plain TCP. */\n+\tlimit = READ_ONCE(sk-\u003esk_rcvbuf);\n+\tlimit += (limit \u003e\u003e 1) + 64 * 1024;\n+\tlimit = min_t(u64, limit, UINT_MAX);\n+\tif (msk-\u003ebacklog_len \u003e limit \u0026\u0026 !__mptcp_check_fallback(msk)) {\n+\t\t__MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_BACKLOGDROP);\n+\t\tkfree_skb_reason(skb, SKB_DROP_REASON_SOCKET_BACKLOG);\n+\t\treturn;\n+\t}\n+\n \t/* Try to coalesce with the last skb in our backlog */\n \tif (!list_empty(\u0026msk-\u003ebacklog_list))\n \t\ttail = list_last_entry(\u0026msk-\u003ebacklog_list, struct sk_buff, list);\n@@ -759,7 +836,7 @@ static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk,\n \n \t\t\tmptcp_init_skb(ssk, skb, offset, len);\n \n-\t\t\tif (own_msk \u0026\u0026 sk_rmem_alloc_get(sk) \u003c sk-\u003esk_rcvbuf) {\n+\t\t\tif (own_msk) {\n \t\t\t\tmptcp_subflow_lend_fwdmem(subflow, skb);\n \t\t\t\tret |= __mptcp_move_skb(sk, skb);\n \t\t\t} else {\n@@ -1142,13 +1219,6 @@ static void __mptcp_clean_una_wakeup(struct sock *sk)\n \tmptcp_write_space(sk);\n }\n \n-static void mptcp_clean_una_wakeup(struct sock *sk)\n-{\n-\tmptcp_data_lock(sk);\n-\t__mptcp_clean_una_wakeup(sk);\n-\tmptcp_data_unlock(sk);\n-}\n-\n static void mptcp_enter_memory_pressure(struct sock *sk)\n {\n \tstruct mptcp_subflow_context *subflow;\n@@ -2215,12 +2285,7 @@ static bool __mptcp_move_skbs(struct sock *sk, struct list_head *skbs, u32 *delt\n \tstruct mptcp_sock *msk = mptcp_sk(sk);\n \tbool moved = false;\n \n-\t*delta = 0;\n \twhile (1) {\n-\t\t/* If the msk recvbuf is full stop, don't drop */\n-\t\tif (sk_rmem_alloc_get(sk) \u003e sk-\u003esk_rcvbuf)\n-\t\t\tbreak;\n-\n \t\tprefetch(skb-\u003enext);\n \t\tlist_del(\u0026skb-\u003elist);\n \t\t*delta += skb-\u003etruesize;\n@@ -2248,9 +2313,7 @@ static bool mptcp_can_spool_backlog(struct sock *sk, struct list_head *skbs)\n \tDEBUG_NET_WARN_ON_ONCE(msk-\u003ebacklog_unaccounted \u0026\u0026 sk-\u003esk_socket \u0026\u0026\n \t\t\t mem_cgroup_from_sk(sk));\n \n-\t/* Don't spool the backlog if the rcvbuf is full. */\n-\tif (list_empty(\u0026msk-\u003ebacklog_list) ||\n-\t sk_rmem_alloc_get(sk) \u003e sk-\u003esk_rcvbuf)\n+\tif (list_empty(\u0026msk-\u003ebacklog_list))\n \t\treturn false;\n \n \tINIT_LIST_HEAD(skbs);\n@@ -2258,20 +2321,12 @@ static bool mptcp_can_spool_backlog(struct sock *sk, struct list_head *skbs)\n \treturn true;\n }\n \n-static void mptcp_backlog_spooled(struct sock *sk, u32 moved,\n-\t\t\t\t struct list_head *skbs)\n-{\n-\tstruct mptcp_sock *msk = mptcp_sk(sk);\n-\n-\tWRITE_ONCE(msk-\u003ebacklog_len, msk-\u003ebacklog_len - moved);\n-\tlist_splice(skbs, \u0026msk-\u003ebacklog_list);\n-}\n-\n static bool mptcp_move_skbs(struct sock *sk)\n {\n+\tstruct mptcp_sock *msk = mptcp_sk(sk);\n \tstruct list_head skbs;\n \tbool enqueued = false;\n-\tu32 moved;\n+\tu32 moved = 0;\n \n \tmptcp_data_lock(sk);\n \twhile (mptcp_can_spool_backlog(sk, \u0026skbs)) {\n@@ -2279,8 +2334,8 @@ static bool mptcp_move_skbs(struct sock *sk)\n \t\tenqueued |= __mptcp_move_skbs(sk, \u0026skbs, \u0026moved);\n \n \t\tmptcp_data_lock(sk);\n-\t\tmptcp_backlog_spooled(sk, moved, \u0026skbs);\n \t}\n+\tWRITE_ONCE(msk-\u003ebacklog_len, msk-\u003ebacklog_len - moved);\n \tmptcp_data_unlock(sk);\n \n \tif (enqueued \u0026\u0026 mptcp_epollin_ready(sk))\n@@ -2469,7 +2524,6 @@ struct sock *mptcp_subflow_get_retrans(struct mptcp_sock *msk)\n \n \t\t/* still data outstanding at TCP level? skip this */\n \t\tif (!tcp_rtx_and_write_queues_empty(ssk)) {\n-\t\t\tmptcp_pm_subflow_chk_stale(msk, ssk);\n \t\t\tmin_stale_count = min_t(int, min_stale_count, subflow-\u003estale_count);\n \t\t\tcontinue;\n \t\t}\n@@ -2791,44 +2845,22 @@ static void mptcp_check_fastclose(struct mptcp_sock *msk)\n \tsk_error_report(sk);\n }\n \n-static void __mptcp_retrans(struct sock *sk)\n+/*\n+ * Retransmit the specified data fragment on all the selected subflows,\n+ * starting from the specified sequence\n+ */\n+static int __mptcp_push_retrans(struct sock *sk, struct mptcp_data_frag *dfrag,\n+\t\t\t\tu64 sent_seq)\n {\n \tstruct mptcp_sendmsg_info info = { .data_lock_held = true, };\n \tstruct mptcp_sock *msk = mptcp_sk(sk);\n \tstruct mptcp_subflow_context *subflow;\n-\tstruct mptcp_data_frag *dfrag;\n \tstruct sock *ssk;\n-\tint ret, err;\n-\tu16 len = 0;\n-\n-\tmptcp_clean_una_wakeup(sk);\n-\n-\t/* first check ssk: need to kick \"stale\" logic */\n-\terr = mptcp_sched_get_retrans(msk);\n-\tdfrag = mptcp_rtx_head(sk);\n-\tif (!dfrag) {\n-\t\tif (mptcp_data_fin_enabled(msk)) {\n-\t\t\tstruct inet_connection_sock *icsk = inet_csk(sk);\n-\n-\t\t\tWRITE_ONCE(icsk-\u003eicsk_retransmits,\n-\t\t\t\t icsk-\u003eicsk_retransmits + 1);\n-\t\t\tmptcp_set_datafin_timeout(sk);\n-\t\t\tmptcp_send_ack(msk);\n-\n-\t\t\tgoto reset_timer;\n-\t\t}\n-\n-\t\tif (!mptcp_send_head(sk))\n-\t\t\tgoto clear_scheduled;\n-\n-\t\tgoto reset_timer;\n-\t}\n-\n-\tif (err)\n-\t\tgoto reset_timer;\n+\tint ret, len = 0;\n \n \tmptcp_for_each_subflow(msk, subflow) {\n \t\tif (READ_ONCE(subflow-\u003escheduled)) {\n+\t\t\tu16 offset = sent_seq - dfrag-\u003edata_seq;\n \t\t\tu16 copied = 0;\n \n \t\t\tmptcp_subflow_set_scheduled(subflow, false);\n@@ -2838,7 +2870,7 @@ static void __mptcp_retrans(struct sock *sk)\n \t\t\tlock_sock(ssk);\n \n \t\t\t/* limit retransmission to the bytes already sent on some subflows */\n-\t\t\tinfo.sent = 0;\n+\t\t\tinfo.sent = offset;\n \t\t\tinfo.limit = READ_ONCE(msk-\u003ecsum_enabled) ? dfrag-\u003edata_len :\n \t\t\t\t\t\t\t\t dfrag-\u003ealready_sent;\n \n@@ -2853,7 +2885,7 @@ static void __mptcp_retrans(struct sock *sk)\n \t\t\t !msk-\u003eallow_subflows) {\n \t\t\t\tspin_unlock_bh(\u0026msk-\u003efallback_lock);\n \t\t\t\trelease_sock(ssk);\n-\t\t\t\tgoto clear_scheduled;\n+\t\t\t\treturn -1;\n \t\t\t}\n \n \t\t\twhile (info.sent \u003c info.limit) {\n@@ -2876,13 +2908,99 @@ static void __mptcp_retrans(struct sock *sk)\n \t\t\trelease_sock(ssk);\n \t\t}\n \t}\n+\treturn len;\n+}\n+\n+static void __mptcp_retrans(struct sock *sk)\n+{\n+\tstruct mptcp_sock *msk = mptcp_sk(sk);\n+\tstruct mptcp_subflow_context *subflow;\n+\tstruct mptcp_data_frag *dfrag;\n+\tu64 retrans_seq, sent_seq;\n+\tbool need_retrans;\n+\tint err, len;\n+\n+\tmptcp_pm_chk_stale(msk);\n+\n+\t/* Get an updated and consistent rtx queue status. */\n+\tmptcp_data_lock(sk);\n+\t__mptcp_clean_una_wakeup(sk);\n+\tretrans_seq = msk-\u003esnd_una;\n+\tdfrag = mptcp_rtx_head(sk);\n+\tneed_retrans = !!dfrag;\n+\tmptcp_data_unlock(sk);\n+\n+\tfor (;;) {\n+\t\tbool already_acked;\n+\n+\t\terr = mptcp_sched_get_retrans(msk);\n+\t\tif (err)\n+\t\t\tbreak;\n+\n+\t\t/* `already_sent` can be 0 for `dfrag` belonging to the RTX\n+\t\t * queue due to __mptcp_retransmit_pending_data().\n+\t\t */\n+\t\tif (!dfrag || !dfrag-\u003ealready_sent)\n+\t\t\tbreak;\n+\n+\t\t/* Can fail only in case of fallback. */\n+\t\tlen = __mptcp_push_retrans(sk, dfrag, retrans_seq);\n+\t\tif (len \u003c 0)\n+\t\t\tgoto clear_scheduled;\n+\n+\t\tretrans_seq += len;\n+\t\tmsk-\u003ebytes_retrans += len;\n+\t\tdfrag-\u003ealready_sent = max_t(u16, dfrag-\u003ealready_sent,\n+\t\t\t\t\t retrans_seq - dfrag-\u003edata_seq);\n+\n+\t\t/* With csum enabled, retransmission can send new data. */\n+\t\tsent_seq = dfrag-\u003ealready_sent + dfrag-\u003edata_seq;\n+\t\tif (after64(sent_seq, msk-\u003esnd_nxt))\n+\t\t\tWRITE_ONCE(msk-\u003esnd_nxt, sent_seq);\n+\n+\t\t/* Attempt the next fragment only if the current one is\n+\t\t * completely retransmitted.\n+\t\t */\n+\t\tif (before64(retrans_seq, dfrag-\u003edata_seq + dfrag-\u003edata_len))\n+\t\t\tbreak;\n+\n+\t\tdfrag = list_is_last(\u0026dfrag-\u003elist, \u0026msk-\u003ertx_queue) ?\n+\t\t\t\tNULL : list_next_entry(dfrag, list);\n+\t\tif (!dfrag)\n+\t\t\tbreak;\n+\n+\t\t/* Incoming acks can move snd_una after the current dfrag\n+\t\t * across loop iterations, if so start again from RTX head.\n+\t\t */\n+\t\tmptcp_data_lock(sk);\n+\t\talready_acked = !before64(msk-\u003esnd_una, dfrag-\u003edata_seq +\n+\t\t\t\t\t dfrag-\u003ealready_sent);\n+\t\tif (already_acked) {\n+\t\t\t__mptcp_clean_una_wakeup(sk);\n+\t\t\tretrans_seq = msk-\u003esnd_una;\n+\t\t\tdfrag = mptcp_rtx_head(sk);\n+\t\t\tneed_retrans = !!dfrag;\n+\t\t} else if (after64(msk-\u003esnd_una, retrans_seq)) {\n+\t\t\tretrans_seq = msk-\u003esnd_una;\n+\t\t}\n+\t\tmptcp_data_unlock(sk);\n+\t}\n \n-\tmsk-\u003ebytes_retrans += len;\n-\tdfrag-\u003ealready_sent = max(dfrag-\u003ealready_sent, len);\n+\t/* Attempt data-fin retransmission only when the RTX queue is empty. */\n+\tif (!need_retrans) {\n+\t\tif (mptcp_data_fin_enabled(msk)) {\n+\t\t\tstruct inet_connection_sock *icsk = inet_csk(sk);\n \n-\t/* With csum enabled retransmission can send new data. */\n-\tif (after64(dfrag-\u003ealready_sent + dfrag-\u003edata_seq, msk-\u003esnd_nxt))\n-\t\tWRITE_ONCE(msk-\u003esnd_nxt, dfrag-\u003ealready_sent + dfrag-\u003edata_seq);\n+\t\t\tWRITE_ONCE(icsk-\u003eicsk_retransmits,\n+\t\t\t\t icsk-\u003eicsk_retransmits + 1);\n+\t\t\tmptcp_set_datafin_timeout(sk);\n+\t\t\tmptcp_send_ack(msk);\n+\t\t\tgoto reset_timer;\n+\t\t}\n+\n+\t\tif (!mptcp_send_head(sk))\n+\t\t\tgoto clear_scheduled;\n+\t}\n \n reset_timer:\n \tmptcp_check_and_set_pending(sk);\n@@ -3676,12 +3794,12 @@ static void mptcp_release_cb(struct sock *sk)\n \t__must_hold(\u0026sk-\u003esk_lock.slock)\n {\n \tstruct mptcp_sock *msk = mptcp_sk(sk);\n+\tu32 moved = 0;\n \n \tfor (;;) {\n \t\tunsigned long flags = (msk-\u003ecb_flags \u0026 MPTCP_FLAGS_PROCESS_CTX_NEED);\n \t\tstruct list_head join_list, skbs;\n \t\tbool spool_bl;\n-\t\tu32 moved;\n \n \t\tspool_bl = mptcp_can_spool_backlog(sk, \u0026skbs);\n \t\tif (!flags \u0026\u0026 !spool_bl)\n@@ -3714,9 +3832,9 @@ static void mptcp_release_cb(struct sock *sk)\n \n \t\tcond_resched();\n \t\tspin_lock_bh(\u0026sk-\u003esk_lock.slock);\n-\t\tif (spool_bl)\n-\t\t\tmptcp_backlog_spooled(sk, moved, \u0026skbs);\n \t}\n+\tif (moved)\n+\t\tWRITE_ONCE(msk-\u003ebacklog_len, msk-\u003ebacklog_len - moved);\n \n \tif (__test_and_clear_bit(MPTCP_CLEAN_UNA, \u0026msk-\u003ecb_flags))\n \t\t__mptcp_clean_una_wakeup(sk);\ndiff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h\nindex 1b80f2d6ec5a2..b3af3462bdd16 100644\n--- a/net/mptcp/protocol.h\n+++ b/net/mptcp/protocol.h\n@@ -580,12 +580,11 @@ struct mptcp_subflow_context {\n \t\tremote_key_valid : 1, /* received the peer key from */\n \t\tdisposable : 1,\t /* ctx can be free at ulp release time */\n \t\tclosing : 1,\t /* must not pass rx data to msk anymore */\n-\t\tstale : 1,\t /* unable to snd/rcv data, do not use for xmit */\n \t\tvalid_csum_seen : 1, /* at least one csum validated */\n \t\tis_mptfo : 1,\t /* subflow is doing TFO */\n \t\tclose_event_done : 1, /* has done the post-closed part */\n \t\tmpc_drop : 1,\t /* the MPC option has been dropped in a rtx */\n-\t\t__unused : 8;\n+\t\t__unused : 9;\n \tbool\tdata_avail;\n \tbool\tscheduled;\n \tbool\tpm_listener;\t /* a listener managed by the kernel PM? */\n@@ -604,7 +603,11 @@ struct mptcp_subflow_context {\n \tu8\treset_seen:1;\n \tu8\treset_transient:1;\n \tu8\treset_reason:4;\n-\tu8\tstale_count;\n+\tu8\tstale_count;\t /* Protected by the msk socket lock */\n+\tu8\tstale;\t\t /* Protected by the msk socket lock,\n+\t\t\t\t * if set the subflow is unable to snd/rcv\n+\t\t\t\t * data, the schedule should skip it\n+\t\t\t\t */\n \n \tu32\tsubflow_id;\n \n@@ -1103,7 +1106,7 @@ int mptcp_pm_parse_entry(struct nlattr *attr, struct genl_info *info,\n bool mptcp_pm_addr_families_match(const struct sock *sk,\n \t\t\t\t const struct mptcp_addr_info *loc,\n \t\t\t\t const struct mptcp_addr_info *rem);\n-void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk);\n+void mptcp_pm_chk_stale(const struct mptcp_sock *msk);\n void mptcp_pm_new_connection(struct mptcp_sock *msk, const struct sock *ssk, int server_side);\n void mptcp_pm_fully_established(struct mptcp_sock *msk, const struct sock *ssk);\n bool mptcp_pm_allow_new_subflow(struct mptcp_sock *msk);\n"
} |
| 2/1 | 2026/08/07 16:21 | agent | patch-evaluator |
12mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"MPTCP",
"MPTCP_IPV6"
],
"FocusSymbols": [
"mptcp_incoming_options",
"mptcp_pm_chk_stale",
"mptcp_subflow_get_retrans",
"mptcp_data_ready",
"mptcp_recvmsg",
"mptcp_sendmsg"
],
"Reasoning": "The patch modifies the MPTCP protocol implementation to handle memory limits, backlog drops, and retransmission logic. It adds new MIB counters and modifies core MPTCP functions like `mptcp_incoming_options`, `mptcp_pm_chk_stale`, and `__mptcp_retrans`. These are reachable from standard MPTCP socket operations and network receive paths, making them highly relevant for fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit b606a7bc46510b8a98488ab5264b73fb863fa89a
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 7 16:21:12 2026 +0000
syz-cluster: applied patch under review
diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c
index f23fda0c55a72..2569385bab7ca 100644
--- a/net/mptcp/mib.c
+++ b/net/mptcp/mib.c
@@ -85,6 +85,9 @@ static const struct snmp_mib mptcp_snmp_list[] = {
SNMP_MIB_ITEM("SimultConnectFallback", MPTCP_MIB_SIMULTCONNFALLBACK),
SNMP_MIB_ITEM("FallbackFailed", MPTCP_MIB_FALLBACKFAILED),
SNMP_MIB_ITEM("WinProbe", MPTCP_MIB_WINPROBE),
+ SNMP_MIB_ITEM("BacklogDrop", MPTCP_MIB_BACKLOGDROP),
+ SNMP_MIB_ITEM("RcvPruned", MPTCP_MIB_RCVPRUNED),
+ SNMP_MIB_ITEM("OFOPruned", MPTCP_MIB_OFOPRUNED),
};
/* mptcp_mib_alloc - allocate percpu mib counters
diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h
index 812218b5ed2bf..3a3425e258a76 100644
--- a/net/mptcp/mib.h
+++ b/net/mptcp/mib.h
@@ -88,6 +88,9 @@ enum linux_mptcp_mib_field {
MPTCP_MIB_SIMULTCONNFALLBACK, /* Simultaneous connect */
MPTCP_MIB_FALLBACKFAILED, /* Can't fallback due to msk status */
MPTCP_MIB_WINPROBE, /* MPTCP-level zero window probe */
+ MPTCP_MIB_BACKLOGDROP, /* Backlog over memory limit */
+ MPTCP_MIB_RCVPRUNED, /* Dropped due to memory constraints */
+ MPTCP_MIB_OFOPRUNED, /* MPTCP-level OoO queue pruned */
__MPTCP_MIB_MAX
};
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index 1057d500577b0..b8318e0301389 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -1190,8 +1190,34 @@ static bool add_addr_hmac_valid(struct mptcp_sock *msk,
return hmac == mp_opt->ahmac;
}
-/* Return false in case of error (or subflow has been reset),
- * else return true.
+static bool mptcp_over_limit(struct sock *sk, struct sock *ssk,
+ const struct sk_buff *skb)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ u32 rcvbuf = READ_ONCE(sk->sk_rcvbuf);
+
+ if (likely((u32)sk_rmem_alloc_get(sk) <= rcvbuf &&
+ READ_ONCE(msk->backlog_len) <= rcvbuf))
+ return false;
+
+ /* Avoid silently dropping pure acks, fin, rst or already-acked segm. */
+ if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq ||
+ TCP_SKB_CB(skb)->tcp_flags & (TCPHDR_FIN | TCPHDR_RST) ||
+ !after(TCP_SKB_CB(skb)->end_seq, tcp_sk(ssk)->rcv_nxt))
+ return false;
+
+ /* Dropped due to memory constraints, schedule an ack. */
+ inet_csk(ssk)->icsk_ack.pending |= ICSK_ACK_NOMEM | ICSK_ACK_NOW;
+ inet_csk_schedule_ack(ssk);
+
+ /* Plain TCP (fallback) and skb is dropped before the TCP recv queue. */
+ NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPRCVQDROP);
+
+ return true;
+}
+
+/* Return false when the caller must drop the packet, i.e. in case of error,
+ * subflow has been reset, or over memory limits.
*/
bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)
{
@@ -1217,7 +1243,7 @@ bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)
__mptcp_data_acked(subflow->conn);
mptcp_data_unlock(subflow->conn);
- return true;
+ return !mptcp_over_limit(subflow->conn, sk, skb);
}
mptcp_get_options(skb, &mp_opt);
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 64a1236aabee9..d1f73c3e39fa3 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -1065,7 +1065,8 @@ bool mptcp_pm_is_backup(struct mptcp_sock *msk, struct sock_common *skc)
return mptcp_pm_nl_is_backup(msk, &skc_local);
}
-static void mptcp_pm_subflows_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)
+static void
+mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)
{
struct mptcp_subflow_context *iter, *subflow = mptcp_subflow_ctx(ssk);
struct sock *sk = (struct sock *)msk;
@@ -1102,22 +1103,34 @@ static void mptcp_pm_subflows_chk_stale(const struct mptcp_sock *msk, struct soc
}
}
-void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)
+void mptcp_pm_chk_stale(const struct mptcp_sock *msk)
{
- struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
- u32 rcv_tstamp = READ_ONCE(tcp_sk(ssk)->rcv_tstamp);
-
- /* keep track of rtx periods with no progress */
- if (!subflow->stale_count) {
- subflow->stale_rcv_tstamp = rcv_tstamp;
- subflow->stale_count++;
- } else if (subflow->stale_rcv_tstamp == rcv_tstamp) {
- if (subflow->stale_count < U8_MAX)
+ struct mptcp_subflow_context *subflow;
+
+ mptcp_for_each_subflow(msk, subflow) {
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+ u32 rcv_tstamp;
+
+ if (!__mptcp_subflow_active(subflow))
+ continue;
+
+ /* No data outstanding at TCP level? not stale */
+ if (tcp_rtx_and_write_queues_empty(ssk))
+ continue;
+
+ /* keep track of rtx periods with no progress */
+ rcv_tstamp = READ_ONCE(tcp_sk(ssk)->rcv_tstamp);
+ if (!subflow->stale_count) {
+ subflow->stale_rcv_tstamp = rcv_tstamp;
subflow->stale_count++;
- mptcp_pm_subflows_chk_stale(msk, ssk);
- } else {
- subflow->stale_count = 0;
- mptcp_subflow_set_active(subflow);
+ } else if (subflow->stale_rcv_tstamp == rcv_tstamp) {
+ if (subflow->stale_count < U8_MAX)
+ subflow->stale_count++;
+ mptcp_pm_subflow_chk_stale(msk, ssk);
+ } else {
+ subflow->stale_count = 0;
+ mptcp_subflow_set_active(subflow);
+ }
}
}
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 7c8180d8d5eff..ec874d2ead6a4 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -242,6 +242,65 @@ static bool mptcp_rcvbuf_grow(struct sock *sk, u32 newval)
return false;
}
+/* "Inspired" from the TCP version; main difference: stop as soon as the MPTCP
+ * socket is under memory limit.
+ */
+static void mptcp_prune_ofo_queue(struct sock *sk,
+ const struct sk_buff *in_skb)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct rb_node *node, *prev;
+ bool pruned = false;
+ u64 mem;
+
+ if (RB_EMPTY_ROOT(&msk->out_of_order_queue))
+ return;
+
+ node = &msk->ooo_last_skb->rbnode;
+
+ do {
+ struct sk_buff *skb = rb_to_skb(node);
+
+ /* Stop pruning if the incoming skb would land in OoO tail. */
+ if (after64(MPTCP_SKB_CB(in_skb)->map_seq,
+ MPTCP_SKB_CB(skb)->map_seq))
+ break;
+
+ pruned = true;
+ prev = rb_prev(node);
+ rb_erase(node, &msk->out_of_order_queue);
+ mptcp_drop(sk, skb);
+ msk->ooo_last_skb = rb_to_skb(prev);
+
+ mem = (unsigned int)sk_rmem_alloc_get(sk);
+ if (mem <= sk->sk_rcvbuf)
+ break;
+
+ node = prev;
+ } while (node);
+
+ if (pruned)
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOPRUNED);
+}
+
+/* The stack can't drop packets for fallback socket at the msk level, or the
+ * stream will break.
+ */
+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));
+}
+
+static bool mptcp_try_rmem_schedule(struct sock *sk, const struct sk_buff *skb)
+{
+ if (!mptcp_can_ingest(sk)) {
+ mptcp_prune_ofo_queue(sk, skb);
+ return mptcp_can_ingest(sk);
+ }
+ return true;
+}
+
/* "inspired" by tcp_data_queue_ofo(), main differences:
* - use mptcp seqs
* - don't cope with sacks
@@ -253,6 +312,12 @@ 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);
@@ -389,6 +454,13 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) {
/* in sequence */
+insert:
+ if (!mptcp_try_rmem_schedule(sk, skb)) {
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED);
+ mptcp_drop(sk, skb);
+ return false;
+ }
+
msk->bytes_received += copy_len;
WRITE_ONCE(msk->ack_seq, msk->ack_seq + copy_len);
tail = skb_peek_tail(&sk->sk_receive_queue);
@@ -403,26 +475,20 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
return false;
}
- /* Completely old data? */
- if (!after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
- MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
- mptcp_drop(sk, skb);
- return false;
+ /* Partial packet */
+ if (after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
+ copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq;
+ MPTCP_SKB_CB(skb)->offset += msk->ack_seq -
+ MPTCP_SKB_CB(skb)->map_seq;
+ MPTCP_SKB_CB(skb)->map_seq += msk->ack_seq -
+ MPTCP_SKB_CB(skb)->map_seq;
+ goto insert;
}
- /* Partial packet: map_seq < ack_seq < end_seq.
- * Skip the already-acked bytes and enqueue the new data.
- */
- copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq;
- MPTCP_SKB_CB(skb)->offset += msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq;
- MPTCP_SKB_CB(skb)->map_seq += msk->ack_seq -
- MPTCP_SKB_CB(skb)->map_seq;
- msk->bytes_received += copy_len;
- WRITE_ONCE(msk->ack_seq, msk->ack_seq + copy_len);
-
- skb_set_owner_r(skb, sk);
- __skb_queue_tail(&sk->sk_receive_queue, skb);
- return true;
+ /* Completely old data */
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
+ mptcp_drop(sk, skb);
+ return false;
}
static void mptcp_stop_rtx_timer(struct sock *sk)
@@ -681,6 +747,7 @@ static void __mptcp_add_backlog(struct sock *sk,
struct sk_buff *tail = NULL;
struct sock *ssk = skb->sk;
bool fragstolen;
+ u64 limit;
int delta;
if (unlikely(sk->sk_state == TCP_CLOSE)) {
@@ -688,6 +755,16 @@ static void __mptcp_add_backlog(struct sock *sk,
return;
}
+ /* Similar additional allowance as plain TCP. */
+ limit = READ_ONCE(sk->sk_rcvbuf);
+ limit += (limit >> 1) + 64 * 1024;
+ limit = min_t(u64, limit, UINT_MAX);
+ if (msk->backlog_len > limit && !__mptcp_check_fallback(msk)) {
+ __MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_BACKLOGDROP);
+ kfree_skb_reason(skb, SKB_DROP_REASON_SOCKET_BACKLOG);
+ return;
+ }
+
/* Try to coalesce with the last skb in our backlog */
if (!list_empty(&msk->backlog_list))
tail = list_last_entry(&msk->backlog_list, struct sk_buff, list);
@@ -759,7 +836,7 @@ static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk,
mptcp_init_skb(ssk, skb, offset, len);
- if (own_msk && sk_rmem_alloc_get(sk) < sk->sk_rcvbuf) {
+ if (own_msk) {
mptcp_subflow_lend_fwdmem(subflow, skb);
ret |= __mptcp_move_skb(sk, skb);
} else {
@@ -1142,13 +1219,6 @@ static void __mptcp_clean_una_wakeup(struct sock *sk)
mptcp_write_space(sk);
}
-static void mptcp_clean_una_wakeup(struct sock *sk)
-{
- mptcp_data_lock(sk);
- __mptcp_clean_una_wakeup(sk);
- mptcp_data_unlock(sk);
-}
-
static void mptcp_enter_memory_pressure(struct sock *sk)
{
struct mptcp_subflow_context *subflow;
@@ -2215,12 +2285,7 @@ static bool __mptcp_move_skbs(struct sock *sk, struct list_head *skbs, u32 *delt
struct mptcp_sock *msk = mptcp_sk(sk);
bool moved = false;
- *delta = 0;
while (1) {
- /* If the msk recvbuf is full stop, don't drop */
- if (sk_rmem_alloc_get(sk) > sk->sk_rcvbuf)
- break;
-
prefetch(skb->next);
list_del(&skb->list);
*delta += skb->truesize;
@@ -2248,9 +2313,7 @@ static bool mptcp_can_spool_backlog(struct sock *sk, struct list_head *skbs)
DEBUG_NET_WARN_ON_ONCE(msk->backlog_unaccounted && sk->sk_socket &&
mem_cgroup_from_sk(sk));
- /* Don't spool the backlog if the rcvbuf is full. */
- if (list_empty(&msk->backlog_list) ||
- sk_rmem_alloc_get(sk) > sk->sk_rcvbuf)
+ if (list_empty(&msk->backlog_list))
return false;
INIT_LIST_HEAD(skbs);
@@ -2258,20 +2321,12 @@ static bool mptcp_can_spool_backlog(struct sock *sk, struct list_head *skbs)
return true;
}
-static void mptcp_backlog_spooled(struct sock *sk, u32 moved,
- struct list_head *skbs)
-{
- struct mptcp_sock *msk = mptcp_sk(sk);
-
- WRITE_ONCE(msk->backlog_len, msk->backlog_len - moved);
- list_splice(skbs, &msk->backlog_list);
-}
-
static bool mptcp_move_skbs(struct sock *sk)
{
+ struct mptcp_sock *msk = mptcp_sk(sk);
struct list_head skbs;
bool enqueued = false;
- u32 moved;
+ u32 moved = 0;
mptcp_data_lock(sk);
while (mptcp_can_spool_backlog(sk, &skbs)) {
@@ -2279,8 +2334,8 @@ static bool mptcp_move_skbs(struct sock *sk)
enqueued |= __mptcp_move_skbs(sk, &skbs, &moved);
mptcp_data_lock(sk);
- mptcp_backlog_spooled(sk, moved, &skbs);
}
+ WRITE_ONCE(msk->backlog_len, msk->backlog_len - moved);
mptcp_data_unlock(sk);
if (enqueued && mptcp_epollin_ready(sk))
@@ -2469,7 +2524,6 @@ struct sock *mptcp_subflow_get_retrans(struct mptcp_sock *msk)
/* still data outstanding at TCP level? skip this */
if (!tcp_rtx_and_write_queues_empty(ssk)) {
- mptcp_pm_subflow_chk_stale(msk, ssk);
min_stale_count = min_t(int, min_stale_count, subflow->stale_count);
continue;
}
@@ -2791,44 +2845,22 @@ static void mptcp_check_fastclose(struct mptcp_sock *msk)
sk_error_report(sk);
}
-static void __mptcp_retrans(struct sock *sk)
+/*
+ * Retransmit the specified data fragment on all the selected subflows,
+ * starting from the specified sequence
+ */
+static int __mptcp_push_retrans(struct sock *sk, struct mptcp_data_frag *dfrag,
+ u64 sent_seq)
{
struct mptcp_sendmsg_info info = { .data_lock_held = true, };
struct mptcp_sock *msk = mptcp_sk(sk);
struct mptcp_subflow_context *subflow;
- struct mptcp_data_frag *dfrag;
struct sock *ssk;
- int ret, err;
- u16 len = 0;
-
- mptcp_clean_una_wakeup(sk);
-
- /* first check ssk: need to kick "stale" logic */
- err = mptcp_sched_get_retrans(msk);
- dfrag = mptcp_rtx_head(sk);
- if (!dfrag) {
- if (mptcp_data_fin_enabled(msk)) {
- struct inet_connection_sock *icsk = inet_csk(sk);
-
- WRITE_ONCE(icsk->icsk_retransmits,
- icsk->icsk_retransmits + 1);
- mptcp_set_datafin_timeout(sk);
- mptcp_send_ack(msk);
-
- goto reset_timer;
- }
-
- if (!mptcp_send_head(sk))
- goto clear_scheduled;
-
- goto reset_timer;
- }
-
- if (err)
- goto reset_timer;
+ int ret, len = 0;
mptcp_for_each_subflow(msk, subflow) {
if (READ_ONCE(subflow->scheduled)) {
+ u16 offset = sent_seq - dfrag->data_seq;
u16 copied = 0;
mptcp_subflow_set_scheduled(subflow, false);
@@ -2838,7 +2870,7 @@ static void __mptcp_retrans(struct sock *sk)
lock_sock(ssk);
/* limit retransmission to the bytes already sent on some subflows */
- info.sent = 0;
+ info.sent = offset;
info.limit = READ_ONCE(msk->csum_enabled) ? dfrag->data_len :
dfrag->already_sent;
@@ -2853,7 +2885,7 @@ static void __mptcp_retrans(struct sock *sk)
!msk->allow_subflows) {
spin_unlock_bh(&msk->fallback_lock);
release_sock(ssk);
- goto clear_scheduled;
+ return -1;
}
while (info.sent < info.limit) {
@@ -2876,13 +2908,99 @@ static void __mptcp_retrans(struct sock *sk)
release_sock(ssk);
}
}
+ return len;
+}
+
+static void __mptcp_retrans(struct sock *sk)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct mptcp_subflow_context *subflow;
+ struct mptcp_data_frag *dfrag;
+ u64 retrans_seq, sent_seq;
+ bool need_retrans;
+ int err, len;
+
+ mptcp_pm_chk_stale(msk);
+
+ /* Get an updated and consistent rtx queue status. */
+ mptcp_data_lock(sk);
+ __mptcp_clean_una_wakeup(sk);
+ retrans_seq = msk->snd_una;
+ dfrag = mptcp_rtx_head(sk);
+ need_retrans = !!dfrag;
+ mptcp_data_unlock(sk);
+
+ for (;;) {
+ bool already_acked;
+
+ err = mptcp_sched_get_retrans(msk);
+ if (err)
+ break;
+
+ /* `already_sent` can be 0 for `dfrag` belonging to the RTX
+ * queue due to __mptcp_retransmit_pending_data().
+ */
+ if (!dfrag || !dfrag->already_sent)
+ break;
+
+ /* Can fail only in case of fallback. */
+ len = __mptcp_push_retrans(sk, dfrag, retrans_seq);
+ if (len < 0)
+ goto clear_scheduled;
+
+ retrans_seq += len;
+ msk->bytes_retrans += len;
+ dfrag->already_sent = max_t(u16, dfrag->already_sent,
+ retrans_seq - dfrag->data_seq);
+
+ /* With csum enabled, retransmission can send new data. */
+ sent_seq = dfrag->already_sent + dfrag->data_seq;
+ if (after64(sent_seq, msk->snd_nxt))
+ WRITE_ONCE(msk->snd_nxt, sent_seq);
+
+ /* Attempt the next fragment only if the current one is
+ * completely retransmitted.
+ */
+ if (before64(retrans_seq, dfrag->data_seq + dfrag->data_len))
+ break;
+
+ dfrag = list_is_last(&dfrag->list, &msk->rtx_queue) ?
+ NULL : list_next_entry(dfrag, list);
+ if (!dfrag)
+ break;
+
+ /* Incoming acks can move snd_una after the current dfrag
+ * across loop iterations, if so start again from RTX head.
+ */
+ mptcp_data_lock(sk);
+ already_acked = !before64(msk->snd_una, dfrag->data_seq +
+ dfrag->already_sent);
+ if (already_acked) {
+ __mptcp_clean_una_wakeup(sk);
+ retrans_seq = msk->snd_una;
+ dfrag = mptcp_rtx_head(sk);
+ need_retrans = !!dfrag;
+ } else if (after64(msk->snd_una, retrans_seq)) {
+ retrans_seq = msk->snd_una;
+ }
+ mptcp_data_unlock(sk);
+ }
- msk->bytes_retrans += len;
- dfrag->already_sent = max(dfrag->already_sent, len);
+ /* Attempt data-fin retransmission only when the RTX queue is empty. */
+ if (!need_retrans) {
+ if (mptcp_data_fin_enabled(msk)) {
+ struct inet_connection_sock *icsk = inet_csk(sk);
- /* With csum enabled retransmission can send new data. */
- if (after64(dfrag->already_sent + dfrag->data_seq, msk->snd_nxt))
- WRITE_ONCE(msk->snd_nxt, dfrag->already_sent + dfrag->data_seq);
+ WRITE_ONCE(icsk->icsk_retransmits,
+ icsk->icsk_retransmits + 1);
+ mptcp_set_datafin_timeout(sk);
+ mptcp_send_ack(msk);
+ goto reset_timer;
+ }
+
+ if (!mptcp_send_head(sk))
+ goto clear_scheduled;
+ }
reset_timer:
mptcp_check_and_set_pending(sk);
@@ -3676,12 +3794,12 @@ static void mptcp_release_cb(struct sock *sk)
__must_hold(&sk->sk_lock.slock)
{
struct mptcp_sock *msk = mptcp_sk(sk);
+ u32 moved = 0;
for (;;) {
unsigned long flags = (msk->cb_flags & MPTCP_FLAGS_PROCESS_CTX_NEED);
struct list_head join_list, skbs;
bool spool_bl;
- u32 moved;
spool_bl = mptcp_can_spool_backlog(sk, &skbs);
if (!flags && !spool_bl)
@@ -3714,9 +3832,9 @@ static void mptcp_release_cb(struct sock *sk)
cond_resched();
spin_lock_bh(&sk->sk_lock.slock);
- if (spool_bl)
- mptcp_backlog_spooled(sk, moved, &skbs);
}
+ if (moved)
+ WRITE_ONCE(msk->backlog_len, msk->backlog_len - moved);
if (__test_and_clear_bit(MPTCP_CLEAN_UNA, &msk->cb_flags))
__mptcp_clean_una_wakeup(sk);
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 1b80f2d6ec5a2..b3af3462bdd16 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -580,12 +580,11 @@ struct mptcp_subflow_context {
remote_key_valid : 1, /* received the peer key from */
disposable : 1, /* ctx can be free at ulp release time */
closing : 1, /* must not pass rx data to msk anymore */
- stale : 1, /* unable to snd/rcv data, do not use for xmit */
valid_csum_seen : 1, /* at least one csum validated */
is_mptfo : 1, /* subflow is doing TFO */
close_event_done : 1, /* has done the post-closed part */
mpc_drop : 1, /* the MPC option has been dropped in a rtx */
- __unused : 8;
+ __unused : 9;
bool data_avail;
bool scheduled;
bool pm_listener; /* a listener managed by the kernel PM? */
@@ -604,7 +603,11 @@ struct mptcp_subflow_context {
u8 reset_seen:1;
u8 reset_transient:1;
u8 reset_reason:4;
- u8 stale_count;
+ u8 stale_count; /* Protected by the msk socket lock */
+ u8 stale; /* Protected by the msk socket lock,
+ * if set the subflow is unable to snd/rcv
+ * data, the schedule should skip it
+ */
u32 subflow_id;
@@ -1103,7 +1106,7 @@ int mptcp_pm_parse_entry(struct nlattr *attr, struct genl_info *info,
bool mptcp_pm_addr_families_match(const struct sock *sk,
const struct mptcp_addr_info *loc,
const struct mptcp_addr_info *rem);
-void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk);
+void mptcp_pm_chk_stale(const struct mptcp_sock *msk);
void mptcp_pm_new_connection(struct mptcp_sock *msk, const struct sock *ssk, int server_side);
void mptcp_pm_fully_established(struct mptcp_sock *msk, const struct sock *ssk);
bool mptcp_pm_allow_new_subflow(struct mptcp_sock *msk);
|
| 3/2 | 2026/08/07 16:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10698 output: 30 thoughts: 315 Thoughts: **MPTCP Code Changes and Fuzzing Considerations** Okay, so I've got this patch here that's touching quite a few files within the MPTCP subsystem: `net/mptcp/mib.c`, `net/mptcp/mib.h`, `net/mptcp/options.c`, `net/mptcp/pm.c`, `net/mptcp/protocol.c`, and `net/mptcp/protocol.h`. Looks like the core MPTCP implementation is being updated. Specifically, the patch is introducing new MIB counters for MPTCP-specific events, like backlog drops, receive pruning, and out-of-order data pruning. That’s a good addition for debugging and performance analysis. I also see modifications to `mptcp_incoming_options`, probably to integrate a check for `mptcp_over_limit`. Makes sense; managing memory limits is crucial in a Multipath context. And the fact that we're seeing changes to `mptcp_pm_chk_stale` and `mptcp_pm_subflow_chk_stale` hints at some updates to the path manager, possibly related to handling stale subflows or memory limitations. Beyond that, the patch seems to touch on core data handling with adjustments to `mptcp_data_queue_ofo`, `__mptcp_move_skb`, `__mptcp_add_backlog`, `__mptcp_move_skbs_from_subflow`, `mptcp_move_skbs`, `__mptcp_retrans`, and `mptcp_release_cb`. It definitely looks like a significant functional change within MPTCP. It's handling memory management, backlog drops, and retransmission logic, which means it is handling crucial states and the core functionality of how MPTCP operates. Given the scope of the changes and the critical nature of these functions, this is prime territory for fuzzing. I need to figure out what CONFIG options enable MPTCP so I can build a test environment. Obviously, `CONFIG_MPTCP` is the key, so I'll need to check `net/mptcp/Kconfig` to see if there are any other related options I should be aware of. Good chance to really shake things down and hunt for any potential vulnerabilities or performance bottlenecks. |
| 4/2 | 2026/08/07 16:21 | tool | grepper |
0mArgs:{
"Expression": "config MPTCP",
"PathPrefix": "net/mptcp/"
}Results: {
"Output": "net/mptcp/Kconfig-1-\nnet/mptcp/Kconfig:2:config MPTCP\nnet/mptcp/Kconfig-3-\tbool \"MPTCP: Multipath TCP\"\n--\nnet/mptcp/Kconfig=16=config INET_MPTCP_DIAG\n--\nnet/mptcp/Kconfig-19-\nnet/mptcp/Kconfig:20:config MPTCP_IPV6\nnet/mptcp/Kconfig-21-\tbool \"MPTCP: IPv6 support for Multipath TCP\"\n--\nnet/mptcp/Kconfig-24-\nnet/mptcp/Kconfig:25:config MPTCP_KUNIT_TEST\nnet/mptcp/Kconfig-26-\ttristate \"This builds the MPTCP KUnit tests\" if !KUNIT_ALL_TESTS\n"
} |
| 5/2 | 2026/08/07 16:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11774 output: 34 thoughts: 37 Thoughts: Okay, here's my take on those thoughts, framed as my own: **Analyzing the MPTCP Implementation** Alright, so the task is clear: we're dealing with MPTCP. First things first, I need to confirm we've got the core components in place: `MPTCP` is the essential piece, of course. Then, `MPTCP_IPV6`—while optional, it's pretty much a no-brainer to enable that, considering the future of networking. Now, where do I start digging for the crucial details? I'll head straight for `net/mptcp/Kconfig`. That's where all the dependencies and configuration options are defined. Time to dissect that and ensure everything is set up correctly. This should be a fairly straightforward exercise, but as always, thoroughness is key. |
| 6/2 | 2026/08/07 16:21 | tool | read-file |
0mArgs:{
"File": "net/mptcp/Kconfig",
"FirstLine": 1,
"LineCount": 20
}Results: {
"Contents": " 1:\t\n 2:\tconfig MPTCP\n 3:\t\tbool \"MPTCP: Multipath TCP\"\n 4:\t\tdepends on INET\n 5:\t\tselect SKB_EXTENSIONS\n 6:\t\tselect CRYPTO_LIB_SHA256\n 7:\t\tselect CRYPTO_LIB_UTILS\n 8:\t\thelp\n 9:\t\t Multipath TCP (MPTCP) connections send and receive data over multiple\n 10:\t\t subflows in order to utilize multiple network paths. Each subflow\n 11:\t\t uses the TCP protocol, and TCP options carry header information for\n 12:\t\t MPTCP.\n 13:\t\n 14:\tif MPTCP\n 15:\t\n 16:\tconfig INET_MPTCP_DIAG\n 17:\t\tdepends on INET_DIAG\n 18:\t\tdef_tristate INET_DIAG\n 19:\t\n 20:\tconfig MPTCP_IPV6\n"
} |
| 7/2 | 2026/08/07 16:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12270 output: 39 thoughts: 169 Thoughts: **Analyzing the MPTCP Configuration and Modified Functions** Okay, so I'm looking at this; it seems pretty clear that the `INET` dependency is playing a role, but the real meat of the configuration here centers around `MPTCP`. That's the core focus. Now, let's drill down into the function modifications. I need to understand what's been tweaked, and why. We have a set of functions that have been changed. I need to understand how these have been altered, and the implications of those alterations. Specifically, the list includes `mptcp_incoming_options`, `mptcp_pm_chk_stale`, `mptcp_data_queue_ofo`, `__mptcp_move_skb`, `__mptcp_add_backlog`, `__mptcp_move_skbs_from_subflow`, `mptcp_move_skbs`, `__mptcp_retrans`, and `mptcp_release_cb`. It seems a pretty broad range of areas have been touched. Okay, let's see, given the context, I think the next logical step is to dig into `net/mptcp/protocol.c` for specific changes to `__mptcp_retrans` and `mptcp_release_cb`. That'll probably tell me the best way forward. Let's see what's actually been modified. |
| 8/2 | 2026/08/07 16:21 | tool | grepper |
0mArgs:{
"Expression": "^static void __mptcp_retrans",
"PathPrefix": "net/mptcp/protocol.c"
}Results: {
"Output": "net/mptcp/protocol.c=2852=static int __mptcp_push_retrans(struct sock *sk, struct mptcp_data_frag *dfrag,\n--\nnet/mptcp/protocol.c-2913-\nnet/mptcp/protocol.c:2914:static void __mptcp_retrans(struct sock *sk)\nnet/mptcp/protocol.c-2915-{\n"
} |
| 9/2 | 2026/08/07 16:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12907 output: 38 thoughts: 33 Thoughts: Okay, here's my take on those thoughts, framed as my own mental process, given my expertise: **Fuzzing Target Identification: A Quick Scan** Alright, time to get down to brass tacks. I need to zero in on the most promising targets for fuzzing. We're looking for code paths that are dynamic, that take input, and are therefore susceptible to unexpected data. Static functions are, by their nature, less likely to offer us interesting vulnerabilities – they're predictable. So, let's skip those. The real gold is in the functions that are exposed, the entry points to the system, where external data can flow in. Let's see... *mptcp_incoming_options*. Ah, interesting. The fact that it's *non-static* immediately piques my interest. Non-static, I interpret that as potentially exposed, potentially accepting external input. This is the kind of function that deserves a closer look. It's a prime candidate for further analysis to determine the precise nature of its input and processing logic. I need to assess whether this is the right place to begin fuzzing and what I should aim for. |
| 10/2 | 2026/08/07 16:21 | tool | grepper |
0mArgs:{
"Expression": "^bool mptcp_incoming_options",
"PathPrefix": "net/mptcp/options.c"
}Results: {
"Output": "net/mptcp/options.c=1193=static bool mptcp_over_limit(struct sock *sk, struct sock *ssk,\n--\nnet/mptcp/options.c-1221- */\nnet/mptcp/options.c:1222:bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)\nnet/mptcp/options.c-1223-{\n"
} |
| 11/2 | 2026/08/07 16:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13343 output: 41 thoughts: 17 Thoughts: Okay, here's my summary, considering my expertise: **My Initial Assessment of `mptcp_pm_chk_stale`** Right, `mptcp_pm_chk_stale`. The fact that it's *not* declared as `static` immediately catches my attention. That implies this function has external linkage within the MultiPath TCP (MPTCP) kernel module. It means it's potentially accessible and callable from other modules or, at the very least, exposed in the module's symbol table. This raises a few immediate considerations: * **Dependency Management:** If it's used elsewhere, I need to understand its role within the broader MPTCP ecosystem and how it interacts with other MPTCP functions, data structures, and kernel subsystems. I'll need to trace its call sites to determine who's relying on its existence and what those external usages are. * **API Design (if any):** Is `mptcp_pm_chk_stale` meant to be a part of a defined MPTCP API? If so, is this a *good* API? Is it stable, well-documented, and backward compatible? * **Security Implications:** If it's exposed, a careful review is necessary to see if it could be called with malicious intent. Are there any known vulnerabilities associated with its inputs or return value? * **Maintainability and Refactoring:** If it's widely used, making changes to its signature or behavior becomes a bigger deal. This non-static declaration potentially imposes restrictions on future refactoring efforts within the MPTCP code. If it's only used internally, it might be possible to make it static without affecting anyone. I will also have to check if it's used elsewhere, and if it is, the code using it. My next step will be to dive deeper into the code to figure out *why* it's non-static. The answer will significantly influence my understanding of the MPTCP architecture and how this function fits into the bigger picture. Understanding if this is intentional API design or an accident will be my goal. |
| 12/2 | 2026/08/07 16:21 | tool | grepper |
0mArgs:{
"Expression": "^void mptcp_pm_chk_stale",
"PathPrefix": "net/mptcp/pm.c"
}Results: {
"Output": "net/mptcp/pm.c=1069=mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)\n--\nnet/mptcp/pm.c-1105-\nnet/mptcp/pm.c:1106:void mptcp_pm_chk_stale(const struct mptcp_sock *msk)\nnet/mptcp/pm.c-1107-{\n"
} |
| 13/2 | 2026/08/07 16:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13977 output: 36 thoughts: 12 Thoughts: **My Internal Notes on `mptcp_release_cb`** Okay, so I'm looking at this `mptcp_release_cb` function and immediately, the fact that it's declared `static` jumps out at me. That means its scope is limited to this particular compilation unit, which, given its name, suggests it's likely a callback function within the Multipath TCP (MPTCP) module. This has implications for how it can be accessed and, more importantly, *why* it's designed this way. It's not meant to be called externally, which is typical for internal management functions. It's going to be something handling cleanup or release of resources within the MPTCP stack itself, and the static declaration enforces that encapsulation. |
| 14/2 | 2026/08/07 16:21 | tool | grepper |
0mArgs:{
"Expression": "mptcp_release_cb",
"PathPrefix": "net/mptcp/protocol.c"
}Results: {
"Output": "net/mptcp/protocol.c=3780=void __mptcp_check_push(struct sock *sk, struct sock *ssk)\n--\nnet/mptcp/protocol.c-3792-/* processes deferred events and flush wmem */\nnet/mptcp/protocol.c:3793:static void mptcp_release_cb(struct sock *sk)\nnet/mptcp/protocol.c-3794-\t__must_hold(\u0026sk-\u003esk_lock.slock)\n--\nnet/mptcp/protocol.c=4196=static struct proto mptcp_prot = {\n--\nnet/mptcp/protocol.c-4209-\t.recvmsg\t= mptcp_recvmsg,\nnet/mptcp/protocol.c:4210:\t.release_cb\t= mptcp_release_cb,\nnet/mptcp/protocol.c-4211-\t.hash\t\t= mptcp_hash,\n"
} |
| 15/2 | 2026/08/07 16:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14441 output: 38 thoughts: 34 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Thinking Through MPTCP Release Callbacks** Alright, let's see... so, `mptcp_release_cb` is registered in `mptcp_prot`, got it. That's the expected place, it's the core protocol handling. Makes sense. But my mind is already wandering a bit deeper... I need to be sure I understand the full flow, the edge cases. Now, wait a minute... what about `mptcp_data_queue_ofo`? Where does *that* fit into the picture? That function is crucial for out-of-order packet handling, especially in the context of multipath scenarios. I need to refresh my understanding of how those two interplay. The release callback should probably interact with that out-of-order queue or depend on it directly to perform some necessary house-keeping. Are we reordering stuff before release? After? Or is it more subtle than that? Gotta dig into the relationship between these functions now. |
| 16/2 | 2026/08/07 16:21 | tool | grepper |
0mArgs:{
"Expression": "mptcp_data_queue_ofo",
"PathPrefix": "net/mptcp/protocol.c"
}Results: {
"Output": "net/mptcp/protocol.c=295=static bool mptcp_try_rmem_schedule(struct sock *sk, const struct sk_buff *skb)\n--\nnet/mptcp/protocol.c-307- */\nnet/mptcp/protocol.c:308:static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)\nnet/mptcp/protocol.c-309-{\n--\nnet/mptcp/protocol.c=447=static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)\n--\nnet/mptcp/protocol.c-473-\t} else if (after64(MPTCP_SKB_CB(skb)-\u003emap_seq, msk-\u003eack_seq)) {\nnet/mptcp/protocol.c:474:\t\tmptcp_data_queue_ofo(msk, skb);\nnet/mptcp/protocol.c-475-\t\treturn false;\n"
} |
| 17/2 | 2026/08/07 16:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15012 output: 36 thoughts: 27 Thoughts: Here's my summary, as though I'm thinking through this myself, given my expertise: **Examining MPTCP Data Queue and Retransmission Logic** Okay, so I'm looking at this `mptcp_data_queue_ofo` thing. It's declared as `static`, which immediately tells me this is likely internal to its module or file. That's good, keeps it contained and avoids potential naming collisions. But, hang on... what about the function `__mptcp_retrans`? I need to understand its relationship to the off-order queue. Is it accessing or modifying that queue? I need to quickly recall the purpose of that retransmission function in the MPTCP stack. Is it involved with handling lost segments, or simply scheduling retransmissions? Knowing this will help me determine if this `static` nature of `mptcp_data_queue_ofo` will present any potential issues, like limited visibility or access for retransmissions that might be happening from other parts of the MPTCP subsystem. Time to dig into the code and clarify the relationship between the two. |
| 18/2 | 2026/08/07 16:21 | tool | grepper |
0mArgs:{
"Expression": "__mptcp_retrans",
"PathPrefix": "net/mptcp/protocol.c"
}Results: {
"Output": "net/mptcp/protocol.c=2513=struct sock *mptcp_subflow_get_retrans(struct mptcp_sock *msk)\n--\nnet/mptcp/protocol.c-2547-\nnet/mptcp/protocol.c:2548:bool __mptcp_retransmit_pending_data(struct sock *sk)\nnet/mptcp/protocol.c-2549-{\n--\nnet/mptcp/protocol.c=2632=static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,\n--\nnet/mptcp/protocol.c-2681-\nnet/mptcp/protocol.c:2682:\tneed_push = (flags \u0026 MPTCP_CF_PUSH) \u0026\u0026 __mptcp_retransmit_pending_data(sk);\nnet/mptcp/protocol.c-2683-\tif (!dispose_it) {\n--\nnet/mptcp/protocol.c=2852=static int __mptcp_push_retrans(struct sock *sk, struct mptcp_data_frag *dfrag,\n--\nnet/mptcp/protocol.c-2913-\nnet/mptcp/protocol.c:2914:static void __mptcp_retrans(struct sock *sk)\nnet/mptcp/protocol.c-2915-{\n--\nnet/mptcp/protocol.c-2940-\t\t/* `already_sent` can be 0 for `dfrag` belonging to the RTX\nnet/mptcp/protocol.c:2941:\t\t * queue due to __mptcp_retransmit_pending_data().\nnet/mptcp/protocol.c-2942-\t\t */\n--\nnet/mptcp/protocol.c=3113=static void mptcp_worker(struct work_struct *work)\n--\nnet/mptcp/protocol.c-3150-\tif (test_and_clear_bit(MPTCP_WORK_RTX, \u0026msk-\u003eflags))\nnet/mptcp/protocol.c:3151:\t\t__mptcp_retrans(sk);\nnet/mptcp/protocol.c-3152-\n--\nnet/mptcp/protocol.c=3793=static void mptcp_release_cb(struct sock *sk)\n--\nnet/mptcp/protocol.c-3825-\t\tif (flags \u0026 BIT(MPTCP_RETRANSMIT))\nnet/mptcp/protocol.c:3826:\t\t\t__mptcp_retrans(sk);\nnet/mptcp/protocol.c-3827-\t\tif (spool_bl \u0026\u0026 __mptcp_move_skbs(sk, \u0026skbs, \u0026moved)) {\n"
} |
| 19/2 | 2026/08/07 16:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16017 output: 35 thoughts: 104 Thoughts: **Digging into the MPTCP Retransmission Logic** Okay, so I'm tracing the retransmission path here. My initial focus is on how data retransmission is triggered in the MPTCP stack. It seems that the `mptcp_worker` and `mptcp_release_cb` functions both lead to a call to `__mptcp_retrans`. That's the core. Now, I'm thinking, where else do we see interactions that might influence retransmission? Let's check `mptcp_incoming_options`. Ah, it's called from `mptcp_subflow_discard_data` and a few other places. Interesting. Could this play a role in how the stack handles incoming data during a retransmission event? It's worth further investigation to see how this fits into the bigger picture. Next, I need to check `mptcp_pm_chk_stale`, I remember that it's called from `__mptcp_retrans` as well. That confirms my suspicion that it's tightly coupled with the retransmission process. This probably relates to stale path management or something similar. Finally, I need to check what happens in `mptcp_recvmsg`. It's essential to understand the data receiving and processing side during retransmission. |
| 20/2 | 2026/08/07 16:22 | tool | grepper |
0mArgs:{
"Expression": "mptcp_recvmsg",
"PathPrefix": "net/mptcp/protocol.c"
}Results: {
"Output": "net/mptcp/protocol.c=2130=static void mptcp_eat_recv_skb(struct sock *sk, struct sk_buff *skb)\n--\nnet/mptcp/protocol.c-2140-\nnet/mptcp/protocol.c:2141:static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,\nnet/mptcp/protocol.c-2142-\t\t\t\tsize_t len, int flags, int copied_total,\n--\nnet/mptcp/protocol.c=2347=static unsigned int mptcp_inq_hint(const struct sock *sk)\n--\nnet/mptcp/protocol.c-2367-\nnet/mptcp/protocol.c:2368:static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,\nnet/mptcp/protocol.c-2369-\t\t\t int flags)\n--\nnet/mptcp/protocol.c-2400-\nnet/mptcp/protocol.c:2401:\t\tbytes_read = __mptcp_recvmsg_mskq(sk, msg, len - copied, flags,\nnet/mptcp/protocol.c-2402-\t\t\t\t\t\t copied, \u0026tss, \u0026cmsg_flags,\n--\nnet/mptcp/protocol.c=4196=static struct proto mptcp_prot = {\n--\nnet/mptcp/protocol.c-4208-\t.ioctl\t\t= mptcp_ioctl,\nnet/mptcp/protocol.c:4209:\t.recvmsg\t= mptcp_recvmsg,\nnet/mptcp/protocol.c-4210-\t.release_cb\t= mptcp_release_cb,\n"
} |
| 21/2 | 2026/08/07 16:22 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16904 output: 33 thoughts: 82 Thoughts: Here's my summary, as though I'm thinking through this process: **MPTCP Entry Point Investigation** Okay, so I'm diving into this MPTCP code. From what I recall, `mptcp_recvmsg` is definitely a key starting point, and I think `mptcp_incoming_options` is another solid entry point to consider. I also recall the `mptcp_worker` function - although it's likely a workqueue function and not a *direct* entry point per se, understanding its role is going to be important in the overall flow, so I'll keep that in mind. Right, let's start with `mptcp_incoming_options`. I seem to recall that one's defined in `net/mptcp/options.c`. Let's take a look there and see what's happening. I remember this area being pretty complex, but `mptcp_incoming_options` *should* give me some insight into how options are handled when data arrives. |
| 22/2 | 2026/08/07 16:22 | tool | grepper |
0mArgs:{
"Expression": "mptcp_incoming_options",
"PathPrefix": "net/mptcp/"
}Results: {
"Output": "net/mptcp/options.c=1116=static void ack_update_msk(struct mptcp_sock *msk,\n--\nnet/mptcp/options.c-1141-\nnet/mptcp/options.c:1142:\t/* this assumes mptcp_incoming_options() is invoked after tcp_ack() */\nnet/mptcp/options.c-1143-\tif (after64(msk-\u003ewnd_end, snd_nxt))\n--\nnet/mptcp/options.c=1193=static bool mptcp_over_limit(struct sock *sk, struct sock *ssk,\n--\nnet/mptcp/options.c-1221- */\nnet/mptcp/options.c:1222:bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)\nnet/mptcp/options.c-1223-{\n"
} |
| 23/2 | 2026/08/07 16:22 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17484 output: 33 thoughts: 13 Thoughts: Okay, here's my attempt at summarizing my thought process on finding the calls to `mptcp_incoming_options`, assuming I'm an expert: **Tracing the mptcp_incoming_options Function** Right, so I need to figure out where `mptcp_incoming_options` is being used. That means I need to understand the network code that handles Multipath TCP (MPTCP) option negotiation. I know `mptcp_incoming_options` is a key function for parsing and handling incoming MPTCP options. So, I need to trace its call sites to grasp how the MPTCP stack processes these incoming messages. My mental checklist starts with the network stack's entry points. I'd first look at the usual suspects: `inet_rcv_skb` or its MPTCP-specific counterparts. I'm going to quickly skim the relevant code paths, likely involving the raw socket handling, or even better, the `netfilter` subsystem, where such options might get preprocessed. I'd be looking for hooks or callbacks that could trigger option processing. I need to see how the MPTCP stack intercepts and processes incoming data, especially the parts concerned with option parsing. That's my main area of focus. I’ll start by using my existing knowledge of MPTCP structures and functions to guide my search. I would be using `grep`, `cscope`, or my IDE's symbol lookup to search for all instances where `mptcp_incoming_options` is invoked, and then analyzing each call site in the broader context of the MPTCP connection setup and data transfer flow. I'm thinking of focusing on any function calls after socket creation, and specifically those where options are examined on incoming packets. This will give me a good understanding of the control flow around that function, and what its role is in the MPTCP stack. |
| 24/2 | 2026/08/07 16:22 | tool | grepper |
0mArgs:{
"Expression": "mptcp_incoming_options",
"PathPrefix": "net/mptcp/"
}Results: {
"Output": "net/mptcp/options.c=1116=static void ack_update_msk(struct mptcp_sock *msk,\n--\nnet/mptcp/options.c-1141-\nnet/mptcp/options.c:1142:\t/* this assumes mptcp_incoming_options() is invoked after tcp_ack() */\nnet/mptcp/options.c-1143-\tif (after64(msk-\u003ewnd_end, snd_nxt))\n--\nnet/mptcp/options.c=1193=static bool mptcp_over_limit(struct sock *sk, struct sock *ssk,\n--\nnet/mptcp/options.c-1221- */\nnet/mptcp/options.c:1222:bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)\nnet/mptcp/options.c-1223-{\n"
} |
| 25/2 | 2026/08/07 16:22 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18172 output: 37 thoughts: 49 Thoughts: Okay, here's my summarized thought process, assuming my expertise: **Expanding the Scope of My Investigation** Alright, hang on a second. I was initially focused on `net/mptcp/` for that grep, but that's potentially too narrow. I need to broaden the search to encompass the likely areas where this could be handled at a lower level or in a related function. Should I check `net/ipv4/` and `net/ipv6/`? Those are the core protocol stacks, so it's entirely possible the critical logic resides there. Or perhaps it's related to the actual implementation of the MPTCP subflows, in which case `net/mptcp/subflow.c` is the most obvious place. I need to be methodical and exhaust the plausible locations for this issue. This feels like I might have jumped the gun with the first grep. I need a more complete picture. |
| 26/2 | 2026/08/07 16:22 | tool | grepper |
0mError:no matches Args: {
"Expression": "mptcp_incoming_options",
"PathPrefix": "net/mptcp/subflow.c"
}Results: {
"error": "no matches"
} |
| 27/2 | 2026/08/07 16:22 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18467 output: 27 thoughts: 9 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue: **Navigating the Codebase: A `grep` Strategy** Alright, I'm facing a problem where I need to find something. It could be a specific function call, a variable definition, a particular string literal, or maybe even just a pattern of code. The problem is, I don't know *exactly* where it lives within this codebase. It's a large project, and manually searching through files one by one would be insanely inefficient and prone to error. My instinct, as always, leans towards automation and efficiency. Instead of that brute-force manual approach, I immediately think, *`grep` is the tool for the job*. I'm picturing myself running `grep`... and immediately I know I need to specify the search across the *entire* directory tree. I need to cast a wide net to ensure I don’t miss any potential occurrences, wherever they might be hidden. So, the mental image is of running a recursive `grep` across the entire project structure. This is the optimal approach for a comprehensive search; I can rely on `grep` to quickly scan all files, directories, and subdirectories. This way I can find the information in a relatively short timeframe and get on with the real work. |
| 28/2 | 2026/08/07 16:22 | tool | grepper |
0mArgs:{
"Expression": "mptcp_incoming_options",
"PathPrefix": ""
}Results: {
"Output": "include/net/mptcp.h=156=int mptcp_established_options(struct sock *sk, struct sk_buff *skb,\n--\ninclude/net/mptcp.h-158-\t\t\t struct mptcp_out_options *opts);\ninclude/net/mptcp.h:159:bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb);\ninclude/net/mptcp.h-160-\n--\ninclude/net/mptcp.h=266=static inline bool mptcp_synack_options(const struct request_sock *req,\n--\ninclude/net/mptcp.h-272-\ninclude/net/mptcp.h:273:static inline bool mptcp_incoming_options(struct sock *sk,\ninclude/net/mptcp.h-274-\t\t\t\t\t struct sk_buff *skb)\n--\nnet/ipv4/tcp_input.c=4884=void tcp_reset(struct sock *sk, struct sk_buff *skb)\n--\nnet/ipv4/tcp_input.c-4890-\t/* mptcp can't tell us to ignore reset pkts,\nnet/ipv4/tcp_input.c:4891:\t * so just ignore the return value of mptcp_incoming_options().\nnet/ipv4/tcp_input.c-4892-\t */\nnet/ipv4/tcp_input.c-4893-\tif (sk_is_mptcp(sk))\nnet/ipv4/tcp_input.c:4894:\t\tmptcp_incoming_options(sk, skb);\nnet/ipv4/tcp_input.c-4895-\n--\nnet/ipv4/tcp_input.c=5607=static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)\n--\nnet/ipv4/tcp_input.c-5616-\t */\nnet/ipv4/tcp_input.c:5617:\tif (sk_is_mptcp(sk) \u0026\u0026 !mptcp_incoming_options(sk, skb)) {\nnet/ipv4/tcp_input.c-5618-\t\t__kfree_skb(skb);\n--\nnet/ipv4/tcp_input.c=7152=tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)\n--\nnet/ipv4/tcp_input.c-7379-\t\t\t */\nnet/ipv4/tcp_input.c:7380:\t\t\tif (sk_is_mptcp(sk) \u0026\u0026 !mptcp_incoming_options(sk, skb))\nnet/ipv4/tcp_input.c-7381-\t\t\t\tgoto discard;\n--\nnet/mptcp/options.c=1116=static void ack_update_msk(struct mptcp_sock *msk,\n--\nnet/mptcp/options.c-1141-\nnet/mptcp/options.c:1142:\t/* this assumes mptcp_incoming_options() is invoked after tcp_ack() */\nnet/mptcp/options.c-1143-\tif (after64(msk-\u003ewnd_end, snd_nxt))\n--\nnet/mptcp/options.c=1193=static bool mptcp_over_limit(struct sock *sk, struct sock *ssk,\n--\nnet/mptcp/options.c-1221- */\nnet/mptcp/options.c:1222:bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)\nnet/mptcp/options.c-1223-{\n"
} |
| 29/2 | 2026/08/07 16:22 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19732 output: 30 thoughts: 61 Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience: **Analyzing MPTCP Option Handling in the Network Stack** Right, so `mptcp_incoming_options` is triggered directly from the core TCP data path, specifically within `tcp_data_queue` and `tcp_rcv_state_process`. That instantly confirms its importance and reachability from the network stack's processing of incoming segments. I need to be aware of all the paths that can possibly be taken. Now, let's switch gears and focus on the `mptcp_pm_chk_stale` function. I will check for stale flows now. |
| 30/2 | 2026/08/07 16:22 | tool | grepper |
0mArgs:{
"Expression": "mptcp_pm_chk_stale",
"PathPrefix": ""
}Results: {
"Output": "net/mptcp/pm.c=1069=mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)\n--\nnet/mptcp/pm.c-1105-\nnet/mptcp/pm.c:1106:void mptcp_pm_chk_stale(const struct mptcp_sock *msk)\nnet/mptcp/pm.c-1107-{\n--\nnet/mptcp/protocol.c=2914=static void __mptcp_retrans(struct sock *sk)\n--\nnet/mptcp/protocol.c-2922-\nnet/mptcp/protocol.c:2923:\tmptcp_pm_chk_stale(msk);\nnet/mptcp/protocol.c-2924-\n--\nnet/mptcp/protocol.h=1106=bool mptcp_pm_addr_families_match(const struct sock *sk,\n--\nnet/mptcp/protocol.h-1108-\t\t\t\t const struct mptcp_addr_info *rem);\nnet/mptcp/protocol.h:1109:void mptcp_pm_chk_stale(const struct mptcp_sock *msk);\nnet/mptcp/protocol.h-1110-void mptcp_pm_new_connection(struct mptcp_sock *msk, const struct sock *ssk, int server_side);\n"
} |
| 31/2 | 2026/08/07 16:22 | llm | patch-evaluator |
9mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20346 output: 35 thoughts: 90 Thoughts: Okay, let's break this down. **Tracing the MPTCP Control Flow** Right, so `mptcp_pm_chk_stale` – that's the function we're interested in – is called from `__mptcp_retrans`. Noted. Now, where does `__mptcp_retrans` fit into the picture? Ah, it's called from two key places: `mptcp_worker` and `mptcp_release_cb`. That gives us a good starting point for understanding its role in the retransmission process and how it interacts with the MP-TCP worker threads and connection release procedures. Now, let's shift gears and look at how user space interacts. I need to confirm how the core user space interaction functions are called. `mptcp_recvmsg`... Yes, absolutely, that one's definitely reachable from user space. Check. And what about `mptcp_sendmsg`? That's going to be key for how data gets into the kernel in the first place, and I will want to see how that interacts with my suspected area of concern. Excellent. |
| 32/2 | 2026/08/07 16:32 | tool | grepper |
0mArgs:{
"Expression": "mptcp_sendmsg",
"PathPrefix": "net/mptcp/protocol.c"
}Results: {
"Output": "net/mptcp/protocol.c=1255=mptcp_carve_data_frag(const struct mptcp_sock *msk, struct page_frag *pfrag,\n--\nnet/mptcp/protocol.c-1272-\nnet/mptcp/protocol.c:1273:struct mptcp_sendmsg_info {\nnet/mptcp/protocol.c-1274-\tint mss_now;\n--\nnet/mptcp/protocol.c=1368=static void mptcp_update_infinite_map(struct mptcp_sock *msk,\n--\nnet/mptcp/protocol.c-1388-\nnet/mptcp/protocol.c:1389:static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk,\nnet/mptcp/protocol.c-1390-\t\t\t struct mptcp_data_frag *dfrag,\nnet/mptcp/protocol.c:1391:\t\t\t struct mptcp_sendmsg_info *info)\nnet/mptcp/protocol.c-1392-{\n--\nnet/mptcp/protocol.c-1527-\t\tmptcp_update_infinite_map(msk, ssk, mpext);\nnet/mptcp/protocol.c:1528:\ttrace_mptcp_sendmsg_frag(mpext);\nnet/mptcp/protocol.c-1529-\tmptcp_subflow_ctx(ssk)-\u003erel_write_seq += copy;\n--\nnet/mptcp/protocol.c=1581=struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)\n--\nnet/mptcp/protocol.c-1656-\nnet/mptcp/protocol.c:1657:static void mptcp_push_release(struct sock *ssk, struct mptcp_sendmsg_info *info)\nnet/mptcp/protocol.c-1658-{\n--\nnet/mptcp/protocol.c=1699=static int __subflow_push_pending(struct sock *sk, struct sock *ssk,\nnet/mptcp/protocol.c:1700:\t\t\t\t struct mptcp_sendmsg_info *info)\nnet/mptcp/protocol.c-1701-{\n--\nnet/mptcp/protocol.c-1712-\nnet/mptcp/protocol.c:1713:\t\t\tret = mptcp_sendmsg_frag(sk, ssk, dfrag, info);\nnet/mptcp/protocol.c-1714-\t\t\tif (ret \u003c= 0) {\n--\nnet/mptcp/protocol.c=1743=void __mptcp_push_pending(struct sock *sk, unsigned int flags)\n--\nnet/mptcp/protocol.c-1746-\tstruct mptcp_sock *msk = mptcp_sk(sk);\nnet/mptcp/protocol.c:1747:\tstruct mptcp_sendmsg_info info = {\nnet/mptcp/protocol.c-1748-\t\t\t\t.flags = flags,\n--\nnet/mptcp/protocol.c=1811=static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk, bool first)\n--\nnet/mptcp/protocol.c-1813-\tstruct mptcp_sock *msk = mptcp_sk(sk);\nnet/mptcp/protocol.c:1814:\tstruct mptcp_sendmsg_info info = {\nnet/mptcp/protocol.c-1815-\t\t.data_lock_held = true,\n--\nnet/mptcp/protocol.c=1878=static int mptcp_disconnect(struct sock *sk, int flags);\nnet/mptcp/protocol.c-1879-\nnet/mptcp/protocol.c:1880:static int mptcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg,\nnet/mptcp/protocol.c-1881-\t\t\t\t size_t len, int *copied_syn)\n--\nnet/mptcp/protocol.c=1974=static void mptcp_rps_record_subflows(const struct mptcp_sock *msk)\n--\nnet/mptcp/protocol.c-1987-\nnet/mptcp/protocol.c:1988:static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)\nnet/mptcp/protocol.c-1989-{\n--\nnet/mptcp/protocol.c-2007-\nnet/mptcp/protocol.c:2008:\t\tret = mptcp_sendmsg_fastopen(sk, msg, len, \u0026copied_syn);\nnet/mptcp/protocol.c-2009-\t\tcopied += copied_syn;\n--\nnet/mptcp/protocol.c=2852=static int __mptcp_push_retrans(struct sock *sk, struct mptcp_data_frag *dfrag,\n--\nnet/mptcp/protocol.c-2854-{\nnet/mptcp/protocol.c:2855:\tstruct mptcp_sendmsg_info info = { .data_lock_held = true, };\nnet/mptcp/protocol.c-2856-\tstruct mptcp_sock *msk = mptcp_sk(sk);\n--\nnet/mptcp/protocol.c-2891-\t\t\twhile (info.sent \u003c info.limit) {\nnet/mptcp/protocol.c:2892:\t\t\t\tret = mptcp_sendmsg_frag(sk, ssk, dfrag, \u0026info);\nnet/mptcp/protocol.c-2893-\t\t\t\tif (ret \u003c= 0)\n--\nnet/mptcp/protocol.c=4196=static struct proto mptcp_prot = {\n--\nnet/mptcp/protocol.c-4206-\t.destroy\t= mptcp_destroy,\nnet/mptcp/protocol.c:4207:\t.sendmsg\t= mptcp_sendmsg,\nnet/mptcp/protocol.c-4208-\t.ioctl\t\t= mptcp_ioctl,\n"
} |
| 33/2 | 2026/08/07 16:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 22320 output: 37 thoughts: 228 Thoughts: **Analyzing the MPTCP Code Changes** Okay, so I'm looking at this patch and tracing the key entry points for user-space interaction: `mptcp_sendmsg` and `mptcp_recvmsg`. That's where it all begins and ends from the application's perspective. From there, I see the ripple effect of the changes. The core of the modifications seems to be in several key areas. First, `mptcp_incoming_options`, which is crucial as it's directly integrated into the TCP receive path. That's a critical point to consider. We know how that impacts the overall state of the connection based on received options. Then there's `__mptcp_retrans`. This is interesting as it's triggered from the `mptcp_worker` and `mptcp_release_cb` paths. So, it's involved in handling retransmissions and the release process of resources. The modifications to `mptcp_pm_chk_stale`, which is called within `__mptcp_retrans`, are also important. This suggests a focus on improving the stale path management, particularly in the retransmission context. I see that `mptcp_data_queue_ofo` is also being touched, and that's called from `__mptcp_move_skb`. `__mptcp_move_skb` itself gets called from two critical functions: `__mptcp_move_skbs_from_subflow` and `__mptcp_move_skbs`. This points toward changes in how we're handling the movement of Socket Buffer (skb) structures between subflows and within the overall MPTCP connection, specifically related to out-of-order data. Finally, `__mptcp_add_backlog` is modified, which is called directly from `mptcp_add_backlog`. I'll have to take a closer look at that function specifically. Let's see what's happening there. |
| 34/2 | 2026/08/07 16:32 | tool | grepper |
0mArgs:{
"Expression": "mptcp_add_backlog",
"PathPrefix": "net/mptcp/protocol.c"
}Results: {
"Output": "net/mptcp/protocol.c=734=static void mptcp_dss_corruption(struct mptcp_sock *msk, struct sock *ssk)\n--\nnet/mptcp/protocol.c-741-\nnet/mptcp/protocol.c:742:static void __mptcp_add_backlog(struct sock *sk,\nnet/mptcp/protocol.c-743-\t\t\t\tstruct mptcp_subflow_context *subflow,\n--\nnet/mptcp/protocol.c=795=static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk,\n--\nnet/mptcp/protocol.c-842-\t\t\t} else {\nnet/mptcp/protocol.c:843:\t\t\t\t__mptcp_add_backlog(sk, subflow, skb);\nnet/mptcp/protocol.c-844-\t\t\t}\n"
} |
| 35/2 | 2026/08/07 16:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 23266 output: 43 thoughts: 54 Thoughts: Okay, here's my take on those thoughts, framed as my own mental processing as an expert in the field: **Tracing the MPTCP Data Flow** Alright, let's see, I'm tracking the data flow in MPTCP, and I've zeroed in on `__mptcp_move_skbs_from_subflow`. I know that's where `__mptcp_add_backlog` gets invoked. The key question now becomes, *where* does `__mptcp_move_skbs_from_subflow` itself get called? Understanding its call sites will give me the bigger picture. I need to trace this function to see how the SKBs are moving and where the backlog is actually being managed. Pinpointing this will help me understand the overall flow in MPTCP. |
| 36/2 | 2026/08/07 16:32 | tool | grepper |
0mArgs:{
"Expression": "__mptcp_move_skbs_from_subflow",
"PathPrefix": "net/mptcp/protocol.c"
}Results: {
"Output": "net/mptcp/protocol.c=742=static void __mptcp_add_backlog(struct sock *sk,\n--\nnet/mptcp/protocol.c-794-\nnet/mptcp/protocol.c:795:static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk,\nnet/mptcp/protocol.c-796-\t\t\t\t\t struct sock *ssk, bool own_msk)\n--\nnet/mptcp/protocol.c=959=static bool move_skbs_to_msk(struct mptcp_sock *msk, struct sock *ssk)\n--\nnet/mptcp/protocol.c-963-\nnet/mptcp/protocol.c:964:\tmoved = __mptcp_move_skbs_from_subflow(msk, ssk, true);\nnet/mptcp/protocol.c-965-\t__mptcp_ofo_queue(msk);\n--\nnet/mptcp/protocol.c=1005=void mptcp_data_ready(struct sock *sk, struct sock *ssk)\n--\nnet/mptcp/protocol.c-1023-\t} else {\nnet/mptcp/protocol.c:1024:\t\t__mptcp_move_skbs_from_subflow(msk, ssk, false);\nnet/mptcp/protocol.c-1025-\t}\n"
} |
| 37/2 | 2026/08/07 16:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 23912 output: 33 thoughts: 137 Thoughts: **Analyzing the Reach of Proposed Changes** Okay, so the `mptcp_data_ready` function is triggered by TCP data ready callbacks. That's good; it means any changes here will likely have a pretty broad impact, touching a significant portion of standard TCP and MPTCP operations. It makes sense to focus on the key functions to understand the propagation of those effects. Right, let me summarize those: `mptcp_incoming_options`, `mptcp_pm_chk_stale`, `mptcp_recvmsg`, `mptcp_sendmsg`, and then, of course, the ever-important `mptcp_data_ready`. Hang on a second… `mptcp_data_ready` itself is a really attractive entry point. It's the point where everything kind of converges. Let me just quickly confirm that `mptcp_data_ready` isn't declared as a static function. Knowing this will tell me how easily I can hook into that routine. |
| 38/2 | 2026/08/07 16:32 | tool | grepper |
0mArgs:{
"Expression": "mptcp_data_ready",
"PathPrefix": "net/mptcp/"
}Results: {
"Output": "net/mptcp/protocol.c=979=static void mptcp_rcv_rtt_update(struct mptcp_sock *msk,\n--\nnet/mptcp/protocol.c-1004-\nnet/mptcp/protocol.c:1005:void mptcp_data_ready(struct sock *sk, struct sock *ssk)\nnet/mptcp/protocol.c-1006-{\n--\nnet/mptcp/protocol.h=961=static inline u64 mptcp_stamp(void)\n--\nnet/mptcp/protocol.h-965-\nnet/mptcp/protocol.h:966:void mptcp_data_ready(struct sock *sk, struct sock *ssk);\nnet/mptcp/protocol.h-967-bool mptcp_finish_join(struct sock *sk);\n--\nnet/mptcp/subflow.c=1501=static void subflow_data_ready(struct sock *sk)\n--\nnet/mptcp/subflow.c-1525-\tif (mptcp_subflow_data_available(sk)) {\nnet/mptcp/subflow.c:1526:\t\tmptcp_data_ready(parent, sk);\nnet/mptcp/subflow.c-1527-\n--\nnet/mptcp/subflow.c=1869=static void subflow_state_change(struct sock *sk)\n--\nnet/mptcp/subflow.c-1880-\tif (mptcp_subflow_data_available(sk))\nnet/mptcp/subflow.c:1881:\t\tmptcp_data_ready(parent, sk);\nnet/mptcp/subflow.c-1882-\telse if (unlikely(sk-\u003esk_err))\n"
} |
| 39/2 | 2026/08/07 16:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 24771 output: 40 thoughts: 311 Thoughts: **Analyzing Non-Static Function Modifications in the MPTCP Patch** Okay, so I'm digging into this patch. I see that `mptcp_data_ready` is definitely non-static – that much is clear. But I need to systematically review this to understand the impact fully. Let's see... `mptcp_pm_chk_stale` is also non-static. And then there's `mptcp_incoming_options`, also non-static. Good, I'm getting a handle on the pattern. Time to broaden the search. I need to make sure I haven't missed anything else in the modified functions that are non-static. Ah, let's see, `mptcp_pm_chk_stale` is in `net/mptcp/pm.c` and `mptcp_incoming_options` is in `net/mptcp/options.c` — that's helpful context. Okay, here's one, `__mptcp_push_pending` is in `net/mptcp/protocol.c`. Wait...no, `__mptcp_push_pending` *wasn't* modified, that's a relief! Okay, summarizing the modified functions, and their static-ness, the patch actually changed: * `mptcp_incoming_options` (non-static) * `mptcp_pm_chk_stale` (non-static) * `mptcp_subflow_get_retrans` (non-static) * `mptcp_release_cb` (static) * `__mptcp_retrans` (static) * `__mptcp_move_skb` (static) * `mptcp_data_queue_ofo` (static) * `__mptcp_add_backlog` (static) * `mptcp_move_skbs` (static) Now, I'm going to focus my attention on `mptcp_subflow_get_retrans`. |
| 40/2 | 2026/08/07 16:32 | tool | grepper |
0mArgs:{
"Expression": "mptcp_subflow_get_retrans",
"PathPrefix": "net/mptcp/protocol.c"
}Results: {
"Output": "net/mptcp/protocol.c=2498=static void mptcp_tout_timer(struct timer_list *t)\n--\nnet/mptcp/protocol.c-2512- */\nnet/mptcp/protocol.c:2513:struct sock *mptcp_subflow_get_retrans(struct mptcp_sock *msk)\nnet/mptcp/protocol.c-2514-{\n"
} |
| 41/2 | 2026/08/07 16:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25707 output: 34 thoughts: 265 Thoughts: **Analyzing MPTCP Function Scope** Okay, so I'm looking at these MPTCP functions, specifically trying to nail down how they're exposed and where the modifications are happening. My initial thought was to check for a focus on these symbols: `mptcp_incoming_options`, `mptcp_pm_chk_stale`, `mptcp_subflow_get_retrans`, `mptcp_data_ready`, `mptcp_recvmsg`, and `mptcp_sendmsg`. Then it clicked: `mptcp_data_ready`, `mptcp_recvmsg`, and `mptcp_sendmsg` aren't *directly* modified in the sense that they're the entry points that call the other functions. `mptcp_incoming_options`, `mptcp_pm_chk_stale`, and `mptcp_subflow_get_retrans` are the key functions to investigate further, since they are directly modified. A quick check confirms `mptcp_subflow_get_retrans` is indeed non-static and visible, so I know I'll have to investigate that further. Same with `mptcp_pm_chk_stale`. Now I should check to see if there are any calls to `mptcp_worker` which is the background processing function. |
| 42/2 | 2026/08/07 16:32 | tool | grepper |
0mArgs:{
"Expression": "mptcp_worker",
"PathPrefix": "net/mptcp/protocol.c"
}Results: {
"Output": "net/mptcp/protocol.c=1091=bool mptcp_schedule_work(struct sock *sk)\n--\nnet/mptcp/protocol.c-1095-\nnet/mptcp/protocol.c:1096:\t/* Get a reference on this socket, mptcp_worker() will release it.\nnet/mptcp/protocol.c:1097:\t * As mptcp_worker() might complete before us, we can not avoid\nnet/mptcp/protocol.c-1098-\t * a sock_hold()/sock_put() if schedule_work() returns false.\n--\nnet/mptcp/protocol.c=2632=static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,\n--\nnet/mptcp/protocol.c-2667-\t (sock_flag(sk, SOCK_DEAD) || sock_flag(ssk, SOCK_DEAD))) {\nnet/mptcp/protocol.c:2668:\t\t/* ensure later check in mptcp_worker() will dispose the msk */\nnet/mptcp/protocol.c-2669-\t\tsock_set_flag(sk, SOCK_DEAD);\n--\nnet/mptcp/protocol.c=2799=static void mptcp_check_fastclose(struct mptcp_sock *msk)\n--\nnet/mptcp/protocol.c-2839-\nnet/mptcp/protocol.c:2840:\t/* the calling mptcp_worker will properly destroy the socket */\nnet/mptcp/protocol.c-2841-\tif (sock_flag(sk, SOCK_DEAD))\n--\nnet/mptcp/protocol.c=3077=static void mptcp_do_fastclose(struct sock *sk)\n--\nnet/mptcp/protocol.c-3112-\nnet/mptcp/protocol.c:3113:static void mptcp_worker(struct work_struct *work)\nnet/mptcp/protocol.c-3114-{\n--\nnet/mptcp/protocol.c=3162=static void __mptcp_init_sock(struct sock *sk)\n--\nnet/mptcp/protocol.c-3169-\tINIT_LIST_HEAD(\u0026msk-\u003ebacklog_list);\nnet/mptcp/protocol.c:3170:\tINIT_WORK(\u0026msk-\u003ework, mptcp_worker);\nnet/mptcp/protocol.c-3171-\tmsk-\u003eout_of_order_queue = RB_ROOT;\n"
} |
| 43/2 | 2026/08/07 16:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 26953 output: 34 thoughts: 303 Thoughts: **Analyzing MPTCP Function Calls and Configuration** Okay, so I'm looking at this `mptcp_worker` function – it's static. That's good to know. Now, let's take a look at `mptcp_release_cb`. It's also static, which means my focus needs to be on the non-static functions that either directly modify or call it. Right, so, from what I can see, those functions are: `mptcp_incoming_options`, `mptcp_pm_chk_stale`, `mptcp_subflow_get_retrans`, `mptcp_recvmsg`, and `mptcp_sendmsg`. Ah, and it looks like `mptcp_recvmsg` and `mptcp_sendmsg` call `mptcp_release_cb` indirectly through `release_sock()`. That makes sense; they're the primary entry points for MPTCP sockets. Given all of that, I should concentrate on those five functions: `mptcp_incoming_options`, `mptcp_pm_chk_stale`, `mptcp_subflow_get_retrans`, `mptcp_recvmsg`, and `mptcp_sendmsg`. That's where I need to look closer. Right, time to check the configurations. `MPTCP` is the main one, obviously. And `MPTCP_IPV6` would be a good one to have as well, definitely. Any other crucial configurations to consider? Let me just double-check the `net/mptcp/Kconfig` file again to be completely sure. |
| 44/2 | 2026/08/07 16:32 | tool | read-file |
0mArgs:{
"File": "net/mptcp/Kconfig",
"FirstLine": 1,
"LineCount": 40
}Results: {
"Contents": " 1:\t\n 2:\tconfig MPTCP\n 3:\t\tbool \"MPTCP: Multipath TCP\"\n 4:\t\tdepends on INET\n 5:\t\tselect SKB_EXTENSIONS\n 6:\t\tselect CRYPTO_LIB_SHA256\n 7:\t\tselect CRYPTO_LIB_UTILS\n 8:\t\thelp\n 9:\t\t Multipath TCP (MPTCP) connections send and receive data over multiple\n 10:\t\t subflows in order to utilize multiple network paths. Each subflow\n 11:\t\t uses the TCP protocol, and TCP options carry header information for\n 12:\t\t MPTCP.\n 13:\t\n 14:\tif MPTCP\n 15:\t\n 16:\tconfig INET_MPTCP_DIAG\n 17:\t\tdepends on INET_DIAG\n 18:\t\tdef_tristate INET_DIAG\n 19:\t\n 20:\tconfig MPTCP_IPV6\n 21:\t\tbool \"MPTCP: IPv6 support for Multipath TCP\"\n 22:\t\tdepends on IPV6=y\n 23:\t\tdefault y\n 24:\t\n 25:\tconfig MPTCP_KUNIT_TEST\n 26:\t\ttristate \"This builds the MPTCP KUnit tests\" if !KUNIT_ALL_TESTS\n 27:\t\tdepends on KUNIT\n 28:\t\tdefault KUNIT_ALL_TESTS\n 29:\t\thelp\n 30:\t\t Currently covers the MPTCP crypto and token helpers.\n 31:\t\t Only useful for kernel devs running KUnit test harness and are not\n 32:\t\t for inclusion into a production build.\n 33:\t\n 34:\t\t For more information on KUnit and unit tests in general please refer\n 35:\t\t to the KUnit documentation in Documentation/dev-tools/kunit/.\n 36:\t\n 37:\t\t If unsure, say N.\n 38:\t\n 39:\tendif\n"
} |
| 45/2 | 2026/08/07 16:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28147 output: 39 thoughts: 125 Thoughts: **Analyzing MPTCP Configuration and Function Calls** Okay, so I'm looking at these configurations, `MPTCP` and `MPTCP_IPV6`. I need to understand the full scope of the changes. Beyond these configs, what else has been touched? Let me do a quick audit of the modified functions. Alright, `mptcp_pm_chk_stale` is in `net/mptcp/pm.c`. I'll want to revisit that one. Then there's `mptcp_incoming_options` in `net/mptcp/options.c` and `mptcp_subflow_get_retrans` in `net/mptcp/protocol.c`. I'll definitely need to circle back to `mptcp_pm_chk_stale`; that one seems particularly relevant. |
| 46/2 | 2026/08/07 16:33 | tool | grepper |
0mArgs:{
"Expression": "mptcp_pm_chk_stale",
"PathPrefix": "net/mptcp/pm.c"
}Results: {
"Output": "net/mptcp/pm.c=1069=mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)\n--\nnet/mptcp/pm.c-1105-\nnet/mptcp/pm.c:1106:void mptcp_pm_chk_stale(const struct mptcp_sock *msk)\nnet/mptcp/pm.c-1107-{\n"
} |
| 47/2 | 2026/08/07 16:33 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28623 output: 40 thoughts: 17 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing `mptcp_subflow_get_retrans`** Okay, time to dive into `mptcp_subflow_get_retrans`. I need to understand how the MultiPath TCP stack handles retransmissions on subflows. Specifically, I want to see how the system is designed to acquire the count. This is a crucial metric when considering congestion control and overall performance. I need to understand if the returned value from this function correctly represents the state and if it is used correctly in the bigger picture. Are there any potential concurrency issues, race conditions, or locking mechanisms I need to keep in mind related to the count update and retrieval? It's essential to pinpoint where, when, and how this function is called within the retransmission logic. Let's start with the code. |
| 48/2 | 2026/08/07 16:33 | tool | grepper |
0mArgs:{
"Expression": "mptcp_subflow_get_retrans",
"PathPrefix": "net/mptcp/protocol.c"
}Results: {
"Output": "net/mptcp/protocol.c=2498=static void mptcp_tout_timer(struct timer_list *t)\n--\nnet/mptcp/protocol.c-2512- */\nnet/mptcp/protocol.c:2513:struct sock *mptcp_subflow_get_retrans(struct mptcp_sock *msk)\nnet/mptcp/protocol.c-2514-{\n"
} |
| 49/2 | 2026/08/07 16:33 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28985 output: 36 thoughts: 13 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Analyzing `mptcp_incoming_options`** Alright, let's dive into this `mptcp_incoming_options` situation. My first instinct is to see what's actually *in* this thing. What's the context? Is it related to the core MP-TCP negotiation or more downstream? What kind of options are we talking about here? I need to quickly determine if we're looking at standard MP-TCP options like `MP_CAPABLE`, `MP_JOIN`, etc., or something more application-specific that might be flowing through the MPTCP layer. I immediately think about parsing and validation. Is there a defined structure? Are there potential buffer overflows or other security vulnerabilities I should be looking for? The implementation of this part of the code could be crucial for overall system stability and performance so I am interested in looking at the details. We should check this thoroughly. Let's start digging. |
| 50/2 | 2026/08/07 16:33 | tool | grepper |
0mArgs:{
"Expression": "mptcp_incoming_options",
"PathPrefix": "net/mptcp/options.c"
}Results: {
"Output": "net/mptcp/options.c=1116=static void ack_update_msk(struct mptcp_sock *msk,\n--\nnet/mptcp/options.c-1141-\nnet/mptcp/options.c:1142:\t/* this assumes mptcp_incoming_options() is invoked after tcp_ack() */\nnet/mptcp/options.c-1143-\tif (after64(msk-\u003ewnd_end, snd_nxt))\n--\nnet/mptcp/options.c=1193=static bool mptcp_over_limit(struct sock *sk, struct sock *ssk,\n--\nnet/mptcp/options.c-1221- */\nnet/mptcp/options.c:1222:bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)\nnet/mptcp/options.c-1223-{\n"
} |
| 51/2 | 2026/08/07 16:33 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 29490 output: 36 thoughts: 13 Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert: **Investigating `mptcp_data_ready`** Right, let's dive into this. The mental checklist starts with `mptcp_data_ready`. It's the first place to look. I need to refresh my understanding of where this function sits in the MPTCP data flow. What are its dependencies? What triggers it? What are the common pitfalls I should immediately consider, given the context of what we are troubleshooting? Is it related to any specific congestion control mechanisms? What happens if the network conditions change rapidly? Is it accurately reflecting the availability of data in the subflows, or could there be a subtle synchronization issue lurking? And, of course, the ever-present question: have I checked the obvious? Any recent code changes in the area? Are there any compiler optimisations potentially affecting it? Let's get to the bottom of this. |
| 52/2 | 2026/08/07 16:33 | tool | grepper |
0mArgs:{
"Expression": "mptcp_data_ready",
"PathPrefix": "net/mptcp/protocol.c"
}Results: {
"Output": "net/mptcp/protocol.c=979=static void mptcp_rcv_rtt_update(struct mptcp_sock *msk,\n--\nnet/mptcp/protocol.c-1004-\nnet/mptcp/protocol.c:1005:void mptcp_data_ready(struct sock *sk, struct sock *ssk)\nnet/mptcp/protocol.c-1006-{\n"
} |
| 53/2 | 2026/08/07 16:33 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 29866 output: 197 thoughts: 182 Thoughts: Here's my thought process on this MPTCP patch: **MPTCP Patch Analysis and Fuzzing Strategy** Okay, so I'm integrating these specific MPTCP functions into the `FocusSymbols` list for fuzzing: `mptcp_incoming_options`, `mptcp_pm_chk_stale`, `mptcp_subflow_get_retrans`, `mptcp_data_ready`, `mptcp_recvmsg`, and `mptcp_sendmsg`. This patch is a functional modification, dealing with memory limits and retransmission behavior, which immediately screams "fuzzing target." We *need* to put this through its paces. Now, before we launch, a quick sanity check of the relevant configurations. We've got `MPTCP` and `MPTCP_IPV6`, which is exactly what I expected. No surprises there. Let's make sure, though. Ah, right. `INET` is a given, a foundational dependency. And, of course, `IPV6` is required if `MPTCP_IPV6` is enabled. Good. Finally, I'll use the `set-results` tool to present the fuzzing results in a clear and structured way. We want this to be easily digestible for the team. |
| 54/2 | 2026/08/07 16:33 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"MPTCP",
"MPTCP_IPV6"
],
"FocusSymbols": [
"mptcp_incoming_options",
"mptcp_pm_chk_stale",
"mptcp_subflow_get_retrans",
"mptcp_data_ready",
"mptcp_recvmsg",
"mptcp_sendmsg"
],
"Reasoning": "The patch modifies the MPTCP protocol implementation to handle memory limits, backlog drops, and retransmission logic. It adds new MIB counters and modifies core MPTCP functions like `mptcp_incoming_options`, `mptcp_pm_chk_stale`, and `__mptcp_retrans`. These are reachable from standard MPTCP socket operations and network receive paths, making them highly relevant for fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"MPTCP",
"MPTCP_IPV6"
],
"FocusSymbols": [
"mptcp_incoming_options",
"mptcp_pm_chk_stale",
"mptcp_subflow_get_retrans",
"mptcp_data_ready",
"mptcp_recvmsg",
"mptcp_sendmsg"
],
"Reasoning": "The patch modifies the MPTCP protocol implementation to handle memory limits, backlog drops, and retransmission logic. It adds new MIB counters and modifies core MPTCP functions like `mptcp_incoming_options`, `mptcp_pm_chk_stale`, and `__mptcp_retrans`. These are reachable from standard MPTCP socket operations and network receive paths, making them highly relevant for fuzzing.",
"WorthFuzzing": true
} |
| 55/1 | 2026/08/07 16:33 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 56/2 | 2026/08/07 16:33 | agent | kmsan-evaluator |
0mModel:best-expensive Args: null Results: {
"KMSANReasoning": "The patch series focuses on MPTCP memory accounting, packet dropping under memory pressure, and retransmission logic. It introduces new MIB counters, adds memory limit checks (`mptcp_over_limit`, `mptcp_try_rmem_schedule`), implements out-of-order queue pruning (`mptcp_prune_ofo_queue`), and refactors the retransmission loop (`__mptcp_retrans`). None of these changes involve allocating new complex structures, exposing kernel memory to user space, or relying on potentially uninitialized memory for control flow. Any potential bugs introduced by these changes, such as use-after-free or out-of-bounds accesses during packet dropping or queue manipulation, would be effectively caught by standard KASAN and other default bug detectors. There is no risk of uninitialized memory usage or info-leaks that would necessitate a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: For your convenience, here is the diff of the changes:
commit b606a7bc46510b8a98488ab5264b73fb863fa89a
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 7 16:21:12 2026 +0000
syz-cluster: applied patch under review
diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c
index f23fda0c55a72..2569385bab7ca 100644
--- a/net/mptcp/mib.c
+++ b/net/mptcp/mib.c
@@ -85,6 +85,9 @@ static const struct snmp_mib mptcp_snmp_list[] = {
SNMP_MIB_ITEM("SimultConnectFallback", MPTCP_MIB_SIMULTCONNFALLBACK),
SNMP_MIB_ITEM("FallbackFailed", MPTCP_MIB_FALLBACKFAILED),
SNMP_MIB_ITEM("WinProbe", MPTCP_MIB_WINPROBE),
+ SNMP_MIB_ITEM("BacklogDrop", MPTCP_MIB_BACKLOGDROP),
+ SNMP_MIB_ITEM("RcvPruned", MPTCP_MIB_RCVPRUNED),
+ SNMP_MIB_ITEM("OFOPruned", MPTCP_MIB_OFOPRUNED),
};
/* mptcp_mib_alloc - allocate percpu mib counters
diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h
index 812218b5ed2bf..3a3425e258a76 100644
--- a/net/mptcp/mib.h
+++ b/net/mptcp/mib.h
@@ -88,6 +88,9 @@ enum linux_mptcp_mib_field {
MPTCP_MIB_SIMULTCONNFALLBACK, /* Simultaneous connect */
MPTCP_MIB_FALLBACKFAILED, /* Can't fallback due to msk status */
MPTCP_MIB_WINPROBE, /* MPTCP-level zero window probe */
+ MPTCP_MIB_BACKLOGDROP, /* Backlog over memory limit */
+ MPTCP_MIB_RCVPRUNED, /* Dropped due to memory constraints */
+ MPTCP_MIB_OFOPRUNED, /* MPTCP-level OoO queue pruned */
__MPTCP_MIB_MAX
};
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index 1057d500577b0..b8318e0301389 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -1190,8 +1190,34 @@ static bool add_addr_hmac_valid(struct mptcp_sock *msk,
return hmac == mp_opt->ahmac;
}
-/* Return false in case of error (or subflow has been reset),
- * else return true.
+static bool mptcp_over_limit(struct sock *sk, struct sock *ssk,
+ const struct sk_buff *skb)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ u32 rcvbuf = READ_ONCE(sk->sk_rcvbuf);
+
+ if (likely((u32)sk_rmem_alloc_get(sk) <= rcvbuf &&
+ READ_ONCE(msk->backlog_len) <= rcvbuf))
+ return false;
+
+ /* Avoid silently dropping pure acks, fin, rst or already-acked segm. */
+ if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq ||
+ TCP_SKB_CB(skb)->tcp_flags & (TCPHDR_FIN | TCPHDR_RST) ||
+ !after(TCP_SKB_CB(skb)->end_seq, tcp_sk(ssk)->rcv_nxt))
+ return false;
+
+ /* Dropped due to memory constraints, schedule an ack. */
+ inet_csk(ssk)->icsk_ack.pending |= ICSK_ACK_NOMEM | ICSK_ACK_NOW;
+ inet_csk_schedule_ack(ssk);
+
+ /* Plain TCP (fallback) and skb is dropped before the TCP recv queue. */
+ NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPRCVQDROP);
+
+ return true;
+}
+
+/* Return false when the caller must drop the packet, i.e. in case of error,
+ * subflow has been reset, or over memory limits.
*/
bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)
{
@@ -1217,7 +1243,7 @@ bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)
__mptcp_data_acked(subflow->conn);
mptcp_data_unlock(subflow->conn);
- return true;
+ return !mptcp_over_limit(subflow->conn, sk, skb);
}
mptcp_get_options(skb, &mp_opt);
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 64a1236aabee9..d1f73c3e39fa3 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -1065,7 +1065,8 @@ bool mptcp_pm_is_backup(struct mptcp_sock *msk, struct sock_common *skc)
return mptcp_pm_nl_is_backup(msk, &skc_local);
}
-static void mptcp_pm_subflows_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)
+static void
+mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)
{
struct mptcp_subflow_context *iter, *subflow = mptcp_subflow_ctx(ssk);
struct sock *sk = (struct sock *)msk;
@@ -1102,22 +1103,34 @@ static void mptcp_pm_subflows_chk_stale(const struct mptcp_sock *msk, struct soc
}
}
-void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)
+void mptcp_pm_chk_stale(const struct mptcp_sock *msk)
{
- struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
- u32 rcv_tstamp = READ_ONCE(tcp_sk(ssk)->rcv_tstamp);
-
- /* keep track of rtx periods with no progress */
- if (!subflow->stale_count) {
- subflow->stale_rcv_tstamp = rcv_tstamp;
- subflow->stale_count++;
- } else if (subflow->stale_rcv_tstamp == rcv_tstamp) {
- if (subflow->stale_count < U8_MAX)
+ struct mptcp_subflow_context *subflow;
+
+ mptcp_for_each_subflow(msk, subflow) {
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+ u32 rcv_tstamp;
+
+ if (!__mptcp_subflow_active(subflow))
+ continue;
+
+ /* No data outstanding at TCP level? not stale */
+ if (tcp_rtx_and_write_queues_empty(ssk))
+ continue;
+
+ /* keep track of rtx periods with no progress */
+ rcv_tstamp = READ_ONCE(tcp_sk(ssk)->rcv_tstamp);
+ if (!subflow->stale_count) {
+ subflow->stale_rcv_tstamp = rcv_tstamp;
subflow->stale_count++;
- mptcp_pm_subflows_chk_stale(msk, ssk);
- } else {
- subflow->stale_count = 0;
- mptcp_subflow_set_active(subflow);
+ } else if (subflow->stale_rcv_tstamp == rcv_tstamp) {
+ if (subflow->stale_count < U8_MAX)
+ subflow->stale_count++;
+ mptcp_pm_subflow_chk_stale(msk, ssk);
+ } else {
+ subflow->stale_count = 0;
+ mptcp_subflow_set_active(subflow);
+ }
}
}
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 7c8180d8d5eff..ec874d2ead6a4 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -242,6 +242,65 @@ static bool mptcp_rcvbuf_grow(struct sock *sk, u32 newval)
return false;
}
+/* "Inspired" from the TCP version; main difference: stop as soon as the MPTCP
+ * socket is under memory limit.
+ */
+static void mptcp_prune_ofo_queue(struct sock *sk,
+ const struct sk_buff *in_skb)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct rb_node *node, *prev;
+ bool pruned = false;
+ u64 mem;
+
+ if (RB_EMPTY_ROOT(&msk->out_of_order_queue))
+ return;
+
+ node = &msk->ooo_last_skb->rbnode;
+
+ do {
+ struct sk_buff *skb = rb_to_skb(node);
+
+ /* Stop pruning if the incoming skb would land in OoO tail. */
+ if (after64(MPTCP_SKB_CB(in_skb)->map_seq,
+ MPTCP_SKB_CB(skb)->map_seq))
+ break;
+
+ pruned = true;
+ prev = rb_prev(node);
+ rb_erase(node, &msk->out_of_order_queue);
+ mptcp_drop(sk, skb);
+ msk->ooo_last_skb = rb_to_skb(prev);
+
+ mem = (unsigned int)sk_rmem_alloc_get(sk);
+ if (mem <= sk->sk_rcvbuf)
+ break;
+
+ node = prev;
+ } while (node);
+
+ if (pruned)
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOPRUNED);
+}
+
+/* The stack can't drop packets for fallback socket at the msk level, or the
+ * stream will break.
+ */
+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));
+}
+
+static bool mptcp_try_rmem_schedule(struct sock *sk, const struct sk_buff *skb)
+{
+ if (!mptcp_can_ingest(sk)) {
+ mptcp_prune_ofo_queue(sk, skb);
+ return mptcp_can_ingest(sk);
+ }
+ return true;
+}
+
/* "inspired" by tcp_data_queue_ofo(), main differences:
* - use mptcp seqs
* - don't cope with sacks
@@ -253,6 +312,12 @@ 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);
@@ -389,6 +454,13 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) {
/* in sequence */
+insert:
+ if (!mptcp_try_rmem_schedule(sk, skb)) {
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED);
+ mptcp_drop(sk, skb);
+ return false;
+ }
+
msk->bytes_received += copy_len;
WRITE_ONCE(msk->ack_seq, msk->ack_seq + copy_len);
tail = skb_peek_tail(&sk->sk_receive_queue);
@@ -403,26 +475,20 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
return false;
}
- /* Completely old data? */
- if (!after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
- MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
- mptcp_drop(sk, skb);
- return false;
+ /* Partial packet */
+ if (after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
+ copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq;
+ MPTCP_SKB_CB(skb)->offset += msk->ack_seq -
+ MPTCP_SKB_CB(skb)->map_seq;
+ MPTCP_SKB_CB(skb)->map_seq += msk->ack_seq -
+ MPTCP_SKB_CB(skb)->map_seq;
+ goto insert;
}
- /* Partial packet: map_seq < ack_seq < end_seq.
- * Skip the already-acked bytes and enqueue the new data.
- */
- copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq;
- MPTCP_SKB_CB(skb)->offset += msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq;
- MPTCP_SKB_CB(skb)->map_seq += msk->ack_seq -
- MPTCP_SKB_CB(skb)->map_seq;
- msk->bytes_received += copy_len;
- WRITE_ONCE(msk->ack_seq, msk->ack_seq + copy_len);
-
- skb_set_owner_r(skb, sk);
- __skb_queue_tail(&sk->sk_receive_queue, skb);
- return true;
+ /* Completely old data */
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
+ mptcp_drop(sk, skb);
+ return false;
}
static void mptcp_stop_rtx_timer(struct sock *sk)
@@ -681,6 +747,7 @@ static void __mptcp_add_backlog(struct sock *sk,
struct sk_buff *tail = NULL;
struct sock *ssk = skb->sk;
bool fragstolen;
+ u64 limit;
int delta;
if (unlikely(sk->sk_state == TCP_CLOSE)) {
@@ -688,6 +755,16 @@ static void __mptcp_add_backlog(struct sock *sk,
return;
}
+ /* Similar additional allowance as plain TCP. */
+ limit = READ_ONCE(sk->sk_rcvbuf);
+ limit += (limit >> 1) + 64 * 1024;
+ limit = min_t(u64, limit, UINT_MAX);
+ if (msk->backlog_len > limit && !__mptcp_check_fallback(msk)) {
+ __MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_BACKLOGDROP);
+ kfree_skb_reason(skb, SKB_DROP_REASON_SOCKET_BACKLOG);
+ return;
+ }
+
/* Try to coalesce with the last skb in our backlog */
if (!list_empty(&msk->backlog_list))
tail = list_last_entry(&msk->backlog_list, struct sk_buff, list);
@@ -759,7 +836,7 @@ static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk,
mptcp_init_skb(ssk, skb, offset, len);
- if (own_msk && sk_rmem_alloc_get(sk) < sk->sk_rcvbuf) {
+ if (own_msk) {
mptcp_subflow_lend_fwdmem(subflow, skb);
ret |= __mptcp_move_skb(sk, skb);
} else {
@@ -1142,13 +1219,6 @@ static void __mptcp_clean_una_wakeup(struct sock *sk)
mptcp_write_space(sk);
}
-static void mptcp_clean_una_wakeup(struct sock *sk)
-{
- mptcp_data_lock(sk);
- __mptcp_clean_una_wakeup(sk);
- mptcp_data_unlock(sk);
-}
-
static void mptcp_enter_memory_pressure(struct sock *sk)
{
struct mptcp_subflow_context *subflow;
@@ -2215,12 +2285,7 @@ static bool __mptcp_move_skbs(struct sock *sk, struct list_head *skbs, u32 *delt
struct mptcp_sock *msk = mptcp_sk(sk);
bool moved = false;
- *delta = 0;
while (1) {
- /* If the msk recvbuf is full stop, don't drop */
- if (sk_rmem_alloc_get(sk) > sk->sk_rcvbuf)
- break;
-
prefetch(skb->next);
list_del(&skb->list);
*delta += skb->truesize;
@@ -2248,9 +2313,7 @@ static bool mptcp_can_spool_backlog(struct sock *sk, struct list_head *skbs)
DEBUG_NET_WARN_ON_ONCE(msk->backlog_unaccounted && sk->sk_socket &&
mem_cgroup_from_sk(sk));
- /* Don't spool the backlog if the rcvbuf is full. */
- if (list_empty(&msk->backlog_list) ||
- sk_rmem_alloc_get(sk) > sk->sk_rcvbuf)
+ if (list_empty(&msk->backlog_list))
return false;
INIT_LIST_HEAD(skbs);
@@ -2258,20 +2321,12 @@ static bool mptcp_can_spool_backlog(struct sock *sk, struct list_head *skbs)
return true;
}
-static void mptcp_backlog_spooled(struct sock *sk, u32 moved,
- struct list_head *skbs)
-{
- struct mptcp_sock *msk = mptcp_sk(sk);
-
- WRITE_ONCE(msk->backlog_len, msk->backlog_len - moved);
- list_splice(skbs, &msk->backlog_list);
-}
-
static bool mptcp_move_skbs(struct sock *sk)
{
+ struct mptcp_sock *msk = mptcp_sk(sk);
struct list_head skbs;
bool enqueued = false;
- u32 moved;
+ u32 moved = 0;
mptcp_data_lock(sk);
while (mptcp_can_spool_backlog(sk, &skbs)) {
@@ -2279,8 +2334,8 @@ static bool mptcp_move_skbs(struct sock *sk)
enqueued |= __mptcp_move_skbs(sk, &skbs, &moved);
mptcp_data_lock(sk);
- mptcp_backlog_spooled(sk, moved, &skbs);
}
+ WRITE_ONCE(msk->backlog_len, msk->backlog_len - moved);
mptcp_data_unlock(sk);
if (enqueued && mptcp_epollin_ready(sk))
@@ -2469,7 +2524,6 @@ struct sock *mptcp_subflow_get_retrans(struct mptcp_sock *msk)
/* still data outstanding at TCP level? skip this */
if (!tcp_rtx_and_write_queues_empty(ssk)) {
- mptcp_pm_subflow_chk_stale(msk, ssk);
min_stale_count = min_t(int, min_stale_count, subflow->stale_count);
continue;
}
@@ -2791,44 +2845,22 @@ static void mptcp_check_fastclose(struct mptcp_sock *msk)
sk_error_report(sk);
}
-static void __mptcp_retrans(struct sock *sk)
+/*
+ * Retransmit the specified data fragment on all the selected subflows,
+ * starting from the specified sequence
+ */
+static int __mptcp_push_retrans(struct sock *sk, struct mptcp_data_frag *dfrag,
+ u64 sent_seq)
{
struct mptcp_sendmsg_info info = { .data_lock_held = true, };
struct mptcp_sock *msk = mptcp_sk(sk);
struct mptcp_subflow_context *subflow;
- struct mptcp_data_frag *dfrag;
struct sock *ssk;
- int ret, err;
- u16 len = 0;
-
- mptcp_clean_una_wakeup(sk);
-
- /* first check ssk: need to kick "stale" logic */
- err = mptcp_sched_get_retrans(msk);
- dfrag = mptcp_rtx_head(sk);
- if (!dfrag) {
- if (mptcp_data_fin_enabled(msk)) {
- struct inet_connection_sock *icsk = inet_csk(sk);
-
- WRITE_ONCE(icsk->icsk_retransmits,
- icsk->icsk_retransmits + 1);
- mptcp_set_datafin_timeout(sk);
- mptcp_send_ack(msk);
-
- goto reset_timer;
- }
-
- if (!mptcp_send_head(sk))
- goto clear_scheduled;
-
- goto reset_timer;
- }
-
- if (err)
- goto reset_timer;
+ int ret, len = 0;
mptcp_for_each_subflow(msk, subflow) {
if (READ_ONCE(subflow->scheduled)) {
+ u16 offset = sent_seq - dfrag->data_seq;
u16 copied = 0;
mptcp_subflow_set_scheduled(subflow, false);
@@ -2838,7 +2870,7 @@ static void __mptcp_retrans(struct sock *sk)
lock_sock(ssk);
/* limit retransmission to the bytes already sent on some subflows */
- info.sent = 0;
+ info.sent = offset;
info.limit = READ_ONCE(msk->csum_enabled) ? dfrag->data_len :
dfrag->already_sent;
@@ -2853,7 +2885,7 @@ static void __mptcp_retrans(struct sock *sk)
!msk->allow_subflows) {
spin_unlock_bh(&msk->fallback_lock);
release_sock(ssk);
- goto clear_scheduled;
+ return -1;
}
while (info.sent < info.limit) {
@@ -2876,13 +2908,99 @@ static void __mptcp_retrans(struct sock *sk)
release_sock(ssk);
}
}
+ return len;
+}
+
+static void __mptcp_retrans(struct sock *sk)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct mptcp_subflow_context *subflow;
+ struct mptcp_data_frag *dfrag;
+ u64 retrans_seq, sent_seq;
+ bool need_retrans;
+ int err, len;
+
+ mptcp_pm_chk_stale(msk);
+
+ /* Get an updated and consistent rtx queue status. */
+ mptcp_data_lock(sk);
+ __mptcp_clean_una_wakeup(sk);
+ retrans_seq = msk->snd_una;
+ dfrag = mptcp_rtx_head(sk);
+ need_retrans = !!dfrag;
+ mptcp_data_unlock(sk);
+
+ for (;;) {
+ bool already_acked;
+
+ err = mptcp_sched_get_retrans(msk);
+ if (err)
+ break;
+
+ /* `already_sent` can be 0 for `dfrag` belonging to the RTX
+ * queue due to __mptcp_retransmit_pending_data().
+ */
+ if (!dfrag || !dfrag->already_sent)
+ break;
+
+ /* Can fail only in case of fallback. */
+ len = __mptcp_push_retrans(sk, dfrag, retrans_seq);
+ if (len < 0)
+ goto clear_scheduled;
+
+ retrans_seq += len;
+ msk->bytes_retrans += len;
+ dfrag->already_sent = max_t(u16, dfrag->already_sent,
+ retrans_seq - dfrag->data_seq);
+
+ /* With csum enabled, retransmission can send new data. */
+ sent_seq = dfrag->already_sent + dfrag->data_seq;
+ if (after64(sent_seq, msk->snd_nxt))
+ WRITE_ONCE(msk->snd_nxt, sent_seq);
+
+ /* Attempt the next fragment only if the current one is
+ * completely retransmitted.
+ */
+ if (before64(retrans_seq, dfrag->data_seq + dfrag->data_len))
+ break;
+
+ dfrag = list_is_last(&dfrag->list, &msk->rtx_queue) ?
+ NULL : list_next_entry(dfrag, list);
+ if (!dfrag)
+ break;
+
+ /* Incoming acks can move snd_una after the current dfrag
+ * across loop iterations, if so start again from RTX head.
+ */
+ mptcp_data_lock(sk);
+ already_acked = !before64(msk->snd_una, dfrag->data_seq +
+ dfrag->already_sent);
+ if (already_acked) {
+ __mptcp_clean_una_wakeup(sk);
+ retrans_seq = msk->snd_una;
+ dfrag = mptcp_rtx_head(sk);
+ need_retrans = !!dfrag;
+ } else if (after64(msk->snd_una, retrans_seq)) {
+ retrans_seq = msk->snd_una;
+ }
+ mptcp_data_unlock(sk);
+ }
- msk->bytes_retrans += len;
- dfrag->already_sent = max(dfrag->already_sent, len);
+ /* Attempt data-fin retransmission only when the RTX queue is empty. */
+ if (!need_retrans) {
+ if (mptcp_data_fin_enabled(msk)) {
+ struct inet_connection_sock *icsk = inet_csk(sk);
- /* With csum enabled retransmission can send new data. */
- if (after64(dfrag->already_sent + dfrag->data_seq, msk->snd_nxt))
- WRITE_ONCE(msk->snd_nxt, dfrag->already_sent + dfrag->data_seq);
+ WRITE_ONCE(icsk->icsk_retransmits,
+ icsk->icsk_retransmits + 1);
+ mptcp_set_datafin_timeout(sk);
+ mptcp_send_ack(msk);
+ goto reset_timer;
+ }
+
+ if (!mptcp_send_head(sk))
+ goto clear_scheduled;
+ }
reset_timer:
mptcp_check_and_set_pending(sk);
@@ -3676,12 +3794,12 @@ static void mptcp_release_cb(struct sock *sk)
__must_hold(&sk->sk_lock.slock)
{
struct mptcp_sock *msk = mptcp_sk(sk);
+ u32 moved = 0;
for (;;) {
unsigned long flags = (msk->cb_flags & MPTCP_FLAGS_PROCESS_CTX_NEED);
struct list_head join_list, skbs;
bool spool_bl;
- u32 moved;
spool_bl = mptcp_can_spool_backlog(sk, &skbs);
if (!flags && !spool_bl)
@@ -3714,9 +3832,9 @@ static void mptcp_release_cb(struct sock *sk)
cond_resched();
spin_lock_bh(&sk->sk_lock.slock);
- if (spool_bl)
- mptcp_backlog_spooled(sk, moved, &skbs);
}
+ if (moved)
+ WRITE_ONCE(msk->backlog_len, msk->backlog_len - moved);
if (__test_and_clear_bit(MPTCP_CLEAN_UNA, &msk->cb_flags))
__mptcp_clean_una_wakeup(sk);
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 1b80f2d6ec5a2..b3af3462bdd16 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -580,12 +580,11 @@ struct mptcp_subflow_context {
remote_key_valid : 1, /* received the peer key from */
disposable : 1, /* ctx can be free at ulp release time */
closing : 1, /* must not pass rx data to msk anymore */
- stale : 1, /* unable to snd/rcv data, do not use for xmit */
valid_csum_seen : 1, /* at least one csum validated */
is_mptfo : 1, /* subflow is doing TFO */
close_event_done : 1, /* has done the post-closed part */
mpc_drop : 1, /* the MPC option has been dropped in a rtx */
- __unused : 8;
+ __unused : 9;
bool data_avail;
bool scheduled;
bool pm_listener; /* a listener managed by the kernel PM? */
@@ -604,7 +603,11 @@ struct mptcp_subflow_context {
u8 reset_seen:1;
u8 reset_transient:1;
u8 reset_reason:4;
- u8 stale_count;
+ u8 stale_count; /* Protected by the msk socket lock */
+ u8 stale; /* Protected by the msk socket lock,
+ * if set the subflow is unable to snd/rcv
+ * data, the schedule should skip it
+ */
u32 subflow_id;
@@ -1103,7 +1106,7 @@ int mptcp_pm_parse_entry(struct nlattr *attr, struct genl_info *info,
bool mptcp_pm_addr_families_match(const struct sock *sk,
const struct mptcp_addr_info *loc,
const struct mptcp_addr_info *rem);
-void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk);
+void mptcp_pm_chk_stale(const struct mptcp_sock *msk);
void mptcp_pm_new_connection(struct mptcp_sock *msk, const struct sock *ssk, int server_side);
void mptcp_pm_fully_established(struct mptcp_sock *msk, const struct sock *ssk);
bool mptcp_pm_allow_new_subflow(struct mptcp_sock *msk);
|
| 57/3 | 2026/08/07 16:33 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10695 output: 213 thoughts: 705 Thoughts: **Memory Management and MPTCP Stability Review** Okay, so I've got this MPTCP patch in front of me. My initial assessment is that it primarily focuses on improving MPTCP's memory management and handling of overflow conditions. I see it's adding new MIB counters to track things like backlog drops, receiver pruned packets, and out-of-order queue pruning. That's a good start for observability. Looking at the code, it looks like `mptcp_incoming_options` now checks `mptcp_over_limit`, which makes sense for ingress flow control. Similarly, `mptcp_pm_chk_stale` is being modified to hunt down stale subflows. The key areas for review are `mptcp_data_queue_ofo`, `__mptcp_move_skb`, and `__mptcp_add_backlog`, all of which are now checking memory limits and dropping packets if needed using `mptcp_try_rmem_schedule`. `__mptcp_retrans` also looks like it's getting some tweaks to improve its retransmission capabilities, specifically with multiple fragments. And finally, `mptcp_release_cb` and `mptcp_move_skbs` are touched to make sure backlog spooling is handled correctly. My immediate thought is, are there any uninitialized memory concerns here? That's always my first worry with kernel changes. But based on a quick scan, and I've got to do a deeper dive, I don't see any glaring issues. The changes are almost entirely focused on memory accounting, packet dropping when memory limits are reached, and making retransmission logic more robust. There aren't any new structures being sent to user space, no complex new data structures are being allocated and partially initialized. The pattern here suggests careful attention to memory management, which is encouraging. So, at first glance, I *don't* see any obvious uninitialized memory usage or potential information leaks. Let me take a closer look at the changes in `__mptcp_retrans`. The `__mptcp_push_retrans` function is extracted for clarity. It iterates over fragments, then it calls that new function to actually push a retransmission. No uninitialized memory issues are popping out here either. Now, let's drill down into `mptcp_over_limit`. This function is pretty straightforward, reading existing `sk_rcvbuf` and `msk->backlog_len` fields and performing comparisons. It's just checking limits, and I don't see anything wrong with that. Lastly, `mptcp_prune_ofo_queue` is iterating over the out-of-order queue and dropping packets to free up memory. This seems like a reasonable approach to handling congestion. All of these changes really seem targeted at preventing and mitigating memory exhaustion within MPTCP. Based on this deeper review, it’s not introducing any uninitialized memory risks. And importantly, it seems like these changes would be fairly well covered by KASAN. We would expect KASAN to quickly flag any out-of-bounds accesses or use-after-free bugs that these changes might introduce. Honestly, I don't think KMSAN is needed here, unless there are other changes in the patch. Overall, I'm feeling fairly confident that this patch is safe from a memory perspective, pending a more thorough test run. |
| 58/3 | 2026/08/07 16:33 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series focuses on MPTCP memory accounting, packet dropping under memory pressure, and retransmission logic. It introduces new MIB counters, adds memory limit checks (`mptcp_over_limit`, `mptcp_try_rmem_schedule`), implements out-of-order queue pruning (`mptcp_prune_ofo_queue`), and refactors the retransmission loop (`__mptcp_retrans`). None of these changes involve allocating new complex structures, exposing kernel memory to user space, or relying on potentially uninitialized memory for control flow. Any potential bugs introduced by these changes, such as use-after-free or out-of-bounds accesses during packet dropping or queue manipulation, would be effectively caught by standard KASAN and other default bug detectors. There is no risk of uninitialized memory usage or info-leaks that would necessitate a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series focuses on MPTCP memory accounting, packet dropping under memory pressure, and retransmission logic. It introduces new MIB counters, adds memory limit checks (`mptcp_over_limit`, `mptcp_try_rmem_schedule`), implements out-of-order queue pruning (`mptcp_prune_ofo_queue`), and refactors the retransmission loop (`__mptcp_retrans`). None of these changes involve allocating new complex structures, exposing kernel memory to user space, or relying on potentially uninitialized memory for control flow. Any potential bugs introduced by these changes, such as use-after-free or out-of-bounds accesses during packet dropping or queue manipulation, would be effectively caught by standard KASAN and other default bug detectors. There is no risk of uninitialized memory usage or info-leaks that would necessitate a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|