Loading...
Note: File does not exist in v5.9.
1// SPDX-License-Identifier: GPL-2.0
2#define _GNU_SOURCE
3#define __SANE_USERSPACE_TYPES__
4
5#include <fcntl.h>
6#include <limits.h>
7#include <pthread.h>
8#include <sched.h>
9#include <stdio.h>
10#include <string.h>
11#include <sys/stat.h>
12#include <sys/mount.h>
13#include <unistd.h>
14
15#include "../kselftest_harness.h"
16
17#include <linux/types.h>
18#include <linux/mount.h>
19#include <sys/syscall.h>
20
21static inline int sys_fsopen(const char *fsname, unsigned int flags)
22{
23 return syscall(__NR_fsopen, fsname, flags);
24}
25
26static inline int sys_fsconfig(int fd, unsigned int cmd, const char *key,
27 const char *value, int aux)
28{
29 return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
30}
31
32static inline int sys_fsmount(int fd, unsigned int flags,
33 unsigned int attr_flags)
34{
35 return syscall(__NR_fsmount, fd, flags, attr_flags);
36}
37
38#ifndef MOVE_MOUNT_F_EMPTY_PATH
39#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
40#endif
41
42static inline int sys_move_mount(int from_dfd, const char *from_pathname,
43 int to_dfd, const char *to_pathname,
44 unsigned int flags)
45{
46 return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd,
47 to_pathname, flags);
48}
49
50FIXTURE(file_stressor) {
51 int fd_tmpfs;
52 int nr_procs;
53 int max_fds;
54 pid_t *pids_openers;
55 pid_t *pids_getdents;
56 int *fd_proc_pid;
57};
58
59FIXTURE_SETUP(file_stressor)
60{
61 int fd_context;
62
63 ASSERT_EQ(unshare(CLONE_NEWNS), 0);
64 ASSERT_EQ(mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0);
65 ASSERT_EQ(mkdir("/slab_typesafe_by_rcu", 0755), 0);
66
67 fd_context = sys_fsopen("tmpfs", 0);
68 ASSERT_GE(fd_context, 0);
69
70 ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0);
71 self->fd_tmpfs = sys_fsmount(fd_context, 0, 0);
72 ASSERT_GE(self->fd_tmpfs, 0);
73 ASSERT_EQ(close(fd_context), 0);
74
75 ASSERT_EQ(sys_move_mount(self->fd_tmpfs, "", -EBADF, "/slab_typesafe_by_rcu", MOVE_MOUNT_F_EMPTY_PATH), 0);
76
77 self->nr_procs = sysconf(_SC_NPROCESSORS_ONLN);
78 self->pids_openers = malloc(sizeof(pid_t) * self->nr_procs);
79 ASSERT_NE(self->pids_openers, NULL);
80 self->pids_getdents = malloc(sizeof(pid_t) * self->nr_procs);
81 ASSERT_NE(self->pids_getdents, NULL);
82 self->fd_proc_pid = malloc(sizeof(int) * self->nr_procs);
83 ASSERT_NE(self->fd_proc_pid, NULL);
84 self->max_fds = 500;
85}
86
87FIXTURE_TEARDOWN(file_stressor)
88{
89 for (int i = 0; i < self->nr_procs; i++) {
90 int wstatus;
91 pid_t pid;
92
93 pid = waitpid(self->pids_openers[i], &wstatus, 0);
94 ASSERT_EQ(pid, self->pids_openers[i]);
95 ASSERT_TRUE(!WIFEXITED(wstatus) || !WIFSIGNALED(wstatus));
96
97 pid = waitpid(self->pids_getdents[i], &wstatus, 0);
98 ASSERT_EQ(pid, self->pids_getdents[i]);
99 ASSERT_TRUE(!WIFEXITED(wstatus) || !WIFSIGNALED(wstatus));
100 }
101 free(self->pids_openers);
102 free(self->pids_getdents);
103 ASSERT_EQ(close(self->fd_tmpfs), 0);
104
105 umount2("/slab_typesafe_by_rcu", 0);
106 ASSERT_EQ(rmdir("/slab_typesafe_by_rcu"), 0);
107}
108
109TEST_F_TIMEOUT(file_stressor, slab_typesafe_by_rcu, 900 * 2)
110{
111 for (int i = 0; i < self->nr_procs; i++) {
112 pid_t pid_self;
113
114 self->pids_openers[i] = fork();
115 ASSERT_GE(self->pids_openers[i], 0);
116
117 if (self->pids_openers[i] != 0)
118 continue;
119
120 self->pids_openers[i] = getpid();
121 for (;;) {
122 for (int i = 0; i < self->max_fds; i++) {
123 char path[PATH_MAX];
124 int fd;
125
126 sprintf(path, "/slab_typesafe_by_rcu/file-%d-%d", self->pids_openers[i], i);
127 fd = open(path, O_CREAT | O_RDONLY | O_CLOEXEC, 0644);
128 if (fd < 0)
129 continue;
130 }
131
132 close_range(3, ~0U, 0);
133 }
134
135 exit(0);
136 }
137
138 for (int i = 0; i < self->nr_procs; i++) {
139 char path[PATH_MAX];
140
141 sprintf(path, "/proc/%d/fd/", self->pids_openers[i]);
142 self->fd_proc_pid[i] = open(path, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
143 ASSERT_GE(self->fd_proc_pid[i], 0);
144 }
145
146 for (int i = 0; i < self->nr_procs; i++) {
147 self->pids_getdents[i] = fork();
148 ASSERT_GE(self->pids_getdents[i], 0);
149
150 if (self->pids_getdents[i] != 0)
151 continue;
152
153 self->pids_getdents[i] = getpid();
154 for (;;) {
155 char ents[1024];
156 ssize_t nr_read;
157
158 /*
159 * Concurrently read /proc/<pid>/fd/ which rougly does:
160 *
161 * f = fget_task_next(p, &fd);
162 * if (!f)
163 * break;
164 * data.mode = f->f_mode;
165 * fput(f);
166 *
167 * Which means that it'll try to get a reference to a
168 * file in another task's file descriptor table.
169 *
170 * Under heavy file load it is increasingly likely that
171 * the other task will manage to close @file and @file
172 * is being recycled due to SLAB_TYPEAFE_BY_RCU
173 * concurrently. This will trigger various warnings in
174 * the file reference counting code.
175 */
176 do {
177 nr_read = syscall(SYS_getdents64, self->fd_proc_pid[i], ents, sizeof(ents));
178 } while (nr_read >= 0);
179
180 lseek(self->fd_proc_pid[i], 0, SEEK_SET);
181 }
182
183 exit(0);
184 }
185
186 ASSERT_EQ(clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec){ .tv_sec = 900 /* 15 min */ }, NULL), 0);
187
188 for (int i = 0; i < self->nr_procs; i++) {
189 kill(self->pids_openers[i], SIGKILL);
190 kill(self->pids_getdents[i], SIGKILL);
191 }
192}
193
194TEST_HARNESS_MAIN