Re: BUG: bpf_iter_tcp can trigger inet_csk_listen_stop() WARN_ON(sock_owned_by_user(child))
Kuniyuki Iwashima <[email protected]>
| Newsgroups | org.kernel.vger.bpf,org.kernel.vger.netdev |
|---|---|
| Message-ID | <CAAVpQUD6BuJnn289vfbaQN=akU00c1pW44nRQm4ZcL7_tY-D9A@mail.gmail.com> |
On Fri, Aug 7, 2026 at 2:57 AM Junseo Lim <[email protected]> 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/[email protected]/). > 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 <[email protected]> > > 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; > }