From d2ea5a3fd02a40c888a98f2d4d0d5146e6293f6b Mon Sep 17 00:00:00 2001 From: lstocchi Date: Wed, 7 Jan 2026 12:42:55 +0100 Subject: [PATCH 1/2] prevent starting/stopping legacy Hyper-v machines when not elevated Podman 5.x and earlier required to run as admin to work with Hyper-v. Starting from Podman 6 this is not mandatory anymore as Registry entries are handled differently. However, it may happen the user have a legacy machine running when switching to Podman 6 or starts an old machine in elevated mode and then tries to stop it as a normal user with Podman 6. If that happens the system will end up in a corrupted state as the gvproxy process will not be stopped. To prevent such scenario and issues, this commit maintains the original behavior Podman 5.x has. Legacy Hyper-v machines needs to be handled with elevated rights. Signed-off-by: lstocchi --- pkg/machine/hyperv/hutil.go | 1 + pkg/machine/hyperv/stubber.go | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pkg/machine/hyperv/hutil.go b/pkg/machine/hyperv/hutil.go index 72cf15dc71..c2fb950285 100644 --- a/pkg/machine/hyperv/hutil.go +++ b/pkg/machine/hyperv/hutil.go @@ -15,6 +15,7 @@ var ( ErrHypervRegistryInitRequiresElevation = errors.New("the first time Podman initializes a Hyper-V machine, it requires admin rights. Please run Podman as an administrator") ErrHypervRegistryRemoveRequiresElevation = errors.New("removing this Hyper-V machine requires admin rights to clean up the Windows Registry. Please run Podman as an administrator") ErrHypervRegistryUpdateRequiresElevation = errors.New("this machine's configuration requires additional Hyper-V networking (hvsock) entries in the Windows Registry. Please run Podman as an administrator") + ErrHypervLegacyMachineRequiresElevation = errors.New("starting or stopping Hyper-V machines created with Podman 5.x or earlier requires admin rights. Please run Podman as an administrator") ) func HasHyperVAdminRights() bool { diff --git a/pkg/machine/hyperv/stubber.go b/pkg/machine/hyperv/stubber.go index 245860127d..2a30607bec 100644 --- a/pkg/machine/hyperv/stubber.go +++ b/pkg/machine/hyperv/stubber.go @@ -265,6 +265,21 @@ func (h HyperVStubber) canRemove(mc *vmconfigs.MachineConfig) error { return ErrHypervRegistryRemoveRequiresElevation } +// canStartOrStop checks if the machine can be started or stopped. +// Legacy machines require admin rights to start or stop. +func (h HyperVStubber) canStartOrStop(mc *vmconfigs.MachineConfig) error { + if windows.HasAdminRights() { + return nil + } + + // if machine is legacy (machineName field), require admin rights to start or stop + if isLegacyMachine(mc) { + return ErrHypervLegacyMachineRequiresElevation + } + + return nil +} + // countMachinesWithToolname counts only machines that have a toolname field with value "podman". func (h HyperVStubber) countMachinesWithToolname() (int, error) { dirs, err := env.GetMachineDirs(h.VMType()) @@ -345,7 +360,9 @@ func (h HyperVStubber) StartNetworking(mc *vmconfigs.MachineConfig, cmd *gvproxy } func (h HyperVStubber) StartVM(mc *vmconfigs.MachineConfig) (func() error, func() error, error) { - var err error + if err := h.canStartOrStop(mc); err != nil { + return nil, nil, err + } _, vm, err := GetVMFromMC(mc) if err != nil { @@ -412,6 +429,10 @@ func (h HyperVStubber) State(mc *vmconfigs.MachineConfig, _ bool) (define.Status } func (h HyperVStubber) StopVM(mc *vmconfigs.MachineConfig, hardStop bool) error { + if err := h.canStartOrStop(mc); err != nil { + return err + } + vmm := hypervctl.NewVirtualMachineManager() vm, err := vmm.GetMachine(mc.Name) if err != nil { From ca44e3a4d7ff79b163c48f7a3ffec2f2636d3faa Mon Sep 17 00:00:00 2001 From: lstocchi Date: Thu, 8 Jan 2026 15:26:26 +0100 Subject: [PATCH 2/2] Fix race condition in CleanupGVProxy when reading gvproxy PID file When startVM fails quickly, CleanupGVProxy may attempt to read the gvproxy.pid file before gvproxy has written it, causing cleanup to fail. This commit adds retry logic that waits up to 2 seconds for the PID file to appear. Signed-off-by: lstocchi --- pkg/machine/gvproxy.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/pkg/machine/gvproxy.go b/pkg/machine/gvproxy.go index 97eef42ac0..5f75c11557 100644 --- a/pkg/machine/gvproxy.go +++ b/pkg/machine/gvproxy.go @@ -5,13 +5,37 @@ import ( "fmt" "io/fs" "strconv" + "time" "github.com/containers/podman/v6/pkg/machine/define" ) +const ( + pidFileWaitTimeout = 2 * time.Second + pidFileCheckInterval = 50 * time.Millisecond +) + +func readPIDFileWithRetry(f define.VMFile) ([]byte, error) { + deadline := time.Now().Add(pidFileWaitTimeout) + + for time.Now().Before(deadline) { + gvPid, err := f.Read() + if err == nil { + return gvPid, nil + } + if !errors.Is(err, fs.ErrNotExist) { + return nil, err + } + time.Sleep(pidFileCheckInterval) + } + + // Final attempt after timeout + return f.Read() +} + // CleanupGVProxy reads the --pid-file for gvproxy attempts to stop it func CleanupGVProxy(f define.VMFile) error { - gvPid, err := f.Read() + gvPid, err := readPIDFileWithRetry(f) if err != nil { // The file will also be removed by gvproxy when it exits so // we need to account for the race and can just ignore it here.