Fix: Reattach stale cached device after backing volume is reattached (#41379)
The current disk attach caching logic only checks the vhd path. When the backing volume is detached than reattached, the check will pass but the stale reference won't work inside the VM. The backing volume reattaching can happen after bitlocker locking and unlocking #12599, or vhdx detaching then reattaching. This PR checks if the cached vhd is stale by holding a handle to the vhd file. And checks if it's still valid before reuse. If not, the vhd will be reattached.
Feng Wang committed
Aug 21, 2026 at 10:24 UTC
93f74a35817beb9f6d771f0d8be4d90f7f74072e
3 files changed
+38
-4
src/windows/service/exe/WslCoreVm.cpp
+36
-3
@@ -73,6 +73,21 @@ RequiredExtraMmioSpaceForPmemFileInMb(_In_ PCWSTR FilePath)
73
// Convert from bytes to megabytes. Ensure that we don't truncate a 512kb file to 0mb.
74
return std::max(fileSizeBytes.QuadPart / static_cast<INT64>(_1MB), 1i64);
75
}
76
+
77
+wil::unique_hfile OpenVhdBackingFile(_In_ PCWSTR Path)
78
+{
79
+ wil::unique_hfile file{CreateFileW(
80
+ Path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)};
81
+ THROW_LAST_ERROR_IF(!file);
82
+
83
+ return file;
84
+}
85
+
86
+bool IsBackingVolumeMounted(_In_ HANDLE File)
87
+{
88
+ DWORD bytesReturned{};
89
+ return DeviceIoControl(File, FSCTL_IS_VOLUME_MOUNTED, nullptr, 0, nullptr, 0, &bytesReturned, nullptr);
90
+}
91
} // namespace
92
93
WslCoreVm::WslCoreVm(_In_ wsl::core::Config&& VmConfig, _In_ InitializeDrvFsCallback InitializeDrvFs) :
@@ -993,6 +1008,7 @@ ULONG WslCoreVm::AttachDiskLockHeld(
1008
1009
// Set a scope exit variable to perform cleanup if attaching the disk fails.
1010
DiskStateFlags diskFlags{};
1011
+ wil::unique_hfile backingFile;
1012
auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {
1013
FreeLun(Lun.value());
1014
if (WI_IsFlagSet(diskFlags, DiskStateFlags::AccessGranted))
@@ -1050,9 +1066,25 @@ ULONG WslCoreVm::AttachDiskLockHeld(
1066
// Prevent user from launching a distro vhd after manually mounting it; otherwise, return the LUN of the mounted disk.
1067
THROW_HR_IF(WSL_E_USER_VHD_ALREADY_ATTACHED, found->first.User);
1068
1053
- return found->second.Lun;
1069
+ // Check if the lun is still valid. It could be stale if the backing volume is reattached.
1070
+ if (IsBackingVolumeMounted(found->second.BackingFile.get()))
1071
+ {
1072
+ return found->second.Lun;
1073
+ }
1074
+
1075
+ const auto staleLun = found->second.Lun;
1076
+ wsl::windows::common::hcs::RemoveScsiDisk(m_system.get(), staleLun);
1077
+ if (WI_IsFlagSet(found->second.Flags, DiskStateFlags::AccessGranted))
1078
+ {
1079
+ wsl::windows::common::hcs::RevokeVmAccess(m_machineId.c_str(), found->first.Path.c_str());
1080
+ }
1081
+
1082
+ m_attachedDisks.erase(found);
1083
+ FreeLun(staleLun);
1084
}
1085
1086
+ backingFile = OpenVhdBackingFile(Disk);
1087
+
1088
auto grantDiskAccess = [&]() {
1089
auto runAsUser = wil::impersonate_token(UserToken);
1090
wsl::windows::common::hcs::GrantVmAccess(m_machineId.c_str(), Disk);
@@ -1087,7 +1119,7 @@ ULONG WslCoreVm::AttachDiskLockHeld(
1119
result, Localization::MessageFailedToAttachDisk(Disk, wsl::windows::common::wslutil::GetSystemErrorString(result)));
1120
}
1121
1090
- m_attachedDisks.emplace(AttachedDisk{Type, Disk, IsUserDisk}, DiskState{Lun.value(), {}, diskFlags});
1122
+ m_attachedDisks.emplace(AttachedDisk{Type, Disk, IsUserDisk}, DiskState{Lun.value(), {}, diskFlags, std::move(backingFile)});
1123
cleanup.release();
1124
1125
return Lun.value();
@@ -1747,6 +1779,7 @@ std::wstring WslCoreVm::GenerateConfigJson()
1779
// inherited ACLs; otherwise StartComputeSystem will surface E_ACCESSDENIED.
1780
auto attachDisk = [&](PCWSTR path, bool grantVmAccess) {
1781
auto lun = ReserveLun();
1782
+ auto backingFile = OpenVhdBackingFile(path);
1783
hcs::Attachment disk{};
1784
disk.Type = hcs::AttachmentType::VirtualDisk;
1785
disk.Path = path;
@@ -1768,7 +1801,7 @@ std::wstring WslCoreVm::GenerateConfigJson()
1801
CATCH_LOG()
1802
}
1803
1771
- m_attachedDisks.emplace(AttachedDisk{DiskType::VHD, path, false}, DiskState{lun, {}, diskFlags});
1804
+ m_attachedDisks.emplace(AttachedDisk{DiskType::VHD, path, false}, DiskState{lun, {}, diskFlags, std::move(backingFile)});
1805
return lun;
1806
};
1807
src/windows/service/exe/WslCoreVm.h
+1
@@ -163,6 +163,7 @@ private:
163
ULONG Lun;
164
std::map<ULONG, Mount> Mounts;
165
DiskStateFlags Flags;
166
+ wil::unique_hfile BackingFile;
167
};
168
169
struct VirtioFsShare
test/windows/UnitTests.cpp
+1
-1
@@ -1503,7 +1503,7 @@ class UnitTests
1503
L"-d DummyBrokenDistro",
1504
L"Failed to attach disk 'C:\\DoesNotExit\\ext4.vhdx' to WSL2: The system cannot find the path "
1505
L"specified. ",
1506
- L"Wsl/Service/CreateInstance/MountDisk/HCS/ERROR_PATH_NOT_FOUND");
1506
+ L"Wsl/Service/CreateInstance/MountDisk/ERROR_PATH_NOT_FOUND");
1507
1508
// Purposefully set an incorrect value type to validate registry error handling.
1509
wsl::windows::common::registry::WriteString(distroKey.get(), nullptr, L"Version", L"Broken");