xref: /linux/tools/testing/selftests/bpf/prog_tests/exceptions.c (revision 5fc5768c7ca92895ccd1de94dc521e5a55ae7896)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include <network_helpers.h>
4 
5 #include "exceptions.skel.h"
6 #include "exceptions_ext.skel.h"
7 #include "exceptions_fail.skel.h"
8 #include "exceptions_assert.skel.h"
9 
10 static char log_buf[1024 * 1024];
11 
test_exceptions_failure(void)12 static void test_exceptions_failure(void)
13 {
14 	RUN_TESTS(exceptions_fail);
15 }
16 
test_exceptions_success(void)17 static void test_exceptions_success(void)
18 {
19 	LIBBPF_OPTS(bpf_test_run_opts, ropts,
20 		.data_in = &pkt_v4,
21 		.data_size_in = sizeof(pkt_v4),
22 		.repeat = 1,
23 	);
24 	struct exceptions_ext *eskel = NULL;
25 	struct exceptions *skel;
26 	int ret;
27 
28 	skel = exceptions__open();
29 	if (!ASSERT_OK_PTR(skel, "exceptions__open"))
30 		return;
31 
32 	ret = exceptions__load(skel);
33 	if (!ASSERT_OK(ret, "exceptions__load"))
34 		goto done;
35 
36 	if (!ASSERT_OK(bpf_map_update_elem(bpf_map__fd(skel->maps.jmp_table), &(int){0},
37 					   &(int){bpf_program__fd(skel->progs.exception_tail_call_target)}, BPF_ANY),
38 		       "bpf_map_update_elem jmp_table"))
39 		goto done;
40 
41 #define RUN_SUCCESS(_prog, return_val)						  \
42 	if (!test__start_subtest(#_prog)) goto _prog##_##return_val;		  \
43 	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs._prog), &ropts); \
44 	ASSERT_OK(ret, #_prog " prog run ret");					  \
45 	ASSERT_EQ(ropts.retval, return_val, #_prog " prog run retval");		  \
46 	_prog##_##return_val:
47 
48 	RUN_SUCCESS(exception_throw_always_1, 64);
49 	RUN_SUCCESS(exception_throw_always_2, 32);
50 	RUN_SUCCESS(exception_throw_unwind_1, 16);
51 	RUN_SUCCESS(exception_throw_unwind_2, 32);
52 	RUN_SUCCESS(exception_throw_default, 0);
53 	RUN_SUCCESS(exception_throw_default_value, 5);
54 	RUN_SUCCESS(exception_tail_call, 24);
55 	RUN_SUCCESS(exception_ext, 0);
56 	RUN_SUCCESS(exception_ext_mod_cb_runtime, 35);
57 	RUN_SUCCESS(exception_throw_subprog, 1);
58 	RUN_SUCCESS(exception_throw_subprog_stack_cb, 0x1234);
59 	RUN_SUCCESS(exception_assert_nz_gfunc, 1);
60 	RUN_SUCCESS(exception_assert_zero_gfunc, 1);
61 	RUN_SUCCESS(exception_assert_neg_gfunc, 1);
62 	RUN_SUCCESS(exception_assert_pos_gfunc, 1);
63 	RUN_SUCCESS(exception_assert_negeq_gfunc, 1);
64 	RUN_SUCCESS(exception_assert_poseq_gfunc, 1);
65 	RUN_SUCCESS(exception_assert_nz_gfunc_with, 1);
66 	RUN_SUCCESS(exception_assert_zero_gfunc_with, 1);
67 	RUN_SUCCESS(exception_assert_neg_gfunc_with, 1);
68 	RUN_SUCCESS(exception_assert_pos_gfunc_with, 1);
69 	RUN_SUCCESS(exception_assert_negeq_gfunc_with, 1);
70 	RUN_SUCCESS(exception_assert_poseq_gfunc_with, 1);
71 	RUN_SUCCESS(exception_bad_assert_nz_gfunc, 0);
72 	RUN_SUCCESS(exception_bad_assert_zero_gfunc, 0);
73 	RUN_SUCCESS(exception_bad_assert_neg_gfunc, 0);
74 	RUN_SUCCESS(exception_bad_assert_pos_gfunc, 0);
75 	RUN_SUCCESS(exception_bad_assert_negeq_gfunc, 0);
76 	RUN_SUCCESS(exception_bad_assert_poseq_gfunc, 0);
77 	RUN_SUCCESS(exception_bad_assert_nz_gfunc_with, 100);
78 	RUN_SUCCESS(exception_bad_assert_zero_gfunc_with, 105);
79 	RUN_SUCCESS(exception_bad_assert_neg_gfunc_with, 200);
80 	RUN_SUCCESS(exception_bad_assert_pos_gfunc_with, 0);
81 	RUN_SUCCESS(exception_bad_assert_negeq_gfunc_with, 101);
82 	RUN_SUCCESS(exception_bad_assert_poseq_gfunc_with, 99);
83 	RUN_SUCCESS(exception_assert_range, 1);
84 	RUN_SUCCESS(exception_assert_range_with, 1);
85 	RUN_SUCCESS(exception_bad_assert_range, 0);
86 	RUN_SUCCESS(exception_bad_assert_range_with, 10);
87 	RUN_SUCCESS(exception_throw_from_void_global, 11);
88 
89 	if (skel->rodata->has_stack_arg) {
90 		RUN_SUCCESS(exception_throw_stack_arg, 56);
91 		RUN_SUCCESS(exception_throw_after_stack_arg, 56);
92 		RUN_SUCCESS(exception_throw_subprog_stack_arg, 56);
93 		RUN_SUCCESS(exception_throw_subprog_after_stack_arg, 56);
94 	}
95 
96 #define RUN_EXT(load_ret, attach_err, expr, msg, after_link)			  \
97 	{									  \
98 		LIBBPF_OPTS(bpf_object_open_opts, o, .kernel_log_buf = log_buf,		 \
99 						     .kernel_log_size = sizeof(log_buf), \
100 						     .kernel_log_level = 2);		 \
101 		exceptions_ext__destroy(eskel);					  \
102 		eskel = exceptions_ext__open_opts(&o);				  \
103 		struct bpf_program *prog = NULL;				  \
104 		struct bpf_link *link = NULL;					  \
105 		if (!ASSERT_OK_PTR(eskel, "exceptions_ext__open"))		  \
106 			goto done;						  \
107 		(expr);								  \
108 		ASSERT_OK_PTR(bpf_program__name(prog), bpf_program__name(prog));  \
109 		if (!ASSERT_EQ(exceptions_ext__load(eskel), load_ret,		  \
110 			       "exceptions_ext__load"))	{			  \
111 			printf("%s\n", log_buf);				  \
112 			goto done;						  \
113 		}								  \
114 		if (load_ret != 0) {						  \
115 			if (!ASSERT_OK_PTR(strstr(log_buf, msg), "strstr")) {	  \
116 				printf("%s\n", log_buf);			  \
117 				goto done;					  \
118 			}							  \
119 		}								  \
120 		if (!load_ret && attach_err) {					  \
121 			if (!ASSERT_ERR_PTR(link = bpf_program__attach(prog), "attach err")) \
122 				goto done;					  \
123 		} else if (!load_ret) {						  \
124 			if (!ASSERT_OK_PTR(link = bpf_program__attach(prog), "attach ok"))  \
125 				goto done;					  \
126 			(void)(after_link);					  \
127 			bpf_link__destroy(link);				  \
128 		}								  \
129 	}
130 
131 	if (test__start_subtest("non-throwing fentry -> exception_cb"))
132 		RUN_EXT(-EINVAL, true, ({
133 			prog = eskel->progs.pfentry;
134 			bpf_program__set_autoload(prog, true);
135 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
136 				       bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime),
137 				       "exception_cb_mod"), "set_attach_target"))
138 				goto done;
139 		}), "Tracing programs cannot attach to exception callback", 0);
140 
141 	if (test__start_subtest("throwing fentry -> exception_cb"))
142 		RUN_EXT(-EINVAL, true, ({
143 			prog = eskel->progs.throwing_fentry;
144 			bpf_program__set_autoload(prog, true);
145 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
146 				       bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime),
147 				       "exception_cb_mod"), "set_attach_target"))
148 				goto done;
149 		}), "Tracing programs cannot attach to exception callback", 0);
150 
151 	if (test__start_subtest("non-throwing fexit -> exception_cb"))
152 		RUN_EXT(-EINVAL, true, ({
153 			prog = eskel->progs.pfexit;
154 			bpf_program__set_autoload(prog, true);
155 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
156 				       bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime),
157 				       "exception_cb_mod"), "set_attach_target"))
158 				goto done;
159 		}), "Tracing programs cannot attach to exception callback", 0);
160 
161 	if (test__start_subtest("throwing fexit -> exception_cb"))
162 		RUN_EXT(-EINVAL, true, ({
163 			prog = eskel->progs.throwing_fexit;
164 			bpf_program__set_autoload(prog, true);
165 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
166 				       bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime),
167 				       "exception_cb_mod"), "set_attach_target"))
168 				goto done;
169 		}), "Tracing programs cannot attach to exception callback", 0);
170 
171 	if (test__start_subtest("throwing extension (with custom cb) -> exception_cb"))
172 		RUN_EXT(-EINVAL, true, ({
173 			prog = eskel->progs.throwing_exception_cb_extension;
174 			bpf_program__set_autoload(prog, true);
175 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
176 				       bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime),
177 				       "exception_cb_mod"), "set_attach_target"))
178 				goto done;
179 		}), "Extension programs cannot attach to exception callback", 0);
180 
181 	if (test__start_subtest("throwing extension -> global func in exception_cb"))
182 		RUN_EXT(0, false, ({
183 			prog = eskel->progs.throwing_exception_cb_extension;
184 			bpf_program__set_autoload(prog, true);
185 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
186 				       bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime),
187 				       "exception_cb_mod_global"), "set_attach_target"))
188 				goto done;
189 		}), "", ({ RUN_SUCCESS(exception_ext_mod_cb_runtime, 131); }));
190 
191 	if (test__start_subtest("throwing extension (with custom cb) -> global func in exception_cb"))
192 		RUN_EXT(0, false, ({
193 			prog = eskel->progs.throwing_extension;
194 			bpf_program__set_autoload(prog, true);
195 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
196 				       bpf_program__fd(skel->progs.exception_ext),
197 				       "exception_ext_global"), "set_attach_target"))
198 				goto done;
199 		}), "", ({ RUN_SUCCESS(exception_ext, 128); }));
200 
201 	if (test__start_subtest("non-throwing fentry -> non-throwing subprog"))
202 		/* non-throwing fentry -> non-throwing subprog : OK */
203 		RUN_EXT(0, false, ({
204 			prog = eskel->progs.pfentry;
205 			bpf_program__set_autoload(prog, true);
206 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
207 				       bpf_program__fd(skel->progs.exception_throw_subprog),
208 				       "subprog"), "set_attach_target"))
209 				goto done;
210 		}), "", 0);
211 
212 	if (test__start_subtest("throwing fentry -> non-throwing subprog"))
213 		/* throwing fentry -> non-throwing subprog : OK */
214 		RUN_EXT(0, false, ({
215 			prog = eskel->progs.throwing_fentry;
216 			bpf_program__set_autoload(prog, true);
217 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
218 				       bpf_program__fd(skel->progs.exception_throw_subprog),
219 				       "subprog"), "set_attach_target"))
220 				goto done;
221 		}), "", 0);
222 
223 	if (test__start_subtest("non-throwing fentry -> throwing subprog"))
224 		/* non-throwing fentry -> throwing subprog : OK */
225 		RUN_EXT(0, false, ({
226 			prog = eskel->progs.pfentry;
227 			bpf_program__set_autoload(prog, true);
228 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
229 				       bpf_program__fd(skel->progs.exception_throw_subprog),
230 				       "throwing_subprog"), "set_attach_target"))
231 				goto done;
232 		}), "", 0);
233 
234 	if (test__start_subtest("throwing fentry -> throwing subprog"))
235 		/* throwing fentry -> throwing subprog : OK */
236 		RUN_EXT(0, false, ({
237 			prog = eskel->progs.throwing_fentry;
238 			bpf_program__set_autoload(prog, true);
239 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
240 				       bpf_program__fd(skel->progs.exception_throw_subprog),
241 				       "throwing_subprog"), "set_attach_target"))
242 				goto done;
243 		}), "", 0);
244 
245 	if (test__start_subtest("non-throwing fexit -> non-throwing subprog"))
246 		/* non-throwing fexit -> non-throwing subprog : OK */
247 		RUN_EXT(0, false, ({
248 			prog = eskel->progs.pfexit;
249 			bpf_program__set_autoload(prog, true);
250 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
251 				       bpf_program__fd(skel->progs.exception_throw_subprog),
252 				       "subprog"), "set_attach_target"))
253 				goto done;
254 		}), "", 0);
255 
256 	if (test__start_subtest("throwing fexit -> non-throwing subprog"))
257 		/* throwing fexit -> non-throwing subprog : OK */
258 		RUN_EXT(0, false, ({
259 			prog = eskel->progs.throwing_fexit;
260 			bpf_program__set_autoload(prog, true);
261 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
262 				       bpf_program__fd(skel->progs.exception_throw_subprog),
263 				       "subprog"), "set_attach_target"))
264 				goto done;
265 		}), "", 0);
266 
267 	if (test__start_subtest("non-throwing fexit -> throwing subprog"))
268 		/* non-throwing fexit -> throwing subprog : OK */
269 		RUN_EXT(0, false, ({
270 			prog = eskel->progs.pfexit;
271 			bpf_program__set_autoload(prog, true);
272 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
273 				       bpf_program__fd(skel->progs.exception_throw_subprog),
274 				       "throwing_subprog"), "set_attach_target"))
275 				goto done;
276 		}), "", 0);
277 
278 	if (test__start_subtest("throwing fexit -> throwing subprog"))
279 		/* throwing fexit -> throwing subprog : OK */
280 		RUN_EXT(0, false, ({
281 			prog = eskel->progs.throwing_fexit;
282 			bpf_program__set_autoload(prog, true);
283 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
284 				       bpf_program__fd(skel->progs.exception_throw_subprog),
285 				       "throwing_subprog"), "set_attach_target"))
286 				goto done;
287 		}), "", 0);
288 
289 	/* fmod_ret not allowed for subprog - Check so we remember to handle its
290 	 * throwing specification compatibility with target when supported.
291 	 */
292 	if (test__start_subtest("non-throwing fmod_ret -> non-throwing subprog"))
293 		RUN_EXT(-EINVAL, true, ({
294 			prog = eskel->progs.pfmod_ret;
295 			bpf_program__set_autoload(prog, true);
296 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
297 				       bpf_program__fd(skel->progs.exception_throw_subprog),
298 				       "subprog"), "set_attach_target"))
299 				goto done;
300 		}), "can't modify return codes of BPF program", 0);
301 
302 	/* fmod_ret not allowed for subprog - Check so we remember to handle its
303 	 * throwing specification compatibility with target when supported.
304 	 */
305 	if (test__start_subtest("non-throwing fmod_ret -> non-throwing global subprog"))
306 		RUN_EXT(-EINVAL, true, ({
307 			prog = eskel->progs.pfmod_ret;
308 			bpf_program__set_autoload(prog, true);
309 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
310 				       bpf_program__fd(skel->progs.exception_throw_subprog),
311 				       "global_subprog"), "set_attach_target"))
312 				goto done;
313 		}), "can't modify return codes of BPF program", 0);
314 
315 	if (test__start_subtest("non-throwing extension -> non-throwing subprog"))
316 		/* non-throwing extension -> non-throwing subprog : BAD (!global) */
317 		RUN_EXT(-EINVAL, true, ({
318 			prog = eskel->progs.extension;
319 			bpf_program__set_autoload(prog, true);
320 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
321 				       bpf_program__fd(skel->progs.exception_throw_subprog),
322 				       "subprog"), "set_attach_target"))
323 				goto done;
324 		}), "subprog() is not a global function", 0);
325 
326 	if (test__start_subtest("non-throwing extension -> throwing subprog"))
327 		/* non-throwing extension -> throwing subprog : BAD (!global) */
328 		RUN_EXT(-EINVAL, true, ({
329 			prog = eskel->progs.extension;
330 			bpf_program__set_autoload(prog, true);
331 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
332 				       bpf_program__fd(skel->progs.exception_throw_subprog),
333 				       "throwing_subprog"), "set_attach_target"))
334 				goto done;
335 		}), "throwing_subprog() is not a global function", 0);
336 
337 	if (test__start_subtest("non-throwing extension -> non-throwing subprog"))
338 		/* non-throwing extension -> non-throwing global subprog : OK */
339 		RUN_EXT(0, false, ({
340 			prog = eskel->progs.extension;
341 			bpf_program__set_autoload(prog, true);
342 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
343 				       bpf_program__fd(skel->progs.exception_throw_subprog),
344 				       "global_subprog"), "set_attach_target"))
345 				goto done;
346 		}), "", 0);
347 
348 	if (test__start_subtest("non-throwing extension -> throwing global subprog"))
349 		/* non-throwing extension -> throwing global subprog : OK */
350 		RUN_EXT(0, false, ({
351 			prog = eskel->progs.extension;
352 			bpf_program__set_autoload(prog, true);
353 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
354 				       bpf_program__fd(skel->progs.exception_throw_subprog),
355 				       "throwing_global_subprog"), "set_attach_target"))
356 				goto done;
357 		}), "", 0);
358 
359 	if (test__start_subtest("throwing extension -> throwing global subprog"))
360 		/* throwing extension -> throwing global subprog : OK */
361 		RUN_EXT(0, false, ({
362 			prog = eskel->progs.throwing_extension;
363 			bpf_program__set_autoload(prog, true);
364 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
365 				       bpf_program__fd(skel->progs.exception_throw_subprog),
366 				       "throwing_global_subprog"), "set_attach_target"))
367 				goto done;
368 		}), "", 0);
369 
370 	if (test__start_subtest("throwing extension -> non-throwing global subprog"))
371 		/* throwing extension -> non-throwing global subprog : OK */
372 		RUN_EXT(0, false, ({
373 			prog = eskel->progs.throwing_extension;
374 			bpf_program__set_autoload(prog, true);
375 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
376 				       bpf_program__fd(skel->progs.exception_throw_subprog),
377 				       "global_subprog"), "set_attach_target"))
378 				goto done;
379 		}), "", 0);
380 
381 	if (test__start_subtest("non-throwing extension -> main subprog"))
382 		/* non-throwing extension -> main subprog : OK */
383 		RUN_EXT(0, false, ({
384 			prog = eskel->progs.extension;
385 			bpf_program__set_autoload(prog, true);
386 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
387 				       bpf_program__fd(skel->progs.exception_throw_subprog),
388 				       "exception_throw_subprog"), "set_attach_target"))
389 				goto done;
390 		}), "", 0);
391 
392 	if (test__start_subtest("throwing extension -> main subprog"))
393 		/* throwing extension -> main subprog : OK */
394 		RUN_EXT(0, false, ({
395 			prog = eskel->progs.throwing_extension;
396 			bpf_program__set_autoload(prog, true);
397 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
398 				       bpf_program__fd(skel->progs.exception_throw_subprog),
399 				       "exception_throw_subprog"), "set_attach_target"))
400 				goto done;
401 		}), "", 0);
402 
403 done:
404 	exceptions_ext__destroy(eskel);
405 	exceptions__destroy(skel);
406 }
407 
test_exceptions_assertions(void)408 static void test_exceptions_assertions(void)
409 {
410 	RUN_TESTS(exceptions_assert);
411 }
412 
test_exceptions(void)413 void test_exceptions(void)
414 {
415 	test_exceptions_success();
416 	test_exceptions_failure();
417 	test_exceptions_assertions();
418 }
419