root/tools/testing/selftests/bpf/prog_tests/module_fentry_shadow.c
// 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]);
        }
}