bugs: 004 non-PID-1 invocation (zyginit -v) clobbers live control socket
bugs: 004 non-PID-1 invocation (zyginit -v) clobbers live control socket Filed by the Hammerhead team. Running the zyginit binary directly on a host where zyginit is already PID 1 -- e.g. 'zyginit -v' to check the version -- falls through to the full supervisor path (only --version/-V short-circuit, not lowercase -v) and the un-guarded create_socket() unlinks+rebinds /var/run/zyginit.sock, clobbering the live PID 1's socket. zygctl then can't reach init; recovery requires reboot. Suggested: make -v print version+exit; refuse to unlink a socket a live server is listening on; gate non-PID-1 supervisor mode behind an explicit flag / distinct socket path.
Author:
Chris Tusa <chris.tusa@leafscale.com>
Date:
Jul 21, 2026 13:33
Changeset:
593a8091c02bd3240c1c4c4a7977f01b670f0131
Branch:
default
Changed files:
Diff
diff -r 0ffb8dfc1b98 -r 593a8091c02b bugs/004-nonpid1-invocation-clobbers-live-control-socket.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bugs/004-nonpid1-invocation-clobbers-live-control-socket.md Tue Jul 21 13:33:36 2026 -0500 @@ -0,0 +1,74 @@ +# Bug 004: running the zyginit binary as a non-PID-1 process (e.g. `zyginit -v`) re-runs init and clobbers the live control socket + +## Summary + +On a system where zyginit is already running as PID 1, invoking the zyginit +binary directly from a shell — e.g. an admin running **`zyginit -v`** to check +the version — does **not** short-circuit and exit. Instead it falls through +into the full supervisor sequence ("non-init mode"), which creates the control +socket. `ctlsocket.create_socket()` **unlinks the existing +`/var/run/zyginit.sock` and binds a new one**, clobbering the socket owned by +the live PID 1. From then on `zygctl` cannot reach the real init: + +``` +zygctl: cannot connect to zyginit at /var/run/zyginit.sock +zygctl: is zyginit running? +``` + +The system otherwise keeps running (PID 1 is untouched in memory), but it is no +longer controllable via `zygctl`, and per `main.reef:534` recovery "requires +reboot." + +Two distinct defects combine to make this an easy foot-gun: + +1. **`-v` is not recognized as a version flag.** Only `--version` and `-V` + (capital) short-circuit (`main.reef:943`). Lowercase `-v` — the conventional + "show version" flag for most CLI tools — is unmatched, so `zyginit -v` runs + the init/supervisor path. An admin's muscle-memory `zyginit -v` is exactly + the wrong thing. + +2. **Non-PID-1 invocation runs the full supervisor with no live-instance + guard.** The `getpid() != 1` branch is labeled "supervisor mode for testing" + (`main.reef` ~979/1015), but on a production host it still loads services, + enters the event loop, and calls the un-guarded socket setup (~`main.reef:1124`, + not wrapped in `is_pid_1()`), so `create_socket()` clobbers the production + socket. There is no check for "a live PID-1 zyginit already owns this socket." + +## Severity + +**Medium.** Not a boot-path defect, but a sharp operational foot-gun: a +completely reasonable admin action (`zyginit -v` to read the version) silently +breaks `zygctl` control of the running system until the next reboot. It is easy +to trigger and non-obvious to diagnose (the socket just "disappears"). + +## Affected Version + +zyginit 0.2.6 (confirmed by source inspection) and 0.2.5 (observed on +Hammerhead hh-alpha9 — an admin ran `zyginit -v` and `zygctl` then reported the +socket unreachable; system was fine but uncontrollable until reboot). + +## Suggested Fix (for the zyginit team) + +1. **Make `zyginit -v` safe.** Treat `-v` as a version alias (or, more + conservatively, reject unknown flags with a usage message) so `zyginit -v` + prints the version and exits **before** any PID-1/supervisor/socket setup — + the same short-circuit that already exists for `--version`/`-V`. +2. **Guard the socket against a live instance.** Before `create_socket()` + unlinks/binds `/var/run/zyginit.sock`, detect whether a live PID-1 zyginit + already owns it — e.g. try to `connect()` first (a successful connect means + a live server is present → refuse and exit), or check `/proc/1` is zyginit. + Never unlink a socket that a live server is actively listening on. +3. **Gate "supervisor test mode" behind an explicit opt-in.** Non-PID-1 + supervisor runs (for integration testing) should require an explicit flag + (e.g. `--supervisor-test`) and/or use a distinct socket path (e.g. + `$ZYGINIT_SOCK` / a temp path) so a bare `zyginit ...` on a production host + can never touch the production socket. + +## Reported by + +Hammerhead team (Chris Tusa), 2026-07-21. Triggered on hh-alpha9 (zyginit 0.2.5, +PID 1) by running `zyginit -v` from a root shell; `zygctl` subsequently could +not connect to `/var/run/zyginit.sock`. Cross-ref Hammerhead memory +`zyginit-no-exec-init-binary` (the known "don't exec the init binary on a +zyginit host — clobbers the socket, recover via `virsh destroy && virsh start`" +note).