Mount plugin folders on behalf of the user owning the wsl session (#41331)
Blue committed
Aug 12, 2026 at 21:28 UTC
78b9cf201df66ea470dc6de096ef6722905ba785
4 files changed
+79
-4
src/windows/service/exe/WslCoreVm.cpp
+2
-1
@@ -2064,7 +2064,8 @@ void WslCoreVm::MountRootNamespaceFolder(_In_ LPCWSTR HostPath, _In_ LPCWSTR Gue
2064
auto lock = m_lock.lock_exclusive();
2065
2066
const auto flags = (ReadOnly ? hcs::Plan9ShareFlags::ReadOnly : hcs::Plan9ShareFlags::None) | hcs::Plan9ShareFlags::AllowOptions;
2067
- wsl::windows::common::hcs::AddPlan9Share(m_system.get(), Name, Name, HostPath, LX_INIT_UTILITY_VM_PLAN9_PORT, flags);
2067
+ wsl::windows::common::hcs::AddPlan9Share(
2068
+ m_system.get(), Name, Name, HostPath, LX_INIT_UTILITY_VM_PLAN9_PORT, flags, m_userToken.get());
2069
2070
wsl::shared::MessageWriter<LX_MINI_INIT_MOUNT_FOLDER_MESSAGE> message(LxMiniInitMountFolder);
2071
message.WriteString(message->PathIndex, GuestPath);
test/windows/PluginTests.cpp
+58
-1
@@ -89,7 +89,7 @@ class PluginTests
89
return true;
90
}
91
92
- void ConfigurePlugin(PluginTestType testCase) const
92
+ void ConfigurePlugin(PluginTestType testCase, LPCWSTR mountFolder = L"") const
93
{
94
StopWslService();
95
if (!DeleteFile(logFile.c_str()))
@@ -100,6 +100,7 @@ class PluginTests
100
const auto testKey = OpenTestRegistryKey(KEY_SET_VALUE);
101
WriteDword(testKey.get(), nullptr, c_testType, static_cast<DWORD>(testCase));
102
WriteString(testKey.get(), nullptr, c_logFile, logFile.c_str());
103
+ WriteString(testKey.get(), nullptr, c_mountFolder, mountFolder);
104
105
const auto lxssKey =
106
CreateKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Lxss\\Plugins", KEY_SET_VALUE, nullptr, 0);
@@ -172,6 +173,62 @@ class PluginTests
173
ValidateLogFile(ExpectedOutput);
174
}
175
176
+ WSL2_TEST_METHOD(MountFolderAccess)
177
+ {
178
+ const auto testFolder = std::filesystem::current_path() / "deny-write";
179
+ VERIFY_IS_TRUE(std::filesystem::create_directory(testFolder));
180
+
181
+ auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { std::filesystem::remove_all(testFolder); });
182
+
183
+ const auto user = wil::get_token_information<TOKEN_USER>();
184
+ EXPLICIT_ACCESSW access{};
185
+ access.grfAccessPermissions = FILE_ADD_FILE;
186
+ access.grfAccessMode = DENY_ACCESS;
187
+ access.grfInheritance = NO_INHERITANCE;
188
+ access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
189
+ access.Trustee.ptstrName = static_cast<LPWSTR>(user->User.Sid);
190
+
191
+ PACL acl = nullptr;
192
+ wil::unique_hlocal descriptor;
193
+ THROW_IF_WIN32_ERROR(GetNamedSecurityInfoW(
194
+ testFolder.c_str(), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, &acl, nullptr, &descriptor));
195
+
196
+ wsl::windows::common::security::unique_acl newAcl;
197
+ THROW_IF_WIN32_ERROR(SetEntriesInAclW(1, &access, acl, &newAcl));
198
+ THROW_IF_WIN32_ERROR(SetNamedSecurityInfoW(
199
+ const_cast<LPWSTR>(testFolder.c_str()), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, newAcl.get(), nullptr));
200
+
201
+ const auto testFile = testFolder / L"plugin-test.txt";
202
+ wil::unique_hfile deniedFile{CreateFileW(testFile.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr)};
203
+ VERIFY_IS_TRUE(!deniedFile);
204
+ VERIFY_ARE_EQUAL(GetLastError(), ERROR_ACCESS_DENIED);
205
+
206
+ auto resetAcl = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
207
+ wsl::windows::common::security::unique_acl restoredAcl;
208
+ access.grfAccessPermissions = 0;
209
+ access.grfAccessMode = REVOKE_ACCESS;
210
+
211
+ THROW_IF_WIN32_ERROR(SetEntriesInAclW(1, &access, acl, &restoredAcl));
212
+
213
+ THROW_IF_WIN32_ERROR(SetNamedSecurityInfoW(
214
+ const_cast<LPWSTR>(testFolder.c_str()), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, restoredAcl.get(), nullptr));
215
+ });
216
+
217
+ ConfigurePlugin(PluginTestType::MountFolderAccess, testFolder.c_str());
218
+
219
+ constexpr auto ExpectedOutput =
220
+ LR"(Plugin loaded. TestMode=25
221
+ VM created (settings->CustomConfigurationFlags=0)
222
+ /bin/sh: line 1: /test-plugin-access/plugin-test.txt: Permission denied
223
+ Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=13
224
+ Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=13
225
+ VM Stopping)";
226
+
227
+ StartWsl(0);
228
+ VERIFY_IS_FALSE(std::filesystem::exists(testFile));
229
+ ValidateLogFile(ExpectedOutput);
230
+ }
231
+
232
WSL2_TEST_METHOD(CustomKernelOverriddenByPolicy)
233
{
234
RegistryKeyChange policy(
test/windows/PluginTests.h
+3
-1
@@ -44,11 +44,13 @@ enum class PluginTestType
44
WslcImagePull,
45
WslcVmRestart,
46
WslcVmStopCommitted,
47
- WslcVmNeverStarted
47
+ WslcVmNeverStarted,
48
+ MountFolderAccess
49
};
50
51
constexpr auto c_testType = L"TestType";
52
constexpr auto c_logFile = L"LogFile";
53
+constexpr auto c_mountFolder = L"MountFolder";
54
55
inline wil::unique_hkey OpenTestRegistryKey(REGSAM AccessMask)
56
{
test/windows/testplugin/Plugin.cpp
+16
-1
@@ -123,6 +123,21 @@ HRESULT OnVmStarted(const WSLSessionInformation* Session, const WSLVmCreationSet
123
return E_ABORT;
124
}
125
}
126
+ else if (g_testType == PluginTestType::MountFolderAccess)
127
+ {
128
+ const auto key = OpenTestRegistryKey(KEY_READ);
129
+ const auto mountSource = ReadString(key.get(), nullptr, c_mountFolder);
130
+
131
+ RETURN_IF_FAILED(
132
+ g_api->MountFolder(Session->SessionId, mountSource.c_str(), L"/test-plugin-access", false, L"test-plugin-access"));
133
+
134
+ std::vector<const char*> arguments = {"/bin/sh", "-c", "{ echo test > /test-plugin-access/plugin-test.txt; } 2>&1", nullptr};
135
+ wil::unique_socket socket;
136
+ RETURN_IF_FAILED(g_api->ExecuteBinary(Session->SessionId, arguments[0], arguments.data(), &socket));
137
+
138
+ const auto output = ReadFromSocket(socket.get());
139
+ g_logfile.write(output.data(), output.size());
140
+ }
141
else if (g_testType == PluginTestType::ApiErrors)
142
{
143
auto result = g_api->MountFolder(Session->SessionId, L"C:\\DoesNotExit", L"/dummy", true, L"test-plugin-mount");
@@ -787,7 +802,7 @@ EXTERN_C __declspec(dllexport) HRESULT WSLPLUGINAPI_ENTRYPOINTV1(const WSLPlugin
802
THROW_HR_IF(E_UNEXPECTED, !g_logfile);
803
804
g_testType = static_cast<PluginTestType>(ReadDword(key.get(), nullptr, c_testType, static_cast<DWORD>(PluginTestType::Invalid)));
790
- THROW_HR_IF(E_INVALIDARG, static_cast<DWORD>(g_testType) <= 0 || static_cast<DWORD>(g_testType) > static_cast<DWORD>(PluginTestType::WslcVmNeverStarted));
805
+ THROW_HR_IF(E_INVALIDARG, static_cast<DWORD>(g_testType) <= 0 || static_cast<DWORD>(g_testType) > static_cast<DWORD>(PluginTestType::MountFolderAccess));
806
807
g_logfile << "Plugin loaded. TestMode=" << static_cast<DWORD>(g_testType) << std::endl;
808
g_api = Api;