summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/prog_tests/recursive_attach.c
blob: 8100509e561b2ce2609243f29699b677de3eb5f1 (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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023 Red Hat, Inc. */
#include <test_progs.h>
#include "fentry_recursive.skel.h"
#include "fentry_recursive_target.skel.h"
#include <bpf/btf.h>
#include "bpf/libbpf_internal.h"

/* Test recursive attachment of tracing progs with more than one nesting level
 * is not possible. Create a chain of attachment, verify that the last prog
 * will fail. Depending on the arguments, following cases are tested:
 *
 * - Recursive loading of tracing progs, without attaching (attach = false,
 *   detach = false). The chain looks like this:
 *       load target
 *       load fentry1 -> target
 *       load fentry2 -> fentry1 (fail)
 *
 * - Recursive attach of tracing progs (attach = true, detach = false). The
 *   chain looks like this:
 *       load target
 *       load fentry1 -> target
 *       attach fentry1 -> target
 *       load fentry2 -> fentry1 (fail)
 *
 * - Recursive attach and detach of tracing progs (attach = true, detach =
 *   true). This validates that attach_tracing_prog flag will be set throughout
 *   the whole lifecycle of an fentry prog, independently from whether it's
 *   detached. The chain looks like this:
 *       load target
 *       load fentry1 -> target
 *       attach fentry1 -> target
 *       detach fentry1
 *       load fentry2 -> fentry1 (fail)
 */
static void test_recursive_fentry_chain(bool attach, bool detach)
{
	struct fentry_recursive_target *target_skel = NULL;
	struct fentry_recursive *tracing_chain[2] = {};
	struct bpf_program *prog;
	int prev_fd, err;

	target_skel = fentry_recursive_target__open_and_load();
	if (!ASSERT_OK_PTR(target_skel, "fentry_recursive_target__open_and_load"))
		return;

	/* Create an attachment chain with two fentry progs */
	for (int i = 0; i < 2; i++) {
		tracing_chain[i] = fentry_recursive__open();
		if (!ASSERT_OK_PTR(tracing_chain[i], "fentry_recursive__open"))
			goto close_prog;

		/* The first prog in the chain is going to be attached to the target
		 * fentry program, the second one to the previous in the chain.
		 */
		prog = tracing_chain[i]->progs.recursive_attach;
		if (i == 0) {
			prev_fd = bpf_program__fd(target_skel->progs.test1);
			err = bpf_program__set_attach_target(prog, prev_fd, "test1");
		} else {
			prev_fd = bpf_program__fd(tracing_chain[i-1]->progs.recursive_attach);
			err = bpf_program__set_attach_target(prog, prev_fd, "recursive_attach");
		}

		if (!ASSERT_OK(err, "bpf_program__set_attach_target"))
			goto close_prog;

		err = fentry_recursive__load(tracing_chain[i]);
		/* The first attach should succeed, the second fail */
		if (i == 0) {
			if (!ASSERT_OK(err, "fentry_recursive__load"))
				goto close_prog;

			if (attach) {
				err = fentry_recursive__attach(tracing_chain[i]);
				if (!ASSERT_OK(err, "fentry_recursive__attach"))
					goto close_prog;
			}

			if (detach) {
				/* Flag attach_tracing_prog should still be set, preventing
				 * attachment of the following prog.
				 */
				fentry_recursive__detach(tracing_chain[i]);
			}
		} else {
			if (!ASSERT_ERR(err, "fentry_recursive__load"))
				goto close_prog;
		}
	}

close_prog:
	fentry_recursive_target__destroy(target_skel);
	for (int i = 0; i < 2; i++) {
		fentry_recursive__destroy(tracing_chain[i]);
	}
}

void test_recursive_fentry(void)
{
	if (test__start_subtest("attach"))
		test_recursive_fentry_chain(true, false);
	if (test__start_subtest("load"))
		test_recursive_fentry_chain(false, false);
	if (test__start_subtest("detach"))
		test_recursive_fentry_chain(true, true);
}

/* Test that a tracing prog reattachment (when we land in
 * "prog->aux->dst_trampoline and tgt_prog is NULL" branch in
 * bpf_tracing_prog_attach) does not lead to a crash due to missing attach_btf
 */
void test_fentry_attach_btf_presence(void)
{
	struct fentry_recursive_target *target_skel = NULL;
	struct fentry_recursive *tracing_skel = NULL;
	struct bpf_program *prog;
	int err, link_fd, tgt_prog_fd;

	target_skel = fentry_recursive_target__open_and_load();
	if (!ASSERT_OK_PTR(target_skel, "fentry_recursive_target__open_and_load"))
		goto close_prog;

	tracing_skel = fentry_recursive__open();
	if (!ASSERT_OK_PTR(tracing_skel, "fentry_recursive__open"))
		goto close_prog;

	prog = tracing_skel->progs.recursive_attach;
	tgt_prog_fd = bpf_program__fd(target_skel->progs.fentry_target);
	err = bpf_program__set_attach_target(prog, tgt_prog_fd, "fentry_target");
	if (!ASSERT_OK(err, "bpf_program__set_attach_target"))
		goto close_prog;

	err = fentry_recursive__load(tracing_skel);
	if (!ASSERT_OK(err, "fentry_recursive__load"))
		goto close_prog;

	tgt_prog_fd = bpf_program__fd(tracing_skel->progs.recursive_attach);
	link_fd = bpf_link_create(tgt_prog_fd, 0, BPF_TRACE_FENTRY, NULL);
	if (!ASSERT_GE(link_fd, 0, "link_fd"))
		goto close_prog;

	fentry_recursive__detach(tracing_skel);

	err = fentry_recursive__attach(tracing_skel);
	ASSERT_ERR(err, "fentry_recursive__attach");

close_prog:
	fentry_recursive_target__destroy(target_skel);
	fentry_recursive__destroy(tracing_skel);
}