diff --git a/go/internal/runtime/microvm/launch.go b/go/internal/runtime/microvm/launch.go index 174b7c0c..102f9d9a 100644 --- a/go/internal/runtime/microvm/launch.go +++ b/go/internal/runtime/microvm/launch.go @@ -281,10 +281,11 @@ func vmmArgs(cfg BootConfig, consolePath string, opts launchOptions) []string { return args } -// startChild sets the child's Pdeathsig (a best-effort orphan guard: on Linux -// PR_SET_PDEATHSIG fires on the spawning THREAD's death, so the spawn holds the -// OS thread for it to bind reliably) and captures its stdout+stderr to logPath -// so a boot failure can surface the daemon's own diagnostics. The real teardown +// startChild installs the child's best-effort orphan guard via the +// platform-specific orphanGuardSysProcAttr (Linux PR_SET_PDEATHSIG, which fires +// on the spawning THREAD's death — so the spawn holds the OS thread for it to +// bind reliably; a no-op elsewhere) and captures its stdout+stderr to logPath so +// a boot failure can surface the daemon's own diagnostics. The real teardown // guarantee is Shutdown, not Pdeathsig (record §(g) lines 300-303). func startChild(c *child) error { logFile, err := os.OpenFile(c.logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600) @@ -295,7 +296,7 @@ func startChild(c *child) error { // dup'd it into the child. Close after Start below. c.cmd.Stdout = logFile c.cmd.Stderr = logFile - c.cmd.SysProcAttr = &syscall.SysProcAttr{Pdeathsig: syscall.SIGTERM} + c.cmd.SysProcAttr = orphanGuardSysProcAttr() runtime.LockOSThread() defer runtime.UnlockOSThread() diff --git a/go/internal/runtime/microvm/orphanguard_noop.go b/go/internal/runtime/microvm/orphanguard_noop.go new file mode 100644 index 00000000..af7057ea --- /dev/null +++ b/go/internal/runtime/microvm/orphanguard_noop.go @@ -0,0 +1,16 @@ +//go:build unix && !linux + +package microvm + +import "syscall" + +// orphanGuardSysProcAttr returns nil on non-Linux unix (darwin): there is no +// PR_SET_PDEATHSIG equivalent, so the child gets no parent-death guard. The +// microVM launcher only runs for real on Linux (KVM); this build exists so the +// unix-tagged package cross-compiles on darwin — e.g. when `go build +// ./cmd/compass-app` pulls microvm into a darwin compile — without naming the +// Linux-only Pdeathsig field. Teardown correctness does not depend on the guard +// (Shutdown is the real guarantee, record §(g)). +func orphanGuardSysProcAttr() *syscall.SysProcAttr { + return nil +} diff --git a/go/internal/runtime/microvm/orphanguard_pdeathsig.go b/go/internal/runtime/microvm/orphanguard_pdeathsig.go new file mode 100644 index 00000000..0b6b5913 --- /dev/null +++ b/go/internal/runtime/microvm/orphanguard_pdeathsig.go @@ -0,0 +1,19 @@ +//go:build linux + +package microvm + +import "syscall" + +// orphanGuardSysProcAttr returns the SysProcAttr that installs the child's +// best-effort orphan guard. On Linux that is PR_SET_PDEATHSIG (SIGTERM): the +// signal fires on the spawning THREAD's death, so startChild holds the OS thread +// across cmd.Start for it to bind reliably. The real teardown guarantee is +// Shutdown, not Pdeathsig (record §(g)); this only shortens the window a wedged +// host leaves a VMM orphaned. +// +// Pdeathsig is a Linux-only field of syscall.SysProcAttr, so naming it directly +// under //go:build unix would fail the darwin compile — this helper is the +// per-platform seam that keeps the unix-tagged launcher portable. +func orphanGuardSysProcAttr() *syscall.SysProcAttr { + return &syscall.SysProcAttr{Pdeathsig: syscall.SIGTERM} +}