diff options
Diffstat (limited to 'src/basic')
-rw-r--r-- | src/basic/namespace-util.c | 18 | ||||
-rw-r--r-- | src/basic/pidref.c | 10 | ||||
-rw-r--r-- | src/basic/process-util.c | 53 | ||||
-rw-r--r-- | src/basic/process-util.h | 3 | ||||
-rw-r--r-- | src/basic/virt.c | 19 |
5 files changed, 72 insertions, 31 deletions
diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index 36ebda9ba4..6c559e4bf8 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -519,12 +519,10 @@ int userns_acquire_empty(void) { _cleanup_(pidref_done_sigkill_wait) PidRef pid = PIDREF_NULL; int r; - r = pidref_safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid); + r = pidref_safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS|FORK_FREEZE, &pid); if (r < 0) return r; - if (r == 0) - /* Child. We do nothing here, just freeze until somebody kills us. */ - freeze(); + assert(r > 0); return pidref_namespace_open_by_type(&pid, NAMESPACE_USER); } @@ -541,12 +539,10 @@ int userns_acquire(const char *uid_map, const char *gid_map) { * and then kills the process again. This way we have a userns fd that is not bound to any * process. We can use that for file system mounts and similar. */ - r = pidref_safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid); + r = pidref_safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS|FORK_FREEZE, &pid); if (r < 0) return r; - if (r == 0) - /* Child. We do nothing here, just freeze until somebody kills us. */ - freeze(); + assert(r > 0); xsprintf(path, "/proc/" PID_FMT "/uid_map", pid.pid); r = write_string_file(path, uid_map, WRITE_STRING_FILE_DISABLE_BUFFER); @@ -762,12 +758,10 @@ int netns_acquire(void) { /* Forks off a process in a new network namespace, acquires a network namespace fd, and then kills * the process again. This way we have a netns fd that is not bound to any process. */ - r = pidref_safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS, &pid); + r = pidref_safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS|FORK_FREEZE, &pid); if (r < 0) return log_debug_errno(r, "Failed to fork process into new netns: %m"); - if (r == 0) - /* Child. We do nothing here, just freeze until somebody kills us. */ - freeze(); + assert(r > 0); return pidref_namespace_open_by_type(&pid, NAMESPACE_NET); } diff --git a/src/basic/pidref.c b/src/basic/pidref.c index a275f77b56..ccfa2903b6 100644 --- a/src/basic/pidref.c +++ b/src/basic/pidref.c @@ -40,6 +40,10 @@ int pidref_acquire_pidfd_id(PidRef *pidref) { bool pidref_equal(PidRef *a, PidRef *b) { + /* If this is the very same structure, it definitely refers to the same process */ + if (a == b) + return true; + if (!pidref_is_set(a)) return !pidref_is_set(b); @@ -58,9 +62,15 @@ bool pidref_equal(PidRef *a, PidRef *b) { if (a->fd_id == 0 || b->fd_id == 0) return true; } else { + /* If the other side is remote, then this is not the same */ if (pidref_is_remote(b)) return false; + /* PID1 cannot exit, hence it cannot change pidfs ids, hence no point in comparing them, we + * can shortcut things */ + if (a->pid == 1) + return true; + /* Try to compare pidfds using their inode numbers. This way we can ensure that we * don't spuriously consider two PidRefs equal if the pid has been reused once. Note * that we ignore all errors here, not only EOPNOTSUPP, as fstat() might fail due to diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 0367270724..21e296864a 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -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"); - return inode_same(root, "/proc/1/root", 0); + int result = inode_same(roota, rootb, 0); + if (result == -ENOENT) + return proc_mounted() == 0 ? -ENOSYS : -ESRCH; + if (result < 0) + return result; + + 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) { @@ -1763,6 +1798,9 @@ int safe_fork_full( } } + if (FLAGS_SET(flags, FORK_FREEZE)) + freeze(); + if (ret_pid) *ret_pid = getpid_cached(); @@ -1959,7 +1997,8 @@ _noreturn_ void freeze(void) { break; } - /* waitid() failed with an unexpected error, things are really borked. Freeze now! */ + /* waitid() failed with an ECHLD error (because there are no left-over child processes) or any other + * (unexpected) error. Freeze for good now! */ for (;;) pause(); } diff --git a/src/basic/process-util.h b/src/basic/process-util.h index d4d7d87c06..cfb967c3bc 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -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); @@ -192,6 +192,7 @@ typedef enum ForkFlags { FORK_NEW_USERNS = 1 << 19, /* Run child in its own user namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ FORK_NEW_NETNS = 1 << 20, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ FORK_NEW_PIDNS = 1 << 21, /* Run child in its own PID namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ + FORK_FREEZE = 1 << 22, /* Don't return in child, just call freeze() instead */ } ForkFlags; int safe_fork_full( diff --git a/src/basic/virt.c b/src/basic/virt.c index 1ba8d36dba..0d6fcaee56 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -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; |