| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright (c) 2022 Red Hat */ |
| #include <test_progs.h> |
| #include <bpf/btf.h> |
| #include "bpf/libbpf_internal.h" |
| #include "cgroup_helpers.h" |
| #include "bpf_util.h" |
| |
| static const char *module_name = "bpf_testmod"; |
| static const char *symbol_name = "bpf_fentry_shadow_test"; |
| |
| static int get_bpf_testmod_btf_fd(void) |
| { |
| struct bpf_btf_info info; |
| char name[64]; |
| __u32 id = 0, len; |
| int err, fd; |
| |
| while (true) { |
| err = bpf_btf_get_next_id(id, &id); |
| if (err) { |
| log_err("failed to iterate BTF objects"); |
| return err; |
| } |
| |
| fd = bpf_btf_get_fd_by_id(id); |
| if (fd < 0) { |
| if (errno == ENOENT) |
| continue; /* expected race: BTF was unloaded */ |
| err = -errno; |
| log_err("failed to get FD for BTF object #%d", id); |
| return err; |
| } |
| |
| len = sizeof(info); |
| memset(&info, 0, sizeof(info)); |
| info.name = ptr_to_u64(name); |
| info.name_len = sizeof(name); |
| |
| err = bpf_obj_get_info_by_fd(fd, &info, &len); |
| if (err) { |
| err = -errno; |
| log_err("failed to get info for BTF object #%d", id); |
| close(fd); |
| return err; |
| } |
| |
| if (strcmp(name, module_name) == 0) |
| return fd; |
| |
| close(fd); |
| } |
| return -ENOENT; |
| } |
| |
| void test_module_fentry_shadow(void) |
| { |
| struct btf *vmlinux_btf = NULL, *mod_btf = NULL; |
| int err, i; |
| int btf_fd[2] = {}; |
| int prog_fd[2] = {}; |
| int link_fd[2] = {}; |
| __s32 btf_id[2] = {}; |
| |
| if (!env.has_testmod) { |
| test__skip(); |
| return; |
| } |
| |
| LIBBPF_OPTS(bpf_prog_load_opts, load_opts, |
| .expected_attach_type = BPF_TRACE_FENTRY, |
| ); |
| |
| const struct bpf_insn trace_program[] = { |
| BPF_MOV64_IMM(BPF_REG_0, 0), |
| BPF_EXIT_INSN(), |
| }; |
| |
| vmlinux_btf = btf__load_vmlinux_btf(); |
| if (!ASSERT_OK_PTR(vmlinux_btf, "load_vmlinux_btf")) |
| return; |
| |
| btf_fd[1] = get_bpf_testmod_btf_fd(); |
| if (!ASSERT_GE(btf_fd[1], 0, "get_bpf_testmod_btf_fd")) |
| goto out; |
| |
| mod_btf = btf_get_from_fd(btf_fd[1], vmlinux_btf); |
| if (!ASSERT_OK_PTR(mod_btf, "btf_get_from_fd")) |
| goto out; |
| |
| btf_id[0] = btf__find_by_name_kind(vmlinux_btf, symbol_name, BTF_KIND_FUNC); |
| if (!ASSERT_GT(btf_id[0], 0, "btf_find_by_name")) |
| goto out; |
| |
| btf_id[1] = btf__find_by_name_kind(mod_btf, symbol_name, BTF_KIND_FUNC); |
| if (!ASSERT_GT(btf_id[1], 0, "btf_find_by_name")) |
| goto out; |
| |
| for (i = 0; i < 2; i++) { |
| load_opts.attach_btf_id = btf_id[i]; |
| load_opts.attach_btf_obj_fd = btf_fd[i]; |
| prog_fd[i] = bpf_prog_load(BPF_PROG_TYPE_TRACING, NULL, "GPL", |
| trace_program, |
| ARRAY_SIZE(trace_program), |
| &load_opts); |
| if (!ASSERT_GE(prog_fd[i], 0, "bpf_prog_load")) |
| goto out; |
| |
| /* If the verifier incorrectly resolves addresses of the |
| * shadowed functions and uses the same address for both the |
| * vmlinux and the bpf_testmod functions, this will fail on |
| * attempting to create two trampolines for the same address, |
| * which is forbidden. |
| */ |
| link_fd[i] = bpf_link_create(prog_fd[i], 0, BPF_TRACE_FENTRY, NULL); |
| if (!ASSERT_GE(link_fd[i], 0, "bpf_link_create")) |
| goto out; |
| } |
| |
| err = bpf_prog_test_run_opts(prog_fd[0], NULL); |
| ASSERT_OK(err, "running test"); |
| |
| out: |
| btf__free(vmlinux_btf); |
| btf__free(mod_btf); |
| for (i = 0; i < 2; i++) { |
| if (btf_fd[i]) |
| close(btf_fd[i]); |
| if (prog_fd[i] > 0) |
| close(prog_fd[i]); |
| if (link_fd[i] > 0) |
| close(link_fd[i]); |
| } |
| } |