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/ioctl.h> 12 #include <sys/socket.h> 13 #include <sys/stat.h> 14 #include <sys/syscall.h> 15 #include <sys/types.h> 16 #include <sys/wait.h> 17 #include <unistd.h> 18 #include "../kselftest_harness.h" 19 #include "../filesystems/utils.h" 20 #include "wrappers.h" 21 22 /* 23 * Test basic listns() functionality with the unified namespace tree. 24 * List all active namespaces globally. 25 */ 26 TEST(listns_basic_unified) 27 { 28 struct ns_id_req req = { 29 .size = sizeof(req), 30 .spare = 0, 31 .ns_id = 0, 32 .ns_type = 0, /* All types */ 33 .spare2 = 0, 34 .user_ns_id = 0, /* Global listing */ 35 }; 36 __u64 ns_ids[100]; 37 ssize_t ret; 38 39 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 40 if (ret < 0) { 41 if (errno == ENOSYS) 42 SKIP(return, "listns() not supported"); 43 TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno); 44 ASSERT_TRUE(false); 45 } 46 47 /* Should find at least the initial namespaces */ 48 ASSERT_GT(ret, 0); 49 TH_LOG("Found %zd active namespaces", ret); 50 51 /* Verify all returned IDs are non-zero */ 52 for (ssize_t i = 0; i < ret; i++) { 53 ASSERT_NE(ns_ids[i], 0); 54 TH_LOG(" [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]); 55 } 56 } 57 58 /* 59 * Test listns() with type filtering. 60 * List only network namespaces. 61 */ 62 TEST(listns_filter_by_type) 63 { 64 struct ns_id_req req = { 65 .size = sizeof(req), 66 .spare = 0, 67 .ns_id = 0, 68 .ns_type = CLONE_NEWNET, /* Only network namespaces */ 69 .spare2 = 0, 70 .user_ns_id = 0, 71 }; 72 __u64 ns_ids[100]; 73 ssize_t ret; 74 75 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 76 if (ret < 0) { 77 if (errno == ENOSYS) 78 SKIP(return, "listns() not supported"); 79 TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno); 80 ASSERT_TRUE(false); 81 } 82 ASSERT_GE(ret, 0); 83 84 /* Should find at least init_net */ 85 ASSERT_GT(ret, 0); 86 TH_LOG("Found %zd active network namespaces", ret); 87 88 /* Verify we can open each namespace and it's actually a network namespace */ 89 for (ssize_t i = 0; i < ret && i < 5; i++) { 90 struct nsfs_file_handle nsfh = { 91 .ns_id = ns_ids[i], 92 .ns_type = CLONE_NEWNET, 93 .ns_inum = 0, 94 }; 95 struct file_handle *fh; 96 int fd; 97 98 fh = (struct file_handle *)malloc(sizeof(*fh) + sizeof(nsfh)); 99 ASSERT_NE(fh, NULL); 100 fh->handle_bytes = sizeof(nsfh); 101 fh->handle_type = 0; 102 memcpy(fh->f_handle, &nsfh, sizeof(nsfh)); 103 104 fd = open_by_handle_at(-10003, fh, O_RDONLY); 105 free(fh); 106 107 if (fd >= 0) { 108 int ns_type; 109 /* Verify it's a network namespace via ioctl */ 110 ns_type = ioctl(fd, NS_GET_NSTYPE); 111 if (ns_type >= 0) { 112 ASSERT_EQ(ns_type, CLONE_NEWNET); 113 } 114 close(fd); 115 } 116 } 117 } 118 119 /* 120 * Test listns() pagination. 121 * List namespaces in batches. 122 */ 123 TEST(listns_pagination) 124 { 125 struct ns_id_req req = { 126 .size = sizeof(req), 127 .spare = 0, 128 .ns_id = 0, 129 .ns_type = 0, 130 .spare2 = 0, 131 .user_ns_id = 0, 132 }; 133 __u64 batch1[2], batch2[2]; 134 ssize_t ret1, ret2; 135 136 /* Get first batch */ 137 ret1 = sys_listns(&req, batch1, ARRAY_SIZE(batch1), 0); 138 if (ret1 < 0) { 139 if (errno == ENOSYS) 140 SKIP(return, "listns() not supported"); 141 TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno); 142 ASSERT_TRUE(false); 143 } 144 ASSERT_GE(ret1, 0); 145 146 if (ret1 == 0) 147 SKIP(return, "No namespaces found"); 148 149 TH_LOG("First batch: %zd namespaces", ret1); 150 151 /* Get second batch using last ID from first batch */ 152 if (ret1 == ARRAY_SIZE(batch1)) { 153 req.ns_id = batch1[ret1 - 1]; 154 ret2 = sys_listns(&req, batch2, ARRAY_SIZE(batch2), 0); 155 ASSERT_GE(ret2, 0); 156 157 TH_LOG("Second batch: %zd namespaces (after ns_id=%llu)", 158 ret2, (unsigned long long)req.ns_id); 159 160 /* If we got more results, verify IDs are monotonically increasing */ 161 if (ret2 > 0) { 162 ASSERT_GT(batch2[0], batch1[ret1 - 1]); 163 TH_LOG("Pagination working: %llu > %llu", 164 (unsigned long long)batch2[0], 165 (unsigned long long)batch1[ret1 - 1]); 166 } 167 } else { 168 TH_LOG("All namespaces fit in first batch"); 169 } 170 } 171 172 /* 173 * Test listns() with LISTNS_CURRENT_USER. 174 * List namespaces owned by current user namespace. 175 */ 176 TEST(listns_current_user) 177 { 178 struct ns_id_req req = { 179 .size = sizeof(req), 180 .spare = 0, 181 .ns_id = 0, 182 .ns_type = 0, 183 .spare2 = 0, 184 .user_ns_id = LISTNS_CURRENT_USER, 185 }; 186 __u64 ns_ids[100]; 187 ssize_t ret; 188 189 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 190 if (ret < 0) { 191 if (errno == ENOSYS) 192 SKIP(return, "listns() not supported"); 193 TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno); 194 ASSERT_TRUE(false); 195 } 196 ASSERT_GE(ret, 0); 197 198 /* Should find at least the initial namespaces if we're in init_user_ns */ 199 TH_LOG("Found %zd namespaces owned by current user namespace", ret); 200 201 for (ssize_t i = 0; i < ret; i++) 202 TH_LOG(" [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]); 203 } 204 205 /* 206 * Test that listns() only returns active namespaces. 207 * Create a namespace, let it become inactive, verify it's not listed. 208 */ 209 TEST(listns_only_active) 210 { 211 struct ns_id_req req = { 212 .size = sizeof(req), 213 .spare = 0, 214 .ns_id = 0, 215 .ns_type = CLONE_NEWNET, 216 .spare2 = 0, 217 .user_ns_id = 0, 218 }; 219 __u64 ns_ids_before[100], ns_ids_after[100]; 220 ssize_t ret_before, ret_after; 221 int pipefd[2]; 222 pid_t pid; 223 __u64 new_ns_id = 0; 224 int status; 225 226 /* Get initial list */ 227 ret_before = sys_listns(&req, ns_ids_before, ARRAY_SIZE(ns_ids_before), 0); 228 if (ret_before < 0) { 229 if (errno == ENOSYS) 230 SKIP(return, "listns() not supported"); 231 TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno); 232 ASSERT_TRUE(false); 233 } 234 ASSERT_GE(ret_before, 0); 235 236 TH_LOG("Before: %zd active network namespaces", ret_before); 237 238 /* Create a new namespace in a child process and get its ID */ 239 ASSERT_EQ(pipe(pipefd), 0); 240 241 pid = fork(); 242 ASSERT_GE(pid, 0); 243 244 if (pid == 0) { 245 int fd; 246 __u64 ns_id; 247 248 close(pipefd[0]); 249 250 /* Create new network namespace */ 251 if (unshare(CLONE_NEWNET) < 0) { 252 close(pipefd[1]); 253 exit(1); 254 } 255 256 /* Get its ID */ 257 fd = open("/proc/self/ns/net", O_RDONLY); 258 if (fd < 0) { 259 close(pipefd[1]); 260 exit(1); 261 } 262 263 if (ioctl(fd, NS_GET_ID, &ns_id) < 0) { 264 close(fd); 265 close(pipefd[1]); 266 exit(1); 267 } 268 close(fd); 269 270 /* Send ID to parent */ 271 write(pipefd[1], &ns_id, sizeof(ns_id)); 272 close(pipefd[1]); 273 274 /* Keep namespace active briefly */ 275 usleep(100000); 276 exit(0); 277 } 278 279 /* Parent reads the new namespace ID */ 280 { 281 int bytes; 282 283 close(pipefd[1]); 284 bytes = read(pipefd[0], &new_ns_id, sizeof(new_ns_id)); 285 close(pipefd[0]); 286 287 if (bytes == sizeof(new_ns_id)) { 288 __u64 ns_ids_during[100]; 289 int ret_during; 290 291 TH_LOG("Child created namespace with ID %llu", (unsigned long long)new_ns_id); 292 293 /* List namespaces while child is still alive - should see new one */ 294 ret_during = sys_listns(&req, ns_ids_during, ARRAY_SIZE(ns_ids_during), 0); 295 ASSERT_GE(ret_during, 0); 296 TH_LOG("During: %d active network namespaces", ret_during); 297 298 /* Should have more namespaces than before */ 299 ASSERT_GE(ret_during, ret_before); 300 } 301 } 302 303 /* Wait for child to exit */ 304 waitpid(pid, &status, 0); 305 306 /* Give time for namespace to become inactive */ 307 usleep(100000); 308 309 /* List namespaces after child exits - should not see new one */ 310 ret_after = sys_listns(&req, ns_ids_after, ARRAY_SIZE(ns_ids_after), 0); 311 ASSERT_GE(ret_after, 0); 312 TH_LOG("After: %zd active network namespaces", ret_after); 313 314 /* Verify the new namespace ID is not in the after list */ 315 if (new_ns_id != 0) { 316 bool found = false; 317 318 for (ssize_t i = 0; i < ret_after; i++) { 319 if (ns_ids_after[i] == new_ns_id) { 320 found = true; 321 break; 322 } 323 } 324 ASSERT_FALSE(found); 325 } 326 } 327 328 /* 329 * Test listns() with specific user namespace ID. 330 * Create a user namespace and list namespaces it owns. 331 */ 332 TEST(listns_specific_userns) 333 { 334 struct ns_id_req req = { 335 .size = sizeof(req), 336 .spare = 0, 337 .ns_id = 0, 338 .ns_type = 0, 339 .spare2 = 0, 340 .user_ns_id = 0, /* Will be filled with created userns ID */ 341 }; 342 __u64 ns_ids[100]; 343 int sv[2]; 344 pid_t pid; 345 int status; 346 __u64 user_ns_id = 0; 347 int bytes; 348 ssize_t ret; 349 350 ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), 0); 351 352 pid = fork(); 353 ASSERT_GE(pid, 0); 354 355 if (pid == 0) { 356 int fd; 357 __u64 ns_id; 358 char buf; 359 360 close(sv[0]); 361 362 /* Create new user namespace */ 363 if (setup_userns() < 0) { 364 close(sv[1]); 365 exit(1); 366 } 367 368 /* Get user namespace ID */ 369 fd = open("/proc/self/ns/user", O_RDONLY); 370 if (fd < 0) { 371 close(sv[1]); 372 exit(1); 373 } 374 375 if (ioctl(fd, NS_GET_ID, &ns_id) < 0) { 376 close(fd); 377 close(sv[1]); 378 exit(1); 379 } 380 close(fd); 381 382 /* Send ID to parent */ 383 if (write(sv[1], &ns_id, sizeof(ns_id)) != sizeof(ns_id)) { 384 close(sv[1]); 385 exit(1); 386 } 387 388 /* Create some namespaces owned by this user namespace */ 389 unshare(CLONE_NEWNET); 390 unshare(CLONE_NEWUTS); 391 392 /* Wait for parent signal */ 393 if (read(sv[1], &buf, 1) != 1) { 394 close(sv[1]); 395 exit(1); 396 } 397 close(sv[1]); 398 exit(0); 399 } 400 401 /* Parent */ 402 close(sv[1]); 403 bytes = read(sv[0], &user_ns_id, sizeof(user_ns_id)); 404 405 if (bytes != sizeof(user_ns_id)) { 406 close(sv[0]); 407 kill(pid, SIGKILL); 408 waitpid(pid, NULL, 0); 409 SKIP(return, "Failed to get user namespace ID from child"); 410 } 411 412 TH_LOG("Child created user namespace with ID %llu", (unsigned long long)user_ns_id); 413 414 /* List namespaces owned by this user namespace */ 415 req.user_ns_id = user_ns_id; 416 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 417 418 if (ret < 0) { 419 TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno); 420 close(sv[0]); 421 kill(pid, SIGKILL); 422 waitpid(pid, NULL, 0); 423 if (errno == ENOSYS) { 424 SKIP(return, "listns() not supported"); 425 } 426 ASSERT_GE(ret, 0); 427 } 428 429 TH_LOG("Found %zd namespaces owned by user namespace %llu", ret, 430 (unsigned long long)user_ns_id); 431 432 /* Should find at least the network and UTS namespaces we created */ 433 if (ret > 0) { 434 for (ssize_t i = 0; i < ret && i < 10; i++) 435 TH_LOG(" [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]); 436 } 437 438 /* Signal child to exit */ 439 if (write(sv[0], "X", 1) != 1) { 440 close(sv[0]); 441 kill(pid, SIGKILL); 442 waitpid(pid, NULL, 0); 443 ASSERT_TRUE(false); 444 } 445 close(sv[0]); 446 waitpid(pid, &status, 0); 447 } 448 449 /* 450 * Test listns() with multiple namespace types filter. 451 */ 452 TEST(listns_multiple_types) 453 { 454 struct ns_id_req req = { 455 .size = sizeof(req), 456 .spare = 0, 457 .ns_id = 0, 458 .ns_type = CLONE_NEWNET | CLONE_NEWUTS, /* Network and UTS */ 459 .spare2 = 0, 460 .user_ns_id = 0, 461 }; 462 __u64 ns_ids[100]; 463 ssize_t ret; 464 465 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 466 if (ret < 0) { 467 if (errno == ENOSYS) 468 SKIP(return, "listns() not supported"); 469 TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno); 470 ASSERT_TRUE(false); 471 } 472 ASSERT_GE(ret, 0); 473 474 TH_LOG("Found %zd active network/UTS namespaces", ret); 475 476 for (ssize_t i = 0; i < ret; i++) 477 TH_LOG(" [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]); 478 } 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 630 /* 631 * Test error cases for listns(). 632 */ 633 TEST(listns_error_cases) 634 { 635 struct ns_id_req req = { 636 .size = sizeof(req), 637 .spare = 0, 638 .ns_id = 0, 639 .ns_type = 0, 640 .spare2 = 0, 641 .user_ns_id = 0, 642 }; 643 __u64 ns_ids[10]; 644 int ret; 645 646 /* Test with invalid flags */ 647 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0xFFFF); 648 if (errno == ENOSYS) { 649 /* listns() not supported, skip this check */ 650 } else { 651 ASSERT_LT(ret, 0); 652 ASSERT_EQ(errno, EINVAL); 653 } 654 655 /* Test with NULL ns_ids array */ 656 ret = sys_listns(&req, NULL, 10, 0); 657 ASSERT_LT(ret, 0); 658 659 /* Test with invalid spare field */ 660 req.spare = 1; 661 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 662 if (errno == ENOSYS) { 663 /* listns() not supported, skip this check */ 664 } else { 665 ASSERT_LT(ret, 0); 666 ASSERT_EQ(errno, EINVAL); 667 } 668 req.spare = 0; 669 670 /* Test with huge nr_ns_ids */ 671 ret = sys_listns(&req, ns_ids, 2000000, 0); 672 if (errno == ENOSYS) { 673 /* listns() not supported, skip this check */ 674 } else { 675 ASSERT_LT(ret, 0); 676 } 677 } 678 679 TEST_HARNESS_MAIN 680