fix(store): do not trust PID files left behind by a previous boot - #5437
Merged
Conversation
Member
Even within a single boot, PID can be reused. Less likely though. |
AkihiroSuda
reviewed
Aug 23, 2026
AkihiroSuda
reviewed
Aug 23, 2026
ekalinin
force-pushed
the
fix/stale-pid-files
branch
from
August 23, 2026 18:57
83e2073 to
c24a290
Compare
`ReadPIDFile` decides whether a PID file belongs to a running process by signaling the PID. Within a single boot this is fine, because a PID file of a process that has already exited gets removed. Across a reboot it is not: the recorded PID may have been reused by an unrelated process, and Lima then reports the instance as `Broken`, either with "driver is running but host agent is not", or by trying to connect to a stale `ha.sock`. `limactl start` fails, and a LaunchDaemon with `KeepAlive` crash-loops it. Record the boot session ID of the host next to the PID files of an instance, and ignore a PID file that was written during a different boot. The ID is read from the `kern.bootsessionuuid` sysctl on macOS and from `/proc/sys/kernel/random/boot_id` on Linux; on platforms without such an identifier the behavior is unchanged, as it is for instances that were started by an older version of Lima and have no marker. The marker is shared by the PID files of the directory, so writing a PID file during a new boot removes all of them before the marker is refreshed: refreshing it must never make a PID file of a previous boot look current. Readers check the boot before they read the PID, and leave the removal to the next writer, so that a reader can never remove a PID file that another process is writing at that very moment. The PID file itself is renamed into place. When the boot session ID cannot be determined the PID file is not written at all, because a marker left over from a previous boot would otherwise void a PID file that is current. Only the platforms without support for a boot session ID fall back to the previous behavior. The PID of a stale PID file is never signaled, because it may belong to an unrelated process. `StartWithPaths` checked for `ha.pid` with `os.Stat`, which bypassed this logic; it now uses `ReadPIDFile` as well. Related to lima-vm#5087 Related to lima-vm#5075 Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
ekalinin
force-pushed
the
fix/stale-pid-files
branch
from
August 23, 2026 19:13
c24a290 to
e529ce0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
store.ReadPIDFile()decides whether a PID file belongs to a running process by signaling the PID. Within a single boot that is fine: the PID file of a process that has already exited is removed. Across a reboot it is not, because the recorded PID may have been reused by an unrelated process.osutil.ProcessAlive()also treatsEPERMas "alive", which makes a false positive likely when the reused PID belongs to a process of another user.The instance is then reported as
Broken, either with<vmtype> driver is running but host agent is not, or by trying to connect to a staleha.sock.limactl startfails immediately, and a LaunchDaemon withKeepAlivecrash-loops it.This is the root cause of bug 2 of #5087 and of #5075.
Changes
osutil.BootSessionID()returns an identifier of the current boot: thekern.bootsessionuuidsysctl on macOS,/proc/sys/kernel/random/boot_idon Linux. On other platforms it returnsErrBootSessionNotSupportedand nothing changes.store.WritePIDFile()writes a PID file and keeps aboot-session.tmpmarker in the same directory.ReadPIDFile()ignores a PID file whose marker is from a different boot, and never signals its PID.WritePIDFile().qemu.pidis written by QEMU itself, but the marker is per instance directory, so it is covered too.StartWithPaths()checked forha.pidwithos.Stat(), which bypassed all of this; it now usesReadPIDFile(), likelimactl hostagentalready did.The marker is shared by the PID files of a directory, so the ordering matters:
An instance that was started by an older version of Lima has no marker, and keeps the previous behavior.
The
.tmpsuffix of the marker is deliberate: it is then already covered by the cleanup inStopForcibly()and by the skip list used when cloning an instance.Testing
go test ./pkg/store/...covers writing the marker, a PID file from a previous boot (the PID used in the test is alive, and is still reported as gone), the sweep of the PID files of a previous boot, a truncated PID file from a previous boot, a failure to determine the boot session ID on both the read and the write path, instances without a marker, and platforms without support for a boot session ID.go test ./...,go test -race ./pkg/store/...,make golangci-lintand cross builds for linux and windows are clean.Assisted-by: Claude Code