1 // SPDX-License-Identifier: GPL-2.0 2 #include <test_progs.h> 3 #include <io_uring/mini_liburing.h> 4 #include "cgroup_helpers.h" 5 6 static char bpf_log_buf[4096]; 7 static bool verbose; 8 9 #ifndef PAGE_SIZE 10 #define PAGE_SIZE 4096 11 #endif 12 13 enum 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 24 static 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 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) 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 */ 992 static 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 1022 fail: 1023 io_uring_queue_exit(&ring); 1024 1025 return err; 1026 } 1027 1028 static 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 1035 static 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 */ 1052 static 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 */ 1062 static 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 1071 static 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 1182 free_optval: 1183 free(optval); 1184 close_sock_fd: 1185 close(sock_fd); 1186 detach_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 } 1193 close_prog_fd: 1194 close(prog_fd); 1195 return ret; 1196 } 1197 1198 void 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