Fix force shutdown deadlock race (#41269)
During force shutdown, the shutdown is called before the WslCoreVm destructor clears the m_onExit callback. The shutdown operation will trigger the HCS OnExit callback. If that callback is triggered before the m_onExit callback is cleared, the m_onExit callback still gets called. Then it waits for the m_instanceLock which is already locked by the shutdown call. Meanwhile the shutdown call will wait for the HCS OnExit callback to return in _VmTerminate via the HcsCloseComputeSystem call. Which creates a deadlock and blocks the wsl service. This PR adds an atomic flag to disable the caller provided callback.
Feng Wang committed
Aug 10, 2026 at 10:04 UTC
f2f0667e4b75dd6c33f89773963a3806587a3685
2 files changed
+15
src/windows/service/exe/LxssUserSession.cpp
+10
@@ -2152,12 +2152,16 @@ HRESULT LxssUserSessionImpl::Shutdown(_In_ bool PreventNewInstances, ShutdownBeh
2152
{
2153
try
2154
{
2155
+ auto resetVmTerminationCallback = wil::scope_exit([&]() { m_suppressVmTerminationCallback.store(false); });
2156
+
2157
auto forceTerminate = [this]() {
2158
auto vmId = m_vmId.load();
2159
if (!IsEqualGUID(vmId, GUID_NULL))
2160
{
2161
auto vmIdStr = wsl::shared::string::GuidToString<wchar_t>(vmId, wsl::shared::string::GuidToStringFlags::Uppercase);
2162
2163
+ m_suppressVmTerminationCallback.store(true);
2164
+
2165
auto result = wil::ResultFromException([&]() {
2166
auto computeSystem = wsl::windows::common::hcs::OpenComputeSystem(vmIdStr.c_str(), GENERIC_ALL);
2167
wsl::windows::common::hcs::TerminateComputeSystem(computeSystem.get());
@@ -2209,6 +2213,7 @@ HRESULT LxssUserSessionImpl::Shutdown(_In_ bool PreventNewInstances, ShutdownBeh
2213
2214
// Terminate the utility VM.
2215
_VmTerminate();
2216
+ resetVmTerminationCallback.reset();
2217
2218
// Reset the proxy state.
2219
// We don't clear it in _VMTerminate because we want to cache results if possible.
@@ -4236,6 +4241,11 @@ try
4241
{
4242
UNREFERENCED_PARAMETER(VmId);
4243
4244
+ if (UserSession->m_suppressVmTerminationCallback.load())
4245
+ {
4246
+ return;
4247
+ }
4248
+
4249
UserSession->TerminateByClientId(LXSS_CLIENT_ID_WILDCARD);
4250
return;
4251
}
src/windows/service/exe/LxssUserSession.h
+5
@@ -816,6 +816,11 @@ private:
816
817
std::atomic<GUID> m_vmId{GUID_NULL};
818
819
+ /// <summary>
820
+ /// True when the VM termination callback should not perform session cleanup.
821
+ /// </summary>
822
+ std::atomic<bool> m_suppressVmTerminationCallback{false};
823
+
824
/// <summary>
825
/// Contains the user sid for the session.
826
/// </summary>