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/[email protected]/ Suggested-by: John Fastabend <[email protected]> Signed-off-by: Michal Luczaj <[email protected]> --- .../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

