@samitouri / QOSAMI-WSL / commits / 66be0a58

Solve various issues found by verifier (#41445)

* Save state * Save state * Save state * Cleanup diff

Blue committed Aug 26, 2026 at 18:41 UTC 66be0a58a9280f37dfe4e70317f9994ea796be57
22 files changed +173 -97
src/windows/common/HandleIO.cpp
+35
@@ -140,6 +140,26 @@ BOOL GetNextCharacter(_In_ INPUT_RECORD* InputRecord, _Out_ PWCHAR NextCharacter
140
141 // HandleWrapper
142
143 +HandleWrapper::HandleWrapper(HandleWrapper&& other) noexcept :
144 + Handle(std::exchange(other.Handle, nullptr)), OwnedHandle(std::move(other.OwnedHandle)), OnClose(std::move(other.OnClose))
145 +{
146 + other.OnClose = nullptr;
147 +}
148 +
149 +HandleWrapper& HandleWrapper::operator=(HandleWrapper&& other) noexcept
150 +{
151 + if (this != &other)
152 + {
153 + Reset();
154 + Handle = std::exchange(other.Handle, nullptr);
155 + OwnedHandle = std::move(other.OwnedHandle);
156 + OnClose = std::move(other.OnClose);
157 + other.OnClose = nullptr;
158 + }
159 +
160 + return *this;
161 +}
162 +
163 HandleWrapper::HandleWrapper(wil::unique_handle&& handle, std::function<void()>&& OnClose) :
164 Handle(handle.get()), OwnedHandle(std::move(handle)), OnClose(std::move(OnClose))
165 {
@@ -150,6 +170,16 @@ HandleWrapper::HandleWrapper(wil::unique_socket&& handle, std::function<void()>&
170 {
171 }
172
173 +HandleWrapper::HandleWrapper(wil::shared_handle handle, std::function<void()>&& OnClose) :
174 + Handle(handle.get()), OwnedHandle(std::move(handle)), OnClose(std::move(OnClose))
175 +{
176 +}
177 +
178 +HandleWrapper::HandleWrapper(wil::shared_socket handle, std::function<void()>&& OnClose) :
179 + Handle(reinterpret_cast<HANDLE>(handle.get())), OwnedHandle(std::move(handle)), OnClose(std::move(OnClose))
180 +{
181 +}
182 +
183 HandleWrapper::HandleWrapper(wil::unique_event&& handle, std::function<void()>&& OnClose) :
184 Handle(handle.get()), OwnedHandle(wil::unique_handle{handle.release()}), OnClose(std::move(OnClose))
185 {
@@ -179,6 +209,11 @@ HANDLE HandleWrapper::Get() const
209 return Handle;
210 }
211
212 +bool HandleWrapper::IsValid() const
213 +{
214 + return Handle != nullptr && Handle != INVALID_HANDLE_VALUE;
215 +}
216 +
217 void HandleWrapper::Reset()
218 {
219 if (OnClose != nullptr)
src/windows/common/HandleIO.h
+7 -2
@@ -22,11 +22,15 @@ enum class IOHandleStatus
22
23 struct HandleWrapper
24 {
25 - DEFAULT_MOVABLE(HandleWrapper);
25 NON_COPYABLE(HandleWrapper)
26
27 + HandleWrapper() = default;
28 + HandleWrapper(HandleWrapper&& other) noexcept;
29 + HandleWrapper& operator=(HandleWrapper&& other) noexcept;
30 HandleWrapper(wil::unique_handle&& handle, std::function<void()>&& OnClose = []() {});
31 HandleWrapper(wil::unique_socket&& handle, std::function<void()>&& OnClose = []() {});
32 + HandleWrapper(wil::shared_handle handle, std::function<void()>&& OnClose = []() {});
33 + HandleWrapper(wil::shared_socket handle, std::function<void()>&& OnClose = []() {});
34 HandleWrapper(wil::unique_event&& handle, std::function<void()>&& OnClose = []() {});
35 HandleWrapper(SOCKET handle, std::function<void()>&& OnClose = []() {});
36 HandleWrapper(HANDLE handle, std::function<void()>&& OnClose = []() {});
@@ -34,11 +38,12 @@ struct HandleWrapper
38 ~HandleWrapper();
39
40 HANDLE Get() const;
41 + bool IsValid() const;
42 void Reset();
43
44 private:
45 HANDLE Handle{};
41 - std::variant<wil::unique_handle, wil::unique_socket> OwnedHandle;
46 + std::variant<wil::unique_handle, wil::unique_socket, wil::shared_handle, wil::shared_socket> OwnedHandle;
47 std::function<void()> OnClose;
48 };
49
src/windows/common/WSLCProcessLauncher.cpp
+1 -1
@@ -223,7 +223,7 @@ ClientRunningWSLCProcess::ClientRunningWSLCProcess(wil::com_ptr<IWSLCProcess>&&
223 {
224 }
225
226 -wil::unique_handle ClientRunningWSLCProcess::GetStdHandle(int Index)
226 +wsl::windows::common::io::HandleWrapper ClientRunningWSLCProcess::GetStdHandle(int Index)
227 {
228 wslutil::COMOutputHandle handle;
229 THROW_IF_FAILED_MSG(m_process->GetStdHandle(static_cast<WSLCFD>(Index), &handle), "Failed to get handle: %i", Index);
src/windows/common/WSLCProcessLauncher.h
+3 -2
@@ -15,6 +15,7 @@ Abstract:
15 --*/
16
17 #pragma once
18 +#include "HandleIO.h"
19 #include "wslc.h"
20 #include <variant>
21 #include <vector>
@@ -37,7 +38,7 @@ public:
38
39 ProcessResult WaitAndCaptureOutput(DWORD TimeoutMs = INFINITE, std::vector<std::unique_ptr<io::OverlappedIOHandle>>&& ExtraHandles = {});
40 int Wait(DWORD TimeoutMs = INFINITE);
40 - virtual wil::unique_handle GetStdHandle(int Index) = 0;
41 + virtual io::HandleWrapper GetStdHandle(int Index) = 0;
42 virtual wil::unique_event GetExitEvent() = 0;
43 int GetExitCode();
44 WSLCProcessState State();
@@ -57,7 +58,7 @@ public:
58 DEFAULT_MOVABLE(ClientRunningWSLCProcess);
59
60 ClientRunningWSLCProcess(wil::com_ptr<IWSLCProcess>&& process, WSLCProcessFlags Flags);
60 - wil::unique_handle GetStdHandle(int Index) override;
61 + io::HandleWrapper GetStdHandle(int Index) override;
62 wil::unique_event GetExitEvent() override;
63 IWSLCProcess& Get();
64
src/windows/common/wslutil.cpp
+16
@@ -21,6 +21,7 @@ Abstract:
21
22 #include "ConsoleProgressBar.h"
23 #include "ExecutionContext.h"
24 +#include "HandleIO.h"
25 #include "MsiQuery.h"
26 #include "WslInstall.h"
27
@@ -33,6 +34,21 @@ using namespace wsl::windows::common::wslutil;
34
35 constexpr auto c_latestReleaseUrl = L"https://api.github.com/repos/Microsoft/WSL/releases/latest";
36 constexpr auto c_releaseListUrl = L"https://api.github.com/repos/Microsoft/WSL/releases";
37 +
38 +wsl::windows::common::io::HandleWrapper COMOutputHandle::Release()
39 +{
40 + const auto type = Type;
41 + const auto handle = Handle.File;
42 + Handle.File = nullptr;
43 + Type = WSLCHandleTypeUnknown;
44 +
45 + if (type == WSLCHandleTypeSocket)
46 + {
47 + return wsl::windows::common::io::HandleWrapper{wil::unique_socket{reinterpret_cast<SOCKET>(handle)}};
48 + }
49 +
50 + return wsl::windows::common::io::HandleWrapper{wil::unique_handle{handle}};
51 +}
52 constexpr auto c_specificReleaseListUrl = L"https://api.github.com/repos/Microsoft/WSL/releases/tags/";
53 constexpr auto c_userAgent = L"wsl-install"; // required to use the GitHub API
54 constexpr auto c_pipePrefix = L"\\\\.\\pipe\\";
src/windows/common/wslutil.h
+15 -8
@@ -24,6 +24,10 @@ Abstract:
24 namespace wsl::windows::common {
25 struct Error;
26
27 +namespace io {
28 + struct HandleWrapper;
29 +}
30 +
31 struct ErrorStrings
32 {
33 std::wstring Message;
@@ -96,18 +100,21 @@ struct COMOutputHandle : public WSLCHandle
100 {
101 if (!Empty())
102 {
99 - LOG_IF_WIN32_BOOL_FALSE(CloseHandle(Handle.File));
103 + if (Type == WSLCHandleTypeSocket)
104 + {
105 + LOG_LAST_ERROR_IF(closesocket(reinterpret_cast<SOCKET>(Handle.Socket)) == SOCKET_ERROR);
106 + }
107 + else
108 + {
109 + LOG_IF_WIN32_BOOL_FALSE(CloseHandle(Handle.File));
110 + }
111 +
112 Handle.File = nullptr;
113 + Type = WSLCHandleTypeUnknown;
114 }
115 }
116
104 - [[nodiscard]] wil::unique_handle Release() noexcept
105 - {
106 - wil::unique_handle handle(Handle.File);
107 - Handle.File = nullptr;
108 -
109 - return handle;
110 - }
117 + [[nodiscard]] io::HandleWrapper Release();
118
119 HANDLE Get() const noexcept
120 {
src/windows/wslc/services/ConsoleService.cpp
+8 -6
@@ -18,6 +18,7 @@ Abstract:
18 namespace wsl::windows::wslc::services {
19
20 using wsl::windows::common::ClientRunningWSLCProcess;
21 +using wsl::windows::common::io::HandleWrapper;
22 using wsl::windows::common::io::MultiHandleWait;
23 using wsl::windows::common::io::OverlappedIOHandle;
24 using wsl::windows::common::io::ReadConsoleHandle;
@@ -130,7 +131,7 @@ bool ConsoleService::RelayInteractiveTty(wsl::windows::common::ConsoleState& Con
131 return !detached;
132 }
133
133 -void ConsoleService::RelayNonTtyProcess(wil::unique_handle&& Stdin, wil::unique_handle&& Stdout, wil::unique_handle&& Stderr)
134 +void ConsoleService::RelayNonTtyProcess(HandleWrapper&& Stdin, HandleWrapper&& Stdout, HandleWrapper&& Stderr)
135 {
136 // Process output is UTF-8.
137 wsl::windows::common::ConsoleState console;
@@ -143,7 +144,7 @@ void ConsoleService::RelayNonTtyProcess(wil::unique_handle&& Stdin, wil::unique_
144
145 auto joinThread = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { InterruptAndJoinInputThread(inputThread, exitEvent); });
146
146 - if (Stdin.is_valid())
147 + if (Stdin.IsValid())
148 {
149 auto input = GetStdHandle(STD_INPUT_HANDLE);
150
@@ -162,11 +163,11 @@ void ConsoleService::RelayNonTtyProcess(wil::unique_handle&& Stdin, wil::unique_
163 inputThread = std::thread{[&]() {
164 try
165 {
165 - windows::common::relay::InterruptableRelay(GetStdHandle(STD_INPUT_HANDLE), Stdin.get(), exitEvent.get());
166 + windows::common::relay::InterruptableRelay(GetStdHandle(STD_INPUT_HANDLE), Stdin.Get(), exitEvent.get());
167 }
168 CATCH_LOG();
169
169 - Stdin.reset();
170 + Stdin.Reset();
171 }};
172 }
173 }
@@ -182,7 +183,8 @@ int ConsoleService::AttachToCurrentConsole(
183 {
184 if (WI_IsFlagSet(process.Flags(), WSLCProcessFlagsTty))
185 {
185 - if (!RelayInteractiveTty(console, process, process.GetStdHandle(WSLCFDTty).get(), triggerRefresh))
186 + auto tty = process.GetStdHandle(WSLCFDTty);
187 + if (!RelayInteractiveTty(console, process, tty.Get(), triggerRefresh))
188 {
189 terminal.Info(L"[detached]\n");
190 return 0;
@@ -190,7 +192,7 @@ int ConsoleService::AttachToCurrentConsole(
192 }
193 else
194 {
193 - wil::unique_handle stdinHandle;
195 + HandleWrapper stdinHandle;
196 if (WI_IsFlagSet(process.Flags(), WSLCProcessFlagsStdin))
197 {
198 stdinHandle = process.GetStdHandle(WSLCFDStdin);
src/windows/wslc/services/ConsoleService.h
+4 -1
@@ -26,6 +26,9 @@ public:
26 Terminal& terminal, wsl::windows::common::ConsoleState& console, wsl::windows::common::ClientRunningWSLCProcess&& process, bool triggerRefresh = false);
27 static bool RelayInteractiveTty(
28 wsl::windows::common::ConsoleState& console, wsl::windows::common::ClientRunningWSLCProcess& process, HANDLE tty, bool triggerRefresh = false);
29 - static void RelayNonTtyProcess(wil::unique_handle&& Stdin, wil::unique_handle&& Stdout, wil::unique_handle&& Stderr);
29 + static void RelayNonTtyProcess(
30 + wsl::windows::common::io::HandleWrapper&& Stdin,
31 + wsl::windows::common::io::HandleWrapper&& Stdout,
32 + wsl::windows::common::io::HandleWrapper&& Stderr);
33 };
34 } // namespace wsl::windows::wslc::services
src/windows/wslc/services/ContainerService.cpp
+1 -1
@@ -329,7 +329,7 @@ int ContainerService::Attach(Terminal& terminal, Session& session, const std::st
329 // TTY process - relay using interactive TTY handling
330 WI_ASSERT(stderrLogs.Empty());
331 wsl::windows::common::ConsoleState console;
332 - if (!ConsoleService::RelayInteractiveTty(console, runningProcess, stdinLogs.Release().get(), true))
332 + if (!ConsoleService::RelayInteractiveTty(console, runningProcess, stdinLogs.Release().Get(), true))
333 {
334 terminal.Info(L"[detached]\n");
335 return 0; // Exit early if user detached
src/windows/wslc/services/SessionService.cpp
+2 -2
@@ -92,7 +92,7 @@ int SessionService::Attach(Terminal& terminal, const Session& session)
92 try
93 {
94 wsl::windows::common::relay::StandardInputRelay(
95 - GetStdHandle(STD_INPUT_HANDLE), tty.get(), updateTerminalSize, exitEvent.get());
95 + GetStdHandle(STD_INPUT_HANDLE), tty.Get(), updateTerminalSize, exitEvent.get());
96 }
97 catch (...)
98 {
@@ -109,7 +109,7 @@ int SessionService::Attach(Terminal& terminal, const Session& session)
109 });
110
111 // Relay tty output -> console (blocks until output ends).
112 - wsl::windows::common::relay::InterruptableRelay(tty.get(), GetStdHandle(STD_OUTPUT_HANDLE), exitEvent.get());
112 + wsl::windows::common::relay::InterruptableRelay(tty.Get(), GetStdHandle(STD_OUTPUT_HANDLE), exitEvent.get());
113
114 process.GetExitEvent().wait();
115
src/windows/wslcsession/ServiceProcessLauncher.cpp
+2 -2
@@ -25,9 +25,9 @@ ServiceRunningProcess::ServiceRunningProcess(const Microsoft::WRL::ComPtr<WSLCPr
25 process.CopyTo(m_process.GetAddressOf());
26 }
27
28 -wil::unique_handle ServiceRunningProcess::GetStdHandle(int Index)
28 +wsl::windows::common::io::HandleWrapper ServiceRunningProcess::GetStdHandle(int Index)
29 {
30 - return std::move(Get().GetStdHandle(Index));
30 + return Get().GetStdHandle(Index);
31 }
32
33 wil::unique_event ServiceRunningProcess::GetExitEvent()
src/windows/wslcsession/ServiceProcessLauncher.h
+1 -1
@@ -27,7 +27,7 @@ public:
27 DEFAULT_MOVABLE(ServiceRunningProcess);
28
29 ServiceRunningProcess(const Microsoft::WRL::ComPtr<WSLCProcess>& process, WSLCProcessFlags Flags);
30 - wil::unique_handle GetStdHandle(int Index) override;
30 + common::io::HandleWrapper GetStdHandle(int Index) override;
31 wil::unique_event GetExitEvent() override;
32 WSLCProcess& Get();
33
src/windows/wslcsession/WSLCContainer.cpp
+17 -23
@@ -976,11 +976,12 @@ void WSLCContainerImpl::Attach(LPCSTR DetachKeys, WSLCHandle* Stdin, WSLCHandle*
976
977 THROW_HR_WITH_USER_ERROR_IF(WSLC_E_CONTAINER_NOT_RUNNING, Localization::MessageWslcContainerNotRunning(m_id.c_str()), m_state != WslcContainerStateRunning);
978
979 - wil::unique_socket ioHandle;
979 + wil::shared_socket ioHandle;
980
981 try
982 {
983 - ioHandle = m_runtime.Docker().AttachContainer(m_id, DetachKeys == nullptr ? std::nullopt : std::optional<std::string>(DetachKeys));
983 + ioHandle = wil::shared_socket{
984 + m_runtime.Docker().AttachContainer(m_id, DetachKeys == nullptr ? std::nullopt : std::optional<std::string>(DetachKeys))};
985 }
986 CATCH_AND_THROW_DOCKER_USER_ERROR("Failed to attach to container '%hs'", m_id.c_str());
987
@@ -1002,14 +1003,13 @@ void WSLCContainerImpl::Attach(LPCSTR DetachKeys, WSLCHandle* Stdin, WSLCHandle*
1003 std::vector<std::unique_ptr<OverlappedIOHandle>> handles;
1004
1005 // This is required for docker to know when stdin is closed.
1005 - auto onInputComplete = [handle = ioHandle.get()]() { LOG_LAST_ERROR_IF(shutdown(handle, SD_SEND) == SOCKET_ERROR); };
1006 + auto onInputComplete = [ioHandle]() { LOG_LAST_ERROR_IF(shutdown(ioHandle.get(), SD_SEND) == SOCKET_ERROR); };
1007
1007 - // N.B. Ownership of the io handle is given to the DockerIORelayHandle relay, so it can be closed when docker closes the connection.
1008 - handles.emplace_back(
1009 - std::make_unique<RelayHandle<ReadHandle>>(HandleWrapper{std::move(stdinRead), std::move(onInputComplete)}, ioHandle.get()));
1008 + handles.emplace_back(std::make_unique<RelayHandle<ReadHandle>>(
1009 + HandleWrapper{std::move(stdinRead), std::move(onInputComplete)}, HandleWrapper{ioHandle}));
1010
1011 handles.emplace_back(std::make_unique<DockerIORelayHandle>(
1012 - std::move(ioHandle), std::move(stdoutWrite), std::move(stderrWrite), DockerIORelayHandle::Format::Raw));
1012 + HandleWrapper{ioHandle}, std::move(stdoutWrite), std::move(stderrWrite), DockerIORelayHandle::Format::Raw));
1013
1014 m_runtime.Relay()->AddHandles(std::move(handles));
1015
@@ -1059,13 +1059,11 @@ void WSLCContainerImpl::Start(WSLCContainerStartFlags Flags, const WSLCProcessSt
1059 {
1060 if (WI_IsFlagSet(m_initProcessFlags, WSLCProcessFlagsTty))
1061 {
1062 - io = std::make_unique<TTYProcessIO>(TypedHandle{
1063 - wil::unique_handle{(HANDLE)m_runtime.Docker().AttachContainer(m_id, detachKeys).release()}, WSLCHandleTypeSocket});
1062 + io = std::make_unique<TTYProcessIO>(TypedHandle{m_runtime.Docker().AttachContainer(m_id, detachKeys), WSLCHandleTypeSocket});
1063 }
1064 else
1065 {
1067 - wil::unique_handle stream{reinterpret_cast<HANDLE>(m_runtime.Docker().AttachContainer(m_id, detachKeys).release())};
1068 - io = CreateRelayedProcessIO(std::move(stream), m_initProcessFlags);
1066 + io = CreateRelayedProcessIO(wil::shared_socket{m_runtime.Docker().AttachContainer(m_id, detachKeys)}, m_initProcessFlags);
1067 }
1068 }
1069 }
@@ -1785,10 +1783,8 @@ void WSLCContainerImpl::Exec(const WSLCProcessOptions* Options, const WSLCProces
1783
1784 // N.B. There's no way to delete a created exec instance, it is removed when the container is deleted.
1785
1788 - wil::unique_handle stream{
1789 - (HANDLE)m_runtime.Docker()
1790 - .StartExec(result.Id, common::docker_schema::StartExec{.Tty = request.Tty, .ConsoleSize = request.ConsoleSize})
1791 - .release()};
1786 + auto stream = m_runtime.Docker().StartExec(
1787 + result.Id, common::docker_schema::StartExec{.Tty = request.Tty, .ConsoleSize = request.ConsoleSize});
1788
1789 std::unique_ptr<WSLCProcessIO> io;
1790 if (request.Tty)
@@ -1797,7 +1793,7 @@ void WSLCContainerImpl::Exec(const WSLCProcessOptions* Options, const WSLCProces
1793 }
1794 else
1795 {
1800 - io = CreateRelayedProcessIO(std::move(stream), Options->Flags);
1796 + io = CreateRelayedProcessIO(wil::shared_socket{std::move(stream)}, Options->Flags);
1797 }
1798
1799 auto control = std::make_shared<DockerExecProcessControl>(*this, result.Id, m_runtime.Docker(), m_runtime.Events());
@@ -2739,22 +2735,20 @@ void WSLCContainerImpl::Stats(LPSTR* Output) const
2735 CATCH_AND_THROW_DOCKER_USER_ERROR("Failed to get stats for container '%hs'", m_id.c_str());
2736 }
2737
2742 -std::unique_ptr<RelayedProcessIO> WSLCContainerImpl::CreateRelayedProcessIO(wil::unique_handle&& stream, WSLCProcessFlags flags)
2738 +std::unique_ptr<RelayedProcessIO> WSLCContainerImpl::CreateRelayedProcessIO(wil::shared_socket stream, WSLCProcessFlags flags)
2739 {
2740 // Create one pipe for each STD handle.
2741 std::vector<std::unique_ptr<OverlappedIOHandle>> ioHandles;
2742 std::map<ULONG, TypedHandle> fds;
2743
2744 // This is required for docker to know when stdin is closed.
2749 - auto closeStdin = [socket = stream.get(), this]() {
2750 - LOG_LAST_ERROR_IF(shutdown(reinterpret_cast<SOCKET>(socket), SD_SEND) == SOCKET_ERROR);
2751 - };
2745 + auto closeStdin = [stream]() { LOG_LAST_ERROR_IF(shutdown(stream.get(), SD_SEND) == SOCKET_ERROR); };
2746
2747 if (WI_IsFlagSet(flags, WSLCProcessFlagsStdin))
2748 {
2749 auto [stdinRead, stdinWrite] = common::wslutil::OpenAnonymousPipe(LX_RELAY_BUFFER_SIZE, true, true);
2756 - ioHandles.emplace_back(
2757 - std::make_unique<RelayHandle<ReadHandle>>(HandleWrapper{std::move(stdinRead), std::move(closeStdin)}, stream.get()));
2750 + ioHandles.emplace_back(std::make_unique<RelayHandle<ReadHandle>>(
2751 + HandleWrapper{std::move(stdinRead), std::move(closeStdin)}, HandleWrapper{stream}));
2752
2753 fds.emplace(WSLCFDStdin, TypedHandle{wil::unique_handle{stdinWrite.release()}, WSLCHandleTypePipe});
2754 }
@@ -2771,7 +2765,7 @@ std::unique_ptr<RelayedProcessIO> WSLCContainerImpl::CreateRelayedProcessIO(wil:
2765 fds.emplace(WSLCFDStderr, TypedHandle{wil::unique_handle{stderrRead.release()}, WSLCHandleTypePipe});
2766
2767 ioHandles.emplace_back(std::make_unique<DockerIORelayHandle>(
2774 - std::move(stream), std::move(stdoutWrite), std::move(stderrWrite), common::io::DockerIORelayHandle::Format::Raw));
2768 + HandleWrapper{stream}, std::move(stdoutWrite), std::move(stderrWrite), common::io::DockerIORelayHandle::Format::Raw));
2769
2770 m_runtime.Relay()->AddHandles(std::move(ioHandles));
2771
src/windows/wslcsession/WSLCContainer.h
+1 -1
@@ -202,7 +202,7 @@ private:
202 void SetExitCode(int ExitCode) noexcept;
203 void SignalInitProcessExit() noexcept;
204
205 - std::unique_ptr<RelayedProcessIO> CreateRelayedProcessIO(wil::unique_handle&& stream, WSLCProcessFlags flags);
205 + std::unique_ptr<RelayedProcessIO> CreateRelayedProcessIO(wil::shared_socket stream, WSLCProcessFlags flags);
206
207 wsl::windows::common::wslc_schema::InspectContainer BuildInspectContainer(const wsl::windows::common::docker_schema::InspectContainer& dockerInspect) const;
208
src/windows/wslcsession/WSLCProcess.cpp
+1 -1
@@ -80,7 +80,7 @@ try
80 }
81 CATCH_RETURN();
82
83 -wil::unique_handle WSLCProcess::GetStdHandle(int Index)
83 +wsl::windows::common::io::HandleWrapper WSLCProcess::GetStdHandle(int Index)
84 {
85 THROW_WIN32_IF(ERROR_INVALID_STATE, !m_io);
86
src/windows/wslcsession/WSLCProcess.h
+1 -1
@@ -41,7 +41,7 @@ public:
41 // IWSLCCompatProcess - converts the WSLCCompat types to the wslc.idl types and forwards to the methods above.
42 IFACEMETHOD(GetStdHandle)(_In_ WSLCFD Fd, _Out_ WSLCCompatHandle* Handle) override;
43
44 - wil::unique_handle GetStdHandle(int Index);
44 + common::io::HandleWrapper GetStdHandle(int Index);
45 HANDLE GetExitEvent();
46 int GetPid() const;
47
src/windows/wslcsession/WSLCProcessIO.h
+5 -4
@@ -13,27 +13,28 @@ Abstract:
13 --*/
14
15 #pragma once
16 +#include "HandleIO.h"
17 #include "wslc.h"
18
19 namespace wsl::windows::service::wslc {
20
21 struct TypedHandle
22 {
22 - wil::unique_handle Handle;
23 + common::io::HandleWrapper Handle;
24 WSLCHandleType Type = WSLCHandleTypeUnknown;
25
26 TypedHandle() = default;
26 - TypedHandle(wil::unique_handle&& handle, WSLCHandleType type) : Handle(std::move(handle)), Type(type)
27 + TypedHandle(common::io::HandleWrapper&& handle, WSLCHandleType type) : Handle(std::move(handle)), Type(type)
28 {
29 }
30
31 bool is_valid() const noexcept
32 {
32 - return Handle.is_valid();
33 + return Handle.IsValid();
34 }
35 HANDLE get() const noexcept
36 {
36 - return Handle.get();
37 + return Handle.Get();
38 }
39 };
40
src/windows/wslcsession/WSLCVirtualMachine.cpp
+5 -4
@@ -25,6 +25,7 @@ Abstract:
25 #include "lxinitshared.h"
26
27 using namespace wsl::windows::common;
28 +using wsl::windows::common::io::HandleWrapper;
29 using wsl::windows::service::wslc::TypedHandle;
30 using wsl::windows::service::wslc::VmPortAllocation;
31 using wsl::windows::service::wslc::VMPortMapping;
@@ -463,15 +464,15 @@ void WSLCVirtualMachine::ConfigureNetworking()
464 // Call back to the service to configure the networking engine.
465 auto gnsHandle = process->GetStdHandle(gnsChannelFd);
466
466 - wil::unique_handle dnsHandle;
467 + HandleWrapper dnsHandle;
468 HANDLE dnsSocketHandle = nullptr;
469 if (enableDnsTunneling)
470 {
471 dnsHandle = process->GetStdHandle(dnsChannelFd);
471 - dnsSocketHandle = dnsHandle.get();
472 + dnsSocketHandle = dnsHandle.Get();
473 }
474
474 - THROW_IF_FAILED(m_vm->ConfigureNetworking(gnsHandle.get(), enableDnsTunneling ? &dnsSocketHandle : nullptr));
475 + THROW_IF_FAILED(m_vm->ConfigureNetworking(gnsHandle.Get(), enableDnsTunneling ? &dnsSocketHandle : nullptr));
476
477 // Launch port relay for port forwarding
478 LaunchPortRelay();
@@ -929,7 +930,7 @@ Microsoft::WRL::ComPtr<WSLCProcess> WSLCVirtualMachine::CreateLinuxProcessImpl(
930 std::map<ULONG, TypedHandle> stdHandles;
931 for (auto& [fd, handle] : sockets)
932 {
932 - stdHandles.emplace(fd, TypedHandle{wil::unique_handle{reinterpret_cast<HANDLE>(handle.release())}, WSLCHandleTypeSocket});
933 + stdHandles.emplace(fd, TypedHandle{std::move(handle), WSLCHandleTypeSocket});
934 }
935
936 auto io = std::make_unique<VMProcessIO>(std::move(stdHandles));
src/windows/wslcsession/main.cpp
+1
@@ -76,6 +76,7 @@ try
76 // Initialize COM
77 auto coInit = wil::CoInitializeEx(COINIT_MULTITHREADED);
78 wsl::windows::common::wslutil::CoInitializeSecurity();
79 + auto cleanupWinrt = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { winrt::clear_factory_cache(); });
80
81 // Register the class factory (single-use: one factory per process)
82 auto factory = winrt::make<WSLCSessionFactoryClassFactory>();
test/windows/Common.cpp
+6 -1
@@ -2807,6 +2807,11 @@ PartialHandleRead::PartialHandleRead(HANDLE Handle) : m_handle(Handle)
2807 }
2808
2809 PartialHandleRead::~PartialHandleRead()
2810 +{
2811 + Stop();
2812 +}
2813 +
2814 +void PartialHandleRead::Stop()
2815 {
2816 m_exitEvent.SetEvent();
2817 if (m_thread.joinable())
@@ -2950,7 +2955,7 @@ private:
2955 std::string m_targetValue;
2956 };
2957
2953 -void WaitForOutput(wil::unique_handle handle, std::string_view targetValue, std::chrono::milliseconds timeout)
2958 +void WaitForOutput(wsl::windows::common::io::HandleWrapper handle, std::string_view targetValue, std::chrono::milliseconds timeout)
2959 {
2960 wsl::windows::common::io::MultiHandleWait io;
2961 io.AddHandle(std::make_unique<ReadHandleWithTargetValue>(std::move(handle), targetValue));
test/windows/Common.h
+6 -1
@@ -400,6 +400,7 @@ public:
400 void Expect(const std::string& Expected);
401 void ExpectConsume(const std::string& Expected);
402 void ExpectClosed(DWORD Timeout = 60 * 1000);
403 + void Stop();
404
405 std::string ReadBytes(size_t Length);
406 std::string ConsumeBytes(size_t Length);
@@ -646,7 +647,11 @@ std::pair<wil::unique_socket, wil::unique_socket> MakeSocketPair();
647 std::wstring ReadFileContent(const std::string& Path);
648 std::wstring ReadFileContent(const std::wstring& Path);
649
649 -void WaitForOutput(wil::unique_handle handle, std::string_view targetValue, std::chrono::milliseconds timeout = 60s);
650 +void WaitForOutput(wsl::windows::common::io::HandleWrapper handle, std::string_view targetValue, std::chrono::milliseconds timeout = 60s);
651 +inline void WaitForOutput(wil::unique_handle handle, std::string_view targetValue, std::chrono::milliseconds timeout = 60s)
652 +{
653 + WaitForOutput(wsl::windows::common::io::HandleWrapper{std::move(handle)}, targetValue, timeout);
654 +}
655
656 std::string EscapeString(const std::string& Input);
657
test/windows/WSLCTests.cpp
+35 -35
@@ -3669,7 +3669,7 @@ class WSLCTests
3669 WSLCProcessLauncher launcher("/bin/sh", {"/bin/sh"}, {"TERM=xterm-256color"}, WSLCProcessFlagsTty | WSLCProcessFlagsStdin);
3670 auto process = launcher.Launch(*m_defaultSession);
3671
3672 - wil::unique_handle tty = process.GetStdHandle(WSLCFDTty);
3672 + auto tty = process.GetStdHandle(WSLCFDTty);
3673
3674 auto validateTtyOutput = [&](const std::string& expected) {
3675 std::string buffer(expected.size(), '\0');
@@ -3679,7 +3679,7 @@ class WSLCTests
3679 while (offset < buffer.size())
3680 {
3681 DWORD bytesRead{};
3682 - VERIFY_IS_TRUE(ReadFile(tty.get(), buffer.data() + offset, static_cast<DWORD>(buffer.size() - offset), &bytesRead, nullptr));
3682 + VERIFY_IS_TRUE(ReadFile(tty.Get(), buffer.data() + offset, static_cast<DWORD>(buffer.size() - offset), &bytesRead, nullptr));
3683
3684 offset += bytesRead;
3685 }
@@ -3689,7 +3689,7 @@ class WSLCTests
3689 };
3690
3691 auto writeTty = [&](const std::string& content) {
3692 - VERIFY_IS_TRUE(WriteFile(tty.get(), content.data(), static_cast<DWORD>(content.size()), nullptr, nullptr));
3692 + VERIFY_IS_TRUE(WriteFile(tty.Get(), content.data(), static_cast<DWORD>(content.size()), nullptr, nullptr));
3693 };
3694
3695 // Expect the shell prompt to be displayed
@@ -7360,7 +7360,7 @@ class WSLCTests
7360 auto initProcess = container.GetInitProcess();
7361 auto input = initProcess.GetStdHandle(0);
7362 auto outputHandle = initProcess.GetStdHandle(1);
7363 - PartialHandleRead output{outputHandle.get()};
7363 + PartialHandleRead output{outputHandle.Get()};
7364 output.ExpectConsume("ready\n");
7365
7366 HRESULT stopResult{};
@@ -7369,7 +7369,7 @@ class WSLCTests
7369 std::thread killThread;
7370
7371 auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
7372 - input.reset();
7372 + input.Reset();
7373
7374 if (stopThread.joinable())
7375 {
@@ -7401,9 +7401,9 @@ class WSLCTests
7401
7402 const char stopInput = '\n';
7403 DWORD bytesWritten{};
7404 - VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(input.get(), &stopInput, sizeof(stopInput), &bytesWritten, nullptr));
7404 + VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(input.Get(), &stopInput, sizeof(stopInput), &bytesWritten, nullptr));
7405 VERIFY_ARE_EQUAL(bytesWritten, static_cast<DWORD>(sizeof(stopInput)));
7406 - input.reset();
7406 + input.Reset();
7407
7408 VERIFY_ARE_EQUAL(WaitForSingleObject(stopThread.native_handle(), 30 * 1000), WAIT_OBJECT_0);
7409
@@ -7431,7 +7431,7 @@ class WSLCTests
7431 auto container = launcher.Launch(*m_defaultSession);
7432 auto initProcess = container.GetInitProcess();
7433 auto outputHandle = initProcess.GetStdHandle(1);
7434 - PartialHandleRead output{outputHandle.get()};
7434 + PartialHandleRead output{outputHandle.Get()};
7435 output.ExpectConsume("ready\n");
7436
7437 HRESULT stopResult{};
@@ -7484,7 +7484,7 @@ class WSLCTests
7484 auto initProcess = container.GetInitProcess();
7485 auto input = initProcess.GetStdHandle(0);
7486 auto outputHandle = initProcess.GetStdHandle(1);
7487 - PartialHandleRead output{outputHandle.get()};
7487 + PartialHandleRead output{outputHandle.Get()};
7488 output.ExpectConsume("ready\n");
7489
7490 HRESULT indefiniteStopResult{};
@@ -7493,7 +7493,7 @@ class WSLCTests
7493 std::thread immediateStopThread;
7494
7495 auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
7496 - input.reset();
7496 + input.Reset();
7497
7498 if (indefiniteStopThread.joinable())
7499 {
@@ -7532,7 +7532,7 @@ class WSLCTests
7532 auto initProcess = container.GetInitProcess();
7533 auto input = initProcess.GetStdHandle(0);
7534 auto outputHandle = initProcess.GetStdHandle(1);
7535 - PartialHandleRead output{outputHandle.get()};
7535 + PartialHandleRead output{outputHandle.Get()};
7536 output.ExpectConsume("ready\n");
7537
7538 HRESULT stopResult{};
@@ -7541,7 +7541,7 @@ class WSLCTests
7541 std::thread startThread;
7542
7543 auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
7544 - input.reset();
7544 + input.Reset();
7545
7546 if (stopThread.joinable())
7547 {
@@ -7568,9 +7568,9 @@ class WSLCTests
7568
7569 const char stopInput = '\n';
7570 DWORD bytesWritten{};
7571 - VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(input.get(), &stopInput, sizeof(stopInput), &bytesWritten, nullptr));
7571 + VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(input.Get(), &stopInput, sizeof(stopInput), &bytesWritten, nullptr));
7572 VERIFY_ARE_EQUAL(bytesWritten, static_cast<DWORD>(sizeof(stopInput)));
7573 - input.reset();
7573 + input.Reset();
7574
7575 VERIFY_ARE_EQUAL(WaitForSingleObject(stopThread.native_handle(), 30 * 1000), WAIT_OBJECT_0);
7576 stopThread.join();
@@ -7586,7 +7586,7 @@ class WSLCTests
7586 auto initProcess = container.GetInitProcess();
7587 auto input = initProcess.GetStdHandle(0);
7588 auto outputHandle = initProcess.GetStdHandle(1);
7589 - PartialHandleRead output{outputHandle.get()};
7589 + PartialHandleRead output{outputHandle.Get()};
7590 output.ExpectConsume("ready\n");
7591
7592 HRESULT stopResult{};
@@ -7595,7 +7595,7 @@ class WSLCTests
7595 std::thread deleteThread;
7596
7597 auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
7598 - input.reset();
7598 + input.Reset();
7599
7600 if (stopThread.joinable())
7601 {
@@ -7620,7 +7620,7 @@ class WSLCTests
7620
7621 deleteThread.join();
7622 stopThread.join();
7623 - input.reset();
7623 + input.Reset();
7624 cleanup.release();
7625
7626 VERIFY_SUCCEEDED(stopResult);
@@ -11432,7 +11432,7 @@ class WSLCTests
11432 auto container = launcher.Launch(*m_defaultSession);
11433 auto initProcess = container.GetInitProcess();
11434
11435 - ValidateHandleOutput(initProcess.GetStdHandle(WSLCFDTty).get(), "Type: devpts\r\n");
11435 + ValidateHandleOutput(initProcess.GetStdHandle(WSLCFDTty).Get(), "Type: devpts\r\n");
11436 VERIFY_ARE_EQUAL(initProcess.Wait(), 0);
11437
11438 expectLogs(container.Get(), "Type: devpts\r\n", {});
@@ -11458,13 +11458,13 @@ class WSLCTests
11458 PartialHandleRead reader(stdoutHandle.Get());
11459
11460 auto containerStdin = initProcess.GetStdHandle(0);
11461 - VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(containerStdin.get(), "line1\n", 6, nullptr, nullptr));
11461 + VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(containerStdin.Get(), "line1\n", 6, nullptr, nullptr));
11462
11463 reader.Expect("line1\n");
11464 - VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(containerStdin.get(), "line2\n", 6, nullptr, nullptr));
11464 + VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(containerStdin.Get(), "line2\n", 6, nullptr, nullptr));
11465 reader.Expect("line1\nline2\n");
11466
11467 - containerStdin.reset();
11467 + containerStdin.Reset();
11468 reader.ExpectClosed();
11469
11470 expectLogs(container.Get(), "line1\nline2\n", "");
@@ -11492,7 +11492,7 @@ class WSLCTests
11492 auto initProcess = container.GetInitProcess();
11493
11494 auto containerStdin = initProcess.GetStdHandle(0);
11495 - VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(containerStdin.get(), "OK\n", 3, nullptr, nullptr));
11495 + VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(containerStdin.Get(), "OK\n", 3, nullptr, nullptr));
11496
11497 std::atomic<size_t> readersReady{0};
11498 std::atomic<size_t> readersSucceeded{0};
@@ -11873,11 +11873,11 @@ class WSLCTests
11873 stderrHandle.Reset();
11874 VERIFY_SUCCEEDED(container->Get().Attach(nullptr, &stdinHandle, &stdoutHandle, &stderrHandle));
11875
11876 - PartialHandleRead originalReader(originalStdout.get());
11876 + PartialHandleRead originalReader(originalStdout.Get());
11877 PartialHandleRead attachedReader(stdoutHandle.Get());
11878
11879 // Write content on the original stdin.
11880 - VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(originalStdin.get(), "line1\n", 6, nullptr, nullptr));
11880 + VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(originalStdin.Get(), "line1\n", 6, nullptr, nullptr));
11881
11882 // Content should be relayed on both stdouts.
11883 originalReader.Expect("line1\n");
@@ -11891,7 +11891,7 @@ class WSLCTests
11891 attachedReader.Expect("line1\nline2\n");
11892
11893 // Close the original stdin.
11894 - originalStdin.reset();
11894 + originalStdin.Reset();
11895
11896 // Expect both readers to be closed.
11897 originalReader.ExpectClosed();
@@ -11936,7 +11936,7 @@ class WSLCTests
11936 COMOutputHandle attachedStderr;
11937 VERIFY_SUCCEEDED(container.Get().Attach(nullptr, &attachedStdin, &attachedStdout, &attachedStderr));
11938
11939 - PartialHandleRead originalReader(originalStdout.get());
11939 + PartialHandleRead originalReader(originalStdout.Get());
11940 PartialHandleRead attachedReader(attachedStdout.Get());
11941
11942 attachedStdin.Reset();
@@ -11959,7 +11959,7 @@ class WSLCTests
11959 COMOutputHandle dummyHandle2{};
11960 VERIFY_SUCCEEDED(container.Get().Attach(nullptr, &attachedTty, &dummyHandle1, &dummyHandle2));
11961
11962 - PartialHandleRead originalReader(originalTty.get());
11962 + PartialHandleRead originalReader(originalTty.Get());
11963 PartialHandleRead attachedReader(attachedTty.Get());
11964
11965 // Read the prompt from the original tty (hardcoded bytes since behavior is constant).
@@ -11972,12 +11972,12 @@ class WSLCTests
11972 auto attachedPrompt = attachedReader.ReadBytes(13);
11973 VerifyPatternMatch(attachedPrompt, "*root@*");
11974
11975 - // Close the tty.
11976 - originalTty.reset();
11977 - attachedTty.Reset();
11975 + // Stop pending reads before closing the handles borrowed by the readers.
11976 + originalReader.Stop();
11977 + attachedReader.Stop();
11978
11979 - originalReader.ExpectClosed();
11980 - attachedReader.ExpectClosed();
11979 + originalTty.Reset();
11980 + attachedTty.Reset();
11981 }
11982
11983 // Validate that containers can be started in detached mode and attached to later.
@@ -12026,7 +12026,7 @@ class WSLCTests
12026 auto tty = process.GetStdHandle(WSLCFDTty);
12027
12028 // Wait for the size to be reflected in a loop, since the tty size is applied asynchronously.
12029 - PartialHandleRead reader(tty.get());
12029 + PartialHandleRead reader(tty.Get());
12030 wsl::shared::retry::RetryWithTimeout<void>(
12031 [&]() { THROW_HR_IF(E_ABORT, reader.GetData().find(expectedSize) == std::string::npos); },
12032 std::chrono::milliseconds(100),
@@ -12652,7 +12652,7 @@ class WSLCTests
12652 // Validate detaching from a started container with the attach flag.
12653 {
12654 auto tty = initProcess.GetStdHandle(WSLCFDTty);
12655 - validateDetaches(tty.get(), tty.get(), DetachSequence);
12655 + validateDetaches(tty.Get(), tty.Get(), DetachSequence);
12656 }
12657
12658 // Validate detaching from an attached tty.
@@ -12677,7 +12677,7 @@ class WSLCTests
12677 auto process = processLauncher.Launch(container.Get());
12678 auto tty = process.GetStdHandle(WSLCFDTty);
12679
12680 - validateDetaches(tty.get(), tty.get(), DetachSequence);
12680 + validateDetaches(tty.Get(), tty.Get(), DetachSequence);
12681 }
12682 };
12683