]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
selftests/namespace: first threaded active reference count test
authorChristian Brauner <brauner@kernel.org>
Wed, 29 Oct 2025 12:21:20 +0000 (13:21 +0100)
committerChristian Brauner <brauner@kernel.org>
Mon, 3 Nov 2025 16:41:24 +0000 (17:41 +0100)
Test that namespace becomes inactive after thread exits. This verifies
active reference counting works with threads, not just processes.

Link: https://patch.msgid.link/20251029-work-namespace-nstree-listns-v4-67-2e6f823ebdc0@kernel.org
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
tools/testing/selftests/namespaces/ns_active_ref_test.c

index b7fa973a2572c9ce019f75685ba57d01dd47ac34..0c6c4869bb168273579a4ee4400f0a82207f749c 100644 (file)
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <sys/syscall.h>
 #include <unistd.h>
+#include <pthread.h>
 #include "../kselftest_harness.h"
 #include "../filesystems/utils.h"
+#include "wrappers.h"
 
 #ifndef FD_NSFS_ROOT
 #define FD_NSFS_ROOT -10003 /* Root of the nsfs filesystem */
@@ -2113,4 +2116,139 @@ TEST(ns_mixed_types_same_owner)
        ASSERT_LT(u_fd, 0);
 }
 
+/* Thread test helpers and structures */
+struct thread_ns_info {
+       __u64 ns_id;
+       int pipefd;
+       int syncfd_read;
+       int syncfd_write;
+       int exit_code;
+};
+
+static void *thread_create_namespace(void *arg)
+{
+       struct thread_ns_info *info = (struct thread_ns_info *)arg;
+       int ret;
+
+       /* Create new network namespace */
+       ret = unshare(CLONE_NEWNET);
+       if (ret < 0) {
+               info->exit_code = 1;
+               return NULL;
+       }
+
+       /* Get namespace ID */
+       int fd = open("/proc/thread-self/ns/net", O_RDONLY);
+       if (fd < 0) {
+               info->exit_code = 2;
+               return NULL;
+       }
+
+       ret = ioctl(fd, NS_GET_ID, &info->ns_id);
+       close(fd);
+       if (ret < 0) {
+               info->exit_code = 3;
+               return NULL;
+       }
+
+       /* Send namespace ID to main thread */
+       if (write(info->pipefd, &info->ns_id, sizeof(info->ns_id)) != sizeof(info->ns_id)) {
+               info->exit_code = 4;
+               return NULL;
+       }
+
+       /* Wait for signal to exit */
+       char sync_byte;
+       if (read(info->syncfd_read, &sync_byte, 1) != 1) {
+               info->exit_code = 5;
+               return NULL;
+       }
+
+       info->exit_code = 0;
+       return NULL;
+}
+
+/*
+ * Test that namespace becomes inactive after thread exits.
+ * This verifies active reference counting works with threads, not just processes.
+ */
+TEST(thread_ns_inactive_after_exit)
+{
+       pthread_t thread;
+       struct thread_ns_info info;
+       struct file_handle *handle;
+       int pipefd[2];
+       int syncpipe[2];
+       int ret;
+       char sync_byte;
+       char buf[sizeof(*handle) + MAX_HANDLE_SZ];
+
+       ASSERT_EQ(pipe(pipefd), 0);
+       ASSERT_EQ(pipe(syncpipe), 0);
+
+       info.pipefd = pipefd[1];
+       info.syncfd_read = syncpipe[0];
+       info.syncfd_write = -1;
+       info.exit_code = -1;
+
+       /* Create thread that will create a namespace */
+       ret = pthread_create(&thread, NULL, thread_create_namespace, &info);
+       ASSERT_EQ(ret, 0);
+
+       /* Read namespace ID from thread */
+       __u64 ns_id;
+       ret = read(pipefd[0], &ns_id, sizeof(ns_id));
+       if (ret != sizeof(ns_id)) {
+               sync_byte = 'X';
+               write(syncpipe[1], &sync_byte, 1);
+               pthread_join(thread, NULL);
+               close(pipefd[0]);
+               close(pipefd[1]);
+               close(syncpipe[0]);
+               close(syncpipe[1]);
+               SKIP(return, "Failed to read namespace ID from thread");
+       }
+
+       TH_LOG("Thread created namespace with ID %llu", (unsigned long long)ns_id);
+
+       /* Construct file handle */
+       handle = (struct file_handle *)buf;
+       handle->handle_bytes = sizeof(struct nsfs_file_handle);
+       handle->handle_type = FILEID_NSFS;
+       struct nsfs_file_handle *fh = (struct nsfs_file_handle *)handle->f_handle;
+       fh->ns_id = ns_id;
+       fh->ns_type = 0;
+       fh->ns_inum = 0;
+
+       /* Namespace should be active while thread is alive */
+       TH_LOG("Attempting to open namespace while thread is alive (should succeed)");
+       int nsfd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
+       ASSERT_GE(nsfd, 0);
+       close(nsfd);
+
+       /* Signal thread to exit */
+       TH_LOG("Signaling thread to exit");
+       sync_byte = 'X';
+       ASSERT_EQ(write(syncpipe[1], &sync_byte, 1), 1);
+       close(syncpipe[1]);
+
+       /* Wait for thread to exit */
+       ASSERT_EQ(pthread_join(thread, NULL), 0);
+       close(pipefd[0]);
+       close(pipefd[1]);
+       close(syncpipe[0]);
+
+       if (info.exit_code != 0)
+               SKIP(return, "Thread failed to create namespace");
+
+       TH_LOG("Thread exited, namespace should be inactive");
+
+       /* Namespace should now be inactive */
+       nsfd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
+       ASSERT_LT(nsfd, 0);
+       /* Should fail with ENOENT (inactive) or ESTALE (gone) */
+       TH_LOG("Namespace inactive as expected: %s (errno=%d)", strerror(errno), errno);
+       ASSERT_TRUE(errno == ENOENT || errno == ESTALE);
+}
+
 TEST_HARNESS_MAIN