1 /* Tests for opening/closing pseudo terminals - by D.C. van Moolenbroek */
2 /*
3 * As of the introduction of Unix98 PTY support, this test set actually relies
4 * on the ability to create Unix98 PTYs. The system still supports old-style
5 * PTYs but there is no way to force openpty(3) to use them. However, part of
6 * this test set can still be used to test old-style PTYs: first disable Unix98
7 * PTYs, for example by unmounting PTYFS or temporarily removing /dev/ptmx, and
8 * then run the a-f subtests from this test set as root.
9 */
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <signal.h>
14 #include <termios.h>
15 #include <sys/wait.h>
16 #include <sys/syslimits.h>
17 #include <paths.h>
18 #include <dirent.h>
19 #include <grp.h>
20 #include <fcntl.h>
21 #include <util.h>
22
23 #define ITERATIONS 10
24
25 #define MIN_PTYS 4
26
27 #include "common.h"
28
29 static int sighups; /* number of SIGHUP signals received */
30
31 /*
32 * Signal handler for SIGHUP and SIGUSR1.
33 */
34 static void
signal_handler(int sig)35 signal_handler(int sig)
36 {
37 if (sig == SIGHUP)
38 sighups++;
39 }
40
41 /*
42 * Set the slave side of the pseudo terminal to raw mode. This simplifies
43 * testing communication.
44 */
45 static void
make_raw(int slavefd)46 make_raw(int slavefd)
47 {
48 struct termios tios;
49
50 if (tcgetattr(slavefd, &tios) < 0) e(0);
51
52 cfmakeraw(&tios);
53
54 if (tcsetattr(slavefd, TCSANOW, &tios) < 0) e(0);
55 }
56
57 /*
58 * See if the given pseudo terminal can successfully perform basic
59 * communication between master and slave.
60 */
61 static void
test_comm(int masterfd,int slavefd)62 test_comm(int masterfd, int slavefd)
63 {
64 char c;
65
66 make_raw(slavefd);
67
68 c = 'A';
69 if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
70 if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
71 if (c != 'A') e(0);
72
73 c = 'B';
74 if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
75 if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
76 if (c != 'B') e(0);
77
78 c = 'C';
79 if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
80 if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
81 if (c != 'C') e(0);
82
83 c = 'D';
84 if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
85 if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
86 if (c != 'D') e(0);
87 }
88
89 /*
90 * Obtain a pseudo terminal. The master end is opened and its file descriptor
91 * stored in 'pfd'. The slave path name is stored in 'tname'. For old-style
92 * PTYs, the function returns 1 and stores the master name in 'pname' if not
93 * NULL. For Unix98 PTYs, the function returns 0, in which case no master name
94 * is available. For old-style PTYs, the caller may close and reopen the
95 * master. In that case, we make the assumption that nobody snatches the pair
96 * while we are running. For Unix98 PTYs, the master must be kept open.
97 */
98 static int
get_pty(int * pfd,char pname[PATH_MAX],char tname[PATH_MAX])99 get_pty(int *pfd, char pname[PATH_MAX], char tname[PATH_MAX])
100 {
101 char *name;
102 int len, masterfd, slavefd;
103
104 /*
105 * First try Unix98 PTY allocation, mainly to avoid opening the slave
106 * end immediately. If this fails, try openpty(3) as well.
107 */
108 if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) != -1) {
109 if (grantpt(masterfd) != -1 && unlockpt(masterfd) != -1 &&
110 (name = ptsname(masterfd)) != NULL) {
111 *pfd = masterfd;
112 strlcpy(tname, name, PATH_MAX);
113
114 return 0;
115 }
116 if (close(masterfd) < 0) e(0);
117 }
118
119 if (openpty(&masterfd, &slavefd, tname, NULL, NULL) < 0) e(0);
120
121 test_comm(masterfd, slavefd);
122
123 *pfd = masterfd;
124
125 if (close(slavefd) < 0) e(0);
126
127 /*
128 * openpty(3) gives us only the slave name, but we also want the master
129 * name.
130 */
131 len = strlen(_PATH_DEV);
132 if (strncmp(tname, _PATH_DEV, len)) e(0);
133
134 if (strncmp(&tname[len], "tty", 3))
135 return 0; /* Unix98 after all? Well okay, whatever.. */
136
137 if (pname != NULL) {
138 strlcpy(pname, tname, PATH_MAX);
139 pname[len] = 'p';
140 }
141
142 return 1;
143 }
144
145 /*
146 * Test various orders of opening and closing the master and slave sides of a
147 * pseudo terminal, as well as opening/closing one side without ever opening
148 * the other. This test is meaningful mainly for old-style pseudoterminals.
149 */
150 static void
test77a(void)151 test77a(void)
152 {
153 struct sigaction act, oact;
154 char pname[PATH_MAX], tname[PATH_MAX];
155 int oldstyle, masterfd, slavefd;
156
157 subtest = 1;
158
159 /* We do not want to get SIGHUP signals in this test. */
160 memset(&act, 0, sizeof(act));
161 act.sa_handler = SIG_IGN;
162 if (sigaction(SIGHUP, &act, &oact) < 0) e(0);
163
164 /* Obtain a pseudo terminal. */
165 oldstyle = get_pty(&masterfd, pname, tname);
166
167 if (oldstyle) {
168 /* Try closing the master. */
169 if (close(masterfd) < 0) e(0);
170
171 /* See if we can reopen the master. */
172 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
173 }
174
175 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
176
177 test_comm(masterfd, slavefd);
178
179 /* In the meantime, test different closing orders. This is order A. */
180 if (close(slavefd) < 0) e(0);
181 if (close(masterfd) < 0) e(0);
182
183 /* Now try opening the pair (or a new pair) again. */
184 if (!oldstyle)
185 oldstyle = get_pty(&masterfd, pname, tname);
186 else
187 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
188
189 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
190
191 test_comm(masterfd, slavefd);
192
193 if (close(slavefd) < 0) e(0);
194
195 /*
196 * Try reopening the slave after closing it. It is not very important
197 * that this works, but the TTY driver should currently support it.
198 */
199 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
200
201 test_comm(masterfd, slavefd);
202
203 /* This is closing order B. This may or may not cause a SIGHUP. */
204 if (close(masterfd) < 0) e(0);
205 if (close(slavefd) < 0) e(0);
206
207 /* Try the normal open procedure. */
208 if (!oldstyle)
209 oldstyle = get_pty(&masterfd, pname, tname);
210 else
211 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
212
213 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
214
215 test_comm(masterfd, slavefd);
216
217 if (close(slavefd) < 0) e(0);
218 if (close(masterfd) < 0) e(0);
219
220 /*
221 * Try reopening and closing the slave, without opening the master.
222 * This should work on old-style PTYS, but not on Unix98 PTYs.
223 */
224 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) >= 0) {
225 if (!oldstyle) e(0);
226
227 if (close(slavefd) < 0) e(0);
228 } else
229 if (oldstyle) e(0);
230
231 /* Again, try the normal open procedure. */
232 if (!oldstyle)
233 oldstyle = get_pty(&masterfd, pname, tname);
234 else
235 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
236
237 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
238
239 test_comm(masterfd, slavefd);
240
241 if (close(slavefd) < 0) e(0);
242 if (close(masterfd) < 0) e(0);
243
244 /*
245 * Finally, try opening the slave first. This does not work with
246 * Unix98 PTYs.
247 */
248 if (oldstyle) {
249 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
250 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
251
252 test_comm(masterfd, slavefd);
253
254 if (close(slavefd) < 0) e(0);
255 if (close(masterfd) < 0) e(0);
256 }
257
258 if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
259 }
260
261 /*
262 * Test opening a single side multiple times.
263 */
264 static void
test77b(void)265 test77b(void)
266 {
267 char pname[PATH_MAX], tname[PATH_MAX];
268 int oldstyle, masterfd, slavefd, extrafd;
269
270 subtest = 2;
271
272 /* Obtain a pseudo terminal. */
273 oldstyle = get_pty(&masterfd, pname, tname);
274
275 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
276
277 /*
278 * It must not be possible to open the master multiple times. Doing so
279 * is possible only if we have a named master, i.e., an old-style PTY.
280 */
281 test_comm(masterfd, slavefd);
282
283 if (oldstyle) {
284 if ((extrafd = open(pname, O_RDWR | O_NOCTTY)) >= 0) e(0);
285 if (errno != EIO) e(0);
286 }
287
288 test_comm(masterfd, slavefd);
289
290 if (close(slavefd) < 0) e(0);
291 if (close(masterfd) < 0) e(0);
292
293 /* The slave can be opened multiple times, though. */
294 oldstyle = get_pty(&masterfd, pname, tname);
295
296 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
297
298 test_comm(masterfd, slavefd);
299
300 if ((extrafd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
301
302 test_comm(masterfd, extrafd);
303 test_comm(masterfd, slavefd);
304
305 if (close(slavefd) < 0) e(0);
306 if (close(extrafd) < 0) e(0);
307 if (close(masterfd) < 0) e(0);
308 }
309
310 /*
311 * Test communication on half-open pseudo terminals.
312 */
313 static void
test77c(void)314 test77c(void)
315 {
316 struct sigaction act, oact;
317 char pname[PATH_MAX], tname[PATH_MAX];
318 int oldstyle, masterfd, slavefd;
319 char c;
320
321 subtest = 3;
322
323 /* We do not want to get SIGHUP signals in this test. */
324 memset(&act, 0, sizeof(act));
325 act.sa_handler = SIG_IGN;
326 if (sigaction(SIGHUP, &act, &oact) < 0) e(0);
327
328 /* Obtain a pseudo terminal. */
329 oldstyle = get_pty(&masterfd, pname, tname);
330
331 /*
332 * For old-style pseudo terminals, we have just opened and closed the
333 * slave end, which alters the behavior we are testing below. Close
334 * and reopen the master to start fresh.
335 */
336 if (oldstyle) {
337 if (close(masterfd) < 0) e(0);
338
339 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
340 }
341
342 /* Writes to the master should be buffered until there is a slave. */
343 c = 'E';
344 if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
345
346 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
347
348 make_raw(slavefd);
349
350 if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
351 if (c != 'E') e(0);
352
353 /* Discard the echo on the master. */
354 if (tcflush(slavefd, TCOFLUSH) != 0) e(0);
355
356 test_comm(masterfd, slavefd);
357
358 if (close(slavefd) < 0) e(0);
359
360 /* Writes to the master after the slave has been closed should fail. */
361 if (write(masterfd, &c, sizeof(c)) >= 0) e(0);
362 if (errno != EIO) e(0);
363
364 if (oldstyle)
365 if (close(masterfd) < 0) e(0);
366
367 /*
368 * Writes to the slave should be buffered until there is a master.
369 * This applies to old-style PTYs only.
370 */
371 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
372
373 if (oldstyle) {
374 make_raw(slavefd);
375
376 c = 'F';
377 if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
378
379 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
380
381 if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
382 if (c != 'F') e(0);
383 }
384
385 test_comm(masterfd, slavefd);
386
387 if (close(masterfd) < 0) e(0);
388
389 if (write(slavefd, &c, sizeof(c)) >= 0) e(0);
390 if (errno != EIO) e(0);
391
392 /* Reads from the slave should return EOF if the master is gone. */
393 if (read(slavefd, &c, sizeof(c)) != 0) e(0);
394
395 if (close(slavefd) < 0) e(0);
396
397 if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
398 }
399
400 /*
401 * Wait for a child process to terminate. Return 0 if the child exited without
402 * errors, -1 otherwise.
403 */
404 static int
waitchild(void)405 waitchild(void)
406 {
407 int status;
408
409 if (wait(&status) <= 0) return -1;
410 if (!WIFEXITED(status)) return -1;
411 if (WEXITSTATUS(status) != 0) return -1;
412
413 return 0;
414 }
415
416 /*
417 * Test opening the slave side with and without the O_NOCTTY flag.
418 */
419 static void
test77d(void)420 test77d(void)
421 {
422 char pname[PATH_MAX], tname[PATH_MAX];
423 int masterfd, slavefd;
424
425 subtest = 4;
426
427 /* Make ourselves process group leader if we aren't already. */
428 (void)setsid();
429
430 /* Obtain a pseudo terminal. */
431 (void)get_pty(&masterfd, NULL, tname);
432
433 /*
434 * Opening the slave with O_NOCTTY should not change its controlling
435 * terminal.
436 */
437 switch (fork()) {
438 case 0:
439 if (close(masterfd) < 0) e(0);
440
441 if (setsid() < 0) e(0);
442
443 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
444
445 if (open("/dev/tty", O_RDWR) >= 0) e(0);
446 if (errno != ENXIO) e(0);
447
448 exit(errct);
449 case -1:
450 e(0);
451 default:
452 break;
453 }
454
455 if (waitchild() < 0) e(0);
456
457 if (close(masterfd) < 0) e(0);
458
459 (void)get_pty(&masterfd, pname, tname);
460
461 /*
462 * Opening the slave without O_NOCTTY should change its controlling
463 * terminal, though.
464 */
465 switch (fork()) {
466 case 0:
467 if (close(masterfd) < 0) e(0);
468
469 if (setsid() < 0) e(0);
470
471 if ((slavefd = open(tname, O_RDWR)) < 0) e(0);
472
473 if (open("/dev/tty", O_RDWR) < 0) e(0);
474
475 exit(errct);
476 case -1:
477 e(0);
478 default:
479 break;
480 }
481
482 if (waitchild() < 0) e(0);
483
484 if (close(masterfd) < 0) e(0);
485 }
486
487 /*
488 * Test receiving of SIGHUP on master hang-up. All of the tests so far have
489 * ignored SIGHUP, and probably would not have received one anyway, since the
490 * process was not its own session leader. Time to test this aspect.
491 */
492 static void
test77e(void)493 test77e(void)
494 {
495 struct sigaction act, hup_oact, usr_oact;
496 sigset_t set, oset;
497 char tname[PATH_MAX];
498 int masterfd, slavefd;
499
500 subtest = 5;
501
502 memset(&act, 0, sizeof(act));
503 act.sa_handler = signal_handler;
504 if (sigaction(SIGHUP, &act, &hup_oact) < 0) e(0);
505
506 memset(&act, 0, sizeof(act));
507 act.sa_handler = signal_handler;
508 if (sigaction(SIGUSR1, &act, &usr_oact) < 0) e(0);
509
510 sigemptyset(&set);
511 sigaddset(&set, SIGHUP);
512 sigaddset(&set, SIGUSR1);
513 if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) e(0);
514
515 sighups = 0;
516
517 /* Make ourselves process group leader if we aren't already. */
518 (void)setsid();
519
520 /* Obtain a pseudo terminal. */
521 (void)get_pty(&masterfd, NULL, tname);
522
523 switch (fork()) {
524 case 0:
525 if (close(masterfd) < 0) e(0);
526
527 /* Become session leader. */
528 if (setsid() < 0) e(0);
529
530 if ((slavefd = open(tname, O_RDWR)) < 0) e(0);
531
532 /* Tell the parent we are ready. */
533 kill(getppid(), SIGUSR1);
534
535 /* We should now get a SIGHUP. */
536 set = oset;
537 if (sigsuspend(&set) >= 0) e(0);
538
539 if (sighups != 1) e(0);
540
541 exit(errct);
542 case -1:
543 e(0);
544 default:
545 break;
546 }
547
548 /* Wait for SIGUSR1 from the child. */
549 set = oset;
550 if (sigsuspend(&set) >= 0) e(0);
551
552 /* Closing the master should now raise a SIGHUP signal in the child. */
553 if (close(masterfd) < 0) e(0);
554
555 if (waitchild() < 0) e(0);
556
557 if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) e(0);
558
559 if (sigaction(SIGHUP, &hup_oact, NULL) < 0) e(0);
560 if (sigaction(SIGUSR1, &usr_oact, NULL) < 0) e(0);
561 }
562
563 /*
564 * Test basic select functionality on /dev/tty. While this test should not be
565 * part of this test set, we already have all the infrastructure we need here.
566 */
567 static void
test77f(void)568 test77f(void)
569 {
570 struct sigaction act, oact;
571 char c, tname[PATH_MAX];
572 struct timeval tv;
573 fd_set fd_set;
574 int fd, maxfd, masterfd, slavefd;
575
576 subtest = 6;
577
578 /* We do not want to get SIGHUP signals in this test. */
579 memset(&act, 0, sizeof(act));
580 act.sa_handler = SIG_IGN;
581 if (sigaction(SIGHUP, &act, &oact) < 0) e(0);
582
583 /* Obtain a pseudo terminal. */
584 (void)get_pty(&masterfd, NULL, tname);
585
586 switch (fork()) {
587 case 0:
588 if (close(masterfd) < 0) e(0);
589
590 if (setsid() < 0) e(0);
591
592 if ((slavefd = open(tname, O_RDWR)) < 0) e(0);
593
594 if ((fd = open("/dev/tty", O_RDWR)) < 0) e(0);
595
596 make_raw(fd);
597
598 /* Without slave input, /dev/tty is not ready for reading. */
599 FD_ZERO(&fd_set);
600 FD_SET(fd, &fd_set);
601 tv.tv_sec = 0;
602 tv.tv_usec = 0;
603
604 if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
605 if (FD_ISSET(fd, &fd_set)) e(0);
606
607 FD_SET(fd, &fd_set);
608 tv.tv_sec = 0;
609 tv.tv_usec = 10000;
610
611 if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
612 if (FD_ISSET(fd, &fd_set)) e(0);
613
614 /* It will be ready for writing, though. */
615 FD_SET(fd, &fd_set);
616
617 if (select(fd + 1, NULL, &fd_set, NULL, NULL) != 1) e(0);
618 if (!FD_ISSET(fd, &fd_set)) e(0);
619
620 /* Test mixing file descriptors to the same terminal. */
621 FD_ZERO(&fd_set);
622 FD_SET(fd, &fd_set);
623 FD_SET(slavefd, &fd_set);
624 tv.tv_sec = 0;
625 tv.tv_usec = 10000;
626
627 maxfd = fd > slavefd ? fd : slavefd;
628 if (select(maxfd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
629 if (FD_ISSET(fd, &fd_set)) e(0);
630 if (FD_ISSET(slavefd, &fd_set)) e(0);
631
632 /* The delayed echo on the master must wake up our select. */
633 c = 'A';
634 if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
635
636 FD_ZERO(&fd_set);
637 FD_SET(fd, &fd_set);
638
639 if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0);
640 if (!FD_ISSET(fd, &fd_set)) e(0);
641
642 /* Select must now still flag readiness for reading. */
643 tv.tv_sec = 0;
644 tv.tv_usec = 0;
645
646 if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 1) e(0);
647 if (!FD_ISSET(fd, &fd_set)) e(0);
648
649 /* That is, until we read the byte. */
650 if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
651 if (c != 'B') e(0);
652
653 if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
654 if (FD_ISSET(fd, &fd_set)) e(0);
655
656 /* Ask the parent to close the master. */
657 c = 'C';
658 if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
659
660 FD_SET(fd, &fd_set);
661
662 /* The closure must cause an EOF condition on the slave. */
663 if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0);
664 if (!FD_ISSET(fd, &fd_set)) e(0);
665
666 if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0);
667 if (!FD_ISSET(fd, &fd_set)) e(0);
668
669 if (read(slavefd, &c, sizeof(c)) != 0) e(0);
670
671 exit(errct);
672 case -1:
673 e(0);
674 default:
675 /* Wait for the child to write something to the slave. */
676 FD_ZERO(&fd_set);
677 FD_SET(masterfd, &fd_set);
678
679 if (select(masterfd + 1, &fd_set, NULL, NULL, NULL) != 1)
680 e(0);
681 if (!FD_ISSET(masterfd, &fd_set)) e(0);
682
683 if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
684 if (c != 'A') e(0);
685
686 /* Write a reply once the child is blocked in its select. */
687 tv.tv_sec = 1;
688 tv.tv_usec = 0;
689 if (select(masterfd + 1, &fd_set, NULL, NULL, &tv) != 0)
690 e(0);
691
692 c = 'B';
693 if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
694
695 /* Wait for the child to request closing the master. */
696 if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
697 if (c != 'C') e(0);
698
699 /* Close the master once the child is blocked in its select. */
700 sleep(1);
701
702 close(masterfd);
703
704 break;
705 }
706
707 if (waitchild() < 0) e(0);
708
709 if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
710 }
711
712 /*
713 * See if the directory contents of /dev/pts are as we expect. We have to keep
714 * in mind that other programs may have pseudo terminals open while we are
715 * running, although we assume that those programs do not open or close PTYs
716 * while we are running.
717 */
718 static void
test_getdents(int nindex,int array[3],int present[3])719 test_getdents(int nindex, int array[3], int present[3])
720 {
721 struct group *group;
722 DIR *dirp;
723 struct dirent *dp;
724 struct stat buf;
725 char path[PATH_MAX], *endp;
726 gid_t tty_gid;
727 int i, n, seen_dot, seen_dotdot, seen_index[3], *seen;
728
729 seen_dot = seen_dotdot = 0;
730 for (i = 0; i < nindex; i++)
731 seen_index[i] = 0;
732
733 if ((group = getgrnam("tty")) == NULL) e(0);
734 tty_gid = group->gr_gid;
735
736 if ((dirp = opendir(_PATH_DEV_PTS)) == NULL) e(0);
737
738 while ((dp = readdir(dirp)) != NULL) {
739 snprintf(path, sizeof(path), _PATH_DEV_PTS "%s", dp->d_name);
740 if (stat(path, &buf) < 0) e(0);
741
742 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
743 seen =
744 (dp->d_name[1] == '.') ? &seen_dot : &seen_dotdot;
745 if (*seen) e(0);
746 *seen = 1;
747
748 /* Check basic dirent and stat fields. */
749 if (dp->d_type != DT_DIR) e(0);
750 if (dp->d_name[1] == '\0' &&
751 buf.st_ino != dp->d_fileno) e(0);
752 if (!S_ISDIR(buf.st_mode)) e(0);
753 if (buf.st_nlink < 2) e(0);
754 } else {
755 /* The file name must be a number. */
756 errno = 0;
757 n = strtol(dp->d_name, &endp, 10);
758 if (errno != 0) e(0);
759 if (dp->d_name[0] == '\0' || *endp != '\0') e(0);
760 if (n < 0) e(0);
761
762 /* Check basic dirent and stat fields. */
763 if (dp->d_type != DT_CHR) e(0);
764 if (buf.st_ino != dp->d_fileno) e(0);
765 if (!S_ISCHR(buf.st_mode)) e(0);
766 if (buf.st_nlink != 1) e(0);
767 if (buf.st_size != 0) e(0);
768 if (buf.st_rdev == 0) e(0);
769
770 /* Is this one of the PTYs we created? */
771 for (i = 0; i < nindex; i++) {
772 if (array[i] == n) {
773 if (seen_index[i]) e(0);
774 seen_index[i] = 1;
775
776 break;
777 }
778 }
779
780 /* If so, perform some extra tests. */
781 if (i < nindex) {
782 if ((buf.st_mode & ALLPERMS) != 0620) e(0);
783 if (buf.st_uid != getuid()) e(0);
784 if (buf.st_gid != tty_gid) e(0);
785 }
786 }
787 }
788
789 if (closedir(dirp) < 0) e(0);
790
791 if (!seen_dot) e(0);
792 if (!seen_dotdot) e(0);
793 for (i = 0; i < nindex; i++)
794 if (seen_index[i] != present[i]) e(0);
795 }
796
797 /*
798 * Obtain a Unix98 PTY. Return an open file descriptor for the master side,
799 * and store the name of the slave side in 'tptr'.
800 */
801 static int
get_unix98_pty(char ** tptr)802 get_unix98_pty(char ** tptr)
803 {
804 int masterfd;
805
806 if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) < 0) e(0);
807
808 if (grantpt(masterfd) < 0) e(0);
809
810 /* This call is a no-op on MINIX3. */
811 if (unlockpt(masterfd) < 0) e(0);
812
813 if ((*tptr = ptsname(masterfd)) == NULL) e(0);
814
815 return masterfd;
816 }
817
818 /*
819 * Test for Unix98 PTY support and PTYFS.
820 */
821 static void
test77g(void)822 test77g(void)
823 {
824 char *tname;
825 struct stat buf;
826 size_t len;
827 int i, masterfd, slavefd, fd[3], array[3], present[3];
828
829 subtest = 7;
830
831 /*
832 * Test basic operation, and verify that the slave node disappears
833 * after both sides of the pseudo terminal have been closed. We check
834 * different combinations of open master and slave ends (with 'i'):
835 * 0) opening and closing the master only, 1) closing a slave before
836 * the master, and 2) closing the slave after the master.
837 */
838 for (i = 0; i <= 2; i++) {
839 masterfd = get_unix98_pty(&tname);
840
841 if (access(tname, R_OK | W_OK) < 0) e(0);
842
843 if (i > 0) {
844 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0)
845 e(0);
846
847 if (access(tname, R_OK | W_OK) < 0) e(0);
848
849 if (i > 1) {
850 if (close(masterfd) < 0) e(0);
851
852 masterfd = slavefd; /* ugly but saving code */
853 } else
854 if (close(slavefd) < 0) e(0);
855 }
856
857 if (access(tname, R_OK | W_OK) < 0) e(0);
858
859 if (close(masterfd) < 0) e(0);
860
861 if (access(tname, R_OK | W_OK) == 0) e(0);
862 }
863
864 /*
865 * Test whether we can open multiple pseudo terminals. We need to be
866 * able to open three PTYs. Verify that they are properly listed in
867 * the /dev/pts directory contents, and have proper attributes set.
868 */
869 test_getdents(0, NULL, NULL);
870
871 for (i = 0; i < 3; i++) {
872 fd[i] = get_unix98_pty(&tname);
873
874 /* Figure out the slave index number. */
875 len = strlen(_PATH_DEV_PTS);
876 if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0);
877 array[i] = atoi(&tname[len]);
878 present[i] = 1;
879 }
880
881 test_getdents(3, array, present);
882
883 if (close(fd[0]) < 0) e(0);
884 present[0] = 0;
885
886 test_getdents(3, array, present);
887
888 if (close(fd[2]) < 0) e(0);
889 present[2] = 0;
890
891 test_getdents(3, array, present);
892
893 if (close(fd[1]) < 0) e(0);
894 present[1] = 0;
895
896 test_getdents(3, array, present);
897
898 /*
899 * Test chmod(2) on a slave node, and multiple calls to grantpt(3).
900 * The first grantpt(3) call should create the slave node (we currently
901 * can not test this: the slave node may be created earlier as well,
902 * but we do not know its name), whereas subsequent grantpt(3) calls
903 * should reset its mode, uid, and gid. Testing the latter two and
904 * chown(2) on the slave node requires root, so we skip that part.
905 *
906 * Finally, NetBSD revokes access to existing slave file descriptors
907 * upon a call to grantpt(3). This is not a POSIX requirement, but
908 * NetBSD needs this for security reasons because it already creates
909 * the slave node when the master is opened (and it does not lock the
910 * slave until a call to unlockpt(3)). MINIX3 does not implement
911 * revocation this way, because the slave node is created only upon the
912 * call to grantpt(3), thus leaving no insecure window for the slave
913 * side between posix_openpt(3) and grantpt(3). While this behavior
914 * may be changed later, we test for the lack of revocation here now.
915 */
916 masterfd = get_unix98_pty(&tname);
917
918 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
919
920 if (stat(tname, &buf) != 0) e(0);
921 if (buf.st_mode != (S_IFCHR | 0620)) e(0);
922
923 if (chmod(tname, S_IFCHR | 0630) != 0) e(0);
924
925 if (stat(tname, &buf) != 0) e(0);
926 if (buf.st_mode != (S_IFCHR | 0630)) e(0);
927
928 if (grantpt(masterfd) != 0) e(0);
929
930 if (stat(tname, &buf) != 0) e(0);
931 if (buf.st_mode != (S_IFCHR | 0620)) e(0);
932
933 test_comm(masterfd, slavefd);
934
935 if (close(slavefd) < 0) e(0);
936 if (close(masterfd) < 0) e(0);
937
938 test_getdents(0, NULL, NULL);
939 }
940
941 /*
942 * Check that the given PTY index, which is in use for an old-style PTY, is not
943 * allocated through Unix98 PTY allocation. This test is not foolproof, but it
944 * does the job well enough.
945 */
946 static void
test_overlap(int m)947 test_overlap(int m)
948 {
949 char *tname;
950 size_t len;
951 int i, n, fd[MIN_PTYS];
952
953 for (i = 0; i < MIN_PTYS; i++) {
954 if ((fd[i] = posix_openpt(O_RDWR | O_NOCTTY)) < 0)
955 break; /* out of PTYs */
956 if (grantpt(fd[i]) < 0) e(0);
957 if (unlockpt(fd[i]) < 0) e(0);
958 if ((tname = ptsname(fd[i])) == NULL) e(0);
959
960 len = strlen(_PATH_DEV_PTS);
961 if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0);
962 n = atoi(&tname[len]);
963 if (n < 0 || n > 9) e(0);
964
965 if (m == n) e(0);
966 }
967
968 for (i--; i >= 0; i--)
969 if (close(fd[i]) < 0) e(0);
970 }
971
972 /*
973 * Test for mixing access to old-style and Unix98 PTYs. Since the PTY service
974 * internally shares the set of pseudo terminals between the two types, it has
975 * to implement checks to prevent that a PTY opened as one type is also
976 * accessed through the other type. We test some of those checks here.
977 */
978 static void
test77h(void)979 test77h(void)
980 {
981 char *tname, ptest[PATH_MAX], ttest[PATH_MAX];
982 struct sigaction act, oact;
983 size_t len;
984 int i, n, masterfd, slavefd;
985
986 subtest = 8;
987
988 /* We do not want to get SIGHUP signals in this test. */
989 memset(&act, 0, sizeof(act));
990 act.sa_handler = SIG_IGN;
991 if (sigaction(SIGHUP, &act, &oact) < 0) e(0);
992
993 /*
994 * Check that Unix98 PTYs cannot be accessed through old-style device
995 * nodes. We check different combinations of open master and
996 * slave ends for the Unix98 side (with 'i'): 0) opening and closing
997 * the master only, 1) closing a slave before the master, and 2)
998 * closing the slave after the master.
999 *
1000 * This test relies on the implementation aspect that /dev/ttypN and
1001 * /dev/pts/N (with N in the range 0..9) map to the same PTY. It also
1002 * relies on lack of concurrent PTY allocation outside the test.
1003 */
1004 for (i = 0; i <= 2; i++) {
1005 /* Open a Unix98 PTY and get the slave name. */
1006 masterfd = get_unix98_pty(&tname);
1007
1008 /* Figure out the slave index number. */
1009 len = strlen(_PATH_DEV_PTS);
1010 if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0);
1011 n = atoi(&tname[len]);
1012 if (n < 0 || n > 9) e(0);
1013
1014 /* Use this index number to create old-style device names. */
1015 snprintf(ptest, sizeof(ptest), _PATH_DEV "ptyp%u", n);
1016 snprintf(ttest, sizeof(ttest), _PATH_DEV "ttyp%u", n);
1017
1018 /*
1019 * Now make sure that opening the old-style master and slave
1020 * fails as long as either side of the Unix98 PTY is open.
1021 */
1022 if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1023 if (errno != EACCES && errno != EIO) e(0);
1024 if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1025 if (errno != EACCES && errno != EIO) e(0);
1026
1027 if (i > 0) {
1028 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0)
1029 e(0);
1030
1031 if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1032 if (errno != EACCES && errno != EIO) e(0);
1033 if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1034 if (errno != EACCES && errno != EIO) e(0);
1035
1036 if (close(slavefd) < 0) e(0);
1037
1038 if (i > 1) {
1039 if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1040 if (errno != EACCES && errno != EIO) e(0);
1041 if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1042 if (errno != EACCES && errno != EIO) e(0);
1043
1044 if ((slavefd =
1045 open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
1046
1047 if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1048 if (errno != EACCES && errno != EIO) e(0);
1049 if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1050 if (errno != EACCES && errno != EIO) e(0);
1051
1052 if (close(masterfd) < 0) e(0);
1053
1054 masterfd = slavefd; /* ugly but saving code */
1055 }
1056
1057 if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1058 if (errno != EACCES && errno != EIO) e(0);
1059 if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1060 if (errno != EACCES && errno != EIO) e(0);
1061 }
1062
1063 if (close(masterfd) < 0) e(0);
1064
1065 /*
1066 * Once both Unix98 sides are closed, the pseudo terminal can
1067 * be reused. Thus, opening the old-style master should now
1068 * succeed. However, it is possible that we do not have
1069 * permission to open the master at all.
1070 */
1071 if ((masterfd = open(ptest, O_RDWR | O_NOCTTY)) < 0 &&
1072 errno != EACCES) e(0);
1073
1074 if (masterfd >= 0 && close(masterfd) < 0) e(0);
1075 }
1076
1077 /*
1078 * The reverse test, which would check that old-style PTYs cannot be
1079 * accessed through Unix98 device nodes, is impossible to perform
1080 * properly without root privileges, as we would have to create device
1081 * nodes manually with mknod(2). All we can do here is ensure that if
1082 * an old-style PTY is opened, it will not also be allocated as a
1083 * Unix98 PTY. We do a rather basic check, but only if we can open an
1084 * old-style master at all. We check two closing orders (with 'i'):
1085 * 0) the slave first, 1) the master first. Here, we make the hard
1086 * assumption that the system supports at least four pseudo terminals,
1087 * of which at least one is currently free.
1088 */
1089 for (i = 0; i <= 1; i++) {
1090 for (n = 0; n < MIN_PTYS; n++) {
1091 snprintf(ptest, sizeof(ptest), _PATH_DEV "ptyp%u", n);
1092
1093 if ((masterfd = open(ptest, O_RDWR | O_NOCTTY)) >= 0)
1094 break;
1095 }
1096
1097 if (n >= MIN_PTYS)
1098 break;
1099
1100 test_overlap(n);
1101
1102 snprintf(ttest, sizeof(ttest), _PATH_DEV "ttyp%u", n);
1103
1104 /* We can do part of the test only if we can open the slave. */
1105 if ((slavefd = open(ttest, O_RDWR | O_NOCTTY)) >= 0) {
1106 test_overlap(n);
1107
1108 if (i > 0) {
1109 if (close(masterfd) < 0) e(0);
1110
1111 masterfd = slavefd; /* again, ugly */
1112 } else
1113 if (close(slavefd) < 0) e(0);
1114
1115 test_overlap(n);
1116 }
1117
1118 if (close(masterfd) < 0) e(0);
1119 }
1120
1121 if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
1122 }
1123
1124 int
main(int argc,char ** argv)1125 main(int argc, char **argv)
1126 {
1127 int i, m;
1128
1129 start(77);
1130
1131 if (argc == 2)
1132 m = atoi(argv[1]);
1133 else
1134 m = 0xFF;
1135
1136 for (i = 0; i < ITERATIONS; i++) {
1137 if (m & 0x01) test77a();
1138 if (m & 0x02) test77b();
1139 if (m & 0x04) test77c();
1140 if (m & 0x08) test77d();
1141 if (m & 0x10) test77e();
1142 if (m & 0x20) test77f();
1143 if (m & 0x40) test77g();
1144 if (m & 0x80) test77h();
1145 }
1146
1147 quit();
1148 }
1149