From b46aa15afbfce5ea1ef3731779353194e79fa6cd Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 3 Jul 2026 17:20:48 +0000 Subject: [PATCH] =?UTF-8?q?fix(windows-drivers):=20pf-vdisplay=20robustnes?= =?UTF-8?q?s=20=E2=80=94=20AdapterInitStatus=20gate,=20pooled-device=20TDR?= =?UTF-8?q?=20check,=20MMCSS-optional=20worker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Batch B of the audit's medium tier (M4+M5+M6): - M4: adapter_init_finished now reads AdapterInitStatus (was ignored) and only stashes the adapter on NT_SUCCESS, per the MS sample. A failed async init previously produced a HUSK adapter: monitors created on it arrive but the OS never assigns a swap-chain — every session black-screens with no visible cause (the exact signature live fault-injection produced after a WUDFHost kill). Unset adapter → ADD fails cleanly (host-retryable) and a re-entrant D0 retries the init; the status is now in the debug log. - M5: pooled_device checks GetDeviceRemovedReason on a cache hit — a TDR'd device was returned for its LUID forever (SetDevice fail-loop, black virtual display until device teardown); now it falls through to a fresh create. - M6: an AvSetMmThreadCharacteristicsW failure no longer aborts the worker before draining (which stalled the monitor and leaked the WDF swap-chain object) — continue unprioritized like the MS sample; revert only if MMCSS actually engaged. Co-Authored-By: Claude Fable 5 --- .../drivers/pf-vdisplay/src/callbacks.rs | 18 +++++++++--- .../pf-vdisplay/src/direct_3d_device.rs | 11 ++++++- .../pf-vdisplay/src/swap_chain_processor.rs | 29 +++++++++++++------ 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/packaging/windows/drivers/pf-vdisplay/src/callbacks.rs b/packaging/windows/drivers/pf-vdisplay/src/callbacks.rs index ba0b1d9..7001e45 100644 --- a/packaging/windows/drivers/pf-vdisplay/src/callbacks.rs +++ b/packaging/windows/drivers/pf-vdisplay/src/callbacks.rs @@ -25,13 +25,23 @@ pub unsafe extern "C" fn device_d0_entry( crate::adapter::init_adapter(device) } -/// Async completion of `IddCxAdapterInitAsync`: stash the adapter for later DDIs. STEP 4 also starts the -/// watchdog here. +/// Async completion of `IddCxAdapterInitAsync`: stash the adapter for later DDIs — IFF the init +/// actually SUCCEEDED. STEP 4 also starts the watchdog here. pub unsafe extern "C" fn adapter_init_finished( adapter: iddcx::IDDCX_ADAPTER, - _p_in: *const iddcx::IDARG_IN_ADAPTER_INIT_FINISHED, + p_in: *const iddcx::IDARG_IN_ADAPTER_INIT_FINISHED, ) -> NTSTATUS { - dbglog!("[pf-vd] adapter_init_finished"); + // SAFETY: the framework supplies a valid, live input-args pointer for the call. + let status = unsafe { (*p_in).AdapterInitStatus }; + dbglog!("[pf-vd] adapter_init_finished (AdapterInitStatus={status:#010x})"); + // The MS sample gates on NT_SUCCESS(AdapterInitStatus). An adapter whose async init FAILED is a + // husk the contract forbids using: monitors created on it arrive but are never activated (no + // swap-chain ever assigned) — every session then black-screens with no visible cause. Leaving + // the ADAPTER unset makes `create_monitor` fail the ADD cleanly (host-visible + retryable), and + // a re-entrant D0 retries the init (`init_adapter` only short-circuits once the stash is set). + if status < 0 { + return STATUS_SUCCESS; // the callback itself succeeded; the failure is in NOT adopting + } crate::adapter::set_adapter(adapter); crate::control::start_watchdog(); STATUS_SUCCESS diff --git a/packaging/windows/drivers/pf-vdisplay/src/direct_3d_device.rs b/packaging/windows/drivers/pf-vdisplay/src/direct_3d_device.rs index 57d2e27..49a3af8 100644 --- a/packaging/windows/drivers/pf-vdisplay/src/direct_3d_device.rs +++ b/packaging/windows/drivers/pf-vdisplay/src/direct_3d_device.rs @@ -150,7 +150,16 @@ pub fn pooled_device(luid: LUID) -> Option> { if let Some((k, dev)) = pool.as_ref() && *k == key { - return Some(dev.clone()); + // A TDR / driver reset REMOVES the pooled device permanently; handing it out again gives + // every future swap-chain a dead device (SetDevice fail-loop → black virtual display until + // device teardown). Detect and fall through to a fresh create instead. + // SAFETY: plain status query on the live pooled device. + match unsafe { dev.device.GetDeviceRemovedReason() } { + Ok(()) => return Some(dev.clone()), + Err(e) => { + dbglog!("[pf-vd] pooled D3D device was REMOVED ({e:?}) — recreating on {key:#x}"); + } + } } match Direct3DDevice::init(luid) { Ok(d) => { diff --git a/packaging/windows/drivers/pf-vdisplay/src/swap_chain_processor.rs b/packaging/windows/drivers/pf-vdisplay/src/swap_chain_processor.rs index 0163e5d..f19d707 100644 --- a/packaging/windows/drivers/pf-vdisplay/src/swap_chain_processor.rs +++ b/packaging/windows/drivers/pf-vdisplay/src/swap_chain_processor.rs @@ -117,9 +117,18 @@ impl SwapChainProcessor { // SAFETY: `w!("Distribution")` is a 'static null-terminated UTF-16 task name; `av_task` is a // valid local out-param. The returned handle is reverted with AvRevertMmThreadCharacteristics. let res = unsafe { AvSetMmThreadCharacteristicsW(w!("Distribution"), &mut av_task) }; - let Ok(av_handle) = res else { - dbglog!("[pf-vd] swap-chain: failed to prioritize thread: {res:?}"); - return; + // MMCSS can fail under the restricted WUDFHost token ('Distribution' task unregistered / + // service unavailable). The MS sample CONTINUES unprioritized — never abort: returning + // here would leave the assigned swap-chain undrained (the monitor stalls, DWM blocks on + // it) and leak the WDF swap-chain object until device teardown. + let av_handle = match res { + Ok(h) => Some(h), + Err(e) => { + dbglog!( + "[pf-vd] swap-chain: MMCSS prioritization failed ({e:?}) — continuing unprioritized" + ); + None + } }; Self::run_core( @@ -144,12 +153,14 @@ impl SwapChainProcessor { call_unsafe_wdf_function_binding!(WdfObjectDelete, swap_chain.0 as WDFOBJECT); } - // Revert the thread to normal once it's done. - // SAFETY: `av_handle` is the live characteristics handle returned by AvSetMmThreadCharacteristicsW - // above, reverted exactly once here at thread exit. - let res = unsafe { AvRevertMmThreadCharacteristics(av_handle) }; - if let Err(e) = res { - dbglog!("[pf-vd] swap-chain: failed to revert prioritized thread: {e:?}"); + // Revert the thread to normal once it's done (only if MMCSS was actually engaged). + if let Some(h) = av_handle { + // SAFETY: `h` is the live characteristics handle returned by + // AvSetMmThreadCharacteristicsW above, reverted exactly once here at thread exit. + let res = unsafe { AvRevertMmThreadCharacteristics(h) }; + if let Err(e) = res { + dbglog!("[pf-vd] swap-chain: failed to revert prioritized thread: {e:?}"); + } } });