* BUG: bpf_iter_tcp can trigger inet_csk_listen_stop() WARN_ON(sock_owned_by_user(child))
@ 2026-08-07 9:57 Junseo Lim
2026-08-07 21:43 ` Kuniyuki Iwashima
0 siblings, 1 reply; 2+ messages in thread
From: Junseo Lim @ 2026-08-07 9:57 UTC (permalink / raw)
To: Eric Dumazet, Neal Cardwell
Cc: David S. Miller, Kuniyuki Iwashima, Alexei Starovoitov,
Daniel Borkmann, netdev, bpf, Sechang Lim, Martin KaFai Lau
Hi,
inet_csk_listen_stop() removes a request from the accept queue, locks the
child socket with bh_lock_sock(), and then checks:
while ((req = reqsk_queue_remove(queue, sk)) != NULL) {
...
WARN_ON(sock_owned_by_user(child));
...
}
This assumes that no process context owns the child socket at this point.
However, once a connection is completed for the listener, the child
socket is also visible from the established hash table.
In the normal connection-completion path, tcp_v4_syn_recv_sock() inserts
the child into ehash via inet_ehash_nolisten(), and
inet_csk_reqsk_queue_add() later stores the same child in req->sk on the
listener accept queue. bpf_iter_tcp can then take the child from ehash and
lock it in bpf_iter_tcp_seq_show().
This is different from the recent bpf_iter_tcp_established_batch
request_sock refcount issue
(https://lore.kernel.org/all/20260803112218.3361213-1-dingning04@gmail.com/).
In this case the socket is already a full child socket, so the iterator
can successfully take a reference and then acquire lock_sock() before the
listener teardown path reaches the child.
If acquiring lock_sock() in the BPF iterator is valid for this socket,
then the WARN_ON() in inet_csk_listen_stop() may simply encode an invariant
that is no longer true. In that case, removing the WARN_ON() and
documenting this case may be the right fix.
Otherwise, bpf_iter_tcp may need to skip full child sockets that are still
queued on the listener's accept queue.
I included a C reproducer, below.
- tested in the bpf/master at 7a3c0289c3c8eb4607dff448ae9ff9f902c813af
This was found by a custom fuzzer developed
by Sechang Lim <rhkrqnwk98@gmail.com>
Thanks,
Junseo Lim
---
[ 5.970506] ------------[ cut here ]------------
[ 5.970910] WARNING: net/ipv4/inet_connection_sock.c:1474 at inet_csk_listen_stop+0x85a/0xc10, CPU#0: bpf_iter_tcp_lo/90
[ 5.971656] CPU: 0 UID: 0 PID: 90 Comm: bpf_iter_tcp_lo Not tainted 7.2.0-rc4-00484-g7a3c0289c3c8 #87 PREEMPT(full)
[ 5.972340] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Arch Linux 1.17.0-2-2 04/01/2014
[ 5.972955] RIP: 0010:inet_csk_listen_stop+0x85a/0xc10
[ 5.973309] Code: 00 00 00 e8 b8 d5 f1 fe e9 7a f9 ff ff e8 8e 70 15 fe 48 8b 3c 24 be 02 00 00 00 e8 a0 d5 f1 fe e9 62 f9 ff ff e8 76 70 15 fe <0f> 0b e9 04 f9 ff ff e8 6a 70 15 fe e8 d5 20 04 fe 84 c0 0f 85 f2
[ 5.974547] RSP: 0018:ffffc900004dfc98 EFLAGS: 00010293
[ 5.976344] RAX: 0000000000000000 RBX: ffff888107f21a00 RCX: ffffffff83724aca
[ 5.976918] RDX: ffff88810613c400 RSI: ffffffff84c7aae2 RDI: ffff888107f21bc0
[ 5.977416] RBP: ffff8881073fb3e0 R08: 0000000000000001 R09: 0000000000000001
[ 5.977960] R10: 0000000000000000 R11: fffff5200009bf87 R12: 0000000000000000
[ 5.978466] R13: 0000000000000001 R14: dffffc0000000000 R15: ffff8881073b0d00
[ 5.979354] FS: 00000000005cd400(0000) GS:ffff888193fda000(0000) knlGS:0000000000000000
[ 5.979921] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 5.980685] CR2: 00007ffd196dd820 CR3: 0000000105d33000 CR4: 0000000000750ef0
[ 5.981356] PKRU: 55555554
[ 5.981547] Call Trace:
[ 5.981723] <TASK>
[ 5.981886] ? __pfx_sock_close+0x10/0x10
[ 5.982174] __tcp_close+0xc04/0xfd0
[ 5.982457] ? __pfx_sock_close+0x10/0x10
[ 5.982743] ? srso_alias_return_thunk+0x5/0xfbef5
[ 5.983079] ? trace_hardirqs_on+0x18/0x1a0
[ 5.983390] ? __pfx_sock_close+0x10/0x10
[ 5.983675] tcp_close+0x31/0xf0
[ 5.983915] ? srso_alias_return_thunk+0x5/0xfbef5
[ 5.984244] inet_release+0x121/0x2b0
[ 5.984510] sock_close+0x96/0x250
[ 5.984756] ? srso_alias_return_thunk+0x5/0xfbef5
[ 5.985090] ? security_file_release+0x2b/0x70
[ 5.985404] ? srso_alias_return_thunk+0x5/0xfbef5
[ 5.985735] __fput+0x39f/0xa00
[ 5.985973] fput_close_sync+0xf6/0x210
[ 5.986243] ? __pfx_fput_close_sync+0x10/0x10
[ 5.986587] ? srso_alias_return_thunk+0x5/0xfbef5
[ 5.986924] __x64_sys_close+0x96/0x110
[ 5.987214] do_syscall_64+0xae/0x5e0
[ 5.987475] ? srso_alias_return_thunk+0x5/0xfbef5
[ 5.987805] ? exc_page_fault+0x85/0xa0
[ 5.988086] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 5.988433] RIP: 0033:0x4555ae
[ 5.988652] Code: 4d 89 d8 e8 14 1f 00 00 4c 8b 5d f8 41 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 11 c9 c3 0f 1f 80 00 00 00 00 48 8b 45 10 0f 05 <c9> c3 83 e2 39 83 fa 08 75 e7 e8 03 ff ff ff 0f 1f 00 f3 0f 1e fa
[ 5.989837] RSP: 002b:00007ffd196de830 EFLAGS: 00000202 ORIG_RAX: 0000000000000003
[ 5.990585] RAX: ffffffffffffffda RBX: 0000000000000040 RCX: 00000000004555ae
[ 5.991066] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000005
[ 5.991552] RBP: 00007ffd196de840 R08: 0000000000000000 R09: 0000000000000000
[ 5.992029] R10: 0000000000000000 R11: 0000000000000202 R12: 0000000000000040
[ 5.992496] R13: 0000000000000005 R14: 0000000000000004 R15: 0000000000000045
[ 5.992981] </TASK>
[ 5.993145] Kernel panic - not syncing: kernel: panic_on_warn set ...
[ 5.993593] CPU: 0 UID: 0 PID: 90 Comm: bpf_iter_tcp_lo Not tainted 7.2.0-rc4-00484-g7a3c0289c3c8 #87 PREEMPT(full)
[ 5.994318] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Arch Linux 1.17.0-2-2 04/01/2014
[ 5.994976] Call Trace:
[ 5.995158] <TASK>
[ 5.995315] dump_stack_lvl+0xb0/0x110
[ 5.995591] vpanic+0x230/0x5b0
[ 5.995824] panic+0xd0/0xd0
[ 5.996045] ? __pfx_panic+0x10/0x10
[ 5.996307] ? check_panic_on_warn.cold+0x5/0x34
[ 5.996630] ? panic_on_this_cpu+0x1a/0x30
[ 5.996920] ? inet_csk_listen_stop+0x85a/0xc10
[ 5.997236] check_panic_on_warn.cold+0x19/0x34
[ 5.997554] __warn.cold+0x294/0x2d1
[ 5.997814] ? inet_csk_listen_stop+0x85a/0xc10
[ 5.998135] __report_bug+0x2bf/0x470
[ 5.998417] ? __pfx___report_bug+0x10/0x10
[ 5.998716] ? srso_alias_return_thunk+0x5/0xfbef5
[ 5.999055] ? mark_held_locks+0x40/0x70
[ 5.999330] ? srso_alias_return_thunk+0x5/0xfbef5
[ 5.999660] ? lockdep_hardirqs_on_prepare+0xdc/0x1d0
[ 6.000016] ? srso_alias_return_thunk+0x5/0xfbef5
[ 6.000346] ? trace_hardirqs_on+0x18/0x1a0
[ 6.000639] ? inet_csk_listen_stop+0x85a/0xc10
[ 6.000958] report_bug+0xab/0x220
[ 6.001199] ? srso_alias_return_thunk+0x5/0xfbef5
[ 6.001526] ? inet_csk_listen_stop+0x85c/0xc10
[ 6.001843] handle_bug+0x273/0x2d0
[ 6.002097] ? inet_csk_listen_stop+0x85a/0xc10
[ 6.002444] exc_invalid_op+0x1d/0x60
[ 6.002703] ? srso_alias_return_thunk+0x5/0xfbef5
[ 6.003048] asm_exc_invalid_op+0x1a/0x20
[ 6.003327] RIP: 0010:inet_csk_listen_stop+0x85a/0xc10
[ 6.003680] Code: 00 00 00 e8 b8 d5 f1 fe e9 7a f9 ff ff e8 8e 70 15 fe 48 8b 3c 24 be 02 00 00 00 e8 a0 d5 f1 fe e9 62 f9 ff ff e8 76 70 15 fe <0f> 0b e9 04 f9 ff ff e8 6a 70 15 fe e8 d5 20 04 fe 84 c0 0f 85 f2
[ 6.004883] RSP: 0018:ffffc900004dfc98 EFLAGS: 00010293
[ 6.005240] RAX: 0000000000000000 RBX: ffff888107f21a00 RCX: ffffffff83724aca
[ 6.005710] RDX: ffff88810613c400 RSI: ffffffff84c7aae2 RDI: ffff888107f21bc0
[ 6.006181] RBP: ffff8881073fb3e0 R08: 0000000000000001 R09: 0000000000000001
[ 6.006733] R10: 0000000000000000 R11: fffff5200009bf87 R12: 0000000000000000
[ 6.007784] R13: 0000000000000001 R14: dffffc0000000000 R15: ffff8881073b0d00
[ 6.008799] ? inet_csk_listen_stop+0x85a/0xc10
[ 6.009123] ? inet_csk_listen_stop+0x85a/0xc10
[ 6.009443] ? __pfx_sock_close+0x10/0x10
[ 6.009726] __tcp_close+0xc04/0xfd0
[ 6.009985] ? __pfx_sock_close+0x10/0x10
[ 6.010283] ? srso_alias_return_thunk+0x5/0xfbef5
[ 6.010613] ? trace_hardirqs_on+0x18/0x1a0
[ 6.010907] ? __pfx_sock_close+0x10/0x10
[ 6.011192] tcp_close+0x31/0xf0
[ 6.011431] ? srso_alias_return_thunk+0x5/0xfbef5
[ 6.011769] inet_release+0x121/0x2b0
[ 6.012043] sock_close+0x96/0x250
[ 6.012292] ? srso_alias_return_thunk+0x5/0xfbef5
[ 6.012620] ? security_file_release+0x2b/0x70
[ 6.012934] ? srso_alias_return_thunk+0x5/0xfbef5
[ 6.013263] __fput+0x39f/0xa00
[ 6.013495] fput_close_sync+0xf6/0x210
[ 6.013767] ? __pfx_fput_close_sync+0x10/0x10
[ 6.014079] ? srso_alias_return_thunk+0x5/0xfbef5
[ 6.014424] __x64_sys_close+0x96/0x110
[ 6.014698] do_syscall_64+0xae/0x5e0
[ 6.014961] ? srso_alias_return_thunk+0x5/0xfbef5
[ 6.015291] ? exc_page_fault+0x85/0xa0
[ 6.015564] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 6.015910] RIP: 0033:0x4555ae
[ 6.016127] Code: 4d 89 d8 e8 14 1f 00 00 4c 8b 5d f8 41 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 11 c9 c3 0f 1f 80 00 00 00 00 48 8b 45 10 0f 05 <c9> c3 83 e2 39 83 fa 08 75 e7 e8 03 ff ff ff 0f 1f 00 f3 0f 1e fa
[ 6.017315] RSP: 002b:00007ffd196de830 EFLAGS: 00000202 ORIG_RAX: 0000000000000003
[ 6.017814] RAX: ffffffffffffffda RBX: 0000000000000040 RCX: 00000000004555ae
[ 6.018294] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000005
[ 6.018758] RBP: 00007ffd196de840 R08: 0000000000000000 R09: 0000000000000000
[ 6.019227] R10: 0000000000000000 R11: 0000000000000202 R12: 0000000000000040
[ 6.019693] R13: 0000000000000005 R14: 0000000000000004 R15: 0000000000000045
[ 6.020171] </TASK>
[ 6.020459] Kernel Offset: disabled
[ 6.020715] ---[ end Kernel panic - not syncing: kernel: panic_on_warn set ... ]---
---
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/bpf.h>
#include <linux/types.h>
#include <netinet/in.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#ifndef __NR_bpf
#define __NR_bpf 321
#endif
#ifndef BPF_LINK_CREATE
#define BPF_LINK_CREATE 28
#endif
#ifndef BPF_ITER_CREATE
#define BPF_ITER_CREATE 33
#endif
#ifndef BPF_PROG_TYPE_TRACING
#define BPF_PROG_TYPE_TRACING 26
#endif
#ifndef BPF_TRACE_ITER
#define BPF_TRACE_ITER 28
#endif
#ifndef BPF_OBJ_NAME_LEN
#define BPF_OBJ_NAME_LEN 16
#endif
#define DEFAULT_ROUNDS 20000
#define DEFAULT_CLIENTS 64
#define DEFAULT_WORKERS 1
#define DEFAULT_BURN_INSNS 65536
#define READ_BUF_SIZE 4096
#define BTF_MAGIC 0xeb9f
#define BTF_KIND_INT 1
#define BTF_KIND_PTR 2
#define BTF_KIND_ARRAY 3
#define BTF_KIND_STRUCT 4
#define BTF_KIND_UNION 5
#define BTF_KIND_ENUM 6
#define BTF_KIND_FWD 7
#define BTF_KIND_TYPEDEF 8
#define BTF_KIND_VOLATILE 9
#define BTF_KIND_CONST 10
#define BTF_KIND_RESTRICT 11
#define BTF_KIND_FUNC 12
#define BTF_KIND_FUNC_PROTO 13
#define BTF_KIND_VAR 14
#define BTF_KIND_DATASEC 15
#define BTF_KIND_FLOAT 16
#define BTF_KIND_DECL_TAG 17
#define BTF_KIND_TYPE_TAG 18
#define BTF_KIND_ENUM64 19
#define BPF_ALU64_IMM(OP, DST, IMM) \
((struct bpf_insn){ .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
.dst_reg = DST, .imm = IMM })
#define BPF_EXIT_INSN() \
((struct bpf_insn){ .code = BPF_JMP | BPF_EXIT })
union bpf_attr_local {
struct {
uint32_t prog_type;
uint32_t insn_cnt;
uint64_t insns;
uint64_t license;
uint32_t log_level;
uint32_t log_size;
uint64_t log_buf;
uint32_t kern_version;
uint32_t prog_flags;
char prog_name[BPF_OBJ_NAME_LEN];
uint32_t prog_ifindex;
uint32_t expected_attach_type;
uint32_t prog_btf_fd;
uint32_t func_info_rec_size;
uint64_t func_info;
uint32_t func_info_cnt;
uint32_t line_info_rec_size;
uint64_t line_info;
uint32_t line_info_cnt;
uint32_t attach_btf_id;
} prog_load;
struct {
uint32_t prog_fd;
uint32_t target_fd;
uint32_t attach_type;
uint32_t flags;
uint64_t iter_info;
uint32_t iter_info_len;
} link_create;
struct {
uint32_t link_fd;
uint32_t flags;
} iter_create;
uint8_t pad[256];
};
struct btf_header {
uint16_t magic;
uint8_t version;
uint8_t flags;
uint32_t hdr_len;
uint32_t type_off;
uint32_t type_len;
uint32_t str_off;
uint32_t str_len;
};
struct btf_type {
uint32_t name_off;
uint32_t info;
union {
uint32_t size;
uint32_t type;
};
};
static volatile int stop_workers;
static uint64_t ptr_to_u64(const void *ptr)
{
return (uint64_t)(uintptr_t)ptr;
}
static long sys_bpf(int cmd, union bpf_attr_local *attr)
{
return syscall(__NR_bpf, cmd, attr, sizeof(*attr));
}
static void *read_file(const char *path, size_t *size_out)
{
size_t done = 0;
off_t size;
char *buf;
int fd;
fd = open(path, O_RDONLY);
if (fd < 0) {
perror(path);
return NULL;
}
size = lseek(fd, 0, SEEK_END);
if (size <= 0 || lseek(fd, 0, SEEK_SET) < 0) {
perror("lseek");
close(fd);
return NULL;
}
buf = malloc((size_t)size);
if (!buf) {
perror("malloc");
close(fd);
return NULL;
}
while (done < (size_t)size) {
ssize_t n = read(fd, buf + done, (size_t)size - done);
if (n < 0) {
if (errno == EINTR)
continue;
perror("read");
free(buf);
close(fd);
return NULL;
}
if (n == 0)
break;
done += (size_t)n;
}
close(fd);
*size_out = done;
return buf;
}
static uint32_t btf_kind(uint32_t info)
{
return (info >> 24) & 0x1f;
}
static uint32_t btf_vlen(uint32_t info)
{
return info & 0xffff;
}
static size_t btf_extra_size(const struct btf_type *t)
{
uint32_t kind = btf_kind(t->info);
uint32_t vlen = btf_vlen(t->info);
switch (kind) {
case BTF_KIND_INT:
case BTF_KIND_VAR:
case BTF_KIND_DECL_TAG:
return 4;
case BTF_KIND_ARRAY:
return 12;
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
return (size_t)vlen * 12;
case BTF_KIND_ENUM:
case BTF_KIND_FUNC_PROTO:
return (size_t)vlen * 8;
case BTF_KIND_DATASEC:
case BTF_KIND_ENUM64:
return (size_t)vlen * 12;
case BTF_KIND_PTR:
case BTF_KIND_FWD:
case BTF_KIND_TYPEDEF:
case BTF_KIND_VOLATILE:
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
case BTF_KIND_FUNC:
case BTF_KIND_FLOAT:
case BTF_KIND_TYPE_TAG:
return 0;
default:
fprintf(stderr, "unknown BTF kind %u\n", kind);
exit(1);
}
}
static int find_btf_func_id(const char *name)
{
const struct btf_header *hdr;
const uint8_t *types, *types_end;
const char *strs;
size_t btf_size;
void *btf;
uint32_t id = 1;
btf = read_file("/sys/kernel/btf/vmlinux", &btf_size);
if (!btf)
return -1;
if (btf_size < sizeof(*hdr)) {
fprintf(stderr, "short BTF header\n");
free(btf);
return -1;
}
hdr = btf;
if (hdr->magic != BTF_MAGIC) {
fprintf(stderr, "unexpected BTF magic 0x%x\n", hdr->magic);
free(btf);
return -1;
}
if ((size_t)hdr->hdr_len + hdr->type_off + hdr->type_len > btf_size ||
(size_t)hdr->hdr_len + hdr->str_off + hdr->str_len > btf_size) {
fprintf(stderr, "invalid BTF section bounds\n");
free(btf);
return -1;
}
types = (const uint8_t *)btf + hdr->hdr_len + hdr->type_off;
types_end = types + hdr->type_len;
strs = (const char *)btf + hdr->hdr_len + hdr->str_off;
while (types + sizeof(struct btf_type) <= types_end) {
const struct btf_type *t = (const struct btf_type *)types;
size_t rec_size = sizeof(*t) + btf_extra_size(t);
if (types + rec_size > types_end) {
fprintf(stderr, "truncated BTF type section\n");
free(btf);
return -1;
}
if (btf_kind(t->info) == BTF_KIND_FUNC &&
t->name_off < hdr->str_len &&
strcmp(strs + t->name_off, name) == 0) {
free(btf);
return (int)id;
}
types += rec_size;
id++;
}
fprintf(stderr, "BTF FUNC %s not found\n", name);
free(btf);
return -1;
}
static int load_tcp_iter_prog(uint32_t attach_btf_id, int burn_insns)
{
static char log_buf[1 << 20];
static char license[] = "GPL";
union bpf_attr_local attr;
struct bpf_insn *insns;
int insn_cnt;
int saved_errno;
int fd;
int i;
if (burn_insns < 0)
burn_insns = 0;
insn_cnt = burn_insns + 3;
insns = calloc((size_t)insn_cnt, sizeof(*insns));
if (!insns) {
perror("calloc(insns)");
return -1;
}
insns[0] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0);
for (i = 1; i <= burn_insns; i++)
insns[i] = BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1);
insns[insn_cnt - 2] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0);
insns[insn_cnt - 1] = BPF_EXIT_INSN();
memset(&attr, 0, sizeof(attr));
attr.prog_load.prog_type = BPF_PROG_TYPE_TRACING;
attr.prog_load.insn_cnt = (uint32_t)insn_cnt;
attr.prog_load.insns = ptr_to_u64(insns);
attr.prog_load.license = ptr_to_u64(license);
attr.prog_load.expected_attach_type = BPF_TRACE_ITER;
attr.prog_load.attach_btf_id = attach_btf_id;
memcpy(attr.prog_load.prog_name, "tcpit_burn", sizeof("tcpit_burn"));
fd = (int)sys_bpf(BPF_PROG_LOAD, &attr);
if (fd >= 0) {
free(insns);
return fd;
}
saved_errno = errno;
if (saved_errno == ENOSPC) {
fprintf(stderr,
"BPF_PROG_LOAD without verifier log failed with ENOSPC; "
"try a smaller burn_insns value\n");
free(insns);
errno = saved_errno;
return -1;
}
attr.prog_load.log_level = 1;
attr.prog_load.log_size = sizeof(log_buf);
attr.prog_load.log_buf = ptr_to_u64(log_buf);
fd = (int)sys_bpf(BPF_PROG_LOAD, &attr);
if (fd < 0) {
perror("BPF_PROG_LOAD(iter/tcp)");
if (log_buf[0])
fprintf(stderr, "verifier log:\n%s\n", log_buf);
}
free(insns);
return fd;
}
static int create_iter_link(int prog_fd)
{
union bpf_attr_local attr;
int link_fd;
memset(&attr, 0, sizeof(attr));
attr.link_create.prog_fd = (uint32_t)prog_fd;
attr.link_create.target_fd = 0;
attr.link_create.attach_type = BPF_TRACE_ITER;
attr.link_create.flags = 0;
attr.link_create.iter_info = 0;
attr.link_create.iter_info_len = 0;
link_fd = (int)sys_bpf(BPF_LINK_CREATE, &attr);
if (link_fd < 0)
perror("BPF_LINK_CREATE(iter/tcp)");
return link_fd;
}
static int create_iter_fd(int link_fd)
{
union bpf_attr_local attr;
int iter_fd;
memset(&attr, 0, sizeof(attr));
attr.iter_create.link_fd = (uint32_t)link_fd;
attr.iter_create.flags = 0;
iter_fd = (int)sys_bpf(BPF_ITER_CREATE, &attr);
return iter_fd;
}
static void iter_worker(int link_fd)
{
char buf[READ_BUF_SIZE];
while (!stop_workers) {
int iter_fd = create_iter_fd(link_fd);
if (iter_fd < 0)
continue;
for (;;) {
ssize_t n = read(iter_fd, buf, sizeof(buf));
if (n <= 0)
break;
}
close(iter_fd);
}
_exit(0);
}
static int make_listener(uint16_t *port_out)
{
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
int one = 1;
int fd;
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0)
return -1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = 0;
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
goto fail;
if (listen(fd, 256) < 0)
goto fail;
if (getsockname(fd, (struct sockaddr *)&addr, &len) < 0)
goto fail;
*port_out = ntohs(addr.sin_port);
return fd;
fail:
close(fd);
return -1;
}
static int connect_client(uint16_t port)
{
struct sockaddr_in addr;
int fd;
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0)
return -1;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = htons(port);
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
close(fd);
return -1;
}
return fd;
}
static int fill_accept_queue(uint16_t port, int *clients, int max_clients)
{
int i, n = 0;
for (i = 0; i < max_clients; i++) {
int fd = connect_client(port);
if (fd < 0)
break;
clients[n++] = fd;
}
return n;
}
static void close_clients(int *clients, int n)
{
int i;
for (i = 0; i < n; i++)
if (clients[i] >= 0)
close(clients[i]);
}
static int run_round(int clients_per_round)
{
int *clients;
uint16_t port;
int listener;
int n;
clients = calloc((size_t)clients_per_round, sizeof(*clients));
if (!clients)
return -1;
listener = make_listener(&port);
if (listener < 0) {
free(clients);
return -1;
}
n = fill_accept_queue(port, clients, clients_per_round);
usleep(100);
close(listener);
close_clients(clients, n);
free(clients);
return n > 0 ? 0 : -1;
}
int main(int argc, char **argv)
{
int rounds = DEFAULT_ROUNDS;
int clients = DEFAULT_CLIENTS;
int workers = DEFAULT_WORKERS;
int burn_insns = DEFAULT_BURN_INSNS;
pid_t *pids;
int attach_btf_id;
int prog_fd;
int link_fd;
int i;
printf("[repro] rounds=%d clients=%d workers=%d burn_insns=%d\n",
rounds, clients, workers, burn_insns);
attach_btf_id = find_btf_func_id("bpf_iter_tcp");
if (attach_btf_id < 0)
return 1;
printf("[repro] bpf_iter_tcp attach_btf_id=%d\n", attach_btf_id);
prog_fd = load_tcp_iter_prog((uint32_t)attach_btf_id, burn_insns);
if (prog_fd < 0)
return 1;
link_fd = create_iter_link(prog_fd);
if (link_fd < 0)
return 1;
pids = calloc((size_t)workers, sizeof(*pids));
if (!pids)
return 1;
for (i = 0; i < workers; i++) {
pids[i] = fork();
if (pids[i] == 0)
iter_worker(link_fd);
}
for (i = 0; i < rounds; i++) {
run_round(clients);
if ((i & 0xff) == 0)
fprintf(stderr, "[repro] round %d/%d\n", i, rounds);
}
stop_workers = 1;
for (i = 0; i < workers; i++) {
if (pids[i] > 0) {
kill(pids[i], SIGTERM);
waitpid(pids[i], NULL, 0);
}
}
free(pids);
close(link_fd);
close(prog_fd);
return 0;
}
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: BUG: bpf_iter_tcp can trigger inet_csk_listen_stop() WARN_ON(sock_owned_by_user(child))
2026-08-07 9:57 BUG: bpf_iter_tcp can trigger inet_csk_listen_stop() WARN_ON(sock_owned_by_user(child)) Junseo Lim
@ 2026-08-07 21:43 ` Kuniyuki Iwashima
0 siblings, 0 replies; 2+ messages in thread
From: Kuniyuki Iwashima @ 2026-08-07 21:43 UTC (permalink / raw)
To: Junseo Lim
Cc: Eric Dumazet, Neal Cardwell, David S. Miller, Alexei Starovoitov,
Daniel Borkmann, netdev, bpf, Sechang Lim, Martin KaFai Lau
On Fri, Aug 7, 2026 at 2:57 AM Junseo Lim <zirajs7@gmail.com> wrote:
>
> Hi,
>
> inet_csk_listen_stop() removes a request from the accept queue, locks the
> child socket with bh_lock_sock(), and then checks:
>
> while ((req = reqsk_queue_remove(queue, sk)) != NULL) {
> ...
> WARN_ON(sock_owned_by_user(child));
> ...
> }
>
> This assumes that no process context owns the child socket at this point.
> However, once a connection is completed for the listener, the child
> socket is also visible from the established hash table.
>
> In the normal connection-completion path, tcp_v4_syn_recv_sock() inserts
> the child into ehash via inet_ehash_nolisten(), and
> inet_csk_reqsk_queue_add() later stores the same child in req->sk on the
> listener accept queue. bpf_iter_tcp can then take the child from ehash and
> lock it in bpf_iter_tcp_seq_show().
>
> This is different from the recent bpf_iter_tcp_established_batch
> request_sock refcount issue
> (https://lore.kernel.org/all/20260803112218.3361213-1-dingning04@gmail.com/).
> In this case the socket is already a full child socket, so the iterator
> can successfully take a reference and then acquire lock_sock() before the
> listener teardown path reaches the child.
>
> If acquiring lock_sock() in the BPF iterator is valid for this socket,
It seems not.
->diag_destroy() should be okay as tcp_abort() acquires
bh_lock_sock(), but bpf_{g,s}etsockopt() look unsafe.
e.g. bpf_getsockopt(TCP_SAVED_SYN) might read freed data
or trigger null-ptr-deref.
But looking at 9378096e8a65, we cannot hold bh_lock_sock() at
->show() due to ->diag_destroy() (or we could release the lock just
before that, or hold lock_sock_nested() in inet_csk_listen_stop()).
> then the WARN_ON() in inet_csk_listen_stop() may simply encode an invariant
> that is no longer true. In that case, removing the WARN_ON() and
> documenting this case may be the right fix.
> Otherwise, bpf_iter_tcp may need to skip full child sockets that are still
> queued on the listener's accept queue.
I think this is not an option.
I'll look into the issue this weekend.
Thanks
>
> I included a C reproducer, below.
> - tested in the bpf/master at 7a3c0289c3c8eb4607dff448ae9ff9f902c813af
>
> This was found by a custom fuzzer developed
> by Sechang Lim <rhkrqnwk98@gmail.com>
>
> Thanks,
> Junseo Lim
>
> ---
>
> [ 5.970506] ------------[ cut here ]------------
> [ 5.970910] WARNING: net/ipv4/inet_connection_sock.c:1474 at inet_csk_listen_stop+0x85a/0xc10, CPU#0: bpf_iter_tcp_lo/90
> [ 5.971656] CPU: 0 UID: 0 PID: 90 Comm: bpf_iter_tcp_lo Not tainted 7.2.0-rc4-00484-g7a3c0289c3c8 #87 PREEMPT(full)
> [ 5.972340] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Arch Linux 1.17.0-2-2 04/01/2014
> [ 5.972955] RIP: 0010:inet_csk_listen_stop+0x85a/0xc10
> [ 5.973309] Code: 00 00 00 e8 b8 d5 f1 fe e9 7a f9 ff ff e8 8e 70 15 fe 48 8b 3c 24 be 02 00 00 00 e8 a0 d5 f1 fe e9 62 f9 ff ff e8 76 70 15 fe <0f> 0b e9 04 f9 ff ff e8 6a 70 15 fe e8 d5 20 04 fe 84 c0 0f 85 f2
> [ 5.974547] RSP: 0018:ffffc900004dfc98 EFLAGS: 00010293
> [ 5.976344] RAX: 0000000000000000 RBX: ffff888107f21a00 RCX: ffffffff83724aca
> [ 5.976918] RDX: ffff88810613c400 RSI: ffffffff84c7aae2 RDI: ffff888107f21bc0
> [ 5.977416] RBP: ffff8881073fb3e0 R08: 0000000000000001 R09: 0000000000000001
> [ 5.977960] R10: 0000000000000000 R11: fffff5200009bf87 R12: 0000000000000000
> [ 5.978466] R13: 0000000000000001 R14: dffffc0000000000 R15: ffff8881073b0d00
> [ 5.979354] FS: 00000000005cd400(0000) GS:ffff888193fda000(0000) knlGS:0000000000000000
> [ 5.979921] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [ 5.980685] CR2: 00007ffd196dd820 CR3: 0000000105d33000 CR4: 0000000000750ef0
> [ 5.981356] PKRU: 55555554
> [ 5.981547] Call Trace:
> [ 5.981723] <TASK>
> [ 5.981886] ? __pfx_sock_close+0x10/0x10
> [ 5.982174] __tcp_close+0xc04/0xfd0
> [ 5.982457] ? __pfx_sock_close+0x10/0x10
> [ 5.982743] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 5.983079] ? trace_hardirqs_on+0x18/0x1a0
> [ 5.983390] ? __pfx_sock_close+0x10/0x10
> [ 5.983675] tcp_close+0x31/0xf0
> [ 5.983915] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 5.984244] inet_release+0x121/0x2b0
> [ 5.984510] sock_close+0x96/0x250
> [ 5.984756] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 5.985090] ? security_file_release+0x2b/0x70
> [ 5.985404] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 5.985735] __fput+0x39f/0xa00
> [ 5.985973] fput_close_sync+0xf6/0x210
> [ 5.986243] ? __pfx_fput_close_sync+0x10/0x10
> [ 5.986587] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 5.986924] __x64_sys_close+0x96/0x110
> [ 5.987214] do_syscall_64+0xae/0x5e0
> [ 5.987475] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 5.987805] ? exc_page_fault+0x85/0xa0
> [ 5.988086] entry_SYSCALL_64_after_hwframe+0x76/0x7e
> [ 5.988433] RIP: 0033:0x4555ae
> [ 5.988652] Code: 4d 89 d8 e8 14 1f 00 00 4c 8b 5d f8 41 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 11 c9 c3 0f 1f 80 00 00 00 00 48 8b 45 10 0f 05 <c9> c3 83 e2 39 83 fa 08 75 e7 e8 03 ff ff ff 0f 1f 00 f3 0f 1e fa
> [ 5.989837] RSP: 002b:00007ffd196de830 EFLAGS: 00000202 ORIG_RAX: 0000000000000003
> [ 5.990585] RAX: ffffffffffffffda RBX: 0000000000000040 RCX: 00000000004555ae
> [ 5.991066] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000005
> [ 5.991552] RBP: 00007ffd196de840 R08: 0000000000000000 R09: 0000000000000000
> [ 5.992029] R10: 0000000000000000 R11: 0000000000000202 R12: 0000000000000040
> [ 5.992496] R13: 0000000000000005 R14: 0000000000000004 R15: 0000000000000045
> [ 5.992981] </TASK>
> [ 5.993145] Kernel panic - not syncing: kernel: panic_on_warn set ...
> [ 5.993593] CPU: 0 UID: 0 PID: 90 Comm: bpf_iter_tcp_lo Not tainted 7.2.0-rc4-00484-g7a3c0289c3c8 #87 PREEMPT(full)
> [ 5.994318] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Arch Linux 1.17.0-2-2 04/01/2014
> [ 5.994976] Call Trace:
> [ 5.995158] <TASK>
> [ 5.995315] dump_stack_lvl+0xb0/0x110
> [ 5.995591] vpanic+0x230/0x5b0
> [ 5.995824] panic+0xd0/0xd0
> [ 5.996045] ? __pfx_panic+0x10/0x10
> [ 5.996307] ? check_panic_on_warn.cold+0x5/0x34
> [ 5.996630] ? panic_on_this_cpu+0x1a/0x30
> [ 5.996920] ? inet_csk_listen_stop+0x85a/0xc10
> [ 5.997236] check_panic_on_warn.cold+0x19/0x34
> [ 5.997554] __warn.cold+0x294/0x2d1
> [ 5.997814] ? inet_csk_listen_stop+0x85a/0xc10
> [ 5.998135] __report_bug+0x2bf/0x470
> [ 5.998417] ? __pfx___report_bug+0x10/0x10
> [ 5.998716] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 5.999055] ? mark_held_locks+0x40/0x70
> [ 5.999330] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 5.999660] ? lockdep_hardirqs_on_prepare+0xdc/0x1d0
> [ 6.000016] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 6.000346] ? trace_hardirqs_on+0x18/0x1a0
> [ 6.000639] ? inet_csk_listen_stop+0x85a/0xc10
> [ 6.000958] report_bug+0xab/0x220
> [ 6.001199] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 6.001526] ? inet_csk_listen_stop+0x85c/0xc10
> [ 6.001843] handle_bug+0x273/0x2d0
> [ 6.002097] ? inet_csk_listen_stop+0x85a/0xc10
> [ 6.002444] exc_invalid_op+0x1d/0x60
> [ 6.002703] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 6.003048] asm_exc_invalid_op+0x1a/0x20
> [ 6.003327] RIP: 0010:inet_csk_listen_stop+0x85a/0xc10
> [ 6.003680] Code: 00 00 00 e8 b8 d5 f1 fe e9 7a f9 ff ff e8 8e 70 15 fe 48 8b 3c 24 be 02 00 00 00 e8 a0 d5 f1 fe e9 62 f9 ff ff e8 76 70 15 fe <0f> 0b e9 04 f9 ff ff e8 6a 70 15 fe e8 d5 20 04 fe 84 c0 0f 85 f2
> [ 6.004883] RSP: 0018:ffffc900004dfc98 EFLAGS: 00010293
> [ 6.005240] RAX: 0000000000000000 RBX: ffff888107f21a00 RCX: ffffffff83724aca
> [ 6.005710] RDX: ffff88810613c400 RSI: ffffffff84c7aae2 RDI: ffff888107f21bc0
> [ 6.006181] RBP: ffff8881073fb3e0 R08: 0000000000000001 R09: 0000000000000001
> [ 6.006733] R10: 0000000000000000 R11: fffff5200009bf87 R12: 0000000000000000
> [ 6.007784] R13: 0000000000000001 R14: dffffc0000000000 R15: ffff8881073b0d00
> [ 6.008799] ? inet_csk_listen_stop+0x85a/0xc10
> [ 6.009123] ? inet_csk_listen_stop+0x85a/0xc10
> [ 6.009443] ? __pfx_sock_close+0x10/0x10
> [ 6.009726] __tcp_close+0xc04/0xfd0
> [ 6.009985] ? __pfx_sock_close+0x10/0x10
> [ 6.010283] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 6.010613] ? trace_hardirqs_on+0x18/0x1a0
> [ 6.010907] ? __pfx_sock_close+0x10/0x10
> [ 6.011192] tcp_close+0x31/0xf0
> [ 6.011431] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 6.011769] inet_release+0x121/0x2b0
> [ 6.012043] sock_close+0x96/0x250
> [ 6.012292] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 6.012620] ? security_file_release+0x2b/0x70
> [ 6.012934] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 6.013263] __fput+0x39f/0xa00
> [ 6.013495] fput_close_sync+0xf6/0x210
> [ 6.013767] ? __pfx_fput_close_sync+0x10/0x10
> [ 6.014079] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 6.014424] __x64_sys_close+0x96/0x110
> [ 6.014698] do_syscall_64+0xae/0x5e0
> [ 6.014961] ? srso_alias_return_thunk+0x5/0xfbef5
> [ 6.015291] ? exc_page_fault+0x85/0xa0
> [ 6.015564] entry_SYSCALL_64_after_hwframe+0x76/0x7e
> [ 6.015910] RIP: 0033:0x4555ae
> [ 6.016127] Code: 4d 89 d8 e8 14 1f 00 00 4c 8b 5d f8 41 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 11 c9 c3 0f 1f 80 00 00 00 00 48 8b 45 10 0f 05 <c9> c3 83 e2 39 83 fa 08 75 e7 e8 03 ff ff ff 0f 1f 00 f3 0f 1e fa
> [ 6.017315] RSP: 002b:00007ffd196de830 EFLAGS: 00000202 ORIG_RAX: 0000000000000003
> [ 6.017814] RAX: ffffffffffffffda RBX: 0000000000000040 RCX: 00000000004555ae
> [ 6.018294] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000005
> [ 6.018758] RBP: 00007ffd196de840 R08: 0000000000000000 R09: 0000000000000000
> [ 6.019227] R10: 0000000000000000 R11: 0000000000000202 R12: 0000000000000040
> [ 6.019693] R13: 0000000000000005 R14: 0000000000000004 R15: 0000000000000045
> [ 6.020171] </TASK>
> [ 6.020459] Kernel Offset: disabled
> [ 6.020715] ---[ end Kernel panic - not syncing: kernel: panic_on_warn set ... ]---
>
> ---
>
> // SPDX-License-Identifier: GPL-2.0
> #define _GNU_SOURCE
>
> #include <arpa/inet.h>
> #include <errno.h>
> #include <fcntl.h>
> #include <linux/bpf.h>
> #include <linux/types.h>
> #include <netinet/in.h>
> #include <stdint.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <sys/resource.h>
> #include <sys/socket.h>
> #include <sys/syscall.h>
> #include <sys/types.h>
> #include <sys/wait.h>
> #include <unistd.h>
>
> #ifndef __NR_bpf
> #define __NR_bpf 321
> #endif
>
> #ifndef BPF_LINK_CREATE
> #define BPF_LINK_CREATE 28
> #endif
>
> #ifndef BPF_ITER_CREATE
> #define BPF_ITER_CREATE 33
> #endif
>
> #ifndef BPF_PROG_TYPE_TRACING
> #define BPF_PROG_TYPE_TRACING 26
> #endif
>
> #ifndef BPF_TRACE_ITER
> #define BPF_TRACE_ITER 28
> #endif
>
> #ifndef BPF_OBJ_NAME_LEN
> #define BPF_OBJ_NAME_LEN 16
> #endif
>
> #define DEFAULT_ROUNDS 20000
> #define DEFAULT_CLIENTS 64
> #define DEFAULT_WORKERS 1
> #define DEFAULT_BURN_INSNS 65536
> #define READ_BUF_SIZE 4096
>
> #define BTF_MAGIC 0xeb9f
> #define BTF_KIND_INT 1
> #define BTF_KIND_PTR 2
> #define BTF_KIND_ARRAY 3
> #define BTF_KIND_STRUCT 4
> #define BTF_KIND_UNION 5
> #define BTF_KIND_ENUM 6
> #define BTF_KIND_FWD 7
> #define BTF_KIND_TYPEDEF 8
> #define BTF_KIND_VOLATILE 9
> #define BTF_KIND_CONST 10
> #define BTF_KIND_RESTRICT 11
> #define BTF_KIND_FUNC 12
> #define BTF_KIND_FUNC_PROTO 13
> #define BTF_KIND_VAR 14
> #define BTF_KIND_DATASEC 15
> #define BTF_KIND_FLOAT 16
> #define BTF_KIND_DECL_TAG 17
> #define BTF_KIND_TYPE_TAG 18
> #define BTF_KIND_ENUM64 19
>
> #define BPF_ALU64_IMM(OP, DST, IMM) \
> ((struct bpf_insn){ .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
> .dst_reg = DST, .imm = IMM })
> #define BPF_EXIT_INSN() \
> ((struct bpf_insn){ .code = BPF_JMP | BPF_EXIT })
>
> union bpf_attr_local {
> struct {
> uint32_t prog_type;
> uint32_t insn_cnt;
> uint64_t insns;
> uint64_t license;
> uint32_t log_level;
> uint32_t log_size;
> uint64_t log_buf;
> uint32_t kern_version;
> uint32_t prog_flags;
> char prog_name[BPF_OBJ_NAME_LEN];
> uint32_t prog_ifindex;
> uint32_t expected_attach_type;
> uint32_t prog_btf_fd;
> uint32_t func_info_rec_size;
> uint64_t func_info;
> uint32_t func_info_cnt;
> uint32_t line_info_rec_size;
> uint64_t line_info;
> uint32_t line_info_cnt;
> uint32_t attach_btf_id;
> } prog_load;
> struct {
> uint32_t prog_fd;
> uint32_t target_fd;
> uint32_t attach_type;
> uint32_t flags;
> uint64_t iter_info;
> uint32_t iter_info_len;
> } link_create;
> struct {
> uint32_t link_fd;
> uint32_t flags;
> } iter_create;
> uint8_t pad[256];
> };
>
> struct btf_header {
> uint16_t magic;
> uint8_t version;
> uint8_t flags;
> uint32_t hdr_len;
> uint32_t type_off;
> uint32_t type_len;
> uint32_t str_off;
> uint32_t str_len;
> };
>
> struct btf_type {
> uint32_t name_off;
> uint32_t info;
> union {
> uint32_t size;
> uint32_t type;
> };
> };
>
> static volatile int stop_workers;
>
> static uint64_t ptr_to_u64(const void *ptr)
> {
> return (uint64_t)(uintptr_t)ptr;
> }
>
> static long sys_bpf(int cmd, union bpf_attr_local *attr)
> {
> return syscall(__NR_bpf, cmd, attr, sizeof(*attr));
> }
>
> static void *read_file(const char *path, size_t *size_out)
> {
> size_t done = 0;
> off_t size;
> char *buf;
> int fd;
>
> fd = open(path, O_RDONLY);
> if (fd < 0) {
> perror(path);
> return NULL;
> }
>
> size = lseek(fd, 0, SEEK_END);
> if (size <= 0 || lseek(fd, 0, SEEK_SET) < 0) {
> perror("lseek");
> close(fd);
> return NULL;
> }
>
> buf = malloc((size_t)size);
> if (!buf) {
> perror("malloc");
> close(fd);
> return NULL;
> }
>
> while (done < (size_t)size) {
> ssize_t n = read(fd, buf + done, (size_t)size - done);
>
> if (n < 0) {
> if (errno == EINTR)
> continue;
> perror("read");
> free(buf);
> close(fd);
> return NULL;
> }
> if (n == 0)
> break;
> done += (size_t)n;
> }
>
> close(fd);
> *size_out = done;
> return buf;
> }
>
> static uint32_t btf_kind(uint32_t info)
> {
> return (info >> 24) & 0x1f;
> }
>
> static uint32_t btf_vlen(uint32_t info)
> {
> return info & 0xffff;
> }
>
> static size_t btf_extra_size(const struct btf_type *t)
> {
> uint32_t kind = btf_kind(t->info);
> uint32_t vlen = btf_vlen(t->info);
>
> switch (kind) {
> case BTF_KIND_INT:
> case BTF_KIND_VAR:
> case BTF_KIND_DECL_TAG:
> return 4;
> case BTF_KIND_ARRAY:
> return 12;
> case BTF_KIND_STRUCT:
> case BTF_KIND_UNION:
> return (size_t)vlen * 12;
> case BTF_KIND_ENUM:
> case BTF_KIND_FUNC_PROTO:
> return (size_t)vlen * 8;
> case BTF_KIND_DATASEC:
> case BTF_KIND_ENUM64:
> return (size_t)vlen * 12;
> case BTF_KIND_PTR:
> case BTF_KIND_FWD:
> case BTF_KIND_TYPEDEF:
> case BTF_KIND_VOLATILE:
> case BTF_KIND_CONST:
> case BTF_KIND_RESTRICT:
> case BTF_KIND_FUNC:
> case BTF_KIND_FLOAT:
> case BTF_KIND_TYPE_TAG:
> return 0;
> default:
> fprintf(stderr, "unknown BTF kind %u\n", kind);
> exit(1);
> }
> }
>
> static int find_btf_func_id(const char *name)
> {
> const struct btf_header *hdr;
> const uint8_t *types, *types_end;
> const char *strs;
> size_t btf_size;
> void *btf;
> uint32_t id = 1;
>
> btf = read_file("/sys/kernel/btf/vmlinux", &btf_size);
> if (!btf)
> return -1;
> if (btf_size < sizeof(*hdr)) {
> fprintf(stderr, "short BTF header\n");
> free(btf);
> return -1;
> }
>
> hdr = btf;
> if (hdr->magic != BTF_MAGIC) {
> fprintf(stderr, "unexpected BTF magic 0x%x\n", hdr->magic);
> free(btf);
> return -1;
> }
> if ((size_t)hdr->hdr_len + hdr->type_off + hdr->type_len > btf_size ||
> (size_t)hdr->hdr_len + hdr->str_off + hdr->str_len > btf_size) {
> fprintf(stderr, "invalid BTF section bounds\n");
> free(btf);
> return -1;
> }
>
> types = (const uint8_t *)btf + hdr->hdr_len + hdr->type_off;
> types_end = types + hdr->type_len;
> strs = (const char *)btf + hdr->hdr_len + hdr->str_off;
>
> while (types + sizeof(struct btf_type) <= types_end) {
> const struct btf_type *t = (const struct btf_type *)types;
> size_t rec_size = sizeof(*t) + btf_extra_size(t);
>
> if (types + rec_size > types_end) {
> fprintf(stderr, "truncated BTF type section\n");
> free(btf);
> return -1;
> }
>
> if (btf_kind(t->info) == BTF_KIND_FUNC &&
> t->name_off < hdr->str_len &&
> strcmp(strs + t->name_off, name) == 0) {
> free(btf);
> return (int)id;
> }
>
> types += rec_size;
> id++;
> }
>
> fprintf(stderr, "BTF FUNC %s not found\n", name);
> free(btf);
> return -1;
> }
>
> static int load_tcp_iter_prog(uint32_t attach_btf_id, int burn_insns)
> {
> static char log_buf[1 << 20];
> static char license[] = "GPL";
> union bpf_attr_local attr;
> struct bpf_insn *insns;
> int insn_cnt;
> int saved_errno;
> int fd;
> int i;
>
> if (burn_insns < 0)
> burn_insns = 0;
>
> insn_cnt = burn_insns + 3;
> insns = calloc((size_t)insn_cnt, sizeof(*insns));
> if (!insns) {
> perror("calloc(insns)");
> return -1;
> }
>
> insns[0] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0);
> for (i = 1; i <= burn_insns; i++)
> insns[i] = BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1);
> insns[insn_cnt - 2] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0);
> insns[insn_cnt - 1] = BPF_EXIT_INSN();
>
> memset(&attr, 0, sizeof(attr));
> attr.prog_load.prog_type = BPF_PROG_TYPE_TRACING;
> attr.prog_load.insn_cnt = (uint32_t)insn_cnt;
> attr.prog_load.insns = ptr_to_u64(insns);
> attr.prog_load.license = ptr_to_u64(license);
> attr.prog_load.expected_attach_type = BPF_TRACE_ITER;
> attr.prog_load.attach_btf_id = attach_btf_id;
> memcpy(attr.prog_load.prog_name, "tcpit_burn", sizeof("tcpit_burn"));
>
> fd = (int)sys_bpf(BPF_PROG_LOAD, &attr);
> if (fd >= 0) {
> free(insns);
> return fd;
> }
>
> saved_errno = errno;
> if (saved_errno == ENOSPC) {
> fprintf(stderr,
> "BPF_PROG_LOAD without verifier log failed with ENOSPC; "
> "try a smaller burn_insns value\n");
> free(insns);
> errno = saved_errno;
> return -1;
> }
>
> attr.prog_load.log_level = 1;
> attr.prog_load.log_size = sizeof(log_buf);
> attr.prog_load.log_buf = ptr_to_u64(log_buf);
>
> fd = (int)sys_bpf(BPF_PROG_LOAD, &attr);
> if (fd < 0) {
> perror("BPF_PROG_LOAD(iter/tcp)");
> if (log_buf[0])
> fprintf(stderr, "verifier log:\n%s\n", log_buf);
> }
>
> free(insns);
> return fd;
> }
>
> static int create_iter_link(int prog_fd)
> {
> union bpf_attr_local attr;
> int link_fd;
>
> memset(&attr, 0, sizeof(attr));
> attr.link_create.prog_fd = (uint32_t)prog_fd;
> attr.link_create.target_fd = 0;
> attr.link_create.attach_type = BPF_TRACE_ITER;
> attr.link_create.flags = 0;
> attr.link_create.iter_info = 0;
> attr.link_create.iter_info_len = 0;
>
> link_fd = (int)sys_bpf(BPF_LINK_CREATE, &attr);
> if (link_fd < 0)
> perror("BPF_LINK_CREATE(iter/tcp)");
> return link_fd;
> }
>
> static int create_iter_fd(int link_fd)
> {
> union bpf_attr_local attr;
> int iter_fd;
>
> memset(&attr, 0, sizeof(attr));
> attr.iter_create.link_fd = (uint32_t)link_fd;
> attr.iter_create.flags = 0;
>
> iter_fd = (int)sys_bpf(BPF_ITER_CREATE, &attr);
> return iter_fd;
> }
>
> static void iter_worker(int link_fd)
> {
> char buf[READ_BUF_SIZE];
>
> while (!stop_workers) {
> int iter_fd = create_iter_fd(link_fd);
>
> if (iter_fd < 0)
> continue;
>
> for (;;) {
> ssize_t n = read(iter_fd, buf, sizeof(buf));
>
> if (n <= 0)
> break;
> }
> close(iter_fd);
> }
> _exit(0);
> }
>
> static int make_listener(uint16_t *port_out)
> {
> struct sockaddr_in addr;
> socklen_t len = sizeof(addr);
> int one = 1;
> int fd;
>
> fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
> if (fd < 0)
> return -1;
>
> setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
> setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
>
> memset(&addr, 0, sizeof(addr));
> addr.sin_family = AF_INET;
> addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> addr.sin_port = 0;
>
> if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
> goto fail;
> if (listen(fd, 256) < 0)
> goto fail;
> if (getsockname(fd, (struct sockaddr *)&addr, &len) < 0)
> goto fail;
>
> *port_out = ntohs(addr.sin_port);
> return fd;
>
> fail:
> close(fd);
> return -1;
> }
>
> static int connect_client(uint16_t port)
> {
> struct sockaddr_in addr;
> int fd;
>
> fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
> if (fd < 0)
> return -1;
>
> memset(&addr, 0, sizeof(addr));
> addr.sin_family = AF_INET;
> addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> addr.sin_port = htons(port);
>
> if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
> close(fd);
> return -1;
> }
>
> return fd;
> }
>
> static int fill_accept_queue(uint16_t port, int *clients, int max_clients)
> {
> int i, n = 0;
>
> for (i = 0; i < max_clients; i++) {
> int fd = connect_client(port);
>
> if (fd < 0)
> break;
> clients[n++] = fd;
> }
>
> return n;
> }
>
> static void close_clients(int *clients, int n)
> {
> int i;
>
> for (i = 0; i < n; i++)
> if (clients[i] >= 0)
> close(clients[i]);
> }
>
> static int run_round(int clients_per_round)
> {
> int *clients;
> uint16_t port;
> int listener;
> int n;
>
> clients = calloc((size_t)clients_per_round, sizeof(*clients));
> if (!clients)
> return -1;
>
> listener = make_listener(&port);
> if (listener < 0) {
> free(clients);
> return -1;
> }
>
> n = fill_accept_queue(port, clients, clients_per_round);
> usleep(100);
> close(listener);
> close_clients(clients, n);
> free(clients);
> return n > 0 ? 0 : -1;
> }
>
> int main(int argc, char **argv)
> {
> int rounds = DEFAULT_ROUNDS;
> int clients = DEFAULT_CLIENTS;
> int workers = DEFAULT_WORKERS;
> int burn_insns = DEFAULT_BURN_INSNS;
> pid_t *pids;
> int attach_btf_id;
> int prog_fd;
> int link_fd;
> int i;
>
> printf("[repro] rounds=%d clients=%d workers=%d burn_insns=%d\n",
> rounds, clients, workers, burn_insns);
>
> attach_btf_id = find_btf_func_id("bpf_iter_tcp");
> if (attach_btf_id < 0)
> return 1;
> printf("[repro] bpf_iter_tcp attach_btf_id=%d\n", attach_btf_id);
>
> prog_fd = load_tcp_iter_prog((uint32_t)attach_btf_id, burn_insns);
> if (prog_fd < 0)
> return 1;
>
> link_fd = create_iter_link(prog_fd);
> if (link_fd < 0)
> return 1;
>
> pids = calloc((size_t)workers, sizeof(*pids));
> if (!pids)
> return 1;
>
> for (i = 0; i < workers; i++) {
> pids[i] = fork();
> if (pids[i] == 0)
> iter_worker(link_fd);
> }
>
> for (i = 0; i < rounds; i++) {
> run_round(clients);
> if ((i & 0xff) == 0)
> fprintf(stderr, "[repro] round %d/%d\n", i, rounds);
> }
>
> stop_workers = 1;
> for (i = 0; i < workers; i++) {
> if (pids[i] > 0) {
> kill(pids[i], SIGTERM);
> waitpid(pids[i], NULL, 0);
> }
> }
>
> free(pids);
> close(link_fd);
> close(prog_fd);
> return 0;
> }
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-07 21:43 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-07 9:57 BUG: bpf_iter_tcp can trigger inet_csk_listen_stop() WARN_ON(sock_owned_by_user(child)) Junseo Lim
2026-08-07 21:43 ` Kuniyuki Iwashima
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox