]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
process-util: port pid_from_same_root_fs() to pidref, and port three places over...
authorLennart Poettering <lennart@poettering.net>
Mon, 13 Jan 2025 10:09:49 +0000 (11:09 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 16 Jan 2025 10:55:21 +0000 (11:55 +0100)
src/basic/process-util.c
src/basic/process-util.h
src/basic/virt.c
src/coredump/coredump.c
src/shared/killall.c
src/test/test-process-util.c

index 73c1ab75ef5239de9d18033aa57afad656dc9e30..94d8681c293412388f30965ded9f989c84a51f83 100644 (file)
@@ -1228,18 +1228,53 @@ int pidref_is_alive(const PidRef *pidref) {
         return result;
 }
 
-int pid_from_same_root_fs(pid_t pid) {
-        const char *root;
+int pidref_from_same_root_fs(PidRef *a, PidRef *b) {
+        _cleanup_(pidref_done) PidRef self = PIDREF_NULL;
+        int r;
 
-        if (pid < 0)
+        /* Checks if the two specified processes have the same root fs. Either can be specified as NULL in
+         * which case we'll check against ourselves. */
+
+        if (!a || !b) {
+                r = pidref_set_self(&self);
+                if (r < 0)
+                        return r;
+                if (!a)
+                        a = &self;
+                if (!b)
+                        b = &self;
+        }
+
+        if (!pidref_is_set(a) || !pidref_is_set(b))
+                return -ESRCH;
+
+        /* If one of the two processes have the same root they cannot have the same root fs, but if both of
+         * them do we don't know */
+        if (pidref_is_remote(a) && pidref_is_remote(b))
+                return -EREMOTE;
+        if (pidref_is_remote(a) || pidref_is_remote(b))
                 return false;
 
-        if (pid == 0 || pid == getpid_cached())
+        if (pidref_equal(a, b))
                 return true;
 
-        root = procfs_file_alloca(pid, "root");
+        const char *roota = procfs_file_alloca(a->pid, "root");
+        const char *rootb = procfs_file_alloca(b->pid, "root");
+
+        int result = inode_same(roota, rootb, 0);
+        if (result == -ENOENT)
+                return proc_mounted() == 0 ? -ENOSYS : -ESRCH;
+        if (result < 0)
+                return result;
 
-        return inode_same(root, "/proc/1/root", 0);
+        r = pidref_verify(a);
+        if (r < 0)
+                return r;
+        r = pidref_verify(b);
+        if (r < 0)
+                return r;
+
+        return result;
 }
 
 bool is_main_thread(void) {
index 4c24e00bd46630ec4210bf44fcca93e832f383d9..cfb967c3bc445650986b66d8b4138332003a8b91 100644 (file)
@@ -93,7 +93,7 @@ int pid_is_unwaited(pid_t pid);
 int pidref_is_unwaited(const PidRef *pidref);
 int pid_is_my_child(pid_t pid);
 int pidref_is_my_child(const PidRef *pidref);
-int pid_from_same_root_fs(pid_t pid);
+int pidref_from_same_root_fs(PidRef *a, PidRef *b);
 
 bool is_main_thread(void);
 
index 1ba8d36dba98cdf55a3ed9770adb8535543a0d0f..0d6fcaee56bcc2f4bd74228a4288980c4560a580 100644 (file)
@@ -799,19 +799,16 @@ int running_in_chroot(void) {
         if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
                 return 0;
 
-        r = inode_same("/proc/1/root", "/", 0);
-        if (r == -ENOENT) {
-                r = proc_mounted();
-                if (r == 0) {
-                        if (getpid_cached() == 1)
-                                return false; /* We will mount /proc, assuming we're not in a chroot. */
+        r = pidref_from_same_root_fs(&PIDREF_MAKE_FROM_PID(1), NULL);
+        if (r == -ENOSYS) {
+                if (getpid_cached() == 1)
+                        return false; /* We will mount /proc, assuming we're not in a chroot. */
 
-                        log_debug("/proc is not mounted, assuming we're in a chroot.");
-                        return true;
-                }
-                if (r > 0)  /* If we have fake /proc/, we can't do the check properly. */
-                        return -ENOSYS;
+                log_debug("/proc/ is not mounted, assuming we're in a chroot.");
+                return true;
         }
+        if (r == -ESRCH) /* We must have a fake /proc/, we can't do the check properly. */
+                return -ENOSYS;
         if (r < 0)
                 return r;
 
index 55d075fdf365e276ec983ef86dfe05d521d58d50..fa009bdacfdb9a5d7caaaa1c20c7123ac33a10f1 100644 (file)
@@ -758,23 +758,16 @@ static int compose_open_fds(pid_t pid, char **ret) {
  * Returns a negative number on errors.
  */
 static int get_process_container_parent_cmdline(PidRef *pid, char** ret_cmdline) {
-        const char *proc_root_path;
-        struct stat root_stat, proc_root_stat;
         int r;
 
         assert(pidref_is_set(pid));
         assert(!pidref_is_remote(pid));
 
-        /* To compare inodes of / and /proc/[pid]/root */
-        if (stat("/", &root_stat) < 0)
-                return -errno;
-
-        proc_root_path = procfs_file_alloca(pid->pid, "root");
-        if (stat(proc_root_path, &proc_root_stat) < 0)
-                return -errno;
-
-        /* The process uses system root. */
-        if (stat_inode_same(&proc_root_stat, &root_stat)) {
+        r = pidref_from_same_root_fs(pid, &PIDREF_MAKE_FROM_PID(1));
+        if (r < 0)
+                return r;
+        if (r > 0) {
+                /* The process uses system root. */
                 *ret_cmdline = NULL;
                 return 0;
         }
index 184aec018bdfa99d4e36f27e52b7dfbae3d8dba5..b3a9c7e16fc1908eef284a0fdf0261de6e7c93ba 100644 (file)
 #include "string-util.h"
 #include "terminal-util.h"
 
-static bool argv_has_at(pid_t pid) {
-        _cleanup_fclose_ FILE *f = NULL;
-        const char *p;
-        char c = 0;
+static int argv_has_at(const PidRef *pid) {
+        int r;
 
-        p = procfs_file_alloca(pid, "cmdline");
-        f = fopen(p, "re");
-        if (!f) {
-                log_debug_errno(errno, "Failed to open %s, ignoring: %m", p);
-                return true; /* not really, but has the desired effect */
-        }
+        assert(pidref_is_set(pid));
+        assert(!pidref_is_remote(pid));
+
+        const char *p = procfs_file_alloca(pid->pid, "cmdline");
+        _cleanup_fclose_ FILE *f = fopen(p, "re");
+        if (!f)
+                return log_debug_errno(errno, "Failed to open %s, ignoring: %m", p);
 
         /* Try to read the first character of the command line. If the cmdline is empty (which might be the case for
          * kernel threads but potentially also other stuff), this line won't do anything, but we don't care much, as
          * actual kernel threads are already filtered out above. */
+        char c = 0;
         (void) fread(&c, 1, 1, f);
 
+        r = pidref_verify(pid);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to verify pid " PID_FMT ", ignoring: %m", pid->pid);
+
         /* Processes with argv[0][0] = '@' we ignore from the killing spree.
          *
          * https://systemd.io/ROOT_STORAGE_DAEMONS */
@@ -74,9 +78,8 @@ static bool is_in_survivor_cgroup(const PidRef *pid) {
         return r > 0;
 }
 
-static bool ignore_proc(const PidRef *pid, bool warn_rootfs) {
+static bool ignore_proc(PidRef *pid, bool warn_rootfs) {
         uid_t uid;
-        int r;
 
         assert(pidref_is_set(pid));
 
@@ -85,28 +88,25 @@ static bool ignore_proc(const PidRef *pid, bool warn_rootfs) {
                 return true;
 
         /* Ignore kernel threads */
-        r = pidref_is_kernel_thread(pid);
-        if (r != 0)
+        if (pidref_is_kernel_thread(pid) != 0)
                 return true; /* also ignore processes where we can't determine this */
 
         /* Ignore processes that are part of a cgroup marked with the user.survive_final_kill_signal xattr */
         if (is_in_survivor_cgroup(pid))
                 return true;
 
-        r = pidref_get_uid(pid, &uid);
-        if (r < 0)
+        if (pidref_get_uid(pid, &uid) < 0)
                 return true; /* not really, but better safe than sorry */
 
         /* Non-root processes otherwise are always subject to be killed */
         if (uid != 0)
                 return false;
 
-        if (!argv_has_at(pid->pid))
-                return false;
+        if (argv_has_at(pid) == 0)
+                return false; /* if this fails, ignore the process */
 
         if (warn_rootfs &&
-            pid_from_same_root_fs(pid->pid) > 0) {
-
+            pidref_from_same_root_fs(pid, NULL) > 0) {
                 _cleanup_free_ char *comm = NULL;
 
                 (void) pidref_get_comm(pid, &comm);
index 48c5f44e013a74b5ae10874694093aa0ad4e541c..166c0fa2c9ebd897c8a76406972d0a5a072b9c1a 100644 (file)
@@ -3,6 +3,7 @@
 #include <fcntl.h>
 #include <linux/oom.h>
 #include <pthread.h>
+#include <sys/eventfd.h>
 #include <sys/mount.h>
 #include <sys/personality.h>
 #include <sys/prctl.h>
@@ -1017,6 +1018,53 @@ TEST(pid_get_start_time) {
         ASSERT_GE(start_time2, start_time);
 }
 
+TEST(pidref_from_same_root_fs) {
+        int r;
+
+        _cleanup_(pidref_done) PidRef pid1 = PIDREF_NULL, self = PIDREF_NULL;
+
+        ASSERT_OK(pidref_set_self(&self));
+        ASSERT_OK(pidref_set_pid(&pid1, 1));
+
+        ASSERT_OK_POSITIVE(pidref_from_same_root_fs(&self, &self));
+        ASSERT_OK_POSITIVE(pidref_from_same_root_fs(&pid1, &pid1));
+
+        r = pidref_from_same_root_fs(&pid1, &self);
+        if (ERRNO_IS_NEG_PRIVILEGE(r))
+                return (void) log_tests_skipped("skipping pidref_from_same_root_fs() test, lacking privileged.");
+        ASSERT_OK(r);
+        log_info("PID1 and us have the same rootfs: %s", yes_no(r));
+
+        int q = pidref_from_same_root_fs(&self, &pid1);
+        ASSERT_OK(q);
+        ASSERT_EQ(r, q);
+
+        _cleanup_(pidref_done_sigkill_wait) PidRef child1 = PIDREF_NULL;
+        ASSERT_OK(pidref_safe_fork("(child1)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_FREEZE, &child1));
+        ASSERT_OK_POSITIVE(pidref_from_same_root_fs(&self, &child1));
+
+        _cleanup_close_ int efd = eventfd(0, EFD_CLOEXEC);
+        ASSERT_OK_ERRNO(efd);
+
+        _cleanup_(pidref_done_sigkill_wait) PidRef child2 = PIDREF_NULL;
+        r = pidref_safe_fork("(child2)", FORK_RESET_SIGNALS|FORK_REOPEN_LOG, &child2);
+        ASSERT_OK(r);
+
+        if (r == 0) {
+                ASSERT_OK_ERRNO(chroot("/usr"));
+                uint64_t u = 1;
+
+                ASSERT_OK_EQ_ERRNO(write(efd, &u, sizeof(u)), (ssize_t) sizeof(u));
+                freeze();
+        }
+
+        uint64_t u;
+        ASSERT_OK_EQ_ERRNO(read(efd, &u, sizeof(u)), (ssize_t) sizeof(u));
+
+        ASSERT_OK_ZERO(pidref_from_same_root_fs(&self, &child2));
+        ASSERT_OK_ZERO(pidref_from_same_root_fs(&child2, &self));
+}
+
 static int intro(void) {
         log_show_color(true);
         return EXIT_SUCCESS;