From: Hyunwoo Kim <imv4bel@gmail.com>
To: Matthieu Baerts <matttbe@kernel.org>
Cc: martineau@kernel.org, geliang@kernel.org, pabeni@redhat.com,
mptcp@lists.linux.dev, netdev@vger.kernel.org, imv4bel@gmail.com
Subject: Re: [PATCH net] mptcp: fix use-after-free of the subflow context on the delegated list
Date: Thu, 20 Aug 2026 21:05:47 +0900 [thread overview]
Message-ID: <aobtmznTdBks0frM@v4bel> (raw)
In-Reply-To: <bf25121c-72cf-40a7-bbaf-5db85aee7f59@kernel.org>
On Wed, Aug 19, 2026 at 12:22:18PM +0200, Matthieu Baerts wrote:
> Hi Hyunwoo,
>
> On 19/08/2026 12:13, Hyunwoo Kim wrote:
> > mptcp_subflow_delegate() links the subflow context into a per-CPU napi
> > list and takes a reference on the subflow socket. The context is a
> > separate allocation, reached through icsk_ulp_data, and the list holds
> > no reference to it.
> >
> > inet_csk_destroy_sock() runs sk_prot->destroy() regardless of the
> > outstanding references. That path ends in tcp_cleanup_ulp(), which calls
> > subflow_ulp_release(). subflow_ulp_release() frees the context with
> > kfree_rcu() but does not remove it from the list. Once the grace period
> > elapses and the context is actually freed, the next mptcp_napi_poll()
> > dequeues that entry and mptcp_subflow_delegated_next() writes to the
> > freed context with list_del_init(). That is the use-after-free.
> >
> > The freeing path cannot dequeue the entry itself: the list is per-CPU
> > and only the CPU that queued the subflow may touch it, as
> > tcp_release_cb_override() already notes.
> >
> > Delay the free instead. Add MPTCP_DELEGATE_DEAD.
> > mptcp_subflow_free_ctx() sets it and frees the context only if the
> > subflow is not scheduled, while mptcp_napi_poll() frees it if the dead
> > bit is set when it clears the scheduled bit. Either
> > mptcp_subflow_free_ctx() gets there first, sees the scheduled bit and
> > leaves the free to mptcp_napi_poll(), or mptcp_napi_poll() gets there
> > first, does not see the dead bit and mptcp_subflow_free_ctx() does the
> > free.
> >
> > Also skip mptcp_subflow_process_delegated() once the dead bit is set.
> > subflow_ulp_release() has already dropped the msk reference by then, so
> > the pending actions must not run.
> >
> > Finally, move delegated_status out of struct_group(reset).
> > mptcp_subflow_ctx_reset() clears that group on disconnect, and clearing
> > the scheduled bit while the entry is still queued would let
> > mptcp_subflow_free_ctx() free a context the napi list still points to.
> > delegated_node is already outside the group.
> Thank you for looking at this. Do you have more context about how the
> bug was discovered? Do you have a reproducer and/or a KASAN calltrace or
> similar?
>
> Having this would help the reviewers.
Sorry for the late reply. The poc was extremely unstable, so I wanted
to improve its reproduction rate a bit before sending it.
Attached are the poc, the KASAN log, and the mdelay diff used for
reproduction.
Best regards,
Hyunwoo Kim
---
KASAN log:
[ 220.351834] ==================================================================
[ 220.352257] BUG: KASAN: slab-use-after-free in __list_del_entry_valid_or_report+0x14/0x140
[ 220.352686] Read of size 8 at addr ffff888102e45cc8 by task w/160
[ 220.353004]
[ 220.353096] CPU: 3 UID: 65534 PID: 160 Comm: w Not tainted 7.2.0-rc7-gf5bbbfec59b4-dirty #245 PREEMPT(lazy)
[ 220.353099] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 220.353102] Call Trace:
[ 220.353107] <IRQ>
[ 220.353109] dump_stack_lvl+0x78/0xe0
[ 220.353114] print_report+0xf4/0x610
[ 220.353119] ? preempt_count_sub+0x18/0xd0
[ 220.353123] ? __virt_addr_valid+0x1b0/0x350
[ 220.353126] ? __phys_addr+0x46/0x90
[ 220.353130] ? __list_del_entry_valid_or_report+0x14/0x140
[ 220.353132] kasan_report+0xe0/0x110
[ 220.353136] ? __list_del_entry_valid_or_report+0x14/0x140
[ 220.353141] __list_del_entry_valid_or_report+0x14/0x140
[ 220.353145] mptcp_napi_poll+0x17c/0x530
[ 220.353151] trace_clock_x86_tsc+0x20/0x20
[ 220.353156] net_rx_action+0x33f/0x760
[ 220.353162] ? do_raw_spin_unlock+0x9a/0x100
[ 220.353165] ? __pfx_net_rx_action+0x10/0x10
[ 220.353167] ? _raw_spin_unlock_irqrestore+0x40/0x80
[ 220.353174] ? tmigr_handle_remote+0x15d/0x210
[ 220.353181] ? run_timer_base+0xcf/0x120
[ 220.353184] ? __pfx_run_timer_base+0x10/0x10
[ 220.353188] handle_softirqs+0x165/0x700
[ 220.353194] ? __pfx_handle_softirqs+0x10/0x10
[ 220.353197] ? __flush_smp_call_function_queue+0x223/0x420
[ 220.353202] __irq_exit_rcu+0x112/0x1a0
[ 220.353204] irq_exit_rcu+0xe/0x30
[ 220.353206] sysvec_call_function+0x90/0xb0
[ 220.353210] </IRQ>
[ 220.353211] <TASK>
[ 220.353212] asm_sysvec_call_function+0x1a/0x20
[ 220.353215] RIP: 0010:_raw_spin_unlock_irqrestore+0x36/0x80
[ 220.353218] Code: f5 53 48 8b 74 24 10 48 89 fb 48 83 c7 18 e8 21 fd ac fd 48 89 df e8 b9 44 ad fd f7 c5 00 02 00 00 75 23 9c 58 f6 c4 02 75 37 <bf> 01 00 00 00 e8 f0 f1 a4 fd 65 8b 05 d9 f0 cb 03 85 c0 74 16 5b
[ 220.353220] RSP: 0018:ffffc90000f2fc10 EFLAGS: 00000246
[ 220.353222] RAX: 0000000000000002 RBX: ffff8881015e2900 RCX: 0000000000000080
[ 220.353223] RDX: 0000000000000000 RSI: ffffffff84b6d450 RDI: ffffffff83f21980
[ 220.353225] RBP: 0000000000000282 R08: 0000000000000001 R09: 0000000000000001
[ 220.353226] R10: ffffffff8626fb57 R11: ffff8880113dbac0 R12: ffff8881092c8740
[ 220.353228] R13: 0000000000000000 R14: ffffc90000f2fc58 R15: 00000000ffffffff
[ 220.353235] qlist_free_all+0x47/0xf0
[ 220.353239] kasan_quarantine_reduce+0x195/0x1e0
[ 220.353242] __kasan_slab_alloc+0x4e/0x70
[ 220.353246] kmem_cache_alloc_noprof+0x242/0x670
[ 220.353249] ? security_file_alloc+0x28/0xb0
[ 220.353255] security_file_alloc+0x28/0xb0
[ 220.353258] init_file+0x5b/0x210
[ 220.353263] alloc_empty_file+0x61/0x140
[ 220.353267] alloc_file_pseudo+0x123/0x1c0
[ 220.353270] ? __pfx_alloc_file_pseudo+0x10/0x10
[ 220.353273] ? preempt_count_sub+0x18/0xd0
[ 220.353275] ? _raw_spin_unlock+0x2d/0x50
[ 220.353278] ? alloc_fd+0x213/0x360
[ 220.353282] sock_alloc_file+0x45/0x110
[ 220.353286] __sys_socket+0x135/0x180
[ 220.353290] ? __pfx___sys_socket+0x10/0x10
[ 220.353292] ? xfd_validate_state+0x39/0xc0
[ 220.353298] __x64_sys_socket+0x40/0x50
[ 220.353301] do_syscall_64+0x115/0x6c0
[ 220.353305] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 220.353308] RIP: 0033:0x423a3b
[ 220.353311] Code: c7 c2 b8 ff ff ff f7 d8 64 89 02 b8 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 f3 0f 1e fa b8 29 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b8 ff ff ff f7 d8 64 89 01 48
[ 220.353313] RSP: 002b:00007fbb93b551f8 EFLAGS: 00000297 ORIG_RAX: 0000000000000029
[ 220.353315] RAX: ffffffffffffffda RBX: 000000000000000c RCX: 0000000000423a3b
[ 220.353316] RDX: 0000000000000106 RSI: 0000000000000001 RDI: 0000000000000002
[ 220.353317] RBP: 00007fbb93b55208 R08: 0000000000000000 R09: 0000000000000000
[ 220.353318] R10: 0000000000004000 R11: 0000000000000297 R12: 00007fbb93ca56c0
[ 220.353320] R13: ffffffffffffffb8 R14: 0000000000000000 R15: 00007ffdbd37eb70
[ 220.353326] </TASK>
[ 220.353327]
[ 220.371136] Allocated by task 155:
[ 220.371304] kasan_save_stack+0x33/0x60
[ 220.371491] kasan_save_track+0x14/0x30
[ 220.371677] __kasan_kmalloc+0x8f/0xa0
[ 220.371859] __kmalloc_cache_noprof+0x2bf/0x6a0
[ 220.372077] subflow_create_ctx+0x60/0x160
[ 220.372276] subflow_ulp_init+0x54/0x220
[ 220.372466] tcp_set_ulp+0x190/0x410
[ 220.372642] mptcp_subflow_create_socket+0x241/0x4f0
[ 220.372879] __mptcp_nmpc_sk+0x106/0x3d0
[ 220.373070] mptcp_connect+0x44/0x4b0
[ 220.373251] __inet_stream_connect+0x22c/0x710
[ 220.373466] inet_stream_connect+0x48/0x70
[ 220.373663] __sys_connect+0x101/0x130
[ 220.373846] __x64_sys_connect+0x40/0x50
[ 220.374035] do_syscall_64+0x115/0x6c0
[ 220.374219] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 220.374460]
[ 220.374542] Freed by task 156:
[ 220.374694] kasan_save_stack+0x33/0x60
[ 220.374880] kasan_save_track+0x14/0x30
[ 220.375066] kasan_save_free_info+0x3b/0x60
[ 220.375269] __kasan_slab_free+0x43/0x70
[ 220.375459] kvfree_rcu_cb+0x26a/0x650
[ 220.375641] rcu_core+0x4ff/0xe40
[ 220.375807] handle_softirqs+0x165/0x700
[ 220.375997] __irq_exit_rcu+0x112/0x1a0
[ 220.376185] irq_exit_rcu+0xe/0x30
[ 220.376353] sysvec_apic_timer_interrupt+0x90/0xb0
[ 220.376582] asm_sysvec_apic_timer_interrupt+0x1a/0x20
[ 220.376826]
[ 220.376908] Last potentially related work creation:
[ 220.377142] kasan_save_stack+0x33/0x60
[ 220.377328] kasan_record_aux_stack+0x8c/0xa0
[ 220.377539] __call_rcu_common.constprop.0+0x81/0x7f0
[ 220.377780] tcp_cleanup_ulp+0x45/0x90
[ 220.377962] tcp_v4_destroy_sock+0x147/0x3b0
[ 220.378171] inet_csk_destroy_sock+0xaf/0x1c0
[ 220.378382] __tcp_close+0x6bb/0x7d0
[ 220.378557] __mptcp_close_ssk+0x242/0x910
[ 220.378755] mptcp_destroy_common+0xeb/0x1e0
[ 220.378961] mptcp_destroy+0x3f/0x80
[ 220.379138] __mptcp_destroy_sock+0xd3/0x1f0
[ 220.379345] __mptcp_close+0x3fc/0x5c0
[ 220.379527] mptcp_close+0x23/0x90
[ 220.379694] inet_release+0x93/0x100
[ 220.379869] __sock_release+0x66/0x130
[ 220.380051] sock_close+0x18/0x20
[ 220.380217] __fput+0x1f0/0x4c0
[ 220.380373] fput_close_sync+0xd2/0x190
[ 220.380559] __x64_sys_close+0x55/0x90
[ 220.380742] do_syscall_64+0x115/0x6c0
[ 220.380925] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 220.381166]
[ 220.381248] Second to last potentially related work creation:
[ 220.381519] kasan_save_stack+0x33/0x60
[ 220.381705] kasan_record_aux_stack+0x8c/0xa0
[ 220.381917] kvfree_call_rcu+0x1e/0x60
[ 220.382100] tcp_cleanup_ulp+0x45/0x90
[ 220.382287] tcp_v4_destroy_sock+0x147/0x3b0
[ 220.382494] inet_csk_destroy_sock+0xaf/0x1c0
[ 220.382704] __tcp_close+0x6bb/0x7d0
[ 220.382879] __mptcp_close_ssk+0x242/0x910
[ 220.383077] mptcp_destroy_common+0xeb/0x1e0
[ 220.383285] mptcp_destroy+0x3f/0x80
[ 220.383460] __mptcp_destroy_sock+0xd3/0x1f0
[ 220.383667] __mptcp_close+0x3fc/0x5c0
[ 220.383848] mptcp_close+0x23/0x90
[ 220.384015] inet_release+0x93/0x100
[ 220.384193] __sock_release+0x66/0x130
[ 220.384375] sock_close+0x18/0x20
[ 220.384538] __fput+0x1f0/0x4c0
[ 220.384695] fput_close_sync+0xd2/0x190
[ 220.384880] __x64_sys_close+0x55/0x90
[ 220.385063] do_syscall_64+0x115/0x6c0
[ 220.385247] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 220.385487]
[ 220.385569] The buggy address belongs to the object at ffff888102e45c00
[ 220.385569] which belongs to the cache kmalloc-512 of size 512
[ 220.386140] The buggy address is located 200 bytes inside of
[ 220.386140] freed 512-byte region [ffff888102e45c00, ffff888102e45e00)
[ 220.386699]
[ 220.386781] The buggy address belongs to the physical page:
[ 220.387043] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x102e40
[ 220.387416] head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
[ 220.387771] flags: 0x200000000000040(head|node=0|zone=2)
[ 220.388022] page_type: f5(slab)
[ 220.388181] raw: 0200000000000040 ffff888100042f40 ffffea00041d4010 ffffea00041bd010
[ 220.388540] raw: 0000000000000000 0000000800150015 00000000f5000000 0000000000000000
[ 220.388899] head: 0200000000000040 ffff888100042f40 ffffea00041d4010 ffffea00041bd010
[ 220.389263] head: 0000000000000000 0000000800150015 00000000f5000000 0000000000000000
[ 220.389626] head: 0200000000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
[ 220.389989] head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008
[ 220.390353] page dumped because: kasan: bad access detected
[ 220.390614]
[ 220.390696] Memory state around the buggy address:
[ 220.390924] ffff888102e45b80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 220.391263] ffff888102e45c00: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 220.391599] >ffff888102e45c80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 220.391935] ^
[ 220.392198] ffff888102e45d00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 220.392534] ffff888102e45d80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 220.392870] ==================================================================
mdelay diff:
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -6106,6 +6106,7 @@ static inline void tcp_data_snd_check(struct sock *sk)
{
tcp_push_pending_frames(sk);
+ mdelay(1);
tcp_check_space(sk);
}
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -4617,6 +4617,7 @@ static int mptcp_napi_poll(struct napi_struct *napi, int budget)
delegated = container_of(napi, struct mptcp_delegated_action, napi);
+ mdelay(200);
while ((subflow = mptcp_subflow_delegated_next(delegated)) != NULL) {
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -1024,6 +1024,7 @@ static inline void __mptcp_sync_sndbuf(struct sock *sk)
struct mptcp_subflow_context *subflow;
int ssk_sndbuf, new_sndbuf;
+ mdelay(8);
if (sk->sk_userlocks & SOCK_SNDBUF_LOCK)
return;
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -1540,7 +1540,9 @@ static void subflow_write_space(struct sock *ssk)
struct sock *sk = mptcp_subflow_ctx(ssk)->conn;
+ mdelay(2);
mptcp_propagate_sndbuf(sk, ssk);
+ mdelay(20);
mptcp_write_space(sk);
}
PoC:
// CONFIG_KASAN=y, CONFIG_KASAN_GENERIC=y
// CONFIG_DEBUG_KERNEL=y, CONFIG_RCU_EXPERT=y
// CONFIG_RCU_STRICT_GRACE_PERIOD=y, CONFIG_NR_CPUS=4
// CONFIG_KVFREE_RCU_BATCHED is not set
// CONFIG_RPS=y
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#ifndef IPPROTO_MPTCP
#define IPPROTO_MPTCP 262
#endif
#ifndef MEMBARRIER_CMD_GLOBAL
#define MEMBARRIER_CMD_GLOBAL 1
#endif
#define WORKERS 8
#define CONC 8
#define BYTES (1UL << 20)
#define CLOSE_US 100000
#define READER_US 1000
#define IDLE_US 700000
#define LOWAT 1
#define SPRAYERS 10
#define CLI_CPU 0
#define SRV_CPU 1
#define CLOSER_CPU 2
#define SPRAY_CPU 2
#define RX_CPUS "8"
#define LO_MTU 1500
static uint16_t port;
static int listener;
static void die(const char *m) { perror(m); _exit(1); }
static void pin(int cpu)
{
cpu_set_t s;
if (cpu < 0)
return;
CPU_ZERO(&s);
CPU_SET(cpu, &s);
sched_setaffinity(0, sizeof(s), &s);
}
static void sysfs_write(const char *path, const char *val)
{
int fd = open(path, O_WRONLY);
if (fd < 0)
return;
(void)!write(fd, val, strlen(val));
close(fd);
}
static void sysfs_show(const char *path)
{
char b[64];
int fd = open(path, O_RDONLY);
ssize_t n;
if (fd < 0)
return;
n = read(fd, b, sizeof(b) - 1);
close(fd);
if (n <= 0)
return;
while (n > 0 && (b[n - 1] == '\n' || b[n - 1] == ' '))
n--;
b[n] = 0;
printf("%s = %s\n", path, b);
}
static void setup_env(void)
{
struct ifreq ifr;
int fd;
sysfs_write("/sys/class/net/lo/queues/rx-0/rps_cpus", RX_CPUS "\n");
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd >= 0) {
memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, "lo");
ifr.ifr_mtu = LO_MTU;
ioctl(fd, SIOCSIFMTU, &ifr);
close(fd);
}
sysfs_show("/sys/class/net/lo/mtu");
sysfs_show("/sys/class/net/lo/queues/rx-0/rps_cpus");
}
static void *gp_thread(void *p)
{
(void)p;
for (;;)
syscall(__NR_membarrier, MEMBARRIER_CMD_GLOBAL, 0, 0);
return NULL;
}
#define CLOSEQ 8192
static int closeq[CLOSEQ];
static volatile unsigned long q_head, q_tail;
static pthread_mutex_t q_lock = PTHREAD_MUTEX_INITIALIZER;
static void close_later(int fd)
{
pthread_mutex_lock(&q_lock);
if (q_head - q_tail < CLOSEQ) {
closeq[q_head % CLOSEQ] = fd;
q_head++;
fd = -1;
}
pthread_mutex_unlock(&q_lock);
if (fd >= 0)
close(fd);
}
static void *closer_thread(void *p)
{
(void)p;
pin(CLOSER_CPU);
for (;;) {
int fd = -1;
pthread_mutex_lock(&q_lock);
if (q_tail != q_head) {
fd = closeq[q_tail % CLOSEQ];
q_tail++;
}
pthread_mutex_unlock(&q_lock);
if (fd >= 0) {
static __thread unsigned long r = 12345;
r = r * 1103515245UL + 12345UL;
usleep(1000 + (r >> 16) % CLOSE_US);
close(fd);
} else {
usleep(20);
}
}
return NULL;
}
static void drain(int fd)
{
static __thread char b[65536];
for (;;) {
ssize_t n = read(fd, b, sizeof(b));
if (n > 0) {
(void)send(fd, b, 16384, MSG_NOSIGNAL | MSG_DONTWAIT);
usleep(READER_US);
continue;
}
if (n < 0 && errno == EINTR)
continue;
break;
}
close(fd);
}
static void *reader_thread(void *p)
{
drain((int)(intptr_t)p);
return NULL;
}
static void peer_main(void)
{
pthread_attr_t at;
pin(SRV_CPU);
pthread_attr_init(&at);
pthread_attr_setdetachstate(&at, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&at, 128 * 1024);
for (;;) {
pthread_t th;
int fd = accept(listener, NULL, NULL);
if (fd < 0) {
if (errno == EINTR)
continue;
_exit(0);
}
{
int rb = 4 << 20;
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rb, sizeof(rb));
}
if (pthread_create(&th, &at, reader_thread,
(void *)(intptr_t)fd))
close(fd);
}
}
static void *spray_thread(void *p)
{
struct sockaddr_in a = {
.sin_family = AF_INET,
.sin_port = htons(port),
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
struct linger l = { .l_onoff = 1, .l_linger = 0 };
pin(SPRAY_CPU + (int)(long)p);
for (;;) {
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
if (fd < 0) {
usleep(1000);
continue;
}
setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
if (!connect(fd, (struct sockaddr *)&a, sizeof(a))) {
static __thread char sb[1 << 18];
unsigned int lw = 1;
int fl = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
setsockopt(fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT,
&lw, sizeof(lw));
(void)send(fd, sb, 4096, MSG_NOSIGNAL);
}
close(fd);
}
return NULL;
}
static int mkconn(void)
{
struct sockaddr_in a = {
.sin_family = AF_INET,
.sin_port = htons(port),
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
struct linger l = { .l_onoff = 1, .l_linger = 0 };
int fd, one = 1, fl;
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
if (fd < 0)
return -1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
while (connect(fd, (struct sockaddr *)&a, sizeof(a))) {
if (errno == EINTR)
continue;
close(fd);
return -1;
}
setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
fl = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
return fd;
}
static volatile unsigned long n_cycles, n_eagain, n_conn;
static void one_cycle(int *fds)
{
static __thread char buf[1 << 20];
struct pollfd pfd[CONC];
unsigned long i, spent;
n_cycles++;
for (i = 0; i < CONC; i++) {
fds[i] = mkconn();
if (fds[i] >= 0)
n_conn++;
}
for (i = 0; i < CONC; i++) {
unsigned long sent = 0;
if (fds[i] < 0)
continue;
while (sent < BYTES) {
ssize_t k = send(fds[i], buf, sizeof(buf), MSG_NOSIGNAL);
if (k > 0) { sent += (unsigned long)k; continue; }
if (k < 0 && errno == EINTR) continue;
if (k < 0 && errno == EAGAIN) n_eagain++;
break;
}
}
for (i = 0; i < CONC; i++) {
unsigned int lw = LOWAT;
if (fds[i] >= 0)
setsockopt(fds[i], IPPROTO_TCP, TCP_NOTSENT_LOWAT,
&lw, sizeof(lw));
}
for (i = 0; i < CONC; i++) {
int sb = 512 * 1024;
if (fds[i] >= 0)
setsockopt(fds[i], SOL_SOCKET, SO_SNDBUF, &sb, sizeof(sb));
}
for (i = 0; i < CONC; i++)
if (fds[i] >= 0)
close_later(fds[i]);
for (i = 0; i < CONC; i++) {
pfd[i].fd = fds[i] >= 0 ? fds[i] : -1;
pfd[i].events = POLLOUT;
}
for (spent = 0; spent < IDLE_US; spent += 2000)
if (poll(pfd, CONC, 2) < 0 && errno != EINTR)
break;
}
static void *client_thread(void *p)
{
unsigned long n = 0;
int fds[CONC];
(void)p;
pin(CLI_CPU);
for (;;) {
one_cycle(fds);
if ((++n % 200) == 0) {
printf("cycles=%lu conns=%lu eagain=%lu\n",
n_cycles, n_conn, n_eagain);
fflush(stdout);
}
}
return NULL;
}
int main(void)
{
struct sockaddr_in a = {
.sin_family = AF_INET,
.sin_port = 0,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
socklen_t al = sizeof(a);
struct rlimit rl;
pthread_t th[WORKERS];
unsigned long i;
int one = 1;
signal(SIGPIPE, SIG_IGN);
rl.rlim_cur = rl.rlim_max = 65536;
setrlimit(RLIMIT_NOFILE, &rl);
setup_env();
if (geteuid() == 0 &&
(setgroups(0, NULL) || setresgid(65534, 65534, 65534) ||
setresuid(65534, 65534, 65534)))
die("drop privileges");
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
if (listener < 0)
die("socket(IPPROTO_MPTCP)");
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if (bind(listener, (struct sockaddr *)&a, sizeof(a)))
die("bind");
if (listen(listener, 4096))
die("listen");
getsockname(listener, (struct sockaddr *)&a, &al);
port = ntohs(a.sin_port);
printf("uid=%u port=%u\n", (unsigned)getuid(), port);
fflush(stdout);
if (!fork())
peer_main();
for (i = 0; i < SPRAYERS; i++)
pthread_create(&th[0], NULL, spray_thread, (void *)(long)(i % 8));
for (i = 0; i < 4; i++)
pthread_create(&th[0], NULL, closer_thread, NULL);
for (i = 0; i < 2; i++)
pthread_create(&th[0], NULL, gp_thread, NULL);
for (i = 0; i < WORKERS; i++)
pthread_create(&th[i], NULL, client_thread, NULL);
for (i = 0; i < WORKERS; i++)
pthread_join(th[i], NULL);
return 0;
}
next prev parent reply other threads:[~2026-08-20 12:05 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-19 10:13 [PATCH net] mptcp: fix use-after-free of the subflow context on the delegated list Hyunwoo Kim
2026-08-19 10:22 ` Matthieu Baerts
2026-08-20 12:05 ` Hyunwoo Kim [this message]
2026-08-19 11:33 ` MPTCP CI
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aobtmznTdBks0frM@v4bel \
--to=imv4bel@gmail.com \
--cc=geliang@kernel.org \
--cc=martineau@kernel.org \
--cc=matttbe@kernel.org \
--cc=mptcp@lists.linux.dev \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.