0001
0002
0003
0004
0005
0006
0007
0008 #ifndef _GNU_SOURCE
0009 #define _GNU_SOURCE
0010 #endif
0011 #include <sys/sendfile.h>
0012 #include <sys/stat.h>
0013 #include <sys/syscall.h>
0014 #include <sys/types.h>
0015 #include <sys/wait.h>
0016 #include <errno.h>
0017 #include <fcntl.h>
0018 #include <limits.h>
0019 #include <stdio.h>
0020 #include <stdlib.h>
0021 #include <string.h>
0022 #include <unistd.h>
0023
0024 #include "../kselftest.h"
0025
0026 static char longpath[2 * PATH_MAX] = "";
0027 static char *envp[] = { "IN_TEST=yes", NULL, NULL };
0028 static char *argv[] = { "execveat", "99", NULL };
0029
0030 static int execveat_(int fd, const char *path, char **argv, char **envp,
0031 int flags)
0032 {
0033 #ifdef __NR_execveat
0034 return syscall(__NR_execveat, fd, path, argv, envp, flags);
0035 #else
0036 errno = ENOSYS;
0037 return -1;
0038 #endif
0039 }
0040
0041 #define check_execveat_fail(fd, path, flags, errno) \
0042 _check_execveat_fail(fd, path, flags, errno, #errno)
0043 static int _check_execveat_fail(int fd, const char *path, int flags,
0044 int expected_errno, const char *errno_str)
0045 {
0046 int rc;
0047
0048 errno = 0;
0049 printf("Check failure of execveat(%d, '%s', %d) with %s... ",
0050 fd, path?:"(null)", flags, errno_str);
0051 rc = execveat_(fd, path, argv, envp, flags);
0052
0053 if (rc > 0) {
0054 printf("[FAIL] (unexpected success from execveat(2))\n");
0055 return 1;
0056 }
0057 if (errno != expected_errno) {
0058 printf("[FAIL] (expected errno %d (%s) not %d (%s)\n",
0059 expected_errno, strerror(expected_errno),
0060 errno, strerror(errno));
0061 return 1;
0062 }
0063 printf("[OK]\n");
0064 return 0;
0065 }
0066
0067 static int check_execveat_invoked_rc(int fd, const char *path, int flags,
0068 int expected_rc, int expected_rc2)
0069 {
0070 int status;
0071 int rc;
0072 pid_t child;
0073 int pathlen = path ? strlen(path) : 0;
0074
0075 if (pathlen > 40)
0076 printf("Check success of execveat(%d, '%.20s...%s', %d)... ",
0077 fd, path, (path + pathlen - 20), flags);
0078 else
0079 printf("Check success of execveat(%d, '%s', %d)... ",
0080 fd, path?:"(null)", flags);
0081 child = fork();
0082 if (child < 0) {
0083 printf("[FAIL] (fork() failed)\n");
0084 return 1;
0085 }
0086 if (child == 0) {
0087
0088 rc = execveat_(fd, path, argv, envp, flags);
0089 printf("[FAIL]: execveat() failed, rc=%d errno=%d (%s)\n",
0090 rc, errno, strerror(errno));
0091 exit(1);
0092 }
0093
0094 rc = waitpid(child, &status, 0);
0095 if (rc != child) {
0096 printf("[FAIL] (waitpid(%d,...) returned %d)\n", child, rc);
0097 return 1;
0098 }
0099 if (!WIFEXITED(status)) {
0100 printf("[FAIL] (child %d did not exit cleanly, status=%08x)\n",
0101 child, status);
0102 return 1;
0103 }
0104 if ((WEXITSTATUS(status) != expected_rc) &&
0105 (WEXITSTATUS(status) != expected_rc2)) {
0106 printf("[FAIL] (child %d exited with %d not %d nor %d)\n",
0107 child, WEXITSTATUS(status), expected_rc, expected_rc2);
0108 return 1;
0109 }
0110 printf("[OK]\n");
0111 return 0;
0112 }
0113
0114 static int check_execveat(int fd, const char *path, int flags)
0115 {
0116 return check_execveat_invoked_rc(fd, path, flags, 99, 99);
0117 }
0118
0119 static char *concat(const char *left, const char *right)
0120 {
0121 char *result = malloc(strlen(left) + strlen(right) + 1);
0122
0123 strcpy(result, left);
0124 strcat(result, right);
0125 return result;
0126 }
0127
0128 static int open_or_die(const char *filename, int flags)
0129 {
0130 int fd = open(filename, flags);
0131
0132 if (fd < 0) {
0133 printf("Failed to open '%s'; "
0134 "check prerequisites are available\n", filename);
0135 exit(1);
0136 }
0137 return fd;
0138 }
0139
0140 static void exe_cp(const char *src, const char *dest)
0141 {
0142 int in_fd = open_or_die(src, O_RDONLY);
0143 int out_fd = open(dest, O_RDWR|O_CREAT|O_TRUNC, 0755);
0144 struct stat info;
0145
0146 fstat(in_fd, &info);
0147 sendfile(out_fd, in_fd, NULL, info.st_size);
0148 close(in_fd);
0149 close(out_fd);
0150 }
0151
0152 #define XX_DIR_LEN 200
0153 static int check_execveat_pathmax(int root_dfd, const char *src, int is_script)
0154 {
0155 int fail = 0;
0156 int ii, count, len;
0157 char longname[XX_DIR_LEN + 1];
0158 int fd;
0159
0160 if (*longpath == '\0') {
0161
0162 char *cwd = getcwd(NULL, 0);
0163
0164 if (!cwd) {
0165 printf("Failed to getcwd(), errno=%d (%s)\n",
0166 errno, strerror(errno));
0167 return 2;
0168 }
0169 strcpy(longpath, cwd);
0170 strcat(longpath, "/");
0171 memset(longname, 'x', XX_DIR_LEN - 1);
0172 longname[XX_DIR_LEN - 1] = '/';
0173 longname[XX_DIR_LEN] = '\0';
0174 count = (PATH_MAX - 3 - strlen(cwd)) / XX_DIR_LEN;
0175 for (ii = 0; ii < count; ii++) {
0176 strcat(longpath, longname);
0177 mkdir(longpath, 0755);
0178 }
0179 len = (PATH_MAX - 3 - strlen(cwd)) - (count * XX_DIR_LEN);
0180 if (len <= 0)
0181 len = 1;
0182 memset(longname, 'y', len);
0183 longname[len] = '\0';
0184 strcat(longpath, longname);
0185 free(cwd);
0186 }
0187 exe_cp(src, longpath);
0188
0189
0190
0191
0192
0193
0194 fd = open(longpath, O_RDONLY);
0195 if (fd > 0) {
0196 printf("Invoke copy of '%s' via filename of length %zu:\n",
0197 src, strlen(longpath));
0198 fail += check_execveat(fd, "", AT_EMPTY_PATH);
0199 } else {
0200 printf("Failed to open length %zu filename, errno=%d (%s)\n",
0201 strlen(longpath), errno, strerror(errno));
0202 fail++;
0203 }
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215 if (is_script)
0216 fail += check_execveat_invoked_rc(root_dfd, longpath + 1, 0,
0217 127, 126);
0218 else
0219 fail += check_execveat(root_dfd, longpath + 1, 0);
0220
0221 return fail;
0222 }
0223
0224 static int run_tests(void)
0225 {
0226 int fail = 0;
0227 char *fullname = realpath("execveat", NULL);
0228 char *fullname_script = realpath("script", NULL);
0229 char *fullname_symlink = concat(fullname, ".symlink");
0230 int subdir_dfd = open_or_die("subdir", O_DIRECTORY|O_RDONLY);
0231 int subdir_dfd_ephemeral = open_or_die("subdir.ephemeral",
0232 O_DIRECTORY|O_RDONLY);
0233 int dot_dfd = open_or_die(".", O_DIRECTORY|O_RDONLY);
0234 int root_dfd = open_or_die("/", O_DIRECTORY|O_RDONLY);
0235 int dot_dfd_path = open_or_die(".", O_DIRECTORY|O_RDONLY|O_PATH);
0236 int dot_dfd_cloexec = open_or_die(".", O_DIRECTORY|O_RDONLY|O_CLOEXEC);
0237 int fd = open_or_die("execveat", O_RDONLY);
0238 int fd_path = open_or_die("execveat", O_RDONLY|O_PATH);
0239 int fd_symlink = open_or_die("execveat.symlink", O_RDONLY);
0240 int fd_denatured = open_or_die("execveat.denatured", O_RDONLY);
0241 int fd_denatured_path = open_or_die("execveat.denatured",
0242 O_RDONLY|O_PATH);
0243 int fd_script = open_or_die("script", O_RDONLY);
0244 int fd_ephemeral = open_or_die("execveat.ephemeral", O_RDONLY);
0245 int fd_ephemeral_path = open_or_die("execveat.path.ephemeral",
0246 O_RDONLY|O_PATH);
0247 int fd_script_ephemeral = open_or_die("script.ephemeral", O_RDONLY);
0248 int fd_cloexec = open_or_die("execveat", O_RDONLY|O_CLOEXEC);
0249 int fd_script_cloexec = open_or_die("script", O_RDONLY|O_CLOEXEC);
0250
0251
0252 errno = 0;
0253 execveat_(-1, NULL, NULL, NULL, 0);
0254 if (errno == ENOSYS) {
0255 ksft_exit_skip(
0256 "ENOSYS calling execveat - no kernel support?\n");
0257 }
0258
0259
0260 lseek(fd, 10, SEEK_SET);
0261
0262
0263
0264 fail += check_execveat(subdir_dfd, "../execveat", 0);
0265 fail += check_execveat(dot_dfd, "execveat", 0);
0266 fail += check_execveat(dot_dfd_path, "execveat", 0);
0267
0268 fail += check_execveat(AT_FDCWD, fullname, 0);
0269
0270 fail += check_execveat(99, fullname, 0);
0271
0272 fail += check_execveat(fd, "", AT_EMPTY_PATH);
0273
0274 fail += check_execveat(fd_cloexec, "", AT_EMPTY_PATH);
0275
0276 fail += check_execveat(fd_path, "", AT_EMPTY_PATH);
0277
0278
0279
0280 rename("execveat.ephemeral", "execveat.moved");
0281 fail += check_execveat(fd_ephemeral, "", AT_EMPTY_PATH);
0282
0283 unlink("execveat.moved");
0284 fail += check_execveat(fd_ephemeral, "", AT_EMPTY_PATH);
0285
0286
0287
0288 unlink("execveat.path.ephemeral");
0289 fail += check_execveat(fd_ephemeral_path, "", AT_EMPTY_PATH);
0290
0291
0292 fail += check_execveat_fail(fd, "", 0, ENOENT);
0293 fail += check_execveat_fail(fd, NULL, AT_EMPTY_PATH, EFAULT);
0294
0295
0296
0297 fail += check_execveat(dot_dfd, "execveat.symlink", 0);
0298 fail += check_execveat(dot_dfd_path, "execveat.symlink", 0);
0299
0300 fail += check_execveat(AT_FDCWD, fullname_symlink, 0);
0301
0302 fail += check_execveat(fd_symlink, "", AT_EMPTY_PATH);
0303 fail += check_execveat(fd_symlink, "",
0304 AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW);
0305
0306
0307
0308 fail += check_execveat_fail(dot_dfd, "execveat.symlink",
0309 AT_SYMLINK_NOFOLLOW, ELOOP);
0310 fail += check_execveat_fail(dot_dfd_path, "execveat.symlink",
0311 AT_SYMLINK_NOFOLLOW, ELOOP);
0312
0313 fail += check_execveat_fail(AT_FDCWD, fullname_symlink,
0314 AT_SYMLINK_NOFOLLOW, ELOOP);
0315
0316
0317 fail += check_execveat_fail(dot_dfd, "pipe", 0, EACCES);
0318 unlink("pipe");
0319
0320
0321
0322 fail += check_execveat(subdir_dfd, "../script", 0);
0323 fail += check_execveat(dot_dfd, "script", 0);
0324 fail += check_execveat(dot_dfd_path, "script", 0);
0325
0326 fail += check_execveat(AT_FDCWD, fullname_script, 0);
0327
0328 fail += check_execveat(fd_script, "", AT_EMPTY_PATH);
0329 fail += check_execveat(fd_script, "",
0330 AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW);
0331
0332 fail += check_execveat_fail(fd_script_cloexec, "", AT_EMPTY_PATH,
0333 ENOENT);
0334 fail += check_execveat_fail(dot_dfd_cloexec, "script", 0, ENOENT);
0335
0336
0337
0338 rename("script.ephemeral", "script.moved");
0339 fail += check_execveat(fd_script_ephemeral, "", AT_EMPTY_PATH);
0340
0341 unlink("script.moved");
0342 fail += check_execveat(fd_script_ephemeral, "", AT_EMPTY_PATH);
0343
0344
0345 rename("subdir.ephemeral", "subdir.moved");
0346 fail += check_execveat(subdir_dfd_ephemeral, "../script", 0);
0347 fail += check_execveat(subdir_dfd_ephemeral, "script", 0);
0348
0349 unlink("subdir.moved/script");
0350 unlink("subdir.moved");
0351
0352 fail += check_execveat(subdir_dfd_ephemeral, "../script", 0);
0353 fail += check_execveat_fail(subdir_dfd_ephemeral, "script", 0, ENOENT);
0354
0355
0356 fail += check_execveat_fail(dot_dfd, "execveat", 0xFFFF, EINVAL);
0357
0358 fail += check_execveat_fail(dot_dfd, "no-such-file", 0, ENOENT);
0359 fail += check_execveat_fail(dot_dfd_path, "no-such-file", 0, ENOENT);
0360 fail += check_execveat_fail(AT_FDCWD, "no-such-file", 0, ENOENT);
0361
0362 fail += check_execveat_fail(dot_dfd, "", AT_EMPTY_PATH, EACCES);
0363
0364 fail += check_execveat_fail(dot_dfd, "Makefile", 0, EACCES);
0365 fail += check_execveat_fail(fd_denatured, "", AT_EMPTY_PATH, EACCES);
0366 fail += check_execveat_fail(fd_denatured_path, "", AT_EMPTY_PATH,
0367 EACCES);
0368
0369 fail += check_execveat_fail(99, "", AT_EMPTY_PATH, EBADF);
0370 fail += check_execveat_fail(99, "execveat", 0, EBADF);
0371
0372 fail += check_execveat_fail(fd, "execveat", 0, ENOTDIR);
0373
0374 fail += check_execveat_pathmax(root_dfd, "execveat", 0);
0375 fail += check_execveat_pathmax(root_dfd, "script", 1);
0376 return fail;
0377 }
0378
0379 static void prerequisites(void)
0380 {
0381 int fd;
0382 const char *script = "#!/bin/sh\nexit $*\n";
0383
0384
0385 exe_cp("execveat", "execveat.ephemeral");
0386 exe_cp("execveat", "execveat.path.ephemeral");
0387 exe_cp("script", "script.ephemeral");
0388 mkdir("subdir.ephemeral", 0755);
0389
0390 fd = open("subdir.ephemeral/script", O_RDWR|O_CREAT|O_TRUNC, 0755);
0391 write(fd, script, strlen(script));
0392 close(fd);
0393
0394 mkfifo("pipe", 0755);
0395 }
0396
0397 int main(int argc, char **argv)
0398 {
0399 int ii;
0400 int rc;
0401 const char *verbose = getenv("VERBOSE");
0402
0403 if (argc >= 2) {
0404
0405 const char *in_test = getenv("IN_TEST");
0406
0407 if (verbose) {
0408 printf(" invoked with:");
0409 for (ii = 0; ii < argc; ii++)
0410 printf(" [%d]='%s'", ii, argv[ii]);
0411 printf("\n");
0412 }
0413
0414
0415 if (!in_test || strcmp(in_test, "yes") != 0) {
0416 printf("[FAIL] (no IN_TEST=yes in env)\n");
0417 return 1;
0418 }
0419
0420
0421 rc = atoi(argv[argc - 1]);
0422 fflush(stdout);
0423 } else {
0424 prerequisites();
0425 if (verbose)
0426 envp[1] = "VERBOSE=1";
0427 rc = run_tests();
0428 if (rc > 0)
0429 printf("%d tests failed\n", rc);
0430 }
0431 return rc;
0432 }