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