Filter out more sysctl params that throw Invalid argument when executed on the fly

This commit is contained in:
Miguel Jacq 2026-06-16 16:30:33 +10:00
parent 9546e1b8ed
commit e682aae41e
Signed by: mig5
GPG key ID: 03906B4110AAD3B8
2 changed files with 82 additions and 2 deletions

View file

@ -1121,8 +1121,14 @@ _SYSCTL_GENERATED_SRC_REL = "sysctl/99-enroll.conf"
# config. This avoids generating a file that tries to replay one-shot triggers or # config. This avoids generating a file that tries to replay one-shot triggers or
# host identity that should be managed elsewhere (e.g. /etc/hostname). # host identity that should be managed elsewhere (e.g. /etc/hostname).
_SYSCTL_VOLATILE_KEYS = { _SYSCTL_VOLATILE_KEYS = {
"fs.binfmt_misc.status",
"kernel.domainname", "kernel.domainname",
"kernel.hostname", "kernel.hostname",
"kernel.kexec_load_disabled",
"kernel.kexec_load_limit_panic",
"kernel.kexec_load_limit_reboot",
"kernel.max_rcu_stall_to_panic",
"kernel.modules_disabled",
"kernel.ns_last_pid", "kernel.ns_last_pid",
"net.ipv4.route.flush", "net.ipv4.route.flush",
"net.ipv6.route.flush", "net.ipv6.route.flush",
@ -1131,6 +1137,21 @@ _SYSCTL_VOLATILE_KEYS = {
"vm.stat_refresh", "vm.stat_refresh",
} }
_SYSCTL_VOLATILE_PREFIXES = (
"fs.binfmt_misc.",
"kernel.sched_domain.",
)
# These are paired with ratio/byte counterparts. The inactive side appears as 0
# when read; replaying that 0 through sysctl -p is noisy and can be rejected by
# kernels that enforce minimum values.
_SYSCTL_SKIP_ZERO_VALUE_KEYS = {
"vm.dirty_background_bytes",
"vm.dirty_background_ratio",
"vm.dirty_bytes",
"vm.dirty_ratio",
}
def _sysctl_proc_path(key: str) -> str: def _sysctl_proc_path(key: str) -> str:
return "/proc/sys/" + key.replace(".", "/") return "/proc/sys/" + key.replace(".", "/")
@ -1139,7 +1160,9 @@ def _sysctl_proc_path(key: str) -> str:
def _sysctl_key_is_persistable(key: str) -> tuple[bool, str]: def _sysctl_key_is_persistable(key: str) -> tuple[bool, str]:
if not key or not _SYSCTL_KEY_RE.fullmatch(key): if not key or not _SYSCTL_KEY_RE.fullmatch(key):
return False, "invalid key" return False, "invalid key"
if key in _SYSCTL_VOLATILE_KEYS: if key in _SYSCTL_VOLATILE_KEYS or any(
key.startswith(prefix) for prefix in _SYSCTL_VOLATILE_PREFIXES
):
return False, "volatile/action key" return False, "volatile/action key"
proc_path = _sysctl_proc_path(key) proc_path = _sysctl_proc_path(key)
@ -1155,6 +1178,17 @@ def _sysctl_key_is_persistable(key: str) -> tuple[bool, str]:
return True, "" return True, ""
def _sysctl_entry_is_persistable(key: str, value: str) -> tuple[bool, str]:
ok, reason = _sysctl_key_is_persistable(key)
if not ok:
return ok, reason
if key in _SYSCTL_SKIP_ZERO_VALUE_KEYS and str(value).strip() == "0":
return False, "inactive mutually-exclusive zero value"
return True, ""
def _parse_sysctl_a_output( def _parse_sysctl_a_output(
text: str, text: str,
*, *,
@ -1199,7 +1233,7 @@ def _parse_sysctl_a_output(
skipped["duplicate"] += 1 skipped["duplicate"] += 1
continue continue
if require_persistable: if require_persistable:
ok, _reason = _sysctl_key_is_persistable(key) ok, _reason = _sysctl_entry_is_persistable(key, value)
if not ok: if not ok:
skipped["non_persistable"] += 1 skipped["non_persistable"] += 1
continue continue

View file

@ -327,6 +327,52 @@ def test_parse_sysctl_a_output_keeps_persistable_values(monkeypatch):
assert skipped["duplicate"] == 1 assert skipped["duplicate"] == 1
def test_sysctl_filter_skips_non_replayable_runtime_keys(monkeypatch):
for key in (
"fs.binfmt_misc.status",
"fs.binfmt_misc.register",
"kernel.kexec_load_disabled",
"kernel.kexec_load_limit_panic",
"kernel.kexec_load_limit_reboot",
"kernel.max_rcu_stall_to_panic",
"kernel.modules_disabled",
"kernel.sched_domain.cpu0.domain0.flags",
):
ok, reason = h._sysctl_key_is_persistable(key)
assert ok is False
assert reason == "volatile/action key"
monkeypatch.setattr(h, "_sysctl_key_is_persistable", lambda key: (True, ""))
for key in (
"vm.dirty_background_bytes",
"vm.dirty_background_ratio",
"vm.dirty_bytes",
"vm.dirty_ratio",
):
ok, reason = h._sysctl_entry_is_persistable(key, "0")
assert ok is False
assert reason == "inactive mutually-exclusive zero value"
assert h._sysctl_entry_is_persistable(key, "10")[0] is True
def test_parse_sysctl_a_output_skips_non_replayable_values(monkeypatch):
monkeypatch.setattr(
h,
"_sysctl_key_is_persistable",
lambda key: (key != "kernel.modules_disabled", "volatile/action key"),
)
params, skipped = h._parse_sysctl_a_output(
"kernel.modules_disabled = 0\n"
"vm.dirty_background_bytes = 0\n"
"vm.dirty_ratio = 20\n"
"net.ipv4.ip_forward = 1\n"
)
assert params == {"net.ipv4.ip_forward": "1", "vm.dirty_ratio": "20"}
assert skipped["non_persistable"] == 2
def test_collect_sysctl_snapshot_writes_generated_artifact(monkeypatch, tmp_path: Path): def test_collect_sysctl_snapshot_writes_generated_artifact(monkeypatch, tmp_path: Path):
monkeypatch.setattr( monkeypatch.setattr(
h, h,