xref: /linux/tools/testing/selftests/namespaces/cred_change_test.c (revision d18cf3f9a4ab67869d468672399de6eac0f8f571)
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 <sys/capability.h>
11 #include <sys/ioctl.h>
12 #include <sys/stat.h>
13 #include <sys/syscall.h>
14 #include <sys/types.h>
15 #include <sys/wait.h>
16 #include <unistd.h>
17 #include <linux/nsfs.h>
18 #include "../kselftest_harness.h"
19 #include "../filesystems/utils.h"
20 #include "wrappers.h"
21 
22 /*
23  * Test credential changes and their impact on namespace active references.
24  */
25 
26 /*
27  * Test setuid() in a user namespace properly swaps active references.
28  * Create a user namespace with multiple UIDs mapped, then setuid() between them.
29  * Verify that the user namespace remains active throughout.
30  */
31 TEST(setuid_preserves_active_refs)
32 {
33 	pid_t pid;
34 	int status;
35 	__u64 userns_id;
36 	struct ns_id_req req = {
37 		.size = sizeof(req),
38 		.spare = 0,
39 		.ns_id = 0,
40 		.ns_type = CLONE_NEWUSER,
41 		.spare2 = 0,
42 		.user_ns_id = 0,
43 	};
44 	__u64 ns_ids[256];
45 	ssize_t ret;
46 	int i;
47 	bool found = false;
48 	int pipefd[2];
49 
50 	ASSERT_EQ(pipe(pipefd), 0);
51 
52 	pid = fork();
53 	ASSERT_GE(pid, 0);
54 
55 	if (pid == 0) {
56 		/* Child process */
57 		int fd, userns_fd;
58 		__u64 child_userns_id;
59 		uid_t orig_uid = getuid();
60 		int setuid_count;
61 
62 		close(pipefd[0]);
63 
64 		/* Create new user namespace with multiple UIDs mapped (0-9) */
65 		userns_fd = get_userns_fd(0, orig_uid, 10);
66 		if (userns_fd < 0) {
67 			close(pipefd[1]);
68 			exit(1);
69 		}
70 
71 		if (setns(userns_fd, CLONE_NEWUSER) < 0) {
72 			close(userns_fd);
73 			close(pipefd[1]);
74 			exit(1);
75 		}
76 		close(userns_fd);
77 
78 		/* Get user namespace ID */
79 		fd = open("/proc/self/ns/user", O_RDONLY);
80 		if (fd < 0) {
81 			close(pipefd[1]);
82 			exit(1);
83 		}
84 
85 		if (ioctl(fd, NS_GET_ID, &child_userns_id) < 0) {
86 			close(fd);
87 			close(pipefd[1]);
88 			exit(1);
89 		}
90 		close(fd);
91 
92 		/* Send namespace ID to parent */
93 		write(pipefd[1], &child_userns_id, sizeof(child_userns_id));
94 
95 		/*
96 		 * Perform multiple setuid() calls.
97 		 * Each setuid() triggers commit_creds() which should properly
98 		 * swap active references via switch_cred_namespaces().
99 		 */
100 		for (setuid_count = 0; setuid_count < 50; setuid_count++) {
101 			uid_t target_uid = (setuid_count % 10);
102 			if (setuid(target_uid) < 0) {
103 				if (errno != EPERM) {
104 					close(pipefd[1]);
105 					exit(1);
106 				}
107 			}
108 		}
109 
110 		close(pipefd[1]);
111 		exit(0);
112 	}
113 
114 	/* Parent process */
115 	close(pipefd[1]);
116 
117 	if (read(pipefd[0], &userns_id, sizeof(userns_id)) != sizeof(userns_id)) {
118 		close(pipefd[0]);
119 		kill(pid, SIGKILL);
120 		waitpid(pid, NULL, 0);
121 		SKIP(return, "Failed to get namespace ID from child");
122 	}
123 	close(pipefd[0]);
124 
125 	TH_LOG("Child user namespace ID: %llu", (unsigned long long)userns_id);
126 
127 	/* Verify namespace is active while child is running */
128 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
129 	if (ret < 0) {
130 		kill(pid, SIGKILL);
131 		waitpid(pid, NULL, 0);
132 		if (errno == ENOSYS)
133 			SKIP(return, "listns() not supported");
134 		ASSERT_GE(ret, 0);
135 	}
136 
137 	for (i = 0; i < ret; i++) {
138 		if (ns_ids[i] == userns_id) {
139 			found = true;
140 			break;
141 		}
142 	}
143 	ASSERT_TRUE(found);
144 
145 	waitpid(pid, &status, 0);
146 	ASSERT_TRUE(WIFEXITED(status));
147 	ASSERT_EQ(WEXITSTATUS(status), 0);
148 
149 	/* Verify namespace becomes inactive after child exits */
150 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
151 	ASSERT_GE(ret, 0);
152 
153 	found = false;
154 	for (i = 0; i < ret; i++) {
155 		if (ns_ids[i] == userns_id) {
156 			found = true;
157 			break;
158 		}
159 	}
160 
161 	ASSERT_FALSE(found);
162 	TH_LOG("setuid() correctly preserved active references (no leak)");
163 }
164 
165 /*
166  * Test setgid() in a user namespace properly handles active references.
167  */
168 TEST(setgid_preserves_active_refs)
169 {
170 	pid_t pid;
171 	int status;
172 	__u64 userns_id;
173 	struct ns_id_req req = {
174 		.size = sizeof(req),
175 		.spare = 0,
176 		.ns_id = 0,
177 		.ns_type = CLONE_NEWUSER,
178 		.spare2 = 0,
179 		.user_ns_id = 0,
180 	};
181 	__u64 ns_ids[256];
182 	ssize_t ret;
183 	int i;
184 	bool found = false;
185 	int pipefd[2];
186 
187 	ASSERT_EQ(pipe(pipefd), 0);
188 
189 	pid = fork();
190 	ASSERT_GE(pid, 0);
191 
192 	if (pid == 0) {
193 		/* Child process */
194 		int fd, userns_fd;
195 		__u64 child_userns_id;
196 		uid_t orig_uid = getuid();
197 		int setgid_count;
198 
199 		close(pipefd[0]);
200 
201 		/* Create new user namespace with multiple GIDs mapped */
202 		userns_fd = get_userns_fd(0, orig_uid, 10);
203 		if (userns_fd < 0) {
204 			close(pipefd[1]);
205 			exit(1);
206 		}
207 
208 		if (setns(userns_fd, CLONE_NEWUSER) < 0) {
209 			close(userns_fd);
210 			close(pipefd[1]);
211 			exit(1);
212 		}
213 		close(userns_fd);
214 
215 		/* Get user namespace ID */
216 		fd = open("/proc/self/ns/user", O_RDONLY);
217 		if (fd < 0) {
218 			close(pipefd[1]);
219 			exit(1);
220 		}
221 
222 		if (ioctl(fd, NS_GET_ID, &child_userns_id) < 0) {
223 			close(fd);
224 			close(pipefd[1]);
225 			exit(1);
226 		}
227 		close(fd);
228 
229 		write(pipefd[1], &child_userns_id, sizeof(child_userns_id));
230 
231 		/* Perform multiple setgid() calls */
232 		for (setgid_count = 0; setgid_count < 50; setgid_count++) {
233 			gid_t target_gid = (setgid_count % 10);
234 			if (setgid(target_gid) < 0) {
235 				if (errno != EPERM) {
236 					close(pipefd[1]);
237 					exit(1);
238 				}
239 			}
240 		}
241 
242 		close(pipefd[1]);
243 		exit(0);
244 	}
245 
246 	/* Parent process */
247 	close(pipefd[1]);
248 
249 	if (read(pipefd[0], &userns_id, sizeof(userns_id)) != sizeof(userns_id)) {
250 		close(pipefd[0]);
251 		kill(pid, SIGKILL);
252 		waitpid(pid, NULL, 0);
253 		SKIP(return, "Failed to get namespace ID from child");
254 	}
255 	close(pipefd[0]);
256 
257 	waitpid(pid, &status, 0);
258 	ASSERT_TRUE(WIFEXITED(status));
259 	ASSERT_EQ(WEXITSTATUS(status), 0);
260 
261 	/* Verify namespace becomes inactive */
262 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
263 	if (ret < 0) {
264 		if (errno == ENOSYS)
265 			SKIP(return, "listns() not supported");
266 		ASSERT_GE(ret, 0);
267 	}
268 
269 	for (i = 0; i < ret; i++) {
270 		if (ns_ids[i] == userns_id) {
271 			found = true;
272 			break;
273 		}
274 	}
275 
276 	ASSERT_FALSE(found);
277 	TH_LOG("setgid() correctly preserved active references (no leak)");
278 }
279 
280 /*
281  * Test setresuid() which changes real, effective, and saved UIDs.
282  * This should properly swap active references via commit_creds().
283  */
284 TEST(setresuid_preserves_active_refs)
285 {
286 	pid_t pid;
287 	int status;
288 	__u64 userns_id;
289 	struct ns_id_req req = {
290 		.size = sizeof(req),
291 		.spare = 0,
292 		.ns_id = 0,
293 		.ns_type = CLONE_NEWUSER,
294 		.spare2 = 0,
295 		.user_ns_id = 0,
296 	};
297 	__u64 ns_ids[256];
298 	ssize_t ret;
299 	int i;
300 	bool found = false;
301 	int pipefd[2];
302 
303 	ASSERT_EQ(pipe(pipefd), 0);
304 
305 	pid = fork();
306 	ASSERT_GE(pid, 0);
307 
308 	if (pid == 0) {
309 		/* Child process */
310 		int fd, userns_fd;
311 		__u64 child_userns_id;
312 		uid_t orig_uid = getuid();
313 		int setres_count;
314 
315 		close(pipefd[0]);
316 
317 		/* Create new user namespace */
318 		userns_fd = get_userns_fd(0, orig_uid, 10);
319 		if (userns_fd < 0) {
320 			close(pipefd[1]);
321 			exit(1);
322 		}
323 
324 		if (setns(userns_fd, CLONE_NEWUSER) < 0) {
325 			close(userns_fd);
326 			close(pipefd[1]);
327 			exit(1);
328 		}
329 		close(userns_fd);
330 
331 		/* Get user namespace ID */
332 		fd = open("/proc/self/ns/user", O_RDONLY);
333 		if (fd < 0) {
334 			close(pipefd[1]);
335 			exit(1);
336 		}
337 
338 		if (ioctl(fd, NS_GET_ID, &child_userns_id) < 0) {
339 			close(fd);
340 			close(pipefd[1]);
341 			exit(1);
342 		}
343 		close(fd);
344 
345 		write(pipefd[1], &child_userns_id, sizeof(child_userns_id));
346 
347 		/* Perform multiple setresuid() calls */
348 		for (setres_count = 0; setres_count < 30; setres_count++) {
349 			uid_t uid1 = (setres_count % 5);
350 			uid_t uid2 = ((setres_count + 1) % 5);
351 			uid_t uid3 = ((setres_count + 2) % 5);
352 
353 			if (setresuid(uid1, uid2, uid3) < 0) {
354 				if (errno != EPERM) {
355 					close(pipefd[1]);
356 					exit(1);
357 				}
358 			}
359 		}
360 
361 		close(pipefd[1]);
362 		exit(0);
363 	}
364 
365 	/* Parent process */
366 	close(pipefd[1]);
367 
368 	if (read(pipefd[0], &userns_id, sizeof(userns_id)) != sizeof(userns_id)) {
369 		close(pipefd[0]);
370 		kill(pid, SIGKILL);
371 		waitpid(pid, NULL, 0);
372 		SKIP(return, "Failed to get namespace ID from child");
373 	}
374 	close(pipefd[0]);
375 
376 	waitpid(pid, &status, 0);
377 	ASSERT_TRUE(WIFEXITED(status));
378 	ASSERT_EQ(WEXITSTATUS(status), 0);
379 
380 	/* Verify namespace becomes inactive */
381 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
382 	if (ret < 0) {
383 		if (errno == ENOSYS)
384 			SKIP(return, "listns() not supported");
385 		ASSERT_GE(ret, 0);
386 	}
387 
388 	for (i = 0; i < ret; i++) {
389 		if (ns_ids[i] == userns_id) {
390 			found = true;
391 			break;
392 		}
393 	}
394 
395 	ASSERT_FALSE(found);
396 	TH_LOG("setresuid() correctly preserved active references (no leak)");
397 }
398 
399 /*
400  * Test credential changes across multiple user namespaces.
401  * Create nested user namespaces and verify active reference tracking.
402  */
403 TEST(cred_change_nested_userns)
404 {
405 	pid_t pid;
406 	int status;
407 	__u64 parent_userns_id, child_userns_id;
408 	struct ns_id_req req = {
409 		.size = sizeof(req),
410 		.spare = 0,
411 		.ns_id = 0,
412 		.ns_type = CLONE_NEWUSER,
413 		.spare2 = 0,
414 		.user_ns_id = 0,
415 	};
416 	__u64 ns_ids[256];
417 	ssize_t ret;
418 	int i;
419 	bool found_parent = false, found_child = false;
420 	int pipefd[2];
421 
422 	ASSERT_EQ(pipe(pipefd), 0);
423 
424 	pid = fork();
425 	ASSERT_GE(pid, 0);
426 
427 	if (pid == 0) {
428 		/* Child process */
429 		int fd, userns_fd;
430 		__u64 parent_id, child_id;
431 		uid_t orig_uid = getuid();
432 
433 		close(pipefd[0]);
434 
435 		/* Create first user namespace */
436 		userns_fd = get_userns_fd(0, orig_uid, 1);
437 		if (userns_fd < 0) {
438 			close(pipefd[1]);
439 			exit(1);
440 		}
441 
442 		if (setns(userns_fd, CLONE_NEWUSER) < 0) {
443 			close(userns_fd);
444 			close(pipefd[1]);
445 			exit(1);
446 		}
447 		close(userns_fd);
448 
449 		/* Get first namespace ID */
450 		fd = open("/proc/self/ns/user", O_RDONLY);
451 		if (fd < 0) {
452 			close(pipefd[1]);
453 			exit(1);
454 		}
455 
456 		if (ioctl(fd, NS_GET_ID, &parent_id) < 0) {
457 			close(fd);
458 			close(pipefd[1]);
459 			exit(1);
460 		}
461 		close(fd);
462 
463 		/* Create nested user namespace */
464 		userns_fd = get_userns_fd(0, 0, 1);
465 		if (userns_fd < 0) {
466 			close(pipefd[1]);
467 			exit(1);
468 		}
469 
470 		if (setns(userns_fd, CLONE_NEWUSER) < 0) {
471 			close(userns_fd);
472 			close(pipefd[1]);
473 			exit(1);
474 		}
475 		close(userns_fd);
476 
477 		/* Get nested namespace ID */
478 		fd = open("/proc/self/ns/user", O_RDONLY);
479 		if (fd < 0) {
480 			close(pipefd[1]);
481 			exit(1);
482 		}
483 
484 		if (ioctl(fd, NS_GET_ID, &child_id) < 0) {
485 			close(fd);
486 			close(pipefd[1]);
487 			exit(1);
488 		}
489 		close(fd);
490 
491 		/* Send both IDs to parent */
492 		write(pipefd[1], &parent_id, sizeof(parent_id));
493 		write(pipefd[1], &child_id, sizeof(child_id));
494 
495 		/* Perform some credential changes in nested namespace */
496 		setuid(0);
497 		setgid(0);
498 
499 		close(pipefd[1]);
500 		exit(0);
501 	}
502 
503 	/* Parent process */
504 	close(pipefd[1]);
505 
506 	/* Read both namespace IDs */
507 	if (read(pipefd[0], &parent_userns_id, sizeof(parent_userns_id)) != sizeof(parent_userns_id)) {
508 		close(pipefd[0]);
509 		kill(pid, SIGKILL);
510 		waitpid(pid, NULL, 0);
511 		SKIP(return, "Failed to get parent namespace ID");
512 	}
513 
514 	if (read(pipefd[0], &child_userns_id, sizeof(child_userns_id)) != sizeof(child_userns_id)) {
515 		close(pipefd[0]);
516 		kill(pid, SIGKILL);
517 		waitpid(pid, NULL, 0);
518 		SKIP(return, "Failed to get child namespace ID");
519 	}
520 	close(pipefd[0]);
521 
522 	TH_LOG("Parent userns: %llu, Child userns: %llu",
523 	       (unsigned long long)parent_userns_id,
524 	       (unsigned long long)child_userns_id);
525 
526 	/* Verify both namespaces are active */
527 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
528 	if (ret < 0) {
529 		kill(pid, SIGKILL);
530 		waitpid(pid, NULL, 0);
531 		if (errno == ENOSYS)
532 			SKIP(return, "listns() not supported");
533 		ASSERT_GE(ret, 0);
534 	}
535 
536 	for (i = 0; i < ret; i++) {
537 		if (ns_ids[i] == parent_userns_id)
538 			found_parent = true;
539 		if (ns_ids[i] == child_userns_id)
540 			found_child = true;
541 	}
542 
543 	ASSERT_TRUE(found_parent);
544 	ASSERT_TRUE(found_child);
545 
546 	/* Wait for child */
547 	waitpid(pid, &status, 0);
548 	ASSERT_TRUE(WIFEXITED(status));
549 	ASSERT_EQ(WEXITSTATUS(status), 0);
550 
551 	/* Verify both namespaces become inactive */
552 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
553 	ASSERT_GE(ret, 0);
554 
555 	found_parent = false;
556 	found_child = false;
557 	for (i = 0; i < ret; i++) {
558 		if (ns_ids[i] == parent_userns_id)
559 			found_parent = true;
560 		if (ns_ids[i] == child_userns_id)
561 			found_child = true;
562 	}
563 
564 	ASSERT_FALSE(found_parent);
565 	ASSERT_FALSE(found_child);
566 	TH_LOG("Nested user namespace credential changes preserved active refs (no leak)");
567 }
568 
569 /*
570  * Test rapid credential changes don't cause refcount imbalances.
571  * This stress-tests the switch_cred_namespaces() logic.
572  */
573 TEST(rapid_cred_changes_no_leak)
574 {
575 	pid_t pid;
576 	int status;
577 	__u64 userns_id;
578 	struct ns_id_req req = {
579 		.size = sizeof(req),
580 		.spare = 0,
581 		.ns_id = 0,
582 		.ns_type = CLONE_NEWUSER,
583 		.spare2 = 0,
584 		.user_ns_id = 0,
585 	};
586 	__u64 ns_ids[256];
587 	ssize_t ret;
588 	int i;
589 	bool found = false;
590 	int pipefd[2];
591 
592 	ASSERT_EQ(pipe(pipefd), 0);
593 
594 	pid = fork();
595 	ASSERT_GE(pid, 0);
596 
597 	if (pid == 0) {
598 		/* Child process */
599 		int fd, userns_fd;
600 		__u64 child_userns_id;
601 		uid_t orig_uid = getuid();
602 		int change_count;
603 
604 		close(pipefd[0]);
605 
606 		/* Create new user namespace with wider range of UIDs/GIDs */
607 		userns_fd = get_userns_fd(0, orig_uid, 100);
608 		if (userns_fd < 0) {
609 			close(pipefd[1]);
610 			exit(1);
611 		}
612 
613 		if (setns(userns_fd, CLONE_NEWUSER) < 0) {
614 			close(userns_fd);
615 			close(pipefd[1]);
616 			exit(1);
617 		}
618 		close(userns_fd);
619 
620 		/* Get user namespace ID */
621 		fd = open("/proc/self/ns/user", O_RDONLY);
622 		if (fd < 0) {
623 			close(pipefd[1]);
624 			exit(1);
625 		}
626 
627 		if (ioctl(fd, NS_GET_ID, &child_userns_id) < 0) {
628 			close(fd);
629 			close(pipefd[1]);
630 			exit(1);
631 		}
632 		close(fd);
633 
634 		write(pipefd[1], &child_userns_id, sizeof(child_userns_id));
635 
636 		/*
637 		 * Perform many rapid credential changes.
638 		 * Mix setuid, setgid, setreuid, setregid, setresuid, setresgid.
639 		 */
640 		for (change_count = 0; change_count < 200; change_count++) {
641 			switch (change_count % 6) {
642 			case 0:
643 				setuid(change_count % 50);
644 				break;
645 			case 1:
646 				setgid(change_count % 50);
647 				break;
648 			case 2:
649 				setreuid(change_count % 50, (change_count + 1) % 50);
650 				break;
651 			case 3:
652 				setregid(change_count % 50, (change_count + 1) % 50);
653 				break;
654 			case 4:
655 				setresuid(change_count % 50, (change_count + 1) % 50, (change_count + 2) % 50);
656 				break;
657 			case 5:
658 				setresgid(change_count % 50, (change_count + 1) % 50, (change_count + 2) % 50);
659 				break;
660 			}
661 		}
662 
663 		close(pipefd[1]);
664 		exit(0);
665 	}
666 
667 	/* Parent process */
668 	close(pipefd[1]);
669 
670 	if (read(pipefd[0], &userns_id, sizeof(userns_id)) != sizeof(userns_id)) {
671 		close(pipefd[0]);
672 		kill(pid, SIGKILL);
673 		waitpid(pid, NULL, 0);
674 		SKIP(return, "Failed to get namespace ID from child");
675 	}
676 	close(pipefd[0]);
677 
678 	TH_LOG("Testing with user namespace ID: %llu", (unsigned long long)userns_id);
679 
680 	waitpid(pid, &status, 0);
681 	ASSERT_TRUE(WIFEXITED(status));
682 	ASSERT_EQ(WEXITSTATUS(status), 0);
683 
684 	/* Verify namespace becomes inactive (no leaked active refs) */
685 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
686 	if (ret < 0) {
687 		if (errno == ENOSYS)
688 			SKIP(return, "listns() not supported");
689 		ASSERT_GE(ret, 0);
690 	}
691 
692 	for (i = 0; i < ret; i++) {
693 		if (ns_ids[i] == userns_id) {
694 			found = true;
695 			break;
696 		}
697 	}
698 
699 	ASSERT_FALSE(found);
700 	TH_LOG("200 rapid credential changes completed with no active ref leak");
701 }
702 
703 /*
704  * Test setfsuid/setfsgid which change filesystem UID/GID.
705  * These also trigger credential changes but may have different code paths.
706  */
707 TEST(setfsuid_preserves_active_refs)
708 {
709 	pid_t pid;
710 	int status;
711 	__u64 userns_id;
712 	struct ns_id_req req = {
713 		.size = sizeof(req),
714 		.spare = 0,
715 		.ns_id = 0,
716 		.ns_type = CLONE_NEWUSER,
717 		.spare2 = 0,
718 		.user_ns_id = 0,
719 	};
720 	__u64 ns_ids[256];
721 	ssize_t ret;
722 	int i;
723 	bool found = false;
724 	int pipefd[2];
725 
726 	ASSERT_EQ(pipe(pipefd), 0);
727 
728 	pid = fork();
729 	ASSERT_GE(pid, 0);
730 
731 	if (pid == 0) {
732 		/* Child process */
733 		int fd, userns_fd;
734 		__u64 child_userns_id;
735 		uid_t orig_uid = getuid();
736 		int change_count;
737 
738 		close(pipefd[0]);
739 
740 		/* Create new user namespace */
741 		userns_fd = get_userns_fd(0, orig_uid, 10);
742 		if (userns_fd < 0) {
743 			close(pipefd[1]);
744 			exit(1);
745 		}
746 
747 		if (setns(userns_fd, CLONE_NEWUSER) < 0) {
748 			close(userns_fd);
749 			close(pipefd[1]);
750 			exit(1);
751 		}
752 		close(userns_fd);
753 
754 		/* Get user namespace ID */
755 		fd = open("/proc/self/ns/user", O_RDONLY);
756 		if (fd < 0) {
757 			close(pipefd[1]);
758 			exit(1);
759 		}
760 
761 		if (ioctl(fd, NS_GET_ID, &child_userns_id) < 0) {
762 			close(fd);
763 			close(pipefd[1]);
764 			exit(1);
765 		}
766 		close(fd);
767 
768 		write(pipefd[1], &child_userns_id, sizeof(child_userns_id));
769 
770 		/* Perform multiple setfsuid/setfsgid calls */
771 		for (change_count = 0; change_count < 50; change_count++) {
772 			setfsuid(change_count % 10);
773 			setfsgid(change_count % 10);
774 		}
775 
776 		close(pipefd[1]);
777 		exit(0);
778 	}
779 
780 	/* Parent process */
781 	close(pipefd[1]);
782 
783 	if (read(pipefd[0], &userns_id, sizeof(userns_id)) != sizeof(userns_id)) {
784 		close(pipefd[0]);
785 		kill(pid, SIGKILL);
786 		waitpid(pid, NULL, 0);
787 		SKIP(return, "Failed to get namespace ID from child");
788 	}
789 	close(pipefd[0]);
790 
791 	waitpid(pid, &status, 0);
792 	ASSERT_TRUE(WIFEXITED(status));
793 	ASSERT_EQ(WEXITSTATUS(status), 0);
794 
795 	/* Verify namespace becomes inactive */
796 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
797 	if (ret < 0) {
798 		if (errno == ENOSYS)
799 			SKIP(return, "listns() not supported");
800 		ASSERT_GE(ret, 0);
801 	}
802 
803 	for (i = 0; i < ret; i++) {
804 		if (ns_ids[i] == userns_id) {
805 			found = true;
806 			break;
807 		}
808 	}
809 
810 	ASSERT_FALSE(found);
811 	TH_LOG("setfsuid/setfsgid correctly preserved active references (no leak)");
812 }
813 
814 TEST_HARNESS_MAIN
815