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