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