execveat.c 16 KB

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