Add a selftest that loads the kfunc in sleepable and non-sleepable lsm/socket_connect programs and checks that a value set via fsetxattr() on a socket is read back. Signed-off-by: Christian Brauner (Amutable) --- tools/testing/selftests/bpf/bpf_experimental.h | 3 + .../testing/selftests/bpf/prog_tests/sock_xattr.c | 67 ++++++++++++++++++++++ .../testing/selftests/bpf/progs/sock_read_xattr.c | 54 +++++++++++++++++ 3 files changed, 124 insertions(+) diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h index 2234bd6bc9d3..5b825157b125 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -446,6 +446,9 @@ extern void bpf_iter_dmabuf_destroy(struct bpf_iter_dmabuf *it) __weak __ksym; extern int bpf_cgroup_read_xattr(struct cgroup *cgroup, const char *name__str, struct bpf_dynptr *value_p) __weak __ksym; +extern int bpf_sock_read_xattr(struct socket *sock, const char *name__str, + struct bpf_dynptr *value_p) __weak __ksym; + #define PREEMPT_BITS 8 #define SOFTIRQ_BITS 8 #define HARDIRQ_BITS 4 diff --git a/tools/testing/selftests/bpf/prog_tests/sock_xattr.c b/tools/testing/selftests/bpf/prog_tests/sock_xattr.c new file mode 100644 index 000000000000..b5816e90f01a --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/sock_xattr.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2026 Christian Brauner */ + +#include +#include +#include +#include +#include +#include +#include + +#include "sock_read_xattr.skel.h" + +static const char xattr_value[] = "bpf_sock_value"; +static const char xattr_name[] = "user.bpf_test"; + +static void test_read_sock_xattr(void) +{ + struct sockaddr_in addr = {}; + struct sock_read_xattr *skel = NULL; + struct bpf_link *link = NULL; + int sock_fd = -1, err; + + sock_fd = socket(AF_INET, SOCK_STREAM, 0); + if (!ASSERT_OK_FD(sock_fd, "socket")) + return; + + err = fsetxattr(sock_fd, xattr_name, xattr_value, sizeof(xattr_value), 0); + if (!ASSERT_OK(err, "fsetxattr")) + goto out; + + skel = sock_read_xattr__open_and_load(); + if (!ASSERT_OK_PTR(skel, "sock_read_xattr__open_and_load")) + goto out; + + skel->bss->monitored_pid = sys_gettid(); + + /* Only attach the functional program; the verifier-only programs + * above are not pid-gated and would clobber the shared globals. + */ + link = bpf_program__attach(skel->progs.read_sock_xattr); + if (!ASSERT_OK_PTR(link, "attach read_sock_xattr")) + goto out; + + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + /* Only the lsm/socket_connect hook matters; the connect may fail. */ + connect(sock_fd, (struct sockaddr *)&addr, sizeof(addr)); + + ASSERT_EQ(skel->data->read_ret, sizeof(xattr_value), "read_ret"); + ASSERT_STREQ(skel->bss->value, xattr_value, "value"); + +out: + bpf_link__destroy(link); + if (sock_fd >= 0) + close(sock_fd); + sock_read_xattr__destroy(skel); +} + +void test_sock_xattr(void) +{ + RUN_TESTS(sock_read_xattr); + + if (test__start_subtest("read_sock_xattr")) + test_read_sock_xattr(); +} diff --git a/tools/testing/selftests/bpf/progs/sock_read_xattr.c b/tools/testing/selftests/bpf/progs/sock_read_xattr.c new file mode 100644 index 000000000000..c4a8eae8cc3c --- /dev/null +++ b/tools/testing/selftests/bpf/progs/sock_read_xattr.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Christian Brauner */ + +#include +#include +#include +#include +#include "bpf_experimental.h" +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +char value[16]; +int read_ret = -1; +__u32 monitored_pid = 0; + +static __always_inline void read_xattr(struct socket *sock) +{ + struct bpf_dynptr value_ptr; + + bpf_dynptr_from_mem(value, sizeof(value), 0, &value_ptr); + bpf_sock_read_xattr(sock, "user.bpf_test", &value_ptr); +} + +SEC("lsm.s/socket_connect") +__success +int BPF_PROG(trusted_sock_ptr_sleepable, struct socket *sock) +{ + read_xattr(sock); + return 0; +} + +SEC("lsm/socket_connect") +__success +int BPF_PROG(trusted_sock_ptr_non_sleepable, struct socket *sock) +{ + read_xattr(sock); + return 0; +} + +SEC("lsm.s/socket_connect") +__success +int BPF_PROG(read_sock_xattr, struct socket *sock) +{ + struct bpf_dynptr value_ptr; + __u32 pid = bpf_get_current_pid_tgid() >> 32; + + if (pid != monitored_pid) + return 0; + + bpf_dynptr_from_mem(value, sizeof(value), 0, &value_ptr); + read_ret = bpf_sock_read_xattr(sock, "user.bpf_test", &value_ptr); + return 0; +} -- 2.47.3