From d03a38c494346ba191dfa2e4339f70ec266e7a51 Mon Sep 17 00:00:00 2001 From: Zachary Estrada Date: Thu, 11 Jun 2026 15:12:45 -0400 Subject: [PATCH 1/3] core_pattern_guard: subscribe to core_pattern_lock unconditionally --- pyplugins/interventions/core_pattern_guard.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pyplugins/interventions/core_pattern_guard.py b/pyplugins/interventions/core_pattern_guard.py index 58c48fdbc..afc26a101 100644 --- a/pyplugins/interventions/core_pattern_guard.py +++ b/pyplugins/interventions/core_pattern_guard.py @@ -15,8 +15,11 @@ but never writes through to the original .data target, so the global keeps the value the init script put there. -No-op when core.shared_dir is unset -- there's nowhere to mirror dumps to, -and the init script's hypercall won't fire either. +Dormant when core.shared_dir is unset: the init script only fires the +hypercall when SHARED_DIR is in the guest env. We subscribe unconditionally +anyway so a stray core_pattern_lock (e.g. env.SHARED_DIR set by hand without +core.shared_dir) is honored instead of tripping send_hypercall's +"Unregistered send_hypercall command" error. """ from penguin import Plugin, plugins @@ -66,10 +69,6 @@ def proc_handler(self, ptregs, ctl, write, buffer, lenp, ppos_ptr): class CorePatternGuard(Plugin): def __init__(self): - conf = self.get_arg("conf") or {} - if not conf.get("core", {}).get("shared_dir"): - return - self._registered = False plugins.send_hypercall.subscribe("core_pattern_lock", self._on_lock) From 51daeb7f30af9a3e0b3ac2ab075e633a78d4458b Mon Sep 17 00:00:00 2001 From: Zachary Estrada Date: Thu, 11 Jun 2026 15:12:45 -0400 Subject: [PATCH 2/3] send_hypercall: warn once instead of erroring on unsubscribed commands --- pyplugins/apis/send_hypercall.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pyplugins/apis/send_hypercall.py b/pyplugins/apis/send_hypercall.py index 9000e6cbc..df8850fed 100644 --- a/pyplugins/apis/send_hypercall.py +++ b/pyplugins/apis/send_hypercall.py @@ -71,6 +71,7 @@ def __init__(self) -> None: self.logger.setLevel("DEBUG") self.registered_events: Dict[str, Callable[..., Tuple[int, Union[str, bytes]]]] = {} + self._unhandled_warned: set = set() plugins.subscribe( plugins.Events, "igloo_send_hypercall", self.on_send_hypercall) @@ -157,7 +158,17 @@ def on_send_hypercall(self, cpu: Any, buf_addr: int, # Simulate command cb = self.registered_events.get(cmd) if cb is None: - self.logger.error(f"Unregistered send_hypercall command {cmd}") + # Guest-side init scripts fire some hypercalls (e.g. readiness, + # core_pattern_lock) best-effort: a config generated by an older + # penguin may not load the plugin that subscribes to them. That is + # benign, so warn once per command instead of erroring every boot. + if cmd not in self._unhandled_warned: + self._unhandled_warned.add(cmd) + self.logger.warning( + f"No subscriber for send_hypercall command {cmd!r}; " + f"if this project predates the plugin that handles it, " + f"add that plugin to the config or regenerate the config" + ) return cb_to_call = resolve_bound_method_from_class(cb) From cd45f1a3850aafc022303544c62c5029fb246b2a Mon Sep 17 00:00:00 2001 From: Zachary Estrada Date: Thu, 11 Jun 2026 15:12:45 -0400 Subject: [PATCH 3/3] verifier: tolerate missing conditions --- pyplugins/testing/verifier.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyplugins/testing/verifier.py b/pyplugins/testing/verifier.py index dcc652fdb..6da63b3ac 100644 --- a/pyplugins/testing/verifier.py +++ b/pyplugins/testing/verifier.py @@ -215,7 +215,7 @@ def get_test_case_output(self, name, kind): def check_test_cases(self): results = {} test_cases = [] - for name in self.conditions: + for name in self.conditions or {}: test_type = self.conditions[name]["type"] test = getattr(self, f"test_{test_type}", None) if test is None: