18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2014 Google, Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Selftests for execveat(2).
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#ifndef _GNU_SOURCE
98c2ecf20Sopenharmony_ci#define _GNU_SOURCE  /* to get O_PATH, AT_EMPTY_PATH */
108c2ecf20Sopenharmony_ci#endif
118c2ecf20Sopenharmony_ci#include <sys/sendfile.h>
128c2ecf20Sopenharmony_ci#include <sys/stat.h>
138c2ecf20Sopenharmony_ci#include <sys/syscall.h>
148c2ecf20Sopenharmony_ci#include <sys/types.h>
158c2ecf20Sopenharmony_ci#include <sys/wait.h>
168c2ecf20Sopenharmony_ci#include <errno.h>
178c2ecf20Sopenharmony_ci#include <fcntl.h>
188c2ecf20Sopenharmony_ci#include <limits.h>
198c2ecf20Sopenharmony_ci#include <stdio.h>
208c2ecf20Sopenharmony_ci#include <stdlib.h>
218c2ecf20Sopenharmony_ci#include <string.h>
228c2ecf20Sopenharmony_ci#include <unistd.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "../kselftest.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic char longpath[2 * PATH_MAX] = "";
278c2ecf20Sopenharmony_cistatic char *envp[] = { "IN_TEST=yes", NULL, NULL };
288c2ecf20Sopenharmony_cistatic char *argv[] = { "execveat", "99", NULL };
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int execveat_(int fd, const char *path, char **argv, char **envp,
318c2ecf20Sopenharmony_ci		     int flags)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci#ifdef __NR_execveat
348c2ecf20Sopenharmony_ci	return syscall(__NR_execveat, fd, path, argv, envp, flags);
358c2ecf20Sopenharmony_ci#else
368c2ecf20Sopenharmony_ci	errno = ENOSYS;
378c2ecf20Sopenharmony_ci	return -1;
388c2ecf20Sopenharmony_ci#endif
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define check_execveat_fail(fd, path, flags, errno)	\
428c2ecf20Sopenharmony_ci	_check_execveat_fail(fd, path, flags, errno, #errno)
438c2ecf20Sopenharmony_cistatic int _check_execveat_fail(int fd, const char *path, int flags,
448c2ecf20Sopenharmony_ci				int expected_errno, const char *errno_str)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	int rc;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	errno = 0;
498c2ecf20Sopenharmony_ci	printf("Check failure of execveat(%d, '%s', %d) with %s... ",
508c2ecf20Sopenharmony_ci		fd, path?:"(null)", flags, errno_str);
518c2ecf20Sopenharmony_ci	rc = execveat_(fd, path, argv, envp, flags);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	if (rc > 0) {
548c2ecf20Sopenharmony_ci		printf("[FAIL] (unexpected success from execveat(2))\n");
558c2ecf20Sopenharmony_ci		return 1;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci	if (errno != expected_errno) {
588c2ecf20Sopenharmony_ci		printf("[FAIL] (expected errno %d (%s) not %d (%s)\n",
598c2ecf20Sopenharmony_ci			expected_errno, strerror(expected_errno),
608c2ecf20Sopenharmony_ci			errno, strerror(errno));
618c2ecf20Sopenharmony_ci		return 1;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci	printf("[OK]\n");
648c2ecf20Sopenharmony_ci	return 0;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int check_execveat_invoked_rc(int fd, const char *path, int flags,
688c2ecf20Sopenharmony_ci				     int expected_rc, int expected_rc2)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	int status;
718c2ecf20Sopenharmony_ci	int rc;
728c2ecf20Sopenharmony_ci	pid_t child;
738c2ecf20Sopenharmony_ci	int pathlen = path ? strlen(path) : 0;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (pathlen > 40)
768c2ecf20Sopenharmony_ci		printf("Check success of execveat(%d, '%.20s...%s', %d)... ",
778c2ecf20Sopenharmony_ci			fd, path, (path + pathlen - 20), flags);
788c2ecf20Sopenharmony_ci	else
798c2ecf20Sopenharmony_ci		printf("Check success of execveat(%d, '%s', %d)... ",
808c2ecf20Sopenharmony_ci			fd, path?:"(null)", flags);
818c2ecf20Sopenharmony_ci	child = fork();
828c2ecf20Sopenharmony_ci	if (child < 0) {
838c2ecf20Sopenharmony_ci		printf("[FAIL] (fork() failed)\n");
848c2ecf20Sopenharmony_ci		return 1;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci	if (child == 0) {
878c2ecf20Sopenharmony_ci		/* Child: do execveat(). */
888c2ecf20Sopenharmony_ci		rc = execveat_(fd, path, argv, envp, flags);
898c2ecf20Sopenharmony_ci		printf("[FAIL]: execveat() failed, rc=%d errno=%d (%s)\n",
908c2ecf20Sopenharmony_ci			rc, errno, strerror(errno));
918c2ecf20Sopenharmony_ci		exit(1);  /* should not reach here */
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci	/* Parent: wait for & check child's exit status. */
948c2ecf20Sopenharmony_ci	rc = waitpid(child, &status, 0);
958c2ecf20Sopenharmony_ci	if (rc != child) {
968c2ecf20Sopenharmony_ci		printf("[FAIL] (waitpid(%d,...) returned %d)\n", child, rc);
978c2ecf20Sopenharmony_ci		return 1;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci	if (!WIFEXITED(status)) {
1008c2ecf20Sopenharmony_ci		printf("[FAIL] (child %d did not exit cleanly, status=%08x)\n",
1018c2ecf20Sopenharmony_ci			child, status);
1028c2ecf20Sopenharmony_ci		return 1;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci	if ((WEXITSTATUS(status) != expected_rc) &&
1058c2ecf20Sopenharmony_ci	    (WEXITSTATUS(status) != expected_rc2)) {
1068c2ecf20Sopenharmony_ci		printf("[FAIL] (child %d exited with %d not %d nor %d)\n",
1078c2ecf20Sopenharmony_ci			child, WEXITSTATUS(status), expected_rc, expected_rc2);
1088c2ecf20Sopenharmony_ci		return 1;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci	printf("[OK]\n");
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic int check_execveat(int fd, const char *path, int flags)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	return check_execveat_invoked_rc(fd, path, flags, 99, 99);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic char *concat(const char *left, const char *right)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	char *result = malloc(strlen(left) + strlen(right) + 1);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	strcpy(result, left);
1248c2ecf20Sopenharmony_ci	strcat(result, right);
1258c2ecf20Sopenharmony_ci	return result;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int open_or_die(const char *filename, int flags)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	int fd = open(filename, flags);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (fd < 0) {
1338c2ecf20Sopenharmony_ci		printf("Failed to open '%s'; "
1348c2ecf20Sopenharmony_ci			"check prerequisites are available\n", filename);
1358c2ecf20Sopenharmony_ci		exit(1);
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci	return fd;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void exe_cp(const char *src, const char *dest)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	int in_fd = open_or_die(src, O_RDONLY);
1438c2ecf20Sopenharmony_ci	int out_fd = open(dest, O_RDWR|O_CREAT|O_TRUNC, 0755);
1448c2ecf20Sopenharmony_ci	struct stat info;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	fstat(in_fd, &info);
1478c2ecf20Sopenharmony_ci	sendfile(out_fd, in_fd, NULL, info.st_size);
1488c2ecf20Sopenharmony_ci	close(in_fd);
1498c2ecf20Sopenharmony_ci	close(out_fd);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci#define XX_DIR_LEN 200
1538c2ecf20Sopenharmony_cistatic int check_execveat_pathmax(int root_dfd, const char *src, int is_script)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	int fail = 0;
1568c2ecf20Sopenharmony_ci	int ii, count, len;
1578c2ecf20Sopenharmony_ci	char longname[XX_DIR_LEN + 1];
1588c2ecf20Sopenharmony_ci	int fd;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (*longpath == '\0') {
1618c2ecf20Sopenharmony_ci		/* Create a filename close to PATH_MAX in length */
1628c2ecf20Sopenharmony_ci		char *cwd = getcwd(NULL, 0);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci		if (!cwd) {
1658c2ecf20Sopenharmony_ci			printf("Failed to getcwd(), errno=%d (%s)\n",
1668c2ecf20Sopenharmony_ci			       errno, strerror(errno));
1678c2ecf20Sopenharmony_ci			return 2;
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci		strcpy(longpath, cwd);
1708c2ecf20Sopenharmony_ci		strcat(longpath, "/");
1718c2ecf20Sopenharmony_ci		memset(longname, 'x', XX_DIR_LEN - 1);
1728c2ecf20Sopenharmony_ci		longname[XX_DIR_LEN - 1] = '/';
1738c2ecf20Sopenharmony_ci		longname[XX_DIR_LEN] = '\0';
1748c2ecf20Sopenharmony_ci		count = (PATH_MAX - 3 - strlen(cwd)) / XX_DIR_LEN;
1758c2ecf20Sopenharmony_ci		for (ii = 0; ii < count; ii++) {
1768c2ecf20Sopenharmony_ci			strcat(longpath, longname);
1778c2ecf20Sopenharmony_ci			mkdir(longpath, 0755);
1788c2ecf20Sopenharmony_ci		}
1798c2ecf20Sopenharmony_ci		len = (PATH_MAX - 3 - strlen(cwd)) - (count * XX_DIR_LEN);
1808c2ecf20Sopenharmony_ci		if (len <= 0)
1818c2ecf20Sopenharmony_ci			len = 1;
1828c2ecf20Sopenharmony_ci		memset(longname, 'y', len);
1838c2ecf20Sopenharmony_ci		longname[len] = '\0';
1848c2ecf20Sopenharmony_ci		strcat(longpath, longname);
1858c2ecf20Sopenharmony_ci		free(cwd);
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci	exe_cp(src, longpath);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/*
1908c2ecf20Sopenharmony_ci	 * Execute as a pre-opened file descriptor, which works whether this is
1918c2ecf20Sopenharmony_ci	 * a script or not (because the interpreter sees a filename like
1928c2ecf20Sopenharmony_ci	 * "/dev/fd/20").
1938c2ecf20Sopenharmony_ci	 */
1948c2ecf20Sopenharmony_ci	fd = open(longpath, O_RDONLY);
1958c2ecf20Sopenharmony_ci	if (fd > 0) {
1968c2ecf20Sopenharmony_ci		printf("Invoke copy of '%s' via filename of length %zu:\n",
1978c2ecf20Sopenharmony_ci			src, strlen(longpath));
1988c2ecf20Sopenharmony_ci		fail += check_execveat(fd, "", AT_EMPTY_PATH);
1998c2ecf20Sopenharmony_ci	} else {
2008c2ecf20Sopenharmony_ci		printf("Failed to open length %zu filename, errno=%d (%s)\n",
2018c2ecf20Sopenharmony_ci			strlen(longpath), errno, strerror(errno));
2028c2ecf20Sopenharmony_ci		fail++;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/*
2068c2ecf20Sopenharmony_ci	 * Execute as a long pathname relative to "/".  If this is a script,
2078c2ecf20Sopenharmony_ci	 * the interpreter will launch but fail to open the script because its
2088c2ecf20Sopenharmony_ci	 * name ("/dev/fd/5/xxx....") is bigger than PATH_MAX.
2098c2ecf20Sopenharmony_ci	 *
2108c2ecf20Sopenharmony_ci	 * The failure code is usually 127 (POSIX: "If a command is not found,
2118c2ecf20Sopenharmony_ci	 * the exit status shall be 127."), but some systems give 126 (POSIX:
2128c2ecf20Sopenharmony_ci	 * "If the command name is found, but it is not an executable utility,
2138c2ecf20Sopenharmony_ci	 * the exit status shall be 126."), so allow either.
2148c2ecf20Sopenharmony_ci	 */
2158c2ecf20Sopenharmony_ci	if (is_script)
2168c2ecf20Sopenharmony_ci		fail += check_execveat_invoked_rc(root_dfd, longpath + 1, 0,
2178c2ecf20Sopenharmony_ci						  127, 126);
2188c2ecf20Sopenharmony_ci	else
2198c2ecf20Sopenharmony_ci		fail += check_execveat(root_dfd, longpath + 1, 0);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return fail;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int run_tests(void)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	int fail = 0;
2278c2ecf20Sopenharmony_ci	char *fullname = realpath("execveat", NULL);
2288c2ecf20Sopenharmony_ci	char *fullname_script = realpath("script", NULL);
2298c2ecf20Sopenharmony_ci	char *fullname_symlink = concat(fullname, ".symlink");
2308c2ecf20Sopenharmony_ci	int subdir_dfd = open_or_die("subdir", O_DIRECTORY|O_RDONLY);
2318c2ecf20Sopenharmony_ci	int subdir_dfd_ephemeral = open_or_die("subdir.ephemeral",
2328c2ecf20Sopenharmony_ci					       O_DIRECTORY|O_RDONLY);
2338c2ecf20Sopenharmony_ci	int dot_dfd = open_or_die(".", O_DIRECTORY|O_RDONLY);
2348c2ecf20Sopenharmony_ci	int root_dfd = open_or_die("/", O_DIRECTORY|O_RDONLY);
2358c2ecf20Sopenharmony_ci	int dot_dfd_path = open_or_die(".", O_DIRECTORY|O_RDONLY|O_PATH);
2368c2ecf20Sopenharmony_ci	int dot_dfd_cloexec = open_or_die(".", O_DIRECTORY|O_RDONLY|O_CLOEXEC);
2378c2ecf20Sopenharmony_ci	int fd = open_or_die("execveat", O_RDONLY);
2388c2ecf20Sopenharmony_ci	int fd_path = open_or_die("execveat", O_RDONLY|O_PATH);
2398c2ecf20Sopenharmony_ci	int fd_symlink = open_or_die("execveat.symlink", O_RDONLY);
2408c2ecf20Sopenharmony_ci	int fd_denatured = open_or_die("execveat.denatured", O_RDONLY);
2418c2ecf20Sopenharmony_ci	int fd_denatured_path = open_or_die("execveat.denatured",
2428c2ecf20Sopenharmony_ci					    O_RDONLY|O_PATH);
2438c2ecf20Sopenharmony_ci	int fd_script = open_or_die("script", O_RDONLY);
2448c2ecf20Sopenharmony_ci	int fd_ephemeral = open_or_die("execveat.ephemeral", O_RDONLY);
2458c2ecf20Sopenharmony_ci	int fd_ephemeral_path = open_or_die("execveat.path.ephemeral",
2468c2ecf20Sopenharmony_ci					    O_RDONLY|O_PATH);
2478c2ecf20Sopenharmony_ci	int fd_script_ephemeral = open_or_die("script.ephemeral", O_RDONLY);
2488c2ecf20Sopenharmony_ci	int fd_cloexec = open_or_die("execveat", O_RDONLY|O_CLOEXEC);
2498c2ecf20Sopenharmony_ci	int fd_script_cloexec = open_or_die("script", O_RDONLY|O_CLOEXEC);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/* Check if we have execveat at all, and bail early if not */
2528c2ecf20Sopenharmony_ci	errno = 0;
2538c2ecf20Sopenharmony_ci	execveat_(-1, NULL, NULL, NULL, 0);
2548c2ecf20Sopenharmony_ci	if (errno == ENOSYS) {
2558c2ecf20Sopenharmony_ci		ksft_exit_skip(
2568c2ecf20Sopenharmony_ci			"ENOSYS calling execveat - no kernel support?\n");
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/* Change file position to confirm it doesn't affect anything */
2608c2ecf20Sopenharmony_ci	lseek(fd, 10, SEEK_SET);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/* Normal executable file: */
2638c2ecf20Sopenharmony_ci	/*   dfd + path */
2648c2ecf20Sopenharmony_ci	fail += check_execveat(subdir_dfd, "../execveat", 0);
2658c2ecf20Sopenharmony_ci	fail += check_execveat(dot_dfd, "execveat", 0);
2668c2ecf20Sopenharmony_ci	fail += check_execveat(dot_dfd_path, "execveat", 0);
2678c2ecf20Sopenharmony_ci	/*   absolute path */
2688c2ecf20Sopenharmony_ci	fail += check_execveat(AT_FDCWD, fullname, 0);
2698c2ecf20Sopenharmony_ci	/*   absolute path with nonsense dfd */
2708c2ecf20Sopenharmony_ci	fail += check_execveat(99, fullname, 0);
2718c2ecf20Sopenharmony_ci	/*   fd + no path */
2728c2ecf20Sopenharmony_ci	fail += check_execveat(fd, "", AT_EMPTY_PATH);
2738c2ecf20Sopenharmony_ci	/*   O_CLOEXEC fd + no path */
2748c2ecf20Sopenharmony_ci	fail += check_execveat(fd_cloexec, "", AT_EMPTY_PATH);
2758c2ecf20Sopenharmony_ci	/*   O_PATH fd */
2768c2ecf20Sopenharmony_ci	fail += check_execveat(fd_path, "", AT_EMPTY_PATH);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* Mess with executable file that's already open: */
2798c2ecf20Sopenharmony_ci	/*   fd + no path to a file that's been renamed */
2808c2ecf20Sopenharmony_ci	rename("execveat.ephemeral", "execveat.moved");
2818c2ecf20Sopenharmony_ci	fail += check_execveat(fd_ephemeral, "", AT_EMPTY_PATH);
2828c2ecf20Sopenharmony_ci	/*   fd + no path to a file that's been deleted */
2838c2ecf20Sopenharmony_ci	unlink("execveat.moved"); /* remove the file now fd open */
2848c2ecf20Sopenharmony_ci	fail += check_execveat(fd_ephemeral, "", AT_EMPTY_PATH);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	/* Mess with executable file that's already open with O_PATH */
2878c2ecf20Sopenharmony_ci	/*   fd + no path to a file that's been deleted */
2888c2ecf20Sopenharmony_ci	unlink("execveat.path.ephemeral");
2898c2ecf20Sopenharmony_ci	fail += check_execveat(fd_ephemeral_path, "", AT_EMPTY_PATH);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/* Invalid argument failures */
2928c2ecf20Sopenharmony_ci	fail += check_execveat_fail(fd, "", 0, ENOENT);
2938c2ecf20Sopenharmony_ci	fail += check_execveat_fail(fd, NULL, AT_EMPTY_PATH, EFAULT);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* Symlink to executable file: */
2968c2ecf20Sopenharmony_ci	/*   dfd + path */
2978c2ecf20Sopenharmony_ci	fail += check_execveat(dot_dfd, "execveat.symlink", 0);
2988c2ecf20Sopenharmony_ci	fail += check_execveat(dot_dfd_path, "execveat.symlink", 0);
2998c2ecf20Sopenharmony_ci	/*   absolute path */
3008c2ecf20Sopenharmony_ci	fail += check_execveat(AT_FDCWD, fullname_symlink, 0);
3018c2ecf20Sopenharmony_ci	/*   fd + no path, even with AT_SYMLINK_NOFOLLOW (already followed) */
3028c2ecf20Sopenharmony_ci	fail += check_execveat(fd_symlink, "", AT_EMPTY_PATH);
3038c2ecf20Sopenharmony_ci	fail += check_execveat(fd_symlink, "",
3048c2ecf20Sopenharmony_ci			       AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* Symlink fails when AT_SYMLINK_NOFOLLOW set: */
3078c2ecf20Sopenharmony_ci	/*   dfd + path */
3088c2ecf20Sopenharmony_ci	fail += check_execveat_fail(dot_dfd, "execveat.symlink",
3098c2ecf20Sopenharmony_ci				    AT_SYMLINK_NOFOLLOW, ELOOP);
3108c2ecf20Sopenharmony_ci	fail += check_execveat_fail(dot_dfd_path, "execveat.symlink",
3118c2ecf20Sopenharmony_ci				    AT_SYMLINK_NOFOLLOW, ELOOP);
3128c2ecf20Sopenharmony_ci	/*   absolute path */
3138c2ecf20Sopenharmony_ci	fail += check_execveat_fail(AT_FDCWD, fullname_symlink,
3148c2ecf20Sopenharmony_ci				    AT_SYMLINK_NOFOLLOW, ELOOP);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/*  Non-regular file failure */
3178c2ecf20Sopenharmony_ci	fail += check_execveat_fail(dot_dfd, "pipe", 0, EACCES);
3188c2ecf20Sopenharmony_ci	unlink("pipe");
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* Shell script wrapping executable file: */
3218c2ecf20Sopenharmony_ci	/*   dfd + path */
3228c2ecf20Sopenharmony_ci	fail += check_execveat(subdir_dfd, "../script", 0);
3238c2ecf20Sopenharmony_ci	fail += check_execveat(dot_dfd, "script", 0);
3248c2ecf20Sopenharmony_ci	fail += check_execveat(dot_dfd_path, "script", 0);
3258c2ecf20Sopenharmony_ci	/*   absolute path */
3268c2ecf20Sopenharmony_ci	fail += check_execveat(AT_FDCWD, fullname_script, 0);
3278c2ecf20Sopenharmony_ci	/*   fd + no path */
3288c2ecf20Sopenharmony_ci	fail += check_execveat(fd_script, "", AT_EMPTY_PATH);
3298c2ecf20Sopenharmony_ci	fail += check_execveat(fd_script, "",
3308c2ecf20Sopenharmony_ci			       AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW);
3318c2ecf20Sopenharmony_ci	/*   O_CLOEXEC fd fails for a script (as script file inaccessible) */
3328c2ecf20Sopenharmony_ci	fail += check_execveat_fail(fd_script_cloexec, "", AT_EMPTY_PATH,
3338c2ecf20Sopenharmony_ci				    ENOENT);
3348c2ecf20Sopenharmony_ci	fail += check_execveat_fail(dot_dfd_cloexec, "script", 0, ENOENT);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* Mess with script file that's already open: */
3378c2ecf20Sopenharmony_ci	/*   fd + no path to a file that's been renamed */
3388c2ecf20Sopenharmony_ci	rename("script.ephemeral", "script.moved");
3398c2ecf20Sopenharmony_ci	fail += check_execveat(fd_script_ephemeral, "", AT_EMPTY_PATH);
3408c2ecf20Sopenharmony_ci	/*   fd + no path to a file that's been deleted */
3418c2ecf20Sopenharmony_ci	unlink("script.moved"); /* remove the file while fd open */
3428c2ecf20Sopenharmony_ci	fail += check_execveat(fd_script_ephemeral, "", AT_EMPTY_PATH);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/* Rename a subdirectory in the path: */
3458c2ecf20Sopenharmony_ci	rename("subdir.ephemeral", "subdir.moved");
3468c2ecf20Sopenharmony_ci	fail += check_execveat(subdir_dfd_ephemeral, "../script", 0);
3478c2ecf20Sopenharmony_ci	fail += check_execveat(subdir_dfd_ephemeral, "script", 0);
3488c2ecf20Sopenharmony_ci	/* Remove the subdir and its contents */
3498c2ecf20Sopenharmony_ci	unlink("subdir.moved/script");
3508c2ecf20Sopenharmony_ci	unlink("subdir.moved");
3518c2ecf20Sopenharmony_ci	/* Shell loads via deleted subdir OK because name starts with .. */
3528c2ecf20Sopenharmony_ci	fail += check_execveat(subdir_dfd_ephemeral, "../script", 0);
3538c2ecf20Sopenharmony_ci	fail += check_execveat_fail(subdir_dfd_ephemeral, "script", 0, ENOENT);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* Flag values other than AT_SYMLINK_NOFOLLOW => EINVAL */
3568c2ecf20Sopenharmony_ci	fail += check_execveat_fail(dot_dfd, "execveat", 0xFFFF, EINVAL);
3578c2ecf20Sopenharmony_ci	/* Invalid path => ENOENT */
3588c2ecf20Sopenharmony_ci	fail += check_execveat_fail(dot_dfd, "no-such-file", 0, ENOENT);
3598c2ecf20Sopenharmony_ci	fail += check_execveat_fail(dot_dfd_path, "no-such-file", 0, ENOENT);
3608c2ecf20Sopenharmony_ci	fail += check_execveat_fail(AT_FDCWD, "no-such-file", 0, ENOENT);
3618c2ecf20Sopenharmony_ci	/* Attempt to execute directory => EACCES */
3628c2ecf20Sopenharmony_ci	fail += check_execveat_fail(dot_dfd, "", AT_EMPTY_PATH, EACCES);
3638c2ecf20Sopenharmony_ci	/* Attempt to execute non-executable => EACCES */
3648c2ecf20Sopenharmony_ci	fail += check_execveat_fail(dot_dfd, "Makefile", 0, EACCES);
3658c2ecf20Sopenharmony_ci	fail += check_execveat_fail(fd_denatured, "", AT_EMPTY_PATH, EACCES);
3668c2ecf20Sopenharmony_ci	fail += check_execveat_fail(fd_denatured_path, "", AT_EMPTY_PATH,
3678c2ecf20Sopenharmony_ci				    EACCES);
3688c2ecf20Sopenharmony_ci	/* Attempt to execute nonsense FD => EBADF */
3698c2ecf20Sopenharmony_ci	fail += check_execveat_fail(99, "", AT_EMPTY_PATH, EBADF);
3708c2ecf20Sopenharmony_ci	fail += check_execveat_fail(99, "execveat", 0, EBADF);
3718c2ecf20Sopenharmony_ci	/* Attempt to execute relative to non-directory => ENOTDIR */
3728c2ecf20Sopenharmony_ci	fail += check_execveat_fail(fd, "execveat", 0, ENOTDIR);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	fail += check_execveat_pathmax(root_dfd, "execveat", 0);
3758c2ecf20Sopenharmony_ci	fail += check_execveat_pathmax(root_dfd, "script", 1);
3768c2ecf20Sopenharmony_ci	return fail;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic void prerequisites(void)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	int fd;
3828c2ecf20Sopenharmony_ci	const char *script = "#!/bin/sh\nexit $*\n";
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* Create ephemeral copies of files */
3858c2ecf20Sopenharmony_ci	exe_cp("execveat", "execveat.ephemeral");
3868c2ecf20Sopenharmony_ci	exe_cp("execveat", "execveat.path.ephemeral");
3878c2ecf20Sopenharmony_ci	exe_cp("script", "script.ephemeral");
3888c2ecf20Sopenharmony_ci	mkdir("subdir.ephemeral", 0755);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	fd = open("subdir.ephemeral/script", O_RDWR|O_CREAT|O_TRUNC, 0755);
3918c2ecf20Sopenharmony_ci	write(fd, script, strlen(script));
3928c2ecf20Sopenharmony_ci	close(fd);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	mkfifo("pipe", 0755);
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ciint main(int argc, char **argv)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	int ii;
4008c2ecf20Sopenharmony_ci	int rc;
4018c2ecf20Sopenharmony_ci	const char *verbose = getenv("VERBOSE");
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	if (argc >= 2) {
4048c2ecf20Sopenharmony_ci		/* If we are invoked with an argument, don't run tests. */
4058c2ecf20Sopenharmony_ci		const char *in_test = getenv("IN_TEST");
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		if (verbose) {
4088c2ecf20Sopenharmony_ci			printf("  invoked with:");
4098c2ecf20Sopenharmony_ci			for (ii = 0; ii < argc; ii++)
4108c2ecf20Sopenharmony_ci				printf(" [%d]='%s'", ii, argv[ii]);
4118c2ecf20Sopenharmony_ci			printf("\n");
4128c2ecf20Sopenharmony_ci		}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci		/* Check expected environment transferred. */
4158c2ecf20Sopenharmony_ci		if (!in_test || strcmp(in_test, "yes") != 0) {
4168c2ecf20Sopenharmony_ci			printf("[FAIL] (no IN_TEST=yes in env)\n");
4178c2ecf20Sopenharmony_ci			return 1;
4188c2ecf20Sopenharmony_ci		}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		/* Use the final argument as an exit code. */
4218c2ecf20Sopenharmony_ci		rc = atoi(argv[argc - 1]);
4228c2ecf20Sopenharmony_ci		fflush(stdout);
4238c2ecf20Sopenharmony_ci	} else {
4248c2ecf20Sopenharmony_ci		prerequisites();
4258c2ecf20Sopenharmony_ci		if (verbose)
4268c2ecf20Sopenharmony_ci			envp[1] = "VERBOSE=1";
4278c2ecf20Sopenharmony_ci		rc = run_tests();
4288c2ecf20Sopenharmony_ci		if (rc > 0)
4298c2ecf20Sopenharmony_ci			printf("%d tests failed\n", rc);
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci	return rc;
4328c2ecf20Sopenharmony_ci}
433