1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 
4 /* test_tailcall_1 checks basic functionality by patching multiple locations
5  * in a single program for a single tail call slot with nop->jmp, jmp->nop
6  * and jmp->jmp rewrites. Also checks for nop->nop.
7  */
8 static void test_tailcall_1(void)
9 {
10 	int err, map_fd, prog_fd, main_fd, i, j;
11 	struct bpf_map *prog_array;
12 	struct bpf_program *prog;
13 	struct bpf_object *obj;
14 	__u32 retval, duration;
15 	char prog_name[32];
16 	char buff[128] = {};
17 
18 	err = bpf_prog_load("tailcall1.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
19 			    &prog_fd);
20 	if (CHECK_FAIL(err))
21 		return;
22 
23 	prog = bpf_object__find_program_by_title(obj, "classifier");
24 	if (CHECK_FAIL(!prog))
25 		goto out;
26 
27 	main_fd = bpf_program__fd(prog);
28 	if (CHECK_FAIL(main_fd < 0))
29 		goto out;
30 
31 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
32 	if (CHECK_FAIL(!prog_array))
33 		goto out;
34 
35 	map_fd = bpf_map__fd(prog_array);
36 	if (CHECK_FAIL(map_fd < 0))
37 		goto out;
38 
39 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
40 		snprintf(prog_name, sizeof(prog_name), "classifier/%i", i);
41 
42 		prog = bpf_object__find_program_by_title(obj, prog_name);
43 		if (CHECK_FAIL(!prog))
44 			goto out;
45 
46 		prog_fd = bpf_program__fd(prog);
47 		if (CHECK_FAIL(prog_fd < 0))
48 			goto out;
49 
50 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
51 		if (CHECK_FAIL(err))
52 			goto out;
53 	}
54 
55 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
56 		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
57 					&duration, &retval, NULL);
58 		CHECK(err || retval != i, "tailcall",
59 		      "err %d errno %d retval %d\n", err, errno, retval);
60 
61 		err = bpf_map_delete_elem(map_fd, &i);
62 		if (CHECK_FAIL(err))
63 			goto out;
64 	}
65 
66 	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
67 				&duration, &retval, NULL);
68 	CHECK(err || retval != 3, "tailcall", "err %d errno %d retval %d\n",
69 	      err, errno, retval);
70 
71 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
72 		snprintf(prog_name, sizeof(prog_name), "classifier/%i", i);
73 
74 		prog = bpf_object__find_program_by_title(obj, prog_name);
75 		if (CHECK_FAIL(!prog))
76 			goto out;
77 
78 		prog_fd = bpf_program__fd(prog);
79 		if (CHECK_FAIL(prog_fd < 0))
80 			goto out;
81 
82 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
83 		if (CHECK_FAIL(err))
84 			goto out;
85 	}
86 
87 	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
88 				&duration, &retval, NULL);
89 	CHECK(err || retval != 0, "tailcall", "err %d errno %d retval %d\n",
90 	      err, errno, retval);
91 
92 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
93 		j = bpf_map__def(prog_array)->max_entries - 1 - i;
94 		snprintf(prog_name, sizeof(prog_name), "classifier/%i", j);
95 
96 		prog = bpf_object__find_program_by_title(obj, prog_name);
97 		if (CHECK_FAIL(!prog))
98 			goto out;
99 
100 		prog_fd = bpf_program__fd(prog);
101 		if (CHECK_FAIL(prog_fd < 0))
102 			goto out;
103 
104 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
105 		if (CHECK_FAIL(err))
106 			goto out;
107 	}
108 
109 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
110 		j = bpf_map__def(prog_array)->max_entries - 1 - i;
111 
112 		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
113 					&duration, &retval, NULL);
114 		CHECK(err || retval != j, "tailcall",
115 		      "err %d errno %d retval %d\n", err, errno, retval);
116 
117 		err = bpf_map_delete_elem(map_fd, &i);
118 		if (CHECK_FAIL(err))
119 			goto out;
120 	}
121 
122 	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
123 				&duration, &retval, NULL);
124 	CHECK(err || retval != 3, "tailcall", "err %d errno %d retval %d\n",
125 	      err, errno, retval);
126 
127 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
128 		err = bpf_map_delete_elem(map_fd, &i);
129 		if (CHECK_FAIL(err >= 0 || errno != ENOENT))
130 			goto out;
131 
132 		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
133 					&duration, &retval, NULL);
134 		CHECK(err || retval != 3, "tailcall",
135 		      "err %d errno %d retval %d\n", err, errno, retval);
136 	}
137 
138 out:
139 	bpf_object__close(obj);
140 }
141 
142 /* test_tailcall_2 checks that patching multiple programs for a single
143  * tail call slot works. It also jumps through several programs and tests
144  * the tail call limit counter.
145  */
146 static void test_tailcall_2(void)
147 {
148 	int err, map_fd, prog_fd, main_fd, i;
149 	struct bpf_map *prog_array;
150 	struct bpf_program *prog;
151 	struct bpf_object *obj;
152 	__u32 retval, duration;
153 	char prog_name[32];
154 	char buff[128] = {};
155 
156 	err = bpf_prog_load("tailcall2.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
157 			    &prog_fd);
158 	if (CHECK_FAIL(err))
159 		return;
160 
161 	prog = bpf_object__find_program_by_title(obj, "classifier");
162 	if (CHECK_FAIL(!prog))
163 		goto out;
164 
165 	main_fd = bpf_program__fd(prog);
166 	if (CHECK_FAIL(main_fd < 0))
167 		goto out;
168 
169 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
170 	if (CHECK_FAIL(!prog_array))
171 		goto out;
172 
173 	map_fd = bpf_map__fd(prog_array);
174 	if (CHECK_FAIL(map_fd < 0))
175 		goto out;
176 
177 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
178 		snprintf(prog_name, sizeof(prog_name), "classifier/%i", i);
179 
180 		prog = bpf_object__find_program_by_title(obj, prog_name);
181 		if (CHECK_FAIL(!prog))
182 			goto out;
183 
184 		prog_fd = bpf_program__fd(prog);
185 		if (CHECK_FAIL(prog_fd < 0))
186 			goto out;
187 
188 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
189 		if (CHECK_FAIL(err))
190 			goto out;
191 	}
192 
193 	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
194 				&duration, &retval, NULL);
195 	CHECK(err || retval != 2, "tailcall", "err %d errno %d retval %d\n",
196 	      err, errno, retval);
197 
198 	i = 2;
199 	err = bpf_map_delete_elem(map_fd, &i);
200 	if (CHECK_FAIL(err))
201 		goto out;
202 
203 	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
204 				&duration, &retval, NULL);
205 	CHECK(err || retval != 1, "tailcall", "err %d errno %d retval %d\n",
206 	      err, errno, retval);
207 
208 	i = 0;
209 	err = bpf_map_delete_elem(map_fd, &i);
210 	if (CHECK_FAIL(err))
211 		goto out;
212 
213 	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
214 				&duration, &retval, NULL);
215 	CHECK(err || retval != 3, "tailcall", "err %d errno %d retval %d\n",
216 	      err, errno, retval);
217 out:
218 	bpf_object__close(obj);
219 }
220 
221 /* test_tailcall_3 checks that the count value of the tail call limit
222  * enforcement matches with expectations.
223  */
224 static void test_tailcall_3(void)
225 {
226 	int err, map_fd, prog_fd, main_fd, data_fd, i, val;
227 	struct bpf_map *prog_array, *data_map;
228 	struct bpf_program *prog;
229 	struct bpf_object *obj;
230 	__u32 retval, duration;
231 	char buff[128] = {};
232 
233 	err = bpf_prog_load("tailcall3.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
234 			    &prog_fd);
235 	if (CHECK_FAIL(err))
236 		return;
237 
238 	prog = bpf_object__find_program_by_title(obj, "classifier");
239 	if (CHECK_FAIL(!prog))
240 		goto out;
241 
242 	main_fd = bpf_program__fd(prog);
243 	if (CHECK_FAIL(main_fd < 0))
244 		goto out;
245 
246 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
247 	if (CHECK_FAIL(!prog_array))
248 		goto out;
249 
250 	map_fd = bpf_map__fd(prog_array);
251 	if (CHECK_FAIL(map_fd < 0))
252 		goto out;
253 
254 	prog = bpf_object__find_program_by_title(obj, "classifier/0");
255 	if (CHECK_FAIL(!prog))
256 		goto out;
257 
258 	prog_fd = bpf_program__fd(prog);
259 	if (CHECK_FAIL(prog_fd < 0))
260 		goto out;
261 
262 	i = 0;
263 	err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
264 	if (CHECK_FAIL(err))
265 		goto out;
266 
267 	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
268 				&duration, &retval, NULL);
269 	CHECK(err || retval != 1, "tailcall", "err %d errno %d retval %d\n",
270 	      err, errno, retval);
271 
272 	data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
273 	if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
274 		return;
275 
276 	data_fd = bpf_map__fd(data_map);
277 	if (CHECK_FAIL(map_fd < 0))
278 		return;
279 
280 	i = 0;
281 	err = bpf_map_lookup_elem(data_fd, &i, &val);
282 	CHECK(err || val != 33, "tailcall count", "err %d errno %d count %d\n",
283 	      err, errno, val);
284 
285 	i = 0;
286 	err = bpf_map_delete_elem(map_fd, &i);
287 	if (CHECK_FAIL(err))
288 		goto out;
289 
290 	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
291 				&duration, &retval, NULL);
292 	CHECK(err || retval != 0, "tailcall", "err %d errno %d retval %d\n",
293 	      err, errno, retval);
294 out:
295 	bpf_object__close(obj);
296 }
297 
298 /* test_tailcall_4 checks that the kernel properly selects indirect jump
299  * for the case where the key is not known. Latter is passed via global
300  * data to select different targets we can compare return value of.
301  */
302 static void test_tailcall_4(void)
303 {
304 	int err, map_fd, prog_fd, main_fd, data_fd, i;
305 	struct bpf_map *prog_array, *data_map;
306 	struct bpf_program *prog;
307 	struct bpf_object *obj;
308 	__u32 retval, duration;
309 	static const int zero = 0;
310 	char buff[128] = {};
311 	char prog_name[32];
312 
313 	err = bpf_prog_load("tailcall4.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
314 			    &prog_fd);
315 	if (CHECK_FAIL(err))
316 		return;
317 
318 	prog = bpf_object__find_program_by_title(obj, "classifier");
319 	if (CHECK_FAIL(!prog))
320 		goto out;
321 
322 	main_fd = bpf_program__fd(prog);
323 	if (CHECK_FAIL(main_fd < 0))
324 		goto out;
325 
326 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
327 	if (CHECK_FAIL(!prog_array))
328 		goto out;
329 
330 	map_fd = bpf_map__fd(prog_array);
331 	if (CHECK_FAIL(map_fd < 0))
332 		goto out;
333 
334 	data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
335 	if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
336 		return;
337 
338 	data_fd = bpf_map__fd(data_map);
339 	if (CHECK_FAIL(map_fd < 0))
340 		return;
341 
342 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
343 		snprintf(prog_name, sizeof(prog_name), "classifier/%i", i);
344 
345 		prog = bpf_object__find_program_by_title(obj, prog_name);
346 		if (CHECK_FAIL(!prog))
347 			goto out;
348 
349 		prog_fd = bpf_program__fd(prog);
350 		if (CHECK_FAIL(prog_fd < 0))
351 			goto out;
352 
353 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
354 		if (CHECK_FAIL(err))
355 			goto out;
356 	}
357 
358 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
359 		err = bpf_map_update_elem(data_fd, &zero, &i, BPF_ANY);
360 		if (CHECK_FAIL(err))
361 			goto out;
362 
363 		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
364 					&duration, &retval, NULL);
365 		CHECK(err || retval != i, "tailcall",
366 		      "err %d errno %d retval %d\n", err, errno, retval);
367 	}
368 
369 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
370 		err = bpf_map_update_elem(data_fd, &zero, &i, BPF_ANY);
371 		if (CHECK_FAIL(err))
372 			goto out;
373 
374 		err = bpf_map_delete_elem(map_fd, &i);
375 		if (CHECK_FAIL(err))
376 			goto out;
377 
378 		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
379 					&duration, &retval, NULL);
380 		CHECK(err || retval != 3, "tailcall",
381 		      "err %d errno %d retval %d\n", err, errno, retval);
382 	}
383 out:
384 	bpf_object__close(obj);
385 }
386 
387 /* test_tailcall_5 probes similarly to test_tailcall_4 that the kernel generates
388  * an indirect jump when the keys are const but different from different branches.
389  */
390 static void test_tailcall_5(void)
391 {
392 	int err, map_fd, prog_fd, main_fd, data_fd, i, key[] = { 1111, 1234, 5678 };
393 	struct bpf_map *prog_array, *data_map;
394 	struct bpf_program *prog;
395 	struct bpf_object *obj;
396 	__u32 retval, duration;
397 	static const int zero = 0;
398 	char buff[128] = {};
399 	char prog_name[32];
400 
401 	err = bpf_prog_load("tailcall5.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
402 			    &prog_fd);
403 	if (CHECK_FAIL(err))
404 		return;
405 
406 	prog = bpf_object__find_program_by_title(obj, "classifier");
407 	if (CHECK_FAIL(!prog))
408 		goto out;
409 
410 	main_fd = bpf_program__fd(prog);
411 	if (CHECK_FAIL(main_fd < 0))
412 		goto out;
413 
414 	prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
415 	if (CHECK_FAIL(!prog_array))
416 		goto out;
417 
418 	map_fd = bpf_map__fd(prog_array);
419 	if (CHECK_FAIL(map_fd < 0))
420 		goto out;
421 
422 	data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
423 	if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
424 		return;
425 
426 	data_fd = bpf_map__fd(data_map);
427 	if (CHECK_FAIL(map_fd < 0))
428 		return;
429 
430 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
431 		snprintf(prog_name, sizeof(prog_name), "classifier/%i", i);
432 
433 		prog = bpf_object__find_program_by_title(obj, prog_name);
434 		if (CHECK_FAIL(!prog))
435 			goto out;
436 
437 		prog_fd = bpf_program__fd(prog);
438 		if (CHECK_FAIL(prog_fd < 0))
439 			goto out;
440 
441 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
442 		if (CHECK_FAIL(err))
443 			goto out;
444 	}
445 
446 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
447 		err = bpf_map_update_elem(data_fd, &zero, &key[i], BPF_ANY);
448 		if (CHECK_FAIL(err))
449 			goto out;
450 
451 		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
452 					&duration, &retval, NULL);
453 		CHECK(err || retval != i, "tailcall",
454 		      "err %d errno %d retval %d\n", err, errno, retval);
455 	}
456 
457 	for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) {
458 		err = bpf_map_update_elem(data_fd, &zero, &key[i], BPF_ANY);
459 		if (CHECK_FAIL(err))
460 			goto out;
461 
462 		err = bpf_map_delete_elem(map_fd, &i);
463 		if (CHECK_FAIL(err))
464 			goto out;
465 
466 		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
467 					&duration, &retval, NULL);
468 		CHECK(err || retval != 3, "tailcall",
469 		      "err %d errno %d retval %d\n", err, errno, retval);
470 	}
471 out:
472 	bpf_object__close(obj);
473 }
474 
475 void test_tailcalls(void)
476 {
477 	if (test__start_subtest("tailcall_1"))
478 		test_tailcall_1();
479 	if (test__start_subtest("tailcall_2"))
480 		test_tailcall_2();
481 	if (test__start_subtest("tailcall_3"))
482 		test_tailcall_3();
483 	if (test__start_subtest("tailcall_4"))
484 		test_tailcall_4();
485 	if (test__start_subtest("tailcall_5"))
486 		test_tailcall_5();
487 }
488