1 // SPDX-License-Identifier: GPL-2.0-only 2 3 /* 4 * Copyright 2021 Google LLC. 5 */ 6 7 #include <test_progs.h> 8 #include <cgroup_helpers.h> 9 #include <network_helpers.h> 10 11 #include "cgroup_getset_retval_setsockopt.skel.h" 12 #include "cgroup_getset_retval_getsockopt.skel.h" 13 #include "cgroup_getset_retval_hooks.skel.h" 14 15 #define SOL_CUSTOM 0xdeadbeef 16 17 static int zero; 18 19 static void test_setsockopt_set(int cgroup_fd, int sock_fd) 20 { 21 struct cgroup_getset_retval_setsockopt *obj; 22 struct bpf_link *link_set_eunatch = NULL; 23 24 obj = cgroup_getset_retval_setsockopt__open_and_load(); 25 if (!ASSERT_OK_PTR(obj, "skel-load")) 26 return; 27 28 obj->bss->page_size = sysconf(_SC_PAGESIZE); 29 30 /* Attach setsockopt that sets EUNATCH, assert that 31 * we actually get that error when we run setsockopt() 32 */ 33 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch, 34 cgroup_fd); 35 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch")) 36 goto close_bpf_object; 37 38 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, 39 &zero, sizeof(int)), "setsockopt")) 40 goto close_bpf_object; 41 if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno")) 42 goto close_bpf_object; 43 44 if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations")) 45 goto close_bpf_object; 46 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) 47 goto close_bpf_object; 48 49 close_bpf_object: 50 bpf_link__destroy(link_set_eunatch); 51 52 cgroup_getset_retval_setsockopt__destroy(obj); 53 } 54 55 static void test_setsockopt_set_and_get(int cgroup_fd, int sock_fd) 56 { 57 struct cgroup_getset_retval_setsockopt *obj; 58 struct bpf_link *link_set_eunatch = NULL, *link_get_retval = NULL; 59 60 obj = cgroup_getset_retval_setsockopt__open_and_load(); 61 if (!ASSERT_OK_PTR(obj, "skel-load")) 62 return; 63 64 obj->bss->page_size = sysconf(_SC_PAGESIZE); 65 66 /* Attach setsockopt that sets EUNATCH, and one that gets the 67 * previously set errno. Assert that we get the same errno back. 68 */ 69 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch, 70 cgroup_fd); 71 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch")) 72 goto close_bpf_object; 73 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, 74 cgroup_fd); 75 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) 76 goto close_bpf_object; 77 78 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, 79 &zero, sizeof(int)), "setsockopt")) 80 goto close_bpf_object; 81 if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno")) 82 goto close_bpf_object; 83 84 if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations")) 85 goto close_bpf_object; 86 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) 87 goto close_bpf_object; 88 if (!ASSERT_EQ(obj->bss->retval_value, -EUNATCH, "retval_value")) 89 goto close_bpf_object; 90 91 close_bpf_object: 92 bpf_link__destroy(link_set_eunatch); 93 bpf_link__destroy(link_get_retval); 94 95 cgroup_getset_retval_setsockopt__destroy(obj); 96 } 97 98 static void test_setsockopt_default_zero(int cgroup_fd, int sock_fd) 99 { 100 struct cgroup_getset_retval_setsockopt *obj; 101 struct bpf_link *link_get_retval = NULL; 102 103 obj = cgroup_getset_retval_setsockopt__open_and_load(); 104 if (!ASSERT_OK_PTR(obj, "skel-load")) 105 return; 106 107 obj->bss->page_size = sysconf(_SC_PAGESIZE); 108 109 /* Attach setsockopt that gets the previously set errno. 110 * Assert that, without anything setting one, we get 0. 111 */ 112 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, 113 cgroup_fd); 114 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) 115 goto close_bpf_object; 116 117 if (!ASSERT_OK(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, 118 &zero, sizeof(int)), "setsockopt")) 119 goto close_bpf_object; 120 121 if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations")) 122 goto close_bpf_object; 123 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) 124 goto close_bpf_object; 125 if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value")) 126 goto close_bpf_object; 127 128 close_bpf_object: 129 bpf_link__destroy(link_get_retval); 130 131 cgroup_getset_retval_setsockopt__destroy(obj); 132 } 133 134 static void test_setsockopt_default_zero_and_set(int cgroup_fd, int sock_fd) 135 { 136 struct cgroup_getset_retval_setsockopt *obj; 137 struct bpf_link *link_get_retval = NULL, *link_set_eunatch = NULL; 138 139 obj = cgroup_getset_retval_setsockopt__open_and_load(); 140 if (!ASSERT_OK_PTR(obj, "skel-load")) 141 return; 142 143 obj->bss->page_size = sysconf(_SC_PAGESIZE); 144 145 /* Attach setsockopt that gets the previously set errno, and then 146 * one that sets the errno to EUNATCH. Assert that the get does not 147 * see EUNATCH set later, and does not prevent EUNATCH from being set. 148 */ 149 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, 150 cgroup_fd); 151 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) 152 goto close_bpf_object; 153 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch, 154 cgroup_fd); 155 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch")) 156 goto close_bpf_object; 157 158 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, 159 &zero, sizeof(int)), "setsockopt")) 160 goto close_bpf_object; 161 if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno")) 162 goto close_bpf_object; 163 164 if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations")) 165 goto close_bpf_object; 166 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) 167 goto close_bpf_object; 168 if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value")) 169 goto close_bpf_object; 170 171 close_bpf_object: 172 bpf_link__destroy(link_get_retval); 173 bpf_link__destroy(link_set_eunatch); 174 175 cgroup_getset_retval_setsockopt__destroy(obj); 176 } 177 178 static void test_setsockopt_override(int cgroup_fd, int sock_fd) 179 { 180 struct cgroup_getset_retval_setsockopt *obj; 181 struct bpf_link *link_set_eunatch = NULL, *link_set_eisconn = NULL; 182 struct bpf_link *link_get_retval = NULL; 183 184 obj = cgroup_getset_retval_setsockopt__open_and_load(); 185 if (!ASSERT_OK_PTR(obj, "skel-load")) 186 return; 187 188 obj->bss->page_size = sysconf(_SC_PAGESIZE); 189 190 /* Attach setsockopt that sets EUNATCH, then one that sets EISCONN, 191 * and then one that gets the exported errno. Assert both the syscall 192 * and the helper sees the last set errno. 193 */ 194 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch, 195 cgroup_fd); 196 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch")) 197 goto close_bpf_object; 198 link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn, 199 cgroup_fd); 200 if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn")) 201 goto close_bpf_object; 202 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, 203 cgroup_fd); 204 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) 205 goto close_bpf_object; 206 207 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, 208 &zero, sizeof(int)), "setsockopt")) 209 goto close_bpf_object; 210 if (!ASSERT_EQ(errno, EISCONN, "setsockopt-errno")) 211 goto close_bpf_object; 212 213 if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations")) 214 goto close_bpf_object; 215 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) 216 goto close_bpf_object; 217 if (!ASSERT_EQ(obj->bss->retval_value, -EISCONN, "retval_value")) 218 goto close_bpf_object; 219 220 close_bpf_object: 221 bpf_link__destroy(link_set_eunatch); 222 bpf_link__destroy(link_set_eisconn); 223 bpf_link__destroy(link_get_retval); 224 225 cgroup_getset_retval_setsockopt__destroy(obj); 226 } 227 228 static void test_setsockopt_legacy_eperm(int cgroup_fd, int sock_fd) 229 { 230 struct cgroup_getset_retval_setsockopt *obj; 231 struct bpf_link *link_legacy_eperm = NULL, *link_get_retval = NULL; 232 233 obj = cgroup_getset_retval_setsockopt__open_and_load(); 234 if (!ASSERT_OK_PTR(obj, "skel-load")) 235 return; 236 237 obj->bss->page_size = sysconf(_SC_PAGESIZE); 238 239 /* Attach setsockopt that return a reject without setting errno 240 * (legacy reject), and one that gets the errno. Assert that for 241 * backward compatibility the syscall result in EPERM, and this 242 * is also visible to the helper. 243 */ 244 link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm, 245 cgroup_fd); 246 if (!ASSERT_OK_PTR(link_legacy_eperm, "cg-attach-legacy_eperm")) 247 goto close_bpf_object; 248 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, 249 cgroup_fd); 250 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) 251 goto close_bpf_object; 252 253 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, 254 &zero, sizeof(int)), "setsockopt")) 255 goto close_bpf_object; 256 if (!ASSERT_EQ(errno, EPERM, "setsockopt-errno")) 257 goto close_bpf_object; 258 259 if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations")) 260 goto close_bpf_object; 261 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) 262 goto close_bpf_object; 263 if (!ASSERT_EQ(obj->bss->retval_value, -EPERM, "retval_value")) 264 goto close_bpf_object; 265 266 close_bpf_object: 267 bpf_link__destroy(link_legacy_eperm); 268 bpf_link__destroy(link_get_retval); 269 270 cgroup_getset_retval_setsockopt__destroy(obj); 271 } 272 273 static void test_setsockopt_legacy_no_override(int cgroup_fd, int sock_fd) 274 { 275 struct cgroup_getset_retval_setsockopt *obj; 276 struct bpf_link *link_set_eunatch = NULL, *link_legacy_eperm = NULL; 277 struct bpf_link *link_get_retval = NULL; 278 279 obj = cgroup_getset_retval_setsockopt__open_and_load(); 280 if (!ASSERT_OK_PTR(obj, "skel-load")) 281 return; 282 283 obj->bss->page_size = sysconf(_SC_PAGESIZE); 284 285 /* Attach setsockopt that sets EUNATCH, then one that return a reject 286 * without setting errno, and then one that gets the exported errno. 287 * Assert both the syscall and the helper's errno are unaffected by 288 * the second prog (i.e. legacy rejects does not override the errno 289 * to EPERM). 290 */ 291 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch, 292 cgroup_fd); 293 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch")) 294 goto close_bpf_object; 295 link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm, 296 cgroup_fd); 297 if (!ASSERT_OK_PTR(link_legacy_eperm, "cg-attach-legacy_eperm")) 298 goto close_bpf_object; 299 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, 300 cgroup_fd); 301 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) 302 goto close_bpf_object; 303 304 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, 305 &zero, sizeof(int)), "setsockopt")) 306 goto close_bpf_object; 307 if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno")) 308 goto close_bpf_object; 309 310 if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations")) 311 goto close_bpf_object; 312 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) 313 goto close_bpf_object; 314 if (!ASSERT_EQ(obj->bss->retval_value, -EUNATCH, "retval_value")) 315 goto close_bpf_object; 316 317 close_bpf_object: 318 bpf_link__destroy(link_set_eunatch); 319 bpf_link__destroy(link_legacy_eperm); 320 bpf_link__destroy(link_get_retval); 321 322 cgroup_getset_retval_setsockopt__destroy(obj); 323 } 324 325 static void test_getsockopt_get(int cgroup_fd, int sock_fd) 326 { 327 struct cgroup_getset_retval_getsockopt *obj; 328 struct bpf_link *link_get_retval = NULL; 329 int buf; 330 socklen_t optlen = sizeof(buf); 331 332 obj = cgroup_getset_retval_getsockopt__open_and_load(); 333 if (!ASSERT_OK_PTR(obj, "skel-load")) 334 return; 335 336 obj->bss->page_size = sysconf(_SC_PAGESIZE); 337 338 /* Attach getsockopt that gets previously set errno. Assert that the 339 * error from kernel is in both ctx_retval_value and retval_value. 340 */ 341 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, 342 cgroup_fd); 343 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) 344 goto close_bpf_object; 345 346 if (!ASSERT_ERR(getsockopt(sock_fd, SOL_CUSTOM, 0, 347 &buf, &optlen), "getsockopt")) 348 goto close_bpf_object; 349 if (!ASSERT_EQ(errno, EOPNOTSUPP, "getsockopt-errno")) 350 goto close_bpf_object; 351 352 if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations")) 353 goto close_bpf_object; 354 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) 355 goto close_bpf_object; 356 if (!ASSERT_EQ(obj->bss->retval_value, -EOPNOTSUPP, "retval_value")) 357 goto close_bpf_object; 358 if (!ASSERT_EQ(obj->bss->ctx_retval_value, -EOPNOTSUPP, "ctx_retval_value")) 359 goto close_bpf_object; 360 361 close_bpf_object: 362 bpf_link__destroy(link_get_retval); 363 364 cgroup_getset_retval_getsockopt__destroy(obj); 365 } 366 367 static void test_getsockopt_override(int cgroup_fd, int sock_fd) 368 { 369 struct cgroup_getset_retval_getsockopt *obj; 370 struct bpf_link *link_set_eisconn = NULL; 371 int buf; 372 socklen_t optlen = sizeof(buf); 373 374 obj = cgroup_getset_retval_getsockopt__open_and_load(); 375 if (!ASSERT_OK_PTR(obj, "skel-load")) 376 return; 377 378 obj->bss->page_size = sysconf(_SC_PAGESIZE); 379 380 /* Attach getsockopt that sets retval to -EISCONN. Assert that this 381 * overrides the value from kernel. 382 */ 383 link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn, 384 cgroup_fd); 385 if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn")) 386 goto close_bpf_object; 387 388 if (!ASSERT_ERR(getsockopt(sock_fd, SOL_CUSTOM, 0, 389 &buf, &optlen), "getsockopt")) 390 goto close_bpf_object; 391 if (!ASSERT_EQ(errno, EISCONN, "getsockopt-errno")) 392 goto close_bpf_object; 393 394 if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations")) 395 goto close_bpf_object; 396 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) 397 goto close_bpf_object; 398 399 close_bpf_object: 400 bpf_link__destroy(link_set_eisconn); 401 402 cgroup_getset_retval_getsockopt__destroy(obj); 403 } 404 405 static void test_getsockopt_retval_sync(int cgroup_fd, int sock_fd) 406 { 407 struct cgroup_getset_retval_getsockopt *obj; 408 struct bpf_link *link_set_eisconn = NULL, *link_clear_retval = NULL; 409 struct bpf_link *link_get_retval = NULL; 410 int buf; 411 socklen_t optlen = sizeof(buf); 412 413 obj = cgroup_getset_retval_getsockopt__open_and_load(); 414 if (!ASSERT_OK_PTR(obj, "skel-load")) 415 return; 416 417 obj->bss->page_size = sysconf(_SC_PAGESIZE); 418 419 /* Attach getsockopt that sets retval to -EISCONN, and one that clears 420 * ctx retval. Assert that the clearing ctx retval is synced to helper 421 * and clears any errors both from kernel and BPF.. 422 */ 423 link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn, 424 cgroup_fd); 425 if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn")) 426 goto close_bpf_object; 427 link_clear_retval = bpf_program__attach_cgroup(obj->progs.clear_retval, 428 cgroup_fd); 429 if (!ASSERT_OK_PTR(link_clear_retval, "cg-attach-clear_retval")) 430 goto close_bpf_object; 431 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, 432 cgroup_fd); 433 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) 434 goto close_bpf_object; 435 436 if (!ASSERT_OK(getsockopt(sock_fd, SOL_CUSTOM, 0, 437 &buf, &optlen), "getsockopt")) 438 goto close_bpf_object; 439 440 if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations")) 441 goto close_bpf_object; 442 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) 443 goto close_bpf_object; 444 if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value")) 445 goto close_bpf_object; 446 if (!ASSERT_EQ(obj->bss->ctx_retval_value, 0, "ctx_retval_value")) 447 goto close_bpf_object; 448 449 close_bpf_object: 450 bpf_link__destroy(link_set_eisconn); 451 bpf_link__destroy(link_clear_retval); 452 bpf_link__destroy(link_get_retval); 453 454 cgroup_getset_retval_getsockopt__destroy(obj); 455 } 456 457 struct exposed_hook { 458 const char *name; 459 int expected_err; 460 } exposed_hooks[] = { 461 462 #define BPF_RETVAL_HOOK(NAME, SECTION, CTX, EXPECTED_ERR) \ 463 { \ 464 .name = #NAME, \ 465 .expected_err = EXPECTED_ERR, \ 466 }, 467 468 #include "cgroup_getset_retval_hooks.h" 469 470 #undef BPF_RETVAL_HOOK 471 }; 472 473 static void test_exposed_hooks(int cgroup_fd, int sock_fd) 474 { 475 struct cgroup_getset_retval_hooks *skel; 476 struct bpf_program *prog; 477 int err; 478 int i; 479 480 for (i = 0; i < ARRAY_SIZE(exposed_hooks); i++) { 481 skel = cgroup_getset_retval_hooks__open(); 482 if (!ASSERT_OK_PTR(skel, "cgroup_getset_retval_hooks__open")) 483 continue; 484 485 prog = bpf_object__find_program_by_name(skel->obj, exposed_hooks[i].name); 486 if (!ASSERT_NEQ(prog, NULL, "bpf_object__find_program_by_name")) 487 goto close_skel; 488 489 err = bpf_program__set_autoload(prog, true); 490 if (!ASSERT_OK(err, "bpf_program__set_autoload")) 491 goto close_skel; 492 493 err = cgroup_getset_retval_hooks__load(skel); 494 ASSERT_EQ(err, exposed_hooks[i].expected_err, "expected_err"); 495 496 close_skel: 497 cgroup_getset_retval_hooks__destroy(skel); 498 } 499 } 500 501 void test_cgroup_getset_retval(void) 502 { 503 int cgroup_fd = -1; 504 int sock_fd = -1; 505 506 cgroup_fd = test__join_cgroup("/cgroup_getset_retval"); 507 if (!ASSERT_GE(cgroup_fd, 0, "cg-create")) 508 goto close_fd; 509 510 sock_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0); 511 if (!ASSERT_GE(sock_fd, 0, "start-server")) 512 goto close_fd; 513 514 if (test__start_subtest("setsockopt-set")) 515 test_setsockopt_set(cgroup_fd, sock_fd); 516 517 if (test__start_subtest("setsockopt-set_and_get")) 518 test_setsockopt_set_and_get(cgroup_fd, sock_fd); 519 520 if (test__start_subtest("setsockopt-default_zero")) 521 test_setsockopt_default_zero(cgroup_fd, sock_fd); 522 523 if (test__start_subtest("setsockopt-default_zero_and_set")) 524 test_setsockopt_default_zero_and_set(cgroup_fd, sock_fd); 525 526 if (test__start_subtest("setsockopt-override")) 527 test_setsockopt_override(cgroup_fd, sock_fd); 528 529 if (test__start_subtest("setsockopt-legacy_eperm")) 530 test_setsockopt_legacy_eperm(cgroup_fd, sock_fd); 531 532 if (test__start_subtest("setsockopt-legacy_no_override")) 533 test_setsockopt_legacy_no_override(cgroup_fd, sock_fd); 534 535 if (test__start_subtest("getsockopt-get")) 536 test_getsockopt_get(cgroup_fd, sock_fd); 537 538 if (test__start_subtest("getsockopt-override")) 539 test_getsockopt_override(cgroup_fd, sock_fd); 540 541 if (test__start_subtest("getsockopt-retval_sync")) 542 test_getsockopt_retval_sync(cgroup_fd, sock_fd); 543 544 if (test__start_subtest("exposed_hooks")) 545 test_exposed_hooks(cgroup_fd, sock_fd); 546 547 close_fd: 548 close(cgroup_fd); 549 } 550