[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20260123-selftest-signal-on-connect-v1-2-b0256e7025b6@rbox.co>
Date: Fri, 23 Jan 2026 17:15:57 +0100
From: Michal Luczaj <mhal@...x.co>
To: Alexei Starovoitov <ast@...nel.org>,
Daniel Borkmann <daniel@...earbox.net>, Andrii Nakryiko <andrii@...nel.org>,
Martin KaFai Lau <martin.lau@...ux.dev>,
Eduard Zingerman <eddyz87@...il.com>, Song Liu <song@...nel.org>,
Yonghong Song <yonghong.song@...ux.dev>,
John Fastabend <john.fastabend@...il.com>, KP Singh <kpsingh@...nel.org>,
Stanislav Fomichev <sdf@...ichev.me>, Hao Luo <haoluo@...gle.com>,
Jiri Olsa <jolsa@...nel.org>, Shuah Khan <shuah@...nel.org>,
Kuniyuki Iwashima <kuniyu@...gle.com>
Cc: bpf@...r.kernel.org, linux-kselftest@...r.kernel.org,
linux-kernel@...r.kernel.org, Michal Luczaj <mhal@...x.co>
Subject: [PATCH bpf-next 2/2] selftests/bpf: Add test for connect() racing
sockmap update and signal
Attempt to trigger warnings/crashes by racing connect() against sockmap
updates and signals.
Follow-up to the discussion regarding af_vsock connect():
https://lore.kernel.org/netdev/20250311155601.eui5j2lta3q46i6u@gmail.com/
Suggested-by: John Fastabend <john.fastabend@...il.com>
Signed-off-by: Michal Luczaj <mhal@...x.co>
---
.../bpf/prog_tests/sockmap_interrupted_connect.c | 200 +++++++++++++++++++++
1 file changed, 200 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_interrupted_connect.c b/tools/testing/selftests/bpf/prog_tests/sockmap_interrupted_connect.c
new file mode 100644
index 000000000000..aa48ae483dab
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_interrupted_connect.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#include <linux/string.h>
+#include <linux/time64.h>
+#include <linux/vm_sockets.h>
+
+#include "linux/const.h"
+#include "test_progs.h"
+#include "sockmap_helpers.h"
+
+#define STR(s) #s
+#define XSTR(s) STR(s)
+
+#define TIMEOUT 5 /* seconds */
+#define INVALID 0x4242
+
+struct socket_spec {
+ int domain;
+ int sotype;
+};
+
+struct context {
+ struct sockaddr_storage addr, bad;
+ struct socket_spec *ss;
+ char str[MAX_TEST_NAME];
+ socklen_t alen;
+ int s, c, map;
+};
+
+static void handler(int signum)
+{
+ /* nop */
+}
+
+static void *racer(void *arg)
+{
+ struct context *ctx = arg;
+ int *map = &ctx->map;
+ pid_t pid = getpid();
+
+ for (;;) {
+ if (kill(pid, SIGUSR1)) {
+ FAIL_ERRNO("kill");
+ break;
+ }
+
+ (void)bpf_map_update_elem(*map, &u32(0), &ctx->c, BPF_ANY);
+ pthread_testcancel();
+ }
+
+ return NULL;
+}
+
+static void test_reconnect(struct context *ctx)
+{
+ struct socket_spec *ss = ctx->ss;
+ __u64 tout;
+
+ tout = get_time_ns() + TIMEOUT * NSEC_PER_SEC;
+ do {
+ int c;
+
+ c = xsocket(ss->domain, ss->sotype, 0);
+ if (c < 0)
+ break;
+ ctx->c = c;
+
+ if ((ss->sotype & SOCK_TYPE_MASK) == SOCK_DGRAM) {
+ struct sockaddr_storage ca;
+ socklen_t len;
+
+ init_addr_loopback(ss->domain, &ca, &len);
+ if (xbind(c, sockaddr(&ca), len)) {
+ xclose(c);
+ break;
+ }
+ }
+
+ (void)connect(c, (struct sockaddr *)&ctx->addr, ctx->alen);
+ (void)connect(c, (struct sockaddr *)&ctx->bad, ctx->alen);
+ (void)recv(c, &(char){}, 1, MSG_DONTWAIT);
+
+ for (;;) {
+ int p = accept(ctx->s, NULL, NULL);
+
+ if (p < 0)
+ break;
+ xclose(p);
+ }
+
+ xclose(c);
+ } while (get_time_ns() < tout);
+}
+
+#define __TEST_RECONNECT_ADDR(addr_struct, mangle, mangle_s) \
+ ({ \
+ char str[MAX_TEST_NAME * 2]; \
+ \
+ memcpy(&ctx->bad, &ctx->addr, ctx->alen); \
+ ((struct addr_struct *)&ctx->bad)->mangle; \
+ \
+ snprintf(str, sizeof(str), "%s %-24.24s ", ctx->str, mangle_s); \
+ if (test__start_subtest(str)) \
+ test_reconnect(ctx); \
+ })
+
+#define TEST_RECONNECT_ADDR(addr_struct, mangle) \
+ __TEST_RECONNECT_ADDR(addr_struct, mangle, XSTR(mangle))
+
+static void test_socket(struct context *ctx)
+{
+ struct socket_spec *ss = ctx->ss;
+ socklen_t alen;
+ int s;
+
+ s = socket_loopback(ss->domain, ss->sotype | SOCK_NONBLOCK);
+ if (s < 0)
+ return;
+
+ alen = sizeof(ctx->addr);
+ if (xgetsockname(s, sockaddr(&ctx->addr), &alen))
+ goto cleanup;
+
+ ctx->s = s;
+ ctx->alen = alen;
+ sprintf(ctx->str + strlen(ctx->str), "%-5s ", socket_kind_to_str(s));
+
+ switch (ss->domain) {
+ case AF_UNIX:
+ TEST_RECONNECT_ADDR(sockaddr_un, sun_family = AF_UNSPEC);
+ TEST_RECONNECT_ADDR(sockaddr_un, sun_path[0] = (char)INVALID);
+ break;
+ case AF_VSOCK:
+ TEST_RECONNECT_ADDR(sockaddr_vm, svm_cid = INVALID);
+ break;
+ default:
+ FAIL("Unknown socket domain %#x", ss->domain);
+ }
+
+cleanup:
+ xclose(s);
+}
+
+static void test_map(struct context *ctx, enum bpf_map_type type)
+{
+ /* Filter by any `struct proto` that defines psock_update_sk_prot() */
+ struct socket_spec *ss, sockets[] = {
+ { AF_UNIX, SOCK_STREAM },
+ { AF_UNIX, SOCK_DGRAM },
+ // { AF_UNIX, SOCK_SEQPACKET }, /* see unix_dgram_bpf_update_proto() */
+ { AF_VSOCK, SOCK_STREAM },
+ // { AF_VSOCK, SOCK_DGRAM }, /* see vsock_bpf_update_proto() */
+ { AF_VSOCK, SOCK_SEQPACKET },
+ };
+
+ ctx->map = bpf_map_create(type, NULL, sizeof(int), sizeof(int), 1, NULL);
+ if (!ASSERT_OK_FD(ctx->map, "map"))
+ return;
+
+ for (ss = sockets; ss < sockets + ARRAY_SIZE(sockets); ss++) {
+ sprintf(ctx->str, "%-3s ",
+ type == BPF_MAP_TYPE_SOCKMAP ? "map" : "?");
+ ctx->ss = ss;
+
+ test_socket(ctx);
+ }
+
+ xclose(ctx->map);
+}
+
+void serial_test_sockmap_interrupted_connect(void)
+{
+ sighandler_t orig_handler;
+ struct context ctx = {0};
+ pthread_t tid;
+
+ orig_handler = signal(SIGUSR1, handler);
+ if (!ASSERT_NEQ(orig_handler, SIG_ERR, "signal"))
+ return;
+
+ if (xpthread_create(&tid, NULL, racer, &ctx))
+ goto restore;
+
+ test_map(&ctx, BPF_MAP_TYPE_SOCKMAP);
+
+ if (!xpthread_cancel(tid))
+ xpthread_join(tid, NULL);
+restore:
+ ASSERT_NEQ(signal(SIGUSR1, orig_handler), SIG_ERR, "handler restore");
+}
--
2.52.0
Powered by blists - more mailing lists