]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
process-util: explicitly handle processes lacking parents in get_process_ppid()
authorLennart Poettering <lennart@poettering.net>
Wed, 7 Jul 2021 13:57:51 +0000 (15:57 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Wed, 7 Jul 2021 17:41:08 +0000 (18:41 +0100)
Let's make sure we signal out-of-band via an error message if a process
doesn't have a parent process whose PID we could return. Otherwise we'll
too likely hide errors, as we return an invalid PID 0, which in other
contexts has special meaning (i.e. usually "myself").

Replaces: #20153

This is based on work by @dtardon, but goes a different route, by
ensuring we propagate a proper error in this case.

This modernizes the function in question a bit in other ways, i.e.
renames stuff and makes the return parameter optional.

src/basic/process-util.c
src/coredump/coredump.c
src/test/test-process-util.c

index 20b24414685e37d7961983534d3acd7f47cc21ad..14259ea8dfe4a99371afe1537c0cd8e4eaf677cb 100644 (file)
@@ -643,20 +643,23 @@ int get_process_environ(pid_t pid, char **env) {
         return 0;
 }
 
-int get_process_ppid(pid_t pid, pid_t *_ppid) {
-        int r;
+int get_process_ppid(pid_t pid, pid_t *ret) {
         _cleanup_free_ char *line = NULL;
         long unsigned ppid;
         const char *p;
+        int r;
 
         assert(pid >= 0);
-        assert(_ppid);
 
         if (pid == 0 || pid == getpid_cached()) {
-                *_ppid = getppid();
+                if (ret)
+                        *ret = getppid();
                 return 0;
         }
 
+        if (pid == 1) /* PID 1 has no parent, shortcut this case */
+                return -EADDRNOTAVAIL;
+
         p = procfs_file_alloca(pid, "stat");
         r = read_one_line_file(p, &line);
         if (r == -ENOENT)
@@ -664,9 +667,8 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) {
         if (r < 0)
                 return r;
 
-        /* Let's skip the pid and comm fields. The latter is enclosed
-         * in () but does not escape any () in its value, so let's
-         * skip over it manually */
+        /* Let's skip the pid and comm fields. The latter is enclosed in () but does not escape any () in its
+         * value, so let's skip over it manually */
 
         p = strrchr(line, ')');
         if (!p)
@@ -680,10 +682,17 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) {
                    &ppid) != 1)
                 return -EIO;
 
-        if ((long unsigned) (pid_t) ppid != ppid)
+        /* If ppid is zero the process has no parent. Which might be the case for PID 1 but also for
+         * processes originating in other namespaces that are inserted into a pidns. Return a recognizable
+         * error in this case. */
+        if (ppid == 0)
+                return -EADDRNOTAVAIL;
+
+        if ((pid_t) ppid < 0 || (long unsigned) (pid_t) ppid != ppid)
                 return -ERANGE;
 
-        *_ppid = (pid_t) ppid;
+        if (ret)
+                *ret = (pid_t) ppid;
 
         return 0;
 }
index c6c232c7e73079b31f711c6c0c4a8bcc6fa7fe73..444b9ec374ffa1fed46fae51d5f71962ac848b58 100644 (file)
@@ -668,8 +668,7 @@ static int get_process_ns(pid_t pid, const char *namespace, ino_t *ns) {
         return 0;
 }
 
