selftests/bpf: Add tests for _opts variants of bpf_*_get_fd_by_id()
authorRoberto Sassu <roberto.sassu@huawei.com>
Thu, 6 Oct 2022 11:07:36 +0000 (13:07 +0200)
committerAndrii Nakryiko <andrii@kernel.org>
Mon, 10 Oct 2022 23:49:43 +0000 (16:49 -0700)
Introduce the data_input map, write-protected with a small eBPF program
implementing the lsm/bpf_map hook.

Then, ensure that bpf_map_get_fd_by_id() and bpf_map_get_fd_by_id_opts()
with NULL opts don't succeed due to requesting read-write access to the
write-protected map. Also, ensure that bpf_map_get_fd_by_id_opts() with
open_flags in opts set to BPF_F_RDONLY instead succeeds.

After obtaining a read-only fd, ensure that only map lookup succeeds and
not update. Ensure that update works only with the read-write fd obtained
at program loading time, when the write protection was not yet enabled.

Finally, ensure that the other _opts variants of bpf_*_get_fd_by_id() don't
work if the BPF_F_RDONLY flag is set in opts (due to the kernel not
handling the open_flags member of bpf_attr).

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20221006110736.84253-7-roberto.sassu@huaweicloud.com
tools/testing/selftests/bpf/DENYLIST.s390x
tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c [new file with mode: 0644]

index 0fb03b8..beef123 100644 (file)
@@ -76,3 +76,4 @@ lookup_key                               # JIT does not support calling kernel f
 verify_pkcs7_sig                         # JIT does not support calling kernel function                                (kfunc)
 kfunc_dynptr_param                       # JIT does not support calling kernel function                                (kfunc)
 deny_namespace                           # failed to attach: ERROR: strerror_r(-524)=22                                (trampoline)
+libbpf_get_fd_by_id_opts                 # failed to attach: ERROR: strerror_r(-524)=22                                (trampoline)
diff --git a/tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c b/tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c
new file mode 100644 (file)
index 0000000..25e5dfa
--- /dev/null
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ */
+
+#include <test_progs.h>
+
+#include "test_libbpf_get_fd_by_id_opts.skel.h"
+
+void test_libbpf_get_fd_by_id_opts(void)
+{
+       struct test_libbpf_get_fd_by_id_opts *skel;
+       struct bpf_map_info info_m = {};
+       __u32 len = sizeof(info_m), value;
+       int ret, zero = 0, fd = -1;
+       LIBBPF_OPTS(bpf_get_fd_by_id_opts, fd_opts_rdonly,
+               .open_flags = BPF_F_RDONLY,
+       );
+
+       skel = test_libbpf_get_fd_by_id_opts__open_and_load();
+       if (!ASSERT_OK_PTR(skel,
+                          "test_libbpf_get_fd_by_id_opts__open_and_load"))
+               return;
+
+       ret = test_libbpf_get_fd_by_id_opts__attach(skel);
+       if (!ASSERT_OK(ret, "test_libbpf_get_fd_by_id_opts__attach"))
+               goto close_prog;
+
+       ret = bpf_obj_get_info_by_fd(bpf_map__fd(skel->maps.data_input),
+                                    &info_m, &len);
+       if (!ASSERT_OK(ret, "bpf_obj_get_info_by_fd"))
+               goto close_prog;
+
+       fd = bpf_map_get_fd_by_id(info_m.id);
+       if (!ASSERT_LT(fd, 0, "bpf_map_get_fd_by_id"))
+               goto close_prog;
+
+       fd = bpf_map_get_fd_by_id_opts(info_m.id, NULL);
+       if (!ASSERT_LT(fd, 0, "bpf_map_get_fd_by_id_opts"))
+               goto close_prog;
+
+       fd = bpf_map_get_fd_by_id_opts(info_m.id, &fd_opts_rdonly);
+       if (!ASSERT_GE(fd, 0, "bpf_map_get_fd_by_id_opts"))
+               goto close_prog;
+
+       /* Map lookup should work with read-only fd. */
+       ret = bpf_map_lookup_elem(fd, &zero, &value);
+       if (!ASSERT_OK(ret, "bpf_map_lookup_elem"))
+               goto close_prog;
+
+       if (!ASSERT_EQ(value, 0, "map value mismatch"))
+               goto close_prog;
+
+       /* Map update should not work with read-only fd. */
+       ret = bpf_map_update_elem(fd, &zero, &len, BPF_ANY);
+       if (!ASSERT_LT(ret, 0, "bpf_map_update_elem"))
+               goto close_prog;
+
+       /* Map update should work with read-write fd. */
+       ret = bpf_map_update_elem(bpf_map__fd(skel->maps.data_input), &zero,
+                                 &len, BPF_ANY);
+       if (!ASSERT_OK(ret, "bpf_map_update_elem"))
+               goto close_prog;
+
+       /* Prog get fd with opts set should not work (no kernel support). */
+       ret = bpf_prog_get_fd_by_id_opts(0, &fd_opts_rdonly);
+       if (!ASSERT_EQ(ret, -EINVAL, "bpf_prog_get_fd_by_id_opts"))
+               goto close_prog;
+
+       /* Link get fd with opts set should not work (no kernel support). */
+       ret = bpf_link_get_fd_by_id_opts(0, &fd_opts_rdonly);
+       if (!ASSERT_EQ(ret, -EINVAL, "bpf_link_get_fd_by_id_opts"))
+               goto close_prog;
+
+       /* BTF get fd with opts set should not work (no kernel support). */
+       ret = bpf_btf_get_fd_by_id_opts(0, &fd_opts_rdonly);
+       ASSERT_EQ(ret, -EINVAL, "bpf_btf_get_fd_by_id_opts");
+
+close_prog:
+       if (fd >= 0)
+               close(fd);
+
+       test_libbpf_get_fd_by_id_opts__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c b/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c
new file mode 100644 (file)
index 0000000..f5ac5f3
--- /dev/null
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ */
+
+#include "vmlinux.h"
+#include <errno.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+/* From include/linux/mm.h. */
+#define FMODE_WRITE    0x2
+
+struct {
+       __uint(type, BPF_MAP_TYPE_ARRAY);
+       __uint(max_entries, 1);
+       __type(key, __u32);
+       __type(value, __u32);
+} data_input SEC(".maps");
+
+char _license[] SEC("license") = "GPL";
+
+SEC("lsm/bpf_map")
+int BPF_PROG(check_access, struct bpf_map *map, fmode_t fmode)
+{
+       if (map != (struct bpf_map *)&data_input)
+               return 0;
+
+       if (fmode & FMODE_WRITE)
+               return -EACCES;
+
+       return 0;
+}