1 /* Copyright (C) 2017-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public License as
6    published by the Free Software Foundation; either version 2.1 of the
7    License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; see the file COPYING.LIB.  If
16    not, see <https://www.gnu.org/licenses/>.  */
17 
18 #include <dirent.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <sched.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/mount.h>
27 #include <sys/prctl.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <sys/resource.h>
31 #include <unistd.h>
32 
33 #include <support/check.h>
34 #include <support/namespace.h>
35 #include <support/support.h>
36 #include <support/temp_file.h>
37 #include <support/test-driver.h>
38 #include <support/xunistd.h>
39 
40 /* generic utilities */
41 
42 #define VERIFY(expr)                                                    \
43   do {                                                                  \
44     if (!(expr))                                                        \
45       {                                                                 \
46         printf ("error: %s:%d: %s: %m\n",                               \
47                 __FILE__, __LINE__, #expr);                             \
48         exit (1);                                                       \
49       }                                                                 \
50   } while (0)
51 
52 static void
touch(const char * path,mode_t mode)53 touch (const char *path, mode_t mode)
54 {
55   xclose (xopen (path, O_WRONLY|O_CREAT|O_NOCTTY, mode));
56 }
57 
58 static size_t
trim_prefix(char * str,size_t str_len,const char * prefix)59 trim_prefix (char *str, size_t str_len, const char *prefix)
60 {
61   size_t prefix_len = strlen (prefix);
62   if (str_len > prefix_len && memcmp (str, prefix, prefix_len) == 0)
63     {
64       memmove (str, str + prefix_len, str_len - prefix_len);
65       return str_len - prefix_len;
66     }
67   return str_len;
68 }
69 
70 /* returns a pointer to static storage */
71 static char *
proc_fd_readlink(const char * linkname)72 proc_fd_readlink (const char *linkname)
73 {
74   static char target[PATH_MAX+1];
75   ssize_t target_len = readlink (linkname, target, PATH_MAX);
76   VERIFY (target_len > 0);
77   target_len = trim_prefix (target, target_len, "(unreachable)");
78   target[target_len] = '\0';
79   return target;
80 }
81 
82 /* plain ttyname runner */
83 
84 struct result
85 {
86   const char *name;
87   int err;
88 };
89 
90 /* strings in result structure are in static storage */
91 static struct result
run_ttyname(int fd)92 run_ttyname (int fd)
93 {
94   struct result ret;
95   errno = 0;
96   ret.name = ttyname (fd);
97   ret.err = errno;
98   return ret;
99 }
100 
101 static bool
eq_ttyname(struct result actual,struct result expected)102 eq_ttyname (struct result actual, struct result expected)
103 {
104   char *actual_name, *expected_name;
105 
106   if ((actual.err == expected.err)
107       && (!actual.name == !expected.name)
108       && (actual.name ? strcmp (actual.name, expected.name) == 0 : true))
109     {
110       if (expected.name)
111         expected_name = xasprintf ("\"%s\"", expected.name);
112       else
113 	expected_name = xstrdup ("NULL");
114 
115       printf ("info:      ttyname: PASS {name=%s, errno=%d}\n",
116 	      expected_name, expected.err);
117 
118       free (expected_name);
119       return true;
120     }
121 
122   if (actual.name)
123     actual_name = xasprintf ("\"%s\"", actual.name);
124   else
125     actual_name = xstrdup ("NULL");
126 
127   if (expected.name)
128     expected_name = xasprintf ("\"%s\"", expected.name);
129   else
130     expected_name = xstrdup ("NULL");
131 
132   printf ("error:     ttyname: actual {name=%s, errno=%d} != expected {name=%s, errno=%d}\n",
133 	  actual_name, actual.err,
134 	  expected_name, expected.err);
135 
136   free (actual_name);
137   free (expected_name);
138   return false;
139 }
140 
141 /* ttyname_r runner */
142 
143 struct result_r
144 {
145   const char *name;
146   int ret;
147   int err;
148 };
149 
150 /* strings in result structure are in static storage */
151 static struct result_r
run_ttyname_r(int fd)152 run_ttyname_r (int fd)
153 {
154   static char buf[TTY_NAME_MAX];
155 
156   struct result_r ret;
157   errno = 0;
158   ret.ret = ttyname_r (fd, buf, TTY_NAME_MAX);
159   ret.err = errno;
160   if (ret.ret == 0)
161     ret.name = buf;
162   else
163     ret.name = NULL;
164   return ret;
165 }
166 
167 static bool
eq_ttyname_r(struct result_r actual,struct result_r expected)168 eq_ttyname_r (struct result_r actual, struct result_r expected)
169 {
170   char *actual_name, *expected_name;
171 
172   if ((actual.err == expected.err)
173       && (actual.ret == expected.ret)
174       && (!actual.name == !expected.name)
175       && (actual.name ? strcmp (actual.name, expected.name) == 0 : true))
176     {
177       if (expected.name)
178         expected_name = xasprintf ("\"%s\"", expected.name);
179       else
180         expected_name = xstrdup ("NULL");
181 
182       printf ("info:      ttyname_r: PASS {name=%s, ret=%d, errno=%d}\n",
183               expected_name, expected.ret, expected.err);
184 
185       free (expected_name);
186       return true;
187     }
188 
189   if (actual.name)
190     actual_name = xasprintf ("\"%s\"", actual.name);
191   else
192     actual_name = xstrdup ("NULL");
193 
194   if (expected.name)
195     expected_name = xasprintf ("\"%s\"", expected.name);
196   else
197     expected_name = xstrdup ("NULL");
198 
199   printf ("error:     ttyname_r: actual {name=%s, ret=%d, errno=%d} != expected {name=%s, ret=%d, errno=%d}\n",
200 	  actual_name, actual.ret, actual.err,
201 	  expected_name, expected.ret, expected.err);
202 
203   free (actual_name);
204   free (expected_name);
205   return false;
206 }
207 
208 /* combined runner */
209 
210 static bool
doit(int fd,const char * testname,struct result_r expected_r)211 doit (int fd, const char *testname, struct result_r expected_r)
212 {
213   struct result expected = {.name=expected_r.name, .err=expected_r.ret};
214   bool ret = true;
215 
216   printf ("info:    testcase: %s\n", testname);
217 
218   if (!eq_ttyname (run_ttyname (fd), expected))
219     ret = false;
220   if (!eq_ttyname_r (run_ttyname_r (fd), expected_r))
221     ret = false;
222 
223   if (!ret)
224     support_record_failure ();
225 
226   return ret;
227 }
228 
229 /* chroot setup */
230 
231 static char *chrootdir;
232 
233 static void
prepare(int argc,char ** argv)234 prepare (int argc, char **argv)
235 {
236   chrootdir = xasprintf ("%s/tst-ttyname-XXXXXX", test_dir);
237   if (mkdtemp (chrootdir) == NULL)
238     FAIL_EXIT1 ("mkdtemp (\"%s\"): %m", chrootdir);
239   add_temp_file (chrootdir);
240 }
241 #define PREPARE prepare
242 
243 /* Adjust the file limit so that we have a chance to open PTY.  */
244 static void
adjust_file_limit(const char * pty)245 adjust_file_limit (const char *pty)
246 {
247   int number = -1;
248   if (sscanf (pty, "/dev/pts/%d", &number) != 1 || number < 0)
249     FAIL_EXIT1 ("invalid PTY name: \"%s\"", pty);
250 
251   /* Add a few additional descriptors to cover standard I/O streams
252      etc.  */
253   rlim_t desired_limit = number + 10;
254 
255   struct rlimit lim;
256   if (getrlimit (RLIMIT_NOFILE, &lim) != 0)
257     FAIL_EXIT1 ("getrlimit (RLIMIT_NOFILE): %m");
258   if (lim.rlim_cur < desired_limit)
259     {
260       printf ("info: adjusting RLIMIT_NOFILE from %llu to %llu\n",
261 	      (unsigned long long int) lim.rlim_cur,
262 	      (unsigned long long int) desired_limit);
263       lim.rlim_cur = desired_limit;
264       if (setrlimit (RLIMIT_NOFILE, &lim) != 0)
265 	printf ("warning: setrlimit (RLIMIT_NOFILE) failed: %m\n");
266     }
267 }
268 
269 /* These chroot setup functions put the TTY at at "/console" (where it
270    won't be found by ttyname), and create "/dev/console" as an
271    ordinary file.  This way, it's easier to write test-cases that
272    expect ttyname to fail; test-cases that expect it to succeed need
273    to explicitly remount it at "/dev/console".  */
274 
275 static int
do_in_chroot_1(int (* cb)(const char *,int))276 do_in_chroot_1 (int (*cb)(const char *, int))
277 {
278   printf ("info:  entering chroot 1\n");
279 
280   /* Open the PTS that we'll be testing on.  */
281   int master;
282   char *slavename;
283   master = posix_openpt (O_RDWR|O_NOCTTY|O_NONBLOCK);
284   if (master < 0)
285     {
286       if (errno == ENOENT)
287 	FAIL_UNSUPPORTED ("posix_openpt: %m");
288       else
289 	FAIL_EXIT1 ("posix_openpt: %m");
290     }
291   VERIFY ((slavename = ptsname (master)));
292   VERIFY (unlockpt (master) == 0);
293   if (strncmp (slavename, "/dev/pts/", 9) != 0)
294     FAIL_UNSUPPORTED ("slave pseudo-terminal is not under /dev/pts/: %s",
295                       slavename);
296   adjust_file_limit (slavename);
297   int slave = xopen (slavename, O_RDWR, 0);
298   if (!doit (slave, "basic smoketest",
299              (struct result_r){.name=slavename, .ret=0, .err=0}))
300     return 1;
301 
302   pid_t pid = xfork ();
303   if (pid == 0)
304     {
305       xclose (master);
306 
307       if (!support_enter_mount_namespace ())
308 	FAIL_UNSUPPORTED ("could not enter new mount namespace");
309 
310       VERIFY (mount ("tmpfs", chrootdir, "tmpfs", 0, "mode=755") == 0);
311       VERIFY (chdir (chrootdir) == 0);
312 
313       xmkdir ("proc", 0755);
314       xmkdir ("dev", 0755);
315       xmkdir ("dev/pts", 0755);
316 
317       VERIFY (mount ("/proc", "proc", NULL, MS_BIND|MS_REC, NULL) == 0);
318       VERIFY (mount ("devpts", "dev/pts", "devpts",
319                      MS_NOSUID|MS_NOEXEC,
320                      "newinstance,ptmxmode=0666,mode=620") == 0);
321       VERIFY (symlink ("pts/ptmx", "dev/ptmx") == 0);
322 
323       touch ("console", 0);
324       touch ("dev/console", 0);
325       VERIFY (mount (slavename, "console", NULL, MS_BIND, NULL) == 0);
326 
327       xchroot (".");
328 
329       char *linkname = xasprintf ("/proc/self/fd/%d", slave);
330       char *target = proc_fd_readlink (linkname);
331       VERIFY (strcmp (target, slavename) == 0);
332       free (linkname);
333 
334       _exit (cb (slavename, slave));
335     }
336   int status;
337   xwaitpid (pid, &status, 0);
338   VERIFY (WIFEXITED (status));
339   xclose (master);
340   xclose (slave);
341   return WEXITSTATUS (status);
342 }
343 
344 static int
do_in_chroot_2(int (* cb)(const char *,int))345 do_in_chroot_2 (int (*cb)(const char *, int))
346 {
347   printf ("info:  entering chroot 2\n");
348 
349   int pid_pipe[2];
350   xpipe (pid_pipe);
351   int exit_pipe[2];
352   xpipe (exit_pipe);
353 
354   /* Open the PTS that we'll be testing on.  */
355   int master;
356   char *slavename;
357   VERIFY ((master = posix_openpt (O_RDWR|O_NOCTTY|O_NONBLOCK)) >= 0);
358   VERIFY ((slavename = ptsname (master)));
359   VERIFY (unlockpt (master) == 0);
360   if (strncmp (slavename, "/dev/pts/", 9) != 0)
361     FAIL_UNSUPPORTED ("slave pseudo-terminal is not under /dev/pts/: %s",
362                       slavename);
363   adjust_file_limit (slavename);
364   /* wait until in a new mount ns to open the slave */
365 
366   /* enable `wait`ing on grandchildren */
367   VERIFY (prctl (PR_SET_CHILD_SUBREAPER, 1) == 0);
368 
369   pid_t pid = xfork (); /* outer child */
370   if (pid == 0)
371     {
372       xclose (master);
373       xclose (pid_pipe[0]);
374       xclose (exit_pipe[1]);
375 
376       if (!support_enter_mount_namespace ())
377 	FAIL_UNSUPPORTED ("could not enter new mount namespace");
378 
379       int slave = xopen (slavename, O_RDWR, 0);
380       if (!doit (slave, "basic smoketest",
381                  (struct result_r){.name=slavename, .ret=0, .err=0}))
382         _exit (1);
383 
384       VERIFY (mount ("tmpfs", chrootdir, "tmpfs", 0, "mode=755") == 0);
385       VERIFY (chdir (chrootdir) == 0);
386 
387       xmkdir ("proc", 0755);
388       xmkdir ("dev", 0755);
389       xmkdir ("dev/pts", 0755);
390 
391       VERIFY (mount ("devpts", "dev/pts", "devpts",
392                      MS_NOSUID|MS_NOEXEC,
393                      "newinstance,ptmxmode=0666,mode=620") == 0);
394       VERIFY (symlink ("pts/ptmx", "dev/ptmx") == 0);
395 
396       touch ("console", 0);
397       touch ("dev/console", 0);
398       VERIFY (mount (slavename, "console", NULL, MS_BIND, NULL) == 0);
399 
400       xchroot (".");
401 
402       if (unshare (CLONE_NEWNS | CLONE_NEWPID) < 0)
403         FAIL_UNSUPPORTED ("could not enter new PID namespace");
404       pid = xfork (); /* inner child */
405       if (pid == 0)
406         {
407           xclose (pid_pipe[1]);
408 
409           /* wait until the outer child has exited */
410           char c;
411           VERIFY (read (exit_pipe[0], &c, 1) == 0);
412           xclose (exit_pipe[0]);
413 
414           VERIFY (mount ("proc", "/proc", "proc",
415                          MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) == 0);
416 
417           char *linkname = xasprintf ("/proc/self/fd/%d", slave);
418           char *target = proc_fd_readlink (linkname);
419           VERIFY (strcmp (target, strrchr (slavename, '/')) == 0);
420           free (linkname);
421 
422           _exit (cb (slavename, slave));
423         }
424       xwrite (pid_pipe[1], &pid, sizeof pid);
425       _exit (0);
426     }
427   xclose (pid_pipe[1]);
428   xclose (exit_pipe[0]);
429   xclose (exit_pipe[1]);
430 
431   /* wait for the outer child */
432   int status;
433   xwaitpid (pid, &status, 0);
434   VERIFY (WIFEXITED (status));
435   int ret = WEXITSTATUS (status);
436   if (ret != 0)
437     return ret;
438 
439   /* set 'pid' to the inner child */
440   VERIFY (read (pid_pipe[0], &pid, sizeof pid) == sizeof pid);
441   xclose (pid_pipe[0]);
442 
443   /* wait for the inner child */
444   xwaitpid (pid, &status, 0);
445   VERIFY (WIFEXITED (status));
446   xclose (master);
447   return WEXITSTATUS (status);
448 }
449 
450 /* main test */
451 
452 static int
run_chroot_tests(const char * slavename,int slave)453 run_chroot_tests (const char *slavename, int slave)
454 {
455   struct stat st;
456   bool ok = true;
457 
458   /* There are 3 groups of tests here.  The first group fairly
459      generically does things known to mess up ttyname, and verifies
460      that ttyname copes correctly.  The remaining groups are
461      increasingly convoluted, as we target specific parts of ttyname
462      to try to confuse.  */
463 
464   /* Basic tests that it doesn't get confused by multiple devpts
465      instances.  */
466   {
467     VERIFY (stat (slavename, &st) < 0); /* sanity check */
468     if (!doit (slave, "no conflict, no match",
469                (struct result_r){.name=NULL, .ret=ENODEV, .err=ENODEV}))
470       ok = false;
471     VERIFY (mount ("/console", "/dev/console", NULL, MS_BIND, NULL) == 0);
472     if (!doit (slave, "no conflict, console",
473                (struct result_r){.name="/dev/console", .ret=0, .err=0}))
474       ok = false;
475     VERIFY (umount ("/dev/console") == 0);
476 
477     /* Keep creating PTYs until we we get a name collision.  */
478     while (true)
479       {
480 	if (stat (slavename, &st) == 0)
481 	  break;
482 	if (posix_openpt (O_RDWR|O_NOCTTY|O_NONBLOCK) < 0)
483 	  {
484 	    if (errno == ENOSPC || errno == EMFILE || errno == ENFILE)
485 	      FAIL_UNSUPPORTED ("cannot re-create PTY \"%s\" in chroot: %m"
486 				" (consider increasing limits)", slavename);
487 	    else
488 	      FAIL_EXIT1 ("cannot re-create PTY \"%s\" chroot: %m", slavename);
489 	  }
490       }
491 
492     if (!doit (slave, "conflict, no match",
493                (struct result_r){.name=NULL, .ret=ENODEV, .err=ENODEV}))
494       ok = false;
495     VERIFY (mount ("/console", "/dev/console", NULL, MS_BIND, NULL) == 0);
496     if (!doit (slave, "conflict, console",
497                (struct result_r){.name="/dev/console", .ret=0, .err=0}))
498       ok = false;
499     VERIFY (umount ("/dev/console") == 0);
500   }
501 
502   /* The first tests kinda assumed that they hit certain code-paths
503      based on assuming that the readlink target is 'slavename', but
504      that's not quite always true.  They're still a good preliminary
505      sanity check, so keep them, but let's add tests that make sure
506      that those code-paths are hit by doing a readlink ourself.  */
507   {
508     char *linkname = xasprintf ("/proc/self/fd/%d", slave);
509     char *target = proc_fd_readlink (linkname);
510     free (linkname);
511     /* Depeding on how we set up the chroot, the kernel may or may not
512        trim the leading path to the target (it may give us "/6",
513        instead of "/dev/pts/6").  We test it both ways (do_in_chroot_1
514        and do_in_chroot_2).  This test group relies on the target
515        existing, so guarantee that it does exist by creating it if
516        necessary.  */
517     if (stat (target, &st) < 0)
518       {
519         VERIFY (errno == ENOENT);
520         touch (target, 0);
521       }
522 
523     VERIFY (mount ("/console", "/dev/console", NULL, MS_BIND, NULL) == 0);
524     VERIFY (mount ("/console", target, NULL, MS_BIND, NULL) == 0);
525     if (!doit (slave, "with readlink target",
526                (struct result_r){.name=target, .ret=0, .err=0}))
527       ok = false;
528     VERIFY (umount (target) == 0);
529     VERIFY (umount ("/dev/console") == 0);
530 
531     VERIFY (mount ("/console", "/dev/console", NULL, MS_BIND, NULL) == 0);
532     VERIFY (mount (slavename, target, NULL, MS_BIND, NULL) == 0);
533     if (!doit (slave, "with readlink trap; fallback",
534                (struct result_r){.name="/dev/console", .ret=0, .err=0}))
535       ok = false;
536     VERIFY (umount (target) == 0);
537     VERIFY (umount ("/dev/console") == 0);
538 
539     VERIFY (mount (slavename, target, NULL, MS_BIND, NULL) == 0);
540     if (!doit (slave, "with readlink trap; no fallback",
541                (struct result_r){.name=NULL, .ret=ENODEV, .err=ENODEV}))
542       ok = false;
543     VERIFY (umount (target) == 0);
544   }
545 
546   /* This test makes sure that everything still works OK if readdir
547      finds a pseudo-match before and/or after the actual match.  Now,
548      to do that, we need to control that readdir finds the
549      pseudo-matches before and after the actual match; and there's no
550      good way to control that order in absence of whitebox testing.
551      So, just create 3 files, then use opendir/readdir to see what
552      order they are in, and assign meaning based on that order, not by
553      name; assigning the first to be a pseudo-match, the second to be
554      the actual match, and the third to be a pseudo-match.  This
555      assumes that (on tmpfs) ordering within the directory is stable
556      in the absence of modification, which seems reasonably safe.  */
557   {
558     /* since we're testing the fallback search, disable the readlink
559        happy-path */
560     VERIFY (umount2 ("/proc", MNT_DETACH) == 0);
561 
562     touch ("/dev/console1", 0);
563     touch ("/dev/console2", 0);
564     touch ("/dev/console3", 0);
565 
566     char *c[3];
567     int ci = 0;
568     DIR *dirstream = opendir ("/dev");
569     VERIFY (dirstream != NULL);
570     struct dirent *d;
571     while ((d = readdir (dirstream)) != NULL && ci < 3)
572       {
573         if (strcmp (d->d_name, "console1")
574             && strcmp (d->d_name, "console2")
575             && strcmp (d->d_name, "console3") )
576           continue;
577         c[ci++] = xasprintf ("/dev/%s", d->d_name);
578       }
579     VERIFY (ci == 3);
580     VERIFY (closedir (dirstream) == 0);
581 
582     VERIFY (mount (slavename, c[0], NULL, MS_BIND, NULL) == 0);
583     VERIFY (mount ("/console", c[1], NULL, MS_BIND, NULL) == 0);
584     VERIFY (mount (slavename, c[2], NULL, MS_BIND, NULL) == 0);
585     VERIFY (umount2 ("/dev/pts", MNT_DETACH) == 0);
586     if (!doit (slave, "with search-path trap",
587                (struct result_r){.name=c[1], .ret=0, .err=0}))
588       ok = false;
589     for (int i = 0; i < 3; i++)
590       {
591         VERIFY (umount (c[i]) == 0);
592         VERIFY (unlink (c[i]) == 0);
593         free (c[i]);
594       }
595   }
596 
597   return ok ? 0 : 1;
598 }
599 
600 static int
do_test(void)601 do_test (void)
602 {
603   support_become_root ();
604 
605   int ret1 = do_in_chroot_1 (run_chroot_tests);
606   if (ret1 == EXIT_UNSUPPORTED)
607     return ret1;
608 
609   int ret2 = do_in_chroot_2 (run_chroot_tests);
610   if (ret2 == EXIT_UNSUPPORTED)
611     return ret2;
612 
613   return  ret1 | ret2;
614 }
615 
616 #include <support/test-driver.c>
617