Harden the COM service API (#40749)
* Harden the COM service API * Apply PR feedback
Blue committed
Jun 8, 2026 at 18:02 UTC
1309fd1c26628673e258357bcff52b6672baa19f
8 files changed
+181
-1
src/windows/service/exe/HcsVirtualMachine.cpp
+6
@@ -375,6 +375,8 @@ bool HcsVirtualMachine::FeatureEnabled(WSLCFeatureFlags Value) const
375
HRESULT HcsVirtualMachine::GetId(_Out_ GUID* VmId)
376
try
377
{
378
+ RETURN_HR_IF_NULL(E_POINTER, VmId);
379
+
380
*VmId = m_vmId;
381
return S_OK;
382
}
@@ -383,6 +385,8 @@ CATCH_RETURN()
385
HRESULT HcsVirtualMachine::AcceptConnection(_Out_ HANDLE* Socket)
386
try
387
{
388
+ RETURN_HR_IF_NULL(E_POINTER, Socket);
389
+
390
auto socket = wsl::windows::common::hvsocket::CancellableAccept(m_listenSocket.get(), m_bootTimeoutMs, m_vmExitEvent.get());
391
THROW_HR_IF(E_ABORT, !socket.has_value());
392
@@ -658,6 +662,8 @@ CATCH_RETURN()
662
HRESULT HcsVirtualMachine::GetTerminationEvent(_Out_ HANDLE* Event)
663
try
664
{
665
+ RETURN_HR_IF_NULL(E_POINTER, Event);
666
+
667
*Event = wslutil::DuplicateHandle(m_vmExitEvent.get());
668
669
return S_OK;
src/windows/service/exe/WSLCSessionManager.cpp
+12
@@ -167,6 +167,8 @@ CATCH_LOG()
167
void WSLCSessionManagerImpl::CreateSession(
168
_In_ const WSLCSessionSettings* Settings, _In_ WSLCSessionFlags Flags, _In_opt_ IWarningCallback* WarningCallback, _Out_ IWSLCSession** WslcSession)
169
{
170
+ THROW_HR_IF_NULL(E_POINTER, WslcSession);
171
+
172
auto tokenInfo = GetCallingProcessTokenInfo();
173
const auto callerToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);
174
@@ -183,6 +185,9 @@ void WSLCSessionManagerImpl::CreateSession(
185
THROW_HR_IF(WSLC_E_INVALID_SESSION_NAME, Settings->DisplayName == nullptr || wcslen(Settings->DisplayName) == 0);
186
THROW_HR_IF(E_INVALIDARG, Settings->StoragePath != nullptr && wcslen(Settings->StoragePath) == 0);
187
THROW_HR_IF(WSLC_E_INVALID_SESSION_NAME, wcslen(Settings->DisplayName) >= std::size(WSLCSessionListEntry{}.DisplayName));
188
+ THROW_HR_IF_MSG(E_INVALIDARG, WI_IsAnyFlagSet(Flags, ~WSLCSessionFlagsValid), "Invalid session flags: 0x%x", Flags);
189
+ THROW_HR_IF_MSG(
190
+ E_INVALIDARG, WI_IsAnyFlagSet(Settings->FeatureFlags, ~WSLCFeatureFlagsValid), "Invalid feature flags: 0x%x", Settings->FeatureFlags);
191
THROW_HR_IF_MSG(
192
E_INVALIDARG,
193
WI_IsAnyFlagSet(Settings->StorageFlags, ~WSLCSessionStorageFlagsValid),
@@ -334,6 +339,8 @@ void WSLCSessionManagerImpl::CreateSession(
339
340
void WSLCSessionManagerImpl::OpenSession(ULONG Id, IWSLCSession** Session)
341
{
342
+ THROW_HR_IF_NULL(E_POINTER, Session);
343
+
344
auto tokenInfo = GetCallingProcessTokenInfo();
345
auto result = ForEachSession<HRESULT>([&](auto& entry, const wil::com_ptr<IWSLCSession>& session) noexcept -> std::optional<HRESULT> {
346
if (entry.SessionId != Id)
@@ -353,6 +360,8 @@ void WSLCSessionManagerImpl::OpenSession(ULONG Id, IWSLCSession** Session)
360
361
void WSLCSessionManagerImpl::OpenSessionByName(LPCWSTR DisplayName, IWSLCSession** Session)
362
{
363
+ THROW_HR_IF_NULL(E_POINTER, Session);
364
+
365
auto tokenInfo = GetCallingProcessTokenInfo();
366
367
// Null name = default session, resolved from caller's token + username.
@@ -381,6 +390,9 @@ void WSLCSessionManagerImpl::OpenSessionByName(LPCWSTR DisplayName, IWSLCSession
390
391
void WSLCSessionManagerImpl::ListSessions(_Out_ WSLCSessionListEntry** Sessions, _Out_ ULONG* SessionsCount)
392
{
393
+ THROW_HR_IF_NULL(E_POINTER, Sessions);
394
+ THROW_HR_IF_NULL(E_POINTER, SessionsCount);
395
+
396
std::vector<WSLCSessionListEntry> sessionInfo;
397
398
ForEachSession<void>([&](auto& entry, const auto&) noexcept {
src/windows/wslcsession/WSLCContainer.cpp
+14
@@ -2054,6 +2054,10 @@ HRESULT WSLCContainer::Attach(LPCSTR DetachKeys, WSLCHandle* Stdin, WSLCHandle*
2054
{
2055
WSLCExecutionContext context(&m_session);
2056
2057
+ RETURN_HR_IF_NULL(E_POINTER, Stdin);
2058
+ RETURN_HR_IF_NULL(E_POINTER, Stdout);
2059
+ RETURN_HR_IF_NULL(E_POINTER, Stderr);
2060
+
2061
*Stdin = {};
2062
*Stdout = {};
2063
*Stderr = {};
@@ -2092,6 +2096,8 @@ HRESULT WSLCContainer::GetInitProcess(IWSLCProcess** Process)
2096
{
2097
WSLCExecutionContext context(&m_session);
2098
2099
+ RETURN_HR_IF_NULL(E_POINTER, Process);
2100
+
2101
*Process = nullptr;
2102
2103
HRESULT hr = CallImpl(&WSLCContainerImpl::GetInitProcess, Process);
@@ -2118,6 +2124,10 @@ HRESULT WSLCContainer::Exec(const WSLCProcessOptions* Options, LPCSTR DetachKeys
2124
{
2125
WSLCExecutionContext context(&m_session);
2126
2127
+ RETURN_HR_IF_NULL(E_POINTER, Options);
2128
+ RETURN_HR_IF_NULL(E_POINTER, Process);
2129
+ RETURN_HR_IF_MSG(E_INVALIDARG, WI_IsAnyFlagSet(Options->Flags, ~WSLCProcessFlagsValid), "Invalid flags: 0x%x", Options->Flags);
2130
+
2131
*Process = nullptr;
2132
return CallImpl(&WSLCContainerImpl::Exec, Options, DetachKeys, Process);
2133
}
@@ -2151,6 +2161,8 @@ HRESULT WSLCContainer::Inspect(LPSTR* Output)
2161
{
2162
WSLCExecutionContext context(&m_session);
2163
2164
+ RETURN_HR_IF_NULL(E_POINTER, Output);
2165
+
2166
*Output = nullptr;
2167
2168
return CallImpl(&WSLCContainerImpl::Inspect, Output);
@@ -2227,6 +2239,8 @@ try
2239
{
2240
WSLCExecutionContext context(&m_session);
2241
2242
+ RETURN_HR_IF_NULL(E_POINTER, Id);
2243
+
2244
const auto hr = wil::ResultFromException([&] {
2245
auto [lock, impl] = LockImpl();
2246
WI_VERIFY(strcpy_s(Id, std::size<char>(WSLCContainerId{}), impl->ID().c_str()) == 0);
src/windows/wslcsession/WSLCProcess.cpp
+10
@@ -34,6 +34,8 @@ CATCH_RETURN();
34
HRESULT WSLCProcess::GetExitEvent(HANDLE* Event)
35
try
36
{
37
+ RETURN_HR_IF_NULL(E_POINTER, Event);
38
+
39
*Event = wsl::windows::common::wslutil::DuplicateHandle(m_control->GetExitEvent().get(), SYNCHRONIZE, FALSE);
40
return S_OK;
41
}
@@ -42,6 +44,7 @@ CATCH_RETURN();
44
HRESULT WSLCProcess::GetStdHandle(WSLCFD Fd, WSLCHandle* Handle)
45
try
46
{
47
+ RETURN_HR_IF_NULL(E_POINTER, Handle);
48
RETURN_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), !m_io, "Process IO not attached");
49
50
auto typedHandle = m_io->OpenFd(Fd);
@@ -68,6 +71,8 @@ CATCH_RETURN();
71
HRESULT WSLCProcess::GetFlags(WSLCProcessFlags* Flags)
72
try
73
{
74
+ RETURN_HR_IF_NULL(E_POINTER, Flags);
75
+
76
*Flags = m_flags;
77
return S_OK;
78
}
@@ -88,6 +93,8 @@ HANDLE WSLCProcess::GetExitEvent()
93
HRESULT WSLCProcess::GetPid(int* Pid)
94
try
95
{
96
+ RETURN_HR_IF_NULL(E_POINTER, Pid);
97
+
98
*Pid = m_control->GetPid();
99
return S_OK;
100
}
@@ -101,6 +108,9 @@ int WSLCProcess::GetPid() const
108
HRESULT WSLCProcess::GetState(WSLCProcessState* State, int* Code)
109
try
110
{
111
+ RETURN_HR_IF_NULL(E_POINTER, State);
112
+ RETURN_HR_IF_NULL(E_POINTER, Code);
113
+
114
std::tie(*State, *Code) = m_control->GetState();
115
return S_OK;
116
}
src/windows/wslcsession/WSLCSession.cpp
+36
-1
@@ -266,6 +266,14 @@ try
266
RETURN_HR_IF(E_POINTER, Settings == nullptr || Vm == nullptr);
267
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED), m_virtualMachine.has_value());
268
269
+ THROW_HR_IF_MSG(
270
+ E_INVALIDARG, WI_IsAnyFlagSet(Settings->FeatureFlags, ~WSLCFeatureFlagsValid), "Invalid feature flags: 0x%x", Settings->FeatureFlags);
271
+ THROW_HR_IF_MSG(
272
+ E_INVALIDARG,
273
+ WI_IsAnyFlagSet(Settings->StorageFlags, ~WSLCSessionStorageFlagsValid),
274
+ "Invalid storage flags: 0x%x",
275
+ Settings->StorageFlags);
276
+
277
// Set up a warning context for the duration of initialization so that non-fatal
278
// failures (e.g., container/volume/network recovery) are streamed to the CLI.
279
WSLCExecutionContext warningContext(this, WarningCallback);
@@ -439,6 +447,8 @@ void WSLCSession::ConfigureStorage(const WSLCSessionInitSettings& Settings, PSID
447
448
HRESULT WSLCSession::GetId(ULONG* Id)
449
{
450
+ RETURN_HR_IF_NULL(E_POINTER, Id);
451
+
452
*Id = m_id;
453
454
return S_OK;
@@ -1672,7 +1682,17 @@ try
1682
WSLCExecutionContext context(this, WarningCallback);
1683
THROW_HR_IF_NULL(E_POINTER, containerOptions);
1684
THROW_HR_IF_NULL(E_POINTER, Container);
1675
- THROW_HR_IF(E_POINTER, containerOptions->Image == nullptr);
1685
+ THROW_HR_IF_NULL(E_POINTER, containerOptions->Image);
1686
+ THROW_HR_IF_MSG(
1687
+ E_INVALIDARG,
1688
+ WI_IsAnyFlagSet(containerOptions->Flags, ~WSLCContainerFlagsValid),
1689
+ "Invalid container flags: 0x%x",
1690
+ containerOptions->Flags);
1691
+ THROW_HR_IF_MSG(
1692
+ E_INVALIDARG,
1693
+ WI_IsAnyFlagSet(containerOptions->InitProcessOptions.Flags, ~WSLCProcessFlagsValid),
1694
+ "Invalid process flags: 0x%x",
1695
+ containerOptions->InitProcessOptions.Flags);
1696
1697
auto lock = m_lock.lock_shared();
1698
@@ -1776,6 +1796,9 @@ try
1796
{
1797
WSLCExecutionContext context(this);
1798
1799
+ RETURN_HR_IF_NULL(E_POINTER, Id);
1800
+ RETURN_HR_IF_NULL(E_POINTER, Container);
1801
+
1802
ValidateName(Id, WSLC_MAX_CONTAINER_NAME_LENGTH);
1803
1804
// Look for an exact ID match first.
@@ -1991,6 +2014,11 @@ try
2014
{
2015
WSLCExecutionContext context(this);
2016
2017
+ THROW_HR_IF_NULL(E_POINTER, Executable);
2018
+ THROW_HR_IF_NULL(E_POINTER, Options);
2019
+ THROW_HR_IF_NULL(E_POINTER, Process);
2020
+ THROW_HR_IF_MSG(E_INVALIDARG, WI_IsAnyFlagSet(Options->Flags, ~WSLCProcessFlagsValid), "Invalid flags: 0x%x", Options->Flags);
2021
+
2022
if (Errno != nullptr)
2023
{
2024
*Errno = -1; // Make sure not to return 0 if something fails.
@@ -2600,6 +2628,9 @@ try
2628
{
2629
WSLCExecutionContext context(this);
2630
2631
+ RETURN_HR_IF_NULL(E_POINTER, WindowsPath);
2632
+ RETURN_HR_IF_NULL(E_POINTER, LinuxPath);
2633
+
2634
auto lock = m_lock.lock_shared();
2635
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
2636
@@ -2612,6 +2643,8 @@ try
2643
{
2644
WSLCExecutionContext context(this);
2645
2646
+ RETURN_HR_IF_NULL(E_POINTER, LinuxPath);
2647
+
2648
auto lock = m_lock.lock_shared();
2649
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
2650
@@ -2826,6 +2859,8 @@ void WSLCSession::OnContainerDeleted(const WSLCContainerImpl* Container)
2859
2860
HRESULT WSLCSession::GetState(_Out_ WSLCSessionState* State)
2861
{
2862
+ RETURN_HR_IF_NULL(E_POINTER, State);
2863
+
2864
*State = m_terminated ? WSLCSessionStateTerminated : WSLCSessionStateRunning;
2865
return S_OK;
2866
}
src/windows/wslcsession/WSLCSessionFactory.cpp
+5
@@ -38,6 +38,9 @@ HRESULT wslc::WSLCSessionFactory::CreateSession(
38
_Out_ IWSLCSessionReference** ServiceRef)
39
try
40
{
41
+ RETURN_HR_IF_NULL(E_POINTER, Session);
42
+ RETURN_HR_IF_NULL(E_POINTER, ServiceRef);
43
+
44
*Session = nullptr;
45
*ServiceRef = nullptr;
46
@@ -71,6 +74,8 @@ CATCH_RETURN()
74
HRESULT wslc::WSLCSessionFactory::GetProcessHandle(_Out_ HANDLE* ProcessHandle)
75
try
76
{
77
+ RETURN_HR_IF_NULL(E_POINTER, ProcessHandle);
78
+
79
wil::unique_handle process{OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, FALSE, GetCurrentProcessId())};
80
RETURN_LAST_ERROR_IF(!process);
81
src/windows/wslcsession/WSLCSessionReference.cpp
+2
@@ -33,6 +33,8 @@ wslc::WSLCSessionReference::~WSLCSessionReference() = default;
33
34
HRESULT wslc::WSLCSessionReference::OpenSession(_Out_ IWSLCSession** Session)
35
{
36
+ RETURN_HR_IF_NULL(E_POINTER, Session);
37
+
38
*Session = nullptr;
39
40
Microsoft::WRL::ComPtr<IWSLCSession> lockedSession;
test/windows/WSLCTests.cpp
+96
@@ -451,6 +451,39 @@ class WSLCTests
451
wil::com_ptr<IWSLCSession> session;
452
VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session), E_INVALIDARG);
453
}
454
+
455
+ // Reject invalid session flags.
456
+ {
457
+ auto settings = GetDefaultSessionSettings(L"invalid-session-flags");
458
+ wil::com_ptr<IWSLCSession> session;
459
+ VERIFY_ARE_EQUAL(E_INVALIDARG, sessionManager->CreateSession(&settings, static_cast<WSLCSessionFlags>(0x4), nullptr, &session));
460
+ }
461
+
462
+ // Reject invalid feature flags.
463
+ {
464
+ auto settings = GetDefaultSessionSettings(L"invalid-feature-flags");
465
+ settings.FeatureFlags = static_cast<WSLCFeatureFlags>(0x20);
466
+ wil::com_ptr<IWSLCSession> session;
467
+ VERIFY_ARE_EQUAL(E_INVALIDARG, sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session));
468
+ }
469
+
470
+ // Reject NULL output pointers across the session manager API.
471
+ {
472
+ auto settings = GetDefaultSessionSettings(L"null-out-session");
473
+ VERIFY_ARE_EQUAL(
474
+ HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, nullptr));
475
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), sessionManager->OpenSession(0, nullptr));
476
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), sessionManager->OpenSessionByName(c_testSessionName, nullptr));
477
+
478
+ WSLCSessionListEntry* entries = nullptr;
479
+ ULONG count = 0;
480
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), sessionManager->ListSessions(nullptr, &count));
481
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), sessionManager->ListSessions(&entries, nullptr));
482
+ }
483
+
484
+ // The session object must reject NULL output pointers.
485
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), m_defaultSession->GetId(nullptr));
486
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), m_defaultSession->GetState(nullptr));
487
}
488
489
struct VmInfo
@@ -3585,6 +3618,15 @@ class WSLCTests
3618
3619
WSLC_TEST_METHOD(CreateRootNamespaceProcess)
3620
{
3621
+ // Reject invalid process flags.
3622
+ {
3623
+ WSLCProcessOptions options{};
3624
+ options.Flags = static_cast<WSLCProcessFlags>(0x4);
3625
+ wil::com_ptr<IWSLCProcess> process;
3626
+ int err = 0;
3627
+ VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateRootNamespaceProcess("/bin/true", &options, &process, &err));
3628
+ }
3629
+
3630
// Simple case
3631
{
3632
auto result = ExpectCommandResult(m_defaultSession.get(), {"/bin/sh", "-c", "echo OK"}, 0);
@@ -3626,6 +3668,17 @@ class WSLCTests
3668
// Validate that a null out handle is rejected.
3669
3670
VERIFY_ARE_EQUAL(process.Get().GetStdHandle(WSLCFDStdout, nullptr), HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER));
3671
+
3672
+ // Validate that every IWSLCProcess output pointer is rejected when null.
3673
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), process.Get().GetExitEvent(nullptr));
3674
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), process.Get().GetPid(nullptr));
3675
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), process.Get().GetState(nullptr, nullptr));
3676
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), process.Get().GetFlags(nullptr));
3677
+
3678
+ // GetFlags succeeds with a valid pointer and reports the launched flags.
3679
+ WSLCProcessFlags flags{};
3680
+ VERIFY_SUCCEEDED(process.Get().GetFlags(&flags));
3681
+ VERIFY_IS_TRUE(WI_IsFlagSet(flags, WSLCProcessFlagsStdin));
3682
}
3683
3684
// Create a stuck process and kill it.
@@ -5108,6 +5161,25 @@ class WSLCTests
5161
VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), container.Get().GetInitProcess(nullptr));
5162
}
5163
5164
+ // Validate that CreateContainer rejects a null image and invalid flags.
5165
+ {
5166
+ WSLCContainerOptions options{};
5167
+ wil::com_ptr<IWSLCContainer> container;
5168
+
5169
+ // A null Image field is rejected with E_POINTER.
5170
+ VERIFY_ARE_EQUAL(E_POINTER, m_defaultSession->CreateContainer(&options, nullptr, &container));
5171
+
5172
+ // Invalid container flags are rejected with E_INVALIDARG.
5173
+ options.Image = "debian:latest";
5174
+ options.Flags = static_cast<WSLCContainerFlags>(0x10);
5175
+ VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateContainer(&options, nullptr, &container));
5176
+
5177
+ // Invalid init process flags are rejected with E_INVALIDARG.
5178
+ options.Flags = WSLCContainerFlagsNone;
5179
+ options.InitProcessOptions.Flags = static_cast<WSLCProcessFlags>(0x4);
5180
+ VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateContainer(&options, nullptr, &container));
5181
+ }
5182
+
5183
// Validate that env is correctly wired.
5184
{
5185
WSLCContainerLauncher launcher("debian:latest", "test-env", {"/bin/sh", "-c", "echo $testenv"}, {{"testenv=testvalue"}});
@@ -5600,6 +5672,13 @@ class WSLCTests
5672
5673
// Test error paths
5674
{
5675
+ // A null id and a null output pointer are rejected by the marshaller.
5676
+ {
5677
+ wil::com_ptr<IWSLCContainer> container;
5678
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), m_defaultSession->OpenContainer(nullptr, &container));
5679
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), m_defaultSession->OpenContainer("named-container", nullptr));
5680
+ }
5681
+
5682
expectOpen("", E_INVALIDARG);
5683
ValidateCOMErrorMessage(L"Invalid name: ''");
5684
@@ -6854,6 +6933,10 @@ class WSLCTests
6933
launcher.AddTmpfs("/mnt/wslc-tmpfs-inspect", "");
6934
6935
auto container = launcher.Launch(*m_defaultSession);
6936
+
6937
+ // Validate that inspect fails with a null pointer.
6938
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), container.Get().Inspect(nullptr));
6939
+
6940
auto details = container.Inspect();
6941
6942
// Verify basic container metadata.
@@ -6956,6 +7039,19 @@ class WSLCTests
7039
ValidateProcessOutput(process, {{1, "OK\n"}});
7040
}
7041
7042
+ // Validate that Exec rejects invalid flags and a null output pointer.
7043
+ {
7044
+ WSLCProcessOptions options{};
7045
+ wil::com_ptr<IWSLCProcess> process;
7046
+
7047
+ // A null output pointer is rejected by the marshaller.
7048
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), container.Get().Exec(&options, nullptr, nullptr));
7049
+
7050
+ // Invalid process flags are rejected with E_INVALIDARG.
7051
+ options.Flags = static_cast<WSLCProcessFlags>(0x4);
7052
+ VERIFY_ARE_EQUAL(E_INVALIDARG, container.Get().Exec(&options, nullptr, &process));
7053
+ }
7054
+
7055
// Validate that the working directory is correctly wired.
7056
{
7057
WSLCProcessLauncher launcher({}, {"pwd"});