diff options
| -rw-r--r-- | tools/testing/selftests/bpf/bpf_experimental.h | 3 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/prog_tests/cgroup_xattr.c | 145 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/progs/cgroup_read_xattr.c | 158 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/progs/read_cgroupfs_xattr.c | 60 | 
4 files changed, 366 insertions, 0 deletions
| diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h index 5e512a1d09d1..da7e230f2781 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -596,4 +596,7 @@ extern int bpf_iter_dmabuf_new(struct bpf_iter_dmabuf *it) __weak __ksym;  extern struct dma_buf *bpf_iter_dmabuf_next(struct bpf_iter_dmabuf *it) __weak __ksym;  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; +  #endif diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_xattr.c b/tools/testing/selftests/bpf/prog_tests/cgroup_xattr.c new file mode 100644 index 000000000000..87978a0f7eb7 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_xattr.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ + +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/xattr.h> + +#include <test_progs.h> + +#include "read_cgroupfs_xattr.skel.h" +#include "cgroup_read_xattr.skel.h" + +#define CGROUP_FS_ROOT "/sys/fs/cgroup/" +#define CGROUP_FS_PARENT CGROUP_FS_ROOT "foo/" +#define CGROUP_FS_CHILD CGROUP_FS_PARENT "bar/" + +static int move_pid_to_cgroup(const char *cgroup_folder, pid_t pid) +{ +	char filename[128]; +	char pid_str[64]; +	int procs_fd; +	int ret; + +	snprintf(filename, sizeof(filename), "%scgroup.procs", cgroup_folder); +	snprintf(pid_str, sizeof(pid_str), "%d", pid); + +	procs_fd = open(filename, O_WRONLY | O_APPEND); +	if (!ASSERT_OK_FD(procs_fd, "open")) +		return -1; + +	ret = write(procs_fd, pid_str, strlen(pid_str)); +	close(procs_fd); +	if (!ASSERT_GT(ret, 0, "write cgroup.procs")) +		return -1; +	return 0; +} + +static void reset_cgroups_and_lo(void) +{ +	rmdir(CGROUP_FS_CHILD); +	rmdir(CGROUP_FS_PARENT); +	system("ip addr del 1.1.1.1/32 dev lo"); +	system("ip link set dev lo down"); +} + +static const char xattr_value_a[] = "bpf_selftest_value_a"; +static const char xattr_value_b[] = "bpf_selftest_value_b"; +static const char xattr_name[] = "user.bpf_test"; + +static int setup_cgroups_and_lo(void) +{ +	int err; + +	err = mkdir(CGROUP_FS_PARENT, 0755); +	if (!ASSERT_OK(err, "mkdir 1")) +		goto error; +	err = mkdir(CGROUP_FS_CHILD, 0755); +	if (!ASSERT_OK(err, "mkdir 2")) +		goto error; + +	err = setxattr(CGROUP_FS_PARENT, xattr_name, xattr_value_a, +		       strlen(xattr_value_a) + 1, 0); +	if (!ASSERT_OK(err, "setxattr 1")) +		goto error; + +	err = setxattr(CGROUP_FS_CHILD, xattr_name, xattr_value_b, +		       strlen(xattr_value_b) + 1, 0); +	if (!ASSERT_OK(err, "setxattr 2")) +		goto error; + +	err = system("ip link set dev lo up"); +	if (!ASSERT_OK(err, "lo up")) +		goto error; + +	err = system("ip addr add 1.1.1.1 dev lo"); +	if (!ASSERT_OK(err, "lo addr v4")) +		goto error; + +	err = write_sysctl("/proc/sys/net/ipv4/ping_group_range", "0 0"); +	if (!ASSERT_OK(err, "write_sysctl")) +		goto error; + +	return 0; +error: +	reset_cgroups_and_lo(); +	return err; +} + +static void test_read_cgroup_xattr(void) +{ +	struct sockaddr_in sa4 = { +		.sin_family = AF_INET, +		.sin_addr.s_addr = htonl(INADDR_LOOPBACK), +	}; +	struct read_cgroupfs_xattr *skel = NULL; +	pid_t pid = gettid(); +	int sock_fd = -1; +	int connect_fd = -1; + +	if (!ASSERT_OK(setup_cgroups_and_lo(), "setup_cgroups_and_lo")) +		return; +	if (!ASSERT_OK(move_pid_to_cgroup(CGROUP_FS_CHILD, pid), +		       "move_pid_to_cgroup")) +		goto out; + +	skel = read_cgroupfs_xattr__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "read_cgroupfs_xattr__open_and_load")) +		goto out; + +	skel->bss->target_pid = pid; + +	if (!ASSERT_OK(read_cgroupfs_xattr__attach(skel), "read_cgroupfs_xattr__attach")) +		goto out; + +	sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); +	if (!ASSERT_OK_FD(sock_fd, "sock create")) +		goto out; + +	connect_fd = connect(sock_fd, &sa4, sizeof(sa4)); +	if (!ASSERT_OK_FD(connect_fd, "connect 1")) +		goto out; +	close(connect_fd); + +	ASSERT_TRUE(skel->bss->found_value_a, "found_value_a"); +	ASSERT_TRUE(skel->bss->found_value_b, "found_value_b"); + +out: +	close(connect_fd); +	close(sock_fd); +	read_cgroupfs_xattr__destroy(skel); +	move_pid_to_cgroup(CGROUP_FS_ROOT, pid); +	reset_cgroups_and_lo(); +} + +void test_cgroup_xattr(void) +{ +	RUN_TESTS(cgroup_read_xattr); + +	if (test__start_subtest("read_cgroupfs_xattr")) +		test_read_cgroup_xattr(); +} diff --git a/tools/testing/selftests/bpf/progs/cgroup_read_xattr.c b/tools/testing/selftests/bpf/progs/cgroup_read_xattr.c new file mode 100644 index 000000000000..092db1d0435e --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgroup_read_xattr.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ + +#include <vmlinux.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_core_read.h> +#include "bpf_experimental.h" +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +char value[16]; + +static __always_inline void read_xattr(struct cgroup *cgroup) +{ +	struct bpf_dynptr value_ptr; + +	bpf_dynptr_from_mem(value, sizeof(value), 0, &value_ptr); +	bpf_cgroup_read_xattr(cgroup, "user.bpf_test", +			      &value_ptr); +} + +SEC("lsm.s/socket_connect") +__success +int BPF_PROG(trusted_cgroup_ptr_sleepable) +{ +	u64 cgrp_id = bpf_get_current_cgroup_id(); +	struct cgroup *cgrp; + +	cgrp = bpf_cgroup_from_id(cgrp_id); +	if (!cgrp) +		return 0; + +	read_xattr(cgrp); +	bpf_cgroup_release(cgrp); +	return 0; +} + +SEC("lsm/socket_connect") +__success +int BPF_PROG(trusted_cgroup_ptr_non_sleepable) +{ +	u64 cgrp_id = bpf_get_current_cgroup_id(); +	struct cgroup *cgrp; + +	cgrp = bpf_cgroup_from_id(cgrp_id); +	if (!cgrp) +		return 0; + +	read_xattr(cgrp); +	bpf_cgroup_release(cgrp); +	return 0; +} + +SEC("lsm/socket_connect") +__success +int BPF_PROG(use_css_iter_non_sleepable) +{ +	u64 cgrp_id = bpf_get_current_cgroup_id(); +	struct cgroup_subsys_state *css; +	struct cgroup *cgrp; + +	cgrp = bpf_cgroup_from_id(cgrp_id); +	if (!cgrp) +		return 0; + +	bpf_for_each(css, css, &cgrp->self, BPF_CGROUP_ITER_ANCESTORS_UP) +		read_xattr(css->cgroup); + +	bpf_cgroup_release(cgrp); +	return 0; +} + +SEC("lsm.s/socket_connect") +__failure __msg("expected an RCU CS") +int BPF_PROG(use_css_iter_sleepable_missing_rcu_lock) +{ +	u64 cgrp_id = bpf_get_current_cgroup_id(); +	struct cgroup_subsys_state *css; +	struct cgroup *cgrp; + +	cgrp = bpf_cgroup_from_id(cgrp_id); +	if (!cgrp) +		return 0; + +	bpf_for_each(css, css, &cgrp->self, BPF_CGROUP_ITER_ANCESTORS_UP) +		read_xattr(css->cgroup); + +	bpf_cgroup_release(cgrp); +	return 0; +} + +SEC("lsm.s/socket_connect") +__success +int BPF_PROG(use_css_iter_sleepable_with_rcu_lock) +{ +	u64 cgrp_id = bpf_get_current_cgroup_id(); +	struct cgroup_subsys_state *css; +	struct cgroup *cgrp; + +	bpf_rcu_read_lock(); +	cgrp = bpf_cgroup_from_id(cgrp_id); +	if (!cgrp) +		goto out; + +	bpf_for_each(css, css, &cgrp->self, BPF_CGROUP_ITER_ANCESTORS_UP) +		read_xattr(css->cgroup); + +	bpf_cgroup_release(cgrp); +out: +	bpf_rcu_read_unlock(); +	return 0; +} + +SEC("lsm/socket_connect") +__success +int BPF_PROG(use_bpf_cgroup_ancestor) +{ +	u64 cgrp_id = bpf_get_current_cgroup_id(); +	struct cgroup *cgrp, *ancestor; + +	cgrp = bpf_cgroup_from_id(cgrp_id); +	if (!cgrp) +		return 0; + +	ancestor = bpf_cgroup_ancestor(cgrp, 1); +	if (!ancestor) +		goto out; + +	read_xattr(cgrp); +	bpf_cgroup_release(ancestor); +out: +	bpf_cgroup_release(cgrp); +	return 0; +} + +SEC("cgroup/sendmsg4") +__success +int BPF_PROG(cgroup_skb) +{ +	u64 cgrp_id = bpf_get_current_cgroup_id(); +	struct cgroup *cgrp, *ancestor; + +	cgrp = bpf_cgroup_from_id(cgrp_id); +	if (!cgrp) +		return 0; + +	ancestor = bpf_cgroup_ancestor(cgrp, 1); +	if (!ancestor) +		goto out; + +	read_xattr(cgrp); +	bpf_cgroup_release(ancestor); +out: +	bpf_cgroup_release(cgrp); +	return 0; +} diff --git a/tools/testing/selftests/bpf/progs/read_cgroupfs_xattr.c b/tools/testing/selftests/bpf/progs/read_cgroupfs_xattr.c new file mode 100644 index 000000000000..855f85fc5522 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/read_cgroupfs_xattr.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ + +#include <vmlinux.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_core_read.h> +#include "bpf_experimental.h" + +char _license[] SEC("license") = "GPL"; + +pid_t target_pid = 0; + +char xattr_value[64]; +static const char expected_value_a[] = "bpf_selftest_value_a"; +static const char expected_value_b[] = "bpf_selftest_value_b"; +bool found_value_a; +bool found_value_b; + +SEC("lsm.s/socket_connect") +int BPF_PROG(test_socket_connect) +{ +	u64 cgrp_id = bpf_get_current_cgroup_id(); +	struct cgroup_subsys_state *css, *tmp; +	struct bpf_dynptr value_ptr; +	struct cgroup *cgrp; + +	if ((bpf_get_current_pid_tgid() >> 32) != target_pid) +		return 0; + +	bpf_rcu_read_lock(); +	cgrp = bpf_cgroup_from_id(cgrp_id); +	if (!cgrp) { +		bpf_rcu_read_unlock(); +		return 0; +	} + +	css = &cgrp->self; +	bpf_dynptr_from_mem(xattr_value, sizeof(xattr_value), 0, &value_ptr); +	bpf_for_each(css, tmp, css, BPF_CGROUP_ITER_ANCESTORS_UP) { +		int ret; + +		ret = bpf_cgroup_read_xattr(tmp->cgroup, "user.bpf_test", +					    &value_ptr); +		if (ret < 0) +			continue; + +		if (ret == sizeof(expected_value_a) && +		    !bpf_strncmp(xattr_value, sizeof(expected_value_a), expected_value_a)) +			found_value_a = true; +		if (ret == sizeof(expected_value_b) && +		    !bpf_strncmp(xattr_value, sizeof(expected_value_b), expected_value_b)) +			found_value_b = true; +	} + +	bpf_rcu_read_unlock(); +	bpf_cgroup_release(cgrp); + +	return 0; +} | 
