1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include <io_uring/mini_liburing.h>
4 #include "cgroup_helpers.h"
6 static char bpf_log_buf[4096];
10 #define PAGE_SIZE 4096
13 enum sockopt_test_error {
17 EOPNOTSUPP_GETSOCKOPT,
24 static struct sockopt_test {
26 const struct bpf_insn insns[64];
27 enum bpf_prog_type prog_type;
28 enum bpf_attach_type attach_type;
29 enum bpf_attach_type expected_attach_type;
33 const char set_optval[64];
38 const char get_optval[64];
40 socklen_t get_optlen_ret;
42 enum sockopt_test_error error;
43 bool io_uring_support;
46 /* ==================== getsockopt ==================== */
49 .descr = "getsockopt: no expected_attach_type",
52 BPF_MOV64_IMM(BPF_REG_0, 1),
56 .attach_type = BPF_CGROUP_GETSOCKOPT,
57 .expected_attach_type = 0,
61 .descr = "getsockopt: wrong expected_attach_type",
64 BPF_MOV64_IMM(BPF_REG_0, 1),
68 .attach_type = BPF_CGROUP_GETSOCKOPT,
69 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
73 .descr = "getsockopt: bypass bpf hook",
76 BPF_MOV64_IMM(BPF_REG_0, 1),
79 .attach_type = BPF_CGROUP_GETSOCKOPT,
80 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
85 .get_optname = IP_TOS,
86 .set_optname = IP_TOS,
88 .set_optval = { 1 << 3 },
91 .get_optval = { 1 << 3 },
95 .descr = "getsockopt: return EPERM from bpf hook",
97 BPF_MOV64_IMM(BPF_REG_0, 0),
100 .attach_type = BPF_CGROUP_GETSOCKOPT,
101 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
104 .get_optname = IP_TOS,
107 .error = EPERM_GETSOCKOPT,
110 .descr = "getsockopt: no optval bounds check, deny loading",
112 /* r6 = ctx->optval */
113 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
114 offsetof(struct bpf_sockopt, optval)),
116 /* ctx->optval[0] = 0x80 */
117 BPF_MOV64_IMM(BPF_REG_0, 0x80),
118 BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
121 BPF_MOV64_IMM(BPF_REG_0, 1),
124 .attach_type = BPF_CGROUP_GETSOCKOPT,
125 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
129 .descr = "getsockopt: read ctx->level",
131 /* r6 = ctx->level */
132 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
133 offsetof(struct bpf_sockopt, level)),
135 /* if (ctx->level == 123) { */
136 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
137 /* ctx->retval = 0 */
138 BPF_MOV64_IMM(BPF_REG_0, 0),
139 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
140 offsetof(struct bpf_sockopt, retval)),
142 BPF_MOV64_IMM(BPF_REG_0, 1),
146 BPF_MOV64_IMM(BPF_REG_0, 0),
150 .attach_type = BPF_CGROUP_GETSOCKOPT,
151 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
158 .descr = "getsockopt: deny writing to ctx->level",
161 BPF_MOV64_IMM(BPF_REG_0, 1),
162 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
163 offsetof(struct bpf_sockopt, level)),
166 .attach_type = BPF_CGROUP_GETSOCKOPT,
167 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
172 .descr = "getsockopt: read ctx->optname",
174 /* r6 = ctx->optname */
175 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
176 offsetof(struct bpf_sockopt, optname)),
178 /* if (ctx->optname == 123) { */
179 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
180 /* ctx->retval = 0 */
181 BPF_MOV64_IMM(BPF_REG_0, 0),
182 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
183 offsetof(struct bpf_sockopt, retval)),
185 BPF_MOV64_IMM(BPF_REG_0, 1),
189 BPF_MOV64_IMM(BPF_REG_0, 0),
193 .attach_type = BPF_CGROUP_GETSOCKOPT,
194 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
201 .descr = "getsockopt: read ctx->retval",
203 /* r6 = ctx->retval */
204 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
205 offsetof(struct bpf_sockopt, retval)),
208 BPF_MOV64_IMM(BPF_REG_0, 1),
211 .attach_type = BPF_CGROUP_GETSOCKOPT,
212 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
215 .get_optname = IP_TOS,
219 .descr = "getsockopt: deny writing to ctx->optname",
221 /* ctx->optname = 1 */
222 BPF_MOV64_IMM(BPF_REG_0, 1),
223 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
224 offsetof(struct bpf_sockopt, optname)),
227 .attach_type = BPF_CGROUP_GETSOCKOPT,
228 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
233 .descr = "getsockopt: read ctx->optlen",
235 /* r6 = ctx->optlen */
236 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
237 offsetof(struct bpf_sockopt, optlen)),
239 /* if (ctx->optlen == 64) { */
240 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
241 /* ctx->retval = 0 */
242 BPF_MOV64_IMM(BPF_REG_0, 0),
243 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
244 offsetof(struct bpf_sockopt, retval)),
246 BPF_MOV64_IMM(BPF_REG_0, 1),
250 BPF_MOV64_IMM(BPF_REG_0, 0),
254 .attach_type = BPF_CGROUP_GETSOCKOPT,
255 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
257 .get_level = SOL_SOCKET,
259 .io_uring_support = true,
262 .descr = "getsockopt: deny bigger ctx->optlen",
264 /* ctx->optlen = 65 */
265 BPF_MOV64_IMM(BPF_REG_0, 65),
266 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
267 offsetof(struct bpf_sockopt, optlen)),
269 /* ctx->retval = 0 */
270 BPF_MOV64_IMM(BPF_REG_0, 0),
271 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
272 offsetof(struct bpf_sockopt, retval)),
275 BPF_MOV64_IMM(BPF_REG_0, 1),
278 .attach_type = BPF_CGROUP_GETSOCKOPT,
279 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
283 .error = EFAULT_GETSOCKOPT,
284 .io_uring_support = true,
287 .descr = "getsockopt: ignore >PAGE_SIZE optlen",
289 /* write 0xFF to the first optval byte */
291 /* r6 = ctx->optval */
292 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
293 offsetof(struct bpf_sockopt, optval)),
294 /* r2 = ctx->optval */
295 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
296 /* r6 = ctx->optval + 1 */
297 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
299 /* r7 = ctx->optval_end */
300 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
301 offsetof(struct bpf_sockopt, optval_end)),
303 /* if (ctx->optval + 1 <= ctx->optval_end) { */
304 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
305 /* ctx->optval[0] = 0xF0 */
306 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xFF),
309 /* retval changes are ignored */
310 /* ctx->retval = 5 */
311 BPF_MOV64_IMM(BPF_REG_0, 5),
312 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
313 offsetof(struct bpf_sockopt, retval)),
316 BPF_MOV64_IMM(BPF_REG_0, 1),
319 .attach_type = BPF_CGROUP_GETSOCKOPT,
320 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
324 .get_optval = {}, /* the changes are ignored */
325 .get_optlen = PAGE_SIZE + 1,
326 .error = EOPNOTSUPP_GETSOCKOPT,
327 .io_uring_support = true,
330 .descr = "getsockopt: support smaller ctx->optlen",
332 /* ctx->optlen = 32 */
333 BPF_MOV64_IMM(BPF_REG_0, 32),
334 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
335 offsetof(struct bpf_sockopt, optlen)),
336 /* ctx->retval = 0 */
337 BPF_MOV64_IMM(BPF_REG_0, 0),
338 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
339 offsetof(struct bpf_sockopt, retval)),
341 BPF_MOV64_IMM(BPF_REG_0, 1),
344 .attach_type = BPF_CGROUP_GETSOCKOPT,
345 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
347 .get_level = SOL_SOCKET,
349 .get_optlen_ret = 32,
350 .io_uring_support = true,
353 .descr = "getsockopt: deny writing to ctx->optval",
355 /* ctx->optval = 1 */
356 BPF_MOV64_IMM(BPF_REG_0, 1),
357 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
358 offsetof(struct bpf_sockopt, optval)),
361 .attach_type = BPF_CGROUP_GETSOCKOPT,
362 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
367 .descr = "getsockopt: deny writing to ctx->optval_end",
369 /* ctx->optval_end = 1 */
370 BPF_MOV64_IMM(BPF_REG_0, 1),
371 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
372 offsetof(struct bpf_sockopt, optval_end)),
375 .attach_type = BPF_CGROUP_GETSOCKOPT,
376 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
381 .descr = "getsockopt: rewrite value",
383 /* r6 = ctx->optval */
384 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
385 offsetof(struct bpf_sockopt, optval)),
386 /* r2 = ctx->optval */
387 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
388 /* r6 = ctx->optval + 1 */
389 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
391 /* r7 = ctx->optval_end */
392 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
393 offsetof(struct bpf_sockopt, optval_end)),
395 /* if (ctx->optval + 1 <= ctx->optval_end) { */
396 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
397 /* ctx->optval[0] = 0xF0 */
398 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
401 /* ctx->retval = 0 */
402 BPF_MOV64_IMM(BPF_REG_0, 0),
403 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
404 offsetof(struct bpf_sockopt, retval)),
407 BPF_MOV64_IMM(BPF_REG_0, 1),
410 .attach_type = BPF_CGROUP_GETSOCKOPT,
411 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
414 .get_optname = IP_TOS,
416 .get_optval = { 0xF0 },
420 /* ==================== setsockopt ==================== */
423 .descr = "setsockopt: no expected_attach_type",
426 BPF_MOV64_IMM(BPF_REG_0, 1),
430 .attach_type = BPF_CGROUP_SETSOCKOPT,
431 .expected_attach_type = 0,
435 .descr = "setsockopt: wrong expected_attach_type",
438 BPF_MOV64_IMM(BPF_REG_0, 1),
442 .attach_type = BPF_CGROUP_SETSOCKOPT,
443 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
444 .error = DENY_ATTACH,
447 .descr = "setsockopt: bypass bpf hook",
450 BPF_MOV64_IMM(BPF_REG_0, 1),
453 .attach_type = BPF_CGROUP_SETSOCKOPT,
454 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
459 .get_optname = IP_TOS,
460 .set_optname = IP_TOS,
462 .set_optval = { 1 << 3 },
465 .get_optval = { 1 << 3 },
469 .descr = "setsockopt: return EPERM from bpf hook",
472 BPF_MOV64_IMM(BPF_REG_0, 0),
475 .attach_type = BPF_CGROUP_SETSOCKOPT,
476 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
479 .set_optname = IP_TOS,
482 .error = EPERM_SETSOCKOPT,
485 .descr = "setsockopt: no optval bounds check, deny loading",
487 /* r6 = ctx->optval */
488 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
489 offsetof(struct bpf_sockopt, optval)),
491 /* r0 = ctx->optval[0] */
492 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
495 BPF_MOV64_IMM(BPF_REG_0, 1),
498 .attach_type = BPF_CGROUP_SETSOCKOPT,
499 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
503 .descr = "setsockopt: read ctx->level",
505 /* r6 = ctx->level */
506 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
507 offsetof(struct bpf_sockopt, level)),
509 /* if (ctx->level == 123) { */
510 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
511 /* ctx->optlen = -1 */
512 BPF_MOV64_IMM(BPF_REG_0, -1),
513 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
514 offsetof(struct bpf_sockopt, optlen)),
516 BPF_MOV64_IMM(BPF_REG_0, 1),
520 BPF_MOV64_IMM(BPF_REG_0, 0),
524 .attach_type = BPF_CGROUP_SETSOCKOPT,
525 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
530 .io_uring_support = true,
533 .descr = "setsockopt: allow changing ctx->level",
535 /* ctx->level = SOL_IP */
536 BPF_MOV64_IMM(BPF_REG_0, SOL_IP),
537 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
538 offsetof(struct bpf_sockopt, level)),
540 BPF_MOV64_IMM(BPF_REG_0, 1),
543 .attach_type = BPF_CGROUP_SETSOCKOPT,
544 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
547 .set_level = 234, /* should be rewritten to SOL_IP */
549 .get_optname = IP_TOS,
550 .set_optname = IP_TOS,
552 .set_optval = { 1 << 3 },
554 .get_optval = { 1 << 3 },
558 .descr = "setsockopt: read ctx->optname",
560 /* r6 = ctx->optname */
561 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
562 offsetof(struct bpf_sockopt, optname)),
564 /* if (ctx->optname == 123) { */
565 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
566 /* ctx->optlen = -1 */
567 BPF_MOV64_IMM(BPF_REG_0, -1),
568 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
569 offsetof(struct bpf_sockopt, optlen)),
571 BPF_MOV64_IMM(BPF_REG_0, 1),
575 BPF_MOV64_IMM(BPF_REG_0, 0),
579 .attach_type = BPF_CGROUP_SETSOCKOPT,
580 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
585 .io_uring_support = true,
588 .descr = "setsockopt: allow changing ctx->optname",
590 /* ctx->optname = IP_TOS */
591 BPF_MOV64_IMM(BPF_REG_0, IP_TOS),
592 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
593 offsetof(struct bpf_sockopt, optname)),
595 BPF_MOV64_IMM(BPF_REG_0, 1),
598 .attach_type = BPF_CGROUP_SETSOCKOPT,
599 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
604 .get_optname = IP_TOS,
605 .set_optname = 456, /* should be rewritten to IP_TOS */
607 .set_optval = { 1 << 3 },
609 .get_optval = { 1 << 3 },
613 .descr = "setsockopt: read ctx->optlen",
615 /* r6 = ctx->optlen */
616 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
617 offsetof(struct bpf_sockopt, optlen)),
619 /* if (ctx->optlen == 64) { */
620 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
621 /* ctx->optlen = -1 */
622 BPF_MOV64_IMM(BPF_REG_0, -1),
623 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
624 offsetof(struct bpf_sockopt, optlen)),
626 BPF_MOV64_IMM(BPF_REG_0, 1),
630 BPF_MOV64_IMM(BPF_REG_0, 0),
634 .attach_type = BPF_CGROUP_SETSOCKOPT,
635 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
638 .io_uring_support = true,
641 .descr = "setsockopt: ctx->optlen == -1 is ok",
643 /* ctx->optlen = -1 */
644 BPF_MOV64_IMM(BPF_REG_0, -1),
645 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
646 offsetof(struct bpf_sockopt, optlen)),
648 BPF_MOV64_IMM(BPF_REG_0, 1),
651 .attach_type = BPF_CGROUP_SETSOCKOPT,
652 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
655 .io_uring_support = true,
658 .descr = "setsockopt: deny ctx->optlen < 0 (except -1)",
660 /* ctx->optlen = -2 */
661 BPF_MOV64_IMM(BPF_REG_0, -2),
662 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
663 offsetof(struct bpf_sockopt, optlen)),
665 BPF_MOV64_IMM(BPF_REG_0, 1),
668 .attach_type = BPF_CGROUP_SETSOCKOPT,
669 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
673 .error = EFAULT_SETSOCKOPT,
674 .io_uring_support = true,
677 .descr = "setsockopt: deny ctx->optlen > input optlen",
679 /* ctx->optlen = 65 */
680 BPF_MOV64_IMM(BPF_REG_0, 65),
681 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
682 offsetof(struct bpf_sockopt, optlen)),
683 BPF_MOV64_IMM(BPF_REG_0, 1),
686 .attach_type = BPF_CGROUP_SETSOCKOPT,
687 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
691 .error = EFAULT_SETSOCKOPT,
692 .io_uring_support = true,
695 .descr = "setsockopt: ignore >PAGE_SIZE optlen",
697 /* write 0xFF to the first optval byte */
699 /* r6 = ctx->optval */
700 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
701 offsetof(struct bpf_sockopt, optval)),
702 /* r2 = ctx->optval */
703 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
704 /* r6 = ctx->optval + 1 */
705 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
707 /* r7 = ctx->optval_end */
708 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
709 offsetof(struct bpf_sockopt, optval_end)),
711 /* if (ctx->optval + 1 <= ctx->optval_end) { */
712 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
713 /* ctx->optval[0] = 0xF0 */
714 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
717 BPF_MOV64_IMM(BPF_REG_0, 1),
720 .attach_type = BPF_CGROUP_SETSOCKOPT,
721 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
724 .set_optname = IP_TOS,
726 .set_optlen = PAGE_SIZE + 1,
729 .get_optname = IP_TOS,
730 .get_optval = {}, /* the changes are ignored */
734 .descr = "setsockopt: allow changing ctx->optlen within bounds",
736 /* r6 = ctx->optval */
737 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
738 offsetof(struct bpf_sockopt, optval)),
739 /* r2 = ctx->optval */
740 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
741 /* r6 = ctx->optval + 1 */
742 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
744 /* r7 = ctx->optval_end */
745 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
746 offsetof(struct bpf_sockopt, optval_end)),
748 /* if (ctx->optval + 1 <= ctx->optval_end) { */
749 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
750 /* ctx->optval[0] = 1 << 3 */
751 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3),
754 /* ctx->optlen = 1 */
755 BPF_MOV64_IMM(BPF_REG_0, 1),
756 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
757 offsetof(struct bpf_sockopt, optlen)),
760 BPF_MOV64_IMM(BPF_REG_0, 1),
763 .attach_type = BPF_CGROUP_SETSOCKOPT,
764 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
769 .get_optname = IP_TOS,
770 .set_optname = IP_TOS,
772 .set_optval = { 1, 1, 1, 1 },
774 .get_optval = { 1 << 3 },
778 .descr = "setsockopt: deny write ctx->retval",
780 /* ctx->retval = 0 */
781 BPF_MOV64_IMM(BPF_REG_0, 0),
782 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
783 offsetof(struct bpf_sockopt, retval)),
786 BPF_MOV64_IMM(BPF_REG_0, 1),
789 .attach_type = BPF_CGROUP_SETSOCKOPT,
790 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
795 .descr = "setsockopt: deny read ctx->retval",
797 /* r6 = ctx->retval */
798 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
799 offsetof(struct bpf_sockopt, retval)),
802 BPF_MOV64_IMM(BPF_REG_0, 1),
805 .attach_type = BPF_CGROUP_SETSOCKOPT,
806 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
811 .descr = "setsockopt: deny writing to ctx->optval",
813 /* ctx->optval = 1 */
814 BPF_MOV64_IMM(BPF_REG_0, 1),
815 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
816 offsetof(struct bpf_sockopt, optval)),
819 .attach_type = BPF_CGROUP_SETSOCKOPT,
820 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
825 .descr = "setsockopt: deny writing to ctx->optval_end",
827 /* ctx->optval_end = 1 */
828 BPF_MOV64_IMM(BPF_REG_0, 1),
829 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
830 offsetof(struct bpf_sockopt, optval_end)),
833 .attach_type = BPF_CGROUP_SETSOCKOPT,
834 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
839 .descr = "setsockopt: allow IP_TOS <= 128",
841 /* r6 = ctx->optval */
842 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
843 offsetof(struct bpf_sockopt, optval)),
844 /* r7 = ctx->optval + 1 */
845 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
846 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
848 /* r8 = ctx->optval_end */
849 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
850 offsetof(struct bpf_sockopt, optval_end)),
852 /* if (ctx->optval + 1 <= ctx->optval_end) { */
853 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
855 /* r9 = ctx->optval[0] */
856 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
858 /* if (ctx->optval[0] < 128) */
859 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
860 BPF_MOV64_IMM(BPF_REG_0, 1),
865 BPF_MOV64_IMM(BPF_REG_0, 0),
870 .attach_type = BPF_CGROUP_SETSOCKOPT,
871 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
876 .get_optname = IP_TOS,
877 .set_optname = IP_TOS,
879 .set_optval = { 0x80 },
881 .get_optval = { 0x80 },
885 .descr = "setsockopt: deny IP_TOS > 128",
887 /* r6 = ctx->optval */
888 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
889 offsetof(struct bpf_sockopt, optval)),
890 /* r7 = ctx->optval + 1 */
891 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
892 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
894 /* r8 = ctx->optval_end */
895 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
896 offsetof(struct bpf_sockopt, optval_end)),
898 /* if (ctx->optval + 1 <= ctx->optval_end) { */
899 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
901 /* r9 = ctx->optval[0] */
902 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
904 /* if (ctx->optval[0] < 128) */
905 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
906 BPF_MOV64_IMM(BPF_REG_0, 1),
911 BPF_MOV64_IMM(BPF_REG_0, 0),
916 .attach_type = BPF_CGROUP_SETSOCKOPT,
917 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
922 .get_optname = IP_TOS,
923 .set_optname = IP_TOS,
925 .set_optval = { 0x81 },
927 .get_optval = { 0x00 },
930 .error = EPERM_SETSOCKOPT,
933 /* ==================== prog_type ==================== */
936 .descr = "can attach only BPF_CGROUP_SETSOCKOP",
939 BPF_MOV64_IMM(BPF_REG_0, 1),
943 .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
944 .attach_type = BPF_CGROUP_SETSOCKOPT,
945 .expected_attach_type = 0,
946 .error = DENY_ATTACH,
950 .descr = "can attach only BPF_CGROUP_GETSOCKOP",
953 BPF_MOV64_IMM(BPF_REG_0, 1),
957 .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
958 .attach_type = BPF_CGROUP_GETSOCKOPT,
959 .expected_attach_type = 0,
960 .error = DENY_ATTACH,
964 static int load_prog(const struct bpf_insn *insns,
965 enum bpf_prog_type prog_type,
966 enum bpf_attach_type expected_attach_type)
968 LIBBPF_OPTS(bpf_prog_load_opts, opts,
969 .expected_attach_type = expected_attach_type,
971 .log_buf = bpf_log_buf,
972 .log_size = sizeof(bpf_log_buf),
974 int fd, insns_cnt = 0;
977 insns[insns_cnt].code != (BPF_JMP | BPF_EXIT);
982 fd = bpf_prog_load(prog_type, NULL, "GPL", insns, insns_cnt, &opts);
983 if (verbose && fd < 0)
984 fprintf(stderr, "%s\n", bpf_log_buf);
989 /* Core function that handles io_uring ring initialization,
990 * sending SQE with sockopt command and waiting for the CQE.
992 static int uring_sockopt(int op, int fd, int level, int optname,
993 const void *optval, socklen_t optlen)
995 struct io_uring_cqe *cqe;
996 struct io_uring_sqe *sqe;
997 struct io_uring ring;
1000 err = io_uring_queue_init(1, &ring, 0);
1001 if (!ASSERT_OK(err, "io_uring initialization"))
1004 sqe = io_uring_get_sqe(&ring);
1005 if (!ASSERT_NEQ(sqe, NULL, "Get an SQE")) {
1010 io_uring_prep_cmd(sqe, op, fd, level, optname, optval, optlen);
1012 err = io_uring_submit(&ring);
1013 if (!ASSERT_EQ(err, 1, "Submit SQE"))
1016 err = io_uring_wait_cqe(&ring, &cqe);
1017 if (!ASSERT_OK(err, "Wait for CQE"))
1023 io_uring_queue_exit(&ring);
1028 static int uring_setsockopt(int fd, int level, int optname, const void *optval,
1031 return uring_sockopt(SOCKET_URING_OP_SETSOCKOPT, fd, level, optname,
1035 static int uring_getsockopt(int fd, int level, int optname, void *optval,
1038 int ret = uring_sockopt(SOCKET_URING_OP_GETSOCKOPT, fd, level, optname,
1043 /* Populate optlen back to be compatible with systemcall interface,
1044 * and simplify the test.
1051 /* Execute the setsocktopt operation */
1052 static int call_setsockopt(bool use_io_uring, int fd, int level, int optname,
1053 const void *optval, socklen_t optlen)
1056 return uring_setsockopt(fd, level, optname, optval, optlen);
1058 return setsockopt(fd, level, optname, optval, optlen);
1061 /* Execute the getsocktopt operation */
1062 static int call_getsockopt(bool use_io_uring, int fd, int level, int optname,
1063 void *optval, socklen_t *optlen)
1066 return uring_getsockopt(fd, level, optname, optval, optlen);
1068 return getsockopt(fd, level, optname, optval, optlen);
1071 static int run_test(int cgroup_fd, struct sockopt_test *test, bool use_io_uring,
1074 int prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT;
1075 int sock_fd, err, prog_fd, link_fd = -1;
1076 void *optval = NULL;
1079 if (test->prog_type)
1080 prog_type = test->prog_type;
1082 prog_fd = load_prog(test->insns, prog_type, test->expected_attach_type);
1084 if (test->error == DENY_LOAD)
1087 log_err("Failed to load BPF program");
1092 err = bpf_link_create(prog_fd, cgroup_fd, test->attach_type, NULL);
1095 err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
1098 if (test->error == DENY_ATTACH)
1101 log_err("Failed to attach BPF program");
1106 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
1108 log_err("Failed to create AF_INET socket");
1113 if (test->set_optlen) {
1114 if (test->set_optlen >= PAGE_SIZE) {
1115 int num_pages = test->set_optlen / PAGE_SIZE;
1116 int remainder = test->set_optlen % PAGE_SIZE;
1118 test->set_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder;
1121 err = call_setsockopt(use_io_uring, sock_fd, test->set_level,
1122 test->set_optname, test->set_optval,
1125 if (errno == EPERM && test->error == EPERM_SETSOCKOPT)
1127 if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)
1130 log_err("Failed to call setsockopt");
1136 if (test->get_optlen) {
1137 if (test->get_optlen >= PAGE_SIZE) {
1138 int num_pages = test->get_optlen / PAGE_SIZE;
1139 int remainder = test->get_optlen % PAGE_SIZE;
1141 test->get_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder;
1144 optval = malloc(test->get_optlen);
1145 memset(optval, 0, test->get_optlen);
1146 socklen_t optlen = test->get_optlen;
1147 socklen_t expected_get_optlen = test->get_optlen_ret ?:
1150 err = call_getsockopt(use_io_uring, sock_fd, test->get_level,
1151 test->get_optname, optval, &optlen);
1153 if (errno == EOPNOTSUPP && test->error == EOPNOTSUPP_GETSOCKOPT)
1155 if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
1157 if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
1160 log_err("Failed to call getsockopt");
1165 if (optlen != expected_get_optlen) {
1167 log_err("getsockopt returned unexpected optlen");
1172 if (memcmp(optval, test->get_optval, optlen) != 0) {
1174 log_err("getsockopt returned unexpected optval");
1180 ret = test->error != OK;
1191 bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
1198 void test_sockopt(void)
1202 cgroup_fd = test__join_cgroup("/sockopt");
1203 if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup"))
1206 for (i = 0; i < ARRAY_SIZE(tests); i++) {
1207 if (!test__start_subtest(tests[i].descr))
1210 ASSERT_OK(run_test(cgroup_fd, &tests[i], false, false),
1212 ASSERT_OK(run_test(cgroup_fd, &tests[i], false, true),
1214 if (tests[i].io_uring_support)
1215 ASSERT_OK(run_test(cgroup_fd, &tests[i], true, false),