xref: /illumos-gate/usr/src/test/libc-tests/tests/fexecve/fexecve_test.c (revision fc910014e8a32a65612105835a10995f2c13d942)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2015 Garrett D'Amore <garrett@damore.org>
14  * Copyright 2024 Oxide Computer Company
15  */
16 
17 /*
18  * This program tests that fexecve works properly.
19  */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <fcntl.h>
25 #include <err.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <note.h>
29 #include <sys/execx.h>
30 #include <sys/utsname.h>
31 #include <sys/wait.h>
32 #include "test_common.h"
33 
34 int extra_debug = 0;
35 
36 struct utsname un;
37 
38 static void
39 forkit(char *msg, const char *expect, void (*postfn)(void))
40 {
41 	int fd;
42 	FILE *f;
43 	pid_t pid;
44 	char *ptr = NULL, *p;
45 	size_t cap = 0;
46 	int wstat;
47 	int rv;
48 	test_t t;
49 	char fname[32];
50 
51 	(void) strcpy(fname, "/tmp/testXXXXXX");
52 	t = test_start(msg);
53 
54 	fd = mkstemp(fname);
55 	if (fd < 0) {
56 		test_failed(t, "mkstemp failed: %s", strerror(errno));
57 		return;
58 	}
59 
60 	/* don't leave it in the filesystem */
61 	(void) unlink(fname);
62 
63 	pid = fork();
64 	switch (pid) {
65 	case -1:
66 		test_failed(t, "fork failed: %s", strerror(errno));
67 		return;
68 	case 0:
69 		if (dup2(fd, 1) < 0) {
70 			test_failed(t, "dup2 failed: %s", strerror(errno));
71 			exit(9);
72 		}
73 		postfn();
74 		exit(0);
75 	default:
76 		break;
77 	}
78 
79 	/* parent */
80 	f = fdopen(fd, "r");
81 	if (f == NULL) {
82 		(void) close(fd);
83 		test_failed(t, "fdopen failed: %s", strerror(errno));
84 		(void) wait(NULL);
85 		return;
86 	}
87 	if (waitpid(pid, &wstat, 0) < 0) {
88 		test_failed(t, "wait failed: %s", strerror(errno));
89 		(void) fclose(f);
90 		return;
91 	}
92 	if (!WIFEXITED(wstat) || WEXITSTATUS(wstat) != 0) {
93 		test_failed(t, "child failed: %#x", wstat);
94 		(void) fclose(f);
95 		return;
96 	}
97 	(void) lseek(fd, 0, SEEK_SET);
98 	if ((rv = getline(&ptr, &cap, f)) < 1) {
99 		test_failed(t, "child gave no data: %d", rv);
100 		(void) fclose(f);
101 		return;
102 	}
103 	(void) fclose(f);
104 
105 	if ((p = strchr(ptr, '\n')) != NULL)
106 		*p = '\0';
107 	if (extra_debug)
108 		printf("Child output: [%s]\n", ptr);
109 	if (strcmp(ptr, expect) != 0) {
110 		test_failed(t, "[%s] != [%s]", ptr, expect);
111 		return;
112 	}
113 
114 	(void) free(ptr);
115 	test_passed(t);
116 }
117 
118 static void
119 case_badf(void)
120 {
121 	int fd = -1;
122 	int rv;
123 	char *args[] = { "uname", NULL };
124 	char *env[] = { NULL };
125 
126 	rv = fexecve(fd, args, env);
127 	if (rv != -1) {
128 		(void) printf("rv is not -1\n");
129 		(void) exit(0);
130 	}
131 	if (errno != EBADF) {
132 		(void) printf("err %d(%s) != EBADF\n", errno, strerror(errno));
133 		(void) exit(0);
134 	}
135 	(void) printf("GOOD\n");
136 	(void) exit(0);
137 }
138 
139 static void
140 case_bad_highf(void)
141 {
142 	int fd = 55;
143 	int rv;
144 	char *args[] = { "uname", NULL };
145 	char *env[] = { NULL };
146 
147 	closefrom(3);
148 	rv = fexecve(fd, args, env);
149 	if (rv != -1) {
150 		(void) printf("rv is not -1\n");
151 		(void) exit(0);
152 	}
153 	if (errno != EBADF) {
154 		(void) printf("err %d(%s) != EBADF\n", errno, strerror(errno));
155 		(void) exit(0);
156 	}
157 	(void) printf("GOOD\n");
158 	(void) exit(0);
159 }
160 
161 static void
162 case_notexec(void)
163 {
164 	int fd;
165 	int rv;
166 	char *args[] = { "uname", NULL };
167 	char *env[] = { NULL };
168 
169 	fd = open("/usr/bin/uname", O_RDONLY);
170 
171 	rv = fexecve(fd, args, env);
172 	if (rv != -1) {
173 		(void) printf("rv is not -1\n");
174 		(void) exit(0);
175 	}
176 	(void) printf("FAILURE\n");
177 	(void) exit(0);
178 }
179 
180 static void
181 case_cloexec(void)
182 {
183 	int fd;
184 	int rv;
185 	char *args[] = { "ls", "-C", "/proc/self/fd", NULL };
186 	char *env[] = { NULL };
187 
188 	/*
189 	 * We set things up so that this process has only stdin, stdout and
190 	 * stderr, then `ls` will open a file descriptor for the directory
191 	 * being listed. If we have more than that then the descriptor we're
192 	 * about to open has leaked through to the new process despite the
193 	 * O_CLOEXEC.
194 	 */
195 	closefrom(3);
196 	fd = open("/usr/bin/ls", O_RDONLY | O_EXEC | O_CLOEXEC);
197 
198 	rv = fexecve(fd, args, env);
199 	if (rv != -1) {
200 		(void) printf("rv is not -1\n");
201 		(void) exit(0);
202 	}
203 	(void) printf("FAILURE\n");
204 	(void) exit(0);
205 }
206 
207 static void
208 case_uname(void)
209 {
210 	int fd;
211 	char *args[] = { "uname", NULL };
212 	char *env[] = { NULL };
213 
214 	fd = open("/usr/bin/uname", O_EXEC);
215 	if (fd < 0) {
216 		(void) printf("failed to open /usr/bin/uname: %s",
217 		    strerror(errno));
218 		(void) exit(0);
219 	}
220 
221 	(void) fexecve(fd, args, env);
222 	(void) printf("EXEC FAILED: %s\n", strerror(errno));
223 	(void) exit(0);
224 }
225 
226 static void
227 case_uname_r(void)
228 {
229 	int fd;
230 	char *args[] = { "uname", "-r", NULL };
231 	char *env[] = { NULL };
232 
233 	fd = open("/usr/bin/uname", O_EXEC);
234 	if (fd < 0) {
235 		(void) printf("failed to open /usr/bin/uname: %s",
236 		    strerror(errno));
237 		(void) exit(0);
238 	}
239 
240 	(void) fexecve(fd, args, env);
241 	(void) printf("EXEC FAILED: %s\n", strerror(errno));
242 	(void) exit(0);
243 }
244 
245 static void
246 case_execvex_bad_flags(void)
247 {
248 	int fd = -1;
249 	int rv;
250 	char *args[] = { "uname", NULL };
251 	char *env[] = { NULL };
252 
253 	rv = execvex(fd, args, env, 0x1005);
254 	if (rv != -1) {
255 		(void) printf("rv is not -1\n");
256 		(void) exit(0);
257 	}
258 	if (errno != EINVAL) {
259 		(void) printf("err %d(%s) != EINVAL\n", errno, strerror(errno));
260 		(void) exit(0);
261 	}
262 	(void) printf("GOOD\n");
263 	(void) exit(0);
264 }
265 
266 static void
267 test_fexecve_badf(void)
268 {
269 	forkit("fexecve (bad FD)", "GOOD", case_badf);
270 }
271 
272 static void
273 test_fexecve_bad_highf(void)
274 {
275 	forkit("fexecve (bad FD)", "GOOD", case_bad_highf);
276 }
277 
278 static void
279 test_fexecve_notexec(void)
280 {
281 	forkit("fexecve (not O_EXEC)", un.sysname, case_notexec);
282 }
283 
284 static void
285 test_fexecve_cloexec(void)
286 {
287 	forkit("fexecve (O_CLOEXEC)", "0  1  2  3", case_cloexec);
288 }
289 
290 static void
291 test_fexecve_uname(void)
292 {
293 	forkit("fexecve (uname)", un.sysname, case_uname);
294 }
295 
296 static void
297 test_fexecve_uname_r(void)
298 {
299 	forkit("fexecve (uname)", un.release, case_uname_r);
300 }
301 
302 static void
303 test_execvex_bad_flags(void)
304 {
305 	forkit("execvex (bad flags)", "GOOD", case_execvex_bad_flags);
306 }
307 
308 int
309 main(int argc, char **argv)
310 {
311 	int optc;
312 
313 	(void) uname(&un);
314 
315 	while ((optc = getopt(argc, argv, "dfD")) != EOF) {
316 		switch (optc) {
317 		case 'd':
318 			test_set_debug();
319 			break;
320 		case 'f':
321 			test_set_force();
322 			break;
323 		case 'D':
324 			test_set_debug();
325 			extra_debug++;
326 			break;
327 		default:
328 			(void) fprintf(stderr, "Usage: %s [-dfD]\n", argv[0]);
329 			exit(1);
330 		}
331 	}
332 
333 	test_fexecve_badf();
334 	test_fexecve_bad_highf();
335 	test_fexecve_notexec();
336 	test_fexecve_cloexec();
337 	test_fexecve_uname();
338 	test_fexecve_uname_r();
339 	test_execvex_bad_flags();
340 
341 	exit(0);
342 }
343