2
0
mirror of https://github.com/xcat2/xcat-dep.git synced 2026-09-12 04:26:25 +00:00

fix(xcat-dep): run mock builds in an rslave mount namespace to protect the host cgroup

mock mounts /sys/fs/cgroup into every build chroot. On the systemd build
hosts every mount carries `shared` propagation, so the chroot cgroup
joins the same peer group as the host's own /sys/fs/cgroup. When mock
tears a chroot down -- its post-build --scrub, or an aborted build's
cleanup -- the cgroup unmount PROPAGATES back through the shared peer
group and unmounts the HOST's /sys/fs/cgroup. Every subsequent mock (and
even new login sessions) then fails with 'Failed to determine whether the
unified cgroups hierarchy is used: No medium found', wedging the whole
build host until an operator remounts cgroup2.

ppc64le is hit hardest because it leaks corpse chroot mounts on abort,
but x86_64 shares the identical shared-cgroup exposure and is one bad
abort away from the same failure.

Re-exec mockbuild-all.pl inside a private mount namespace made rslave
(unshare --mount --propagation slave): the namespace still sees host
mounts one-way, but nothing mock mounts or unmounts can propagate out to
the host, so a chroot teardown can no longer unmount the host cgroup. The
namespace also auto-reaps every mount mock leaks when the process exits,
so an aborted build no longer strands corpse mounts under /var/lib/mock.
Best-effort and guarded: only re-execs as root with unshare present, and
MOCKBUILD_ALL_MOUNTNS prevents a re-exec loop.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-08-13 07:34:09 -03:00
parent 411da1539e
commit 9bf3f7ad22
+30
View File
@@ -18,6 +18,36 @@ use MockBuildUtils qw(sh_quote print_step version_matches required_pkgs
rpm_version rpm_release rpm_sigmd5 restamp_release_line
cross_copy_genesis finalize_xcat_dep bump_dep_release_suffix);
# --- Mount-namespace isolation: guard the host cgroup against mock teardown propagation ----------
# mock mounts /sys/fs/cgroup into every build chroot. On these systemd build hosts every mount is
# `shared`, so the chroot's cgroup joins the HOST's cgroup peer group. When mock tears a chroot down
# -- its post-build --scrub, or an aborted build's cleanup -- the unmount PROPAGATES back through the
# shared peer group and unmounts the HOST's /sys/fs/cgroup, after which every later mock (and even new
# login sessions) dies with "Failed to determine whether the unified cgroups hierarchy is used: No
# medium found". This bit ppc hardest (it leaks corpse chroot mounts on abort) but x86 shares the same
# shared-cgroup exposure. Re-exec inside a private mount namespace made rslave
# (`unshare --mount --propagation slave`): the namespace still sees host mounts (slave = one-way), but
# nothing mock mounts/unmounts can propagate OUT to the host. As a bonus the namespace tears down every
# mount mock leaks when we exit, so an aborted build can no longer leave corpse mounts under
# /var/lib/mock. Best-effort: only as root (needs CAP_SYS_ADMIN) and only if `unshare` exists;
# otherwise warn loudly and continue unisolated. MOCKBUILD_ALL_MOUNTNS guards against a re-exec loop.
unless ($ENV{MOCKBUILD_ALL_MOUNTNS}) {
if ($> != 0) {
warn "WARN: not root -- skipping mount-namespace isolation (host-cgroup propagation guard); "
. "run as root in CI so mock chroot teardown cannot unmount the host /sys/fs/cgroup\n";
} elsif (system('sh', '-c', 'command -v unshare >/dev/null 2>&1') != 0) {
warn "WARN: 'unshare' not found -- skipping mount-namespace isolation; mock chroot teardown "
. "may unmount the host /sys/fs/cgroup on a shared-propagation host\n";
} else {
$ENV{MOCKBUILD_ALL_MOUNTNS} = 1;
my @reexec = ('unshare', '--mount', '--propagation', 'slave', '--', $^X, $0, @ARGV);
exec { $reexec[0] } @reexec;
# exec only returns on failure -- fall through and run unisolated rather than abort the build.
warn "WARN: exec unshare failed ($!) -- continuing without mount-namespace isolation\n";
delete $ENV{MOCKBUILD_ALL_MOUNTNS};
}
}
my $script_dir = abs_path(dirname(__FILE__));
my $repo_root = abs_path($script_dir);
my $xcat_src = "$repo_root/../xcat-core";