Hi Luca,

Am 2026-04-14 13:41, schrieb Luca Boccassi:
Control: close -1

Please stop playing ping-pong with the BTS. This is an issue on your
setup, regardless of whatever is the right option or env var to set.

I've just seen, that it was also reported upstream: https://github.com/systemd/mkosi/issues/1368#issuecomment-4069166111

As mentioned it is fixed in latest upstream git and not reproducible with the version in trixie. I also really don't like the ping-pong. But this is definitely a bug in the 26 release.

Bisecting mkosi points to this:

--8<--
commit 88a91f5053ca6919d39948a7c23c798225900518 (HEAD)
Author: Daan De Meyer <[email protected]>
Date:   Tue Dec 23 17:17:01 2025 +0100

    run: Call execvpe() from preexec function

Python does its own executable lookup in $PATH before executing the preexec function, and hence before we have set up the sandbox which influences the lookup results. To get around that, let's call execvpe() ourselves inside the preexec() function, and not give Python the chance to do it itself. This ensures we can do the proper executable lookup after setting up the sandbox. If we can't find the executable, do nothing, and let Python do its own search logic so it can return a proper error, which we cannot do from the preexec function. Note that by doing this we also skip Python closing all open file descriptors except the ones specified by the user in pass_fds, but since Python opens all file descriptors with O_CLOEXEC anyway, we'll assume we're good and don't need to close open file descriptors
    explicitly.
--8<--

I picked the attached patch in d/patches and the so built package is fine. If you want I can create a MR in salsa for this.

cheers
Manuel
From 88a91f5053ca6919d39948a7c23c798225900518 Mon Sep 17 00:00:00 2001
From: DaanDeMeyer <[email protected]>
Date: Tue, 23 Dec 2025 17:17:01 +0100
Subject: [PATCH] run: Call execvpe() from preexec function

Python does its own executable lookup in $PATH before executing the preexec function, and
hence before we have set up the sandbox which influences the lookup results. To get around
that, let's call execvpe() ourselves inside the preexec() function, and not give Python the
chance to do it itself. This ensures we can do the proper executable lookup after setting
up the sandbox. If we can't find the executable, do nothing, and let Python do its own
search logic so it can return a proper error, which we cannot do from the preexec function.
Note that by doing this we also skip Python closing all open file descriptors except the
ones specified by the user in pass_fds, but since Python opens all file descriptors with
O_CLOEXEC anyway, we'll assume we're good and don't need to close open file descriptors
explicitly.
---
 mkosi/run.py | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git a/mkosi/run.py b/mkosi/run.py
index a0cc19b0..eb4c45ef 100644
--- a/mkosi/run.py
+++ b/mkosi/run.py
@@ -222,9 +222,13 @@ def spawn(
                 preexec()
 
             if sbx and apply_sandbox_in_preexec:
-                # The env passed to subprocess.Popen() replaces the environment wholesale so any
-                # modifications made by mkosi-sandbox would be overridden if we used that. Hence process the
-                # environment in the preexec function.
+                # if we get here we should have neither a prefix nor a setup command to execute.
+                assert not prefix
+                assert not setup
+
+                # mkosi.sandbox.main() updates os.environ but the environment passed to Popen() is not yet in
+                # effect by the time the preexec function is called. To get around that, we update the
+                # environment ourselves here.
                 os.environ.clear()
                 os.environ.update(env)
                 try:
@@ -233,6 +237,19 @@ def spawn(
                     sys.excepthook(*ensure_exc_info())
                     os._exit(1)
 
+                # Python does its own executable lookup in $PATH before executing the preexec function, and
+                # hence before we have set up the sandbox which influences the lookup results. To get around
+                # that, let's call execvp() ourselves inside the preexec() function, and not give Python the
+                # chance to do it itself. This ensures we can do the proper executable lookup after setting
+                # up the sandbox. If we can't find the executable, do nothing, and let Python do its own
+                # search logic so it can return a proper error, which we cannot do from the preexec function.
+                # Note that by doing this we also skip Python closing all open file descriptors except the
+                # ones specified by the user in pass_fds, but since Python opens all file descriptors with
+                # O_CLOEXEC anyway, we'll assume we're good and don't need to close open file descriptors
+                # explicitly.
+                if s := shutil.which(cmd[0]):
+                    os.execvp(s, cmd)
+
         prefix = []
         if sbx and not apply_sandbox_in_preexec:
             module = stack.enter_context(resource_path(sys.modules[__package__ or __name__]))
-- 
2.47.3

Reply via email to