1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 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
|