OSDN Git Service

selftests/bpf: Allow to use kfunc from testmod.ko in test_verifier
authorJiri Olsa <jolsa@kernel.org>
Mon, 15 May 2023 13:37:54 +0000 (15:37 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 17 May 2023 05:09:23 +0000 (22:09 -0700)
Currently the test_verifier allows test to specify kfunc symbol
and search for it in the kernel BTF.

Adding the possibility to search for kfunc also in bpf_testmod
module when it's not found in kernel BTF.

To find bpf_testmod btf we need to get back SYS_ADMIN cap.

Acked-by: David Vernet <void@manifault.com>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Link: https://lore.kernel.org/r/20230515133756.1658301-9-jolsa@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/test_verifier.c

index 285ea4a..71704a3 100644 (file)
@@ -874,8 +874,140 @@ static int create_map_kptr(void)
        return fd;
 }
 
+static void set_root(bool set)
+{
+       __u64 caps;
+
+       if (set) {
+               if (cap_enable_effective(1ULL << CAP_SYS_ADMIN, &caps))
+                       perror("cap_disable_effective(CAP_SYS_ADMIN)");
+       } else {
+               if (cap_disable_effective(1ULL << CAP_SYS_ADMIN, &caps))
+                       perror("cap_disable_effective(CAP_SYS_ADMIN)");
+       }
+}
+
+static __u64 ptr_to_u64(const void *ptr)
+{
+       return (uintptr_t) ptr;
+}
+
+static struct btf *btf__load_testmod_btf(struct btf *vmlinux)
+{
+       struct bpf_btf_info info;
+       __u32 len = sizeof(info);
+       struct btf *btf = NULL;
+       char name[64];
+       __u32 id = 0;
+       int err, fd;
+
+       /* Iterate all loaded BTF objects and find bpf_testmod,
+        * we need SYS_ADMIN cap for that.
+        */
+       set_root(true);
+
+       while (true) {
+               err = bpf_btf_get_next_id(id, &id);
+               if (err) {
+                       if (errno == ENOENT)
+                               break;
+                       perror("bpf_btf_get_next_id failed");
+                       break;
+               }
+
+               fd = bpf_btf_get_fd_by_id(id);
+               if (fd < 0) {
+                       if (errno == ENOENT)
+                               continue;
+                       perror("bpf_btf_get_fd_by_id failed");
+                       break;
+               }
+
+               memset(&info, 0, sizeof(info));
+               info.name_len = sizeof(name);
+               info.name = ptr_to_u64(name);
+               len = sizeof(info);
+
+               err = bpf_obj_get_info_by_fd(fd, &info, &len);
+               if (err) {
+                       close(fd);
+                       perror("bpf_obj_get_info_by_fd failed");
+                       break;
+               }
+
+               if (strcmp("bpf_testmod", name)) {
+                       close(fd);
+                       continue;
+               }
+
+               btf = btf__load_from_kernel_by_id_split(id, vmlinux);
+               if (!btf) {
+                       close(fd);
+                       break;
+               }
+
+               /* We need the fd to stay open so it can be used in fd_array.
+                * The final cleanup call to btf__free will free btf object
+                * and close the file descriptor.
+                */
+               btf__set_fd(btf, fd);
+               break;
+       }
+
+       set_root(false);
+       return btf;
+}
+
+static struct btf *testmod_btf;
+static struct btf *vmlinux_btf;
+
+static void kfuncs_cleanup(void)
+{
+       btf__free(testmod_btf);
+       btf__free(vmlinux_btf);
+}
+
+static void fixup_prog_kfuncs(struct bpf_insn *prog, int *fd_array,
+                             struct kfunc_btf_id_pair *fixup_kfunc_btf_id)
+{
+       /* Patch in kfunc BTF IDs */
+       while (fixup_kfunc_btf_id->kfunc) {
+               int btf_id = 0;
+
+               /* try to find kfunc in kernel BTF */
+               vmlinux_btf = vmlinux_btf ?: btf__load_vmlinux_btf();
+               if (vmlinux_btf) {
+                       btf_id = btf__find_by_name_kind(vmlinux_btf,
+                                                       fixup_kfunc_btf_id->kfunc,
+                                                       BTF_KIND_FUNC);
+                       btf_id = btf_id < 0 ? 0 : btf_id;
+               }
+
+               /* kfunc not found in kernel BTF, try bpf_testmod BTF */
+               if (!btf_id) {
+                       testmod_btf = testmod_btf ?: btf__load_testmod_btf(vmlinux_btf);
+                       if (testmod_btf) {
+                               btf_id = btf__find_by_name_kind(testmod_btf,
+                                                               fixup_kfunc_btf_id->kfunc,
+                                                               BTF_KIND_FUNC);
+                               btf_id = btf_id < 0 ? 0 : btf_id;
+                               if (btf_id) {
+                                       /* We put bpf_testmod module fd into fd_array
+                                        * and its index 1 into instruction 'off'.
+                                        */
+                                       *fd_array = btf__fd(testmod_btf);
+                                       prog[fixup_kfunc_btf_id->insn_idx].off = 1;
+                               }
+                       }
+               }
+
+               prog[fixup_kfunc_btf_id->insn_idx].imm = btf_id;
+               fixup_kfunc_btf_id++;
+       }
+}
+
 static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
