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 */
TEST(listns_basic_unified)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 */
TEST(listns_filter_by_type)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 */
TEST(listns_pagination)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 */
TEST(listns_current_user)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 */
TEST(listns_only_active)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 */
TEST(listns_specific_userns)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 */
TEST(listns_multiple_types)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 */
TEST(listns_hierarchical_visibility)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 */
TEST(listns_error_cases)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