Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add tests for bpf_cgroup_read_xattr

Add tests for different scenarios with bpf_cgroup_read_xattr:
1. Read cgroup xattr from bpf_cgroup_from_id;
2. Read cgroup xattr from bpf_cgroup_ancestor;
3. Read cgroup xattr from css_iter;
4. Use bpf_cgroup_read_xattr in LSM hook security_socket_connect.
5. Use bpf_cgroup_read_xattr in cgroup program.

Signed-off-by: Song Liu <song@kernel.org>
Link: https://lore.kernel.org/20250623063854.1896364-5-song@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>

authored by

Song Liu and committed by
Christian Brauner
21eebc65 5bc9557c

+366
+3
tools/testing/selftests/bpf/bpf_experimental.h
··· 596 596 extern struct dma_buf *bpf_iter_dmabuf_next(struct bpf_iter_dmabuf *it) __weak __ksym; 597 597 extern void bpf_iter_dmabuf_destroy(struct bpf_iter_dmabuf *it) __weak __ksym; 598 598 599 + extern int bpf_cgroup_read_xattr(struct cgroup *cgroup, const char *name__str, 600 + struct bpf_dynptr *value_p) __weak __ksym; 601 + 599 602 #endif
+145
tools/testing/selftests/bpf/prog_tests/cgroup_xattr.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3 + 4 + #include <errno.h> 5 + #include <fcntl.h> 6 + #include <sys/stat.h> 7 + #include <string.h> 8 + #include <unistd.h> 9 + #include <sys/socket.h> 10 + #include <sys/xattr.h> 11 + 12 + #include <test_progs.h> 13 + 14 + #include "read_cgroupfs_xattr.skel.h" 15 + #include "cgroup_read_xattr.skel.h" 16 + 17 + #define CGROUP_FS_ROOT "/sys/fs/cgroup/" 18 + #define CGROUP_FS_PARENT CGROUP_FS_ROOT "foo/" 19 + #define CGROUP_FS_CHILD CGROUP_FS_PARENT "bar/" 20 + 21 + static int move_pid_to_cgroup(const char *cgroup_folder, pid_t pid) 22 + { 23 + char filename[128]; 24 + char pid_str[64]; 25 + int procs_fd; 26 + int ret; 27 + 28 + snprintf(filename, sizeof(filename), "%scgroup.procs", cgroup_folder); 29 + snprintf(pid_str, sizeof(pid_str), "%d", pid); 30 + 31 + procs_fd = open(filename, O_WRONLY | O_APPEND); 32 + if (!ASSERT_OK_FD(procs_fd, "open")) 33 + return -1; 34 + 35 + ret = write(procs_fd, pid_str, strlen(pid_str)); 36 + close(procs_fd); 37 + if (!ASSERT_GT(ret, 0, "write cgroup.procs")) 38 + return -1; 39 + return 0; 40 + } 41 + 42 + static void reset_cgroups_and_lo(void) 43 + { 44 + rmdir(CGROUP_FS_CHILD); 45 + rmdir(CGROUP_FS_PARENT); 46 + system("ip addr del 1.1.1.1/32 dev lo"); 47 + system("ip link set dev lo down"); 48 + } 49 + 50 + static const char xattr_value_a[] = "bpf_selftest_value_a"; 51 + static const char xattr_value_b[] = "bpf_selftest_value_b"; 52 + static const char xattr_name[] = "user.bpf_test"; 53 + 54 + static int setup_cgroups_and_lo(void) 55 + { 56 + int err; 57 + 58 + err = mkdir(CGROUP_FS_PARENT, 0755); 59 + if (!ASSERT_OK(err, "mkdir 1")) 60 + goto error; 61 + err = mkdir(CGROUP_FS_CHILD, 0755); 62 + if (!ASSERT_OK(err, "mkdir 2")) 63 + goto error; 64 + 65 + err = setxattr(CGROUP_FS_PARENT, xattr_name, xattr_value_a, 66 + strlen(xattr_value_a) + 1, 0); 67 + if (!ASSERT_OK(err, "setxattr 1")) 68 + goto error; 69 + 70 + err = setxattr(CGROUP_FS_CHILD, xattr_name, xattr_value_b, 71 + strlen(xattr_value_b) + 1, 0); 72 + if (!ASSERT_OK(err, "setxattr 2")) 73 + goto error; 74 + 75 + err = system("ip link set dev lo up"); 76 + if (!ASSERT_OK(err, "lo up")) 77 + goto error; 78 + 79 + err = system("ip addr add 1.1.1.1 dev lo"); 80 + if (!ASSERT_OK(err, "lo addr v4")) 81 + goto error; 82 + 83 + err = write_sysctl("/proc/sys/net/ipv4/ping_group_range", "0 0"); 84 + if (!ASSERT_OK(err, "write_sysctl")) 85 + goto error; 86 + 87 + return 0; 88 + error: 89 + reset_cgroups_and_lo(); 90 + return err; 91 + } 92 + 93 + static void test_read_cgroup_xattr(void) 94 + { 95 + struct sockaddr_in sa4 = { 96 + .sin_family = AF_INET, 97 + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), 98 + }; 99 + struct read_cgroupfs_xattr *skel = NULL; 100 + pid_t pid = gettid(); 101 + int sock_fd = -1; 102 + int connect_fd = -1; 103 + 104 + if (!ASSERT_OK(setup_cgroups_and_lo(), "setup_cgroups_and_lo")) 105 + return; 106 + if (!ASSERT_OK(move_pid_to_cgroup(CGROUP_FS_CHILD, pid), 107 + "move_pid_to_cgroup")) 108 + goto out; 109 + 110 + skel = read_cgroupfs_xattr__open_and_load(); 111 + if (!ASSERT_OK_PTR(skel, "read_cgroupfs_xattr__open_and_load")) 112 + goto out; 113 + 114 + skel->bss->target_pid = pid; 115 + 116 + if (!ASSERT_OK(read_cgroupfs_xattr__attach(skel), "read_cgroupfs_xattr__attach")) 117 + goto out; 118 + 119 + sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); 120 + if (!ASSERT_OK_FD(sock_fd, "sock create")) 121 + goto out; 122 + 123 + connect_fd = connect(sock_fd, &sa4, sizeof(sa4)); 124 + if (!ASSERT_OK_FD(connect_fd, "connect 1")) 125 + goto out; 126 + close(connect_fd); 127 + 128 + ASSERT_TRUE(skel->bss->found_value_a, "found_value_a"); 129 + ASSERT_TRUE(skel->bss->found_value_b, "found_value_b"); 130 + 131 + out: 132 + close(connect_fd); 133 + close(sock_fd); 134 + read_cgroupfs_xattr__destroy(skel); 135 + move_pid_to_cgroup(CGROUP_FS_ROOT, pid); 136 + reset_cgroups_and_lo(); 137 + } 138 + 139 + void test_cgroup_xattr(void) 140 + { 141 + RUN_TESTS(cgroup_read_xattr); 142 + 143 + if (test__start_subtest("read_cgroupfs_xattr")) 144 + test_read_cgroup_xattr(); 145 + }
+158
tools/testing/selftests/bpf/progs/cgroup_read_xattr.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3 + 4 + #include <vmlinux.h> 5 + #include <bpf/bpf_tracing.h> 6 + #include <bpf/bpf_helpers.h> 7 + #include <bpf/bpf_core_read.h> 8 + #include "bpf_experimental.h" 9 + #include "bpf_misc.h" 10 + 11 + char _license[] SEC("license") = "GPL"; 12 + 13 + char value[16]; 14 + 15 + static __always_inline void read_xattr(struct cgroup *cgroup) 16 + { 17 + struct bpf_dynptr value_ptr; 18 + 19 + bpf_dynptr_from_mem(value, sizeof(value), 0, &value_ptr); 20 + bpf_cgroup_read_xattr(cgroup, "user.bpf_test", 21 + &value_ptr); 22 + } 23 + 24 + SEC("lsm.s/socket_connect") 25 + __success 26 + int BPF_PROG(trusted_cgroup_ptr_sleepable) 27 + { 28 + u64 cgrp_id = bpf_get_current_cgroup_id(); 29 + struct cgroup *cgrp; 30 + 31 + cgrp = bpf_cgroup_from_id(cgrp_id); 32 + if (!cgrp) 33 + return 0; 34 + 35 + read_xattr(cgrp); 36 + bpf_cgroup_release(cgrp); 37 + return 0; 38 + } 39 + 40 + SEC("lsm/socket_connect") 41 + __success 42 + int BPF_PROG(trusted_cgroup_ptr_non_sleepable) 43 + { 44 + u64 cgrp_id = bpf_get_current_cgroup_id(); 45 + struct cgroup *cgrp; 46 + 47 + cgrp = bpf_cgroup_from_id(cgrp_id); 48 + if (!cgrp) 49 + return 0; 50 + 51 + read_xattr(cgrp); 52 + bpf_cgroup_release(cgrp); 53 + return 0; 54 + } 55 + 56 + SEC("lsm/socket_connect") 57 + __success 58 + int BPF_PROG(use_css_iter_non_sleepable) 59 + { 60 + u64 cgrp_id = bpf_get_current_cgroup_id(); 61 + struct cgroup_subsys_state *css; 62 + struct cgroup *cgrp; 63 + 64 + cgrp = bpf_cgroup_from_id(cgrp_id); 65 + if (!cgrp) 66 + return 0; 67 + 68 + bpf_for_each(css, css, &cgrp->self, BPF_CGROUP_ITER_ANCESTORS_UP) 69 + read_xattr(css->cgroup); 70 + 71 + bpf_cgroup_release(cgrp); 72 + return 0; 73 + } 74 + 75 + SEC("lsm.s/socket_connect") 76 + __failure __msg("expected an RCU CS") 77 + int BPF_PROG(use_css_iter_sleepable_missing_rcu_lock) 78 + { 79 + u64 cgrp_id = bpf_get_current_cgroup_id(); 80 + struct cgroup_subsys_state *css; 81 + struct cgroup *cgrp; 82 + 83 + cgrp = bpf_cgroup_from_id(cgrp_id); 84 + if (!cgrp) 85 + return 0; 86 + 87 + bpf_for_each(css, css, &cgrp->self, BPF_CGROUP_ITER_ANCESTORS_UP) 88 + read_xattr(css->cgroup); 89 + 90 + bpf_cgroup_release(cgrp); 91 + return 0; 92 + } 93 + 94 + SEC("lsm.s/socket_connect") 95 + __success 96 + int BPF_PROG(use_css_iter_sleepable_with_rcu_lock) 97 + { 98 + u64 cgrp_id = bpf_get_current_cgroup_id(); 99 + struct cgroup_subsys_state *css; 100 + struct cgroup *cgrp; 101 + 102 + bpf_rcu_read_lock(); 103 + cgrp = bpf_cgroup_from_id(cgrp_id); 104 + if (!cgrp) 105 + goto out; 106 + 107 + bpf_for_each(css, css, &cgrp->self, BPF_CGROUP_ITER_ANCESTORS_UP) 108 + read_xattr(css->cgroup); 109 + 110 + bpf_cgroup_release(cgrp); 111 + out: 112 + bpf_rcu_read_unlock(); 113 + return 0; 114 + } 115 + 116 + SEC("lsm/socket_connect") 117 + __success 118 + int BPF_PROG(use_bpf_cgroup_ancestor) 119 + { 120 + u64 cgrp_id = bpf_get_current_cgroup_id(); 121 + struct cgroup *cgrp, *ancestor; 122 + 123 + cgrp = bpf_cgroup_from_id(cgrp_id); 124 + if (!cgrp) 125 + return 0; 126 + 127 + ancestor = bpf_cgroup_ancestor(cgrp, 1); 128 + if (!ancestor) 129 + goto out; 130 + 131 + read_xattr(cgrp); 132 + bpf_cgroup_release(ancestor); 133 + out: 134 + bpf_cgroup_release(cgrp); 135 + return 0; 136 + } 137 + 138 + SEC("cgroup/sendmsg4") 139 + __success 140 + int BPF_PROG(cgroup_skb) 141 + { 142 + u64 cgrp_id = bpf_get_current_cgroup_id(); 143 + struct cgroup *cgrp, *ancestor; 144 + 145 + cgrp = bpf_cgroup_from_id(cgrp_id); 146 + if (!cgrp) 147 + return 0; 148 + 149 + ancestor = bpf_cgroup_ancestor(cgrp, 1); 150 + if (!ancestor) 151 + goto out; 152 + 153 + read_xattr(cgrp); 154 + bpf_cgroup_release(ancestor); 155 + out: 156 + bpf_cgroup_release(cgrp); 157 + return 0; 158 + }
+60
tools/testing/selftests/bpf/progs/read_cgroupfs_xattr.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3 + 4 + #include <vmlinux.h> 5 + #include <bpf/bpf_tracing.h> 6 + #include <bpf/bpf_helpers.h> 7 + #include <bpf/bpf_core_read.h> 8 + #include "bpf_experimental.h" 9 + 10 + char _license[] SEC("license") = "GPL"; 11 + 12 + pid_t target_pid = 0; 13 + 14 + char xattr_value[64]; 15 + static const char expected_value_a[] = "bpf_selftest_value_a"; 16 + static const char expected_value_b[] = "bpf_selftest_value_b"; 17 + bool found_value_a; 18 + bool found_value_b; 19 + 20 + SEC("lsm.s/socket_connect") 21 + int BPF_PROG(test_socket_connect) 22 + { 23 + u64 cgrp_id = bpf_get_current_cgroup_id(); 24 + struct cgroup_subsys_state *css, *tmp; 25 + struct bpf_dynptr value_ptr; 26 + struct cgroup *cgrp; 27 + 28 + if ((bpf_get_current_pid_tgid() >> 32) != target_pid) 29 + return 0; 30 + 31 + bpf_rcu_read_lock(); 32 + cgrp = bpf_cgroup_from_id(cgrp_id); 33 + if (!cgrp) { 34 + bpf_rcu_read_unlock(); 35 + return 0; 36 + } 37 + 38 + css = &cgrp->self; 39 + bpf_dynptr_from_mem(xattr_value, sizeof(xattr_value), 0, &value_ptr); 40 + bpf_for_each(css, tmp, css, BPF_CGROUP_ITER_ANCESTORS_UP) { 41 + int ret; 42 + 43 + ret = bpf_cgroup_read_xattr(tmp->cgroup, "user.bpf_test", 44 + &value_ptr); 45 + if (ret < 0) 46 + continue; 47 + 48 + if (ret == sizeof(expected_value_a) && 49 + !bpf_strncmp(xattr_value, sizeof(expected_value_a), expected_value_a)) 50 + found_value_a = true; 51 + if (ret == sizeof(expected_value_b) && 52 + !bpf_strncmp(xattr_value, sizeof(expected_value_b), expected_value_b)) 53 + found_value_b = true; 54 + } 55 + 56 + bpf_rcu_read_unlock(); 57 + bpf_cgroup_release(cgrp); 58 + 59 + return 0; 60 + }