xref: /linux/tools/testing/selftests/bpf/prog_tests/exceptions.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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 			if (!ASSERT_OK_PTR(strstr(log_buf, msg), "strstr")) {	  \
107 				printf("%s\n", log_buf);			  \
108 				goto done;					  \
109 			}							  \
110 		}								  \
111 		if (!load_ret && attach_err) {					  \
112 			if (!ASSERT_ERR_PTR(link = bpf_program__attach(prog), "attach err")) \
113 				goto done;					  \
114 		} else if (!load_ret) {						  \
115 			if (!ASSERT_OK_PTR(link = bpf_program__attach(prog), "attach ok"))  \
116 				goto done;					  \
117 			(void)(after_link);					  \
118 			bpf_link__destroy(link);				  \
119 		}								  \
120 	}
121 
122 	if (test__start_subtest("non-throwing fentry -> exception_cb"))
123 		RUN_EXT(-EINVAL, true, ({
124 			prog = eskel->progs.pfentry;
125 			bpf_program__set_autoload(prog, true);
126 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
127 				       bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime),
128 				       "exception_cb_mod"), "set_attach_target"))
129 				goto done;
130 		}), "FENTRY/FEXIT programs cannot attach to exception callback", 0);
131 
132 	if (test__start_subtest("throwing fentry -> exception_cb"))
133 		RUN_EXT(-EINVAL, true, ({
134 			prog = eskel->progs.throwing_fentry;
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 		}), "FENTRY/FEXIT programs cannot attach to exception callback", 0);
141 
142 	if (test__start_subtest("non-throwing fexit -> exception_cb"))
143 		RUN_EXT(-EINVAL, true, ({
144 			prog = eskel->progs.pfexit;
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 		}), "FENTRY/FEXIT programs cannot attach to exception callback", 0);
151 
152 	if (test__start_subtest("throwing fexit -> exception_cb"))
153 		RUN_EXT(-EINVAL, true, ({
154 			prog = eskel->progs.throwing_fexit;
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 		}), "FENTRY/FEXIT programs cannot attach to exception callback", 0);
161 
162 	if (test__start_subtest("throwing extension (with custom cb) -> exception_cb"))
163 		RUN_EXT(-EINVAL, true, ({
164 			prog = eskel->progs.throwing_exception_cb_extension;
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 		}), "Extension programs cannot attach to exception callback", 0);
171 
172 	if (test__start_subtest("throwing extension -> global func in exception_cb"))
173 		RUN_EXT(0, false, ({
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_global"), "set_attach_target"))
179 				goto done;
180 		}), "", ({ RUN_SUCCESS(exception_ext_mod_cb_runtime, 131); }));
181 
182 	if (test__start_subtest("throwing extension (with custom cb) -> global func in exception_cb"))
183 		RUN_EXT(0, false, ({
184 			prog = eskel->progs.throwing_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),
188 				       "exception_ext_global"), "set_attach_target"))
189 				goto done;
190 		}), "", ({ RUN_SUCCESS(exception_ext, 128); }));
191 
192 	if (test__start_subtest("non-throwing fentry -> non-throwing subprog"))
193 		/* non-throwing fentry -> non-throwing subprog : OK */
194 		RUN_EXT(0, false, ({
195 			prog = eskel->progs.pfentry;
196 			bpf_program__set_autoload(prog, true);
197 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
198 				       bpf_program__fd(skel->progs.exception_throw_subprog),
199 				       "subprog"), "set_attach_target"))
200 				goto done;
201 		}), "", 0);
202 
203 	if (test__start_subtest("throwing fentry -> non-throwing subprog"))
204 		/* throwing fentry -> non-throwing subprog : OK */
205 		RUN_EXT(0, false, ({
206 			prog = eskel->progs.throwing_fentry;
207 			bpf_program__set_autoload(prog, true);
208 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
209 				       bpf_program__fd(skel->progs.exception_throw_subprog),
210 				       "subprog"), "set_attach_target"))
211 				goto done;
212 		}), "", 0);
213 
214 	if (test__start_subtest("non-throwing fentry -> throwing subprog"))
215 		/* non-throwing fentry -> throwing subprog : OK */
216 		RUN_EXT(0, false, ({
217 			prog = eskel->progs.pfentry;
218 			bpf_program__set_autoload(prog, true);
219 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
220 				       bpf_program__fd(skel->progs.exception_throw_subprog),
221 				       "throwing_subprog"), "set_attach_target"))
222 				goto done;
223 		}), "", 0);
224 
225 	if (test__start_subtest("throwing fentry -> throwing subprog"))
226 		/* throwing fentry -> throwing subprog : OK */
227 		RUN_EXT(0, false, ({
228 			prog = eskel->progs.throwing_fentry;
229 			bpf_program__set_autoload(prog, true);
230 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
231 				       bpf_program__fd(skel->progs.exception_throw_subprog),
232 				       "throwing_subprog"), "set_attach_target"))
233 				goto done;
234 		}), "", 0);
235 
236 	if (test__start_subtest("non-throwing fexit -> non-throwing subprog"))
237 		/* non-throwing fexit -> non-throwing subprog : OK */
238 		RUN_EXT(0, false, ({
239 			prog = eskel->progs.pfexit;
240 			bpf_program__set_autoload(prog, true);
241 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
242 				       bpf_program__fd(skel->progs.exception_throw_subprog),
243 				       "subprog"), "set_attach_target"))
244 				goto done;
245 		}), "", 0);
246 
247 	if (test__start_subtest("throwing fexit -> non-throwing subprog"))
248 		/* throwing fexit -> non-throwing subprog : OK */
249 		RUN_EXT(0, false, ({
250 			prog = eskel->progs.throwing_fexit;
251 			bpf_program__set_autoload(prog, true);
252 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
253 				       bpf_program__fd(skel->progs.exception_throw_subprog),
254 				       "subprog"), "set_attach_target"))
255 				goto done;
256 		}), "", 0);
257 
258 	if (test__start_subtest("non-throwing fexit -> throwing subprog"))
259 		/* non-throwing fexit -> throwing subprog : OK */
260 		RUN_EXT(0, false, ({
261 			prog = eskel->progs.pfexit;
262 			bpf_program__set_autoload(prog, true);
263 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
264 				       bpf_program__fd(skel->progs.exception_throw_subprog),
265 				       "throwing_subprog"), "set_attach_target"))
266 				goto done;
267 		}), "", 0);
268 
269 	if (test__start_subtest("throwing fexit -> throwing subprog"))
270 		/* throwing fexit -> throwing subprog : OK */
271 		RUN_EXT(0, false, ({
272 			prog = eskel->progs.throwing_fexit;
273 			bpf_program__set_autoload(prog, true);
274 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
275 				       bpf_program__fd(skel->progs.exception_throw_subprog),
276 				       "throwing_subprog"), "set_attach_target"))
277 				goto done;
278 		}), "", 0);
279 
280 	/* fmod_ret not allowed for subprog - Check so we remember to handle its
281 	 * throwing specification compatibility with target when supported.
282 	 */
283 	if (test__start_subtest("non-throwing fmod_ret -> non-throwing subprog"))
284 		RUN_EXT(-EINVAL, true, ({
285 			prog = eskel->progs.pfmod_ret;
286 			bpf_program__set_autoload(prog, true);
287 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
288 				       bpf_program__fd(skel->progs.exception_throw_subprog),
289 				       "subprog"), "set_attach_target"))
290 				goto done;
291 		}), "can't modify return codes of BPF program", 0);
292 
293 	/* fmod_ret not allowed for subprog - Check so we remember to handle its
294 	 * throwing specification compatibility with target when supported.
295 	 */
296 	if (test__start_subtest("non-throwing fmod_ret -> non-throwing global subprog"))
297 		RUN_EXT(-EINVAL, true, ({
298 			prog = eskel->progs.pfmod_ret;
299 			bpf_program__set_autoload(prog, true);
300 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
301 				       bpf_program__fd(skel->progs.exception_throw_subprog),
302 				       "global_subprog"), "set_attach_target"))
303 				goto done;
304 		}), "can't modify return codes of BPF program", 0);
305 
306 	if (test__start_subtest("non-throwing extension -> non-throwing subprog"))
307 		/* non-throwing extension -> non-throwing subprog : BAD (!global) */
308 		RUN_EXT(-EINVAL, true, ({
309 			prog = eskel->progs.extension;
310 			bpf_program__set_autoload(prog, true);
311 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
312 				       bpf_program__fd(skel->progs.exception_throw_subprog),
313 				       "subprog"), "set_attach_target"))
314 				goto done;
315 		}), "subprog() is not a global function", 0);
316 
317 	if (test__start_subtest("non-throwing extension -> throwing subprog"))
318 		/* non-throwing extension -> throwing subprog : BAD (!global) */
319 		RUN_EXT(-EINVAL, true, ({
320 			prog = eskel->progs.extension;
321 			bpf_program__set_autoload(prog, true);
322 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
323 				       bpf_program__fd(skel->progs.exception_throw_subprog),
324 				       "throwing_subprog"), "set_attach_target"))
325 				goto done;
326 		}), "throwing_subprog() is not a global function", 0);
327 
328 	if (test__start_subtest("non-throwing extension -> non-throwing subprog"))
329 		/* non-throwing extension -> non-throwing global subprog : OK */
330 		RUN_EXT(0, false, ({
331 			prog = eskel->progs.extension;
332 			bpf_program__set_autoload(prog, true);
333 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
334 				       bpf_program__fd(skel->progs.exception_throw_subprog),
335 				       "global_subprog"), "set_attach_target"))
336 				goto done;
337 		}), "", 0);
338 
339 	if (test__start_subtest("non-throwing extension -> throwing global subprog"))
340 		/* non-throwing extension -> throwing global subprog : OK */
341 		RUN_EXT(0, false, ({
342 			prog = eskel->progs.extension;
343 			bpf_program__set_autoload(prog, true);
344 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
345 				       bpf_program__fd(skel->progs.exception_throw_subprog),
346 				       "throwing_global_subprog"), "set_attach_target"))
347 				goto done;
348 		}), "", 0);
349 
350 	if (test__start_subtest("throwing extension -> throwing global subprog"))
351 		/* throwing extension -> throwing global subprog : OK */
352 		RUN_EXT(0, false, ({
353 			prog = eskel->progs.throwing_extension;
354 			bpf_program__set_autoload(prog, true);
355 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
356 				       bpf_program__fd(skel->progs.exception_throw_subprog),
357 				       "throwing_global_subprog"), "set_attach_target"))
358 				goto done;
359 		}), "", 0);
360 
361 	if (test__start_subtest("throwing extension -> non-throwing global subprog"))
362 		/* throwing extension -> non-throwing global subprog : OK */
363 		RUN_EXT(0, false, ({
364 			prog = eskel->progs.throwing_extension;
365 			bpf_program__set_autoload(prog, true);
366 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
367 				       bpf_program__fd(skel->progs.exception_throw_subprog),
368 				       "global_subprog"), "set_attach_target"))
369 				goto done;
370 		}), "", 0);
371 
372 	if (test__start_subtest("non-throwing extension -> main subprog"))
373 		/* non-throwing extension -> main subprog : OK */
374 		RUN_EXT(0, false, ({
375 			prog = eskel->progs.extension;
376 			bpf_program__set_autoload(prog, true);
377 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
378 				       bpf_program__fd(skel->progs.exception_throw_subprog),
379 				       "exception_throw_subprog"), "set_attach_target"))
380 				goto done;
381 		}), "", 0);
382 
383 	if (test__start_subtest("throwing extension -> main subprog"))
384 		/* throwing extension -> main subprog : OK */
385 		RUN_EXT(0, false, ({
386 			prog = eskel->progs.throwing_extension;
387 			bpf_program__set_autoload(prog, true);
388 			if (!ASSERT_OK(bpf_program__set_attach_target(prog,
389 				       bpf_program__fd(skel->progs.exception_throw_subprog),
390 				       "exception_throw_subprog"), "set_attach_target"))
391 				goto done;
392 		}), "", 0);
393 
394 done:
395 	exceptions_ext__destroy(eskel);
396 	exceptions__destroy(skel);
397 }
398 
399 static void test_exceptions_assertions(void)
400 {
401 	RUN_TESTS(exceptions_assert);
402 }
403 
404 void test_exceptions(void)
405 {
406 	test_exceptions_success();
407 	test_exceptions_failure();
408 	test_exceptions_assertions();
409 }
410