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
10static char log_buf[1024 * 1024];
11
12static void test_exceptions_failure(void)
13{
14	RUN_TESTS(exceptions_fail);
15}
16
17static 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
394done:
395	exceptions_ext__destroy(eskel);
396	exceptions__destroy(skel);
397}
398
399static void test_exceptions_assertions(void)
400{
401	RUN_TESTS(exceptions_assert);
402}
403
404void test_exceptions(void)
405{
406	test_exceptions_success();
407	test_exceptions_failure();
408	test_exceptions_assertions();
409}
410