diff options
-rw-r--r-- | src/basic/process-util.c | 26 | ||||
-rw-r--r-- | src/test/test-process-util.c | 77 |
2 files changed, 69 insertions, 34 deletions
diff --git a/src/basic/process-util.c b/src/basic/process-util.c index f5bd6c948..2b23ac36c 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -312,19 +312,18 @@ int rename_process(const char name[]) { /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at * the end. This is the best option for changing /proc/self/cmdline. */ - if (mm_size < l+1) { + + /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the + * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is + * present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if + * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but + * mmap() is not. */ + if (geteuid() != 0) + log_debug("Skipping PR_SET_MM, as we don't have privileges."); + else if (mm_size < l+1) { size_t nn_size; char *nn; - /* Let's not bother with this if we don't have euid == 0. Strictly speaking if people do weird stuff - * with capabilities this could work even for euid != 0, but our own code generally doesn't do that, - * hence let's use this as quick bypass check, to avoid calling mmap() if PR_SET_MM_ARG_START fails - * with EPERM later on anyway. After all geteuid() is dead cheap to call, but mmap() is not. */ - if (geteuid() != 0) { - log_debug("Skipping PR_SET_MM_ARG_START, as we don't have privileges."); - goto use_saved_argv; - } - nn_size = PAGE_ALIGN(l+1); nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); if (nn == MAP_FAILED) { @@ -351,9 +350,14 @@ int rename_process(const char name[]) { mm = nn; mm_size = nn_size; - } else + } else { strncpy(mm, name, mm_size); + /* Update the end pointer, continuing regardless of any failure. */ + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0) + log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); + } + use_saved_argv: /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if * it still looks here */ diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index 046a96fdc..e7c9766c4 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -355,38 +355,22 @@ static void test_get_process_cmdline_harder(void) { _exit(0); } -static void test_rename_process_one(const char *p, int ret) { +static void test_rename_process_now(const char *p, int ret) { _cleanup_free_ char *comm = NULL, *cmdline = NULL; - pid_t pid; int r; - pid = fork(); - assert_se(pid >= 0); - - if (pid > 0) { - siginfo_t si; - - assert_se(wait_for_terminate(pid, &si) >= 0); - assert_se(si.si_code == CLD_EXITED); - assert_se(si.si_status == EXIT_SUCCESS); - - return; - } - - /* child */ r = rename_process(p); - assert_se(r == ret || (ret == 0 && r >= 0) || (ret > 0 && r > 0)); if (r < 0) - goto finish; + return; #ifdef HAVE_VALGRIND_VALGRIND_H /* see above, valgrind is weird, we can't verify what we are doing here */ if (RUNNING_ON_VALGRIND) - goto finish; + return; #endif assert_se(get_process_comm(0, &comm) >= 0); @@ -394,11 +378,57 @@ static void test_rename_process_one(const char *p, int ret) { assert_se(strneq(comm, p, 15)); assert_se(get_process_cmdline(0, 0, false, &cmdline) >= 0); - log_info("cmdline = <%s>", cmdline); - assert_se(strneq(p, cmdline, strlen("test-process-util"))); - assert_se(startswith(p, cmdline)); + /* we cannot expect cmdline to be renamed properly without privileges */ + if (geteuid() == 0) { + log_info("cmdline = <%s>", cmdline); + assert_se(strneq(p, cmdline, strlen("test-process-util"))); + assert_se(startswith(p, cmdline)); + } else + log_info("cmdline = <%s> (not verified)", cmdline); +} -finish: +static void test_rename_process_one(const char *p, int ret) { + siginfo_t si; + pid_t pid; + + pid = fork(); + assert_se(pid >= 0); + + if (pid == 0) { + /* child */ + test_rename_process_now(p, ret); + _exit(EXIT_SUCCESS); + } + + assert_se(wait_for_terminate(pid, &si) >= 0); + assert_se(si.si_code == CLD_EXITED); + assert_se(si.si_status == EXIT_SUCCESS); +} + +static void test_rename_process_multi(void) { + pid_t pid; + + pid = fork(); + assert_se(pid >= 0); + + if (pid > 0) { + siginfo_t si; + + assert_se(wait_for_terminate(pid, &si) >= 0); + assert_se(si.si_code == CLD_EXITED); + assert_se(si.si_status == EXIT_SUCCESS); + + return; + } + + /* child */ + test_rename_process_now("one", 1); + test_rename_process_now("more", 0); /* longer than "one", hence truncated */ + setresuid(99, 99, 99); + test_rename_process_now("time!", 0); + test_rename_process_now("0", 1); /* shorter than "one", should fit */ + test_rename_process_one("", -EINVAL); + test_rename_process_one(NULL, -EINVAL); _exit(EXIT_SUCCESS); } @@ -408,6 +438,7 @@ static void test_rename_process(void) { test_rename_process_one("foo", 1); /* should always fit */ test_rename_process_one("this is a really really long process name, followed by some more words", 0); /* unlikely to fit */ test_rename_process_one("1234567", 1); /* should always fit */ + test_rename_process_multi(); /* multiple invocations and dropped privileges */ } static void test_getpid_cached(void) { |