From: Jakub Sitnicki <jakub@cloudflare.com>
To: Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Eduard Zingerman <eddyz87@gmail.com>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>,
Martin KaFai Lau <martin.lau@linux.dev>,
Song Liu <song@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>,
Jiri Olsa <jolsa@kernel.org>,
Emil Tsalapatis <emil@etsalapatis.com>,
Ihor Solodrai <ihor.solodrai@linux.dev>,
Shuah Khan <shuah@kernel.org>,
Eric Dumazet <edumazet@kernel.org>,
Kuniyuki Iwashima <kuniyu@google.com>,
Paolo Abeni <pabeni@redhat.com>,
Willem de Bruijn <willemb@google.com>,
John Fastabend <john.fastabend@gmail.com>,
Jiayuan Chen <jiayuan.chen@linux.dev>,
"David S. Miller" <davem@davemloft.net>,
Jakub Kicinski <kuba@kernel.org>,
Simon Horman <horms@kernel.org>
Cc: bpf@vger.kernel.org, netdev@vger.kernel.org, kernel-team@cloudflare.com
Subject: [PATCH bpf-next 1/2] selftests/bpf: Cover concurrent sockmap update and delete
Date: Fri, 30 Oct 2026 13:01:53 +0100 [thread overview]
Message-ID: <20261030-bpf-next-v1-1-035eabf8eae7@cloudflare.com> (raw)
In-Reply-To: <20261030-bpf-next-v1-0-035eabf8eae7@cloudflare.com>
Add a sockmap_listen subtest where one thread keeps inserting a TCP
socket into a sockmap with an attached verdict program while another
thread deletes the same map entry.
Without any synchronization between the insert and delete paths, both
end up racing on psock teardown and the socket's proto callbacks,
which KCSAN reports as:
==================================================================
BUG: KCSAN: data-race in sock_map_link / tcp_bpf_update_proto
write (marked) to 0xffff88810b1f42a8 of 8 bytes by task 198 on cpu 0:
tcp_bpf_update_proto+0x278/0x400
sock_map_unref+0x1d3/0x240
sock_map_delete_elem+0x7b/0xb0
map_delete_elem+0x1e1/0x270
__sys_bpf+0x71a/0x870
__x64_sys_bpf+0x69/0x80
x64_sys_call+0xf26/0x2550
do_syscall_64+0xb8/0x2f0
entry_SYSCALL_64_after_hwframe+0x76/0x7e
read to 0xffff88810b1f42a8 of 8 bytes by task 194 on cpu 3:
sock_map_link+0x146/0x5b0
sock_map_update_common+0x74/0x2b0
sock_map_update_elem_sys+0x246/0x2d0
bpf_map_update_value+0x80/0x400
map_update_elem+0x35c/0x3e0
__sys_bpf+0x540/0x870
__x64_sys_bpf+0x69/0x80
x64_sys_call+0xf26/0x2550
do_syscall_64+0xb8/0x2f0
entry_SYSCALL_64_after_hwframe+0x76/0x7e
value changed: 0xffffffff857540c0 -> 0xffffffff8468fde8
Reported by Kernel Concurrency Sanitizer on:
CPU: 3 UID: 0 PID: 194 Comm: test_progs Tainted: G OE 7.3.0+ #12 PREEMPT(full)
Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
==================================================================
Deleting an entry that is already gone is expected: sockmap reports
EINVAL and sockhash reports ENOENT. Re-inserting a socket whose psock
still holds the verdict program from a concurrent delete fails with
EBUSY. Tolerate these errnos; the test relies on KCSAN as the witness.
Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
---
.../selftests/bpf/prog_tests/sockmap_listen.c | 77 ++++++++++++++++++++++
1 file changed, 77 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
index 235e1d56f044..dca1f0de92e0 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
@@ -646,6 +646,82 @@ static void test_race_insert_listen(struct test_sockmap_listen *skel __always_un
xclose(s);
}
+struct update_delete_ctx {
+ unsigned int nr_iter;
+ unsigned int done;
+ int mapfd;
+};
+
+static void *delete_elem_thread(void *arg)
+{
+ struct update_delete_ctx *ctx = arg;
+ unsigned int i;
+
+ for (i = 0; i < ctx->nr_iter; i++) {
+ /* Expecting EINVAL (sockmap) or ENOENT (sockhash) when the
+ * entry is already gone.
+ */
+ if (bpf_map_delete_elem(ctx->mapfd, &(u32){0}) &&
+ errno != EINVAL && errno != ENOENT) {
+ FAIL_ERRNO("map_delete");
+ break;
+ }
+ }
+
+ WRITE_ONCE(ctx->done, 1);
+ return NULL;
+}
+
+/* One thread keeps inserting a socket into a sockmap with an attached
+ * verdict program while another thread deletes the same map entry.
+ */
+static void test_update_delete_with_prog(struct test_sockmap_listen *skel,
+ int family, int sotype, int mapfd)
+{
+ struct update_delete_ctx ctx = { 0 };
+ int err, s, verdict;
+ pthread_t t;
+ u64 value;
+
+ verdict = bpf_program__fd(skel->progs.prog_skb_verdict);
+ err = xbpf_prog_attach(verdict, mapfd, BPF_SK_SKB_VERDICT, 0);
+ if (err)
+ return;
+
+ s = xsocket(family, sotype, 0);
+ if (s < 0)
+ goto detach;
+
+ err = xlisten(s, 1);
+ if (err)
+ goto close;
+
+ ctx.mapfd = mapfd;
+ ctx.nr_iter = 10000;
+
+ err = xpthread_create(&t, NULL, delete_elem_thread, &ctx);
+ if (err)
+ goto close;
+
+ value = s;
+ while (!READ_ONCE(ctx.done)) {
+ /* Expecting EBUSY while a deleted socket's psock still
+ * holds the verdict program.
+ */
+ err = bpf_map_update_elem(mapfd, &(u32){0}, &value, BPF_ANY);
+ if (err && errno != EBUSY) {
+ FAIL_ERRNO("map_update");
+ break;
+ }
+ }
+
+ xpthread_join(t, NULL);
+close:
+ xclose(s);
+detach:
+ xbpf_prog_detach2(verdict, mapfd, BPF_SK_SKB_VERDICT);
+}
+
static void zero_verdict_count(int mapfd)
{
unsigned int zero = 0;
@@ -1289,6 +1365,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map,
TEST(test_destroy_orphan_child, SOCK_STREAM),
TEST(test_syn_recv_insert_delete, SOCK_STREAM),
TEST(test_race_insert_listen, SOCK_STREAM),
+ TEST(test_update_delete_with_prog, SOCK_STREAM),
/* child clone */
TEST(test_clone_after_delete, SOCK_STREAM),
TEST(test_accept_after_delete, SOCK_STREAM),
--
2.43.0
next prev parent reply other threads:[~2026-10-30 12:02 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 12:01 [PATCH bpf-next 0/2] Fix psock update-side data races on sk->sk_prot and progs Jakub Sitnicki
2026-10-30 12:01 ` Jakub Sitnicki [this message]
2026-10-30 12:01 ` [PATCH bpf-next 2/2] bpf, sockmap: " Jakub Sitnicki
2026-10-30 12:45 ` bot+bpf-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=20261030-bpf-next-v1-1-035eabf8eae7@cloudflare.com \
--to=jakub@cloudflare.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=eddyz87@gmail.com \
--cc=edumazet@kernel.org \
--cc=emil@etsalapatis.com \
--cc=horms@kernel.org \
--cc=ihor.solodrai@linux.dev \
--cc=jiayuan.chen@linux.dev \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=kernel-team@cloudflare.com \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=martin.lau@linux.dev \
--cc=memxor@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=shuah@kernel.org \
--cc=song@kernel.org \
--cc=willemb@google.com \
--cc=yonghong.song@linux.dev \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox