Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/filesystems/devpts_pts.c
26298 views
1
// SPDX-License-Identifier: GPL-2.0
2
#define _GNU_SOURCE
3
#include <errno.h>
4
#include <fcntl.h>
5
#include <sched.h>
6
#include <stdbool.h>
7
#include <stdio.h>
8
#include <stdlib.h>
9
#include <string.h>
10
#include <unistd.h>
11
#include <asm/ioctls.h>
12
#include <sys/mount.h>
13
#include <sys/wait.h>
14
#include "../kselftest.h"
15
16
static bool terminal_dup2(int duplicate, int original)
17
{
18
int ret;
19
20
ret = dup2(duplicate, original);
21
if (ret < 0)
22
return false;
23
24
return true;
25
}
26
27
static int terminal_set_stdfds(int fd)
28
{
29
int i;
30
31
if (fd < 0)
32
return 0;
33
34
for (i = 0; i < 3; i++)
35
if (!terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO,
36
STDERR_FILENO}[i]))
37
return -1;
38
39
return 0;
40
}
41
42
static int login_pty(int fd)
43
{
44
int ret;
45
46
setsid();
47
48
ret = ioctl(fd, TIOCSCTTY, NULL);
49
if (ret < 0)
50
return -1;
51
52
ret = terminal_set_stdfds(fd);
53
if (ret < 0)
54
return -1;
55
56
if (fd > STDERR_FILENO)
57
close(fd);
58
59
return 0;
60
}
61
62
static int wait_for_pid(pid_t pid)
63
{
64
int status, ret;
65
66
again:
67
ret = waitpid(pid, &status, 0);
68
if (ret == -1) {
69
if (errno == EINTR)
70
goto again;
71
return -1;
72
}
73
if (ret != pid)
74
goto again;
75
76
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
77
return -1;
78
79
return 0;
80
}
81
82
static int resolve_procfd_symlink(int fd, char *buf, size_t buflen)
83
{
84
int ret;
85
char procfd[4096];
86
87
ret = snprintf(procfd, 4096, "/proc/self/fd/%d", fd);
88
if (ret < 0 || ret >= 4096)
89
return -1;
90
91
ret = readlink(procfd, buf, buflen);
92
if (ret < 0 || (size_t)ret >= buflen)
93
return -1;
94
95
buf[ret] = '\0';
96
97
return 0;
98
}
99
100
static int do_tiocgptpeer(char *ptmx, char *expected_procfd_contents)
101
{
102
int ret;
103
int master = -1, slave = -1, fret = -1;
104
105
master = open(ptmx, O_RDWR | O_NOCTTY | O_CLOEXEC);
106
if (master < 0) {
107
fprintf(stderr, "Failed to open \"%s\": %s\n", ptmx,
108
strerror(errno));
109
return -1;
110
}
111
112
/*
113
* grantpt() makes assumptions about /dev/pts/ so ignore it. It's also
114
* not really needed.
115
*/
116
ret = unlockpt(master);
117
if (ret < 0) {
118
fprintf(stderr, "Failed to unlock terminal\n");
119
goto do_cleanup;
120
}
121
122
#ifdef TIOCGPTPEER
123
slave = ioctl(master, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC);
124
#endif
125
if (slave < 0) {
126
if (errno == EINVAL) {
127
fprintf(stderr, "TIOCGPTPEER is not supported. "
128
"Skipping test.\n");
129
fret = KSFT_SKIP;
130
} else {
131
fprintf(stderr,
132
"Failed to perform TIOCGPTPEER ioctl\n");
133
fret = EXIT_FAILURE;
134
}
135
goto do_cleanup;
136
}
137
138
pid_t pid = fork();
139
if (pid < 0)
140
goto do_cleanup;
141
142
if (pid == 0) {
143
char buf[4096];
144
145
ret = login_pty(slave);
146
if (ret < 0) {
147
fprintf(stderr, "Failed to setup terminal\n");
148
_exit(EXIT_FAILURE);
149
}
150
151
ret = resolve_procfd_symlink(STDIN_FILENO, buf, sizeof(buf));
152
if (ret < 0) {
153
fprintf(stderr, "Failed to retrieve pathname of pts "
154
"slave file descriptor\n");
155
_exit(EXIT_FAILURE);
156
}
157
158
if (strncmp(expected_procfd_contents, buf,
159
strlen(expected_procfd_contents)) != 0) {
160
fprintf(stderr, "Received invalid contents for "
161
"\"/proc/<pid>/fd/%d\" symlink: %s\n",
162
STDIN_FILENO, buf);
163
_exit(-1);
164
}
165
166
fprintf(stderr, "Contents of \"/proc/<pid>/fd/%d\" "
167
"symlink are valid: %s\n", STDIN_FILENO, buf);
168
169
_exit(EXIT_SUCCESS);
170
}
171
172
ret = wait_for_pid(pid);
173
if (ret < 0)
174
goto do_cleanup;
175
176
fret = EXIT_SUCCESS;
177
178
do_cleanup:
179
if (master >= 0)
180
close(master);
181
if (slave >= 0)
182
close(slave);
183
184
return fret;
185
}
186
187
static int verify_non_standard_devpts_mount(void)
188
{
189
char *mntpoint;
190
int ret = -1;
191
char devpts[] = P_tmpdir "/devpts_fs_XXXXXX";
192
char ptmx[] = P_tmpdir "/devpts_fs_XXXXXX/ptmx";
193
194
ret = umount("/dev/pts");
195
if (ret < 0) {
196
fprintf(stderr, "Failed to unmount \"/dev/pts\": %s\n",
197
strerror(errno));
198
return -1;
199
}
200
201
(void)umount("/dev/ptmx");
202
203
mntpoint = mkdtemp(devpts);
204
if (!mntpoint) {
205
fprintf(stderr, "Failed to create temporary mountpoint: %s\n",
206
strerror(errno));
207
return -1;
208
}
209
210
ret = mount("devpts", mntpoint, "devpts", MS_NOSUID | MS_NOEXEC,
211
"newinstance,ptmxmode=0666,mode=0620,gid=5");
212
if (ret < 0) {
213
fprintf(stderr, "Failed to mount devpts fs to \"%s\" in new "
214
"mount namespace: %s\n", mntpoint,
215
strerror(errno));
216
unlink(mntpoint);
217
return -1;
218
}
219
220
ret = snprintf(ptmx, sizeof(ptmx), "%s/ptmx", devpts);
221
if (ret < 0 || (size_t)ret >= sizeof(ptmx)) {
222
unlink(mntpoint);
223
return -1;
224
}
225
226
ret = do_tiocgptpeer(ptmx, mntpoint);
227
unlink(mntpoint);
228
if (ret < 0)
229
return -1;
230
231
return 0;
232
}
233
234
static int verify_ptmx_bind_mount(void)
235
{
236
int ret;
237
238
ret = mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_BIND, NULL);
239
if (ret < 0) {
240
fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
241
"\"/dev/ptmx\" mount namespace\n");
242
return -1;
243
}
244
245
ret = do_tiocgptpeer("/dev/ptmx", "/dev/pts/");
246
if (ret < 0)
247
return -1;
248
249
return 0;
250
}
251
252
static int verify_invalid_ptmx_bind_mount(void)
253
{
254
int ret;
255
char mntpoint_fd;
256
char ptmx[] = P_tmpdir "/devpts_ptmx_XXXXXX";
257
258
mntpoint_fd = mkstemp(ptmx);
259
if (mntpoint_fd < 0) {
260
fprintf(stderr, "Failed to create temporary directory: %s\n",
261
strerror(errno));
262
return -1;
263
}
264
265
ret = mount("/dev/pts/ptmx", ptmx, NULL, MS_BIND, NULL);
266
close(mntpoint_fd);
267
if (ret < 0) {
268
fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
269
"\"%s\" mount namespace\n", ptmx);
270
return -1;
271
}
272
273
ret = do_tiocgptpeer(ptmx, "/dev/pts/");
274
if (ret == 0)
275
return -1;
276
277
return 0;
278
}
279
280
int main(int argc, char *argv[])
281
{
282
int ret;
283
284
if (!isatty(STDIN_FILENO)) {
285
fprintf(stderr, "Standard input file descriptor is not attached "
286
"to a terminal. Skipping test\n");
287
exit(KSFT_SKIP);
288
}
289
290
ret = unshare(CLONE_NEWNS);
291
if (ret < 0) {
292
fprintf(stderr, "Failed to unshare mount namespace\n");
293
exit(EXIT_FAILURE);
294
}
295
296
ret = mount("", "/", NULL, MS_PRIVATE | MS_REC, 0);
297
if (ret < 0) {
298
fprintf(stderr, "Failed to make \"/\" MS_PRIVATE in new mount "
299
"namespace\n");
300
exit(EXIT_FAILURE);
301
}
302
303
ret = verify_ptmx_bind_mount();
304
if (ret < 0)
305
exit(EXIT_FAILURE);
306
307
ret = verify_invalid_ptmx_bind_mount();
308
if (ret < 0)
309
exit(EXIT_FAILURE);
310
311
ret = verify_non_standard_devpts_mount();
312
if (ret < 0)
313
exit(EXIT_FAILURE);
314
315
exit(EXIT_SUCCESS);
316
}
317
318