1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2014 Google, Inc. 4 * 5 * Selftests for execveat(2). 6 */ 7 8 #ifndef _GNU_SOURCE 9 #define _GNU_SOURCE /* to get O_PATH, AT_EMPTY_PATH */ 10 #endif 11 #include <sys/sendfile.h> 12 #include <sys/stat.h> 13 #include <sys/syscall.h> 14 #include <sys/types.h> 15 #include <sys/wait.h> 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <limits.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <string.h> 22 #include <unistd.h> 23 24 #include "../kselftest.h" 25 26 #define TESTS_EXPECTED 54 27 #define TEST_NAME_LEN (PATH_MAX * 4) 28 29 #define CHECK_COMM "CHECK_COMM" 30 31 static char longpath[2 * PATH_MAX] = ""; 32 static char *envp[] = { "IN_TEST=yes", NULL, NULL }; 33 static char *argv[] = { "execveat", "99", NULL }; 34 35 static int execveat_(int fd, const char *path, char **argv, char **envp, 36 int flags) 37 { 38 #ifdef __NR_execveat 39 return syscall(__NR_execveat, fd, path, argv, envp, flags); 40 #else 41 errno = ENOSYS; 42 return -1; 43 #endif 44 } 45 46 #define check_execveat_fail(fd, path, flags, errno) \ 47 _check_execveat_fail(fd, path, flags, errno, #errno) 48 static int _check_execveat_fail(int fd, const char *path, int flags, 49 int expected_errno, const char *errno_str) 50 { 51 char test_name[TEST_NAME_LEN]; 52 int rc; 53 54 errno = 0; 55 snprintf(test_name, sizeof(test_name), 56 "Check failure of execveat(%d, '%s', %d) with %s", 57 fd, path?:"(null)", flags, errno_str); 58 rc = execveat_(fd, path, argv, envp, flags); 59 60 if (rc > 0) { 61 ksft_print_msg("unexpected success from execveat(2)\n"); 62 ksft_test_result_fail("%s\n", test_name); 63 return 1; 64 } 65 if (errno != expected_errno) { 66 ksft_print_msg("expected errno %d (%s) not %d (%s)\n", 67 expected_errno, strerror(expected_errno), 68 errno, strerror(errno)); 69 ksft_test_result_fail("%s\n", test_name); 70 return 1; 71 } 72 ksft_test_result_pass("%s\n", test_name); 73 return 0; 74 } 75 76 static int check_execveat_invoked_rc(int fd, const char *path, int flags, 77 int expected_rc, int expected_rc2) 78 { 79 char test_name[TEST_NAME_LEN]; 80 int status; 81 int rc; 82 pid_t child; 83 int pathlen = path ? strlen(path) : 0; 84 85 if (pathlen > 40) 86 snprintf(test_name, sizeof(test_name), 87 "Check success of execveat(%d, '%.20s...%s', %d)... ", 88 fd, path, (path + pathlen - 20), flags); 89 else 90 snprintf(test_name, sizeof(test_name), 91 "Check success of execveat(%d, '%s', %d)... ", 92 fd, path?:"(null)", flags); 93 94 child = fork(); 95 if (child < 0) { 96 ksft_perror("fork() failed"); 97 ksft_test_result_fail("%s\n", test_name); 98 return 1; 99 } 100 if (child == 0) { 101 /* Child: do execveat(). */ 102 rc = execveat_(fd, path, argv, envp, flags); 103 ksft_print_msg("child execveat() failed, rc=%d errno=%d (%s)\n", 104 rc, errno, strerror(errno)); 105 exit(errno); 106 } 107 /* Parent: wait for & check child's exit status. */ 108 rc = waitpid(child, &status, 0); 109 if (rc != child) { 110 ksft_print_msg("waitpid(%d,...) returned %d\n", child, rc); 111 ksft_test_result_fail("%s\n", test_name); 112 return 1; 113 } 114 if (!WIFEXITED(status)) { 115 ksft_print_msg("child %d did not exit cleanly, status=%08x\n", 116 child, status); 117 ksft_test_result_fail("%s\n", test_name); 118 return 1; 119 } 120 if ((WEXITSTATUS(status) != expected_rc) && 121 (WEXITSTATUS(status) != expected_rc2)) { 122 ksft_print_msg("child %d exited with %d neither %d nor %d\n", 123 child, WEXITSTATUS(status), expected_rc, 124 expected_rc2); 125 ksft_test_result_fail("%s\n", test_name); 126 return 1; 127 } 128 ksft_test_result_pass("%s\n", test_name); 129 return 0; 130 } 131 132 static int check_execveat(int fd, const char *path, int flags) 133 { 134 return check_execveat_invoked_rc(fd, path, flags, 99, 99); 135 } 136 137 static char *concat(const char *left, const char *right) 138 { 139 char *result = malloc(strlen(left) + strlen(right) + 1); 140 141 strcpy(result, left); 142 strcat(result, right); 143 return result; 144 } 145 146 static int open_or_die(const char *filename, int flags) 147 { 148 int fd = open(filename, flags); 149 150 if (fd < 0) 151 ksft_exit_fail_msg("Failed to open '%s'; " 152 "check prerequisites are available\n", filename); 153 return fd; 154 } 155 156 static void exe_cp(const char *src, const char *dest) 157 { 158 int in_fd = open_or_die(src, O_RDONLY); 159 int out_fd = open(dest, O_RDWR|O_CREAT|O_TRUNC, 0755); 160 struct stat info; 161 162 fstat(in_fd, &info); 163 sendfile(out_fd, in_fd, NULL, info.st_size); 164 close(in_fd); 165 close(out_fd); 166 } 167 168 #define XX_DIR_LEN 200 169 static int check_execveat_pathmax(int root_dfd, const char *src, int is_script) 170 { 171 int fail = 0; 172 int ii, count, len; 173 char longname[XX_DIR_LEN + 1]; 174 int fd; 175 176 if (*longpath == '\0') { 177 /* Create a filename close to PATH_MAX in length */ 178 char *cwd = getcwd(NULL, 0); 179 180 if (!cwd) { 181 ksft_perror("Failed to getcwd()"); 182 return 2; 183 } 184 strcpy(longpath, cwd); 185 strcat(longpath, "/"); 186 memset(longname, 'x', XX_DIR_LEN - 1); 187 longname[XX_DIR_LEN - 1] = '/'; 188 longname[XX_DIR_LEN] = '\0'; 189 count = (PATH_MAX - 3 - strlen(cwd)) / XX_DIR_LEN; 190 for (ii = 0; ii < count; ii++) { 191 strcat(longpath, longname); 192 mkdir(longpath, 0755); 193 } 194 len = (PATH_MAX - 3 - strlen(cwd)) - (count * XX_DIR_LEN); 195 if (len <= 0) 196 len = 1; 197 memset(longname, 'y', len); 198 longname[len] = '\0'; 199 strcat(longpath, longname); 200 free(cwd); 201 } 202 exe_cp(src, longpath); 203 204 /* 205 * Execute as a pre-opened file descriptor, which works whether this is 206 * a script or not (because the interpreter sees a filename like 207 * "/dev/fd/20"). 208 */ 209 fd = open(longpath, O_RDONLY); 210 if (fd > 0) { 211 ksft_print_msg("Invoke copy of '%s' via filename of length %zu:\n", 212 src, strlen(longpath)); 213 fail += check_execveat(fd, "", AT_EMPTY_PATH); 214 } else { 215 ksft_print_msg("Failed to open length %zu filename, errno=%d (%s)\n", 216 strlen(longpath), errno, strerror(errno)); 217 fail++; 218 } 219 220 /* 221 * Execute as a long pathname relative to "/". If this is a script, 222 * the interpreter will launch but fail to open the script because its 223 * name ("/dev/fd/5/xxx....") is bigger than PATH_MAX. 224 * 225 * The failure code is usually 127 (POSIX: "If a command is not found, 226 * the exit status shall be 127."), but some systems give 126 (POSIX: 227 * "If the command name is found, but it is not an executable utility, 228 * the exit status shall be 126."), so allow either. 229 */ 230 if (is_script) { 231 ksft_print_msg("Invoke script via root_dfd and relative filename\n"); 232 fail += check_execveat_invoked_rc(root_dfd, longpath + 1, 0, 233 127, 126); 234 } else { 235 ksft_print_msg("Invoke exec via root_dfd and relative filename\n"); 236 fail += check_execveat(root_dfd, longpath + 1, 0); 237 } 238 239 return fail; 240 } 241 242 static int check_execveat_comm(int fd, char *argv0, char *expected) 243 { 244 char buf[128], *old_env, *old_argv0; 245 int ret; 246 247 snprintf(buf, sizeof(buf), CHECK_COMM "=%s", expected); 248 249 old_env = envp[1]; 250 envp[1] = buf; 251 252 old_argv0 = argv[0]; 253 argv[0] = argv0; 254 255 ksft_print_msg("Check execveat(AT_EMPTY_PATH)'s comm is %s\n", 256 expected); 257 ret = check_execveat_invoked_rc(fd, "", AT_EMPTY_PATH, 0, 0); 258 259 envp[1] = old_env; 260 argv[0] = old_argv0; 261 262 return ret; 263 } 264 265 static int run_tests(void) 266 { 267 int fail = 0; 268 char *fullname = realpath("execveat", NULL); 269 char *fullname_script = realpath("script", NULL); 270 char *fullname_symlink = concat(fullname, ".symlink"); 271 int subdir_dfd = open_or_die("subdir", O_DIRECTORY|O_RDONLY); 272 int subdir_dfd_ephemeral = open_or_die("subdir.ephemeral", 273 O_DIRECTORY|O_RDONLY); 274 int dot_dfd = open_or_die(".", O_DIRECTORY|O_RDONLY); 275 int root_dfd = open_or_die("/", O_DIRECTORY|O_RDONLY); 276 int dot_dfd_path = open_or_die(".", O_DIRECTORY|O_RDONLY|O_PATH); 277 int dot_dfd_cloexec = open_or_die(".", O_DIRECTORY|O_RDONLY|O_CLOEXEC); 278 int fd = open_or_die("execveat", O_RDONLY); 279 int fd_path = open_or_die("execveat", O_RDONLY|O_PATH); 280 int fd_symlink = open_or_die("execveat.symlink", O_RDONLY); 281 int fd_denatured = open_or_die("execveat.denatured", O_RDONLY); 282 int fd_denatured_path = open_or_die("execveat.denatured", 283 O_RDONLY|O_PATH); 284 int fd_script = open_or_die("script", O_RDONLY); 285 int fd_ephemeral = open_or_die("execveat.ephemeral", O_RDONLY); 286 int fd_ephemeral_path = open_or_die("execveat.path.ephemeral", 287 O_RDONLY|O_PATH); 288 int fd_script_ephemeral = open_or_die("script.ephemeral", O_RDONLY); 289 int fd_cloexec = open_or_die("execveat", O_RDONLY|O_CLOEXEC); 290 int fd_script_cloexec = open_or_die("script", O_RDONLY|O_CLOEXEC); 291 292 /* Check if we have execveat at all, and bail early if not */ 293 errno = 0; 294 execveat_(-1, NULL, NULL, NULL, 0); 295 if (errno == ENOSYS) { 296 ksft_exit_skip( 297 "ENOSYS calling execveat - no kernel support?\n"); 298 } 299 300 /* Change file position to confirm it doesn't affect anything */ 301 lseek(fd, 10, SEEK_SET); 302 303 /* Normal executable file: */ 304 /* dfd + path */ 305 fail += check_execveat(subdir_dfd, "../execveat", 0); 306 fail += check_execveat(dot_dfd, "execveat", 0); 307 fail += check_execveat(dot_dfd_path, "execveat", 0); 308 /* absolute path */ 309 fail += check_execveat(AT_FDCWD, fullname, 0); 310 /* absolute path with nonsense dfd */ 311 fail += check_execveat(99, fullname, 0); 312 /* fd + no path */ 313 fail += check_execveat(fd, "", AT_EMPTY_PATH); 314 /* O_CLOEXEC fd + no path */ 315 fail += check_execveat(fd_cloexec, "", AT_EMPTY_PATH); 316 /* O_PATH fd */ 317 fail += check_execveat(fd_path, "", AT_EMPTY_PATH); 318 319 /* Mess with executable file that's already open: */ 320 /* fd + no path to a file that's been renamed */ 321 rename("execveat.ephemeral", "execveat.moved"); 322 fail += check_execveat(fd_ephemeral, "", AT_EMPTY_PATH); 323 /* fd + no path to a file that's been deleted */ 324 unlink("execveat.moved"); /* remove the file now fd open */ 325 fail += check_execveat(fd_ephemeral, "", AT_EMPTY_PATH); 326 327 /* Mess with executable file that's already open with O_PATH */ 328 /* fd + no path to a file that's been deleted */ 329 unlink("execveat.path.ephemeral"); 330 fail += check_execveat(fd_ephemeral_path, "", AT_EMPTY_PATH); 331 332 /* Invalid argument failures */ 333 fail += check_execveat_fail(fd, "", 0, ENOENT); 334 fail += check_execveat_fail(fd, NULL, AT_EMPTY_PATH, EFAULT); 335 336 /* Symlink to executable file: */ 337 /* dfd + path */ 338 fail += check_execveat(dot_dfd, "execveat.symlink", 0); 339 fail += check_execveat(dot_dfd_path, "execveat.symlink", 0); 340 /* absolute path */ 341 fail += check_execveat(AT_FDCWD, fullname_symlink, 0); 342 /* fd + no path, even with AT_SYMLINK_NOFOLLOW (already followed) */ 343 fail += check_execveat(fd_symlink, "", AT_EMPTY_PATH); 344 fail += check_execveat(fd_symlink, "", 345 AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW); 346 347 /* Symlink fails when AT_SYMLINK_NOFOLLOW set: */ 348 /* dfd + path */ 349 fail += check_execveat_fail(dot_dfd, "execveat.symlink", 350 AT_SYMLINK_NOFOLLOW, ELOOP); 351 fail += check_execveat_fail(dot_dfd_path, "execveat.symlink", 352 AT_SYMLINK_NOFOLLOW, ELOOP); 353 /* absolute path */ 354 fail += check_execveat_fail(AT_FDCWD, fullname_symlink, 355 AT_SYMLINK_NOFOLLOW, ELOOP); 356 357 /* Non-regular file failure */ 358 fail += check_execveat_fail(dot_dfd, "pipe", 0, EACCES); 359 unlink("pipe"); 360 361 /* Shell script wrapping executable file: */ 362 /* dfd + path */ 363 fail += check_execveat(subdir_dfd, "../script", 0); 364 fail += check_execveat(dot_dfd, "script", 0); 365 fail += check_execveat(dot_dfd_path, "script", 0); 366 /* absolute path */ 367 fail += check_execveat(AT_FDCWD, fullname_script, 0); 368 /* fd + no path */ 369 fail += check_execveat(fd_script, "", AT_EMPTY_PATH); 370 fail += check_execveat(fd_script, "", 371 AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW); 372 /* O_CLOEXEC fd fails for a script (as script file inaccessible) */ 373 fail += check_execveat_fail(fd_script_cloexec, "", AT_EMPTY_PATH, 374 ENOENT); 375 fail += check_execveat_fail(dot_dfd_cloexec, "script", 0, ENOENT); 376 377 /* Mess with script file that's already open: */ 378 /* fd + no path to a file that's been renamed */ 379 rename("script.ephemeral", "script.moved"); 380 fail += check_execveat(fd_script_ephemeral, "", AT_EMPTY_PATH); 381 /* fd + no path to a file that's been deleted */ 382 unlink("script.moved"); /* remove the file while fd open */ 383 fail += check_execveat(fd_script_ephemeral, "", AT_EMPTY_PATH); 384 385 /* Rename a subdirectory in the path: */ 386 rename("subdir.ephemeral", "subdir.moved"); 387 fail += check_execveat(subdir_dfd_ephemeral, "../script", 0); 388 fail += check_execveat(subdir_dfd_ephemeral, "script", 0); 389 /* Remove the subdir and its contents */ 390 unlink("subdir.moved/script"); 391 unlink("subdir.moved"); 392 /* Shell loads via deleted subdir OK because name starts with .. */ 393 fail += check_execveat(subdir_dfd_ephemeral, "../script", 0); 394 fail += check_execveat_fail(subdir_dfd_ephemeral, "script", 0, ENOENT); 395 396 /* Flag values other than AT_SYMLINK_NOFOLLOW => EINVAL */ 397 fail += check_execveat_fail(dot_dfd, "execveat", 0xFFFF, EINVAL); 398 /* Invalid path => ENOENT */ 399 fail += check_execveat_fail(dot_dfd, "no-such-file", 0, ENOENT); 400 fail += check_execveat_fail(dot_dfd_path, "no-such-file", 0, ENOENT); 401 fail += check_execveat_fail(AT_FDCWD, "no-such-file", 0, ENOENT); 402 /* Attempt to execute directory => EACCES */ 403 fail += check_execveat_fail(dot_dfd, "", AT_EMPTY_PATH, EACCES); 404 /* Attempt to execute non-executable => EACCES */ 405 fail += check_execveat_fail(dot_dfd, "Makefile", 0, EACCES); 406 fail += check_execveat_fail(fd_denatured, "", AT_EMPTY_PATH, EACCES); 407 fail += check_execveat_fail(fd_denatured_path, "", AT_EMPTY_PATH, 408 EACCES); 409 /* Attempt to execute nonsense FD => EBADF */ 410 fail += check_execveat_fail(99, "", AT_EMPTY_PATH, EBADF); 411 fail += check_execveat_fail(99, "execveat", 0, EBADF); 412 /* Attempt to execute relative to non-directory => ENOTDIR */ 413 fail += check_execveat_fail(fd, "execveat", 0, ENOTDIR); 414 415 fail += check_execveat_pathmax(root_dfd, "execveat", 0); 416 fail += check_execveat_pathmax(root_dfd, "script", 1); 417 418 /* /proc/pid/comm gives filename by default */ 419 fail += check_execveat_comm(fd, "sentinel", "execveat"); 420 /* /proc/pid/comm gives argv[0] when invoked via link */ 421 fail += check_execveat_comm(fd_symlink, "sentinel", "execveat"); 422 /* /proc/pid/comm gives filename if NULL is passed */ 423 fail += check_execveat_comm(fd, NULL, "execveat"); 424 425 return fail; 426 } 427 428 static void prerequisites(void) 429 { 430 int fd; 431 const char *script = "#!/bin/bash\nexit $*\n"; 432 433 /* Create ephemeral copies of files */ 434 exe_cp("execveat", "execveat.ephemeral"); 435 exe_cp("execveat", "execveat.path.ephemeral"); 436 exe_cp("script", "script.ephemeral"); 437 mkdir("subdir.ephemeral", 0755); 438 439 fd = open("subdir.ephemeral/script", O_RDWR|O_CREAT|O_TRUNC, 0755); 440 write(fd, script, strlen(script)); 441 close(fd); 442 443 mkfifo("pipe", 0755); 444 } 445 446 int main(int argc, char **argv) 447 { 448 int ii; 449 int rc; 450 const char *verbose = getenv("VERBOSE"); 451 const char *check_comm = getenv(CHECK_COMM); 452 453 if (argc >= 2 || check_comm) { 454 /* 455 * If we are invoked with an argument, or no arguments but a 456 * command to check, don't run tests. 457 */ 458 const char *in_test = getenv("IN_TEST"); 459 460 if (verbose) { 461 ksft_print_msg("invoked with:\n"); 462 for (ii = 0; ii < argc; ii++) 463 ksft_print_msg("\t[%d]='%s\n'", ii, argv[ii]); 464 } 465 466 /* If the tests wanted us to check the command, do so. */ 467 if (check_comm) { 468 /* TASK_COMM_LEN == 16 */ 469 char buf[32]; 470 int fd, ret; 471 472 fd = open("/proc/self/comm", O_RDONLY); 473 if (fd < 0) { 474 ksft_perror("open() comm failed"); 475 exit(1); 476 } 477 478 ret = read(fd, buf, sizeof(buf)); 479 if (ret < 0) { 480 ksft_perror("read() comm failed"); 481 close(fd); 482 exit(1); 483 } 484 close(fd); 485 486 // trim off the \n 487 buf[ret-1] = 0; 488 489 if (strcmp(buf, check_comm)) { 490 ksft_print_msg("bad comm, got: %s expected: %s\n", 491 buf, check_comm); 492 exit(1); 493 } 494 495 exit(0); 496 } 497 498 /* Check expected environment transferred. */ 499 if (!in_test || strcmp(in_test, "yes") != 0) { 500 ksft_print_msg("no IN_TEST=yes in env\n"); 501 return 1; 502 } 503 504 /* Use the final argument as an exit code. */ 505 rc = atoi(argv[argc - 1]); 506 exit(rc); 507 } else { 508 ksft_print_header(); 509 ksft_set_plan(TESTS_EXPECTED); 510 prerequisites(); 511 if (verbose) 512 envp[1] = "VERBOSE=1"; 513 rc = run_tests(); 514 if (rc > 0) 515 printf("%d tests failed\n", rc); 516 ksft_finished(); 517 } 518 519 return rc; 520 } 521