(root)/
glibc-2.38/
posix/
tst-execveat.c
       1  /* Test execveat at the various corner cases.
       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 <fcntl.h>
      20  #include <errno.h>
      21  #include <stdlib.h>
      22  #include <sys/types.h>
      23  #include <dirent.h>
      24  #include <support/check.h>
      25  #include <support/support.h>
      26  #include <support/temp_file.h>
      27  #include <support/xdlfcn.h>
      28  #include <support/xstdio.h>
      29  #include <support/xunistd.h>
      30  #include <wait.h>
      31  #include <support/test-driver.h>
      32  
      33  int
      34  call_execveat (int fd, const char *pathname, int flags, int expected_fail,
      35                 int num)
      36  {
      37    char *envp[] = { (char *) "FOO=3", NULL };
      38    char *argv[] = { (char *) "sh", (char *) "-c", (char *) "exit $FOO", NULL };
      39    pid_t pid;
      40    int status;
      41  
      42    if (test_verbose > 0)
      43      printf ("call line number: %d\n", num);
      44  
      45    pid = xfork ();
      46    if (pid == 0)
      47      {
      48        TEST_COMPARE (execveat (fd, pathname, argv, envp, flags), -1);
      49        if (errno == ENOSYS)
      50  	exit (EXIT_UNSUPPORTED);
      51        else if (errno == expected_fail)
      52          {
      53            if (test_verbose > 0)
      54              printf ("expected fail: errno %d\n", errno);
      55            _exit (0);
      56          }
      57        else
      58          FAIL_EXIT1 ("execveat failed: %m (%d)", errno);
      59      }
      60    xwaitpid (pid, &status, 0);
      61  
      62    if (!WIFEXITED (status))
      63      FAIL_RET ("child hasn't exited normally");
      64  
      65    if (WIFEXITED (status))
      66      {
      67        if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
      68          FAIL_UNSUPPORTED ("execveat is unimplemented");
      69        else if (expected_fail != 0)
      70          TEST_COMPARE (WEXITSTATUS (status), 0);
      71        else
      72          TEST_COMPARE (WEXITSTATUS (status), 3);
      73      }
      74    return 0;
      75  }
      76  
      77  static int
      78  do_test (void)
      79  {
      80    DIR *dirp;
      81    int fd;
      82  #ifdef O_PATH
      83    int fd_out;
      84    char *tmp_dir, *symlink_name, *tmp_sh;
      85    struct stat64 st;
      86  #endif
      87  
      88    dirp = opendir ("/bin");
      89    if (dirp == NULL)
      90      FAIL_EXIT1 ("failed to open /bin");
      91    fd = dirfd (dirp);
      92  
      93    /* Call execveat for various fd/pathname combinations.  */
      94  
      95    /* Check the pathname relative to a valid dirfd.  */
      96    call_execveat (fd, "sh", 0, 0, __LINE__);
      97    xchdir ("/bin");
      98    /* Use the special value AT_FDCWD as dirfd. Quoting open(2):
      99       If pathname is relative and dirfd is the special value AT_FDCWD, then
     100       pathname is interpreted relative to the current working directory of
     101       the calling process.  */
     102    call_execveat (AT_FDCWD, "sh", 0, 0, __LINE__);
     103    xclose (fd);
     104  #ifdef O_PATH
     105    /* Check the pathname relative to a valid dirfd with O_PATH.  */
     106    fd = xopen ("/bin", O_PATH | O_DIRECTORY, O_RDONLY);
     107    call_execveat (fd, "sh", 0, 0, __LINE__);
     108    xclose (fd);
     109  
     110    /* Check absolute pathname, dirfd should be ignored.  */
     111    call_execveat (AT_FDCWD, "/bin/sh", 0, 0, __LINE__);
     112    fd = xopen ("/usr", O_PATH | O_DIRECTORY, 0);
     113    /* Same check for absolute pathname, but with input file descriptor
     114       opened with different flags.  The dirfd should be ignored.  */
     115    call_execveat (fd, "/bin/sh", 0, 0, __LINE__);
     116    xclose (fd);
     117  #endif
     118  
     119    fd = xopen ("/usr", O_RDONLY, 0);
     120    /* Same check for absolute pathname, but with input file descriptor
     121       opened with different flags.  The dirfd should be ignored.  */
     122    call_execveat (fd, "/bin/sh", 0, 0, __LINE__);
     123    xclose (fd);
     124  
     125    fd = xopen ("/bin/sh", O_RDONLY, 0);
     126    /* Check relative pathname, where dirfd does not point to a directory.  */
     127    call_execveat (fd, "sh", 0, ENOTDIR, __LINE__);
     128    /* Check absolute pathname, but dirfd is a regular file.  The dirfd
     129       should be ignored.  */
     130    call_execveat (fd, "/bin/sh", 0, 0, __LINE__);
     131    xclose (fd);
     132  
     133  #ifdef O_PATH
     134    /* Quoting open(2): O_PATH
     135       Obtain a file descriptor that can be used for two purposes: to
     136       indicate a location in the filesystem tree and to perform
     137       operations that act purely at the file descriptor level.  */
     138    fd = xopen ("/bin/sh", O_PATH, 0);
     139    /* Check the empty pathname.  Dirfd is a regular file with O_PATH.  */
     140    call_execveat (fd, "", 0, ENOENT, __LINE__);
     141    /* Same check for an empty pathname, but with AT_EMPTY_PATH flag.
     142       Quoting open(2):
     143       If oldpath is an empty string, create a link to the file referenced
     144       by olddirfd (which may have been obtained using the open(2) O_PATH flag. */
     145    call_execveat (fd, "", AT_EMPTY_PATH, 0, __LINE__);
     146    call_execveat (fd, "", AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, 0, __LINE__);
     147    xclose (fd);
     148  
     149    /* Create a temporary directory "tmp_dir" and create a symbolik link tmp_sh
     150       pointing to /bin/sh inside the tmp_dir. Open dirfd as a symbolic link.  */
     151    tmp_dir = support_create_temp_directory ("tst-execveat_dir");
     152    symlink_name = xasprintf ("%s/symlink", tmp_dir);
     153    xsymlink ("tmp_sh", symlink_name);
     154    add_temp_file (symlink_name);
     155    tmp_sh = xasprintf ("%s/tmp_sh", tmp_dir);
     156    add_temp_file (tmp_sh);
     157    fd_out = xopen (symlink_name, O_CREAT | O_WRONLY, 0);
     158    xstat ("/bin/sh", &st);
     159    fd = xopen ("/bin/sh", O_RDONLY, 0);
     160    xcopy_file_range (fd, 0, fd_out, 0, st.st_size, 0);
     161    xfchmod (fd_out, 0700);
     162    xclose (fd);
     163    xclose (fd_out);
     164    fd_out = xopen (symlink_name, O_PATH, 0);
     165  
     166   /* Check the empty pathname. Dirfd is a symbolic link.  */
     167    call_execveat (fd_out, "", 0, ENOENT, __LINE__);
     168    call_execveat (fd_out, "", AT_EMPTY_PATH, 0, __LINE__);
     169    call_execveat (fd_out, "", AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, 0,
     170                   __LINE__);
     171    xclose (fd_out);
     172    free (symlink_name);
     173    free (tmp_sh);
     174    free (tmp_dir);
     175  #endif
     176  
     177    /* Call execveat with closed fd, we expect this to fail with EBADF.  */
     178    call_execveat (fd, "sh", 0, EBADF, __LINE__);
     179    /* Call execveat with closed fd, we expect this to pass because the pathname is
     180       absolute.  */
     181    call_execveat (fd, "/bin/sh", 0, 0, __LINE__);
     182  
     183    return 0;
     184  }
     185  
     186  #include <support/test-driver.c>