File Index
Files and directories
Sign in
FILE

listns_pagination_bug.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/namespaces/listns_pagination_bug.c C
  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 */
 24TEST(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
138TEST_HARNESS_MAIN
~/ Read-only file preview.