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