Skip to content

Commit 184a935

Browse files
tych0kees
authored andcommitted
selftests/exec: add a test for execveat()'s comm
In the previous patch we've updated AT_EMPTY_PATH execs to use the dentry filename. Test for this and just to be sure keeps working with symlinks, which was a concern in [1], I've added a test for that as well. The test itself is a bit ugly, because the existing check_execveat_fail() helpers use a hardcoded envp and argv, and we want to "pass" things via the environment to test various argument values, but it seemed cleaner than passing one in everywhere in all the existing tests. Output looks like: ok 51 Check success of execveat(6, 'home/tycho/packages/...yyyyyyyyyyyyyyyyyyyy', 0)... # Check execveat(AT_EMPTY_PATH)'s comm is execveat ok 52 Check success of execveat(9, '', 4096)... # Check execveat(AT_EMPTY_PATH)'s comm is execveat ok 53 Check success of execveat(11, '', 4096)... # Check execveat(AT_EMPTY_PATH)'s comm is execveat [ 25.579272] process 'execveat' launched '/dev/fd/9' with NULL argv: empty string added ok 54 Check success of execveat(9, '', 4096)... Link: https://lore.kernel.org/all/20240925.152228-private.conflict.frozen.trios-TdUGhuI5Sb4v@cyphar.com/ [1] Signed-off-by: Tycho Andersen <tandersen@netflix.com> Link: https://lore.kernel.org/r/20241030203732.248767-2-tycho@tycho.pizza Signed-off-by: Kees Cook <kees@kernel.org>
1 parent 543841d commit 184a935

File tree

1 file changed

+72
-3
lines changed

1 file changed

+72
-3
lines changed

tools/testing/selftests/exec/execveat.c

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323

2424
#include "../kselftest.h"
2525

26-
#define TESTS_EXPECTED 51
26+
#define TESTS_EXPECTED 54
2727
#define TEST_NAME_LEN (PATH_MAX * 4)
2828

29+
#define CHECK_COMM "CHECK_COMM"
30+
2931
static char longpath[2 * PATH_MAX] = "";
3032
static char *envp[] = { "IN_TEST=yes", NULL, NULL };
3133
static char *argv[] = { "execveat", "99", NULL };
@@ -237,6 +239,29 @@ static int check_execveat_pathmax(int root_dfd, const char *src, int is_script)
237239
return fail;
238240
}
239241

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+
240265
static int run_tests(void)
241266
{
242267
int fail = 0;
@@ -389,6 +414,14 @@ static int run_tests(void)
389414

390415
fail += check_execveat_pathmax(root_dfd, "execveat", 0);
391416
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+
392425
return fail;
393426
}
394427

@@ -415,9 +448,13 @@ int main(int argc, char **argv)
415448
int ii;
416449
int rc;
417450
const char *verbose = getenv("VERBOSE");
451+
const char *check_comm = getenv(CHECK_COMM);
418452

419-
if (argc >= 2) {
420-
/* If we are invoked with an argument, don't run tests. */
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+
*/
421458
const char *in_test = getenv("IN_TEST");
422459

423460
if (verbose) {
@@ -426,6 +463,38 @@ int main(int argc, char **argv)
426463
ksft_print_msg("\t[%d]='%s\n'", ii, argv[ii]);
427464
}
428465

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+
429498
/* Check expected environment transferred. */
430499
if (!in_test || strcmp(in_test, "yes") != 0) {
431500
ksft_print_msg("no IN_TEST=yes in env\n");

0 commit comments

Comments
 (0)