-static int get_mount_namespace_leader(pid_t pid, pid_t *container_pid) {
-        pid_t cpid = pid, ppid = 0;
+static int get_mount_namespace_leader(pid_t pid, pid_t *ret) {
         ino_t proc_mntns;
         int r;
 
@@ -679,8 +678,12 @@ static int get_mount_namespace_leader(pid_t pid, pid_t *container_pid) {
 
         for (;;) {
                 ino_t parent_mntns;
+                pid_t ppid;
 
-                r = get_process_ppid(cpid, &ppid);
+                r = get_process_ppid(pid, &ppid);
+                if (r == -EADDRNOTAVAIL) /* Reached the top (i.e. typically PID 1, but could also be a process
+                                          * whose parent is not in our pidns) */
+                        return -ENOENT;
                 if (r < 0)
                         return r;
 
@@ -688,17 +691,13 @@ static int get_mount_namespace_leader(pid_t pid, pid_t *container_pid) {
                 if (r < 0)
                         return r;
 
-                if (proc_mntns != parent_mntns)
-                        break;
-
-                if (ppid == 1)
-                        return -ENOENT;
+                if (proc_mntns != parent_mntns) {
+                        *ret = ppid;
+                        return 0;
+                }
 
-                cpid = ppid;
+                pid = ppid;
         }
-
-        *container_pid = ppid;
-        return 0;
 }
 
 /* Returns 1 if the parent was found.
index 576a9a87531f92c822bced8367af6fdeaf992307..8c76392ae96953ca90a40cfa63c18ae92c171e8e 100644 (file)
@@ -14,9 +14,9 @@
 
 #include "alloc-util.h"
 #include "architecture.h"
-#include "errno-util.h"
-#include "errno-list.h"
 #include "dirent-util.h"
+#include "errno-list.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "log.h"
 #include "macro.h"
@@ -24,6 +24,7 @@
 #include "missing_syscall.h"
 #include "parse-util.h"
 #include "process-util.h"
+#include "procfs-util.h"
 #include "rlimit-util.h"
 #include "signal-util.h"
 #include "stdio-util.h"
@@ -65,9 +66,12 @@ static void test_get_process_comm(pid_t pid) {
         assert_se(get_process_cmdline(pid, 1, 0, &d) >= 0);
         log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d);
 
-        assert_se(get_process_ppid(pid, &e) >= 0);
-        log_info("PID"PID_FMT" PPID: "PID_FMT, pid, e);
-        assert_se(pid == 1 ? e == 0 : e > 0);
+        r = get_process_ppid(pid, &e);
+        assert_se(pid == 1 ? r == -EADDRNOTAVAIL : r >= 0);
+        if (r >= 0) {
+                log_info("PID"PID_FMT" PPID: "PID_FMT, pid, e);
+                assert_se(e > 0);
+        }
 
         assert_se(is_kernel_thread(pid) == 0 || pid != 1);
 
@@ -837,6 +841,39 @@ static void test_setpriority_closest(void) {
         }
 }
 
+static void test_get_process_ppid(void) {
+        uint64_t limit;
+        int r;
+
+        log_info("/* %s */", __func__);
+
+        assert_se(get_process_ppid(1, NULL) == -EADDRNOTAVAIL);
+
+        /* the process with the PID above the global limit definitely doesn't exist. Verify that */
+        assert_se(procfs_tasks_get_limit(&limit) >= 0);
+        assert_se(limit >= INT_MAX || get_process_ppid(limit+1, NULL) == -ESRCH);
+
+        for (pid_t pid = 0;;) {
+                _cleanup_free_ char *c1 = NULL, *c2 = NULL;
+                pid_t ppid;
+
+                r = get_process_ppid(pid, &ppid);
+                if (r == -EADDRNOTAVAIL) {
+                        log_info("No further parent PID");
+                        break;
+                }
+
+                assert_se(r >= 0);
+
+                assert_se(get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &c1) >= 0);
+                assert_se(get_process_cmdline(ppid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &c2) >= 0);
+
+                log_info("Parent of " PID_FMT " (%s) is " PID_FMT " (%s).", pid, c1, ppid, c2);
+
+                pid = ppid;
+        }
+}
+
 int main(int argc, char *argv[]) {
         log_show_color(true);
         test_setup_logging(LOG_INFO);
@@ -866,6 +903,7 @@ int main(int argc, char *argv[]) {
         test_pid_to_ptr();
         test_ioprio_class_from_to_string();
         test_setpriority_closest();
+        test_get_process_ppid();
 
         return 0;
 }