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