I can reproduce the following issue on the Debian package but I asked an AI for a reproducer that doesn't require the Debian package.
I believe the reason I did not spot this earlier is because the embargo patchset didn't carry the runtests.py refactor, but have not double checked.
What happens
protected-regular hangs until the runner kills it whenever the kernel refuses a new
user namespace — most commonly because the tests are already running inside a chroot:
----- protected-regular log follows
TIMEOUT: test took over 300 seconds
----- protected-regular log ends
FAIL protected-regular
Five minutes and a spurious failure per run. We hit it building the Debian package
under sbuild --chroot-mode=unshare.
Version: 3.5.0, also on git master (dcef974c). util-linux 2.42.2, Python 3.14.
Where it stops
The test is not root, so it probes for a usable unshare
(testsuite/protected-regular_test.py:52). Running it under python3 -X faulthandler
and sending it SIGABRT once hung:
Current thread 0x00007fa8da70f200 [python3] (most recent call first):
File "/usr/lib/python3.14/selectors.py", line 398 in select
File "/usr/lib/python3.14/subprocess.py", line 2153 in _communicate
File "/usr/lib/python3.14/subprocess.py", line 1221 in communicate
File "/usr/lib/python3.14/subprocess.py", line 557 in run
File ".../testsuite/protected-regular_test.py", line 52 in <module>
Reproducer
Self-contained — the inner chroot is only there to make the kernel refuse the
namespace. [1] is the probe run directly, [2] is the same probe run the way the
test runs it:
unshare --user --map-root-user --mount --pid --fork --mount-proc sh -c '
mkdir -p /tmp/c; mount --make-rprivate /; mount --rbind / /tmp/c
/usr/sbin/chroot /tmp/c /bin/sh -c "
echo \"[1] direct:\"
t=\$(date +%s); unshare --user --map-root-user --map-users 5001:100000:1 true 2>/dev/null
echo \" rc=\$? after \$((\$(date +%s)-t))s\"
echo \"[2] capturing output:\"
t=\$(date +%s); timeout 10 python3 -c \"
import subprocess
subprocess.run([\\\"unshare\\\",\\\"--user\\\",\\\"--map-root-user\\\",\\\"--map-users\\\",\\\"5001:100000:1\\\",\\\"true\\\"], capture_output=True)\"
echo \" rc=\$? after \$((\$(date +%s)-t))s (124 = timed out)\"
"'
[1] direct:
rc=1 after 0s
[2] capturing output:
rc=124 after 10s (124 = timed out)
unshare(1) exits promptly either way. It is capture_output=True that hangs: a
process unshare leaves behind keeps the inherited stdout/stderr open, so the pipes
never reach EOF and communicate() blocks after the probe itself is gone. Arguably a
util-linux bug as well, and worth reporting there, but the probe only ever reads
.returncode — sending the output to /dev/null instead of capturing it makes the
hang go away and the test skips immediately:
SKIP protected-regular (Can't chown (need root or unshare with uidmap))
Regression
Introduced by 1f689ec0c21b7c2eaa9add1958d2c7ed280aac3e (testsuite: rewrite the
shell testsuite in Python), which turned the shell probe added in
67f1dcf604b6c5f2e860c3bbd174bbfd14d5c504 into a capture_output=True
subprocess.run(). The shell form waited only for its direct child and sent stderr to
/dev/null, so it returned immediately — case [1] above.
I can reproduce the following issue on the Debian package but I asked an AI for a reproducer that doesn't require the Debian package.
I believe the reason I did not spot this earlier is because the embargo patchset didn't carry the runtests.py refactor, but have not double checked.
What happens
protected-regularhangs until the runner kills it whenever the kernel refuses a newuser namespace — most commonly because the tests are already running inside a chroot:
Five minutes and a spurious failure per run. We hit it building the Debian package
under
sbuild --chroot-mode=unshare.Version: 3.5.0, also on git master (
dcef974c). util-linux 2.42.2, Python 3.14.Where it stops
The test is not root, so it probes for a usable
unshare(
testsuite/protected-regular_test.py:52). Running it underpython3 -X faulthandlerand sending it
SIGABRTonce hung:Reproducer
Self-contained — the inner chroot is only there to make the kernel refuse the
namespace.
[1]is the probe run directly,[2]is the same probe run the way thetest runs it:
unshare(1)exits promptly either way. It iscapture_output=Truethat hangs: aprocess
unshareleaves behind keeps the inherited stdout/stderr open, so the pipesnever reach EOF and
communicate()blocks after the probe itself is gone. Arguably autil-linux bug as well, and worth reporting there, but the probe only ever reads
.returncode— sending the output to/dev/nullinstead of capturing it makes thehang go away and the test skips immediately:
Regression
Introduced by
1f689ec0c21b7c2eaa9add1958d2c7ed280aac3e(testsuite: rewrite theshell testsuite in Python), which turned the shell probe added in
67f1dcf604b6c5f2e860c3bbd174bbfd14d5c504into acapture_output=Truesubprocess.run(). The shell form waited only for its direct child and sent stderr to/dev/null, so it returned immediately — case[1]above.