mirror of
https://github.com/torvalds/linux.git
synced 2026-06-03 12:03:54 +02:00
selftests/namespace: test listns() pagination
Minimal test case to reproduce KASAN out-of-bounds in listns pagination. Link: https://patch.msgid.link/20251029-work-namespace-nstree-listns-v4-72-2e6f823ebdc0@kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
parent
fc85885692
commit
2cc1c01fe9
|
|
@ -7,3 +7,4 @@ listns_permissions_test
|
|||
siocgskns_test
|
||||
cred_change_test
|
||||
stress_test
|
||||
listns_pagination_bug
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ TEST_GEN_PROGS := nsid_test \
|
|||
listns_permissions_test \
|
||||
siocgskns_test \
|
||||
cred_change_test \
|
||||
stress_test
|
||||
stress_test \
|
||||
listns_pagination_bug
|
||||
|
||||
include ../lib.mk
|
||||
|
||||
|
|
@ -20,4 +21,5 @@ $(OUTPUT)/listns_permissions_test: ../filesystems/utils.c
|
|||
$(OUTPUT)/siocgskns_test: ../filesystems/utils.c
|
||||
$(OUTPUT)/cred_change_test: ../filesystems/utils.c
|
||||
$(OUTPUT)/stress_test: ../filesystems/utils.c
|
||||
$(OUTPUT)/listns_pagination_bug: ../filesystems/utils.c
|
||||
|
||||
|
|
|
|||
138
tools/testing/selftests/namespaces/listns_pagination_bug.c
Normal file
138
tools/testing/selftests/namespaces/listns_pagination_bug.c
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include "../kselftest_harness.h"
|
||||
#include "../filesystems/utils.h"
|
||||
#include "wrappers.h"
|
||||
|
||||
/*
|
||||
* Minimal test case to reproduce KASAN out-of-bounds in listns pagination.
|
||||
*
|
||||
* The bug occurs when:
|
||||
* 1. Filtering by a specific namespace type (e.g., CLONE_NEWUSER)
|
||||
* 2. Using pagination (req.ns_id != 0)
|
||||
* 3. The lookup_ns_id_at() call in do_listns() passes ns_type=0 instead of
|
||||
* the filtered type, causing it to search the unified tree and potentially
|
||||
* return a namespace of the wrong type.
|
||||
*/
|
||||
TEST(pagination_with_type_filter)
|
||||
{
|
||||
struct ns_id_req req = {
|
||||
.size = sizeof(req),
|
||||
.spare = 0,
|
||||
.ns_id = 0,
|
||||
.ns_type = CLONE_NEWUSER, /* Filter by user namespace */
|
||||
.spare2 = 0,
|
||||
.user_ns_id = 0,
|
||||
};
|
||||
pid_t pids[10];
|
||||
int num_children = 10;
|
||||
int i;
|
||||
int sv[2];
|
||||
__u64 first_batch[3];
|
||||
ssize_t ret;
|
||||
|
||||
ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), 0);
|
||||
|
||||
/* Create children with user namespaces */
|
||||
for (i = 0; i < num_children; i++) {
|
||||
pids[i] = fork();
|
||||
ASSERT_GE(pids[i], 0);
|
||||
|
||||
if (pids[i] == 0) {
|
||||
char c;
|
||||
close(sv[0]);
|
||||
|
||||
if (setup_userns() < 0) {
|
||||
close(sv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Signal parent we're ready */
|
||||
if (write(sv[1], &c, 1) != 1) {
|
||||
close(sv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Wait for parent signal to exit */
|
||||
if (read(sv[1], &c, 1) != 1) {
|
||||
close(sv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
close(sv[1]);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
close(sv[1]);
|
||||
|
||||
/* Wait for all children to signal ready */
|
||||
for (i = 0; i < num_children; i++) {
|
||||
char c;
|
||||
if (read(sv[0], &c, 1) != 1) {
|
||||
close(sv[0]);
|
||||
for (int j = 0; j < num_children; j++)
|
||||
kill(pids[j], SIGKILL);
|
||||
for (int j = 0; j < num_children; j++)
|
||||
waitpid(pids[j], NULL, 0);
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* First batch - this should work */
|
||||
ret = sys_listns(&req, first_batch, 3, 0);
|
||||
if (ret < 0) {
|
||||
if (errno == ENOSYS) {
|
||||
close(sv[0]);
|
||||
for (i = 0; i < num_children; i++)
|
||||
kill(pids[i], SIGKILL);
|
||||
for (i = 0; i < num_children; i++)
|
||||
waitpid(pids[i], NULL, 0);
|
||||
SKIP(return, "listns() not supported");
|
||||
}
|
||||
ASSERT_GE(ret, 0);
|
||||
}
|
||||
|
||||
TH_LOG("First batch returned %zd entries", ret);
|
||||
|
||||
if (ret == 3) {
|
||||
__u64 second_batch[3];
|
||||
|
||||
/* Second batch - pagination triggers the bug */
|
||||
req.ns_id = first_batch[2]; /* Continue from last ID */
|
||||
ret = sys_listns(&req, second_batch, 3, 0);
|
||||
|
||||
TH_LOG("Second batch returned %zd entries", ret);
|
||||
ASSERT_GE(ret, 0);
|
||||
}
|
||||
|
||||
/* Signal all children to exit */
|
||||
for (i = 0; i < num_children; i++) {
|
||||
char c = 'X';
|
||||
if (write(sv[0], &c, 1) != 1) {
|
||||
close(sv[0]);
|
||||
for (int j = i; j < num_children; j++)
|
||||
kill(pids[j], SIGKILL);
|
||||
for (int j = 0; j < num_children; j++)
|
||||
waitpid(pids[j], NULL, 0);
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
}
|
||||
|
||||
close(sv[0]);
|
||||
|
||||
/* Cleanup */
|
||||
for (i = 0; i < num_children; i++) {
|
||||
int status;
|
||||
waitpid(pids[i], &status, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
Loading…
Reference in New Issue
Block a user