xref: /linux/tools/testing/selftests/bpf/prog_tests/exceptions.c (revision beb8592d041626532d01dc42f8010ba37953c1fc)
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 
87 #define RUN_EXT(load_ret, attach_err, expr, msg, after_link)			  \
88 	{									  \
89 		LIBBPF_OPTS(bpf_object_open_opts, o, .kernel_log_buf = log_buf,		 \
90 						     .kernel_log_size = sizeof(log_buf), \
91 						     .kernel_log_level = 2);		 \
92 		exceptions_ext__destroy(eskel);					  \
93 		eskel = exceptions_ext__open_opts(&o);				  \
94 		struct bpf_program *prog = NULL;				  \
95 		struct bpf_link *link = NULL;					  \
96 		if (!ASSERT_OK_PTR(eskel, "exceptions_ext__open"))		  \
97 			goto done;						  \
98 		(expr);								  \
99 		ASSERT_OK_PTR(bpf_program__name(prog), bpf_program__name(prog));  \
100 		if (!ASSERT_EQ(exceptions_ext__load(eskel), load_ret,		  \
101 			       "exceptions_ext__load"))	{			  \
102 			printf("%s\n", log_buf);				  \
103 			goto done;						  \
104 		}								  \
105 		if (load_ret != 0) {						  \
106 			printf("%s\n", log_buf);				  \
107 			if (!ASSERT_OK_PTR(strstr(log_buf, msg), "strstr"))	  \
108 				goto done;					  \
109 		}								  \
110 		if (!load_ret && attach_err) {					  \
111 			if (!ASSERT_ERR_PTR(link = bpf_program__attach(prog), "attach err")) \
112 				goto done;					  \
113 		} else if (!load_ret) {						  \
114 			if (!ASSERT_OK_PTR(link = bpf_program__attach(prog), "attach ok"))  \
115 				goto done;					  \
116 			(void)(after_link);					  \
117 			bpf_link__destroy(link);				  \
118 		}								  \
119 	}
120 
121 	if (test__start_subtest("non-throwing fentry -> exception_cb"))
122 		RUN_EXT(-EINVAL, true, ({
123 			prog = eskel->progs.pfentry;
124 			bpf_program__set_autoload(prog, true);
125 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
126 				       bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime),
127 				       "exception_cb_mod"), "set_attach_target"))
128 				goto done;
129 		}), "FENTRY/FEXIT programs cannot attach to exception callback", 0);
130 
131 	if (test__start_subtest("throwing fentry -> exception_cb"))
132 		RUN_EXT(-EINVAL, true, ({
133 			prog = eskel->progs.throwing_fentry;
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 		}), "FENTRY/FEXIT programs cannot attach to exception callback", 0);
140 
141 	if (test__start_subtest("non-throwing fexit -> exception_cb"))
142 		RUN_EXT(-EINVAL, true, ({
143 			prog = eskel->progs.pfexit;
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 		}), "FENTRY/FEXIT programs cannot attach to exception callback", 0);
150 
151 	if (test__start_subtest("throwing fexit -> exception_cb"))
152 		RUN_EXT(-EINVAL, true, ({
153 			prog = eskel->progs.throwing_fexit;
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 		}), "FENTRY/FEXIT programs cannot attach to exception callback", 0);
160 
161 	if (test__start_subtest("throwing extension (with custom cb) -> exception_cb"))
162 		RUN_EXT(-EINVAL, true, ({
163 			prog = eskel->progs.throwing_exception_cb_extension;
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 		}), "Extension programs cannot attach to exception callback", 0);
170 
171 	if (test__start_subtest("throwing extension -> global func in exception_cb"))
172 		RUN_EXT(0, false, ({
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_global"), "set_attach_target"))
178 				goto done;
179 		}), "", ({ RUN_SUCCESS(exception_ext_mod_cb_runtime, 131); }));
180 
181 	if (test__start_subtest("throwing extension (with custom cb) -> global func in exception_cb"))
182 		RUN_EXT(0, false, ({
183 			prog = eskel->progs.throwing_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),
187 				       "exception_ext_global"), "set_attach_target"))
188 				goto done;
189 		}), "", ({ RUN_SUCCESS(exception_ext, 128); }));
190 
191 	if (test__start_subtest("non-throwing fentry -> non-throwing subprog"))
192 		/* non-throwing fentry -> non-throwing subprog : OK */
193 		RUN_EXT(0, false, ({
194 			prog = eskel->progs.pfentry;
195 			bpf_program__set_autoload(prog, true);
196 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
197 				       bpf_program__fd(skel->progs.exception_throw_subprog),
198 				       "subprog"), "set_attach_target"))
199 				goto done;
200 		}), "", 0);
201 
202 	if (test__start_subtest("throwing fentry -> non-throwing subprog"))
203 		/* throwing fentry -> non-throwing subprog : OK */
204 		RUN_EXT(0, false, ({
205 			prog = eskel->progs.throwing_fentry;
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("non-throwing fentry -> throwing subprog"))
214 		/* non-throwing fentry -> throwing subprog : OK */
215 		RUN_EXT(0, false, ({
216 			prog = eskel->progs.pfentry;
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 				       "throwing_subprog"), "set_attach_target"))
221 				goto done;
222 		}), "", 0);
223 
224 	if (test__start_subtest("throwing fentry -> throwing subprog"))
225 		/* throwing fentry -> throwing subprog : OK */
226 		RUN_EXT(0, false, ({
227 			prog = eskel->progs.throwing_fentry;
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("non-throwing fexit -> non-throwing subprog"))
236 		/* non-throwing fexit -> non-throwing subprog : OK */
237 		RUN_EXT(0, false, ({
238 			prog = eskel->progs.pfexit;
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 				       "subprog"), "set_attach_target"))
243 				goto done;
244 		}), "", 0);
245 
246 	if (test__start_subtest("throwing fexit -> non-throwing subprog"))
247 		/* throwing fexit -> non-throwing subprog : OK */
248 		RUN_EXT(0, false, ({
249 			prog = eskel->progs.throwing_fexit;
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("non-throwing fexit -> throwing subprog"))
258 		/* non-throwing fexit -> throwing subprog : OK */
259 		RUN_EXT(0, false, ({
260 			prog = eskel->progs.pfexit;
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 				       "throwing_subprog"), "set_attach_target"))
265 				goto done;
266 		}), "", 0);
267 
268 	if (test__start_subtest("throwing fexit -> throwing subprog"))
269 		/* throwing fexit -> throwing subprog : OK */
270 		RUN_EXT(0, false, ({
271 			prog = eskel->progs.throwing_fexit;
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 	/* fmod_ret not allowed for subprog - Check so we remember to handle its
280 	 * throwing specification compatibility with target when supported.
281 	 */
282 	if (test__start_subtest("non-throwing fmod_ret -> non-throwing subprog"))
283 		RUN_EXT(-EINVAL, true, ({
284 			prog = eskel->progs.pfmod_ret;
285 			bpf_program__set_autoload(prog, true);
286 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
287 				       bpf_program__fd(skel->progs.exception_throw_subprog),
288 				       "subprog"), "set_attach_target"))
289 				goto done;
290 		}), "can't modify return codes of BPF program", 0);
291 
292 	/* fmod_ret not allowed for subprog - Check so we remember to handle its
293 	 * throwing specification compatibility with target when supported.
294 	 */
295 	if (test__start_subtest("non-throwing fmod_ret -> non-throwing global subprog"))
296 		RUN_EXT(-EINVAL, true, ({
297 			prog = eskel->progs.pfmod_ret;
298 			bpf_program__set_autoload(prog, true);
299 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
300 				       bpf_program__fd(skel->progs.exception_throw_subprog),
301 				       "global_subprog"), "set_attach_target"))
302 				goto done;
303 		}), "can't modify return codes of BPF program", 0);
304 
305 	if (test__start_subtest("non-throwing extension -> non-throwing subprog"))
306 		/* non-throwing extension -> non-throwing subprog : BAD (!global) */
307 		RUN_EXT(-EINVAL, true, ({
308 			prog = eskel->progs.extension;
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 				       "subprog"), "set_attach_target"))
313 				goto done;
314 		}), "subprog() is not a global function", 0);
315 
316 	if (test__start_subtest("non-throwing extension -> throwing subprog"))
317 		/* non-throwing extension -> 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 				       "throwing_subprog"), "set_attach_target"))
324 				goto done;
325 		}), "throwing_subprog() is not a global function", 0);
326 
327 	if (test__start_subtest("non-throwing extension -> non-throwing subprog"))
328 		/* non-throwing extension -> non-throwing global subprog : OK */
329 		RUN_EXT(0, false, ({
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 				       "global_subprog"), "set_attach_target"))
335 				goto done;
336 		}), "", 0);
337 
338 	if (test__start_subtest("non-throwing extension -> throwing global subprog"))
339 		/* non-throwing extension -> 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 				       "throwing_global_subprog"), "set_attach_target"))
346 				goto done;
347 		}), "", 0);
348 
349 	if (test__start_subtest("throwing extension -> throwing global subprog"))
350 		/* throwing extension -> throwing global subprog : OK */
351 		RUN_EXT(0, false, ({
352 			prog = eskel->progs.throwing_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 -> non-throwing global subprog"))
361 		/* throwing extension -> non-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 				       "global_subprog"), "set_attach_target"))
368 				goto done;
369 		}), "", 0);
370 
371 	if (test__start_subtest("non-throwing extension -> main subprog"))
372 		/* non-throwing extension -> main subprog : OK */
373 		RUN_EXT(0, false, ({
374 			prog = eskel->progs.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 				       "exception_throw_subprog"), "set_attach_target"))
379 				goto done;
380 		}), "", 0);
381 
382 	if (test__start_subtest("throwing extension -> main subprog"))
383 		/* throwing extension -> main subprog : OK */
384 		RUN_EXT(0, false, ({
385 			prog = eskel->progs.throwing_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 done:
394 	exceptions_ext__destroy(eskel);
395 	exceptions__destroy(skel);
396 }
397 
398 static void test_exceptions_assertions(void)
399 {
400 	RUN_TESTS(exceptions_assert);
401 }
402 
403 void test_exceptions(void)
404 {
405 	test_exceptions_success();
406 	test_exceptions_failure();
407 	test_exceptions_assertions();
408 }
409