1 // SPDX-License-Identifier: GPL-2.0 2 #include <test_progs.h> 3 #include "cgroup_helpers.h" 4 5 static char bpf_log_buf[4096]; 6 static bool verbose; 7 8 #ifndef PAGE_SIZE 9 #define PAGE_SIZE 4096 10 #endif 11 12 enum sockopt_test_error { 13 OK = 0, 14 DENY_LOAD, 15 DENY_ATTACH, 16 EOPNOTSUPP_GETSOCKOPT, 17 EPERM_GETSOCKOPT, 18 EFAULT_GETSOCKOPT, 19 EPERM_SETSOCKOPT, 20 EFAULT_SETSOCKOPT, 21 }; 22 23 static struct sockopt_test { 24 const char *descr; 25 const struct bpf_insn insns[64]; 26 enum bpf_attach_type attach_type; 27 enum bpf_attach_type expected_attach_type; 28 29 int set_optname; 30 int set_level; 31 const char set_optval[64]; 32 socklen_t set_optlen; 33 34 int get_optname; 35 int get_level; 36 const char get_optval[64]; 37 socklen_t get_optlen; 38 socklen_t get_optlen_ret; 39 40 enum sockopt_test_error error; 41 } tests[] = { 42 43 /* ==================== getsockopt ==================== */ 44 45 { 46 .descr = "getsockopt: no expected_attach_type", 47 .insns = { 48 /* return 1 */ 49 BPF_MOV64_IMM(BPF_REG_0, 1), 50 BPF_EXIT_INSN(), 51 52 }, 53 .attach_type = BPF_CGROUP_GETSOCKOPT, 54 .expected_attach_type = 0, 55 .error = DENY_LOAD, 56 }, 57 { 58 .descr = "getsockopt: wrong expected_attach_type", 59 .insns = { 60 /* return 1 */ 61 BPF_MOV64_IMM(BPF_REG_0, 1), 62 BPF_EXIT_INSN(), 63 64 }, 65 .attach_type = BPF_CGROUP_GETSOCKOPT, 66 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 67 .error = DENY_ATTACH, 68 }, 69 { 70 .descr = "getsockopt: bypass bpf hook", 71 .insns = { 72 /* return 1 */ 73 BPF_MOV64_IMM(BPF_REG_0, 1), 74 BPF_EXIT_INSN(), 75 }, 76 .attach_type = BPF_CGROUP_GETSOCKOPT, 77 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 78 79 .get_level = SOL_IP, 80 .set_level = SOL_IP, 81 82 .get_optname = IP_TOS, 83 .set_optname = IP_TOS, 84 85 .set_optval = { 1 << 3 }, 86 .set_optlen = 1, 87 88 .get_optval = { 1 << 3 }, 89 .get_optlen = 1, 90 }, 91 { 92 .descr = "getsockopt: return EPERM from bpf hook", 93 .insns = { 94 BPF_MOV64_IMM(BPF_REG_0, 0), 95 BPF_EXIT_INSN(), 96 }, 97 .attach_type = BPF_CGROUP_GETSOCKOPT, 98 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 99 100 .get_level = SOL_IP, 101 .get_optname = IP_TOS, 102 103 .get_optlen = 1, 104 .error = EPERM_GETSOCKOPT, 105 }, 106 { 107 .descr = "getsockopt: no optval bounds check, deny loading", 108 .insns = { 109 /* r6 = ctx->optval */ 110 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 111 offsetof(struct bpf_sockopt, optval)), 112 113 /* ctx->optval[0] = 0x80 */ 114 BPF_MOV64_IMM(BPF_REG_0, 0x80), 115 BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0), 116 117 /* return 1 */ 118 BPF_MOV64_IMM(BPF_REG_0, 1), 119 BPF_EXIT_INSN(), 120 }, 121 .attach_type = BPF_CGROUP_GETSOCKOPT, 122 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 123 .error = DENY_LOAD, 124 }, 125 { 126 .descr = "getsockopt: read ctx->level", 127 .insns = { 128 /* r6 = ctx->level */ 129 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, 130 offsetof(struct bpf_sockopt, level)), 131 132 /* if (ctx->level == 123) { */ 133 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4), 134 /* ctx->retval = 0 */ 135 BPF_MOV64_IMM(BPF_REG_0, 0), 136 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 137 offsetof(struct bpf_sockopt, retval)), 138 /* return 1 */ 139 BPF_MOV64_IMM(BPF_REG_0, 1), 140 BPF_JMP_A(1), 141 /* } else { */ 142 /* return 0 */ 143 BPF_MOV64_IMM(BPF_REG_0, 0), 144 /* } */ 145 BPF_EXIT_INSN(), 146 }, 147 .attach_type = BPF_CGROUP_GETSOCKOPT, 148 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 149 150 .get_level = 123, 151 152 .get_optlen = 1, 153 }, 154 { 155 .descr = "getsockopt: deny writing to ctx->level", 156 .insns = { 157 /* ctx->level = 1 */ 158 BPF_MOV64_IMM(BPF_REG_0, 1), 159 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 160 offsetof(struct bpf_sockopt, level)), 161 BPF_EXIT_INSN(), 162 }, 163 .attach_type = BPF_CGROUP_GETSOCKOPT, 164 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 165 166 .error = DENY_LOAD, 167 }, 168 { 169 .descr = "getsockopt: read ctx->optname", 170 .insns = { 171 /* r6 = ctx->optname */ 172 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, 173 offsetof(struct bpf_sockopt, optname)), 174 175 /* if (ctx->optname == 123) { */ 176 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4), 177 /* ctx->retval = 0 */ 178 BPF_MOV64_IMM(BPF_REG_0, 0), 179 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 180 offsetof(struct bpf_sockopt, retval)), 181 /* return 1 */ 182 BPF_MOV64_IMM(BPF_REG_0, 1), 183 BPF_JMP_A(1), 184 /* } else { */ 185 /* return 0 */ 186 BPF_MOV64_IMM(BPF_REG_0, 0), 187 /* } */ 188 BPF_EXIT_INSN(), 189 }, 190 .attach_type = BPF_CGROUP_GETSOCKOPT, 191 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 192 193 .get_optname = 123, 194 195 .get_optlen = 1, 196 }, 197 { 198 .descr = "getsockopt: read ctx->retval", 199 .insns = { 200 /* r6 = ctx->retval */ 201 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, 202 offsetof(struct bpf_sockopt, retval)), 203 204 /* return 1 */ 205 BPF_MOV64_IMM(BPF_REG_0, 1), 206 BPF_EXIT_INSN(), 207 }, 208 .attach_type = BPF_CGROUP_GETSOCKOPT, 209 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 210 211 .get_level = SOL_IP, 212 .get_optname = IP_TOS, 213 .get_optlen = 1, 214 }, 215 { 216 .descr = "getsockopt: deny writing to ctx->optname", 217 .insns = { 218 /* ctx->optname = 1 */ 219 BPF_MOV64_IMM(BPF_REG_0, 1), 220 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 221 offsetof(struct bpf_sockopt, optname)), 222 BPF_EXIT_INSN(), 223 }, 224 .attach_type = BPF_CGROUP_GETSOCKOPT, 225 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 226 227 .error = DENY_LOAD, 228 }, 229 { 230 .descr = "getsockopt: read ctx->optlen", 231 .insns = { 232 /* r6 = ctx->optlen */ 233 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, 234 offsetof(struct bpf_sockopt, optlen)), 235 236 /* if (ctx->optlen == 64) { */ 237 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4), 238 /* ctx->retval = 0 */ 239 BPF_MOV64_IMM(BPF_REG_0, 0), 240 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 241 offsetof(struct bpf_sockopt, retval)), 242 /* return 1 */ 243 BPF_MOV64_IMM(BPF_REG_0, 1), 244 BPF_JMP_A(1), 245 /* } else { */ 246 /* return 0 */ 247 BPF_MOV64_IMM(BPF_REG_0, 0), 248 /* } */ 249 BPF_EXIT_INSN(), 250 }, 251 .attach_type = BPF_CGROUP_GETSOCKOPT, 252 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 253 254 .get_optlen = 64, 255 }, 256 { 257 .descr = "getsockopt: deny bigger ctx->optlen", 258 .insns = { 259 /* ctx->optlen = 65 */ 260 BPF_MOV64_IMM(BPF_REG_0, 65), 261 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 262 offsetof(struct bpf_sockopt, optlen)), 263 264 /* ctx->retval = 0 */ 265 BPF_MOV64_IMM(BPF_REG_0, 0), 266 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 267 offsetof(struct bpf_sockopt, retval)), 268 269 /* return 1 */ 270 BPF_MOV64_IMM(BPF_REG_0, 1), 271 BPF_EXIT_INSN(), 272 }, 273 .attach_type = BPF_CGROUP_GETSOCKOPT, 274 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 275 276 .get_optlen = 64, 277 278 .error = EFAULT_GETSOCKOPT, 279 }, 280 { 281 .descr = "getsockopt: ignore >PAGE_SIZE optlen", 282 .insns = { 283 /* write 0xFF to the first optval byte */ 284 285 /* r6 = ctx->optval */ 286 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 287 offsetof(struct bpf_sockopt, optval)), 288 /* r2 = ctx->optval */ 289 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6), 290 /* r6 = ctx->optval + 1 */ 291 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), 292 293 /* r7 = ctx->optval_end */ 294 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1, 295 offsetof(struct bpf_sockopt, optval_end)), 296 297 /* if (ctx->optval + 1 <= ctx->optval_end) { */ 298 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1), 299 /* ctx->optval[0] = 0xF0 */ 300 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xFF), 301 /* } */ 302 303 /* retval changes are ignored */ 304 /* ctx->retval = 5 */ 305 BPF_MOV64_IMM(BPF_REG_0, 5), 306 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 307 offsetof(struct bpf_sockopt, retval)), 308 309 /* return 1 */ 310 BPF_MOV64_IMM(BPF_REG_0, 1), 311 BPF_EXIT_INSN(), 312 }, 313 .attach_type = BPF_CGROUP_GETSOCKOPT, 314 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 315 316 .get_level = 1234, 317 .get_optname = 5678, 318 .get_optval = {}, /* the changes are ignored */ 319 .get_optlen = PAGE_SIZE + 1, 320 .error = EOPNOTSUPP_GETSOCKOPT, 321 }, 322 { 323 .descr = "getsockopt: support smaller ctx->optlen", 324 .insns = { 325 /* ctx->optlen = 32 */ 326 BPF_MOV64_IMM(BPF_REG_0, 32), 327 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 328 offsetof(struct bpf_sockopt, optlen)), 329 /* ctx->retval = 0 */ 330 BPF_MOV64_IMM(BPF_REG_0, 0), 331 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 332 offsetof(struct bpf_sockopt, retval)), 333 /* return 1 */ 334 BPF_MOV64_IMM(BPF_REG_0, 1), 335 BPF_EXIT_INSN(), 336 }, 337 .attach_type = BPF_CGROUP_GETSOCKOPT, 338 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 339 340 .get_optlen = 64, 341 .get_optlen_ret = 32, 342 }, 343 { 344 .descr = "getsockopt: deny writing to ctx->optval", 345 .insns = { 346 /* ctx->optval = 1 */ 347 BPF_MOV64_IMM(BPF_REG_0, 1), 348 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 349 offsetof(struct bpf_sockopt, optval)), 350 BPF_EXIT_INSN(), 351 }, 352 .attach_type = BPF_CGROUP_GETSOCKOPT, 353 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 354 355 .error = DENY_LOAD, 356 }, 357 { 358 .descr = "getsockopt: deny writing to ctx->optval_end", 359 .insns = { 360 /* ctx->optval_end = 1 */ 361 BPF_MOV64_IMM(BPF_REG_0, 1), 362 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 363 offsetof(struct bpf_sockopt, optval_end)), 364 BPF_EXIT_INSN(), 365 }, 366 .attach_type = BPF_CGROUP_GETSOCKOPT, 367 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 368 369 .error = DENY_LOAD, 370 }, 371 { 372 .descr = "getsockopt: rewrite value", 373 .insns = { 374 /* r6 = ctx->optval */ 375 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 376 offsetof(struct bpf_sockopt, optval)), 377 /* r2 = ctx->optval */ 378 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6), 379 /* r6 = ctx->optval + 1 */ 380 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), 381 382 /* r7 = ctx->optval_end */ 383 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1, 384 offsetof(struct bpf_sockopt, optval_end)), 385 386 /* if (ctx->optval + 1 <= ctx->optval_end) { */ 387 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1), 388 /* ctx->optval[0] = 0xF0 */ 389 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0), 390 /* } */ 391 392 /* ctx->retval = 0 */ 393 BPF_MOV64_IMM(BPF_REG_0, 0), 394 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 395 offsetof(struct bpf_sockopt, retval)), 396 397 /* return 1*/ 398 BPF_MOV64_IMM(BPF_REG_0, 1), 399 BPF_EXIT_INSN(), 400 }, 401 .attach_type = BPF_CGROUP_GETSOCKOPT, 402 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 403 404 .get_level = SOL_IP, 405 .get_optname = IP_TOS, 406 407 .get_optval = { 0xF0 }, 408 .get_optlen = 1, 409 }, 410 411 /* ==================== setsockopt ==================== */ 412 413 { 414 .descr = "setsockopt: no expected_attach_type", 415 .insns = { 416 /* return 1 */ 417 BPF_MOV64_IMM(BPF_REG_0, 1), 418 BPF_EXIT_INSN(), 419 420 }, 421 .attach_type = BPF_CGROUP_SETSOCKOPT, 422 .expected_attach_type = 0, 423 .error = DENY_LOAD, 424 }, 425 { 426 .descr = "setsockopt: wrong expected_attach_type", 427 .insns = { 428 /* return 1 */ 429 BPF_MOV64_IMM(BPF_REG_0, 1), 430 BPF_EXIT_INSN(), 431 432 }, 433 .attach_type = BPF_CGROUP_SETSOCKOPT, 434 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 435 .error = DENY_ATTACH, 436 }, 437 { 438 .descr = "setsockopt: bypass bpf hook", 439 .insns = { 440 /* return 1 */ 441 BPF_MOV64_IMM(BPF_REG_0, 1), 442 BPF_EXIT_INSN(), 443 }, 444 .attach_type = BPF_CGROUP_SETSOCKOPT, 445 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 446 447 .get_level = SOL_IP, 448 .set_level = SOL_IP, 449 450 .get_optname = IP_TOS, 451 .set_optname = IP_TOS, 452 453 .set_optval = { 1 << 3 }, 454 .set_optlen = 1, 455 456 .get_optval = { 1 << 3 }, 457 .get_optlen = 1, 458 }, 459 { 460 .descr = "setsockopt: return EPERM from bpf hook", 461 .insns = { 462 /* return 0 */ 463 BPF_MOV64_IMM(BPF_REG_0, 0), 464 BPF_EXIT_INSN(), 465 }, 466 .attach_type = BPF_CGROUP_SETSOCKOPT, 467 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 468 469 .set_level = SOL_IP, 470 .set_optname = IP_TOS, 471 472 .set_optlen = 1, 473 .error = EPERM_SETSOCKOPT, 474 }, 475 { 476 .descr = "setsockopt: no optval bounds check, deny loading", 477 .insns = { 478 /* r6 = ctx->optval */ 479 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 480 offsetof(struct bpf_sockopt, optval)), 481 482 /* r0 = ctx->optval[0] */ 483 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0), 484 485 /* return 1 */ 486 BPF_MOV64_IMM(BPF_REG_0, 1), 487 BPF_EXIT_INSN(), 488 }, 489 .attach_type = BPF_CGROUP_SETSOCKOPT, 490 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 491 .error = DENY_LOAD, 492 }, 493 { 494 .descr = "setsockopt: read ctx->level", 495 .insns = { 496 /* r6 = ctx->level */ 497 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, 498 offsetof(struct bpf_sockopt, level)), 499 500 /* if (ctx->level == 123) { */ 501 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4), 502 /* ctx->optlen = -1 */ 503 BPF_MOV64_IMM(BPF_REG_0, -1), 504 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 505 offsetof(struct bpf_sockopt, optlen)), 506 /* return 1 */ 507 BPF_MOV64_IMM(BPF_REG_0, 1), 508 BPF_JMP_A(1), 509 /* } else { */ 510 /* return 0 */ 511 BPF_MOV64_IMM(BPF_REG_0, 0), 512 /* } */ 513 BPF_EXIT_INSN(), 514 }, 515 .attach_type = BPF_CGROUP_SETSOCKOPT, 516 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 517 518 .set_level = 123, 519 520 .set_optlen = 1, 521 }, 522 { 523 .descr = "setsockopt: allow changing ctx->level", 524 .insns = { 525 /* ctx->level = SOL_IP */ 526 BPF_MOV64_IMM(BPF_REG_0, SOL_IP), 527 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 528 offsetof(struct bpf_sockopt, level)), 529 /* return 1 */ 530 BPF_MOV64_IMM(BPF_REG_0, 1), 531 BPF_EXIT_INSN(), 532 }, 533 .attach_type = BPF_CGROUP_SETSOCKOPT, 534 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 535 536 .get_level = SOL_IP, 537 .set_level = 234, /* should be rewritten to SOL_IP */ 538 539 .get_optname = IP_TOS, 540 .set_optname = IP_TOS, 541 542 .set_optval = { 1 << 3 }, 543 .set_optlen = 1, 544 .get_optval = { 1 << 3 }, 545 .get_optlen = 1, 546 }, 547 { 548 .descr = "setsockopt: read ctx->optname", 549 .insns = { 550 /* r6 = ctx->optname */ 551 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, 552 offsetof(struct bpf_sockopt, optname)), 553 554 /* if (ctx->optname == 123) { */ 555 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4), 556 /* ctx->optlen = -1 */ 557 BPF_MOV64_IMM(BPF_REG_0, -1), 558 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 559 offsetof(struct bpf_sockopt, optlen)), 560 /* return 1 */ 561 BPF_MOV64_IMM(BPF_REG_0, 1), 562 BPF_JMP_A(1), 563 /* } else { */ 564 /* return 0 */ 565 BPF_MOV64_IMM(BPF_REG_0, 0), 566 /* } */ 567 BPF_EXIT_INSN(), 568 }, 569 .attach_type = BPF_CGROUP_SETSOCKOPT, 570 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 571 572 .set_optname = 123, 573 574 .set_optlen = 1, 575 }, 576 { 577 .descr = "setsockopt: allow changing ctx->optname", 578 .insns = { 579 /* ctx->optname = IP_TOS */ 580 BPF_MOV64_IMM(BPF_REG_0, IP_TOS), 581 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 582 offsetof(struct bpf_sockopt, optname)), 583 /* return 1 */ 584 BPF_MOV64_IMM(BPF_REG_0, 1), 585 BPF_EXIT_INSN(), 586 }, 587 .attach_type = BPF_CGROUP_SETSOCKOPT, 588 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 589 590 .get_level = SOL_IP, 591 .set_level = SOL_IP, 592 593 .get_optname = IP_TOS, 594 .set_optname = 456, /* should be rewritten to IP_TOS */ 595 596 .set_optval = { 1 << 3 }, 597 .set_optlen = 1, 598 .get_optval = { 1 << 3 }, 599 .get_optlen = 1, 600 }, 601 { 602 .descr = "setsockopt: read ctx->optlen", 603 .insns = { 604 /* r6 = ctx->optlen */ 605 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, 606 offsetof(struct bpf_sockopt, optlen)), 607 608 /* if (ctx->optlen == 64) { */ 609 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4), 610 /* ctx->optlen = -1 */ 611 BPF_MOV64_IMM(BPF_REG_0, -1), 612 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 613 offsetof(struct bpf_sockopt, optlen)), 614 /* return 1 */ 615 BPF_MOV64_IMM(BPF_REG_0, 1), 616 BPF_JMP_A(1), 617 /* } else { */ 618 /* return 0 */ 619 BPF_MOV64_IMM(BPF_REG_0, 0), 620 /* } */ 621 BPF_EXIT_INSN(), 622 }, 623 .attach_type = BPF_CGROUP_SETSOCKOPT, 624 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 625 626 .set_optlen = 64, 627 }, 628 { 629 .descr = "setsockopt: ctx->optlen == -1 is ok", 630 .insns = { 631 /* ctx->optlen = -1 */ 632 BPF_MOV64_IMM(BPF_REG_0, -1), 633 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 634 offsetof(struct bpf_sockopt, optlen)), 635 /* return 1 */ 636 BPF_MOV64_IMM(BPF_REG_0, 1), 637 BPF_EXIT_INSN(), 638 }, 639 .attach_type = BPF_CGROUP_SETSOCKOPT, 640 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 641 642 .set_optlen = 64, 643 }, 644 { 645 .descr = "setsockopt: deny ctx->optlen < 0 (except -1)", 646 .insns = { 647 /* ctx->optlen = -2 */ 648 BPF_MOV64_IMM(BPF_REG_0, -2), 649 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 650 offsetof(struct bpf_sockopt, optlen)), 651 /* return 1 */ 652 BPF_MOV64_IMM(BPF_REG_0, 1), 653 BPF_EXIT_INSN(), 654 }, 655 .attach_type = BPF_CGROUP_SETSOCKOPT, 656 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 657 658 .set_optlen = 4, 659 660 .error = EFAULT_SETSOCKOPT, 661 }, 662 { 663 .descr = "setsockopt: deny ctx->optlen > input optlen", 664 .insns = { 665 /* ctx->optlen = 65 */ 666 BPF_MOV64_IMM(BPF_REG_0, 65), 667 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 668 offsetof(struct bpf_sockopt, optlen)), 669 BPF_MOV64_IMM(BPF_REG_0, 1), 670 BPF_EXIT_INSN(), 671 }, 672 .attach_type = BPF_CGROUP_SETSOCKOPT, 673 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 674 675 .set_optlen = 64, 676 677 .error = EFAULT_SETSOCKOPT, 678 }, 679 { 680 .descr = "setsockopt: ignore >PAGE_SIZE optlen", 681 .insns = { 682 /* write 0xFF to the first optval byte */ 683 684 /* r6 = ctx->optval */ 685 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 686 offsetof(struct bpf_sockopt, optval)), 687 /* r2 = ctx->optval */ 688 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6), 689 /* r6 = ctx->optval + 1 */ 690 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), 691 692 /* r7 = ctx->optval_end */ 693 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1, 694 offsetof(struct bpf_sockopt, optval_end)), 695 696 /* if (ctx->optval + 1 <= ctx->optval_end) { */ 697 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1), 698 /* ctx->optval[0] = 0xF0 */ 699 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0), 700 /* } */ 701 702 BPF_MOV64_IMM(BPF_REG_0, 1), 703 BPF_EXIT_INSN(), 704 }, 705 .attach_type = BPF_CGROUP_SETSOCKOPT, 706 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 707 708 .set_level = SOL_IP, 709 .set_optname = IP_TOS, 710 .set_optval = {}, 711 .set_optlen = PAGE_SIZE + 1, 712 713 .get_level = SOL_IP, 714 .get_optname = IP_TOS, 715 .get_optval = {}, /* the changes are ignored */ 716 .get_optlen = 4, 717 }, 718 { 719 .descr = "setsockopt: allow changing ctx->optlen within bounds", 720 .insns = { 721 /* r6 = ctx->optval */ 722 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 723 offsetof(struct bpf_sockopt, optval)), 724 /* r2 = ctx->optval */ 725 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6), 726 /* r6 = ctx->optval + 1 */ 727 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), 728 729 /* r7 = ctx->optval_end */ 730 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1, 731 offsetof(struct bpf_sockopt, optval_end)), 732 733 /* if (ctx->optval + 1 <= ctx->optval_end) { */ 734 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1), 735 /* ctx->optval[0] = 1 << 3 */ 736 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3), 737 /* } */ 738 739 /* ctx->optlen = 1 */ 740 BPF_MOV64_IMM(BPF_REG_0, 1), 741 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 742 offsetof(struct bpf_sockopt, optlen)), 743 744 /* return 1*/ 745 BPF_MOV64_IMM(BPF_REG_0, 1), 746 BPF_EXIT_INSN(), 747 }, 748 .attach_type = BPF_CGROUP_SETSOCKOPT, 749 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 750 751 .get_level = SOL_IP, 752 .set_level = SOL_IP, 753 754 .get_optname = IP_TOS, 755 .set_optname = IP_TOS, 756 757 .set_optval = { 1, 1, 1, 1 }, 758 .set_optlen = 4, 759 .get_optval = { 1 << 3 }, 760 .get_optlen = 1, 761 }, 762 { 763 .descr = "setsockopt: deny write ctx->retval", 764 .insns = { 765 /* ctx->retval = 0 */ 766 BPF_MOV64_IMM(BPF_REG_0, 0), 767 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 768 offsetof(struct bpf_sockopt, retval)), 769 770 /* return 1 */ 771 BPF_MOV64_IMM(BPF_REG_0, 1), 772 BPF_EXIT_INSN(), 773 }, 774 .attach_type = BPF_CGROUP_SETSOCKOPT, 775 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 776 777 .error = DENY_LOAD, 778 }, 779 { 780 .descr = "setsockopt: deny read ctx->retval", 781 .insns = { 782 /* r6 = ctx->retval */ 783 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, 784 offsetof(struct bpf_sockopt, retval)), 785 786 /* return 1 */ 787 BPF_MOV64_IMM(BPF_REG_0, 1), 788 BPF_EXIT_INSN(), 789 }, 790 .attach_type = BPF_CGROUP_SETSOCKOPT, 791 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 792 793 .error = DENY_LOAD, 794 }, 795 { 796 .descr = "setsockopt: deny writing to ctx->optval", 797 .insns = { 798 /* ctx->optval = 1 */ 799 BPF_MOV64_IMM(BPF_REG_0, 1), 800 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 801 offsetof(struct bpf_sockopt, optval)), 802 BPF_EXIT_INSN(), 803 }, 804 .attach_type = BPF_CGROUP_SETSOCKOPT, 805 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 806 807 .error = DENY_LOAD, 808 }, 809 { 810 .descr = "setsockopt: deny writing to ctx->optval_end", 811 .insns = { 812 /* ctx->optval_end = 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_end)), 816 BPF_EXIT_INSN(), 817 }, 818 .attach_type = BPF_CGROUP_SETSOCKOPT, 819 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 820 821 .error = DENY_LOAD, 822 }, 823 { 824 .descr = "setsockopt: allow IP_TOS <= 128", 825 .insns = { 826 /* r6 = ctx->optval */ 827 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 828 offsetof(struct bpf_sockopt, optval)), 829 /* r7 = ctx->optval + 1 */ 830 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6), 831 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1), 832 833 /* r8 = ctx->optval_end */ 834 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1, 835 offsetof(struct bpf_sockopt, optval_end)), 836 837 /* if (ctx->optval + 1 <= ctx->optval_end) { */ 838 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4), 839 840 /* r9 = ctx->optval[0] */ 841 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0), 842 843 /* if (ctx->optval[0] < 128) */ 844 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2), 845 BPF_MOV64_IMM(BPF_REG_0, 1), 846 BPF_JMP_A(1), 847 /* } */ 848 849 /* } else { */ 850 BPF_MOV64_IMM(BPF_REG_0, 0), 851 /* } */ 852 853 BPF_EXIT_INSN(), 854 }, 855 .attach_type = BPF_CGROUP_SETSOCKOPT, 856 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 857 858 .get_level = SOL_IP, 859 .set_level = SOL_IP, 860 861 .get_optname = IP_TOS, 862 .set_optname = IP_TOS, 863 864 .set_optval = { 0x80 }, 865 .set_optlen = 1, 866 .get_optval = { 0x80 }, 867 .get_optlen = 1, 868 }, 869 { 870 .descr = "setsockopt: deny IP_TOS > 128", 871 .insns = { 872 /* r6 = ctx->optval */ 873 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 874 offsetof(struct bpf_sockopt, optval)), 875 /* r7 = ctx->optval + 1 */ 876 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6), 877 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1), 878 879 /* r8 = ctx->optval_end */ 880 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1, 881 offsetof(struct bpf_sockopt, optval_end)), 882 883 /* if (ctx->optval + 1 <= ctx->optval_end) { */ 884 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4), 885 886 /* r9 = ctx->optval[0] */ 887 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0), 888 889 /* if (ctx->optval[0] < 128) */ 890 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2), 891 BPF_MOV64_IMM(BPF_REG_0, 1), 892 BPF_JMP_A(1), 893 /* } */ 894 895 /* } else { */ 896 BPF_MOV64_IMM(BPF_REG_0, 0), 897 /* } */ 898 899 BPF_EXIT_INSN(), 900 }, 901 .attach_type = BPF_CGROUP_SETSOCKOPT, 902 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 903 904 .get_level = SOL_IP, 905 .set_level = SOL_IP, 906 907 .get_optname = IP_TOS, 908 .set_optname = IP_TOS, 909 910 .set_optval = { 0x81 }, 911 .set_optlen = 1, 912 .get_optval = { 0x00 }, 913 .get_optlen = 1, 914 915 .error = EPERM_SETSOCKOPT, 916 }, 917 }; 918 919 static int load_prog(const struct bpf_insn *insns, 920 enum bpf_attach_type expected_attach_type) 921 { 922 LIBBPF_OPTS(bpf_prog_load_opts, opts, 923 .expected_attach_type = expected_attach_type, 924 .log_level = 2, 925 .log_buf = bpf_log_buf, 926 .log_size = sizeof(bpf_log_buf), 927 ); 928 int fd, insns_cnt = 0; 929 930 for (; 931 insns[insns_cnt].code != (BPF_JMP | BPF_EXIT); 932 insns_cnt++) { 933 } 934 insns_cnt++; 935 936 fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCKOPT, NULL, "GPL", insns, insns_cnt, &opts); 937 if (verbose && fd < 0) 938 fprintf(stderr, "%s\n", bpf_log_buf); 939 940 return fd; 941 } 942 943 static int run_test(int cgroup_fd, struct sockopt_test *test) 944 { 945 int sock_fd, err, prog_fd; 946 void *optval = NULL; 947 int ret = 0; 948 949 prog_fd = load_prog(test->insns, test->expected_attach_type); 950 if (prog_fd < 0) { 951 if (test->error == DENY_LOAD) 952 return 0; 953 954 log_err("Failed to load BPF program"); 955 return -1; 956 } 957 958 err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0); 959 if (err < 0) { 960 if (test->error == DENY_ATTACH) 961 goto close_prog_fd; 962 963 log_err("Failed to attach BPF program"); 964 ret = -1; 965 goto close_prog_fd; 966 } 967 968 sock_fd = socket(AF_INET, SOCK_STREAM, 0); 969 if (sock_fd < 0) { 970 log_err("Failed to create AF_INET socket"); 971 ret = -1; 972 goto detach_prog; 973 } 974 975 if (test->set_optlen) { 976 if (test->set_optlen >= PAGE_SIZE) { 977 int num_pages = test->set_optlen / PAGE_SIZE; 978 int remainder = test->set_optlen % PAGE_SIZE; 979 980 test->set_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder; 981 } 982 983 err = setsockopt(sock_fd, test->set_level, test->set_optname, 984 test->set_optval, test->set_optlen); 985 if (err) { 986 if (errno == EPERM && test->error == EPERM_SETSOCKOPT) 987 goto close_sock_fd; 988 if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT) 989 goto free_optval; 990 991 log_err("Failed to call setsockopt"); 992 ret = -1; 993 goto close_sock_fd; 994 } 995 } 996 997 if (test->get_optlen) { 998 if (test->get_optlen >= PAGE_SIZE) { 999 int num_pages = test->get_optlen / PAGE_SIZE; 1000 int remainder = test->get_optlen % PAGE_SIZE; 1001 1002 test->get_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder; 1003 } 1004 1005 optval = malloc(test->get_optlen); 1006 memset(optval, 0, test->get_optlen); 1007 socklen_t optlen = test->get_optlen; 1008 socklen_t expected_get_optlen = test->get_optlen_ret ?: 1009 test->get_optlen; 1010 1011 err = getsockopt(sock_fd, test->get_level, test->get_optname, 1012 optval, &optlen); 1013 if (err) { 1014 if (errno == EOPNOTSUPP && test->error == EOPNOTSUPP_GETSOCKOPT) 1015 goto free_optval; 1016 if (errno == EPERM && test->error == EPERM_GETSOCKOPT) 1017 goto free_optval; 1018 if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT) 1019 goto free_optval; 1020 1021 log_err("Failed to call getsockopt"); 1022 ret = -1; 1023 goto free_optval; 1024 } 1025 1026 if (optlen != expected_get_optlen) { 1027 errno = 0; 1028 log_err("getsockopt returned unexpected optlen"); 1029 ret = -1; 1030 goto free_optval; 1031 } 1032 1033 if (memcmp(optval, test->get_optval, optlen) != 0) { 1034 errno = 0; 1035 log_err("getsockopt returned unexpected optval"); 1036 ret = -1; 1037 goto free_optval; 1038 } 1039 } 1040 1041 ret = test->error != OK; 1042 1043 free_optval: 1044 free(optval); 1045 close_sock_fd: 1046 close(sock_fd); 1047 detach_prog: 1048 bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type); 1049 close_prog_fd: 1050 close(prog_fd); 1051 return ret; 1052 } 1053 1054 void test_sockopt(void) 1055 { 1056 int cgroup_fd, i; 1057 1058 cgroup_fd = test__join_cgroup("/sockopt"); 1059 if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup")) 1060 return; 1061 1062 for (i = 0; i < ARRAY_SIZE(tests); i++) { 1063 if (!test__start_subtest(tests[i].descr)) 1064 continue; 1065 1066 ASSERT_OK(run_test(cgroup_fd, &tests[i]), tests[i].descr); 1067 } 1068 1069 close(cgroup_fd); 1070 } 1071