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
33int
34call_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 (format: "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 (format: "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: &status, flags: 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
77static int
78do_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 (name: "/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, pathname: "sh", flags: 0, expected_fail: 0, __LINE__);
97 xchdir (path: "/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, pathname: "sh", flags: 0, expected_fail: 0, __LINE__);
103 xclose (fd);
104#ifdef O_PATH
105 /* Check the pathname relative to a valid dirfd with O_PATH. */
106 fd = xopen (path: "/bin", O_PATH | O_DIRECTORY, O_RDONLY);
107 call_execveat (fd, pathname: "sh", flags: 0, expected_fail: 0, __LINE__);
108 xclose (fd);
109
110 /* Check absolute pathname, dirfd should be ignored. */
111 call_execveat (AT_FDCWD, pathname: "/bin/sh", flags: 0, expected_fail: 0, __LINE__);
112 fd = xopen (path: "/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, pathname: "/bin/sh", flags: 0, expected_fail: 0, __LINE__);
116 xclose (fd);
117#endif
118
119 fd = xopen (path: "/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, pathname: "/bin/sh", flags: 0, expected_fail: 0, __LINE__);
123 xclose (fd);
124
125 fd = xopen (path: "/bin/sh", O_RDONLY, 0);
126 /* Check relative pathname, where dirfd does not point to a directory. */
127 call_execveat (fd, pathname: "sh", flags: 0, ENOTDIR, __LINE__);
128 /* Check absolute pathname, but dirfd is a regular file. The dirfd
129 should be ignored. */
130 call_execveat (fd, pathname: "/bin/sh", flags: 0, expected_fail: 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 (path: "/bin/sh", O_PATH, 0);
139 /* Check the empty pathname. Dirfd is a regular file with O_PATH. */
140 call_execveat (fd, pathname: "", flags: 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, pathname: "", AT_EMPTY_PATH, expected_fail: 0, __LINE__);
146 call_execveat (fd, pathname: "", AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, expected_fail: 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 (base: "tst-execveat_dir");
152 symlink_name = xasprintf (format: "%s/symlink", tmp_dir);
153 xsymlink (target: "tmp_sh", linkpath: symlink_name);
154 add_temp_file (name: symlink_name);
155 tmp_sh = xasprintf (format: "%s/tmp_sh", tmp_dir);
156 add_temp_file (name: tmp_sh);
157 fd_out = xopen (path: symlink_name, O_CREAT | O_WRONLY, 0);
158 xstat (path: "/bin/sh", &st);
159 fd = xopen (path: "/bin/sh", O_RDONLY, 0);
160 xcopy_file_range (fd_in: fd, off_in: 0, fd_out, off_out: 0, len: st.st_size, flags: 0);
161 xfchmod (fd: fd_out, mode: 0700);
162 xclose (fd);
163 xclose (fd_out);
164 fd_out = xopen (path: symlink_name, O_PATH, 0);
165
166 /* Check the empty pathname. Dirfd is a symbolic link. */
167 call_execveat (fd: fd_out, pathname: "", flags: 0, ENOENT, __LINE__);
168 call_execveat (fd: fd_out, pathname: "", AT_EMPTY_PATH, expected_fail: 0, __LINE__);
169 call_execveat (fd: fd_out, pathname: "", AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, expected_fail: 0,
170 __LINE__);
171 xclose (fd_out);
172 free (ptr: symlink_name);
173 free (ptr: tmp_sh);
174 free (ptr: tmp_dir);
175#endif
176
177 /* Call execveat with closed fd, we expect this to fail with EBADF. */
178 call_execveat (fd, pathname: "sh", flags: 0, EBADF, __LINE__);
179 /* Call execveat with closed fd, we expect this to pass because the pathname is
180 absolute. */
181 call_execveat (fd, pathname: "/bin/sh", flags: 0, expected_fail: 0, __LINE__);
182
183 return 0;
184}
185
186#include <support/test-driver.c>
187

source code of glibc/posix/tst-execveat.c