(root)/
glibc-2.38/
posix/
tst-spawn5.c
       1  /* Tests for posix_spawn signal handling.
       2     Copyright (C) 2021-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <http://www.gnu.org/licenses/>.  */
      18  
      19  #include <stdio.h>
      20  #include <stdlib.h>
      21  #include <getopt.h>
      22  #include <spawn.h>
      23  #include <fcntl.h>
      24  #include <sys/wait.h>
      25  #include <dirent.h>
      26  #include <stdbool.h>
      27  #include <errno.h>
      28  #include <limits.h>
      29  
      30  #include <support/check.h>
      31  #include <support/xunistd.h>
      32  #include <support/support.h>
      33  
      34  #include <arch-fd_to_filename.h>
      35  #include <array_length.h>
      36  
      37  /* Nonzero if the program gets called via `exec'.  */
      38  static int restart;
      39  
      40  /* Hold the four initial argument used to respawn the process, plus
      41     the extra '--direct' and '--restart', and a final NULL.  */
      42  static char *initial_argv[7];
      43  static int initial_argv_count;
      44  
      45  #define CMDLINE_OPTIONS \
      46    { "restart", no_argument, &restart, 1 },
      47  
      48  #define NFDS 100
      49  
      50  static int
      51  parse_fd (const char *str)
      52  {
      53    char *endptr;
      54    long unsigned int fd = strtoul (str, &endptr, 10);
      55    if (*endptr != '\0' || fd > INT_MAX)
      56      FAIL_EXIT1 ("invalid file descriptor value: %s", str);
      57    return fd;
      58  }
      59  
      60  /* Called on process re-execution.  The arguments are the expected opened
      61     file descriptors.  */
      62  _Noreturn static void
      63  handle_restart (int argc, char *argv[])
      64  {
      65    TEST_VERIFY (argc > 0);
      66    int lowfd = parse_fd (argv[0]);
      67  
      68    size_t nfds = argc > 1 ? argc - 1 : 0;
      69    struct fd_t
      70    {
      71      int fd;
      72      _Bool found;
      73    } *fds = xmalloc (sizeof (struct fd_t) * nfds);
      74    for (int i = 0; i < nfds; i++)
      75      {
      76        fds[i].fd = parse_fd (argv[i + 1]);
      77        fds[i].found = false;
      78      }
      79  
      80    DIR *dirp = opendir (FD_TO_FILENAME_PREFIX);
      81    if (dirp == NULL)
      82      FAIL_EXIT1 ("opendir (\"" FD_TO_FILENAME_PREFIX "\"): %m");
      83  
      84    while (true)
      85      {
      86        errno = 0;
      87        struct dirent64 *e = readdir64 (dirp);
      88        if (e == NULL)
      89          {
      90            if (errno != 0)
      91              FAIL_EXIT1 ("readdir: %m");
      92            break;
      93          }
      94  
      95        if (e->d_name[0] == '.')
      96          continue;
      97  
      98        char *endptr;
      99        long int fd = strtol (e->d_name, &endptr, 10);
     100        if (*endptr != '\0' || fd < 0 || fd > INT_MAX)
     101          FAIL_EXIT1 ("readdir: invalid file descriptor name: /proc/self/fd/%s",
     102                      e->d_name);
     103  
     104        /* Ignore the descriptors not in the range of the opened files.  */
     105        if (fd < lowfd || fd == dirfd (dirp))
     106  	continue;
     107  
     108        bool found = false;
     109        for (int i = 0; i < nfds; i++)
     110  	if (fds[i].fd == fd)
     111  	  fds[i].found = found = true;
     112  
     113        if (!found)
     114  	{
     115  	  char *path = xasprintf ("/proc/self/fd/%s", e->d_name);
     116  	  char *resolved = xreadlink (path);
     117  	  FAIL_EXIT1 ("unexpected open file descriptor %ld: %s", fd, resolved);
     118  	}
     119      }
     120    closedir (dirp);
     121  
     122    for (int i = 0; i < nfds; i++)
     123      if (!fds[i].found)
     124        FAIL_EXIT1 ("file descriptor %d not opened", fds[i].fd);
     125  
     126    free (fds);
     127  
     128    exit (EXIT_SUCCESS);
     129  }
     130  
     131  static void
     132  spawn_closefrom_test (posix_spawn_file_actions_t *fa, int lowfd, int highfd,
     133  		      int *extrafds, size_t nextrafds)
     134  {
     135    /* 3 or 7 elements from initial_argv:
     136         + path to ld.so          optional
     137         + --library-path         optional
     138         + the library path       optional
     139         + application name
     140         + --direct
     141         + --restart
     142         + lowest opened file descriptor
     143         + up to 2 * maximum_fd arguments (the expected open file descriptors),
     144  	 plus NULL.  */
     145  
     146    int argv_size = initial_argv_count + 2 * NFDS + 1;
     147    char *args[argv_size];
     148    int argc = 0;
     149  
     150    for (char **arg = initial_argv; *arg != NULL; arg++)
     151      args[argc++] = *arg;
     152  
     153    args[argc++] = xasprintf ("%d", lowfd);
     154  
     155    for (int i = lowfd; i < highfd; i++)
     156      args[argc++] = xasprintf ("%d", i);
     157  
     158    for (int i = 0; i < nextrafds; i++)
     159      args[argc++] = xasprintf ("%d", extrafds[i]);
     160  
     161    args[argc] = NULL;
     162    TEST_VERIFY (argc < argv_size);
     163  
     164    pid_t pid;
     165    int status;
     166  
     167    TEST_COMPARE (posix_spawn (&pid, args[0], fa, NULL, args, environ), 0);
     168    TEST_COMPARE (xwaitpid (pid, &status, 0), pid);
     169    TEST_VERIFY (WIFEXITED (status));
     170    TEST_VERIFY (!WIFSIGNALED (status));
     171    TEST_COMPARE (WEXITSTATUS (status), 0);
     172  }
     173  
     174  static void
     175  do_test_closefrom (void)
     176  {
     177    int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600);
     178    const int half_fd = lowfd + NFDS / 2;
     179  
     180    /* Close half of the descriptors and check result.  */
     181    {
     182      posix_spawn_file_actions_t fa;
     183      TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0);
     184  
     185      int ret = posix_spawn_file_actions_addclosefrom_np (&fa, half_fd);
     186      if (ret == EINVAL)
     187        /* Hurd currently does not support closefrom fileaction.  */
     188        FAIL_UNSUPPORTED ("posix_spawn_file_actions_addclosefrom_np unsupported");
     189      TEST_COMPARE (ret, 0);
     190  
     191      spawn_closefrom_test (&fa, lowfd, half_fd, NULL, 0);
     192  
     193      TEST_COMPARE (posix_spawn_file_actions_destroy (&fa), 0);
     194    }
     195  
     196    /* Create some gaps, close up to a threshold, and check result.  */
     197    xclose (lowfd + 57);
     198    xclose (lowfd + 78);
     199    xclose (lowfd + 81);
     200    xclose (lowfd + 82);
     201    xclose (lowfd + 84);
     202    xclose (lowfd + 90);
     203  
     204    {
     205      posix_spawn_file_actions_t fa;
     206      TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0);
     207  
     208      TEST_COMPARE (posix_spawn_file_actions_addclosefrom_np (&fa, half_fd), 0);
     209  
     210      spawn_closefrom_test (&fa, lowfd, half_fd, NULL, 0);
     211  
     212      TEST_COMPARE (posix_spawn_file_actions_destroy (&fa), 0);
     213    }
     214  
     215    /* Close the remaining but the last one.  */
     216    {
     217      posix_spawn_file_actions_t fa;
     218      TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0);
     219  
     220      TEST_COMPARE (posix_spawn_file_actions_addclosefrom_np (&fa, lowfd + 1), 0);
     221  
     222      spawn_closefrom_test (&fa, lowfd, lowfd + 1, NULL, 0);
     223  
     224      TEST_COMPARE (posix_spawn_file_actions_destroy (&fa), 0);
     225    }
     226  
     227    /* Close everything.  */
     228    {
     229      posix_spawn_file_actions_t fa;
     230      TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0);
     231  
     232      TEST_COMPARE (posix_spawn_file_actions_addclosefrom_np (&fa, lowfd), 0);
     233  
     234      spawn_closefrom_test (&fa, lowfd, lowfd, NULL, 0);
     235  
     236      TEST_COMPARE (posix_spawn_file_actions_destroy (&fa), 0);
     237    }
     238  
     239    /* Close a range and add some file actions.  */
     240    {
     241      posix_spawn_file_actions_t fa;
     242      TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0);
     243  
     244      TEST_COMPARE (posix_spawn_file_actions_addclosefrom_np (&fa, lowfd + 1), 0);
     245      TEST_COMPARE (posix_spawn_file_actions_addopen (&fa, lowfd, "/dev/null",
     246  						    0666, O_RDONLY), 0);
     247      TEST_COMPARE (posix_spawn_file_actions_adddup2 (&fa, lowfd, lowfd + 1), 0);
     248      TEST_COMPARE (posix_spawn_file_actions_addopen (&fa, lowfd, "/dev/null",
     249  						    0666, O_RDONLY), 0);
     250  
     251      spawn_closefrom_test (&fa, lowfd, lowfd, (int[]){lowfd, lowfd + 1}, 2);
     252  
     253      TEST_COMPARE (posix_spawn_file_actions_destroy (&fa), 0);
     254    }
     255  }
     256  
     257  static int
     258  do_test (int argc, char *argv[])
     259  {
     260    /* We must have either:
     261  
     262       - one or four parameters if called initially:
     263         + argv[1]: path for ld.so        optional
     264         + argv[2]: "--library-path"      optional
     265         + argv[3]: the library path      optional
     266         + argv[4]: the application name
     267  
     268       - six parameters left if called through re-execution:
     269         + argv[1]: the application name
     270         + argv[2]: the lowest file descriptor expected
     271         + argv[3]: first expected open file descriptor   optional
     272         + argv[n]: last expected open file descriptor   optional
     273  
     274       * When built with --enable-hardcoded-path-in-tests or issued without
     275         using the loader directly.  */
     276  
     277    if (restart)
     278      /* Ignore the application name. */
     279      handle_restart (argc - 1, &argv[1]);
     280  
     281    TEST_VERIFY_EXIT (argc == 2 || argc == 5);
     282  
     283    int i;
     284  
     285    for (i = 0; i < argc - 1; i++)
     286      initial_argv[i] = argv[i + 1];
     287    initial_argv[i++] = (char *) "--direct";
     288    initial_argv[i++] = (char *) "--restart";
     289  
     290    initial_argv_count = i;
     291  
     292    do_test_closefrom ();
     293  
     294    return 0;
     295  }
     296  
     297  #define TEST_FUNCTION_ARGV do_test
     298  #include <support/test-driver.c>