Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/namespaces: eigth listns() test

Test that hierarchical active reference propagation keeps parent
user namespaces visible in listns().

Link: https://patch.msgid.link/20251029-work-namespace-nstree-listns-v4-46-2e6f823ebdc0@kernel.org
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>

+150
+150
tools/testing/selftests/namespaces/listns_test.c
··· 477 477 TH_LOG(" [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]); 478 478 } 479 479 480 + /* 481 + * Test that hierarchical active reference propagation keeps parent 482 + * user namespaces visible in listns(). 483 + */ 484 + TEST(listns_hierarchical_visibility) 485 + { 486 + struct ns_id_req req = { 487 + .size = sizeof(req), 488 + .spare = 0, 489 + .ns_id = 0, 490 + .ns_type = CLONE_NEWUSER, 491 + .spare2 = 0, 492 + .user_ns_id = 0, 493 + }; 494 + __u64 parent_ns_id = 0, child_ns_id = 0; 495 + int sv[2]; 496 + pid_t pid; 497 + int status; 498 + int bytes; 499 + __u64 ns_ids[100]; 500 + ssize_t ret; 501 + bool found_parent, found_child; 502 + 503 + ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), 0); 504 + 505 + pid = fork(); 506 + ASSERT_GE(pid, 0); 507 + 508 + if (pid == 0) { 509 + int fd; 510 + char buf; 511 + 512 + close(sv[0]); 513 + 514 + /* Create parent user namespace */ 515 + if (setup_userns() < 0) { 516 + close(sv[1]); 517 + exit(1); 518 + } 519 + 520 + fd = open("/proc/self/ns/user", O_RDONLY); 521 + if (fd < 0) { 522 + close(sv[1]); 523 + exit(1); 524 + } 525 + 526 + if (ioctl(fd, NS_GET_ID, &parent_ns_id) < 0) { 527 + close(fd); 528 + close(sv[1]); 529 + exit(1); 530 + } 531 + close(fd); 532 + 533 + /* Create child user namespace */ 534 + if (setup_userns() < 0) { 535 + close(sv[1]); 536 + exit(1); 537 + } 538 + 539 + fd = open("/proc/self/ns/user", O_RDONLY); 540 + if (fd < 0) { 541 + close(sv[1]); 542 + exit(1); 543 + } 544 + 545 + if (ioctl(fd, NS_GET_ID, &child_ns_id) < 0) { 546 + close(fd); 547 + close(sv[1]); 548 + exit(1); 549 + } 550 + close(fd); 551 + 552 + /* Send both IDs to parent */ 553 + if (write(sv[1], &parent_ns_id, sizeof(parent_ns_id)) != sizeof(parent_ns_id)) { 554 + close(sv[1]); 555 + exit(1); 556 + } 557 + if (write(sv[1], &child_ns_id, sizeof(child_ns_id)) != sizeof(child_ns_id)) { 558 + close(sv[1]); 559 + exit(1); 560 + } 561 + 562 + /* Wait for parent signal */ 563 + if (read(sv[1], &buf, 1) != 1) { 564 + close(sv[1]); 565 + exit(1); 566 + } 567 + close(sv[1]); 568 + exit(0); 569 + } 570 + 571 + /* Parent */ 572 + close(sv[1]); 573 + 574 + /* Read both namespace IDs */ 575 + bytes = read(sv[0], &parent_ns_id, sizeof(parent_ns_id)); 576 + bytes += read(sv[0], &child_ns_id, sizeof(child_ns_id)); 577 + 578 + if (bytes != (int)(2 * sizeof(__u64))) { 579 + close(sv[0]); 580 + kill(pid, SIGKILL); 581 + waitpid(pid, NULL, 0); 582 + SKIP(return, "Failed to get namespace IDs from child"); 583 + } 584 + 585 + TH_LOG("Parent user namespace ID: %llu", (unsigned long long)parent_ns_id); 586 + TH_LOG("Child user namespace ID: %llu", (unsigned long long)child_ns_id); 587 + 588 + /* List all user namespaces */ 589 + ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 590 + 591 + if (ret < 0 && errno == ENOSYS) { 592 + close(sv[0]); 593 + kill(pid, SIGKILL); 594 + waitpid(pid, NULL, 0); 595 + SKIP(return, "listns() not supported"); 596 + } 597 + 598 + ASSERT_GE(ret, 0); 599 + TH_LOG("Found %zd active user namespaces", ret); 600 + 601 + /* Both parent and child should be visible (active due to child process) */ 602 + found_parent = false; 603 + found_child = false; 604 + for (ssize_t i = 0; i < ret; i++) { 605 + if (ns_ids[i] == parent_ns_id) 606 + found_parent = true; 607 + if (ns_ids[i] == child_ns_id) 608 + found_child = true; 609 + } 610 + 611 + TH_LOG("Parent namespace %s, child namespace %s", 612 + found_parent ? "found" : "NOT FOUND", 613 + found_child ? "found" : "NOT FOUND"); 614 + 615 + ASSERT_TRUE(found_child); 616 + /* With hierarchical propagation, parent should also be active */ 617 + ASSERT_TRUE(found_parent); 618 + 619 + /* Signal child to exit */ 620 + if (write(sv[0], "X", 1) != 1) { 621 + close(sv[0]); 622 + kill(pid, SIGKILL); 623 + waitpid(pid, NULL, 0); 624 + ASSERT_TRUE(false); 625 + } 626 + close(sv[0]); 627 + waitpid(pid, &status, 0); 628 + } 629 + 480 630 TEST_HARNESS_MAIN