1221 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <test_progs.h>3#include <io_uring/mini_liburing.h>4#include "cgroup_helpers.h"5 6static char bpf_log_buf[4096];7static bool verbose;8 9#ifndef PAGE_SIZE10#define PAGE_SIZE 409611#endif12 13enum sockopt_test_error {14 OK = 0,15 DENY_LOAD,16 DENY_ATTACH,17 EOPNOTSUPP_GETSOCKOPT,18 EPERM_GETSOCKOPT,19 EFAULT_GETSOCKOPT,20 EPERM_SETSOCKOPT,21 EFAULT_SETSOCKOPT,22};23 24static struct sockopt_test {25 const char *descr;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;30 31 int set_optname;32 int set_level;33 const char set_optval[64];34 socklen_t set_optlen;35 36 int get_optname;37 int get_level;38 const char get_optval[64];39 socklen_t get_optlen;40 socklen_t get_optlen_ret;41 42 enum sockopt_test_error error;43 bool io_uring_support;44} tests[] = {45 46 /* ==================== getsockopt ==================== */47 48 {49 .descr = "getsockopt: no expected_attach_type",50 .insns = {51 /* return 1 */52 BPF_MOV64_IMM(BPF_REG_0, 1),53 BPF_EXIT_INSN(),54 55 },56 .attach_type = BPF_CGROUP_GETSOCKOPT,57 .expected_attach_type = 0,58 .error = DENY_LOAD,59 },60 {61 .descr = "getsockopt: wrong expected_attach_type",62 .insns = {63 /* return 1 */64 BPF_MOV64_IMM(BPF_REG_0, 1),65 BPF_EXIT_INSN(),66 67 },68 .attach_type = BPF_CGROUP_GETSOCKOPT,69 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,70 .error = DENY_ATTACH,71 },72 {73 .descr = "getsockopt: bypass bpf hook",74 .insns = {75 /* return 1 */76 BPF_MOV64_IMM(BPF_REG_0, 1),77 BPF_EXIT_INSN(),78 },79 .attach_type = BPF_CGROUP_GETSOCKOPT,80 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,81 82 .get_level = SOL_IP,83 .set_level = SOL_IP,84 85 .get_optname = IP_TOS,86 .set_optname = IP_TOS,87 88 .set_optval = { 1 << 3 },89 .set_optlen = 1,90 91 .get_optval = { 1 << 3 },92 .get_optlen = 1,93 },94 {95 .descr = "getsockopt: return EPERM from bpf hook",96 .insns = {97 BPF_MOV64_IMM(BPF_REG_0, 0),98 BPF_EXIT_INSN(),99 },100 .attach_type = BPF_CGROUP_GETSOCKOPT,101 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,102 103 .get_level = SOL_IP,104 .get_optname = IP_TOS,105 106 .get_optlen = 1,107 .error = EPERM_GETSOCKOPT,108 },109 {110 .descr = "getsockopt: no optval bounds check, deny loading",111 .insns = {112 /* r6 = ctx->optval */113 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,114 offsetof(struct bpf_sockopt, optval)),115 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),119 120 /* return 1 */121 BPF_MOV64_IMM(BPF_REG_0, 1),122 BPF_EXIT_INSN(),123 },124 .attach_type = BPF_CGROUP_GETSOCKOPT,125 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,126 .error = DENY_LOAD,127 },128 {129 .descr = "getsockopt: read ctx->level",130 .insns = {131 /* r6 = ctx->level */132 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,133 offsetof(struct bpf_sockopt, level)),134 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)),141 /* return 1 */142 BPF_MOV64_IMM(BPF_REG_0, 1),143 BPF_JMP_A(1),144 /* } else { */145 /* return 0 */146 BPF_MOV64_IMM(BPF_REG_0, 0),147 /* } */148 BPF_EXIT_INSN(),149 },150 .attach_type = BPF_CGROUP_GETSOCKOPT,151 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,152 153 .get_level = 123,154 155 .get_optlen = 1,156 },157 {158 .descr = "getsockopt: deny writing to ctx->level",159 .insns = {160 /* ctx->level = 1 */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)),164 BPF_EXIT_INSN(),165 },166 .attach_type = BPF_CGROUP_GETSOCKOPT,167 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,168 169 .error = DENY_LOAD,170 },171 {172 .descr = "getsockopt: read ctx->optname",173 .insns = {174 /* r6 = ctx->optname */175 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,176 offsetof(struct bpf_sockopt, optname)),177 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)),184 /* return 1 */185 BPF_MOV64_IMM(BPF_REG_0, 1),186 BPF_JMP_A(1),187 /* } else { */188 /* return 0 */189 BPF_MOV64_IMM(BPF_REG_0, 0),190 /* } */191 BPF_EXIT_INSN(),192 },193 .attach_type = BPF_CGROUP_GETSOCKOPT,194 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,195 196 .get_optname = 123,197 198 .get_optlen = 1,199 },200 {201 .descr = "getsockopt: read ctx->retval",202 .insns = {203 /* r6 = ctx->retval */204 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,205 offsetof(struct bpf_sockopt, retval)),206 207 /* return 1 */208 BPF_MOV64_IMM(BPF_REG_0, 1),209 BPF_EXIT_INSN(),210 },211 .attach_type = BPF_CGROUP_GETSOCKOPT,212 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,213 214 .get_level = SOL_IP,215 .get_optname = IP_TOS,216 .get_optlen = 1,217 },218 {219 .descr = "getsockopt: deny writing to ctx->optname",220 .insns = {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)),225 BPF_EXIT_INSN(),226 },227 .attach_type = BPF_CGROUP_GETSOCKOPT,228 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,229 230 .error = DENY_LOAD,231 },232 {233 .descr = "getsockopt: read ctx->optlen",234 .insns = {235 /* r6 = ctx->optlen */236 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,237 offsetof(struct bpf_sockopt, optlen)),238 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)),245 /* return 1 */246 BPF_MOV64_IMM(BPF_REG_0, 1),247 BPF_JMP_A(1),248 /* } else { */249 /* return 0 */250 BPF_MOV64_IMM(BPF_REG_0, 0),251 /* } */252 BPF_EXIT_INSN(),253 },254 .attach_type = BPF_CGROUP_GETSOCKOPT,255 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,256 257 .get_level = SOL_SOCKET,258 .get_optlen = 64,259 .io_uring_support = true,260 },261 {262 .descr = "getsockopt: deny bigger ctx->optlen",263 .insns = {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)),268 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)),273 274 /* return 1 */275 BPF_MOV64_IMM(BPF_REG_0, 1),276 BPF_EXIT_INSN(),277 },278 .attach_type = BPF_CGROUP_GETSOCKOPT,279 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,280 281 .get_optlen = 64,282 283 .error = EFAULT_GETSOCKOPT,284 .io_uring_support = true,285 },286 {287 .descr = "getsockopt: ignore >PAGE_SIZE optlen",288 .insns = {289 /* write 0xFF to the first optval byte */290 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),298 299 /* r7 = ctx->optval_end */300 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,301 offsetof(struct bpf_sockopt, optval_end)),302 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),307 /* } */308 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)),314 315 /* return 1 */316 BPF_MOV64_IMM(BPF_REG_0, 1),317 BPF_EXIT_INSN(),318 },319 .attach_type = BPF_CGROUP_GETSOCKOPT,320 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,321 322 .get_level = 1234,323 .get_optname = 5678,324 .get_optval = {}, /* the changes are ignored */325 .get_optlen = PAGE_SIZE + 1,326 .error = EOPNOTSUPP_GETSOCKOPT,327 .io_uring_support = true,328 },329 {330 .descr = "getsockopt: support smaller ctx->optlen",331 .insns = {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)),340 /* return 1 */341 BPF_MOV64_IMM(BPF_REG_0, 1),342 BPF_EXIT_INSN(),343 },344 .attach_type = BPF_CGROUP_GETSOCKOPT,345 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,346 347 .get_level = SOL_SOCKET,348 .get_optlen = 64,349 .get_optlen_ret = 32,350 .io_uring_support = true,351 },352 {353 .descr = "getsockopt: deny writing to ctx->optval",354 .insns = {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)),359 BPF_EXIT_INSN(),360 },361 .attach_type = BPF_CGROUP_GETSOCKOPT,362 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,363 364 .error = DENY_LOAD,365 },366 {367 .descr = "getsockopt: deny writing to ctx->optval_end",368 .insns = {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)),373 BPF_EXIT_INSN(),374 },375 .attach_type = BPF_CGROUP_GETSOCKOPT,376 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,377 378 .error = DENY_LOAD,379 },380 {381 .descr = "getsockopt: rewrite value",382 .insns = {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),390 391 /* r7 = ctx->optval_end */392 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,393 offsetof(struct bpf_sockopt, optval_end)),394 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),399 /* } */400 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)),405 406 /* return 1*/407 BPF_MOV64_IMM(BPF_REG_0, 1),408 BPF_EXIT_INSN(),409 },410 .attach_type = BPF_CGROUP_GETSOCKOPT,411 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,412 413 .get_level = SOL_IP,414 .get_optname = IP_TOS,415 416 .get_optval = { 0xF0 },417 .get_optlen = 1,418 },419 420 /* ==================== setsockopt ==================== */421 422 {423 .descr = "setsockopt: no expected_attach_type",424 .insns = {425 /* return 1 */426 BPF_MOV64_IMM(BPF_REG_0, 1),427 BPF_EXIT_INSN(),428 429 },430 .attach_type = BPF_CGROUP_SETSOCKOPT,431 .expected_attach_type = 0,432 .error = DENY_LOAD,433 },434 {435 .descr = "setsockopt: wrong expected_attach_type",436 .insns = {437 /* return 1 */438 BPF_MOV64_IMM(BPF_REG_0, 1),439 BPF_EXIT_INSN(),440 441 },442 .attach_type = BPF_CGROUP_SETSOCKOPT,443 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,444 .error = DENY_ATTACH,445 },446 {447 .descr = "setsockopt: bypass bpf hook",448 .insns = {449 /* return 1 */450 BPF_MOV64_IMM(BPF_REG_0, 1),451 BPF_EXIT_INSN(),452 },453 .attach_type = BPF_CGROUP_SETSOCKOPT,454 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,455 456 .get_level = SOL_IP,457 .set_level = SOL_IP,458 459 .get_optname = IP_TOS,460 .set_optname = IP_TOS,461 462 .set_optval = { 1 << 3 },463 .set_optlen = 1,464 465 .get_optval = { 1 << 3 },466 .get_optlen = 1,467 },468 {469 .descr = "setsockopt: return EPERM from bpf hook",470 .insns = {471 /* return 0 */472 BPF_MOV64_IMM(BPF_REG_0, 0),473 BPF_EXIT_INSN(),474 },475 .attach_type = BPF_CGROUP_SETSOCKOPT,476 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,477 478 .set_level = SOL_IP,479 .set_optname = IP_TOS,480 481 .set_optlen = 1,482 .error = EPERM_SETSOCKOPT,483 },484 {485 .descr = "setsockopt: no optval bounds check, deny loading",486 .insns = {487 /* r6 = ctx->optval */488 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,489 offsetof(struct bpf_sockopt, optval)),490 491 /* r0 = ctx->optval[0] */492 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),493 494 /* return 1 */495 BPF_MOV64_IMM(BPF_REG_0, 1),496 BPF_EXIT_INSN(),497 },498 .attach_type = BPF_CGROUP_SETSOCKOPT,499 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,500 .error = DENY_LOAD,501 },502 {503 .descr = "setsockopt: read ctx->level",504 .insns = {505 /* r6 = ctx->level */506 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,507 offsetof(struct bpf_sockopt, level)),508 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)),515 /* return 1 */516 BPF_MOV64_IMM(BPF_REG_0, 1),517 BPF_JMP_A(1),518 /* } else { */519 /* return 0 */520 BPF_MOV64_IMM(BPF_REG_0, 0),521 /* } */522 BPF_EXIT_INSN(),523 },524 .attach_type = BPF_CGROUP_SETSOCKOPT,525 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,526 527 .set_level = 123,528 529 .set_optlen = 1,530 .io_uring_support = true,531 },532 {533 .descr = "setsockopt: allow changing ctx->level",534 .insns = {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)),539 /* return 1 */540 BPF_MOV64_IMM(BPF_REG_0, 1),541 BPF_EXIT_INSN(),542 },543 .attach_type = BPF_CGROUP_SETSOCKOPT,544 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,545 546 .get_level = SOL_IP,547 .set_level = 234, /* should be rewritten to SOL_IP */548 549 .get_optname = IP_TOS,550 .set_optname = IP_TOS,551 552 .set_optval = { 1 << 3 },553 .set_optlen = 1,554 .get_optval = { 1 << 3 },555 .get_optlen = 1,556 },557 {558 .descr = "setsockopt: read ctx->optname",559 .insns = {560 /* r6 = ctx->optname */561 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,562 offsetof(struct bpf_sockopt, optname)),563 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)),570 /* return 1 */571 BPF_MOV64_IMM(BPF_REG_0, 1),572 BPF_JMP_A(1),573 /* } else { */574 /* return 0 */575 BPF_MOV64_IMM(BPF_REG_0, 0),576 /* } */577 BPF_EXIT_INSN(),578 },579 .attach_type = BPF_CGROUP_SETSOCKOPT,580 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,581 582 .set_optname = 123,583 584 .set_optlen = 1,585 .io_uring_support = true,586 },587 {588 .descr = "setsockopt: allow changing ctx->optname",589 .insns = {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)),594 /* return 1 */595 BPF_MOV64_IMM(BPF_REG_0, 1),596 BPF_EXIT_INSN(),597 },598 .attach_type = BPF_CGROUP_SETSOCKOPT,599 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,600 601 .get_level = SOL_IP,602 .set_level = SOL_IP,603 604 .get_optname = IP_TOS,605 .set_optname = 456, /* should be rewritten to IP_TOS */606 607 .set_optval = { 1 << 3 },608 .set_optlen = 1,609 .get_optval = { 1 << 3 },610 .get_optlen = 1,611 },612 {613 .descr = "setsockopt: read ctx->optlen",614 .insns = {615 /* r6 = ctx->optlen */616 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,617 offsetof(struct bpf_sockopt, optlen)),618 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)),625 /* return 1 */626 BPF_MOV64_IMM(BPF_REG_0, 1),627 BPF_JMP_A(1),628 /* } else { */629 /* return 0 */630 BPF_MOV64_IMM(BPF_REG_0, 0),631 /* } */632 BPF_EXIT_INSN(),633 },634 .attach_type = BPF_CGROUP_SETSOCKOPT,635 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,636 637 .set_optlen = 64,638 .io_uring_support = true,639 },640 {641 .descr = "setsockopt: ctx->optlen == -1 is ok",642 .insns = {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)),647 /* return 1 */648 BPF_MOV64_IMM(BPF_REG_0, 1),649 BPF_EXIT_INSN(),650 },651 .attach_type = BPF_CGROUP_SETSOCKOPT,652 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,653 654 .set_optlen = 64,655 .io_uring_support = true,656 },657 {658 .descr = "setsockopt: deny ctx->optlen < 0 (except -1)",659 .insns = {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)),664 /* return 1 */665 BPF_MOV64_IMM(BPF_REG_0, 1),666 BPF_EXIT_INSN(),667 },668 .attach_type = BPF_CGROUP_SETSOCKOPT,669 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,670 671 .set_optlen = 4,672 673 .error = EFAULT_SETSOCKOPT,674 .io_uring_support = true,675 },676 {677 .descr = "setsockopt: deny ctx->optlen > input optlen",678 .insns = {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),684 BPF_EXIT_INSN(),685 },686 .attach_type = BPF_CGROUP_SETSOCKOPT,687 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,688 689 .set_optlen = 64,690 691 .error = EFAULT_SETSOCKOPT,692 .io_uring_support = true,693 },694 {695 .descr = "setsockopt: ignore >PAGE_SIZE optlen",696 .insns = {697 /* write 0xFF to the first optval byte */698 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),706 707 /* r7 = ctx->optval_end */708 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,709 offsetof(struct bpf_sockopt, optval_end)),710 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),715 /* } */716 717 BPF_MOV64_IMM(BPF_REG_0, 1),718 BPF_EXIT_INSN(),719 },720 .attach_type = BPF_CGROUP_SETSOCKOPT,721 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,722 723 .set_level = SOL_IP,724 .set_optname = IP_TOS,725 .set_optval = {},726 .set_optlen = PAGE_SIZE + 1,727 728 .get_level = SOL_IP,729 .get_optname = IP_TOS,730 .get_optval = {}, /* the changes are ignored */731 .get_optlen = 4,732 },733 {734 .descr = "setsockopt: allow changing ctx->optlen within bounds",735 .insns = {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),743 744 /* r7 = ctx->optval_end */745 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,746 offsetof(struct bpf_sockopt, optval_end)),747 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),752 /* } */753 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)),758 759 /* return 1*/760 BPF_MOV64_IMM(BPF_REG_0, 1),761 BPF_EXIT_INSN(),762 },763 .attach_type = BPF_CGROUP_SETSOCKOPT,764 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,765 766 .get_level = SOL_IP,767 .set_level = SOL_IP,768 769 .get_optname = IP_TOS,770 .set_optname = IP_TOS,771 772 .set_optval = { 1, 1, 1, 1 },773 .set_optlen = 4,774 .get_optval = { 1 << 3 },775 .get_optlen = 1,776 },777 {778 .descr = "setsockopt: deny write ctx->retval",779 .insns = {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)),784 785 /* return 1 */786 BPF_MOV64_IMM(BPF_REG_0, 1),787 BPF_EXIT_INSN(),788 },789 .attach_type = BPF_CGROUP_SETSOCKOPT,790 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,791 792 .error = DENY_LOAD,793 },794 {795 .descr = "setsockopt: deny read ctx->retval",796 .insns = {797 /* r6 = ctx->retval */798 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,799 offsetof(struct bpf_sockopt, retval)),800 801 /* return 1 */802 BPF_MOV64_IMM(BPF_REG_0, 1),803 BPF_EXIT_INSN(),804 },805 .attach_type = BPF_CGROUP_SETSOCKOPT,806 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,807 808 .error = DENY_LOAD,809 },810 {811 .descr = "setsockopt: deny writing to ctx->optval",812 .insns = {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)),817 BPF_EXIT_INSN(),818 },819 .attach_type = BPF_CGROUP_SETSOCKOPT,820 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,821 822 .error = DENY_LOAD,823 },824 {825 .descr = "setsockopt: deny writing to ctx->optval_end",826 .insns = {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)),831 BPF_EXIT_INSN(),832 },833 .attach_type = BPF_CGROUP_SETSOCKOPT,834 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,835 836 .error = DENY_LOAD,837 },838 {839 .descr = "setsockopt: allow IP_TOS <= 128",840 .insns = {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),847 848 /* r8 = ctx->optval_end */849 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,850 offsetof(struct bpf_sockopt, optval_end)),851 852 /* if (ctx->optval + 1 <= ctx->optval_end) { */853 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),854 855 /* r9 = ctx->optval[0] */856 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),857 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),861 BPF_JMP_A(1),862 /* } */863 864 /* } else { */865 BPF_MOV64_IMM(BPF_REG_0, 0),866 /* } */867 868 BPF_EXIT_INSN(),869 },870 .attach_type = BPF_CGROUP_SETSOCKOPT,871 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,872 873 .get_level = SOL_IP,874 .set_level = SOL_IP,875 876 .get_optname = IP_TOS,877 .set_optname = IP_TOS,878 879 .set_optval = { 0x80 },880 .set_optlen = 1,881 .get_optval = { 0x80 },882 .get_optlen = 1,883 },884 {885 .descr = "setsockopt: deny IP_TOS > 128",886 .insns = {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),893 894 /* r8 = ctx->optval_end */895 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,896 offsetof(struct bpf_sockopt, optval_end)),897 898 /* if (ctx->optval + 1 <= ctx->optval_end) { */899 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),900 901 /* r9 = ctx->optval[0] */902 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),903 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),907 BPF_JMP_A(1),908 /* } */909 910 /* } else { */911 BPF_MOV64_IMM(BPF_REG_0, 0),912 /* } */913 914 BPF_EXIT_INSN(),915 },916 .attach_type = BPF_CGROUP_SETSOCKOPT,917 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,918 919 .get_level = SOL_IP,920 .set_level = SOL_IP,921 922 .get_optname = IP_TOS,923 .set_optname = IP_TOS,924 925 .set_optval = { 0x81 },926 .set_optlen = 1,927 .get_optval = { 0x00 },928 .get_optlen = 1,929 930 .error = EPERM_SETSOCKOPT,931 },932 933 /* ==================== prog_type ==================== */934 935 {936 .descr = "can attach only BPF_CGROUP_SETSOCKOP",937 .insns = {938 /* return 1 */939 BPF_MOV64_IMM(BPF_REG_0, 1),940 BPF_EXIT_INSN(),941 942 },943 .prog_type = BPF_PROG_TYPE_CGROUP_SKB,944 .attach_type = BPF_CGROUP_SETSOCKOPT,945 .expected_attach_type = 0,946 .error = DENY_ATTACH,947 },948 949 {950 .descr = "can attach only BPF_CGROUP_GETSOCKOP",951 .insns = {952 /* return 1 */953 BPF_MOV64_IMM(BPF_REG_0, 1),954 BPF_EXIT_INSN(),955 956 },957 .prog_type = BPF_PROG_TYPE_CGROUP_SKB,958 .attach_type = BPF_CGROUP_GETSOCKOPT,959 .expected_attach_type = 0,960 .error = DENY_ATTACH,961 },962};963 964static int load_prog(const struct bpf_insn *insns,965 enum bpf_prog_type prog_type,966 enum bpf_attach_type expected_attach_type)967{968 LIBBPF_OPTS(bpf_prog_load_opts, opts,969 .expected_attach_type = expected_attach_type,970 .log_level = 2,971 .log_buf = bpf_log_buf,972 .log_size = sizeof(bpf_log_buf),973 );974 int fd, insns_cnt = 0;975 976 for (;977 insns[insns_cnt].code != (BPF_JMP | BPF_EXIT);978 insns_cnt++) {979 }980 insns_cnt++;981 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);985 986 return fd;987}988 989/* Core function that handles io_uring ring initialization,990 * sending SQE with sockopt command and waiting for the CQE.991 */992static int uring_sockopt(int op, int fd, int level, int optname,993 const void *optval, socklen_t optlen)994{995 struct io_uring_cqe *cqe;996 struct io_uring_sqe *sqe;997 struct io_uring ring;998 int err;999 1000 err = io_uring_queue_init(1, &ring, 0);1001 if (!ASSERT_OK(err, "io_uring initialization"))1002 return err;1003 1004 sqe = io_uring_get_sqe(&ring);1005 if (!ASSERT_NEQ(sqe, NULL, "Get an SQE")) {1006 err = -1;1007 goto fail;1008 }1009 1010 io_uring_prep_cmd(sqe, op, fd, level, optname, optval, optlen);1011 1012 err = io_uring_submit(&ring);1013 if (!ASSERT_EQ(err, 1, "Submit SQE"))1014 goto fail;1015 1016 err = io_uring_wait_cqe(&ring, &cqe);1017 if (!ASSERT_OK(err, "Wait for CQE"))1018 goto fail;1019 1020 err = cqe->res;1021 1022fail:1023 io_uring_queue_exit(&ring);1024 1025 return err;1026}1027 1028static int uring_setsockopt(int fd, int level, int optname, const void *optval,1029 socklen_t optlen)1030{1031 return uring_sockopt(SOCKET_URING_OP_SETSOCKOPT, fd, level, optname,1032 optval, optlen);1033}1034 1035static int uring_getsockopt(int fd, int level, int optname, void *optval,1036 socklen_t *optlen)1037{1038 int ret = uring_sockopt(SOCKET_URING_OP_GETSOCKOPT, fd, level, optname,1039 optval, *optlen);1040 if (ret < 0)1041 return ret;1042 1043 /* Populate optlen back to be compatible with systemcall interface,1044 * and simplify the test.1045 */1046 *optlen = ret;1047 1048 return 0;1049}1050 1051/* Execute the setsocktopt operation */1052static int call_setsockopt(bool use_io_uring, int fd, int level, int optname,1053 const void *optval, socklen_t optlen)1054{1055 if (use_io_uring)1056 return uring_setsockopt(fd, level, optname, optval, optlen);1057 1058 return setsockopt(fd, level, optname, optval, optlen);1059}1060 1061/* Execute the getsocktopt operation */1062static int call_getsockopt(bool use_io_uring, int fd, int level, int optname,1063 void *optval, socklen_t *optlen)1064{1065 if (use_io_uring)1066 return uring_getsockopt(fd, level, optname, optval, optlen);1067 1068 return getsockopt(fd, level, optname, optval, optlen);1069}1070 1071static int run_test(int cgroup_fd, struct sockopt_test *test, bool use_io_uring,1072 bool use_link)1073{1074 int prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT;1075 int sock_fd, err, prog_fd, link_fd = -1;1076 void *optval = NULL;1077 int ret = 0;1078 1079 if (test->prog_type)1080 prog_type = test->prog_type;1081 1082 prog_fd = load_prog(test->insns, prog_type, test->expected_attach_type);1083 if (prog_fd < 0) {1084 if (test->error == DENY_LOAD)1085 return 0;1086 1087 log_err("Failed to load BPF program");1088 return -1;1089 }1090 1091 if (use_link) {1092 err = bpf_link_create(prog_fd, cgroup_fd, test->attach_type, NULL);1093 link_fd = err;1094 } else {1095 err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);1096 }1097 if (err < 0) {1098 if (test->error == DENY_ATTACH)1099 goto close_prog_fd;1100 1101 log_err("Failed to attach BPF program");1102 ret = -1;1103 goto close_prog_fd;1104 }1105 1106 sock_fd = socket(AF_INET, SOCK_STREAM, 0);1107 if (sock_fd < 0) {1108 log_err("Failed to create AF_INET socket");1109 ret = -1;1110 goto detach_prog;1111 }1112 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;1117 1118 test->set_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder;1119 }1120 1121 err = call_setsockopt(use_io_uring, sock_fd, test->set_level,1122 test->set_optname, test->set_optval,1123 test->set_optlen);1124 if (err) {1125 if (errno == EPERM && test->error == EPERM_SETSOCKOPT)1126 goto close_sock_fd;1127 if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)1128 goto free_optval;1129 1130 log_err("Failed to call setsockopt");1131 ret = -1;1132 goto close_sock_fd;1133 }1134 }1135 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;1140 1141 test->get_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder;1142 }1143 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 ?:1148 test->get_optlen;1149 1150 err = call_getsockopt(use_io_uring, sock_fd, test->get_level,1151 test->get_optname, optval, &optlen);1152 if (err) {1153 if (errno == EOPNOTSUPP && test->error == EOPNOTSUPP_GETSOCKOPT)1154 goto free_optval;1155 if (errno == EPERM && test->error == EPERM_GETSOCKOPT)1156 goto free_optval;1157 if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)1158 goto free_optval;1159 1160 log_err("Failed to call getsockopt");1161 ret = -1;1162 goto free_optval;1163 }1164 1165 if (optlen != expected_get_optlen) {1166 errno = 0;1167 log_err("getsockopt returned unexpected optlen");1168 ret = -1;1169 goto free_optval;1170 }1171 1172 if (memcmp(optval, test->get_optval, optlen) != 0) {1173 errno = 0;1174 log_err("getsockopt returned unexpected optval");1175 ret = -1;1176 goto free_optval;1177 }1178 }1179 1180 ret = test->error != OK;1181 1182free_optval:1183 free(optval);1184close_sock_fd:1185 close(sock_fd);1186detach_prog:1187 if (use_link) {1188 if (link_fd >= 0)1189 close(link_fd);1190 } else {1191 bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);1192 }1193close_prog_fd:1194 close(prog_fd);1195 return ret;1196}1197 1198void test_sockopt(void)1199{1200 int cgroup_fd, i;1201 1202 cgroup_fd = test__join_cgroup("/sockopt");1203 if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup"))1204 return;1205 1206 for (i = 0; i < ARRAY_SIZE(tests); i++) {1207 if (!test__start_subtest(tests[i].descr))1208 continue;1209 1210 ASSERT_OK(run_test(cgroup_fd, &tests[i], false, false),1211 tests[i].descr);1212 ASSERT_OK(run_test(cgroup_fd, &tests[i], false, true),1213 tests[i].descr);1214 if (tests[i].io_uring_support)1215 ASSERT_OK(run_test(cgroup_fd, &tests[i], true, false),1216 tests[i].descr);1217 }1218 1219 close(cgroup_fd);1220}1221