core: fix extensions refreshing (dlopen() shenanigans) (#40112)

This commit is contained in:
Yu Watanabe
2025-12-17 18:41:23 +09:00
committed by GitHub
8 changed files with 27 additions and 9 deletions
+1
View File
@@ -86,6 +86,7 @@ typedef enum CGroupFlags CGroupFlags;
typedef enum CGroupMask CGroupMask;
typedef enum ChaseFlags ChaseFlags;
typedef enum ExtractFlags ExtractFlags;
typedef enum ForkFlags ForkFlags;
typedef enum Glyph Glyph;
typedef enum ImageClass ImageClass;
typedef enum JobMode JobMode;
+4
View File
@@ -8,6 +8,7 @@
#include <sys/mount.h>
#include <unistd.h>
#include "dlfcn-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -217,6 +218,9 @@ int namespace_open(
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
int r;
/* Block dlopen() now, to avoid us inadvertently loading shared library from another namespace */
block_dlopen();
if (userns_fd >= 0) {
/* Can't setns to your own userns, since then you could escalate from non-root to root in
* your own namespace, so check if namespaces are equal before attempting to enter. */
+3 -1
View File
@@ -1703,7 +1703,8 @@ int pidref_safe_fork_full(
* foreign environment. Note that this has no effect on NSS! (i.e. it only has effect on uses of our
* dlopen_safe(), which we use comprehensively in our codebase, but glibc NSS doesn't bother, of
* course.) */
block_dlopen();
if (!FLAGS_SET(flags, FORK_ALLOW_DLOPEN))
block_dlopen();
if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL))
if (prctl(PR_SET_PDEATHSIG, fork_flags_to_signal(flags)) < 0) {
@@ -1906,6 +1907,7 @@ int namespace_fork(
/* This is much like safe_fork(), but forks twice, and joins the specified namespaces in the middle
* process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that
* /proc/self/fd works correctly. */
assert(!FLAGS_SET(flags, FORK_ALLOW_DLOPEN)); /* never allow loading shared library from another ns */
r = safe_fork_full(outer_name,
NULL,
+2 -1
View File
@@ -187,8 +187,9 @@ typedef enum ForkFlags {
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 */
FORK_ALLOW_DLOPEN = 1 << 23, /* Do not block dlopen() in child */
_FORK_PID_ONLY = 1 << 23, /* Don't open a pidfd referencing the child process */
_FORK_PID_ONLY = 1 << 24, /* Don't open a pidfd referencing the child process */
} ForkFlags;
int pidref_safe_fork_full(
+3
View File
@@ -13,6 +13,7 @@
#include "base-filesystem.h"
#include "bitfield.h"
#include "chase.h"
#include "cryptsetup-util.h"
#include "dev-setup.h"
#include "devnum-util.h"
#include "dissect-image.h"
@@ -3852,6 +3853,8 @@ int refresh_extensions_in_namespace(
if (r > 0)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Target namespace is not separate, cannot reload extensions");
(void) dlopen_cryptsetup();
extension_dir = path_join(p->private_namespace_dir, "unit-extensions");
if (!extension_dir)
return log_oom_debug();
+6 -5
View File
@@ -14,7 +14,6 @@
#include "bus-error.h"
#include "bus-util.h"
#include "chase.h"
#include "cryptsetup-util.h"
#include "dbus-service.h"
#include "dbus-unit.h"
#include "devnum-util.h"
@@ -2882,7 +2881,9 @@ static void service_enter_refresh_extensions(Service *s) {
/* Given we are running from PID1, avoid doing potentially heavy I/O operations like opening images
* directly, and instead fork a worker process. */
r = unit_fork_helper_process(UNIT(s), "(sd-refresh-extensions)", /* into_cgroup= */ false, &worker);
r = unit_fork_helper_process_full(UNIT(s), "(sd-refresh-extensions)", /* into_cgroup= */ false,
FORK_ALLOW_DLOPEN, /* permit dlopen() to avoid load of libcryptsetup in pid1 */
&worker);
if (r < 0) {
log_unit_error_errno(UNIT(s), r, "Failed to fork process to refresh extensions in unit's namespace: %m");
goto fail;
@@ -5562,8 +5563,6 @@ static int service_live_mount(
u->id);
}
(void) dlopen_cryptsetup();
service_unwatch_control_pid(s);
s->live_mount_result = SERVICE_SUCCESS;
s->control_command = NULL;
@@ -5584,7 +5583,9 @@ static int service_live_mount(
* directly, and instead fork a worker process. We record the D-Bus message, so that we can reply
* after the operation has finished. This way callers can wait on the message and know that the new
* resource is available (or the operation failed) once they receive the response. */
r = unit_fork_helper_process(u, "(sd-mount-in-ns)", /* into_cgroup= */ false, &worker);
r = unit_fork_helper_process_full(u, "(sd-mount-in-ns)", /* into_cgroup= */ false,
FORK_ALLOW_DLOPEN,
&worker);
if (r < 0) {
log_unit_error_errno(u, r,
"Failed to fork process to mount '%s' on '%s' in unit's namespace: %m",
+7 -2
View File
@@ -5539,12 +5539,13 @@ int unit_set_exec_params(Unit *u, ExecParameters *p) {
return 0;
}
int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef *ret) {
int unit_fork_helper_process_full(Unit *u, const char *name, bool into_cgroup, ForkFlags flags, PidRef *ret) {
CGroupRuntime *crt = NULL;
pid_t pid;
int r;
assert(u);
assert((flags & (FORK_RESET_SIGNALS|FORK_DETACH|FORK_WAIT)) == 0); /* these don't really make sense for manager */
assert(ret);
/* Forks off a helper process and makes sure it is a member of the unit's cgroup, if configured to
@@ -5559,7 +5560,7 @@ int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef
crt = unit_get_cgroup_runtime(u);
}
r = safe_fork(name, FORK_REOPEN_LOG|FORK_DEATHSIG_SIGTERM, &pid);
r = safe_fork(name, FORK_REOPEN_LOG|FORK_DEATHSIG_SIGTERM|flags, &pid);
if (r < 0)
return r;
if (r > 0) {
@@ -5592,6 +5593,10 @@ int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef
return 0;
}
int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef *ret) {
return unit_fork_helper_process_full(u, name, into_cgroup, /* flags = */ 0, ret);
}
int unit_fork_and_watch_rm_rf(Unit *u, char **paths, PidRef *ret_pid) {
_cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
int r;
+1
View File
@@ -1005,6 +1005,7 @@ int unit_acquire_invocation_id(Unit *u);
int unit_set_exec_params(Unit *u, ExecParameters *p);
int unit_fork_helper_process_full(Unit *u, const char *name, bool into_cgroup, ForkFlags flags, PidRef *ret);
int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef *ret);
int unit_fork_and_watch_rm_rf(Unit *u, char **paths, PidRef *ret);