@samitouri / QOSAMI-WSL / commits / 509ce72f

Fix use-after-free in virtiofs request worker thread (#40792)

* Save state * Add test coverage * Format * Simplify socket creation * Apply PR feedback * Cleanup * Reduce to 15 iterations

Blue committed Jun 16, 2026 at 11:03 UTC 509ce72fc4aab4bbb66dc2ce27f6f5a0730fca8e
13 files changed +262 -150
src/windows/common/HandleIO.cpp
+56 -21
@@ -4,6 +4,7 @@
4 #include "HandleIO.h"
5 #pragma hdrstop
6
7 +using wsl::windows::common::io::AcceptHandle;
8 using wsl::windows::common::io::BufferWrapper;
9 using wsl::windows::common::io::DockerIORelayHandle;
10 using wsl::windows::common::io::EventHandle;
@@ -16,7 +17,6 @@ using wsl::windows::common::io::OverlappedIOHandle;
17 using wsl::windows::common::io::ReadHandle;
18 using wsl::windows::common::io::ReadNamedPipe;
19 using wsl::windows::common::io::ReadSocketMessageHandle;
19 -using wsl::windows::common::io::SingleAcceptHandle;
20 using wsl::windows::common::io::WriteHandle;
21 using wsl::windows::common::io::WriteNamedPipe;
22
@@ -360,41 +360,77 @@ void ReadNamedPipe::Collect()
360 ReadHandle::Collect();
361 }
362
363 -// SingleAcceptHandle
363 +// AcceptHandle
364
365 -SingleAcceptHandle::SingleAcceptHandle(HandleWrapper&& ListenSocket, HandleWrapper&& AcceptedSocket, std::function<void()>&& OnAccepted) :
366 - ListenSocket(std::move(ListenSocket)), AcceptedSocket(std::move(AcceptedSocket)), OnAccepted(std::move(OnAccepted))
365 +AcceptHandle::AcceptHandle(HandleWrapper&& ListenSocket, bool AcceptOnce, std::function<void(wil::unique_socket&&)>&& OnAccepted) :
366 + ListenSocket(std::move(ListenSocket)), AcceptOnce(AcceptOnce), OnAccepted(std::move(OnAccepted))
367 {
368 Overlapped.hEvent = Event.get();
369 +
370 + // Query the listen socket so accepted sockets can be created with a matching address family, type, and protocol.
371 + WSAPROTOCOL_INFOW protocolInfo{};
372 + int length = sizeof(protocolInfo);
373 + THROW_LAST_ERROR_IF(
374 + getsockopt(reinterpret_cast<SOCKET>(this->ListenSocket.Get()), SOL_SOCKET, SO_PROTOCOL_INFOW, reinterpret_cast<char*>(&protocolInfo), &length) ==
375 + SOCKET_ERROR);
376 +
377 + AddressFamily = protocolInfo.iAddressFamily;
378 + SocketType = protocolInfo.iSocketType;
379 + Protocol = protocolInfo.iProtocol;
380 }
381
371 -SingleAcceptHandle::~SingleAcceptHandle()
382 +AcceptHandle::~AcceptHandle()
383 {
384 if (State == IOHandleStatus::Pending)
385 {
375 - LOG_IF_WIN32_BOOL_FALSE(CancelIoEx(ListenSocket.Get(), &Overlapped));
386 + CancelPendingIo(reinterpret_cast<SOCKET>(ListenSocket.Get()), Overlapped);
387 + }
388 +}
389
377 - DWORD bytesProcessed{};
378 - DWORD flagsReturned{};
379 - if (!WSAGetOverlappedResult((SOCKET)ListenSocket.Get(), &Overlapped, &bytesProcessed, TRUE, &flagsReturned))
380 - {
381 - auto error = GetLastError();
382 - LOG_LAST_ERROR_IF(error != ERROR_CONNECTION_ABORTED && error != ERROR_OPERATION_ABORTED);
383 - }
390 +void AcceptHandle::CreateAcceptSocket()
391 +{
392 + AcceptedSocket.reset(WSASocketW(AddressFamily, SocketType, Protocol, nullptr, 0, WSA_FLAG_OVERLAPPED));
393 + THROW_LAST_ERROR_IF(!AcceptedSocket);
394 +
395 + if (AddressFamily == AF_HYPERV)
396 + {
397 + ULONG enable = 1;
398 + THROW_LAST_ERROR_IF(
399 + setsockopt(AcceptedSocket.get(), HV_PROTOCOL_RAW, HVSOCKET_CONNECTED_SUSPEND, reinterpret_cast<char*>(&enable), sizeof(enable)) ==
400 + SOCKET_ERROR);
401 + }
402 +}
403 +
404 +void AcceptHandle::OnComplete()
405 +{
406 + wsl::windows::common::socket::SetAcceptContext(AcceptedSocket.get(), reinterpret_cast<SOCKET>(ListenSocket.Get()));
407 +
408 + OnAccepted(std::move(AcceptedSocket));
409 +
410 + if (AcceptOnce)
411 + {
412 + State = IOHandleStatus::Completed;
413 + }
414 + else
415 + {
416 + State = IOHandleStatus::Standby;
417 }
418 }
419
387 -void SingleAcceptHandle::Schedule()
420 +void AcceptHandle::Schedule()
421 {
422 WI_ASSERT(State == IOHandleStatus::Standby);
423
424 + CreateAcceptSocket();
425 +
426 + Event.ResetEvent();
427 +
428 // Schedule the accept.
429 DWORD bytesReturned{};
393 - if (AcceptEx((SOCKET)ListenSocket.Get(), (SOCKET)AcceptedSocket.Get(), &AcceptBuffer, 0, sizeof(SOCKADDR_STORAGE), sizeof(SOCKADDR_STORAGE), &bytesReturned, &Overlapped))
430 + if (AcceptEx((SOCKET)ListenSocket.Get(), AcceptedSocket.get(), &AcceptBuffer, 0, sizeof(SOCKADDR_STORAGE), sizeof(SOCKADDR_STORAGE), &bytesReturned, &Overlapped))
431 {
432 // Accept completed immediately.
396 - State = IOHandleStatus::Completed;
397 - OnAccepted();
433 + OnComplete();
434 }
435 else
436 {
@@ -405,7 +441,7 @@ void SingleAcceptHandle::Schedule()
441 }
442 }
443
408 -void SingleAcceptHandle::Collect()
444 +void AcceptHandle::Collect()
445 {
446 WI_ASSERT(State == IOHandleStatus::Pending);
447
@@ -414,11 +450,10 @@ void SingleAcceptHandle::Collect()
450
451 THROW_IF_WIN32_BOOL_FALSE(WSAGetOverlappedResult((SOCKET)ListenSocket.Get(), &Overlapped, &bytesReceived, false, &flagsReturned));
452
417 - State = IOHandleStatus::Completed;
418 - OnAccepted();
453 + OnComplete();
454 }
455
421 -HANDLE SingleAcceptHandle::GetHandle() const
456 +HANDLE AcceptHandle::GetHandle() const
457 {
458 return Event.get();
459 }
src/windows/common/HandleIO.h
+14 -7
@@ -139,25 +139,32 @@ private:
139 bool m_connected = false;
140 };
141
142 -class SingleAcceptHandle : public OverlappedIOHandle
142 +class AcceptHandle : public OverlappedIOHandle
143 {
144 public:
145 - NON_COPYABLE(SingleAcceptHandle)
146 - NON_MOVABLE(SingleAcceptHandle)
145 + NON_COPYABLE(AcceptHandle)
146 + NON_MOVABLE(AcceptHandle)
147
148 - SingleAcceptHandle(HandleWrapper&& ListenSocket, HandleWrapper&& AcceptedSocket, std::function<void()>&& OnAccepted);
149 - ~SingleAcceptHandle();
148 + AcceptHandle(HandleWrapper&& ListenSocket, bool AcceptOnce, std::function<void(wil::unique_socket&&)>&& OnAccepted);
149 + ~AcceptHandle();
150
151 void Schedule() override;
152 void Collect() override;
153 HANDLE GetHandle() const override;
154
155 private:
156 + void CreateAcceptSocket();
157 + void OnComplete();
158 +
159 HandleWrapper ListenSocket;
157 - HandleWrapper AcceptedSocket;
160 + wil::unique_socket AcceptedSocket;
161 + int AddressFamily{};
162 + int SocketType{};
163 + int Protocol{};
164 + bool AcceptOnce{};
165 wil::unique_event Event{wil::EventOptions::ManualReset};
166 OVERLAPPED Overlapped{};
160 - std::function<void()> OnAccepted;
167 + std::function<void(wil::unique_socket&&)> OnAccepted;
168 char AcceptBuffer[2 * sizeof(SOCKADDR_STORAGE)];
169 };
170
src/windows/common/hvsocket.cpp
-12
@@ -37,18 +37,6 @@ void InitializeWildcardSocketAddress(_Out_ PSOCKADDR_HV Address)
37 }
38 } // namespace
39
40 -std::optional<wil::unique_socket> wsl::windows::common::hvsocket::CancellableAccept(
41 - _In_ SOCKET ListenSocket, _In_ DWORD Timeout, _In_opt_ HANDLE ExitHandle, _In_ const std::source_location& Location)
42 -{
43 - wil::unique_socket Socket = Create();
44 - if (!socket::CancellableAccept(ListenSocket, Socket.get(), Timeout, ExitHandle, Location))
45 - {
46 - return {};
47 - }
48 -
49 - return Socket;
50 -}
51 -
40 wil::unique_socket wsl::windows::common::hvsocket::Connect(
41 _In_ const GUID& VmId, _In_ unsigned long Port, _In_opt_ HANDLE ExitHandle, _In_opt_ ULONG Timeout, _In_ const std::source_location& Location)
42 {
src/windows/common/hvsocket.hpp
-6
@@ -19,12 +19,6 @@ Abstract:
19
20 namespace wsl::windows::common::hvsocket {
21
22 -std::optional<wil::unique_socket> CancellableAccept(
23 - _In_ SOCKET ListenSocket,
24 - _In_ DWORD Timeout,
25 - _In_opt_ HANDLE ExitHandle = nullptr,
26 - const std::source_location& Location = std::source_location::current());
27 -
22 wil::unique_socket Connect(
23 _In_ const GUID& VmId,
24 _In_ unsigned long Port,
src/windows/common/socket.cpp
+21 -10
@@ -26,30 +26,41 @@ void wsl::windows::common::socket::SetAcceptContext(_In_ SOCKET AcceptedSocket,
26 std::format("{}", Location).c_str());
27 }
28
29 -bool wsl::windows::common::socket::CancellableAccept(
30 - _In_ SOCKET ListenSocket, _In_ SOCKET Socket, _In_ DWORD Timeout, _In_opt_ HANDLE ExitHandle, _In_ const std::source_location& Location)
29 +std::optional<wil::unique_socket> wsl::windows::common::socket::CancellableAccept(
30 + _In_ SOCKET ListenSocket, _In_ DWORD Timeout, _In_opt_ HANDLE ExitHandle, _In_ const std::source_location& Location)
31 {
32 io::MultiHandleWait io;
33
34 - bool accepted = false;
34 + std::optional<wil::unique_socket> accepted;
35
36 - io.AddHandle(std::make_unique<io::SingleAcceptHandle>(ListenSocket, Socket, [&]() { accepted = true; }), io::MultiHandleWait::CancelOnCompleted);
36 + io.AddHandle(
37 + std::make_unique<io::AcceptHandle>(
38 + ListenSocket, true, [&accepted](wil::unique_socket&& socket) { accepted = std::move(socket); }),
39 + io::MultiHandleWait::CancelOnCompleted);
40
41 if (ExitHandle != nullptr)
42 {
43 io.AddHandle(std::make_unique<io::EventHandle>(ExitHandle), io::MultiHandleWait::CancelOnCompleted);
44 }
45
43 - io.Run(std::chrono::milliseconds(Timeout));
44 -
45 - if (!accepted)
46 + std::optional<std::chrono::milliseconds> timeout;
47 + if (Timeout != INFINITE)
48 {
47 - return false; // Accept was cancelled by the exit event.
49 + timeout = std::chrono::milliseconds(Timeout);
50 }
51
50 - SetAcceptContext(Socket, ListenSocket, Location);
52 + try
53 + {
54 +
55 + io.Run(timeout);
56 + }
57 + catch (...)
58 + {
59 + auto hr = wil::ResultFromCaughtException();
60 + THROW_HR_MSG(hr, "Failed to accept socket. From: %hs", std::format("{}", Location).c_str());
61 + }
62
52 - return true;
63 + return accepted;
64 }
65
66 std::pair<DWORD, DWORD> wsl::windows::common::socket::GetResult(
src/windows/common/socket.hpp
+1 -2
@@ -21,9 +21,8 @@ namespace wsl::windows::common::socket {
21 // Sets SO_UPDATE_ACCEPT_CONTEXT on a socket accepted via AcceptEx to mark it as connected.
22 void SetAcceptContext(_In_ SOCKET AcceptedSocket, _In_ SOCKET ListenSocket, _In_ const std::source_location& Location = std::source_location::current());
23
24 -bool CancellableAccept(
24 +std::optional<wil::unique_socket> CancellableAccept(
25 _In_ SOCKET ListenSocket,
26 - _In_ SOCKET Socket,
26 _In_ DWORD Timeout,
27 _In_opt_ HANDLE ExitHandle,
28 _In_ const std::source_location& Location = std::source_location::current());
src/windows/service/exe/HcsVirtualMachine.cpp
+2 -2
@@ -381,7 +381,7 @@ try
381 {
382 RETURN_HR_IF_NULL(E_POINTER, Socket);
383
384 - auto socket = wsl::windows::common::hvsocket::CancellableAccept(m_listenSocket.get(), m_bootTimeoutMs, m_vmExitEvent.get());
384 + auto socket = socket::CancellableAccept(m_listenSocket.get(), m_bootTimeoutMs, m_vmExitEvent.get());
385 THROW_HR_IF(E_ABORT, !socket.has_value());
386
387 *Socket = reinterpret_cast<HANDLE>(socket->release());
@@ -924,4 +924,4 @@ try
924 }
925 CATCH_RETURN()
926
927 -} // namespace wsl::windows::service::wslc
\ No newline at end of file
927 +} // namespace wsl::windows::service::wslc
src/windows/service/exe/WslCoreVm.cpp
+99 -75
@@ -856,7 +856,7 @@ WslCoreVm::~WslCoreVm() noexcept
856
857 wil::unique_socket WslCoreVm::AcceptConnection(_In_ DWORD ReceiveTimeout, _In_ const std::source_location& Location) const
858 {
859 - auto socket = hvsocket::CancellableAccept(m_listenSocket.get(), m_vmConfig.KernelBootTimeout, m_terminatingEvent.get(), Location);
859 + auto socket = socket::CancellableAccept(m_listenSocket.get(), m_vmConfig.KernelBootTimeout, m_terminatingEvent.get(), Location);
860 THROW_HR_IF(E_ABORT, !socket.has_value());
861
862 if (ReceiveTimeout != 0)
@@ -1087,7 +1087,7 @@ void WslCoreVm::CollectCrashDumps(wil::unique_socket&& listenSocket) const
1087 {
1088 try
1089 {
1090 - auto socket = hvsocket::CancellableAccept(listenSocket.get(), INFINITE, m_terminatingEvent.get());
1090 + auto socket = socket::CancellableAccept(listenSocket.get(), INFINITE, m_terminatingEvent.get());
1091 if (!socket.has_value())
1092 {
1093 break; // VM is exiting.
@@ -2617,92 +2617,116 @@ try
2617 {
2618 wsl::windows::common::wslutil::SetThreadDescription(L"VirtioFs - Worker");
2619
2620 - for (;;)
2621 - {
2622 - // Create a worker thread to handle each request.
2620 + io::MultiHandleWait io;
2621
2624 - auto socket = hvsocket::CancellableAccept(listenSocket.get(), INFINITE, m_terminatingEvent.get());
2625 - if (!socket.has_value())
2626 - {
2627 - break;
2628 - }
2622 + io.AddHandle(std::make_unique<io::AcceptHandle>(listenSocket.get(), false, [this, &io](wil::unique_socket&& socket) {
2623 + auto channel = std::make_shared<wsl::shared::SocketChannel>(std::move(socket), "VirtioFs");
2624 + auto buffer = std::make_shared<std::vector<gsl::byte>>();
2625 + auto pendingBytes = std::make_shared<std::vector<gsl::byte>>();
2626
2630 - wsl::shared::SocketChannel channel{std::move(socket.value()), "VirtioFs", {m_terminatingEvent.get()}};
2631 - std::thread([this, channel = std::move(channel)]() mutable {
2632 - try
2633 - {
2634 - wsl::windows::common::wslutil::SetThreadDescription(L"VirtioFs - Request");
2627 + io.AddHandle(
2628 + std::make_unique<io::ReadSocketMessageHandle>(
2629 + io::HandleWrapper(channel->Socket()),
2630 + *buffer,
2631 + *pendingBytes,
2632 + [this, &io, channel, buffer, pendingBytes](const gsl::span<gsl::byte>& message) {
2633 + if (message.empty())
2634 + {
2635 + return; // Channel closed, exit.
2636 + }
2637
2636 - auto transaction = channel.ReceiveTransaction();
2637 - auto [message, span] = transaction.ReceiveOrClosed<MESSAGE_HEADER>();
2638 - if (message == nullptr)
2639 - {
2640 - return;
2641 - }
2638 + THROW_HR_IF_MSG(
2639 + E_UNEXPECTED, !pendingBytes->empty(), "Received message with additional bytes: %zu", pendingBytes->size());
2640
2643 - auto respondWithTag = [&](const std::wstring& tag, const std::wstring& source, HRESULT result) {
2644 - // Respond to the guest with the tag that should be used to mount the device.
2641 + try
2642 + {
2643 + auto response = ProcessVirtioFsRequest(message);
2644
2646 - wsl::shared::MessageWriter<LX_INIT_ADD_VIRTIOFS_SHARE_RESPONSE_MESSAGE> response(LxInitMessageAddVirtioFsDeviceResponse);
2647 - response->Result = SUCCEEDED(result) ? 0 : EINVAL; // TODO: Improved HRESULT -> errno mapping.
2648 - response.WriteString(response->TagOffset, tag);
2649 - response.WriteString(response->SourceOffset, source);
2645 + // Move the socket out of the channel into the WriteHandle so it is closed once the reply is sent.
2646 + io.AddHandle(std::make_unique<io::WriteHandle>(channel->Release(), response), io::MultiHandleWait::IgnoreErrors);
2647 + }
2648 + CATCH_LOG();
2649 + }),
2650 + io::MultiHandleWait::IgnoreErrors);
2651 + }));
2652
2651 - transaction.Send<LX_INIT_ADD_VIRTIOFS_SHARE_RESPONSE_MESSAGE>(response.Span());
2652 - };
2653 + io.AddHandle(std::make_unique<io::EventHandle>(m_terminatingEvent.get()), io::MultiHandleWait::CancelOnCompleted);
2654
2654 - if (message->MessageType == LxInitMessageAddVirtioFsDevice)
2655 - {
2656 - std::wstring tag;
2657 - std::wstring source;
2658 - const auto result = wil::ResultFromException([this, span, &tag, &source]() {
2659 - const auto* addShare = gslhelpers::try_get_struct<LX_INIT_ADD_VIRTIOFS_SHARE_MESSAGE>(span);
2660 - THROW_HR_IF(E_UNEXPECTED, !addShare);
2661 -
2662 - const auto path = wsl::shared::string::FromSpan(span, addShare->PathOffset);
2663 - const auto pathWide = wsl::shared::string::MultiByteToWide(path);
2664 - const auto options = wsl::shared::string::FromSpan(span, addShare->OptionsOffset);
2665 - const auto optionsWide = wsl::shared::string::MultiByteToWide(options);
2666 -
2667 - // Acquire the lock and attempt to add the device.
2668 - auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();
2669 - std::tie(tag, source) = AddVirtioFsShare(addShare->Admin, pathWide.c_str(), optionsWide.c_str());
2670 - });
2671 -
2672 - respondWithTag(tag, source, result);
2673 - }
2674 - else if (message->MessageType == LxInitMessageRemountVirtioFsDevice)
2675 - {
2676 - std::wstring newTag;
2677 - std::wstring source;
2678 - const auto result = wil::ResultFromException([this, span, &newTag, &source]() {
2679 - const auto* remountShare = gslhelpers::try_get_struct<LX_INIT_REMOUNT_VIRTIOFS_SHARE_MESSAGE>(span);
2680 - THROW_HR_IF(E_UNEXPECTED, !remountShare);
2655 + io.Run({});
2656 +}
2657 +CATCH_LOG()
2658
2682 - const std::string tag = wsl::shared::string::FromSpan(span, remountShare->TagOffset);
2683 - const auto tagWide = wsl::shared::string::MultiByteToWide(tag);
2684 - auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();
2685 - const auto foundShare = FindVirtioFsShare(tagWide.c_str(), !remountShare->Admin);
2686 - THROW_HR_IF_MSG(E_UNEXPECTED, !foundShare.has_value(), "Unknown tag %ls", tagWide.c_str());
2659 +std::vector<char> WslCoreVm::ProcessVirtioFsRequest(_In_ gsl::span<gsl::byte> Request)
2660 +{
2661 + const auto* header = gslhelpers::try_get_struct<MESSAGE_HEADER>(Request);
2662 + THROW_HR_IF(E_UNEXPECTED, !header);
2663
2688 - std::tie(newTag, source) =
2689 - AddVirtioFsShare(remountShare->Admin, foundShare->Path.c_str(), foundShare->OptionsString().c_str());
2664 + WSL_LOG("VirtiofsMessageRequest", TraceLoggingValue(header->PrettyPrint().c_str(), "Content"));
2665
2691 - WI_ASSERT(source == foundShare->Path);
2692 - });
2666 + auto buildResponse = [header](const std::wstring& tag, const std::wstring& source, HRESULT result) {
2667 + // Respond to the guest with the tag that should be used to mount the device.
2668 + wsl::shared::MessageWriter<LX_INIT_ADD_VIRTIOFS_SHARE_RESPONSE_MESSAGE> response(LxInitMessageAddVirtioFsDeviceResponse);
2669 + response->Result = SUCCEEDED(result) ? 0 : EINVAL; // TODO: Improved HRESULT -> errno mapping.
2670 + response.WriteString(response->TagOffset, tag);
2671 + response.WriteString(response->SourceOffset, source);
2672
2694 - respondWithTag(newTag, source, result);
2695 - }
2696 - else
2697 - {
2698 - THROW_HR_MSG(E_UNEXPECTED, "Unexpected MessageType %d", message->MessageType);
2699 - }
2700 - }
2701 - CATCH_LOG()
2702 - }).detach();
2673 + // Echo the request's transaction id and mark the message as the first (and only) reply.
2674 + response->Header.TransactionId = header->TransactionId;
2675 + response->Header.TransactionStep = static_cast<unsigned int>(TRANSACTION_STEP::FIRST_REPLY);
2676 +
2677 + WSL_LOG("VirtiofsMessageResponse", TraceLoggingValue(response->PrettyPrint().c_str(), "Content"));
2678 +
2679 + const auto span = response.Span();
2680 + return std::vector<char>(reinterpret_cast<const char*>(span.data()), reinterpret_cast<const char*>(span.data()) + span.size());
2681 + };
2682 +
2683 + if (header->MessageType == LxInitMessageAddVirtioFsDevice)
2684 + {
2685 + std::wstring tag;
2686 + std::wstring source;
2687 + const auto result = wil::ResultFromException([&]() {
2688 + const auto* addShare = gslhelpers::try_get_struct<LX_INIT_ADD_VIRTIOFS_SHARE_MESSAGE>(Request);
2689 + THROW_HR_IF(E_UNEXPECTED, !addShare);
2690 +
2691 + const auto path = wsl::shared::string::FromSpan(Request, addShare->PathOffset);
2692 + const auto pathWide = wsl::shared::string::MultiByteToWide(path);
2693 + const auto options = wsl::shared::string::FromSpan(Request, addShare->OptionsOffset);
2694 + const auto optionsWide = wsl::shared::string::MultiByteToWide(options);
2695 +
2696 + // Acquire the lock and attempt to add the device.
2697 + auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();
2698 + std::tie(tag, source) = AddVirtioFsShare(addShare->Admin, pathWide.c_str(), optionsWide.c_str());
2699 + });
2700 +
2701 + return buildResponse(tag, source, result);
2702 + }
2703 + else if (header->MessageType == LxInitMessageRemountVirtioFsDevice)
2704 + {
2705 + std::wstring newTag;
2706 + std::wstring source;
2707 + const auto result = wil::ResultFromException([&]() {
2708 + const auto* remountShare = gslhelpers::try_get_struct<LX_INIT_REMOUNT_VIRTIOFS_SHARE_MESSAGE>(Request);
2709 + THROW_HR_IF(E_UNEXPECTED, !remountShare);
2710 +
2711 + const std::string tag = wsl::shared::string::FromSpan(Request, remountShare->TagOffset);
2712 + const auto tagWide = wsl::shared::string::MultiByteToWide(tag);
2713 + auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();
2714 + const auto foundShare = FindVirtioFsShare(tagWide.c_str(), !remountShare->Admin);
2715 + THROW_HR_IF_MSG(E_UNEXPECTED, !foundShare.has_value(), "Unknown tag %ls", tagWide.c_str());
2716 +
2717 + std::tie(newTag, source) =
2718 + AddVirtioFsShare(remountShare->Admin, foundShare->Path.c_str(), foundShare->OptionsString().c_str());
2719 +
2720 + WI_ASSERT(source == foundShare->Path);
2721 + });
2722 +
2723 + return buildResponse(newTag, source, result);
2724 + }
2725 + else
2726 + {
2727 + THROW_HR_MSG(E_UNEXPECTED, "Unexpected MessageType %d", header->MessageType);
2728 }
2729 }
2705 -CATCH_LOG()
2730
2731 std::string WslCoreVm::s_GetMountTargetName(_In_ PCWSTR Disk, _In_opt_ PCWSTR Name, _In_ int PartitionIndex)
2732 {
src/windows/service/exe/WslCoreVm.h
+2
@@ -251,6 +251,8 @@ private:
251
252 void VirtioFsWorker(_In_ const wil::unique_socket& socket);
253
254 + std::vector<char> ProcessVirtioFsRequest(_In_ gsl::span<gsl::byte> Request);
255 +
256 static std::string s_GetMountTargetName(_In_ PCWSTR Disk, _In_opt_ PCWSTR Name, _In_ int PartitionIndex);
257
258 static LX_INIT_DRVFS_MOUNT s_InitializeDrvFs(_Inout_ WslCoreVm* VmContext, _In_ HANDLE UserToken);
src/windows/wslcsession/WSLCVirtualMachine.cpp
+1 -1
@@ -1259,7 +1259,7 @@ void WSLCVirtualMachine::CollectCrashDumps(wil::unique_socket&& listenSocket)
1259 {
1260 try
1261 {
1262 - auto socket = hvsocket::CancellableAccept(listenSocket.get(), INFINITE, m_vmTerminatingEvent.get());
1262 + auto socket = socket::CancellableAccept(listenSocket.get(), INFINITE, m_vmTerminatingEvent.get());
1263 if (!socket)
1264 {
1265 // VM is exiting.
src/windows/wslrelay/localhost.cpp
+4 -8
@@ -291,15 +291,11 @@ try
291 {
292 // Begin accepting connections until the relay is stopped.
293
294 - const int AddressFamily = WindowsAddressFamily(Arguments->Family);
295 -
294 for (;;)
295 {
298 - wil::unique_socket InetSocket(WSASocket(AddressFamily, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED));
299 - THROW_LAST_ERROR_IF(!InetSocket);
300 -
301 - if (!wsl::windows::common::socket::CancellableAccept(
302 - Arguments->ListenSocket.get(), InetSocket.get(), INFINITE, Arguments->ExitEvent.get()))
296 + auto InetSocket =
297 + wsl::windows::common::socket::CancellableAccept(Arguments->ListenSocket.get(), INFINITE, Arguments->ExitEvent.get());
298 + if (!InetSocket)
299 {
300 break; // Exit event was signaled, exit.
301 }
@@ -308,7 +304,7 @@ try
304
305 WSL_LOG("PortRelayUsage", TraceLoggingValue(Arguments->Family, "family"), TraceLoggingValue(Arguments->Port, "port"), TraceLoggingLevel(WINEVENT_LEVEL_INFO));
306
311 - auto RelayThread = std::thread([Arguments, InetSocket = std::move(InetSocket)]() {
307 + auto RelayThread = std::thread([Arguments, InetSocket = std::move(*InetSocket)]() {
308 try
309 {
310 wsl::windows::common::wslutil::SetThreadDescription(L"Port relay");
src/windows/wslrelay/main.cpp
+3 -5
@@ -123,17 +123,15 @@ try
123
124 THROW_LAST_ERROR_IF(listen(listenSocket.get(), 1) == SOCKET_ERROR);
125
126 - const wil::unique_socket socket(WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED));
127 - THROW_LAST_ERROR_IF(!socket);
128 -
129 - if (!wsl::windows::common::socket::CancellableAccept(listenSocket.get(), socket.get(), INFINITE, exitEvent.get()))
126 + auto socket = wsl::windows::common::socket::CancellableAccept(listenSocket.get(), INFINITE, exitEvent.get());
127 + if (!socket)
128 {
129 return 1;
130 }
131
132 // Begin the relay.
133 wsl::windows::common::relay::BidirectionalRelay(
136 - reinterpret_cast<HANDLE>(socket.get()), pipe.get(), 0x1000, wsl::windows::common::relay::RelayFlags::LeftIsSocket);
134 + reinterpret_cast<HANDLE>(socket->get()), pipe.get(), 0x1000, wsl::windows::common::relay::RelayFlags::LeftIsSocket);
135
136 break;
137 }
test/windows/DrvFsTests.cpp
+59 -1
@@ -409,6 +409,59 @@ public:
409 VERIFY_IS_TRUE(out.find(L"test-file.txt") != std::wstring::npos);
410 }
411
412 + void DrvfsMountManyVirtioFsShares(DrvFsMode Mode)
413 + {
414 + if (Mode != DrvFsMode::VirtioFs)
415 + {
416 + LogSkipped("This test is only applicable to VirtioFs");
417 + return;
418 + }
419 +
420 + WINDOWS_11_TEST_ONLY();
421 + SKIP_TEST_ARM64();
422 +
423 + constexpr auto c_iterations = 15;
424 + auto testDir = std::filesystem::current_path() / "virtiofs-loop-test";
425 +
426 + auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
427 + LxsstuLaunchWsl(L"umount /tmp/virtiofs-loop-test-*");
428 +
429 + std::error_code ec;
430 + std::filesystem::remove_all(testDir, ec);
431 + });
432 +
433 + for (int i = 0; i < c_iterations; ++i)
434 + {
435 + const auto sourceDir = testDir / std::to_string(i);
436 + std::filesystem::create_directories(sourceDir);
437 +
438 + const auto expected = std::format("virtiofs share {}", i);
439 + {
440 + std::ofstream markerFile(std::filesystem::path(sourceDir) / L"marker");
441 + markerFile << expected;
442 + }
443 +
444 + const auto mountPoint = std::format(L"/tmp/virtiofs-loop-test-{}", i);
445 +
446 + // Mount the share.
447 + VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L"mkdir -p '{}'", mountPoint)), 0);
448 + VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L"mount -t drvfs '{}' '{}'", sourceDir.string(), mountPoint)), 0);
449 +
450 + // Validate that it can be accessed.
451 + {
452 + auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L"cat '{}/marker'", mountPoint));
453 + VERIFY_ARE_EQUAL(out, wsl::shared::string::MultiByteToWide(expected));
454 + }
455 +
456 + // Validate the mount options.
457 + {
458 + auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L"findmnt -ln '{}'", mountPoint));
459 +
460 + VerifyPatternMatch(wsl::shared::string::WideToMultiByte(out.c_str()), std::format("{} * virtiofs rw,relatime\n", mountPoint));
461 + }
462 + }
463 + }
464 +
465 // DrvFsTests Private Methods
466 private:
467 static VOID CreateDrvFsTestFiles(bool Metadata)
@@ -1303,6 +1356,11 @@ class WSL1 : public DrvFsTests
1356 { \
1357 DrvFsTests::DrvFsMountUnicodePath(DrvFsMode::##_mode##); \
1358 } \
1359 +\
1360 + WSL2_TEST_METHOD(DrvfsMountManyVirtioFsShares) \
1361 + { \
1362 + DrvFsTests::DrvfsMountManyVirtioFsShares(DrvFsMode::##_mode##); \
1363 + } \
1364 }
1365
1366 WSL2_DRVFS_TEST_CLASS(Plan9);
@@ -1313,4 +1371,4 @@ WSL2_DRVFS_TEST_CLASS(VirtioFs);
1371 // TODO: Enable again once the issue is resolved
1372 // WSL2_DRVFS_TEST_CLASS(Virtio9p);
1373
1316 -} // namespace DrvFsTests
\ No newline at end of file
1374 +} // namespace DrvFsTests