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 */
TEST(pagination_with_type_filter)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