(root)/
glibc-2.38/
io/
tst-close_range.c
       1  /* Test for the close_range system call.
       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     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <dirent.h>
      20  #include <errno.h>
      21  #include <fcntl.h>
      22  #include <limits.h>
      23  #include <getopt.h>
      24  #include <signal.h>
      25  #include <stdbool.h>
      26  #include <stdlib.h>
      27  #include <stdint.h>
      28  
      29  #include <array_length.h>
      30  #include <support/capture_subprocess.h>
      31  #include <support/check.h>
      32  #include <support/descriptors.h>
      33  #include <support/support.h>
      34  #include <support/xsched.h>
      35  #include <support/xunistd.h>
      36  
      37  #define NFDS 100
      38  
      39  static void
      40  close_range_test_max_upper_limit (void)
      41  {
      42    struct support_descriptors *descrs = support_descriptors_list ();
      43  
      44    int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600);
      45  
      46    {
      47      int r = close_range (lowfd, ~0U, 0);
      48      if (r == -1 && errno == ENOSYS)
      49        FAIL_UNSUPPORTED ("close_range not supported");
      50      TEST_COMPARE (r, 0);
      51    }
      52  
      53    support_descriptors_check (descrs);
      54    support_descriptors_free (descrs);
      55  }
      56  
      57  static void
      58  close_range_test_common (int lowfd, unsigned int flags)
      59  {
      60    const int maximum_fd = lowfd + NFDS - 1;
      61    const int half_fd = lowfd + NFDS / 2;
      62    const int gap_1 = maximum_fd - 8;
      63  
      64    /* Close half of the descriptors and check result.  */
      65    TEST_COMPARE (close_range (lowfd, half_fd, flags), 0);
      66    for (int i = lowfd; i <= half_fd; i++)
      67      {
      68        TEST_COMPARE (fcntl (i, F_GETFL), -1);
      69        TEST_COMPARE (errno, EBADF);
      70      }
      71    for (int i = half_fd + 1; i < maximum_fd; i++)
      72      TEST_VERIFY (fcntl (i, F_GETFL) > -1);
      73  
      74    /* Create some gaps, close up to a threshold, and check result.  */
      75    xclose (lowfd + 57);
      76    xclose (lowfd + 78);
      77    xclose (lowfd + 81);
      78    xclose (lowfd + 82);
      79    xclose (lowfd + 84);
      80    xclose (lowfd + 90);
      81  
      82    TEST_COMPARE (close_range (half_fd + 1, gap_1, flags), 0);
      83    for (int i = half_fd + 1; i < gap_1; i++)
      84      {
      85        TEST_COMPARE (fcntl (i, F_GETFL), -1);
      86        TEST_COMPARE (errno, EBADF);
      87      }
      88    for (int i = gap_1 + 1; i < maximum_fd; i++)
      89      TEST_VERIFY (fcntl (i, F_GETFL) > -1);
      90  
      91    /* Close the remaining but the last one.  */
      92    TEST_COMPARE (close_range (gap_1 + 1, maximum_fd - 1, flags), 0);
      93    for (int i = gap_1 + 1; i < maximum_fd - 1; i++)
      94      {
      95        TEST_COMPARE (fcntl (i, F_GETFL), -1);
      96        TEST_COMPARE (errno, EBADF);
      97      }
      98    TEST_VERIFY (fcntl (maximum_fd, F_GETFL) > -1);
      99  
     100    /* Close the last one.  */
     101    TEST_COMPARE (close_range (maximum_fd, maximum_fd, flags), 0);
     102    TEST_COMPARE (fcntl (maximum_fd, F_GETFL), -1);
     103    TEST_COMPARE (errno, EBADF);
     104  }
     105  
     106  /* Basic tests: check if the syscall close ranges with and without gaps.  */
     107  static void
     108  close_range_test (void)
     109  {
     110    struct support_descriptors *descrs = support_descriptors_list ();
     111  
     112    /* Check if the temporary file descriptor has no no gaps.  */
     113    int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600);
     114  
     115    close_range_test_common (lowfd, 0);
     116  
     117    /* Double check by check the /proc.  */
     118    support_descriptors_check (descrs);
     119    support_descriptors_free (descrs);
     120  }
     121  
     122  #ifdef __linux__
     123  _Noreturn static int
     124  close_range_test_fn (void *arg)
     125  {
     126    int lowfd = (int) ((uintptr_t) arg);
     127    close_range_test_common (lowfd, 0);
     128    exit (EXIT_SUCCESS);
     129  }
     130  
     131  /* Check if a clone_range on a subprocess created with CLONE_FILES close
     132     the shared file descriptor table entries in the parent.  */
     133  static void
     134  close_range_test_subprocess (void)
     135  {
     136    struct support_descriptors *descrs = support_descriptors_list ();
     137  
     138    /* Check if the temporary file descriptor has no no gaps.  */
     139    int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600);
     140  
     141    struct support_stack stack = support_stack_alloc (4096);
     142  
     143    pid_t pid = xclone (close_range_test_fn, (void*) (uintptr_t) lowfd,
     144  		      stack.stack, stack.size, CLONE_FILES | SIGCHLD);
     145    TEST_VERIFY_EXIT (pid > 0);
     146    int status;
     147    xwaitpid (pid, &status, 0);
     148    TEST_VERIFY (WIFEXITED (status));
     149    TEST_COMPARE (WEXITSTATUS (status), 0);
     150  
     151    support_stack_free (&stack);
     152  
     153    for (int i = lowfd; i < NFDS; i++)
     154      TEST_VERIFY (fcntl (i, F_GETFL) < 0);
     155  
     156    support_descriptors_check (descrs);
     157    support_descriptors_free (descrs);
     158  }
     159  #endif
     160  
     161  
     162  #ifdef CLOSE_RANGE_UNSHARE
     163  _Noreturn static int
     164  close_range_unshare_test_fn (void *arg)
     165  {
     166    int lowfd = (int) ((uintptr_t) arg);
     167    close_range_test_common (lowfd, CLOSE_RANGE_UNSHARE);
     168    exit (EXIT_SUCCESS);
     169  }
     170  
     171  /* Check if a close_range with CLOSE_RANGE_UNSHARE issued from a subprocess
     172     created with CLONE_FILES does not close the parent file descriptor list.  */
     173  static void
     174  close_range_unshare_test (void)
     175  {
     176    struct support_descriptors *descrs1 = support_descriptors_list ();
     177  
     178    /* Check if the temporary file descriptor has no no gaps.  */
     179    int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600);
     180  
     181    struct support_descriptors *descrs2 = support_descriptors_list ();
     182  
     183    struct support_stack stack = support_stack_alloc (4096);
     184  
     185    pid_t pid = xclone (close_range_unshare_test_fn, (void*) (uintptr_t) lowfd,
     186  		      stack.stack, stack.size, CLONE_FILES | SIGCHLD);
     187    TEST_VERIFY_EXIT (pid > 0);
     188    int status;
     189    xwaitpid (pid, &status, 0);
     190    TEST_VERIFY (WIFEXITED (status));
     191    TEST_COMPARE (WEXITSTATUS (status), 0);
     192  
     193    support_stack_free (&stack);
     194  
     195    for (int i = lowfd; i < lowfd + NFDS; i++)
     196      TEST_VERIFY (fcntl (i, F_GETFL) > -1);
     197  
     198    support_descriptors_check (descrs2);
     199    support_descriptors_free (descrs2);
     200  
     201    TEST_COMPARE (close_range (lowfd, lowfd + NFDS, 0), 0);
     202  
     203    support_descriptors_check (descrs1);
     204    support_descriptors_free (descrs1);
     205  }
     206  #endif
     207  
     208  static bool
     209  is_in_array (int *arr, size_t len, int fd)
     210  {
     211    bool r = false;
     212    for (int i = 0; i < len; i++)
     213      if (arr[i] == fd)
     214        return true;
     215    return r;
     216  }
     217  
     218  static void
     219  close_range_cloexec_test (void)
     220  {
     221    /* Check if the temporary file descriptor has no no gaps.  */
     222    int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600);
     223  
     224    const int maximum_fd = lowfd + NFDS - 1;
     225    const int half_fd = lowfd + NFDS / 2;
     226    const int gap_1 = maximum_fd - 8;
     227  
     228    /* Close half of the descriptors and check result.  */
     229    int r = close_range (lowfd, half_fd, CLOSE_RANGE_CLOEXEC);
     230    if (r == -1 && errno == EINVAL)
     231      {
     232        printf ("%s: CLOSE_RANGE_CLOEXEC not supported\n", __func__);
     233        return;
     234      }
     235    for (int i = lowfd; i <= half_fd; i++)
     236      {
     237        int flags = fcntl (i, F_GETFD);
     238        TEST_VERIFY (flags > -1);
     239        TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC);
     240      }
     241    for (int i = half_fd + 1; i < maximum_fd; i++)
     242      TEST_VERIFY (fcntl (i, F_GETFL) > -1);
     243  
     244    /* Create some gaps, close up to a threshold, and check result.  */
     245    static int gap_close[] = { 57, 78, 81, 82, 84, 90 };
     246    for (int i = 0; i < array_length (gap_close); i++)
     247      xclose (lowfd + gap_close[i]);
     248  
     249    TEST_COMPARE (close_range (half_fd + 1, gap_1, CLOSE_RANGE_CLOEXEC), 0);
     250    for (int i = half_fd + 1; i < gap_1; i++)
     251      {
     252        int flags = fcntl (i, F_GETFD);
     253        if (is_in_array (gap_close, array_length (gap_close), i - lowfd))
     254          TEST_COMPARE (flags, -1);
     255        else
     256          {
     257            TEST_VERIFY (flags > -1);
     258            TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC);
     259          }
     260      }
     261    for (int i = gap_1 + 1; i < maximum_fd; i++)
     262      TEST_VERIFY (fcntl (i, F_GETFL) > -1);
     263  
     264    /* Close the remaining but the last one.  */
     265    TEST_COMPARE (close_range (gap_1 + 1, maximum_fd - 1, CLOSE_RANGE_CLOEXEC),
     266                  0);
     267    for (int i = gap_1 + 1; i < maximum_fd - 1; i++)
     268      {
     269        int flags = fcntl (i, F_GETFD);
     270        TEST_VERIFY (flags > -1);
     271        TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC);
     272      }
     273    TEST_VERIFY (fcntl (maximum_fd, F_GETFL) > -1);
     274  
     275    /* Close the last one.  */
     276    TEST_COMPARE (close_range (maximum_fd, maximum_fd, CLOSE_RANGE_CLOEXEC), 0);
     277    {
     278      int flags = fcntl (maximum_fd, F_GETFD);
     279      TEST_VERIFY (flags > -1);
     280      TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC);
     281    }
     282  }
     283  
     284  static int
     285  do_test (void)
     286  {
     287    close_range_test_max_upper_limit ();
     288    close_range_test ();
     289  #ifdef __linux__
     290    close_range_test_subprocess ();
     291  #endif
     292  #ifdef CLOSE_RANGE_UNSHARE
     293    close_range_unshare_test ();
     294  #endif
     295    close_range_cloexec_test ();
     296  
     297    return 0;
     298  }
     299  
     300  #include <support/test-driver.c>