60-ukify: skip /proc/cmdline in containers

When running inside a container (during image builds or such),
`/proc/cmdline` belongs to the host and would leak the host's root= (or a
faked placeholder like root=/dev/osbuild) into the UKI.

Mirror the same guard that `90-loaderentry.install` has: check
`systemd-detect-virt --container` before falling back to `/proc/cmdline`,
and return an empty cmdline if we are in a container to ensure both
modes (`uki` and `bls`) work the same.

Signed-off-by: Simon de Vlieger <cmdr@supakeen.com>
This commit is contained in:
Simon de Vlieger
2026-08-03 12:05:55 +02:00
committed by Yu Watanabe
parent 7314e1cf76
commit bdb5d53d4f
2 changed files with 24 additions and 1 deletions

View File

@@ -22,6 +22,7 @@
import argparse
import os
import shlex
import subprocess
import types
from shutil import which
from pathlib import Path
@@ -195,6 +196,18 @@ def kernel_cmdline_base() -> list[str]:
if os.getenv('KERNEL_INSTALL_CONF_ROOT'):
return []
# Don't read /proc/cmdline in containers it belongs to the host and would
# leak the host's root= (or a faked value) into the UKI. Mirrors
# the same guard in 90-loaderentry.install.
try:
result = subprocess.run(
['systemd-detect-virt', '--container', '--quiet'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if result.returncode == 0:
return []
except FileNotFoundError:
pass
# If we read /proc/cmdline, we need to do some additional filtering.
options = Path('/proc/cmdline').read_text().split()
return [opt for opt in options

View File

@@ -146,7 +146,9 @@ if [ -f "$ukify" ]; then
python3 - "$ukify_install" <<'PY'
import os
import runpy
import subprocess
import sys
import unittest.mock
ns = runpy.run_path(sys.argv[1], run_name='not_main')
@@ -171,14 +173,22 @@ class FakePath:
module_globals = ns['kernel_cmdline_base'].__globals__
module_globals['Path'] = FakePath
# Mock subprocess.run to simulate not being in a container, so the test
# works regardless of the environment (including container-based CI).
os.environ.pop('KERNEL_INSTALL_CONF_ROOT', None)
assert ns['kernel_cmdline_base']() == ['root=fake', 'quiet']
with unittest.mock.patch.object(subprocess, 'run', return_value=subprocess.CompletedProcess([], 1)):
assert ns['kernel_cmdline_base']() == ['root=fake', 'quiet']
os.environ['KERNEL_INSTALL_CONF_ROOT'] = '/conf-root'
assert ns['kernel_cmdline_base']() == ['root=conf', 'quiet', 'splash']
os.environ['KERNEL_INSTALL_CONF_ROOT'] = '/empty-conf-root'
assert ns['kernel_cmdline_base']() == []
# Test that /proc/cmdline is skipped in containers
os.environ.pop('KERNEL_INSTALL_CONF_ROOT', None)
with unittest.mock.patch.object(subprocess, 'run', return_value=subprocess.CompletedProcess([], 0)):
assert ns['kernel_cmdline_base']() == [], 'should return empty in container'
PY
mkdir "$D/sources/install.conf.d"