1 /* Test execveat at the various corner cases.
2 Copyright (C) 2021-2022 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
call_execveat(int fd,const char * pathname,int flags,int expected_fail,int num)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
do_test(void)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 openend 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 openend 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>
187