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