xref: /linux/tools/testing/selftests/namespaces/listns_pagination_bug.c (revision 7fc2cd2e4b398c57c9cf961cfea05eadbf34c05c)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <errno.h>
4 #include <sched.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <sys/socket.h>
8 #include <sys/wait.h>
9 #include <unistd.h>
10 #include "../kselftest_harness.h"
11 #include "../filesystems/utils.h"
12 #include "wrappers.h"
13 
14 /*
15  * Minimal test case to reproduce KASAN out-of-bounds in listns pagination.
16  *
17  * The bug occurs when:
18  * 1. Filtering by a specific namespace type (e.g., CLONE_NEWUSER)
19  * 2. Using pagination (req.ns_id != 0)
20  * 3. The lookup_ns_id_at() call in do_listns() passes ns_type=0 instead of
21  *    the filtered type, causing it to search the unified tree and potentially
22  *    return a namespace of the wrong type.
23  */
24 TEST(pagination_with_type_filter)
25 {
26 	struct ns_id_req req = {
27 		.size = sizeof(req),
28 		.spare = 0,
29 		.ns_id = 0,
30 		.ns_type = CLONE_NEWUSER,  /* Filter by user namespace */
31 		.spare2 = 0,
32 		.user_ns_id = 0,
33 	};
34 	pid_t pids[10];
35 	int num_children = 10;
36 	int i;
37 	int sv[2];
38 	__u64 first_batch[3];
39 	ssize_t ret;
40 
41 	ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), 0);
42 
43 	/* Create children with user namespaces */
44 	for (i = 0; i < num_children; i++) {
45 		pids[i] = fork();
46 		ASSERT_GE(pids[i], 0);
47 
48 		if (pids[i] == 0) {
49 			char c;
50 			close(sv[0]);
51 
52 			if (setup_userns() < 0) {
53 				close(sv[1]);
54 				exit(1);
55 			}
56 
57 			/* Signal parent we're ready */
58 			if (write(sv[1], &c, 1) != 1) {
59 				close(sv[1]);
60 				exit(1);
61 			}
62 
63 			/* Wait for parent signal to exit */
64 			if (read(sv[1], &c, 1) != 1) {
65 				close(sv[1]);
66 				exit(1);
67 			}
68 
69 			close(sv[1]);
70 			exit(0);
71 		}
72 	}
73 
74 	close(sv[1]);
75 
76 	/* Wait for all children to signal ready */
77 	for (i = 0; i < num_children; i++) {
78 		char c;
79 		if (read(sv[0], &c, 1) != 1) {
80 			close(sv[0]);
81 			for (int j = 0; j < num_children; j++)
82 				kill(pids[j], SIGKILL);
83 			for (int j = 0; j < num_children; j++)
84 				waitpid(pids[j], NULL, 0);
85 			ASSERT_TRUE(false);
86 		}
87 	}
88 
89 	/* First batch - this should work */
90 	ret = sys_listns(&req, first_batch, 3, 0);
91 	if (ret < 0) {
92 		if (errno == ENOSYS) {
93 			close(sv[0]);
94 			for (i = 0; i < num_children; i++)
95 				kill(pids[i], SIGKILL);
96 			for (i = 0; i < num_children; i++)
97 				waitpid(pids[i], NULL, 0);
98 			SKIP(return, "listns() not supported");
99 		}
100 		ASSERT_GE(ret, 0);
101 	}
102 
103 	TH_LOG("First batch returned %zd entries", ret);
104 
105 	if (ret == 3) {
106 		__u64 second_batch[3];
107 
108 		/* Second batch - pagination triggers the bug */
109 		req.ns_id = first_batch[2];  /* Continue from last ID */
110 		ret = sys_listns(&req, second_batch, 3, 0);
111 
112 		TH_LOG("Second batch returned %zd entries", ret);
113 		ASSERT_GE(ret, 0);
114 	}
115 
116 	/* Signal all children to exit */
117 	for (i = 0; i < num_children; i++) {
118 		char c = 'X';
119 		if (write(sv[0], &c, 1) != 1) {
120 			close(sv[0]);
121 			for (int j = i; j < num_children; j++)
122 				kill(pids[j], SIGKILL);
123 			for (int j = 0; j < num_children; j++)
124 				waitpid(pids[j], NULL, 0);
125 			ASSERT_TRUE(false);
126 		}
127 	}
128 
129 	close(sv[0]);
130 
131 	/* Cleanup */
132 	for (i = 0; i < num_children; i++) {
133 		int status;
134 		waitpid(pids[i], &status, 0);
135 	}
136 }
137 
138 TEST_HARNESS_MAIN
139