]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
process-util: add a flag for retaining dlopen()-ability
authorMike Yuan <me@yhndnzj.com>
Tue, 16 Dec 2025 00:54:40 +0000 (01:54 +0100)
committerMike Yuan <me@yhndnzj.com>
Tue, 16 Dec 2025 20:07:20 +0000 (21:07 +0100)
While blocking dlopen() in child generally makes sense, it does
also lead to misery - in the very case of extension/cred refreshing
it then enforces loading of libcryptsetup/libacl in pid1, which
are otherwise never used. Let's add a flag to opt out hence, but
still prohibit it in namespace_fork/enter().

src/basic/namespace-util.c
src/basic/process-util.c
src/basic/process-util.h

index c8ee38b4f121728a2f04d27793c313047d18c23a..3a960e19cc5d271922eba56638b7f92f82c15c0d 100644 (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. */
index 9cb3b131fb7e7d4d50145947aacc8ea2f068fa18..31dbdff87a23d7b0bf26047cf9fb269b5f0a986e 100644 (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,
index 9217eebc8b501d2c92524f783b5f6b4527ff2222..0290978b998aa5ef815b3a43982818f9a21fde0e 100644 (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(