@samitouri / QOSAMI-WSL / commits / 78b9cf20

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;