]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
selftests/bpf: Add retval test for bool and errno LSM cgroup hooks
authorXu Kuohai <xukuohai@huawei.com>
Wed, 10 Jun 2026 20:17:24 +0000 (20:17 +0000)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 13 Jun 2026 03:33:16 +0000 (20:33 -0700)
Add test to check the return value when a BPF program exits with 0 for
a boolean and an errno LSM hook.

For each hook, two BPF programs are attached. The first program returns
0 without calling bpf_set_retval() to exercise the return value translation
logic, while the second program reads the retval via bpf_get_retval().

Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
Link: https://lore.kernel.org/r/20260610201724.733943-3-xukuohai@huaweicloud.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/prog_tests/lsm_cgroup.c
tools/testing/selftests/bpf/progs/lsm_cgroup.c

index 6df25de8f080a0cbf1d699618fa0424605b45ae4..41e867467f6c60e292cd0006747b57125df03d98 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/xattr.h>
 #include <test_progs.h>
 #include <bpf/btf.h>
 
@@ -309,11 +310,89 @@ static void test_lsm_cgroup_nonvoid(void)
        lsm_cgroup_nonvoid__destroy(skel);
 }
 
+static void test_lsm_cgroup_retval(void)
+{
+       struct lsm_cgroup *skel = NULL;
+       int skipcap_prog_fd1, skipcap_prog_fd2, socket_prog_fd1, socket_prog_fd2;
+       int cgroup_fd = -1;
+       int err, fd;
+       char tmpfile[] = "/tmp/test_lsm_cgroup_retval.XXXXXX";
+
+       fd = mkstemp(tmpfile);
+       if (!ASSERT_OK_FD(fd, "mkstemp"))
+               return;
+       close(fd);
+
+       cgroup_fd = test__join_cgroup("/default_retval");
+       if (!ASSERT_OK_FD(cgroup_fd, "join_cgroup"))
+               goto cleanup_tmpfile;
+
+       skel = lsm_cgroup__open_and_load();
+       if (!ASSERT_OK_PTR(skel, "open_and_load"))
+               goto cleanup_cgroup;
+
+       skipcap_prog_fd1 = bpf_program__fd(skel->progs.skipcap_first);
+       skipcap_prog_fd2 = bpf_program__fd(skel->progs.skipcap_second);
+       socket_prog_fd1 = bpf_program__fd(skel->progs.socket_first);
+       socket_prog_fd2 = bpf_program__fd(skel->progs.socket_second);
+
+       err = bpf_prog_attach(skipcap_prog_fd1, cgroup_fd, BPF_LSM_CGROUP, BPF_F_ALLOW_MULTI);
+       if (err == -ENOTSUPP) {
+               test__skip();
+               goto cleanup_skeleton;
+       }
+       if (!ASSERT_OK(err, "attach first skipcap prog"))
+               goto cleanup_skeleton;
+
+       err = bpf_prog_attach(skipcap_prog_fd2, cgroup_fd, BPF_LSM_CGROUP, BPF_F_ALLOW_MULTI);
+       if (!ASSERT_OK(err, "attach second skipcap prog"))
+               goto cleanup_skipcap1;
+
+       err = bpf_prog_attach(socket_prog_fd1, cgroup_fd, BPF_LSM_CGROUP, BPF_F_ALLOW_MULTI);
+       if (!ASSERT_OK(err, "attach first sock_create prog"))
+               goto cleanup_skipcap2;
+
+       err = bpf_prog_attach(socket_prog_fd2, cgroup_fd, BPF_LSM_CGROUP, BPF_F_ALLOW_MULTI);
+       if (!ASSERT_OK(err, "attach second sock_create prog"))
+               goto cleanup_sock_create1;
+
+       /* trigger the bool hook by setxattr */
+       err = setxattr(tmpfile, "user.test", "value", 5, 0);
+       if (!ASSERT_OK(err, "setxattr"))
+               goto cleanup_sock_create2;
+
+       /* trigger the errno hook by creating a socket */
+       fd = socket(AF_INET, SOCK_STREAM, 0);
+       if (!ASSERT_OK_FD(fd, "socket"))
+               goto cleanup_sock_create2;
+       close(fd);
+
+       ASSERT_EQ(skel->data->skipcap_retval, 0, "bool_hook_retval_should_be_0");
+       ASSERT_EQ(skel->data->socket_retval, -EPERM, "errno_hook_retval_should_be_EPERM");
+
+cleanup_sock_create2:
+       bpf_prog_detach2(socket_prog_fd2, cgroup_fd, BPF_LSM_CGROUP);
+cleanup_sock_create1:
+       bpf_prog_detach2(socket_prog_fd1, cgroup_fd, BPF_LSM_CGROUP);
+cleanup_skipcap2:
+       bpf_prog_detach2(skipcap_prog_fd2, cgroup_fd, BPF_LSM_CGROUP);
+cleanup_skipcap1:
+       bpf_prog_detach2(skipcap_prog_fd1, cgroup_fd, BPF_LSM_CGROUP);
+cleanup_skeleton:
+       lsm_cgroup__destroy(skel);
+cleanup_cgroup:
+       close(cgroup_fd);
+cleanup_tmpfile:
+       unlink(tmpfile);
+}
+
 void test_lsm_cgroup(void)
 {
        if (test__start_subtest("functional"))
                test_lsm_cgroup_functional();
        if (test__start_subtest("nonvoid"))
                test_lsm_cgroup_nonvoid();
+       if (test__start_subtest("retval"))
+               test_lsm_cgroup_retval();
        btf__free(btf);
 }
index d7598538aa2dad1565727ffcfe4ed66ec6f8fc63..3bfa479104be8155822470848bc223348dfce4cb 100644 (file)
@@ -35,6 +35,8 @@ int called_socket_bind;
 int called_socket_bind2;
 int called_socket_alloc;
 int called_socket_clone;
+int skipcap_retval = -4095;
+int socket_retval = -4095;
 
 static __always_inline int test_local_storage(void)
 {
@@ -190,3 +192,31 @@ int BPF_PROG(socket_clone, struct sock *newsk, const struct request_sock *req)
 
        return 1;
 }
+
+SEC("lsm_cgroup/inode_xattr_skipcap")
+int BPF_PROG(skipcap_first, const char *name)
+{
+       return 0;
+}
+
+SEC("lsm_cgroup/inode_xattr_skipcap")
+int BPF_PROG(skipcap_second, const char *name)
+{
+       skipcap_retval = bpf_get_retval();
+       bpf_set_retval(0);
+       return 1;
+}
+
+SEC("lsm_cgroup/socket_create")
+int BPF_PROG(socket_first, int family, int type, int protocol, int kern)
+{
+       return 0;
+}
+
+SEC("lsm_cgroup/socket_create")
+int BPF_PROG(socket_second, int family, int type, int protocol, int kern)
+{
+       socket_retval = bpf_get_retval();
+       bpf_set_retval(0);
+       return 1;
+}