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
execveat_(int fd,const char * path,char ** argv,char ** envp,int flags)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)
_check_execveat_fail(int fd,const char * path,int flags,int expected_errno,const char * errno_str)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
check_execveat_invoked_rc(int fd,const char * path,int flags,int expected_rc,int expected_rc2)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
check_execveat(int fd,const char * path,int flags)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
concat(const char * left,const char * right)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
open_or_die(const char * filename,int flags)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
exe_cp(const char * src,const char * dest)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
check_execveat_pathmax(int root_dfd,const char * src,int is_script)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
check_execveat_comm(int fd,char * argv0,char * expected)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
run_tests(void)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
prerequisites(void)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
main(int argc,char ** argv)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