-                         struct bpf_insn *prog, int *map_fds)
+                         struct bpf_insn *prog, int *map_fds, int *fd_array)
 {
        int *fixup_map_hash_8b = test->fixup_map_hash_8b;
        int *fixup_map_hash_48b = test->fixup_map_hash_48b;
@@ -900,7 +1032,6 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
        int *fixup_map_ringbuf = test->fixup_map_ringbuf;
        int *fixup_map_timer = test->fixup_map_timer;
        int *fixup_map_kptr = test->fixup_map_kptr;
-       struct kfunc_btf_id_pair *fixup_kfunc_btf_id = test->fixup_kfunc_btf_id;
 
        if (test->fill_helper) {
                test->fill_insns = calloc(MAX_TEST_INSNS, sizeof(struct bpf_insn));
@@ -1101,25 +1232,7 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
                } while (*fixup_map_kptr);
        }
 
-       /* Patch in kfunc BTF IDs */
-       if (fixup_kfunc_btf_id->kfunc) {
-               struct btf *btf;
-               int btf_id;
-
-               do {
-                       btf_id = 0;
-                       btf = btf__load_vmlinux_btf();
-                       if (btf) {
-                               btf_id = btf__find_by_name_kind(btf,
-                                                               fixup_kfunc_btf_id->kfunc,
-                                                               BTF_KIND_FUNC);
-                               btf_id = btf_id < 0 ? 0 : btf_id;
-                       }
-                       btf__free(btf);
-                       prog[fixup_kfunc_btf_id->insn_idx].imm = btf_id;
-                       fixup_kfunc_btf_id++;
-               } while (fixup_kfunc_btf_id->kfunc);
-       }
+       fixup_prog_kfuncs(prog, fd_array, test->fixup_kfunc_btf_id);
 }
 
 struct libcap {
@@ -1446,6 +1559,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
        int run_errs, run_successes;
        int map_fds[MAX_NR_MAPS];
        const char *expected_err;
+       int fd_array[2] = { -1, -1 };
        int saved_errno;
        int fixup_skips;
        __u32 pflags;
@@ -1459,7 +1573,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
        if (!prog_type)
                prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
        fixup_skips = skips;
-       do_test_fixup(test, prog_type, prog, map_fds);
+       do_test_fixup(test, prog_type, prog, map_fds, &fd_array[1]);
        if (test->fill_insns) {
                prog = test->fill_insns;
                prog_len = test->prog_len;
@@ -1493,6 +1607,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
        else
                opts.log_level = DEFAULT_LIBBPF_LOG_LEVEL;
        opts.prog_flags = pflags;
+       if (fd_array[1] != -1)
+               opts.fd_array = &fd_array[0];
 
        if ((prog_type == BPF_PROG_TYPE_TRACING ||
             prog_type == BPF_PROG_TYPE_LSM) && test->kfunc) {
@@ -1719,6 +1835,7 @@ static int do_test(bool unpriv, unsigned int from, unsigned int to)
        }
 
        unload_bpf_testmod(verbose);
+       kfuncs_cleanup();
 
        printf("Summary: %d PASSED, %d SKIPPED, %d FAILED\n", passes,
               skips, errors);