summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/prog_tests/module_fentry_shadow.c
blob: aa9f67eb1c95b3c6ab6116b638db79b7986a68fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// 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"

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,
					   sizeof(trace_program) / sizeof(struct bpf_insn),
					   &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]);
	}
}