From 7e31020c1cda46d196be900470551ffd4b777431 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 3 Jul 2026 18:53:54 +0000 Subject: [PATCH] =?UTF-8?q?fix(windows-host):=20second-host=20guard=20?= =?UTF-8?q?=E2=80=94=20classify=20ACCESS=5FDENIED=20on=20the=20instance=20?= =?UTF-8?q?mutex=20as=20in-use?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On-glass the SCM service creates Global\punktfunk-vdisplay-manager as SYSTEM, so a second elevated-admin host's CreateMutexW fails ACCESS_DENIED (the implicit open is checked against the SYSTEM DACL) before the ALREADY_EXISTS branch can fire — right refusal, wrong message. Map it to the same loud "another instance is live" error. Co-Authored-By: Claude Fable 5 --- .../src/vdisplay/windows/manager.rs | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/crates/punktfunk-host/src/vdisplay/windows/manager.rs b/crates/punktfunk-host/src/vdisplay/windows/manager.rs index dd3ae89..6ede993 100644 --- a/crates/punktfunk-host/src/vdisplay/windows/manager.rs +++ b/crates/punktfunk-host/src/vdisplay/windows/manager.rs @@ -223,21 +223,28 @@ pub(crate) fn control_device_handle() -> Option { /// second process fail its vdisplay open LOUDLY instead. Held, never released, for the process /// lifetime; the OS reclaims it (and frees the name) when the process exits, however it exits. fn acquire_single_instance() -> Result { - // SAFETY: plain FFI create of a named mutex; the returned handle (checked by `?`) is solely - // owned by the `OwnedHandle`, and `GetLastError` is read immediately after the create — the - // documented ERROR_ALREADY_EXISTS protocol for pre-existing named objects. + const IN_USE: &str = "another punktfunk-host process is already managing pf-vdisplay on this \ + machine — refusing to touch the driver (a second manager's startup CLEAR_ALL would raze \ + the live host's monitors mid-stream). Stop the other instance (e.g. `punktfunk-host \ + service stop`) first."; + // SAFETY: plain FFI create of a named mutex; the returned handle (checked) is solely owned by + // the `OwnedHandle`, and `GetLastError` is read immediately after the create — the documented + // ERROR_ALREADY_EXISTS protocol for pre-existing named objects. unsafe { - let h = CreateMutexW(None, false, w!("Global\\punktfunk-vdisplay-manager")) - .context("CreateMutexW(punktfunk-vdisplay single-instance guard)")?; + let h = match CreateMutexW(None, false, w!("Global\\punktfunk-vdisplay-manager")) { + Ok(h) => h, + // The name exists but its creator's DACL denies this token the implicit OPEN (the SCM + // service creates it as SYSTEM; a second elevated-admin host lands here instead of in + // the ALREADY_EXISTS branch — validated on-glass). Same meaning: an instance is live. + Err(e) if e.code().0 == 0x8007_0005u32 as i32 => anyhow::bail!("{IN_USE}"), + Err(e) => { + return Err(e).context("CreateMutexW(punktfunk-vdisplay single-instance guard)"); + } + }; let already = GetLastError() == ERROR_ALREADY_EXISTS; let owned = OwnedHandle::from_raw_handle(h.0 as _); if already { - anyhow::bail!( - "another punktfunk-host process is already managing pf-vdisplay on this machine — \ - refusing to touch the driver (a second manager's startup CLEAR_ALL would raze the \ - live host's monitors mid-stream). Stop the other instance (e.g. `punktfunk-host \ - service stop`) first." - ); + anyhow::bail!("{IN_USE}"); } Ok(owned) }