summaryrefslogtreecommitdiffstats
path: root/src/basic
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic')
-rw-r--r--src/basic/namespace-util.c18
-rw-r--r--src/basic/pidref.c10
-rw-r--r--src/basic/process-util.c53
-rw-r--r--src/basic/process-util.h3
-rw-r--r--src/basic/virt.c19
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;