12b395c3cSAndy Fiddaman /*
22b395c3cSAndy Fiddaman  * This file and its contents are supplied under the terms of the
32b395c3cSAndy Fiddaman  * Common Development and Distribution License ("CDDL"), version 1.0.
42b395c3cSAndy Fiddaman  * You may only use this file in accordance with the terms of version
52b395c3cSAndy Fiddaman  * 1.0 of the CDDL.
62b395c3cSAndy Fiddaman  *
72b395c3cSAndy Fiddaman  * A full copy of the text of the CDDL should have accompanied this
82b395c3cSAndy Fiddaman  * source.  A copy of the CDDL is also available via the Internet at
92b395c3cSAndy Fiddaman  * http://www.illumos.org/license/CDDL.
102b395c3cSAndy Fiddaman  */
112b395c3cSAndy Fiddaman 
122b395c3cSAndy Fiddaman /*
132b395c3cSAndy Fiddaman  * Copyright 2015 Garrett D'Amore <garrett@damore.org>
142b395c3cSAndy Fiddaman  * Copyright 2024 Oxide Computer Company
152b395c3cSAndy Fiddaman  */
162b395c3cSAndy Fiddaman 
172b395c3cSAndy Fiddaman /*
182b395c3cSAndy Fiddaman  * This program tests that fexecve works properly.
192b395c3cSAndy Fiddaman  */
202b395c3cSAndy Fiddaman 
212b395c3cSAndy Fiddaman #include <stdio.h>
222b395c3cSAndy Fiddaman #include <stdlib.h>
232b395c3cSAndy Fiddaman #include <string.h>
242b395c3cSAndy Fiddaman #include <fcntl.h>
252b395c3cSAndy Fiddaman #include <err.h>
262b395c3cSAndy Fiddaman #include <errno.h>
272b395c3cSAndy Fiddaman #include <unistd.h>
282b395c3cSAndy Fiddaman #include <note.h>
292b395c3cSAndy Fiddaman #include <sys/execx.h>
302b395c3cSAndy Fiddaman #include <sys/utsname.h>
312b395c3cSAndy Fiddaman #include <sys/wait.h>
322b395c3cSAndy Fiddaman #include "test_common.h"
332b395c3cSAndy Fiddaman 
342b395c3cSAndy Fiddaman int extra_debug = 0;
352b395c3cSAndy Fiddaman 
362b395c3cSAndy Fiddaman struct utsname un;
372b395c3cSAndy Fiddaman 
382b395c3cSAndy Fiddaman static void
forkit(char * msg,const char * expect,void (* postfn)(void))392b395c3cSAndy Fiddaman forkit(char *msg, const char *expect, void (*postfn)(void))
402b395c3cSAndy Fiddaman {
412b395c3cSAndy Fiddaman 	int fd;
422b395c3cSAndy Fiddaman 	FILE *f;
432b395c3cSAndy Fiddaman 	pid_t pid;
442b395c3cSAndy Fiddaman 	char *ptr = NULL, *p;
452b395c3cSAndy Fiddaman 	size_t cap = 0;
462b395c3cSAndy Fiddaman 	int wstat;
472b395c3cSAndy Fiddaman 	int rv;
482b395c3cSAndy Fiddaman 	test_t t;
492b395c3cSAndy Fiddaman 	char fname[32];
502b395c3cSAndy Fiddaman 
512b395c3cSAndy Fiddaman 	(void) strcpy(fname, "/tmp/testXXXXXX");
522b395c3cSAndy Fiddaman 	t = test_start(msg);
532b395c3cSAndy Fiddaman 
542b395c3cSAndy Fiddaman 	fd = mkstemp(fname);
552b395c3cSAndy Fiddaman 	if (fd < 0) {
562b395c3cSAndy Fiddaman 		test_failed(t, "mkstemp failed: %s", strerror(errno));
572b395c3cSAndy Fiddaman 		return;
582b395c3cSAndy Fiddaman 	}
592b395c3cSAndy Fiddaman 
602b395c3cSAndy Fiddaman 	/* don't leave it in the filesystem */
612b395c3cSAndy Fiddaman 	(void) unlink(fname);
622b395c3cSAndy Fiddaman 
63*64c19589SAndy Fiddaman 	pid = fork();
642b395c3cSAndy Fiddaman 	switch (pid) {
652b395c3cSAndy Fiddaman 	case -1:
66*64c19589SAndy Fiddaman 		test_failed(t, "fork failed: %s", strerror(errno));
672b395c3cSAndy Fiddaman 		return;
682b395c3cSAndy Fiddaman 	case 0:
692b395c3cSAndy Fiddaman 		if (dup2(fd, 1) < 0) {
702b395c3cSAndy Fiddaman 			test_failed(t, "dup2 failed: %s", strerror(errno));
712b395c3cSAndy Fiddaman 			exit(9);
722b395c3cSAndy Fiddaman 		}
732b395c3cSAndy Fiddaman 		postfn();
742b395c3cSAndy Fiddaman 		exit(0);
752b395c3cSAndy Fiddaman 	default:
762b395c3cSAndy Fiddaman 		break;
772b395c3cSAndy Fiddaman 	}
782b395c3cSAndy Fiddaman 
792b395c3cSAndy Fiddaman 	/* parent */
802b395c3cSAndy Fiddaman 	f = fdopen(fd, "r");
812b395c3cSAndy Fiddaman 	if (f == NULL) {
822b395c3cSAndy Fiddaman 		(void) close(fd);
832b395c3cSAndy Fiddaman 		test_failed(t, "fdopen failed: %s", strerror(errno));
842b395c3cSAndy Fiddaman 		(void) wait(NULL);
852b395c3cSAndy Fiddaman 		return;
862b395c3cSAndy Fiddaman 	}
872b395c3cSAndy Fiddaman 	if (waitpid(pid, &wstat, 0) < 0) {
882b395c3cSAndy Fiddaman 		test_failed(t, "wait failed: %s", strerror(errno));
892b395c3cSAndy Fiddaman 		(void) fclose(f);
902b395c3cSAndy Fiddaman 		return;
912b395c3cSAndy Fiddaman 	}
922b395c3cSAndy Fiddaman 	if (!WIFEXITED(wstat) || WEXITSTATUS(wstat) != 0) {
932b395c3cSAndy Fiddaman 		test_failed(t, "child failed: %#x", wstat);
942b395c3cSAndy Fiddaman 		(void) fclose(f);
952b395c3cSAndy Fiddaman 		return;
962b395c3cSAndy Fiddaman 	}
972b395c3cSAndy Fiddaman 	(void) lseek(fd, 0, SEEK_SET);
982b395c3cSAndy Fiddaman 	if ((rv = getline(&ptr, &cap, f)) < 1) {
992b395c3cSAndy Fiddaman 		test_failed(t, "child gave no data: %d", rv);
1002b395c3cSAndy Fiddaman 		(void) fclose(f);
1012b395c3cSAndy Fiddaman 		return;
1022b395c3cSAndy Fiddaman 	}
1032b395c3cSAndy Fiddaman 	(void) fclose(f);
1042b395c3cSAndy Fiddaman 
1052b395c3cSAndy Fiddaman 	if ((p = strchr(ptr, '\n')) != NULL)
1062b395c3cSAndy Fiddaman 		*p = '\0';
1072b395c3cSAndy Fiddaman 	if (extra_debug)
1082b395c3cSAndy Fiddaman 		printf("Child output: [%s]\n", ptr);
1092b395c3cSAndy Fiddaman 	if (strcmp(ptr, expect) != 0) {
1102b395c3cSAndy Fiddaman 		test_failed(t, "[%s] != [%s]", ptr, expect);
1112b395c3cSAndy Fiddaman 		return;
1122b395c3cSAndy Fiddaman 	}
1132b395c3cSAndy Fiddaman 
1142b395c3cSAndy Fiddaman 	(void) free(ptr);
1152b395c3cSAndy Fiddaman 	test_passed(t);
1162b395c3cSAndy Fiddaman }
1172b395c3cSAndy Fiddaman 
1182b395c3cSAndy Fiddaman static void
case_badf(void)1192b395c3cSAndy Fiddaman case_badf(void)
1202b395c3cSAndy Fiddaman {
1212b395c3cSAndy Fiddaman 	int fd = -1;
1222b395c3cSAndy Fiddaman 	int rv;
1232b395c3cSAndy Fiddaman 	char *args[] = { "uname", NULL };
1242b395c3cSAndy Fiddaman 	char *env[] = { NULL };
1252b395c3cSAndy Fiddaman 
1262b395c3cSAndy Fiddaman 	rv = fexecve(fd, args, env);
1272b395c3cSAndy Fiddaman 	if (rv != -1) {
1282b395c3cSAndy Fiddaman 		(void) printf("rv is not -1\n");
1292b395c3cSAndy Fiddaman 		(void) exit(0);
1302b395c3cSAndy Fiddaman 	}
1312b395c3cSAndy Fiddaman 	if (errno != EBADF) {
1322b395c3cSAndy Fiddaman 		(void) printf("err %d(%s) != EBADF\n", errno, strerror(errno));
1332b395c3cSAndy Fiddaman 		(void) exit(0);
1342b395c3cSAndy Fiddaman 	}
1352b395c3cSAndy Fiddaman 	(void) printf("GOOD\n");
1362b395c3cSAndy Fiddaman 	(void) exit(0);
1372b395c3cSAndy Fiddaman }
1382b395c3cSAndy Fiddaman 
1392b395c3cSAndy Fiddaman static void
case_bad_highf(void)1402b395c3cSAndy Fiddaman case_bad_highf(void)
1412b395c3cSAndy Fiddaman {
1422b395c3cSAndy Fiddaman 	int fd = 55;
1432b395c3cSAndy Fiddaman 	int rv;
1442b395c3cSAndy Fiddaman 	char *args[] = { "uname", NULL };
1452b395c3cSAndy Fiddaman 	char *env[] = { NULL };
1462b395c3cSAndy Fiddaman 
1472b395c3cSAndy Fiddaman 	closefrom(3);
1482b395c3cSAndy Fiddaman 	rv = fexecve(fd, args, env);
1492b395c3cSAndy Fiddaman 	if (rv != -1) {
1502b395c3cSAndy Fiddaman 		(void) printf("rv is not -1\n");
1512b395c3cSAndy Fiddaman 		(void) exit(0);
1522b395c3cSAndy Fiddaman 	}
1532b395c3cSAndy Fiddaman 	if (errno != EBADF) {
1542b395c3cSAndy Fiddaman 		(void) printf("err %d(%s) != EBADF\n", errno, strerror(errno));
1552b395c3cSAndy Fiddaman 		(void) exit(0);
1562b395c3cSAndy Fiddaman 	}
1572b395c3cSAndy Fiddaman 	(void) printf("GOOD\n");
1582b395c3cSAndy Fiddaman 	(void) exit(0);
1592b395c3cSAndy Fiddaman }
1602b395c3cSAndy Fiddaman 
1612b395c3cSAndy Fiddaman static void
case_notexec(void)1622b395c3cSAndy Fiddaman case_notexec(void)
1632b395c3cSAndy Fiddaman {
1642b395c3cSAndy Fiddaman 	int fd;
1652b395c3cSAndy Fiddaman 	int rv;
1662b395c3cSAndy Fiddaman 	char *args[] = { "uname", NULL };
1672b395c3cSAndy Fiddaman 	char *env[] = { NULL };
1682b395c3cSAndy Fiddaman 
1692b395c3cSAndy Fiddaman 	fd = open("/usr/bin/uname", O_RDONLY);
1702b395c3cSAndy Fiddaman 
1712b395c3cSAndy Fiddaman 	rv = fexecve(fd, args, env);
1722b395c3cSAndy Fiddaman 	if (rv != -1) {
1732b395c3cSAndy Fiddaman 		(void) printf("rv is not -1\n");
1742b395c3cSAndy Fiddaman 		(void) exit(0);
1752b395c3cSAndy Fiddaman 	}
1762b395c3cSAndy Fiddaman 	(void) printf("FAILURE\n");
1772b395c3cSAndy Fiddaman 	(void) exit(0);
1782b395c3cSAndy Fiddaman }
1792b395c3cSAndy Fiddaman 
1802b395c3cSAndy Fiddaman static void
case_cloexec(void)1812b395c3cSAndy Fiddaman case_cloexec(void)
1822b395c3cSAndy Fiddaman {
1832b395c3cSAndy Fiddaman 	int fd;
1842b395c3cSAndy Fiddaman 	int rv;
1852b395c3cSAndy Fiddaman 	char *args[] = { "ls", "-C", "/proc/self/fd", NULL };
1862b395c3cSAndy Fiddaman 	char *env[] = { NULL };
1872b395c3cSAndy Fiddaman 
1882b395c3cSAndy Fiddaman 	/*
1892b395c3cSAndy Fiddaman 	 * We set things up so that this process has only stdin, stdout and
1902b395c3cSAndy Fiddaman 	 * stderr, then `ls` will open a file descriptor for the directory
1912b395c3cSAndy Fiddaman 	 * being listed. If we have more than that then the descriptor we're
1922b395c3cSAndy Fiddaman 	 * about to open has leaked through to the new process despite the
1932b395c3cSAndy Fiddaman 	 * O_CLOEXEC.
1942b395c3cSAndy Fiddaman 	 */
1952b395c3cSAndy Fiddaman 	closefrom(3);
1962b395c3cSAndy Fiddaman 	fd = open("/usr/bin/ls", O_RDONLY | O_EXEC | O_CLOEXEC);
1972b395c3cSAndy Fiddaman 
1982b395c3cSAndy Fiddaman 	rv = fexecve(fd, args, env);
1992b395c3cSAndy Fiddaman 	if (rv != -1) {
2002b395c3cSAndy Fiddaman 		(void) printf("rv is not -1\n");
2012b395c3cSAndy Fiddaman 		(void) exit(0);
2022b395c3cSAndy Fiddaman 	}
2032b395c3cSAndy Fiddaman 	(void) printf("FAILURE\n");
2042b395c3cSAndy Fiddaman 	(void) exit(0);
2052b395c3cSAndy Fiddaman }
2062b395c3cSAndy Fiddaman 
2072b395c3cSAndy Fiddaman static void
case_uname(void)2082b395c3cSAndy Fiddaman case_uname(void)
2092b395c3cSAndy Fiddaman {
2102b395c3cSAndy Fiddaman 	int fd;
2112b395c3cSAndy Fiddaman 	char *args[] = { "uname", NULL };
2122b395c3cSAndy Fiddaman 	char *env[] = { NULL };
2132b395c3cSAndy Fiddaman 
2142b395c3cSAndy Fiddaman 	fd = open("/usr/bin/uname", O_EXEC);
2152b395c3cSAndy Fiddaman 	if (fd < 0) {
2162b395c3cSAndy Fiddaman 		(void) printf("failed to open /usr/bin/uname: %s",
2172b395c3cSAndy Fiddaman 		    strerror(errno));
2182b395c3cSAndy Fiddaman 		(void) exit(0);
2192b395c3cSAndy Fiddaman 	}
2202b395c3cSAndy Fiddaman 
2212b395c3cSAndy Fiddaman 	(void) fexecve(fd, args, env);
2222b395c3cSAndy Fiddaman 	(void) printf("EXEC FAILED: %s\n", strerror(errno));
2232b395c3cSAndy Fiddaman 	(void) exit(0);
2242b395c3cSAndy Fiddaman }
2252b395c3cSAndy Fiddaman 
2262b395c3cSAndy Fiddaman static void
case_uname_r(void)2272b395c3cSAndy Fiddaman case_uname_r(void)
2282b395c3cSAndy Fiddaman {
2292b395c3cSAndy Fiddaman 	int fd;
2302b395c3cSAndy Fiddaman 	char *args[] = { "uname", "-r", NULL };
2312b395c3cSAndy Fiddaman 	char *env[] = { NULL };
2322b395c3cSAndy Fiddaman 
2332b395c3cSAndy Fiddaman 	fd = open("/usr/bin/uname", O_EXEC);
2342b395c3cSAndy Fiddaman 	if (fd < 0) {
2352b395c3cSAndy Fiddaman 		(void) printf("failed to open /usr/bin/uname: %s",
2362b395c3cSAndy Fiddaman 		    strerror(errno));
2372b395c3cSAndy Fiddaman 		(void) exit(0);
2382b395c3cSAndy Fiddaman 	}
2392b395c3cSAndy Fiddaman 
2402b395c3cSAndy Fiddaman 	(void) fexecve(fd, args, env);
2412b395c3cSAndy Fiddaman 	(void) printf("EXEC FAILED: %s\n", strerror(errno));
2422b395c3cSAndy Fiddaman 	(void) exit(0);
2432b395c3cSAndy Fiddaman }
2442b395c3cSAndy Fiddaman 
2452b395c3cSAndy Fiddaman static void
case_execvex_bad_flags(void)2462b395c3cSAndy Fiddaman case_execvex_bad_flags(void)
2472b395c3cSAndy Fiddaman {
2482b395c3cSAndy Fiddaman 	int fd = -1;
2492b395c3cSAndy Fiddaman 	int rv;
2502b395c3cSAndy Fiddaman 	char *args[] = { "uname", NULL };
2512b395c3cSAndy Fiddaman 	char *env[] = { NULL };
2522b395c3cSAndy Fiddaman 
2532b395c3cSAndy Fiddaman 	rv = execvex(fd, args, env, 0x1005);
2542b395c3cSAndy Fiddaman 	if (rv != -1) {
2552b395c3cSAndy Fiddaman 		(void) printf("rv is not -1\n");
2562b395c3cSAndy Fiddaman 		(void) exit(0);
2572b395c3cSAndy Fiddaman 	}
2582b395c3cSAndy Fiddaman 	if (errno != EINVAL) {
2592b395c3cSAndy Fiddaman 		(void) printf("err %d(%s) != EINVAL\n", errno, strerror(errno));
2602b395c3cSAndy Fiddaman 		(void) exit(0);
2612b395c3cSAndy Fiddaman 	}
2622b395c3cSAndy Fiddaman 	(void) printf("GOOD\n");
2632b395c3cSAndy Fiddaman 	(void) exit(0);
2642b395c3cSAndy Fiddaman }
2652b395c3cSAndy Fiddaman 
2662b395c3cSAndy Fiddaman static void
test_fexecve_badf(void)2672b395c3cSAndy Fiddaman test_fexecve_badf(void)
2682b395c3cSAndy Fiddaman {
2692b395c3cSAndy Fiddaman 	forkit("fexecve (bad FD)", "GOOD", case_badf);
2702b395c3cSAndy Fiddaman }
2712b395c3cSAndy Fiddaman 
2722b395c3cSAndy Fiddaman static void
test_fexecve_bad_highf(void)2732b395c3cSAndy Fiddaman test_fexecve_bad_highf(void)
2742b395c3cSAndy Fiddaman {
2752b395c3cSAndy Fiddaman 	forkit("fexecve (bad FD)", "GOOD", case_bad_highf);
2762b395c3cSAndy Fiddaman }
2772b395c3cSAndy Fiddaman 
2782b395c3cSAndy Fiddaman static void
test_fexecve_notexec(void)2792b395c3cSAndy Fiddaman test_fexecve_notexec(void)
2802b395c3cSAndy Fiddaman {
2812b395c3cSAndy Fiddaman 	forkit("fexecve (not O_EXEC)", un.sysname, case_notexec);
2822b395c3cSAndy Fiddaman }
2832b395c3cSAndy Fiddaman 
2842b395c3cSAndy Fiddaman static void
test_fexecve_cloexec(void)2852b395c3cSAndy Fiddaman test_fexecve_cloexec(void)
2862b395c3cSAndy Fiddaman {
2872b395c3cSAndy Fiddaman 	forkit("fexecve (O_CLOEXEC)", "0  1  2  3", case_cloexec);
2882b395c3cSAndy Fiddaman }
2892b395c3cSAndy Fiddaman 
2902b395c3cSAndy Fiddaman static void
test_fexecve_uname(void)2912b395c3cSAndy Fiddaman test_fexecve_uname(void)
2922b395c3cSAndy Fiddaman {
2932b395c3cSAndy Fiddaman 	forkit("fexecve (uname)", un.sysname, case_uname);
2942b395c3cSAndy Fiddaman }
2952b395c3cSAndy Fiddaman 
2962b395c3cSAndy Fiddaman static void
test_fexecve_uname_r(void)2972b395c3cSAndy Fiddaman test_fexecve_uname_r(void)
2982b395c3cSAndy Fiddaman {
2992b395c3cSAndy Fiddaman 	forkit("fexecve (uname)", un.release, case_uname_r);
3002b395c3cSAndy Fiddaman }
3012b395c3cSAndy Fiddaman 
3022b395c3cSAndy Fiddaman static void
test_execvex_bad_flags(void)3032b395c3cSAndy Fiddaman test_execvex_bad_flags(void)
3042b395c3cSAndy Fiddaman {
3052b395c3cSAndy Fiddaman 	forkit("execvex (bad flags)", "GOOD", case_execvex_bad_flags);
3062b395c3cSAndy Fiddaman }
3072b395c3cSAndy Fiddaman 
3082b395c3cSAndy Fiddaman int
main(int argc,char ** argv)3092b395c3cSAndy Fiddaman main(int argc, char **argv)
3102b395c3cSAndy Fiddaman {
3112b395c3cSAndy Fiddaman 	int optc;
3122b395c3cSAndy Fiddaman 
3132b395c3cSAndy Fiddaman 	(void) uname(&un);
3142b395c3cSAndy Fiddaman 
3152b395c3cSAndy Fiddaman 	while ((optc = getopt(argc, argv, "dfD")) != EOF) {
3162b395c3cSAndy Fiddaman 		switch (optc) {
3172b395c3cSAndy Fiddaman 		case 'd':
3182b395c3cSAndy Fiddaman 			test_set_debug();
3192b395c3cSAndy Fiddaman 			break;
3202b395c3cSAndy Fiddaman 		case 'f':
3212b395c3cSAndy Fiddaman 			test_set_force();
3222b395c3cSAndy Fiddaman 			break;
3232b395c3cSAndy Fiddaman 		case 'D':
3242b395c3cSAndy Fiddaman 			test_set_debug();
3252b395c3cSAndy Fiddaman 			extra_debug++;
3262b395c3cSAndy Fiddaman 			break;
3272b395c3cSAndy Fiddaman 		default:
3282b395c3cSAndy Fiddaman 			(void) fprintf(stderr, "Usage: %s [-dfD]\n", argv[0]);
3292b395c3cSAndy Fiddaman 			exit(1);
3302b395c3cSAndy Fiddaman 		}
3312b395c3cSAndy Fiddaman 	}
3322b395c3cSAndy Fiddaman 
3332b395c3cSAndy Fiddaman 	test_fexecve_badf();
3342b395c3cSAndy Fiddaman 	test_fexecve_bad_highf();
3352b395c3cSAndy Fiddaman 	test_fexecve_notexec();
3362b395c3cSAndy Fiddaman 	test_fexecve_cloexec();
3372b395c3cSAndy Fiddaman 	test_fexecve_uname();
3382b395c3cSAndy Fiddaman 	test_fexecve_uname_r();
3392b395c3cSAndy Fiddaman 	test_execvex_bad_flags();
3402b395c3cSAndy Fiddaman 
3412b395c3cSAndy Fiddaman 	exit(0);
3422b395c3cSAndy Fiddaman }
343