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_attach_type attach_type;
28 enum bpf_attach_type expected_attach_type;
32 const char set_optval[64];
37 const char get_optval[64];
39 socklen_t get_optlen_ret;
41 enum sockopt_test_error error;
42 bool io_uring_support;
45 /* ==================== getsockopt ==================== */
48 .descr = "getsockopt: no expected_attach_type",
51 BPF_MOV64_IMM(BPF_REG_0, 1),
55 .attach_type = BPF_CGROUP_GETSOCKOPT,
56 .expected_attach_type = 0,
60 .descr = "getsockopt: wrong expected_attach_type",
63 BPF_MOV64_IMM(BPF_REG_0, 1),
67 .attach_type = BPF_CGROUP_GETSOCKOPT,
68 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
72 .descr = "getsockopt: bypass bpf hook",
75 BPF_MOV64_IMM(BPF_REG_0, 1),
78 .attach_type = BPF_CGROUP_GETSOCKOPT,
79 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
84 .get_optname = IP_TOS,
85 .set_optname = IP_TOS,
87 .set_optval = { 1 << 3 },
90 .get_optval = { 1 << 3 },
94 .descr = "getsockopt: return EPERM from bpf hook",
96 BPF_MOV64_IMM(BPF_REG_0, 0),
99 .attach_type = BPF_CGROUP_GETSOCKOPT,
100 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
103 .get_optname = IP_TOS,
106 .error = EPERM_GETSOCKOPT,
109 .descr = "getsockopt: no optval bounds check, deny loading",
111 /* r6 = ctx->optval */
112 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
113 offsetof(struct bpf_sockopt, optval)),
115 /* ctx->optval[0] = 0x80 */
116 BPF_MOV64_IMM(BPF_REG_0, 0x80),
117 BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
120 BPF_MOV64_IMM(BPF_REG_0, 1),
123 .attach_type = BPF_CGROUP_GETSOCKOPT,
124 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
128 .descr = "getsockopt: read ctx->level",
130 /* r6 = ctx->level */
131 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
132 offsetof(struct bpf_sockopt, level)),
134 /* if (ctx->level == 123) { */
135 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
136 /* ctx->retval = 0 */
137 BPF_MOV64_IMM(BPF_REG_0, 0),
138 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
139 offsetof(struct bpf_sockopt, retval)),
141 BPF_MOV64_IMM(BPF_REG_0, 1),
145 BPF_MOV64_IMM(BPF_REG_0, 0),
149 .attach_type = BPF_CGROUP_GETSOCKOPT,
150 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
157 .descr = "getsockopt: deny writing to ctx->level",
160 BPF_MOV64_IMM(BPF_REG_0, 1),
161 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
162 offsetof(struct bpf_sockopt, level)),
165 .attach_type = BPF_CGROUP_GETSOCKOPT,
166 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
171 .descr = "getsockopt: read ctx->optname",
173 /* r6 = ctx->optname */
174 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
175 offsetof(struct bpf_sockopt, optname)),
177 /* if (ctx->optname == 123) { */
178 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
179 /* ctx->retval = 0 */
180 BPF_MOV64_IMM(BPF_REG_0, 0),
181 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
182 offsetof(struct bpf_sockopt, retval)),
184 BPF_MOV64_IMM(BPF_REG_0, 1),
188 BPF_MOV64_IMM(BPF_REG_0, 0),
192 .attach_type = BPF_CGROUP_GETSOCKOPT,
193 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
200 .descr = "getsockopt: read ctx->retval",
202 /* r6 = ctx->retval */
203 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
204 offsetof(struct bpf_sockopt, retval)),
207 BPF_MOV64_IMM(BPF_REG_0, 1),
210 .attach_type = BPF_CGROUP_GETSOCKOPT,
211 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
214 .get_optname = IP_TOS,
218 .descr = "getsockopt: deny writing to ctx->optname",
220 /* ctx->optname = 1 */
221 BPF_MOV64_IMM(BPF_REG_0, 1),
222 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
223 offsetof(struct bpf_sockopt, optname)),
226 .attach_type = BPF_CGROUP_GETSOCKOPT,
227 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
232 .descr = "getsockopt: read ctx->optlen",
234 /* r6 = ctx->optlen */
235 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
236 offsetof(struct bpf_sockopt, optlen)),
238 /* if (ctx->optlen == 64) { */
239 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
240 /* ctx->retval = 0 */
241 BPF_MOV64_IMM(BPF_REG_0, 0),
242 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
243 offsetof(struct bpf_sockopt, retval)),
245 BPF_MOV64_IMM(BPF_REG_0, 1),
249 BPF_MOV64_IMM(BPF_REG_0, 0),
253 .attach_type = BPF_CGROUP_GETSOCKOPT,
254 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
256 .get_level = SOL_SOCKET,
258 .io_uring_support = true,
261 .descr = "getsockopt: deny bigger ctx->optlen",
263 /* ctx->optlen = 65 */
264 BPF_MOV64_IMM(BPF_REG_0, 65),
265 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
266 offsetof(struct bpf_sockopt, optlen)),
268 /* ctx->retval = 0 */
269 BPF_MOV64_IMM(BPF_REG_0, 0),
270 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
271 offsetof(struct bpf_sockopt, retval)),
274 BPF_MOV64_IMM(BPF_REG_0, 1),
277 .attach_type = BPF_CGROUP_GETSOCKOPT,
278 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
282 .error = EFAULT_GETSOCKOPT,
283 .io_uring_support = true,
286 .descr = "getsockopt: ignore >PAGE_SIZE optlen",
288 /* write 0xFF to the first optval byte */
290 /* r6 = ctx->optval */
291 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
292 offsetof(struct bpf_sockopt, optval)),
293 /* r2 = ctx->optval */
294 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
295 /* r6 = ctx->optval + 1 */
296 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
298 /* r7 = ctx->optval_end */
299 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
300 offsetof(struct bpf_sockopt, optval_end)),
302 /* if (ctx->optval + 1 <= ctx->optval_end) { */
303 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
304 /* ctx->optval[0] = 0xF0 */
305 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xFF),
308 /* retval changes are ignored */
309 /* ctx->retval = 5 */
310 BPF_MOV64_IMM(BPF_REG_0, 5),
311 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
312 offsetof(struct bpf_sockopt, retval)),
315 BPF_MOV64_IMM(BPF_REG_0, 1),
318 .attach_type = BPF_CGROUP_GETSOCKOPT,
319 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
323 .get_optval = {}, /* the changes are ignored */
324 .get_optlen = PAGE_SIZE + 1,
325 .error = EOPNOTSUPP_GETSOCKOPT,
326 .io_uring_support = true,
329 .descr = "getsockopt: support smaller ctx->optlen",
331 /* ctx->optlen = 32 */
332 BPF_MOV64_IMM(BPF_REG_0, 32),
333 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
334 offsetof(struct bpf_sockopt, optlen)),
335 /* ctx->retval = 0 */
336 BPF_MOV64_IMM(BPF_REG_0, 0),
337 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
338 offsetof(struct bpf_sockopt, retval)),
340 BPF_MOV64_IMM(BPF_REG_0, 1),
343 .attach_type = BPF_CGROUP_GETSOCKOPT,
344 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
346 .get_level = SOL_SOCKET,
348 .get_optlen_ret = 32,
349 .io_uring_support = true,
352 .descr = "getsockopt: deny writing to ctx->optval",
354 /* ctx->optval = 1 */
355 BPF_MOV64_IMM(BPF_REG_0, 1),
356 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
357 offsetof(struct bpf_sockopt, optval)),
360 .attach_type = BPF_CGROUP_GETSOCKOPT,
361 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
366 .descr = "getsockopt: deny writing to ctx->optval_end",
368 /* ctx->optval_end = 1 */
369 BPF_MOV64_IMM(BPF_REG_0, 1),
370 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
371 offsetof(struct bpf_sockopt, optval_end)),
374 .attach_type = BPF_CGROUP_GETSOCKOPT,
375 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
380 .descr = "getsockopt: rewrite value",
382 /* r6 = ctx->optval */
383 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
384 offsetof(struct bpf_sockopt, optval)),
385 /* r2 = ctx->optval */
386 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
387 /* r6 = ctx->optval + 1 */
388 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
390 /* r7 = ctx->optval_end */
391 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
392 offsetof(struct bpf_sockopt, optval_end)),
394 /* if (ctx->optval + 1 <= ctx->optval_end) { */
395 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
396 /* ctx->optval[0] = 0xF0 */
397 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
400 /* ctx->retval = 0 */
401 BPF_MOV64_IMM(BPF_REG_0, 0),
402 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
403 offsetof(struct bpf_sockopt, retval)),
406 BPF_MOV64_IMM(BPF_REG_0, 1),
409 .attach_type = BPF_CGROUP_GETSOCKOPT,
410 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
413 .get_optname = IP_TOS,
415 .get_optval = { 0xF0 },
419 /* ==================== setsockopt ==================== */
422 .descr = "setsockopt: no expected_attach_type",
425 BPF_MOV64_IMM(BPF_REG_0, 1),
429 .attach_type = BPF_CGROUP_SETSOCKOPT,
430 .expected_attach_type = 0,
434 .descr = "setsockopt: wrong expected_attach_type",
437 BPF_MOV64_IMM(BPF_REG_0, 1),
441 .attach_type = BPF_CGROUP_SETSOCKOPT,
442 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
443 .error = DENY_ATTACH,
446 .descr = "setsockopt: bypass bpf hook",
449 BPF_MOV64_IMM(BPF_REG_0, 1),
452 .attach_type = BPF_CGROUP_SETSOCKOPT,
453 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
458 .get_optname = IP_TOS,
459 .set_optname = IP_TOS,
461 .set_optval = { 1 << 3 },
464 .get_optval = { 1 << 3 },
468 .descr = "setsockopt: return EPERM from bpf hook",
471 BPF_MOV64_IMM(BPF_REG_0, 0),
474 .attach_type = BPF_CGROUP_SETSOCKOPT,
475 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
478 .set_optname = IP_TOS,
481 .error = EPERM_SETSOCKOPT,
484 .descr = "setsockopt: no optval bounds check, deny loading",
486 /* r6 = ctx->optval */
487 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
488 offsetof(struct bpf_sockopt, optval)),
490 /* r0 = ctx->optval[0] */
491 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
494 BPF_MOV64_IMM(BPF_REG_0, 1),
497 .attach_type = BPF_CGROUP_SETSOCKOPT,
498 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
502 .descr = "setsockopt: read ctx->level",
504 /* r6 = ctx->level */
505 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
506 offsetof(struct bpf_sockopt, level)),
508 /* if (ctx->level == 123) { */
509 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
510 /* ctx->optlen = -1 */
511 BPF_MOV64_IMM(BPF_REG_0, -1),
512 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
513 offsetof(struct bpf_sockopt, optlen)),
515 BPF_MOV64_IMM(BPF_REG_0, 1),
519 BPF_MOV64_IMM(BPF_REG_0, 0),
523 .attach_type = BPF_CGROUP_SETSOCKOPT,
524 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
529 .io_uring_support = true,
532 .descr = "setsockopt: allow changing ctx->level",
534 /* ctx->level = SOL_IP */
535 BPF_MOV64_IMM(BPF_REG_0, SOL_IP),
536 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
537 offsetof(struct bpf_sockopt, level)),
539 BPF_MOV64_IMM(BPF_REG_0, 1),
542 .attach_type = BPF_CGROUP_SETSOCKOPT,
543 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
546 .set_level = 234, /* should be rewritten to SOL_IP */
548 .get_optname = IP_TOS,
549 .set_optname = IP_TOS,
551 .set_optval = { 1 << 3 },
553 .get_optval = { 1 << 3 },
557 .descr = "setsockopt: read ctx->optname",
559 /* r6 = ctx->optname */
560 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
561 offsetof(struct bpf_sockopt, optname)),
563 /* if (ctx->optname == 123) { */
564 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
565 /* ctx->optlen = -1 */
566 BPF_MOV64_IMM(BPF_REG_0, -1),
567 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
568 offsetof(struct bpf_sockopt, optlen)),
570 BPF_MOV64_IMM(BPF_REG_0, 1),
574 BPF_MOV64_IMM(BPF_REG_0, 0),
578 .attach_type = BPF_CGROUP_SETSOCKOPT,
579 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
584 .io_uring_support = true,
587 .descr = "setsockopt: allow changing ctx->optname",
589 /* ctx->optname = IP_TOS */
590 BPF_MOV64_IMM(BPF_REG_0, IP_TOS),
591 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
592 offsetof(struct bpf_sockopt, optname)),
594 BPF_MOV64_IMM(BPF_REG_0, 1),
597 .attach_type = BPF_CGROUP_SETSOCKOPT,
598 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
603 .get_optname = IP_TOS,
604 .set_optname = 456, /* should be rewritten to IP_TOS */
606 .set_optval = { 1 << 3 },
608 .get_optval = { 1 << 3 },
612 .descr = "setsockopt: read ctx->optlen",
614 /* r6 = ctx->optlen */
615 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
616 offsetof(struct bpf_sockopt, optlen)),
618 /* if (ctx->optlen == 64) { */
619 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
620 /* ctx->optlen = -1 */
621 BPF_MOV64_IMM(BPF_REG_0, -1),
622 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
623 offsetof(struct bpf_sockopt, optlen)),
625 BPF_MOV64_IMM(BPF_REG_0, 1),
629 BPF_MOV64_IMM(BPF_REG_0, 0),
633 .attach_type = BPF_CGROUP_SETSOCKOPT,
634 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
637 .io_uring_support = true,
640 .descr = "setsockopt: ctx->optlen == -1 is ok",
642 /* ctx->optlen = -1 */
643 BPF_MOV64_IMM(BPF_REG_0, -1),
644 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
645 offsetof(struct bpf_sockopt, optlen)),
647 BPF_MOV64_IMM(BPF_REG_0, 1),
650 .attach_type = BPF_CGROUP_SETSOCKOPT,
651 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
654 .io_uring_support = true,
657 .descr = "setsockopt: deny ctx->optlen < 0 (except -1)",
659 /* ctx->optlen = -2 */
660 BPF_MOV64_IMM(BPF_REG_0, -2),
661 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
662 offsetof(struct bpf_sockopt, optlen)),
664 BPF_MOV64_IMM(BPF_REG_0, 1),
667 .attach_type = BPF_CGROUP_SETSOCKOPT,
668 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
672 .error = EFAULT_SETSOCKOPT,
673 .io_uring_support = true,
676 .descr = "setsockopt: deny ctx->optlen > input optlen",
678 /* ctx->optlen = 65 */
679 BPF_MOV64_IMM(BPF_REG_0, 65),
680 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
681 offsetof(struct bpf_sockopt, optlen)),
682 BPF_MOV64_IMM(BPF_REG_0, 1),
685 .attach_type = BPF_CGROUP_SETSOCKOPT,
686 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
690 .error = EFAULT_SETSOCKOPT,
691 .io_uring_support = true,
694 .descr = "setsockopt: ignore >PAGE_SIZE optlen",
696 /* write 0xFF to the first optval byte */
698 /* r6 = ctx->optval */
699 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
700 offsetof(struct bpf_sockopt, optval)),
701 /* r2 = ctx->optval */
702 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
703 /* r6 = ctx->optval + 1 */
704 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
706 /* r7 = ctx->optval_end */
707 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
708 offsetof(struct bpf_sockopt, optval_end)),
710 /* if (ctx->optval + 1 <= ctx->optval_end) { */
711 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
712 /* ctx->optval[0] = 0xF0 */
713 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
716 BPF_MOV64_IMM(BPF_REG_0, 1),
719 .attach_type = BPF_CGROUP_SETSOCKOPT,
720 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
723 .set_optname = IP_TOS,
725 .set_optlen = PAGE_SIZE + 1,
728 .get_optname = IP_TOS,
729 .get_optval = {}, /* the changes are ignored */
733 .descr = "setsockopt: allow changing ctx->optlen within bounds",
735 /* r6 = ctx->optval */
736 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
737 offsetof(struct bpf_sockopt, optval)),
738 /* r2 = ctx->optval */
739 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
740 /* r6 = ctx->optval + 1 */
741 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
743 /* r7 = ctx->optval_end */
744 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
745 offsetof(struct bpf_sockopt, optval_end)),
747 /* if (ctx->optval + 1 <= ctx->optval_end) { */
748 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
749 /* ctx->optval[0] = 1 << 3 */
750 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3),
753 /* ctx->optlen = 1 */
754 BPF_MOV64_IMM(BPF_REG_0, 1),
755 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
756 offsetof(struct bpf_sockopt, optlen)),
759 BPF_MOV64_IMM(BPF_REG_0, 1),
762 .attach_type = BPF_CGROUP_SETSOCKOPT,
763 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
768 .get_optname = IP_TOS,
769 .set_optname = IP_TOS,
771 .set_optval = { 1, 1, 1, 1 },
773 .get_optval = { 1 << 3 },
777 .descr = "setsockopt: deny write ctx->retval",
779 /* ctx->retval = 0 */
780 BPF_MOV64_IMM(BPF_REG_0, 0),
781 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
782 offsetof(struct bpf_sockopt, retval)),
785 BPF_MOV64_IMM(BPF_REG_0, 1),
788 .attach_type = BPF_CGROUP_SETSOCKOPT,
789 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
794 .descr = "setsockopt: deny read ctx->retval",
796 /* r6 = ctx->retval */
797 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
798 offsetof(struct bpf_sockopt, retval)),
801 BPF_MOV64_IMM(BPF_REG_0, 1),
804 .attach_type = BPF_CGROUP_SETSOCKOPT,
805 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
810 .descr = "setsockopt: deny writing to ctx->optval",
812 /* ctx->optval = 1 */
813 BPF_MOV64_IMM(BPF_REG_0, 1),
814 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
815 offsetof(struct bpf_sockopt, optval)),
818 .attach_type = BPF_CGROUP_SETSOCKOPT,
819 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
824 .descr = "setsockopt: deny writing to ctx->optval_end",
826 /* ctx->optval_end = 1 */
827 BPF_MOV64_IMM(BPF_REG_0, 1),
828 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
829 offsetof(struct bpf_sockopt, optval_end)),
832 .attach_type = BPF_CGROUP_SETSOCKOPT,
833 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
838 .descr = "setsockopt: allow IP_TOS <= 128",
840 /* r6 = ctx->optval */
841 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
842 offsetof(struct bpf_sockopt, optval)),
843 /* r7 = ctx->optval + 1 */
844 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
845 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
847 /* r8 = ctx->optval_end */
848 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
849 offsetof(struct bpf_sockopt, optval_end)),
851 /* if (ctx->optval + 1 <= ctx->optval_end) { */
852 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
854 /* r9 = ctx->optval[0] */
855 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
857 /* if (ctx->optval[0] < 128) */
858 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
859 BPF_MOV64_IMM(BPF_REG_0, 1),
864 BPF_MOV64_IMM(BPF_REG_0, 0),
869 .attach_type = BPF_CGROUP_SETSOCKOPT,
870 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
875 .get_optname = IP_TOS,
876 .set_optname = IP_TOS,
878 .set_optval = { 0x80 },
880 .get_optval = { 0x80 },
884 .descr = "setsockopt: deny IP_TOS > 128",
886 /* r6 = ctx->optval */
887 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
888 offsetof(struct bpf_sockopt, optval)),
889 /* r7 = ctx->optval + 1 */
890 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
891 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
893 /* r8 = ctx->optval_end */
894 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
895 offsetof(struct bpf_sockopt, optval_end)),
897 /* if (ctx->optval + 1 <= ctx->optval_end) { */
898 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
900 /* r9 = ctx->optval[0] */
901 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
903 /* if (ctx->optval[0] < 128) */
904 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
905 BPF_MOV64_IMM(BPF_REG_0, 1),
910 BPF_MOV64_IMM(BPF_REG_0, 0),
915 .attach_type = BPF_CGROUP_SETSOCKOPT,
916 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
921 .get_optname = IP_TOS,
922 .set_optname = IP_TOS,
924 .set_optval = { 0x81 },
926 .get_optval = { 0x00 },
929 .error = EPERM_SETSOCKOPT,
933 static int load_prog(const struct bpf_insn *insns,
934 enum bpf_attach_type expected_attach_type)
936 LIBBPF_OPTS(bpf_prog_load_opts, opts,
937 .expected_attach_type = expected_attach_type,
939 .log_buf = bpf_log_buf,
940 .log_size = sizeof(bpf_log_buf),
942 int fd, insns_cnt = 0;
945 insns[insns_cnt].code != (BPF_JMP | BPF_EXIT);
950 fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCKOPT, NULL, "GPL", insns, insns_cnt, &opts);
951 if (verbose && fd < 0)
952 fprintf(stderr, "%s\n", bpf_log_buf);
957 /* Core function that handles io_uring ring initialization,
958 * sending SQE with sockopt command and waiting for the CQE.
960 static int uring_sockopt(int op, int fd, int level, int optname,
961 const void *optval, socklen_t optlen)
963 struct io_uring_cqe *cqe;
964 struct io_uring_sqe *sqe;
965 struct io_uring ring;
968 err = io_uring_queue_init(1, &ring, 0);
969 if (!ASSERT_OK(err, "io_uring initialization"))
972 sqe = io_uring_get_sqe(&ring);
973 if (!ASSERT_NEQ(sqe, NULL, "Get an SQE")) {
978 io_uring_prep_cmd(sqe, op, fd, level, optname, optval, optlen);
980 err = io_uring_submit(&ring);
981 if (!ASSERT_EQ(err, 1, "Submit SQE"))
984 err = io_uring_wait_cqe(&ring, &cqe);
985 if (!ASSERT_OK(err, "Wait for CQE"))
991 io_uring_queue_exit(&ring);
996 static int uring_setsockopt(int fd, int level, int optname, const void *optval,
999 return uring_sockopt(SOCKET_URING_OP_SETSOCKOPT, fd, level, optname,
1003 static int uring_getsockopt(int fd, int level, int optname, void *optval,
1006 int ret = uring_sockopt(SOCKET_URING_OP_GETSOCKOPT, fd, level, optname,
1011 /* Populate optlen back to be compatible with systemcall interface,
1012 * and simplify the test.
1019 /* Execute the setsocktopt operation */
1020 static int call_setsockopt(bool use_io_uring, int fd, int level, int optname,
1021 const void *optval, socklen_t optlen)
1024 return uring_setsockopt(fd, level, optname, optval, optlen);
1026 return setsockopt(fd, level, optname, optval, optlen);
1029 /* Execute the getsocktopt operation */
1030 static int call_getsockopt(bool use_io_uring, int fd, int level, int optname,
1031 void *optval, socklen_t *optlen)
1034 return uring_getsockopt(fd, level, optname, optval, optlen);
1036 return getsockopt(fd, level, optname, optval, optlen);
1039 static int run_test(int cgroup_fd, struct sockopt_test *test, bool use_io_uring)
1041 int sock_fd, err, prog_fd;
1042 void *optval = NULL;
1045 prog_fd = load_prog(test->insns, test->expected_attach_type);
1047 if (test->error == DENY_LOAD)
1050 log_err("Failed to load BPF program");
1054 err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
1056 if (test->error == DENY_ATTACH)
1059 log_err("Failed to attach BPF program");
1064 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
1066 log_err("Failed to create AF_INET socket");
1071 if (test->set_optlen) {
1072 if (test->set_optlen >= PAGE_SIZE) {
1073 int num_pages = test->set_optlen / PAGE_SIZE;
1074 int remainder = test->set_optlen % PAGE_SIZE;
1076 test->set_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder;
1079 err = call_setsockopt(use_io_uring, sock_fd, test->set_level,
1080 test->set_optname, test->set_optval,
1083 if (errno == EPERM && test->error == EPERM_SETSOCKOPT)
1085 if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)
1088 log_err("Failed to call setsockopt");
1094 if (test->get_optlen) {
1095 if (test->get_optlen >= PAGE_SIZE) {
1096 int num_pages = test->get_optlen / PAGE_SIZE;
1097 int remainder = test->get_optlen % PAGE_SIZE;
1099 test->get_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder;
1102 optval = malloc(test->get_optlen);
1103 memset(optval, 0, test->get_optlen);
1104 socklen_t optlen = test->get_optlen;
1105 socklen_t expected_get_optlen = test->get_optlen_ret ?:
1108 err = call_getsockopt(use_io_uring, sock_fd, test->get_level,
1109 test->get_optname, optval, &optlen);
1111 if (errno == EOPNOTSUPP && test->error == EOPNOTSUPP_GETSOCKOPT)
1113 if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
1115 if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
1118 log_err("Failed to call getsockopt");
1123 if (optlen != expected_get_optlen) {
1125 log_err("getsockopt returned unexpected optlen");
1130 if (memcmp(optval, test->get_optval, optlen) != 0) {
1132 log_err("getsockopt returned unexpected optval");
1138 ret = test->error != OK;
1145 bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
1151 void test_sockopt(void)
1155 cgroup_fd = test__join_cgroup("/sockopt");
1156 if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup"))
1159 for (i = 0; i < ARRAY_SIZE(tests); i++) {
1160 if (!test__start_subtest(tests[i].descr))
1163 ASSERT_OK(run_test(cgroup_fd, &tests[i], false),
1165 if (tests[i].io_uring_support)
1166 ASSERT_OK(run_test(cgroup_fd, &tests[i], true),