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