1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <limits.h> 6 #include <sched.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <linux/nsfs.h> 11 #include <sys/mount.h> 12 #include <sys/stat.h> 13 #include <sys/types.h> 14 #include <sys/wait.h> 15 #include <unistd.h> 16 #include "../kselftest_harness.h" 17 #include "../filesystems/utils.h" 18 19 #ifndef FD_NSFS_ROOT 20 #define FD_NSFS_ROOT -10003 /* Root of the nsfs filesystem */ 21 #endif 22 23 #ifndef FILEID_NSFS 24 #define FILEID_NSFS 0xf1 25 #endif 26 27 /* 28 * Test that initial namespaces can be reopened via file handle. 29 * Initial namespaces should have active ref count of 1 from boot. 30 */ 31 TEST(init_ns_always_active) 32 { 33 struct file_handle *handle; 34 int mount_id; 35 int ret; 36 int fd1, fd2; 37 struct stat st1, st2; 38 39 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ); 40 ASSERT_NE(handle, NULL); 41 42 /* Open initial network namespace */ 43 fd1 = open("/proc/1/ns/net", O_RDONLY); 44 ASSERT_GE(fd1, 0); 45 46 /* Get file handle for initial namespace */ 47 handle->handle_bytes = MAX_HANDLE_SZ; 48 ret = name_to_handle_at(fd1, "", handle, &mount_id, AT_EMPTY_PATH); 49 if (ret < 0 && errno == EOPNOTSUPP) { 50 SKIP(free(handle); close(fd1); 51 return, "nsfs doesn't support file handles"); 52 } 53 ASSERT_EQ(ret, 0); 54 55 /* Close the namespace fd */ 56 close(fd1); 57 58 /* Try to reopen via file handle - should succeed since init ns is always active */ 59 fd2 = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 60 if (fd2 < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) { 61 SKIP(free(handle); 62 return, "open_by_handle_at with FD_NSFS_ROOT not supported"); 63 } 64 ASSERT_GE(fd2, 0); 65 66 /* Verify we opened the same namespace */ 67 fd1 = open("/proc/1/ns/net", O_RDONLY); 68 ASSERT_GE(fd1, 0); 69 ASSERT_EQ(fstat(fd1, &st1), 0); 70 ASSERT_EQ(fstat(fd2, &st2), 0); 71 ASSERT_EQ(st1.st_ino, st2.st_ino); 72 73 close(fd1); 74 close(fd2); 75 free(handle); 76 } 77 78 /* 79 * Test namespace lifecycle: create a namespace in a child process, 80 * get a file handle while it's active, then try to reopen after 81 * the process exits (namespace becomes inactive). 82 */ 83 TEST(ns_inactive_after_exit) 84 { 85 struct file_handle *handle; 86 int mount_id; 87 int ret; 88 int fd; 89 int pipefd[2]; 90 pid_t pid; 91 int status; 92 char buf[sizeof(*handle) + MAX_HANDLE_SZ]; 93 94 /* Create pipe for passing file handle from child */ 95 ASSERT_EQ(pipe(pipefd), 0); 96 97 pid = fork(); 98 ASSERT_GE(pid, 0); 99 100 if (pid == 0) { 101 /* Child process */ 102 close(pipefd[0]); 103 104 /* Create new network namespace */ 105 ret = unshare(CLONE_NEWNET); 106 if (ret < 0) { 107 close(pipefd[1]); 108 exit(1); 109 } 110 111 /* Open our new namespace */ 112 fd = open("/proc/self/ns/net", O_RDONLY); 113 if (fd < 0) { 114 close(pipefd[1]); 115 exit(1); 116 } 117 118 /* Get file handle for the namespace */ 119 handle = (struct file_handle *)buf; 120 handle->handle_bytes = MAX_HANDLE_SZ; 121 ret = name_to_handle_at(fd, "", handle, &mount_id, AT_EMPTY_PATH); 122 close(fd); 123 124 if (ret < 0) { 125 close(pipefd[1]); 126 exit(1); 127 } 128 129 /* Send handle to parent */ 130 write(pipefd[1], buf, sizeof(*handle) + handle->handle_bytes); 131 close(pipefd[1]); 132 133 /* Exit - namespace should become inactive */ 134 exit(0); 135 } 136 137 /* Parent process */ 138 close(pipefd[1]); 139 140 /* Read file handle from child */ 141 ret = read(pipefd[0], buf, sizeof(buf)); 142 close(pipefd[0]); 143 144 /* Wait for child to exit */ 145 waitpid(pid, &status, 0); 146 ASSERT_TRUE(WIFEXITED(status)); 147 ASSERT_EQ(WEXITSTATUS(status), 0); 148 149 ASSERT_GT(ret, 0); 150 handle = (struct file_handle *)buf; 151 152 /* Try to reopen namespace - should fail with ENOENT since it's inactive */ 153 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 154 ASSERT_LT(fd, 0); 155 /* Should fail with ENOENT (namespace inactive) or ESTALE */ 156 ASSERT_TRUE(errno == ENOENT || errno == ESTALE); 157 } 158 159 /* 160 * Test that a namespace remains active while a process is using it, 161 * even after the creating process exits. 162 */ 163 TEST(ns_active_with_multiple_processes) 164 { 165 struct file_handle *handle; 166 int mount_id; 167 int ret; 168 int fd; 169 int pipefd[2]; 170 int syncpipe[2]; 171 pid_t pid1, pid2; 172 int status; 173 char buf[sizeof(*handle) + MAX_HANDLE_SZ]; 174 char sync_byte; 175 176 /* Create pipes for communication */ 177 ASSERT_EQ(pipe(pipefd), 0); 178 ASSERT_EQ(pipe(syncpipe), 0); 179 180 pid1 = fork(); 181 ASSERT_GE(pid1, 0); 182 183 if (pid1 == 0) { 184 /* First child - creates namespace */ 185 close(pipefd[0]); 186 close(syncpipe[1]); 187 188 /* Create new network namespace */ 189 ret = unshare(CLONE_NEWNET); 190 if (ret < 0) { 191 close(pipefd[1]); 192 close(syncpipe[0]); 193 exit(1); 194 } 195 196 /* Open and get handle */ 197 fd = open("/proc/self/ns/net", O_RDONLY); 198 if (fd < 0) { 199 close(pipefd[1]); 200 close(syncpipe[0]); 201 exit(1); 202 } 203 204 handle = (struct file_handle *)buf; 205 handle->handle_bytes = MAX_HANDLE_SZ; 206 ret = name_to_handle_at(fd, "", handle, &mount_id, AT_EMPTY_PATH); 207 close(fd); 208 209 if (ret < 0) { 210 close(pipefd[1]); 211 close(syncpipe[0]); 212 exit(1); 213 } 214 215 /* Send handle to parent */ 216 write(pipefd[1], buf, sizeof(*handle) + handle->handle_bytes); 217 close(pipefd[1]); 218 219 /* Wait for signal before exiting */ 220 read(syncpipe[0], &sync_byte, 1); 221 close(syncpipe[0]); 222 exit(0); 223 } 224 225 /* Parent reads handle */ 226 close(pipefd[1]); 227 ret = read(pipefd[0], buf, sizeof(buf)); 228 close(pipefd[0]); 229 ASSERT_GT(ret, 0); 230 231 handle = (struct file_handle *)buf; 232 233 /* Create second child that will keep namespace active */ 234 pid2 = fork(); 235 ASSERT_GE(pid2, 0); 236 237 if (pid2 == 0) { 238 /* Second child - reopens the namespace */ 239 close(syncpipe[0]); 240 close(syncpipe[1]); 241 242 /* Open the namespace via handle */ 243 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 244 if (fd < 0) { 245 exit(1); 246 } 247 248 /* Join the namespace */ 249 ret = setns(fd, CLONE_NEWNET); 250 close(fd); 251 if (ret < 0) { 252 exit(1); 253 } 254 255 /* Sleep to keep namespace active */ 256 sleep(1); 257 exit(0); 258 } 259 260 /* Let second child enter the namespace */ 261 usleep(100000); /* 100ms */ 262 263 /* Signal first child to exit */ 264 close(syncpipe[0]); 265 sync_byte = 'X'; 266 write(syncpipe[1], &sync_byte, 1); 267 close(syncpipe[1]); 268 269 /* Wait for first child */ 270 waitpid(pid1, &status, 0); 271 ASSERT_TRUE(WIFEXITED(status)); 272 273 /* Namespace should still be active because second child is using it */ 274 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 275 ASSERT_GE(fd, 0); 276 close(fd); 277 278 /* Wait for second child */ 279 waitpid(pid2, &status, 0); 280 ASSERT_TRUE(WIFEXITED(status)); 281 } 282 283 /* 284 * Test user namespace active ref tracking via credential lifecycle 285 */ 286 TEST(userns_active_ref_lifecycle) 287 { 288 struct file_handle *handle; 289 int mount_id; 290 int ret; 291 int fd; 292 int pipefd[2]; 293 pid_t pid; 294 int status; 295 char buf[sizeof(*handle) + MAX_HANDLE_SZ]; 296 297 ASSERT_EQ(pipe(pipefd), 0); 298 299 pid = fork(); 300 ASSERT_GE(pid, 0); 301 302 if (pid == 0) { 303 /* Child process */ 304 close(pipefd[0]); 305 306 /* Create new user namespace */ 307 ret = unshare(CLONE_NEWUSER); 308 if (ret < 0) { 309 close(pipefd[1]); 310 exit(1); 311 } 312 313 /* Set up uid/gid mappings */ 314 int uid_map_fd = open("/proc/self/uid_map", O_WRONLY); 315 int gid_map_fd = open("/proc/self/gid_map", O_WRONLY); 316 int setgroups_fd = open("/proc/self/setgroups", O_WRONLY); 317 318 if (uid_map_fd >= 0 && gid_map_fd >= 0 && setgroups_fd >= 0) { 319 write(setgroups_fd, "deny", 4); 320 close(setgroups_fd); 321 322 char mapping[64]; 323 snprintf(mapping, sizeof(mapping), "0 %d 1", getuid()); 324 write(uid_map_fd, mapping, strlen(mapping)); 325 close(uid_map_fd); 326 327 snprintf(mapping, sizeof(mapping), "0 %d 1", getgid()); 328 write(gid_map_fd, mapping, strlen(mapping)); 329 close(gid_map_fd); 330 } 331 332 /* Get file handle */ 333 fd = open("/proc/self/ns/user", O_RDONLY); 334 if (fd < 0) { 335 close(pipefd[1]); 336 exit(1); 337 } 338 339 handle = (struct file_handle *)buf; 340 handle->handle_bytes = MAX_HANDLE_SZ; 341 ret = name_to_handle_at(fd, "", handle, &mount_id, AT_EMPTY_PATH); 342 close(fd); 343 344 if (ret < 0) { 345 close(pipefd[1]); 346 exit(1); 347 } 348 349 /* Send handle to parent */ 350 write(pipefd[1], buf, sizeof(*handle) + handle->handle_bytes); 351 close(pipefd[1]); 352 exit(0); 353 } 354 355 /* Parent */ 356 close(pipefd[1]); 357 ret = read(pipefd[0], buf, sizeof(buf)); 358 close(pipefd[0]); 359 360 waitpid(pid, &status, 0); 361 ASSERT_TRUE(WIFEXITED(status)); 362 ASSERT_EQ(WEXITSTATUS(status), 0); 363 364 ASSERT_GT(ret, 0); 365 handle = (struct file_handle *)buf; 366 367 /* Namespace should be inactive after all tasks exit */ 368 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 369 ASSERT_LT(fd, 0); 370 ASSERT_TRUE(errno == ENOENT || errno == ESTALE); 371 } 372 373 /* 374 * Test PID namespace active ref tracking 375 */ 376 TEST(pidns_active_ref_lifecycle) 377 { 378 struct file_handle *handle; 379 int mount_id; 380 int ret; 381 int fd; 382 int pipefd[2]; 383 pid_t pid; 384 int status; 385 char buf[sizeof(*handle) + MAX_HANDLE_SZ]; 386 387 ASSERT_EQ(pipe(pipefd), 0); 388 389 pid = fork(); 390 ASSERT_GE(pid, 0); 391 392 if (pid == 0) { 393 /* Child process */ 394 close(pipefd[0]); 395 396 /* Create new PID namespace */ 397 ret = unshare(CLONE_NEWPID); 398 if (ret < 0) { 399 close(pipefd[1]); 400 exit(1); 401 } 402 403 /* Fork to actually enter the PID namespace */ 404 pid_t child = fork(); 405 if (child < 0) { 406 close(pipefd[1]); 407 exit(1); 408 } 409 410 if (child == 0) { 411 /* Grandchild - in new PID namespace */ 412 fd = open("/proc/self/ns/pid", O_RDONLY); 413 if (fd < 0) { 414 exit(1); 415 } 416 417 handle = (struct file_handle *)buf; 418 handle->handle_bytes = MAX_HANDLE_SZ; 419 ret = name_to_handle_at(fd, "", handle, &mount_id, AT_EMPTY_PATH); 420 close(fd); 421 422 if (ret < 0) { 423 exit(1); 424 } 425 426 /* Send handle to grandparent */ 427 write(pipefd[1], buf, sizeof(*handle) + handle->handle_bytes); 428 close(pipefd[1]); 429 exit(0); 430 } 431 432 /* Wait for grandchild */ 433 waitpid(child, NULL, 0); 434 exit(0); 435 } 436 437 /* Parent */ 438 close(pipefd[1]); 439 ret = read(pipefd[0], buf, sizeof(buf)); 440 close(pipefd[0]); 441 442 waitpid(pid, &status, 0); 443 ASSERT_TRUE(WIFEXITED(status)); 444 ASSERT_EQ(WEXITSTATUS(status), 0); 445 446 ASSERT_GT(ret, 0); 447 handle = (struct file_handle *)buf; 448 449 /* Namespace should be inactive after all processes exit */ 450 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 451 ASSERT_LT(fd, 0); 452 ASSERT_TRUE(errno == ENOENT || errno == ESTALE); 453 } 454 455 /* 456 * Test that an open file descriptor keeps a namespace active. 457 * Even after the creating process exits, the namespace should remain 458 * active as long as an fd is held open. 459 */ 460 TEST(ns_fd_keeps_active) 461 { 462 struct file_handle *handle; 463 int mount_id; 464 int ret; 465 int nsfd; 466 int pipe_child_ready[2]; 467 int pipe_parent_ready[2]; 468 pid_t pid; 469 int status; 470 char buf[sizeof(*handle) + MAX_HANDLE_SZ]; 471 char sync_byte; 472 char proc_path[64]; 473 474 ASSERT_EQ(pipe(pipe_child_ready), 0); 475 ASSERT_EQ(pipe(pipe_parent_ready), 0); 476 477 pid = fork(); 478 ASSERT_GE(pid, 0); 479 480 if (pid == 0) { 481 /* Child process */ 482 close(pipe_child_ready[0]); 483 close(pipe_parent_ready[1]); 484 485 TH_LOG("Child: creating new network namespace"); 486 487 /* Create new network namespace */ 488 ret = unshare(CLONE_NEWNET); 489 if (ret < 0) { 490 TH_LOG("Child: unshare(CLONE_NEWNET) failed: %s", strerror(errno)); 491 close(pipe_child_ready[1]); 492 close(pipe_parent_ready[0]); 493 exit(1); 494 } 495 496 TH_LOG("Child: network namespace created successfully"); 497 498 /* Get file handle for the namespace */ 499 nsfd = open("/proc/self/ns/net", O_RDONLY); 500 if (nsfd < 0) { 501 TH_LOG("Child: failed to open /proc/self/ns/net: %s", strerror(errno)); 502 close(pipe_child_ready[1]); 503 close(pipe_parent_ready[0]); 504 exit(1); 505 } 506 507 TH_LOG("Child: opened namespace fd %d", nsfd); 508 509 handle = (struct file_handle *)buf; 510 handle->handle_bytes = MAX_HANDLE_SZ; 511 ret = name_to_handle_at(nsfd, "", handle, &mount_id, AT_EMPTY_PATH); 512 close(nsfd); 513 514 if (ret < 0) { 515 TH_LOG("Child: name_to_handle_at failed: %s", strerror(errno)); 516 close(pipe_child_ready[1]); 517 close(pipe_parent_ready[0]); 518 exit(1); 519 } 520 521 TH_LOG("Child: got file handle (bytes=%u)", handle->handle_bytes); 522 523 /* Send file handle to parent */ 524 ret = write(pipe_child_ready[1], buf, sizeof(*handle) + handle->handle_bytes); 525 TH_LOG("Child: sent %d bytes of file handle to parent", ret); 526 close(pipe_child_ready[1]); 527 528 /* Wait for parent to open the fd */ 529 TH_LOG("Child: waiting for parent to open fd"); 530 ret = read(pipe_parent_ready[0], &sync_byte, 1); 531 close(pipe_parent_ready[0]); 532 533 TH_LOG("Child: parent signaled (read %d bytes), exiting now", ret); 534 /* Exit - namespace should stay active because parent holds fd */ 535 exit(0); 536 } 537 538 /* Parent process */ 539 close(pipe_child_ready[1]); 540 close(pipe_parent_ready[0]); 541 542 TH_LOG("Parent: reading file handle from child"); 543 544 /* Read file handle from child */ 545 ret = read(pipe_child_ready[0], buf, sizeof(buf)); 546 close(pipe_child_ready[0]); 547 ASSERT_GT(ret, 0); 548 handle = (struct file_handle *)buf; 549 550 TH_LOG("Parent: received %d bytes, handle size=%u", ret, handle->handle_bytes); 551 552 /* Open the child's namespace while it's still alive */ 553 snprintf(proc_path, sizeof(proc_path), "/proc/%d/ns/net", pid); 554 TH_LOG("Parent: opening child's namespace at %s", proc_path); 555 nsfd = open(proc_path, O_RDONLY); 556 if (nsfd < 0) { 557 TH_LOG("Parent: failed to open %s: %s", proc_path, strerror(errno)); 558 close(pipe_parent_ready[1]); 559 kill(pid, SIGKILL); 560 waitpid(pid, NULL, 0); 561 SKIP(return, "Failed to open child's namespace"); 562 } 563 564 TH_LOG("Parent: opened child's namespace, got fd %d", nsfd); 565 566 /* Signal child that we have the fd */ 567 sync_byte = 'G'; 568 write(pipe_parent_ready[1], &sync_byte, 1); 569 close(pipe_parent_ready[1]); 570 TH_LOG("Parent: signaled child that we have the fd"); 571 572 /* Wait for child to exit */ 573 waitpid(pid, &status, 0); 574 ASSERT_TRUE(WIFEXITED(status)); 575 ASSERT_EQ(WEXITSTATUS(status), 0); 576 577 TH_LOG("Child exited, parent holds fd %d to namespace", nsfd); 578 579 /* 580 * Namespace should still be ACTIVE because we hold an fd. 581 * We should be able to reopen it via file handle. 582 */ 583 TH_LOG("Attempting to reopen namespace via file handle (should succeed - fd held)"); 584 int fd2 = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 585 ASSERT_GE(fd2, 0); 586 587 TH_LOG("Successfully reopened namespace via file handle, got fd %d", fd2); 588 589 /* Verify it's the same namespace */ 590 struct stat st1, st2; 591 ASSERT_EQ(fstat(nsfd, &st1), 0); 592 ASSERT_EQ(fstat(fd2, &st2), 0); 593 TH_LOG("Namespace inodes: nsfd=%lu, fd2=%lu", st1.st_ino, st2.st_ino); 594 ASSERT_EQ(st1.st_ino, st2.st_ino); 595 close(fd2); 596 597 /* Now close the fd - namespace should become inactive */ 598 TH_LOG("Closing fd %d - namespace should become inactive", nsfd); 599 close(nsfd); 600 601 /* Now reopening should fail - namespace is inactive */ 602 TH_LOG("Attempting to reopen namespace via file handle (should fail - inactive)"); 603 fd2 = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 604 ASSERT_LT(fd2, 0); 605 /* Should fail with ENOENT (inactive) or ESTALE (gone) */ 606 TH_LOG("Reopen failed as expected: %s (errno=%d)", strerror(errno), errno); 607 ASSERT_TRUE(errno == ENOENT || errno == ESTALE); 608 } 609 610 /* 611 * Test hierarchical active reference propagation. 612 * When a child namespace is active, its owning user namespace should also 613 * be active automatically due to hierarchical active reference propagation. 614 * This ensures parents are always reachable when children are active. 615 */ 616 TEST(ns_parent_always_reachable) 617 { 618 struct file_handle *parent_handle, *child_handle; 619 int ret; 620 int child_nsfd; 621 int pipefd[2]; 622 pid_t pid; 623 int status; 624 __u64 parent_id, child_id; 625 char parent_buf[sizeof(*parent_handle) + MAX_HANDLE_SZ]; 626 char child_buf[sizeof(*child_handle) + MAX_HANDLE_SZ]; 627 628 ASSERT_EQ(pipe(pipefd), 0); 629 630 pid = fork(); 631 ASSERT_GE(pid, 0); 632 633 if (pid == 0) { 634 /* Child process */ 635 close(pipefd[0]); 636 637 TH_LOG("Child: creating parent user namespace and setting up mappings"); 638 639 /* Create parent user namespace with mappings */ 640 ret = setup_userns(); 641 if (ret < 0) { 642 TH_LOG("Child: setup_userns() for parent failed: %s", strerror(errno)); 643 close(pipefd[1]); 644 exit(1); 645 } 646 647 TH_LOG("Child: parent user namespace created, now uid=%d gid=%d", getuid(), getgid()); 648 649 /* Get namespace ID for parent user namespace */ 650 int parent_fd = open("/proc/self/ns/user", O_RDONLY); 651 if (parent_fd < 0) { 652 TH_LOG("Child: failed to open parent /proc/self/ns/user: %s", strerror(errno)); 653 close(pipefd[1]); 654 exit(1); 655 } 656 657 TH_LOG("Child: opened parent userns fd %d", parent_fd); 658 659 if (ioctl(parent_fd, NS_GET_ID, &parent_id) < 0) { 660 TH_LOG("Child: NS_GET_ID for parent failed: %s", strerror(errno)); 661 close(parent_fd); 662 close(pipefd[1]); 663 exit(1); 664 } 665 close(parent_fd); 666 667 TH_LOG("Child: got parent namespace ID %llu", (unsigned long long)parent_id); 668 669 /* Create child user namespace within parent */ 670 TH_LOG("Child: creating nested child user namespace"); 671 ret = setup_userns(); 672 if (ret < 0) { 673 TH_LOG("Child: setup_userns() for child failed: %s", strerror(errno)); 674 close(pipefd[1]); 675 exit(1); 676 } 677 678 TH_LOG("Child: nested child user namespace created, uid=%d gid=%d", getuid(), getgid()); 679 680 /* Get namespace ID for child user namespace */ 681 int child_fd = open("/proc/self/ns/user", O_RDONLY); 682 if (child_fd < 0) { 683 TH_LOG("Child: failed to open child /proc/self/ns/user: %s", strerror(errno)); 684 close(pipefd[1]); 685 exit(1); 686 } 687 688 TH_LOG("Child: opened child userns fd %d", child_fd); 689 690 if (ioctl(child_fd, NS_GET_ID, &child_id) < 0) { 691 TH_LOG("Child: NS_GET_ID for child failed: %s", strerror(errno)); 692 close(child_fd); 693 close(pipefd[1]); 694 exit(1); 695 } 696 close(child_fd); 697 698 TH_LOG("Child: got child namespace ID %llu", (unsigned long long)child_id); 699 700 /* Send both namespace IDs to parent */ 701 TH_LOG("Child: sending both namespace IDs to parent"); 702 write(pipefd[1], &parent_id, sizeof(parent_id)); 703 write(pipefd[1], &child_id, sizeof(child_id)); 704 close(pipefd[1]); 705 706 TH_LOG("Child: exiting - parent userns should become inactive"); 707 /* Exit - parent user namespace should become inactive */ 708 exit(0); 709 } 710 711 /* Parent process */ 712 close(pipefd[1]); 713 714 TH_LOG("Parent: reading both namespace IDs from child"); 715 716 /* Read both namespace IDs - fixed size, no parsing needed */ 717 ret = read(pipefd[0], &parent_id, sizeof(parent_id)); 718 if (ret != sizeof(parent_id)) { 719 close(pipefd[0]); 720 waitpid(pid, NULL, 0); 721 SKIP(return, "Failed to read parent namespace ID from child"); 722 } 723 724 ret = read(pipefd[0], &child_id, sizeof(child_id)); 725 close(pipefd[0]); 726 if (ret != sizeof(child_id)) { 727 waitpid(pid, NULL, 0); 728 SKIP(return, "Failed to read child namespace ID from child"); 729 } 730 731 TH_LOG("Parent: received parent_id=%llu, child_id=%llu", 732 (unsigned long long)parent_id, (unsigned long long)child_id); 733 734 /* Construct file handles from namespace IDs */ 735 parent_handle = (struct file_handle *)parent_buf; 736 parent_handle->handle_bytes = sizeof(struct nsfs_file_handle); 737 parent_handle->handle_type = FILEID_NSFS; 738 struct nsfs_file_handle *parent_fh = (struct nsfs_file_handle *)parent_handle->f_handle; 739 parent_fh->ns_id = parent_id; 740 parent_fh->ns_type = 0; 741 parent_fh->ns_inum = 0; 742 743 child_handle = (struct file_handle *)child_buf; 744 child_handle->handle_bytes = sizeof(struct nsfs_file_handle); 745 child_handle->handle_type = FILEID_NSFS; 746 struct nsfs_file_handle *child_fh = (struct nsfs_file_handle *)child_handle->f_handle; 747 child_fh->ns_id = child_id; 748 child_fh->ns_type = 0; 749 child_fh->ns_inum = 0; 750 751 TH_LOG("Parent: opening child namespace BEFORE child exits"); 752 753 /* Open child namespace while child is still alive to keep it active */ 754 child_nsfd = open_by_handle_at(FD_NSFS_ROOT, child_handle, O_RDONLY); 755 if (child_nsfd < 0) { 756 TH_LOG("Failed to open child namespace: %s (errno=%d)", strerror(errno), errno); 757 waitpid(pid, NULL, 0); 758 SKIP(return, "Failed to open child namespace"); 759 } 760 761 TH_LOG("Opened child namespace fd %d", child_nsfd); 762 763 /* Now wait for child to exit */ 764 TH_LOG("Parent: waiting for child to exit"); 765 waitpid(pid, &status, 0); 766 ASSERT_TRUE(WIFEXITED(status)); 767 ASSERT_EQ(WEXITSTATUS(status), 0); 768 769 TH_LOG("Child process exited, parent holds fd to child namespace"); 770 771 /* 772 * With hierarchical active reference propagation: 773 * Since the child namespace is active (parent process holds fd), 774 * the parent user namespace should ALSO be active automatically. 775 * This is because when we took an active reference on the child, 776 * it propagated up to the owning user namespace. 777 */ 778 TH_LOG("Attempting to reopen parent namespace (should SUCCEED - hierarchical propagation)"); 779 int parent_fd = open_by_handle_at(FD_NSFS_ROOT, parent_handle, O_RDONLY); 780 ASSERT_GE(parent_fd, 0); 781 782 TH_LOG("SUCCESS: Parent namespace is active (fd=%d) due to active child", parent_fd); 783 784 /* Verify we can also get parent via NS_GET_USERNS */ 785 TH_LOG("Verifying NS_GET_USERNS also works"); 786 int parent_fd2 = ioctl(child_nsfd, NS_GET_USERNS); 787 if (parent_fd2 < 0) { 788 close(parent_fd); 789 close(child_nsfd); 790 TH_LOG("NS_GET_USERNS failed: %s (errno=%d)", strerror(errno), errno); 791 SKIP(return, "NS_GET_USERNS not supported or failed"); 792 } 793 794 TH_LOG("NS_GET_USERNS succeeded, got parent fd %d", parent_fd2); 795 796 /* Verify both methods give us the same namespace */ 797 struct stat st1, st2; 798 ASSERT_EQ(fstat(parent_fd, &st1), 0); 799 ASSERT_EQ(fstat(parent_fd2, &st2), 0); 800 TH_LOG("Parent namespace inodes: parent_fd=%lu, parent_fd2=%lu", st1.st_ino, st2.st_ino); 801 ASSERT_EQ(st1.st_ino, st2.st_ino); 802 803 /* 804 * Close child fd - parent should remain active because we still 805 * hold direct references to it (parent_fd and parent_fd2). 806 */ 807 TH_LOG("Closing child fd - parent should remain active (direct refs held)"); 808 close(child_nsfd); 809 810 /* Parent should still be openable */ 811 TH_LOG("Verifying parent still active via file handle"); 812 int parent_fd3 = open_by_handle_at(FD_NSFS_ROOT, parent_handle, O_RDONLY); 813 ASSERT_GE(parent_fd3, 0); 814 close(parent_fd3); 815 816 TH_LOG("Closing all fds to parent namespace"); 817 close(parent_fd); 818 close(parent_fd2); 819 820 /* Both should now be inactive */ 821 TH_LOG("Attempting to reopen parent (should fail - inactive, no refs)"); 822 parent_fd = open_by_handle_at(FD_NSFS_ROOT, parent_handle, O_RDONLY); 823 ASSERT_LT(parent_fd, 0); 824 TH_LOG("Parent inactive as expected: %s (errno=%d)", strerror(errno), errno); 825 ASSERT_TRUE(errno == ENOENT || errno == ESTALE); 826 } 827 828 /* 829 * Test that bind mounts keep namespaces in the tree even when inactive 830 */ 831 TEST(ns_bind_mount_keeps_in_tree) 832 { 833 struct file_handle *handle; 834 int mount_id; 835 int ret; 836 int fd; 837 int pipefd[2]; 838 pid_t pid; 839 int status; 840 char buf[sizeof(*handle) + MAX_HANDLE_SZ]; 841 char tmpfile[] = "/tmp/ns-test-XXXXXX"; 842 int tmpfd; 843 844 /* Create temporary file for bind mount */ 845 tmpfd = mkstemp(tmpfile); 846 if (tmpfd < 0) { 847 SKIP(return, "Cannot create temporary file"); 848 } 849 close(tmpfd); 850 851 ASSERT_EQ(pipe(pipefd), 0); 852 853 pid = fork(); 854 ASSERT_GE(pid, 0); 855 856 if (pid == 0) { 857 /* Child process */ 858 close(pipefd[0]); 859 860 /* Unshare mount namespace and make mounts private to avoid propagation */ 861 ret = unshare(CLONE_NEWNS); 862 if (ret < 0) { 863 close(pipefd[1]); 864 unlink(tmpfile); 865 exit(1); 866 } 867 ret = mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL); 868 if (ret < 0) { 869 close(pipefd[1]); 870 unlink(tmpfile); 871 exit(1); 872 } 873 874 /* Create new network namespace */ 875 ret = unshare(CLONE_NEWNET); 876 if (ret < 0) { 877 close(pipefd[1]); 878 unlink(tmpfile); 879 exit(1); 880 } 881 882 /* Bind mount the namespace */ 883 ret = mount("/proc/self/ns/net", tmpfile, NULL, MS_BIND, NULL); 884 if (ret < 0) { 885 close(pipefd[1]); 886 unlink(tmpfile); 887 exit(1); 888 } 889 890 /* Get file handle */ 891 fd = open("/proc/self/ns/net", O_RDONLY); 892 if (fd < 0) { 893 umount(tmpfile); 894 close(pipefd[1]); 895 unlink(tmpfile); 896 exit(1); 897 } 898 899 handle = (struct file_handle *)buf; 900 handle->handle_bytes = MAX_HANDLE_SZ; 901 ret = name_to_handle_at(fd, "", handle, &mount_id, AT_EMPTY_PATH); 902 close(fd); 903 904 if (ret < 0) { 905 umount(tmpfile); 906 close(pipefd[1]); 907 unlink(tmpfile); 908 exit(1); 909 } 910 911 /* Send handle to parent */ 912 write(pipefd[1], buf, sizeof(*handle) + handle->handle_bytes); 913 close(pipefd[1]); 914 exit(0); 915 } 916 917 /* Parent */ 918 close(pipefd[1]); 919 ret = read(pipefd[0], buf, sizeof(buf)); 920 close(pipefd[0]); 921 922 waitpid(pid, &status, 0); 923 ASSERT_TRUE(WIFEXITED(status)); 924 ASSERT_EQ(WEXITSTATUS(status), 0); 925 926 ASSERT_GT(ret, 0); 927 handle = (struct file_handle *)buf; 928 929 /* 930 * Namespace should be inactive but still in tree due to bind mount. 931 * Reopening should fail with ENOENT (inactive) not ESTALE (not in tree). 932 */ 933 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 934 ASSERT_LT(fd, 0); 935 /* Should be ENOENT (inactive) since bind mount keeps it in tree */ 936 if (errno != ENOENT && errno != ESTALE) { 937 TH_LOG("Unexpected error: %d", errno); 938 } 939 940 /* Cleanup */ 941 umount(tmpfile); 942 unlink(tmpfile); 943 } 944 945 /* 946 * Test multi-level hierarchy (3+ levels deep). 947 * Grandparent → Parent → Child 948 * When child is active, both parent AND grandparent should be active. 949 */ 950 TEST(ns_multilevel_hierarchy) 951 { 952 struct file_handle *gp_handle, *p_handle, *c_handle; 953 int ret, pipefd[2]; 954 pid_t pid; 955 int status; 956 __u64 gp_id, p_id, c_id; 957 char gp_buf[sizeof(*gp_handle) + MAX_HANDLE_SZ]; 958 char p_buf[sizeof(*p_handle) + MAX_HANDLE_SZ]; 959 char c_buf[sizeof(*c_handle) + MAX_HANDLE_SZ]; 960 961 ASSERT_EQ(pipe(pipefd), 0); 962 pid = fork(); 963 ASSERT_GE(pid, 0); 964 965 if (pid == 0) { 966 close(pipefd[0]); 967 968 /* Create grandparent user namespace */ 969 if (setup_userns() < 0) { 970 close(pipefd[1]); 971 exit(1); 972 } 973 974 int gp_fd = open("/proc/self/ns/user", O_RDONLY); 975 if (gp_fd < 0) { 976 close(pipefd[1]); 977 exit(1); 978 } 979 if (ioctl(gp_fd, NS_GET_ID, &gp_id) < 0) { 980 close(gp_fd); 981 close(pipefd[1]); 982 exit(1); 983 } 984 close(gp_fd); 985 986 /* Create parent user namespace */ 987 if (setup_userns() < 0) { 988 close(pipefd[1]); 989 exit(1); 990 } 991 992 int p_fd = open("/proc/self/ns/user", O_RDONLY); 993 if (p_fd < 0) { 994 close(pipefd[1]); 995 exit(1); 996 } 997 if (ioctl(p_fd, NS_GET_ID, &p_id) < 0) { 998 close(p_fd); 999 close(pipefd[1]); 1000 exit(1); 1001 } 1002 close(p_fd); 1003 1004 /* Create child user namespace */ 1005 if (setup_userns() < 0) { 1006 close(pipefd[1]); 1007 exit(1); 1008 } 1009 1010 int c_fd = open("/proc/self/ns/user", O_RDONLY); 1011 if (c_fd < 0) { 1012 close(pipefd[1]); 1013 exit(1); 1014 } 1015 if (ioctl(c_fd, NS_GET_ID, &c_id) < 0) { 1016 close(c_fd); 1017 close(pipefd[1]); 1018 exit(1); 1019 } 1020 close(c_fd); 1021 1022 /* Send all three namespace IDs */ 1023 write(pipefd[1], &gp_id, sizeof(gp_id)); 1024 write(pipefd[1], &p_id, sizeof(p_id)); 1025 write(pipefd[1], &c_id, sizeof(c_id)); 1026 close(pipefd[1]); 1027 exit(0); 1028 } 1029 1030 close(pipefd[1]); 1031 1032 /* Read all three namespace IDs - fixed size, no parsing needed */ 1033 ret = read(pipefd[0], &gp_id, sizeof(gp_id)); 1034 if (ret != sizeof(gp_id)) { 1035 close(pipefd[0]); 1036 waitpid(pid, NULL, 0); 1037 SKIP(return, "Failed to read grandparent namespace ID from child"); 1038 } 1039 1040 ret = read(pipefd[0], &p_id, sizeof(p_id)); 1041 if (ret != sizeof(p_id)) { 1042 close(pipefd[0]); 1043 waitpid(pid, NULL, 0); 1044 SKIP(return, "Failed to read parent namespace ID from child"); 1045 } 1046 1047 ret = read(pipefd[0], &c_id, sizeof(c_id)); 1048 close(pipefd[0]); 1049 if (ret != sizeof(c_id)) { 1050 waitpid(pid, NULL, 0); 1051 SKIP(return, "Failed to read child namespace ID from child"); 1052 } 1053 1054 /* Construct file handles from namespace IDs */ 1055 gp_handle = (struct file_handle *)gp_buf; 1056 gp_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1057 gp_handle->handle_type = FILEID_NSFS; 1058 struct nsfs_file_handle *gp_fh = (struct nsfs_file_handle *)gp_handle->f_handle; 1059 gp_fh->ns_id = gp_id; 1060 gp_fh->ns_type = 0; 1061 gp_fh->ns_inum = 0; 1062 1063 p_handle = (struct file_handle *)p_buf; 1064 p_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1065 p_handle->handle_type = FILEID_NSFS; 1066 struct nsfs_file_handle *p_fh = (struct nsfs_file_handle *)p_handle->f_handle; 1067 p_fh->ns_id = p_id; 1068 p_fh->ns_type = 0; 1069 p_fh->ns_inum = 0; 1070 1071 c_handle = (struct file_handle *)c_buf; 1072 c_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1073 c_handle->handle_type = FILEID_NSFS; 1074 struct nsfs_file_handle *c_fh = (struct nsfs_file_handle *)c_handle->f_handle; 1075 c_fh->ns_id = c_id; 1076 c_fh->ns_type = 0; 1077 c_fh->ns_inum = 0; 1078 1079 /* Open child before process exits */ 1080 int c_fd = open_by_handle_at(FD_NSFS_ROOT, c_handle, O_RDONLY); 1081 if (c_fd < 0) { 1082 waitpid(pid, NULL, 0); 1083 SKIP(return, "Failed to open child namespace"); 1084 } 1085 1086 waitpid(pid, &status, 0); 1087 ASSERT_TRUE(WIFEXITED(status)); 1088 ASSERT_EQ(WEXITSTATUS(status), 0); 1089 1090 /* 1091 * With 3-level hierarchy and child active: 1092 * - Child is active (we hold fd) 1093 * - Parent should be active (propagated from child) 1094 * - Grandparent should be active (propagated from parent) 1095 */ 1096 TH_LOG("Testing parent active when child is active"); 1097 int p_fd = open_by_handle_at(FD_NSFS_ROOT, p_handle, O_RDONLY); 1098 ASSERT_GE(p_fd, 0); 1099 1100 TH_LOG("Testing grandparent active when child is active"); 1101 int gp_fd = open_by_handle_at(FD_NSFS_ROOT, gp_handle, O_RDONLY); 1102 ASSERT_GE(gp_fd, 0); 1103 1104 close(c_fd); 1105 close(p_fd); 1106 close(gp_fd); 1107 } 1108 1109 /* 1110 * Test multiple children sharing same parent. 1111 * Parent should stay active as long as ANY child is active. 1112 */ 1113 TEST(ns_multiple_children_same_parent) 1114 { 1115 struct file_handle *p_handle, *c1_handle, *c2_handle; 1116 int ret, pipefd[2]; 1117 pid_t pid; 1118 int status; 1119 __u64 p_id, c1_id, c2_id; 1120 char p_buf[sizeof(*p_handle) + MAX_HANDLE_SZ]; 1121 char c1_buf[sizeof(*c1_handle) + MAX_HANDLE_SZ]; 1122 char c2_buf[sizeof(*c2_handle) + MAX_HANDLE_SZ]; 1123 1124 ASSERT_EQ(pipe(pipefd), 0); 1125 pid = fork(); 1126 ASSERT_GE(pid, 0); 1127 1128 if (pid == 0) { 1129 close(pipefd[0]); 1130 1131 /* Create parent user namespace */ 1132 if (setup_userns() < 0) { 1133 close(pipefd[1]); 1134 exit(1); 1135 } 1136 1137 int p_fd = open("/proc/self/ns/user", O_RDONLY); 1138 if (p_fd < 0) { 1139 close(pipefd[1]); 1140 exit(1); 1141 } 1142 if (ioctl(p_fd, NS_GET_ID, &p_id) < 0) { 1143 close(p_fd); 1144 close(pipefd[1]); 1145 exit(1); 1146 } 1147 close(p_fd); 1148 1149 /* Create first child user namespace */ 1150 if (setup_userns() < 0) { 1151 close(pipefd[1]); 1152 exit(1); 1153 } 1154 1155 int c1_fd = open("/proc/self/ns/user", O_RDONLY); 1156 if (c1_fd < 0) { 1157 close(pipefd[1]); 1158 exit(1); 1159 } 1160 if (ioctl(c1_fd, NS_GET_ID, &c1_id) < 0) { 1161 close(c1_fd); 1162 close(pipefd[1]); 1163 exit(1); 1164 } 1165 close(c1_fd); 1166 1167 /* Return to parent user namespace and create second child */ 1168 /* We can't actually do this easily, so let's create a sibling namespace 1169 * by creating a network namespace instead */ 1170 if (unshare(CLONE_NEWNET) < 0) { 1171 close(pipefd[1]); 1172 exit(1); 1173 } 1174 1175 int c2_fd = open("/proc/self/ns/net", O_RDONLY); 1176 if (c2_fd < 0) { 1177 close(pipefd[1]); 1178 exit(1); 1179 } 1180 if (ioctl(c2_fd, NS_GET_ID, &c2_id) < 0) { 1181 close(c2_fd); 1182 close(pipefd[1]); 1183 exit(1); 1184 } 1185 close(c2_fd); 1186 1187 /* Send all namespace IDs */ 1188 write(pipefd[1], &p_id, sizeof(p_id)); 1189 write(pipefd[1], &c1_id, sizeof(c1_id)); 1190 write(pipefd[1], &c2_id, sizeof(c2_id)); 1191 close(pipefd[1]); 1192 exit(0); 1193 } 1194 1195 close(pipefd[1]); 1196 1197 /* Read all three namespace IDs - fixed size, no parsing needed */ 1198 ret = read(pipefd[0], &p_id, sizeof(p_id)); 1199 if (ret != sizeof(p_id)) { 1200 close(pipefd[0]); 1201 waitpid(pid, NULL, 0); 1202 SKIP(return, "Failed to read parent namespace ID"); 1203 } 1204 1205 ret = read(pipefd[0], &c1_id, sizeof(c1_id)); 1206 if (ret != sizeof(c1_id)) { 1207 close(pipefd[0]); 1208 waitpid(pid, NULL, 0); 1209 SKIP(return, "Failed to read first child namespace ID"); 1210 } 1211 1212 ret = read(pipefd[0], &c2_id, sizeof(c2_id)); 1213 close(pipefd[0]); 1214 if (ret != sizeof(c2_id)) { 1215 waitpid(pid, NULL, 0); 1216 SKIP(return, "Failed to read second child namespace ID"); 1217 } 1218 1219 /* Construct file handles from namespace IDs */ 1220 p_handle = (struct file_handle *)p_buf; 1221 p_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1222 p_handle->handle_type = FILEID_NSFS; 1223 struct nsfs_file_handle *p_fh = (struct nsfs_file_handle *)p_handle->f_handle; 1224 p_fh->ns_id = p_id; 1225 p_fh->ns_type = 0; 1226 p_fh->ns_inum = 0; 1227 1228 c1_handle = (struct file_handle *)c1_buf; 1229 c1_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1230 c1_handle->handle_type = FILEID_NSFS; 1231 struct nsfs_file_handle *c1_fh = (struct nsfs_file_handle *)c1_handle->f_handle; 1232 c1_fh->ns_id = c1_id; 1233 c1_fh->ns_type = 0; 1234 c1_fh->ns_inum = 0; 1235 1236 c2_handle = (struct file_handle *)c2_buf; 1237 c2_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1238 c2_handle->handle_type = FILEID_NSFS; 1239 struct nsfs_file_handle *c2_fh = (struct nsfs_file_handle *)c2_handle->f_handle; 1240 c2_fh->ns_id = c2_id; 1241 c2_fh->ns_type = 0; 1242 c2_fh->ns_inum = 0; 1243 1244 /* Open both children before process exits */ 1245 int c1_fd = open_by_handle_at(FD_NSFS_ROOT, c1_handle, O_RDONLY); 1246 int c2_fd = open_by_handle_at(FD_NSFS_ROOT, c2_handle, O_RDONLY); 1247 1248 if (c1_fd < 0 || c2_fd < 0) { 1249 if (c1_fd >= 0) close(c1_fd); 1250 if (c2_fd >= 0) close(c2_fd); 1251 waitpid(pid, NULL, 0); 1252 SKIP(return, "Failed to open child namespaces"); 1253 } 1254 1255 waitpid(pid, &status, 0); 1256 ASSERT_TRUE(WIFEXITED(status)); 1257 ASSERT_EQ(WEXITSTATUS(status), 0); 1258 1259 /* Parent should be active (both children active) */ 1260 TH_LOG("Both children active - parent should be active"); 1261 int p_fd = open_by_handle_at(FD_NSFS_ROOT, p_handle, O_RDONLY); 1262 ASSERT_GE(p_fd, 0); 1263 close(p_fd); 1264 1265 /* Close first child - parent should STILL be active */ 1266 TH_LOG("Closing first child - parent should still be active"); 1267 close(c1_fd); 1268 p_fd = open_by_handle_at(FD_NSFS_ROOT, p_handle, O_RDONLY); 1269 ASSERT_GE(p_fd, 0); 1270 close(p_fd); 1271 1272 /* Close second child - NOW parent should become inactive */ 1273 TH_LOG("Closing second child - parent should become inactive"); 1274 close(c2_fd); 1275 p_fd = open_by_handle_at(FD_NSFS_ROOT, p_handle, O_RDONLY); 1276 ASSERT_LT(p_fd, 0); 1277 } 1278 1279 /* 1280 * Test that different namespace types with same owner all contribute 1281 * active references to the owning user namespace. 1282 */ 1283 TEST(ns_different_types_same_owner) 1284 { 1285 struct file_handle *u_handle, *n_handle, *ut_handle; 1286 int ret, pipefd[2]; 1287 pid_t pid; 1288 int status; 1289 __u64 u_id, n_id, ut_id; 1290 char u_buf[sizeof(*u_handle) + MAX_HANDLE_SZ]; 1291 char n_buf[sizeof(*n_handle) + MAX_HANDLE_SZ]; 1292 char ut_buf[sizeof(*ut_handle) + MAX_HANDLE_SZ]; 1293 1294 ASSERT_EQ(pipe(pipefd), 0); 1295 pid = fork(); 1296 ASSERT_GE(pid, 0); 1297 1298 if (pid == 0) { 1299 close(pipefd[0]); 1300 1301 /* Create user namespace */ 1302 if (setup_userns() < 0) { 1303 close(pipefd[1]); 1304 exit(1); 1305 } 1306 1307 int u_fd = open("/proc/self/ns/user", O_RDONLY); 1308 if (u_fd < 0) { 1309 close(pipefd[1]); 1310 exit(1); 1311 } 1312 if (ioctl(u_fd, NS_GET_ID, &u_id) < 0) { 1313 close(u_fd); 1314 close(pipefd[1]); 1315 exit(1); 1316 } 1317 close(u_fd); 1318 1319 /* Create network namespace (owned by user namespace) */ 1320 if (unshare(CLONE_NEWNET) < 0) { 1321 close(pipefd[1]); 1322 exit(1); 1323 } 1324 1325 int n_fd = open("/proc/self/ns/net", O_RDONLY); 1326 if (n_fd < 0) { 1327 close(pipefd[1]); 1328 exit(1); 1329 } 1330 if (ioctl(n_fd, NS_GET_ID, &n_id) < 0) { 1331 close(n_fd); 1332 close(pipefd[1]); 1333 exit(1); 1334 } 1335 close(n_fd); 1336 1337 /* Create UTS namespace (also owned by user namespace) */ 1338 if (unshare(CLONE_NEWUTS) < 0) { 1339 close(pipefd[1]); 1340 exit(1); 1341 } 1342 1343 int ut_fd = open("/proc/self/ns/uts", O_RDONLY); 1344 if (ut_fd < 0) { 1345 close(pipefd[1]); 1346 exit(1); 1347 } 1348 if (ioctl(ut_fd, NS_GET_ID, &ut_id) < 0) { 1349 close(ut_fd); 1350 close(pipefd[1]); 1351 exit(1); 1352 } 1353 close(ut_fd); 1354 1355 /* Send all namespace IDs */ 1356 write(pipefd[1], &u_id, sizeof(u_id)); 1357 write(pipefd[1], &n_id, sizeof(n_id)); 1358 write(pipefd[1], &ut_id, sizeof(ut_id)); 1359 close(pipefd[1]); 1360 exit(0); 1361 } 1362 1363 close(pipefd[1]); 1364 1365 /* Read all three namespace IDs - fixed size, no parsing needed */ 1366 ret = read(pipefd[0], &u_id, sizeof(u_id)); 1367 if (ret != sizeof(u_id)) { 1368 close(pipefd[0]); 1369 waitpid(pid, NULL, 0); 1370 SKIP(return, "Failed to read user namespace ID"); 1371 } 1372 1373 ret = read(pipefd[0], &n_id, sizeof(n_id)); 1374 if (ret != sizeof(n_id)) { 1375 close(pipefd[0]); 1376 waitpid(pid, NULL, 0); 1377 SKIP(return, "Failed to read network namespace ID"); 1378 } 1379 1380 ret = read(pipefd[0], &ut_id, sizeof(ut_id)); 1381 close(pipefd[0]); 1382 if (ret != sizeof(ut_id)) { 1383 waitpid(pid, NULL, 0); 1384 SKIP(return, "Failed to read UTS namespace ID"); 1385 } 1386 1387 /* Construct file handles from namespace IDs */ 1388 u_handle = (struct file_handle *)u_buf; 1389 u_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1390 u_handle->handle_type = FILEID_NSFS; 1391 struct nsfs_file_handle *u_fh = (struct nsfs_file_handle *)u_handle->f_handle; 1392 u_fh->ns_id = u_id; 1393 u_fh->ns_type = 0; 1394 u_fh->ns_inum = 0; 1395 1396 n_handle = (struct file_handle *)n_buf; 1397 n_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1398 n_handle->handle_type = FILEID_NSFS; 1399 struct nsfs_file_handle *n_fh = (struct nsfs_file_handle *)n_handle->f_handle; 1400 n_fh->ns_id = n_id; 1401 n_fh->ns_type = 0; 1402 n_fh->ns_inum = 0; 1403 1404 ut_handle = (struct file_handle *)ut_buf; 1405 ut_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1406 ut_handle->handle_type = FILEID_NSFS; 1407 struct nsfs_file_handle *ut_fh = (struct nsfs_file_handle *)ut_handle->f_handle; 1408 ut_fh->ns_id = ut_id; 1409 ut_fh->ns_type = 0; 1410 ut_fh->ns_inum = 0; 1411 1412 /* Open both non-user namespaces before process exits */ 1413 int n_fd = open_by_handle_at(FD_NSFS_ROOT, n_handle, O_RDONLY); 1414 int ut_fd = open_by_handle_at(FD_NSFS_ROOT, ut_handle, O_RDONLY); 1415 1416 if (n_fd < 0 || ut_fd < 0) { 1417 if (n_fd >= 0) close(n_fd); 1418 if (ut_fd >= 0) close(ut_fd); 1419 waitpid(pid, NULL, 0); 1420 SKIP(return, "Failed to open namespaces"); 1421 } 1422 1423 waitpid(pid, &status, 0); 1424 ASSERT_TRUE(WIFEXITED(status)); 1425 ASSERT_EQ(WEXITSTATUS(status), 0); 1426 1427 /* 1428 * Both network and UTS namespaces are active. 1429 * User namespace should be active (gets 2 active refs). 1430 */ 1431 TH_LOG("Both net and uts active - user namespace should be active"); 1432 int u_fd = open_by_handle_at(FD_NSFS_ROOT, u_handle, O_RDONLY); 1433 ASSERT_GE(u_fd, 0); 1434 close(u_fd); 1435 1436 /* Close network namespace - user namespace should STILL be active */ 1437 TH_LOG("Closing network ns - user ns should still be active (uts still active)"); 1438 close(n_fd); 1439 u_fd = open_by_handle_at(FD_NSFS_ROOT, u_handle, O_RDONLY); 1440 ASSERT_GE(u_fd, 0); 1441 close(u_fd); 1442 1443 /* Close UTS namespace - user namespace should become inactive */ 1444 TH_LOG("Closing uts ns - user ns should become inactive"); 1445 close(ut_fd); 1446 u_fd = open_by_handle_at(FD_NSFS_ROOT, u_handle, O_RDONLY); 1447 ASSERT_LT(u_fd, 0); 1448 } 1449 1450 /* 1451 * Test hierarchical propagation with deep namespace hierarchy. 1452 * Create: init_user_ns -> user_A -> user_B -> net_ns 1453 * When net_ns is active, both user_A and user_B should be active. 1454 * This verifies the conditional recursion in __ns_ref_active_put() works. 1455 */ 1456 TEST(ns_deep_hierarchy_propagation) 1457 { 1458 struct file_handle *ua_handle, *ub_handle, *net_handle; 1459 int ret, pipefd[2]; 1460 pid_t pid; 1461 int status; 1462 __u64 ua_id, ub_id, net_id; 1463 char ua_buf[sizeof(*ua_handle) + MAX_HANDLE_SZ]; 1464 char ub_buf[sizeof(*ub_handle) + MAX_HANDLE_SZ]; 1465 char net_buf[sizeof(*net_handle) + MAX_HANDLE_SZ]; 1466 1467 ASSERT_EQ(pipe(pipefd), 0); 1468 pid = fork(); 1469 ASSERT_GE(pid, 0); 1470 1471 if (pid == 0) { 1472 close(pipefd[0]); 1473 1474 /* Create user_A -> user_B -> net hierarchy */ 1475 if (setup_userns() < 0) { 1476 close(pipefd[1]); 1477 exit(1); 1478 } 1479 1480 int ua_fd = open("/proc/self/ns/user", O_RDONLY); 1481 if (ua_fd < 0) { 1482 close(pipefd[1]); 1483 exit(1); 1484 } 1485 if (ioctl(ua_fd, NS_GET_ID, &ua_id) < 0) { 1486 close(ua_fd); 1487 close(pipefd[1]); 1488 exit(1); 1489 } 1490 close(ua_fd); 1491 1492 if (setup_userns() < 0) { 1493 close(pipefd[1]); 1494 exit(1); 1495 } 1496 1497 int ub_fd = open("/proc/self/ns/user", O_RDONLY); 1498 if (ub_fd < 0) { 1499 close(pipefd[1]); 1500 exit(1); 1501 } 1502 if (ioctl(ub_fd, NS_GET_ID, &ub_id) < 0) { 1503 close(ub_fd); 1504 close(pipefd[1]); 1505 exit(1); 1506 } 1507 close(ub_fd); 1508 1509 if (unshare(CLONE_NEWNET) < 0) { 1510 close(pipefd[1]); 1511 exit(1); 1512 } 1513 1514 int net_fd = open("/proc/self/ns/net", O_RDONLY); 1515 if (net_fd < 0) { 1516 close(pipefd[1]); 1517 exit(1); 1518 } 1519 if (ioctl(net_fd, NS_GET_ID, &net_id) < 0) { 1520 close(net_fd); 1521 close(pipefd[1]); 1522 exit(1); 1523 } 1524 close(net_fd); 1525 1526 /* Send all three namespace IDs */ 1527 write(pipefd[1], &ua_id, sizeof(ua_id)); 1528 write(pipefd[1], &ub_id, sizeof(ub_id)); 1529 write(pipefd[1], &net_id, sizeof(net_id)); 1530 close(pipefd[1]); 1531 exit(0); 1532 } 1533 1534 close(pipefd[1]); 1535 1536 /* Read all three namespace IDs - fixed size, no parsing needed */ 1537 ret = read(pipefd[0], &ua_id, sizeof(ua_id)); 1538 if (ret != sizeof(ua_id)) { 1539 close(pipefd[0]); 1540 waitpid(pid, NULL, 0); 1541 SKIP(return, "Failed to read user_A namespace ID"); 1542 } 1543 1544 ret = read(pipefd[0], &ub_id, sizeof(ub_id)); 1545 if (ret != sizeof(ub_id)) { 1546 close(pipefd[0]); 1547 waitpid(pid, NULL, 0); 1548 SKIP(return, "Failed to read user_B namespace ID"); 1549 } 1550 1551 ret = read(pipefd[0], &net_id, sizeof(net_id)); 1552 close(pipefd[0]); 1553 if (ret != sizeof(net_id)) { 1554 waitpid(pid, NULL, 0); 1555 SKIP(return, "Failed to read network namespace ID"); 1556 } 1557 1558 /* Construct file handles from namespace IDs */ 1559 ua_handle = (struct file_handle *)ua_buf; 1560 ua_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1561 ua_handle->handle_type = FILEID_NSFS; 1562 struct nsfs_file_handle *ua_fh = (struct nsfs_file_handle *)ua_handle->f_handle; 1563 ua_fh->ns_id = ua_id; 1564 ua_fh->ns_type = 0; 1565 ua_fh->ns_inum = 0; 1566 1567 ub_handle = (struct file_handle *)ub_buf; 1568 ub_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1569 ub_handle->handle_type = FILEID_NSFS; 1570 struct nsfs_file_handle *ub_fh = (struct nsfs_file_handle *)ub_handle->f_handle; 1571 ub_fh->ns_id = ub_id; 1572 ub_fh->ns_type = 0; 1573 ub_fh->ns_inum = 0; 1574 1575 net_handle = (struct file_handle *)net_buf; 1576 net_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1577 net_handle->handle_type = FILEID_NSFS; 1578 struct nsfs_file_handle *net_fh = (struct nsfs_file_handle *)net_handle->f_handle; 1579 net_fh->ns_id = net_id; 1580 net_fh->ns_type = 0; 1581 net_fh->ns_inum = 0; 1582 1583 /* Open net_ns before child exits to keep it active */ 1584 int net_fd = open_by_handle_at(FD_NSFS_ROOT, net_handle, O_RDONLY); 1585 if (net_fd < 0) { 1586 waitpid(pid, NULL, 0); 1587 SKIP(return, "Failed to open network namespace"); 1588 } 1589 1590 waitpid(pid, &status, 0); 1591 ASSERT_TRUE(WIFEXITED(status)); 1592 ASSERT_EQ(WEXITSTATUS(status), 0); 1593 1594 /* With net_ns active, both user_A and user_B should be active */ 1595 TH_LOG("Testing user_B active (net_ns active causes propagation)"); 1596 int ub_fd = open_by_handle_at(FD_NSFS_ROOT, ub_handle, O_RDONLY); 1597 ASSERT_GE(ub_fd, 0); 1598 1599 TH_LOG("Testing user_A active (propagated through user_B)"); 1600 int ua_fd = open_by_handle_at(FD_NSFS_ROOT, ua_handle, O_RDONLY); 1601 ASSERT_GE(ua_fd, 0); 1602 1603 /* Close net_ns - user_B should stay active (we hold direct ref) */ 1604 TH_LOG("Closing net_ns, user_B should remain active (direct ref held)"); 1605 close(net_fd); 1606 int ub_fd2 = open_by_handle_at(FD_NSFS_ROOT, ub_handle, O_RDONLY); 1607 ASSERT_GE(ub_fd2, 0); 1608 close(ub_fd2); 1609 1610 /* Close user_B - user_A should stay active (we hold direct ref) */ 1611 TH_LOG("Closing user_B, user_A should remain active (direct ref held)"); 1612 close(ub_fd); 1613 int ua_fd2 = open_by_handle_at(FD_NSFS_ROOT, ua_handle, O_RDONLY); 1614 ASSERT_GE(ua_fd2, 0); 1615 close(ua_fd2); 1616 1617 /* Close user_A - everything should become inactive */ 1618 TH_LOG("Closing user_A, all should become inactive"); 1619 close(ua_fd); 1620 1621 /* All should now be inactive */ 1622 ua_fd = open_by_handle_at(FD_NSFS_ROOT, ua_handle, O_RDONLY); 1623 ASSERT_LT(ua_fd, 0); 1624 } 1625 1626 /* 1627 * Test that parent stays active as long as ANY child is active. 1628 * Create parent user namespace with two child net namespaces. 1629 * Parent should remain active until BOTH children are inactive. 1630 */ 1631 TEST(ns_parent_multiple_children_refcount) 1632 { 1633 struct file_handle *parent_handle, *net1_handle, *net2_handle; 1634 int ret, pipefd[2], syncpipe[2]; 1635 pid_t pid; 1636 int status; 1637 __u64 p_id, n1_id, n2_id; 1638 char p_buf[sizeof(*parent_handle) + MAX_HANDLE_SZ]; 1639 char n1_buf[sizeof(*net1_handle) + MAX_HANDLE_SZ]; 1640 char n2_buf[sizeof(*net2_handle) + MAX_HANDLE_SZ]; 1641 char sync_byte; 1642 1643 ASSERT_EQ(pipe(pipefd), 0); 1644 ASSERT_EQ(pipe(syncpipe), 0); 1645 pid = fork(); 1646 ASSERT_GE(pid, 0); 1647 1648 if (pid == 0) { 1649 close(pipefd[0]); 1650 close(syncpipe[1]); 1651 1652 /* Create parent user namespace */ 1653 if (setup_userns() < 0) { 1654 close(pipefd[1]); 1655 exit(1); 1656 } 1657 1658 int p_fd = open("/proc/self/ns/user", O_RDONLY); 1659 if (p_fd < 0) { 1660 close(pipefd[1]); 1661 exit(1); 1662 } 1663 if (ioctl(p_fd, NS_GET_ID, &p_id) < 0) { 1664 close(p_fd); 1665 close(pipefd[1]); 1666 exit(1); 1667 } 1668 close(p_fd); 1669 1670 /* Create first network namespace */ 1671 if (unshare(CLONE_NEWNET) < 0) { 1672 close(pipefd[1]); 1673 close(syncpipe[0]); 1674 exit(1); 1675 } 1676 1677 int n1_fd = open("/proc/self/ns/net", O_RDONLY); 1678 if (n1_fd < 0) { 1679 close(pipefd[1]); 1680 close(syncpipe[0]); 1681 exit(1); 1682 } 1683 if (ioctl(n1_fd, NS_GET_ID, &n1_id) < 0) { 1684 close(n1_fd); 1685 close(pipefd[1]); 1686 close(syncpipe[0]); 1687 exit(1); 1688 } 1689 /* Keep n1_fd open so first namespace stays active */ 1690 1691 /* Create second network namespace */ 1692 if (unshare(CLONE_NEWNET) < 0) { 1693 close(n1_fd); 1694 close(pipefd[1]); 1695 close(syncpipe[0]); 1696 exit(1); 1697 } 1698 1699 int n2_fd = open("/proc/self/ns/net", O_RDONLY); 1700 if (n2_fd < 0) { 1701 close(n1_fd); 1702 close(pipefd[1]); 1703 close(syncpipe[0]); 1704 exit(1); 1705 } 1706 if (ioctl(n2_fd, NS_GET_ID, &n2_id) < 0) { 1707 close(n1_fd); 1708 close(n2_fd); 1709 close(pipefd[1]); 1710 close(syncpipe[0]); 1711 exit(1); 1712 } 1713 /* Keep both n1_fd and n2_fd open */ 1714 1715 /* Send all namespace IDs */ 1716 write(pipefd[1], &p_id, sizeof(p_id)); 1717 write(pipefd[1], &n1_id, sizeof(n1_id)); 1718 write(pipefd[1], &n2_id, sizeof(n2_id)); 1719 close(pipefd[1]); 1720 1721 /* Wait for parent to signal before exiting */ 1722 read(syncpipe[0], &sync_byte, 1); 1723 close(syncpipe[0]); 1724 exit(0); 1725 } 1726 1727 close(pipefd[1]); 1728 close(syncpipe[0]); 1729 1730 /* Read all three namespace IDs - fixed size, no parsing needed */ 1731 ret = read(pipefd[0], &p_id, sizeof(p_id)); 1732 if (ret != sizeof(p_id)) { 1733 close(pipefd[0]); 1734 waitpid(pid, NULL, 0); 1735 SKIP(return, "Failed to read parent namespace ID"); 1736 } 1737 1738 ret = read(pipefd[0], &n1_id, sizeof(n1_id)); 1739 if (ret != sizeof(n1_id)) { 1740 close(pipefd[0]); 1741 waitpid(pid, NULL, 0); 1742 SKIP(return, "Failed to read first network namespace ID"); 1743 } 1744 1745 ret = read(pipefd[0], &n2_id, sizeof(n2_id)); 1746 close(pipefd[0]); 1747 if (ret != sizeof(n2_id)) { 1748 waitpid(pid, NULL, 0); 1749 SKIP(return, "Failed to read second network namespace ID"); 1750 } 1751 1752 /* Construct file handles from namespace IDs */ 1753 parent_handle = (struct file_handle *)p_buf; 1754 parent_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1755 parent_handle->handle_type = FILEID_NSFS; 1756 struct nsfs_file_handle *p_fh = (struct nsfs_file_handle *)parent_handle->f_handle; 1757 p_fh->ns_id = p_id; 1758 p_fh->ns_type = 0; 1759 p_fh->ns_inum = 0; 1760 1761 net1_handle = (struct file_handle *)n1_buf; 1762 net1_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1763 net1_handle->handle_type = FILEID_NSFS; 1764 struct nsfs_file_handle *n1_fh = (struct nsfs_file_handle *)net1_handle->f_handle; 1765 n1_fh->ns_id = n1_id; 1766 n1_fh->ns_type = 0; 1767 n1_fh->ns_inum = 0; 1768 1769 net2_handle = (struct file_handle *)n2_buf; 1770 net2_handle->handle_bytes = sizeof(struct nsfs_file_handle); 1771 net2_handle->handle_type = FILEID_NSFS; 1772 struct nsfs_file_handle *n2_fh = (struct nsfs_file_handle *)net2_handle->f_handle; 1773 n2_fh->ns_id = n2_id; 1774 n2_fh->ns_type = 0; 1775 n2_fh->ns_inum = 0; 1776 1777 /* Open both net namespaces while child is still alive */ 1778 int n1_fd = open_by_handle_at(FD_NSFS_ROOT, net1_handle, O_RDONLY); 1779 int n2_fd = open_by_handle_at(FD_NSFS_ROOT, net2_handle, O_RDONLY); 1780 if (n1_fd < 0 || n2_fd < 0) { 1781 if (n1_fd >= 0) close(n1_fd); 1782 if (n2_fd >= 0) close(n2_fd); 1783 sync_byte = 'G'; 1784 write(syncpipe[1], &sync_byte, 1); 1785 close(syncpipe[1]); 1786 waitpid(pid, NULL, 0); 1787 SKIP(return, "Failed to open net namespaces"); 1788 } 1789 1790 /* Signal child that we have opened the namespaces */ 1791 sync_byte = 'G'; 1792 write(syncpipe[1], &sync_byte, 1); 1793 close(syncpipe[1]); 1794 1795 /* Wait for child to exit */ 1796 waitpid(pid, &status, 0); 1797 ASSERT_TRUE(WIFEXITED(status)); 1798 ASSERT_EQ(WEXITSTATUS(status), 0); 1799 1800 /* Parent should be active (has 2 active children) */ 1801 TH_LOG("Both net namespaces active - parent should be active"); 1802 int p_fd = open_by_handle_at(FD_NSFS_ROOT, parent_handle, O_RDONLY); 1803 ASSERT_GE(p_fd, 0); 1804 close(p_fd); 1805 1806 /* Close first net namespace - parent should STILL be active */ 1807 TH_LOG("Closing first net ns - parent should still be active"); 1808 close(n1_fd); 1809 p_fd = open_by_handle_at(FD_NSFS_ROOT, parent_handle, O_RDONLY); 1810 ASSERT_GE(p_fd, 0); 1811 close(p_fd); 1812 1813 /* Close second net namespace - parent should become inactive */ 1814 TH_LOG("Closing second net ns - parent should become inactive"); 1815 close(n2_fd); 1816 p_fd = open_by_handle_at(FD_NSFS_ROOT, parent_handle, O_RDONLY); 1817 ASSERT_LT(p_fd, 0); 1818 } 1819 1820 TEST_HARNESS_MAIN 1821