1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2023 Red Hat, Inc. */
3 #include <test_progs.h>
4 #include "fentry_recursive.skel.h"
5 #include "fentry_recursive_target.skel.h"
6 #include <bpf/btf.h>
7 #include "bpf/libbpf_internal.h"
8 
9 /* Test recursive attachment of tracing progs with more than one nesting level
10  * is not possible. Create a chain of attachment, verify that the last prog
11  * will fail. Depending on the arguments, following cases are tested:
12  *
13  * - Recursive loading of tracing progs, without attaching (attach = false,
14  *   detach = false). The chain looks like this:
15  *       load target
16  *       load fentry1 -> target
17  *       load fentry2 -> fentry1 (fail)
18  *
19  * - Recursive attach of tracing progs (attach = true, detach = false). The
20  *   chain looks like this:
21  *       load target
22  *       load fentry1 -> target
23  *       attach fentry1 -> target
24  *       load fentry2 -> fentry1 (fail)
25  *
26  * - Recursive attach and detach of tracing progs (attach = true, detach =
27  *   true). This validates that attach_tracing_prog flag will be set throughout
28  *   the whole lifecycle of an fentry prog, independently from whether it's
29  *   detached. The chain looks like this:
30  *       load target
31  *       load fentry1 -> target
32  *       attach fentry1 -> target
33  *       detach fentry1
34  *       load fentry2 -> fentry1 (fail)
35  */
36 static void test_recursive_fentry_chain(bool attach, bool detach)
37 {
38 	struct fentry_recursive_target *target_skel = NULL;
39 	struct fentry_recursive *tracing_chain[2] = {};
40 	struct bpf_program *prog;
41 	int prev_fd, err;
42 
43 	target_skel = fentry_recursive_target__open_and_load();
44 	if (!ASSERT_OK_PTR(target_skel, "fentry_recursive_target__open_and_load"))
45 		return;
46 
47 	/* Create an attachment chain with two fentry progs */
48 	for (int i = 0; i < 2; i++) {
49 		tracing_chain[i] = fentry_recursive__open();
50 		if (!ASSERT_OK_PTR(tracing_chain[i], "fentry_recursive__open"))
51 			goto close_prog;
52 
53 		/* The first prog in the chain is going to be attached to the target
54 		 * fentry program, the second one to the previous in the chain.
55 		 */
56 		prog = tracing_chain[i]->progs.recursive_attach;
57 		if (i == 0) {
58 			prev_fd = bpf_program__fd(target_skel->progs.test1);
59 			err = bpf_program__set_attach_target(prog, prev_fd, "test1");
60 		} else {
61 			prev_fd = bpf_program__fd(tracing_chain[i-1]->progs.recursive_attach);
62 			err = bpf_program__set_attach_target(prog, prev_fd, "recursive_attach");
63 		}
64 
65 		if (!ASSERT_OK(err, "bpf_program__set_attach_target"))
66 			goto close_prog;
67 
68 		err = fentry_recursive__load(tracing_chain[i]);
69 		/* The first attach should succeed, the second fail */
70 		if (i == 0) {
71 			if (!ASSERT_OK(err, "fentry_recursive__load"))
72 				goto close_prog;
73 
74 			if (attach) {
75 				err = fentry_recursive__attach(tracing_chain[i]);
76 				if (!ASSERT_OK(err, "fentry_recursive__attach"))
77 					goto close_prog;
78 			}
79 
80 			if (detach) {
81 				/* Flag attach_tracing_prog should still be set, preventing
82 				 * attachment of the following prog.
83 				 */
84 				fentry_recursive__detach(tracing_chain[i]);
85 			}
86 		} else {
87 			if (!ASSERT_ERR(err, "fentry_recursive__load"))
88 				goto close_prog;
89 		}
90 	}
91 
92 close_prog:
93 	fentry_recursive_target__destroy(target_skel);
94 	for (int i = 0; i < 2; i++) {
95 		fentry_recursive__destroy(tracing_chain[i]);
96 	}
97 }
98 
99 void test_recursive_fentry(void)
100 {
101 	if (test__start_subtest("attach"))
102 		test_recursive_fentry_chain(true, false);
103 	if (test__start_subtest("load"))
104 		test_recursive_fentry_chain(false, false);
105 	if (test__start_subtest("detach"))
106 		test_recursive_fentry_chain(true, true);
107 }
108 
109 /* Test that a tracing prog reattachment (when we land in
110  * "prog->aux->dst_trampoline and tgt_prog is NULL" branch in
111  * bpf_tracing_prog_attach) does not lead to a crash due to missing attach_btf
112  */
113 void test_fentry_attach_btf_presence(void)
114 {
115 	struct fentry_recursive_target *target_skel = NULL;
116 	struct fentry_recursive *tracing_skel = NULL;
117 	struct bpf_program *prog;
118 	int err, link_fd, tgt_prog_fd;
119 
120 	target_skel = fentry_recursive_target__open_and_load();
121 	if (!ASSERT_OK_PTR(target_skel, "fentry_recursive_target__open_and_load"))
122 		goto close_prog;
123 
124 	tracing_skel = fentry_recursive__open();
125 	if (!ASSERT_OK_PTR(tracing_skel, "fentry_recursive__open"))
126 		goto close_prog;
127 
128 	prog = tracing_skel->progs.recursive_attach;
129 	tgt_prog_fd = bpf_program__fd(target_skel->progs.fentry_target);
130 	err = bpf_program__set_attach_target(prog, tgt_prog_fd, "fentry_target");
131 	if (!ASSERT_OK(err, "bpf_program__set_attach_target"))
132 		goto close_prog;
133 
134 	err = fentry_recursive__load(tracing_skel);
135 	if (!ASSERT_OK(err, "fentry_recursive__load"))
136 		goto close_prog;
137 
138 	tgt_prog_fd = bpf_program__fd(tracing_skel->progs.recursive_attach);
139 	link_fd = bpf_link_create(tgt_prog_fd, 0, BPF_TRACE_FENTRY, NULL);
140 	if (!ASSERT_GE(link_fd, 0, "link_fd"))
141 		goto close_prog;
142 
143 	fentry_recursive__detach(tracing_skel);
144 
145 	err = fentry_recursive__attach(tracing_skel);
146 	ASSERT_ERR(err, "fentry_recursive__attach");
147 
148 close_prog:
149 	fentry_recursive_target__destroy(target_skel);
150 	fentry_recursive__destroy(tracing_skel);
151 }
152