|
12 | 12 | #include <sys/stat.h> |
13 | 13 | #include <sys/types.h> |
14 | 14 | #include <sys/wait.h> |
| 15 | +#include <sys/syscall.h> |
15 | 16 | #include <unistd.h> |
| 17 | +#include <pthread.h> |
16 | 18 | #include "../kselftest_harness.h" |
17 | 19 | #include "../filesystems/utils.h" |
| 20 | +#include "wrappers.h" |
18 | 21 |
|
19 | 22 | #ifndef FD_NSFS_ROOT |
20 | 23 | #define FD_NSFS_ROOT -10003 /* Root of the nsfs filesystem */ |
@@ -2113,4 +2116,139 @@ TEST(ns_mixed_types_same_owner) |
2113 | 2116 | ASSERT_LT(u_fd, 0); |
2114 | 2117 | } |
2115 | 2118 |
|
| 2119 | +/* Thread test helpers and structures */ |
| 2120 | +struct thread_ns_info { |
| 2121 | + __u64 ns_id; |
| 2122 | + int pipefd; |
| 2123 | + int syncfd_read; |
| 2124 | + int syncfd_write; |
| 2125 | + int exit_code; |
| 2126 | +}; |
| 2127 | + |
| 2128 | +static void *thread_create_namespace(void *arg) |
| 2129 | +{ |
| 2130 | + struct thread_ns_info *info = (struct thread_ns_info *)arg; |
| 2131 | + int ret; |
| 2132 | + |
| 2133 | + /* Create new network namespace */ |
| 2134 | + ret = unshare(CLONE_NEWNET); |
| 2135 | + if (ret < 0) { |
| 2136 | + info->exit_code = 1; |
| 2137 | + return NULL; |
| 2138 | + } |
| 2139 | + |
| 2140 | + /* Get namespace ID */ |
| 2141 | + int fd = open("/proc/thread-self/ns/net", O_RDONLY); |
| 2142 | + if (fd < 0) { |
| 2143 | + info->exit_code = 2; |
| 2144 | + return NULL; |
| 2145 | + } |
| 2146 | + |
| 2147 | + ret = ioctl(fd, NS_GET_ID, &info->ns_id); |
| 2148 | + close(fd); |
| 2149 | + if (ret < 0) { |
| 2150 | + info->exit_code = 3; |
| 2151 | + return NULL; |
| 2152 | + } |
| 2153 | + |
| 2154 | + /* Send namespace ID to main thread */ |
| 2155 | + if (write(info->pipefd, &info->ns_id, sizeof(info->ns_id)) != sizeof(info->ns_id)) { |
| 2156 | + info->exit_code = 4; |
| 2157 | + return NULL; |
| 2158 | + } |
| 2159 | + |
| 2160 | + /* Wait for signal to exit */ |
| 2161 | + char sync_byte; |
| 2162 | + if (read(info->syncfd_read, &sync_byte, 1) != 1) { |
| 2163 | + info->exit_code = 5; |
| 2164 | + return NULL; |
| 2165 | + } |
| 2166 | + |
| 2167 | + info->exit_code = 0; |
| 2168 | + return NULL; |
| 2169 | +} |
| 2170 | + |
| 2171 | +/* |
| 2172 | + * Test that namespace becomes inactive after thread exits. |
| 2173 | + * This verifies active reference counting works with threads, not just processes. |
| 2174 | + */ |
| 2175 | +TEST(thread_ns_inactive_after_exit) |
| 2176 | +{ |
| 2177 | + pthread_t thread; |
| 2178 | + struct thread_ns_info info; |
| 2179 | + struct file_handle *handle; |
| 2180 | + int pipefd[2]; |
| 2181 | + int syncpipe[2]; |
| 2182 | + int ret; |
| 2183 | + char sync_byte; |
| 2184 | + char buf[sizeof(*handle) + MAX_HANDLE_SZ]; |
| 2185 | + |
| 2186 | + ASSERT_EQ(pipe(pipefd), 0); |
| 2187 | + ASSERT_EQ(pipe(syncpipe), 0); |
| 2188 | + |
| 2189 | + info.pipefd = pipefd[1]; |
| 2190 | + info.syncfd_read = syncpipe[0]; |
| 2191 | + info.syncfd_write = -1; |
| 2192 | + info.exit_code = -1; |
| 2193 | + |
| 2194 | + /* Create thread that will create a namespace */ |
| 2195 | + ret = pthread_create(&thread, NULL, thread_create_namespace, &info); |
| 2196 | + ASSERT_EQ(ret, 0); |
| 2197 | + |
| 2198 | + /* Read namespace ID from thread */ |
| 2199 | + __u64 ns_id; |
| 2200 | + ret = read(pipefd[0], &ns_id, sizeof(ns_id)); |
| 2201 | + if (ret != sizeof(ns_id)) { |
| 2202 | + sync_byte = 'X'; |
| 2203 | + write(syncpipe[1], &sync_byte, 1); |
| 2204 | + pthread_join(thread, NULL); |
| 2205 | + close(pipefd[0]); |
| 2206 | + close(pipefd[1]); |
| 2207 | + close(syncpipe[0]); |
| 2208 | + close(syncpipe[1]); |
| 2209 | + SKIP(return, "Failed to read namespace ID from thread"); |
| 2210 | + } |
| 2211 | + |
| 2212 | + TH_LOG("Thread created namespace with ID %llu", (unsigned long long)ns_id); |
| 2213 | + |
| 2214 | + /* Construct file handle */ |
| 2215 | + handle = (struct file_handle *)buf; |
| 2216 | + handle->handle_bytes = sizeof(struct nsfs_file_handle); |
| 2217 | + handle->handle_type = FILEID_NSFS; |
| 2218 | + struct nsfs_file_handle *fh = (struct nsfs_file_handle *)handle->f_handle; |
| 2219 | + fh->ns_id = ns_id; |
| 2220 | + fh->ns_type = 0; |
| 2221 | + fh->ns_inum = 0; |
| 2222 | + |
| 2223 | + /* Namespace should be active while thread is alive */ |
| 2224 | + TH_LOG("Attempting to open namespace while thread is alive (should succeed)"); |
| 2225 | + int nsfd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); |
| 2226 | + ASSERT_GE(nsfd, 0); |
| 2227 | + close(nsfd); |
| 2228 | + |
| 2229 | + /* Signal thread to exit */ |
| 2230 | + TH_LOG("Signaling thread to exit"); |
| 2231 | + sync_byte = 'X'; |
| 2232 | + ASSERT_EQ(write(syncpipe[1], &sync_byte, 1), 1); |
| 2233 | + close(syncpipe[1]); |
| 2234 | + |
| 2235 | + /* Wait for thread to exit */ |
| 2236 | + ASSERT_EQ(pthread_join(thread, NULL), 0); |
| 2237 | + close(pipefd[0]); |
| 2238 | + close(pipefd[1]); |
| 2239 | + close(syncpipe[0]); |
| 2240 | + |
| 2241 | + if (info.exit_code != 0) |
| 2242 | + SKIP(return, "Thread failed to create namespace"); |
| 2243 | + |
| 2244 | + TH_LOG("Thread exited, namespace should be inactive"); |
| 2245 | + |
| 2246 | + /* Namespace should now be inactive */ |
| 2247 | + nsfd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); |
| 2248 | + ASSERT_LT(nsfd, 0); |
| 2249 | + /* Should fail with ENOENT (inactive) or ESTALE (gone) */ |
| 2250 | + TH_LOG("Namespace inactive as expected: %s (errno=%d)", strerror(errno), errno); |
| 2251 | + ASSERT_TRUE(errno == ENOENT || errno == ESTALE); |
| 2252 | +} |
| 2253 | + |
2116 | 2254 | TEST_HARNESS_MAIN |
0 commit comments