Bug 1761357 [wpt PR 33355] - Fix #33204: Move Safari stable runs to Big Sur, a=testonly
[gecko.git] / ipc / mscom / ProcessRuntime.cpp
blob2b4e613d75371bf3aa5e524564b1f89a32d97d4d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/mscom/ProcessRuntime.h"
9 #if defined(ACCESSIBILITY) && \
10 (defined(MOZILLA_INTERNAL_API) || defined(MOZ_HAS_MOZGLUE))
11 # include "mozilla/mscom/ActCtxResource.h"
12 #endif // defined(ACCESSIBILITY) && (defined(MOZILLA_INTERNAL_API) ||
13 // defined(MOZ_HAS_MOZGLUE))
14 #include "mozilla/Assertions.h"
15 #include "mozilla/DynamicallyLinkedFunctionPtr.h"
16 #include "mozilla/mscom/COMWrappers.h"
17 #include "mozilla/mscom/ProcessRuntimeShared.h"
18 #include "mozilla/RefPtr.h"
19 #include "mozilla/UniquePtr.h"
20 #include "mozilla/Unused.h"
21 #include "mozilla/Vector.h"
22 #include "mozilla/WindowsProcessMitigations.h"
23 #include "mozilla/WindowsVersion.h"
25 #if defined(MOZILLA_INTERNAL_API)
26 # include "mozilla/mscom/EnsureMTA.h"
27 # if defined(MOZ_SANDBOX)
28 # include "mozilla/sandboxTarget.h"
29 # endif // defined(MOZ_SANDBOX)
30 #endif // defined(MOZILLA_INTERNAL_API)
32 #include <accctrl.h>
33 #include <aclapi.h>
34 #include <objbase.h>
35 #include <objidl.h>
37 // This API from oleaut32.dll is not declared in Windows SDK headers
38 extern "C" void __cdecl SetOaNoCache(void);
40 using namespace mozilla::mscom::detail;
42 namespace mozilla {
43 namespace mscom {
45 #if defined(MOZILLA_INTERNAL_API)
46 ProcessRuntime* ProcessRuntime::sInstance = nullptr;
48 ProcessRuntime::ProcessRuntime() : ProcessRuntime(XRE_GetProcessType()) {}
50 ProcessRuntime::ProcessRuntime(const GeckoProcessType aProcessType)
51 : ProcessRuntime(aProcessType == GeckoProcessType_Default
52 ? ProcessCategory::GeckoBrowserParent
53 : ProcessCategory::GeckoChild) {}
54 #endif // defined(MOZILLA_INTERNAL_API)
56 ProcessRuntime::ProcessRuntime(const ProcessCategory aProcessCategory)
57 : mInitResult(CO_E_NOTINITIALIZED), mProcessCategory(aProcessCategory) {
58 #if defined(ACCESSIBILITY)
59 # if defined(MOZILLA_INTERNAL_API)
60 // If we're inside XUL, and we're the parent process, then we trust that
61 // this has already been initialized for us prior to XUL being loaded.
62 // Only required in the child if the Resource ID has been passed down.
63 if (aProcessCategory != ProcessCategory::GeckoBrowserParent &&
64 ActCtxResource::GetAccessibilityResourceId()) {
65 mActCtxRgn.emplace(ActCtxResource::GetAccessibilityResource());
67 # elif defined(MOZ_HAS_MOZGLUE)
68 // If we're here, then we're in mozglue and initializing this for the parent
69 // process.
70 MOZ_ASSERT(aProcessCategory == ProcessCategory::GeckoBrowserParent);
71 mActCtxRgn.emplace(ActCtxResource::GetAccessibilityResource());
72 # endif
73 #endif // defined(ACCESSIBILITY)
75 #if defined(MOZILLA_INTERNAL_API)
76 MOZ_DIAGNOSTIC_ASSERT(!sInstance);
77 sInstance = this;
79 EnsureMTA();
80 /**
81 * From this point forward, all threads in this process are implicitly
82 * members of the multi-threaded apartment, with the following exceptions:
83 * 1. If any Win32 GUI APIs were called on the current thread prior to
84 * executing this constructor, then this thread has already been implicitly
85 * initialized as the process's main STA thread; or
86 * 2. A thread explicitly and successfully calls CoInitialize(Ex) to specify
87 * otherwise.
90 const bool isCurThreadImplicitMTA = IsCurrentThreadImplicitMTA();
91 // We only assert that the implicit MTA precondition holds when not running
92 // as the Gecko parent process.
93 MOZ_DIAGNOSTIC_ASSERT(aProcessCategory ==
94 ProcessCategory::GeckoBrowserParent ||
95 isCurThreadImplicitMTA);
97 # if defined(MOZ_SANDBOX)
98 const bool isLockedDownChildProcess =
99 mProcessCategory == ProcessCategory::GeckoChild && IsWin32kLockedDown();
100 // If our process is running under Win32k lockdown, we cannot initialize
101 // COM with a single-threaded apartment. This is because STAs create a hidden
102 // window, which implicitly requires user32 and Win32k, which are blocked.
103 // Instead we start the multi-threaded apartment and conduct our process-wide
104 // COM initialization there.
105 if (isLockedDownChildProcess) {
106 // Make sure we're still running with the sandbox's privileged impersonation
107 // token.
108 HANDLE rawCurThreadImpToken;
109 if (!::OpenThreadToken(::GetCurrentThread(), TOKEN_DUPLICATE | TOKEN_QUERY,
110 FALSE, &rawCurThreadImpToken)) {
111 mInitResult = HRESULT_FROM_WIN32(::GetLastError());
112 return;
114 nsAutoHandle curThreadImpToken(rawCurThreadImpToken);
116 // Ensure that our current token is still an impersonation token (ie, we
117 // have not yet called RevertToSelf() on this thread).
118 DWORD len;
119 TOKEN_TYPE tokenType;
120 MOZ_RELEASE_ASSERT(
121 ::GetTokenInformation(rawCurThreadImpToken, TokenType, &tokenType,
122 sizeof(tokenType), &len) &&
123 len == sizeof(tokenType) && tokenType == TokenImpersonation);
125 // Ideally we want our current thread to be running implicitly inside the
126 // MTA, but if for some wacky reason we did not end up with that, we may
127 // compensate by completing initialization via EnsureMTA's persistent
128 // thread.
129 if (!isCurThreadImplicitMTA) {
130 InitUsingPersistentMTAThread(curThreadImpToken);
131 return;
134 # endif // defined(MOZ_SANDBOX)
135 #endif // defined(MOZILLA_INTERNAL_API)
137 mAptRegion.Init(GetDesiredApartmentType(mProcessCategory));
139 // It can happen that we are not the outermost COM initialization on this
140 // thread. In fact it should regularly be the case that the outermost
141 // initialization occurs from outside of XUL, before we show the skeleton UI,
142 // at which point we still need to run some things here from within XUL.
143 if (!mAptRegion.IsValidOutermost()) {
144 mInitResult = mAptRegion.GetHResult();
145 #if defined(MOZILLA_INTERNAL_API)
146 MOZ_ASSERT(mProcessCategory == ProcessCategory::GeckoBrowserParent);
147 if (mProcessCategory != ProcessCategory::GeckoBrowserParent) {
148 // This is unexpected unless we're GeckoBrowserParent
149 return;
152 ProcessInitLock lock;
154 // Is another instance of ProcessRuntime responsible for the outer
155 // initialization?
156 const bool prevInit =
157 lock.GetInitState() == ProcessInitState::FullyInitialized;
158 MOZ_ASSERT(prevInit);
159 if (prevInit) {
160 PostInit();
162 #endif // defined(MOZILLA_INTERNAL_API)
163 return;
166 InitInsideApartment();
167 if (FAILED(mInitResult)) {
168 return;
171 #if defined(MOZILLA_INTERNAL_API)
172 # if defined(MOZ_SANDBOX)
173 if (isLockedDownChildProcess) {
174 // In locked-down child processes, defer PostInit until priv drop
175 SandboxTarget::Instance()->RegisterSandboxStartCallback([self = this]() {
176 // Ensure that we're still live and the init was successful before
177 // calling PostInit()
178 if (self == sInstance && SUCCEEDED(self->mInitResult)) {
179 PostInit();
182 return;
184 # endif // defined(MOZ_SANDBOX)
186 PostInit();
187 #endif // defined(MOZILLA_INTERNAL_API)
190 #if defined(MOZILLA_INTERNAL_API)
191 ProcessRuntime::~ProcessRuntime() {
192 MOZ_DIAGNOSTIC_ASSERT(sInstance == this);
193 sInstance = nullptr;
196 # if defined(MOZ_SANDBOX)
197 void ProcessRuntime::InitUsingPersistentMTAThread(
198 const nsAutoHandle& aCurThreadToken) {
199 // Create an impersonation token based on the current thread's token
200 HANDLE rawMtaThreadImpToken = nullptr;
201 if (!::DuplicateToken(aCurThreadToken, SecurityImpersonation,
202 &rawMtaThreadImpToken)) {
203 mInitResult = HRESULT_FROM_WIN32(::GetLastError());
204 return;
206 nsAutoHandle mtaThreadImpToken(rawMtaThreadImpToken);
208 // Impersonate and initialize.
209 bool tokenSet = false;
210 EnsureMTA(
211 [this, rawMtaThreadImpToken, &tokenSet]() -> void {
212 if (!::SetThreadToken(nullptr, rawMtaThreadImpToken)) {
213 mInitResult = HRESULT_FROM_WIN32(::GetLastError());
214 return;
217 tokenSet = true;
218 InitInsideApartment();
220 EnsureMTA::Option::ForceDispatchToPersistentThread);
222 if (!tokenSet) {
223 return;
226 SandboxTarget::Instance()->RegisterSandboxStartCallback(
227 [self = this]() -> void {
228 EnsureMTA(
229 []() -> void {
230 // This is a security risk if it fails, so we release assert
231 MOZ_RELEASE_ASSERT(::RevertToSelf(),
232 "mscom::ProcessRuntime RevertToSelf failed");
234 EnsureMTA::Option::ForceDispatchToPersistentThread);
236 // Ensure that we're still live and the init was successful before
237 // calling PostInit()
238 if (self == sInstance && SUCCEEDED(self->mInitResult)) {
239 PostInit();
243 # endif // defined(MOZ_SANDBOX)
244 #endif // defined(MOZILLA_INTERNAL_API)
246 /* static */
247 COINIT ProcessRuntime::GetDesiredApartmentType(
248 const ProcessRuntime::ProcessCategory aProcessCategory) {
249 switch (aProcessCategory) {
250 case ProcessCategory::GeckoBrowserParent:
251 return COINIT_APARTMENTTHREADED;
252 case ProcessCategory::GeckoChild:
253 if (!IsWin32kLockedDown()) {
254 // If Win32k is not locked down then we probably still need STA.
255 // We disable DDE since that is not usable from child processes.
256 return static_cast<COINIT>(COINIT_APARTMENTTHREADED |
257 COINIT_DISABLE_OLE1DDE);
260 [[fallthrough]];
261 default:
262 return COINIT_MULTITHREADED;
266 void ProcessRuntime::InitInsideApartment() {
267 ProcessInitLock lock;
268 const ProcessInitState prevInitState = lock.GetInitState();
269 if (prevInitState == ProcessInitState::FullyInitialized) {
270 // COM has already been initialized by a previous ProcessRuntime instance
271 mInitResult = S_OK;
272 return;
275 if (prevInitState < ProcessInitState::PartialSecurityInitialized) {
276 // We are required to initialize security prior to configuring global
277 // options.
278 mInitResult = InitializeSecurity(mProcessCategory);
279 MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(mInitResult));
281 // Even though this isn't great, we should try to proceed even when
282 // CoInitializeSecurity has previously been called: the additional settings
283 // we want to change are important enough that we don't want to skip them.
284 if (FAILED(mInitResult) && mInitResult != RPC_E_TOO_LATE) {
285 return;
288 lock.SetInitState(ProcessInitState::PartialSecurityInitialized);
291 if (prevInitState < ProcessInitState::PartialGlobalOptions) {
292 RefPtr<IGlobalOptions> globalOpts;
293 mInitResult = wrapped::CoCreateInstance(
294 CLSID_GlobalOptions, nullptr, CLSCTX_INPROC_SERVER, IID_IGlobalOptions,
295 getter_AddRefs(globalOpts));
296 MOZ_ASSERT(SUCCEEDED(mInitResult));
297 if (FAILED(mInitResult)) {
298 return;
301 // Disable COM's catch-all exception handler
302 mInitResult = globalOpts->Set(COMGLB_EXCEPTION_HANDLING,
303 COMGLB_EXCEPTION_DONOT_HANDLE_ANY);
304 MOZ_ASSERT(SUCCEEDED(mInitResult));
305 if (FAILED(mInitResult)) {
306 return;
309 lock.SetInitState(ProcessInitState::PartialGlobalOptions);
312 // Disable the BSTR cache (as it never invalidates, thus leaking memory)
313 // (This function is itself idempotent, so we do not concern ourselves with
314 // tracking whether or not we've already called it.)
315 ::SetOaNoCache();
317 lock.SetInitState(ProcessInitState::FullyInitialized);
320 #if defined(MOZILLA_INTERNAL_API)
322 * Guaranteed to run *after* the COM (and possible sandboxing) initialization
323 * has successfully completed and stabilized. This method MUST BE IDEMPOTENT!
325 /* static */ void ProcessRuntime::PostInit() {
326 // Currently "roughed-in" but unused.
328 #endif // defined(MOZILLA_INTERNAL_API)
330 /* static */
331 DWORD
332 ProcessRuntime::GetClientThreadId() {
333 DWORD callerTid;
334 HRESULT hr = ::CoGetCallerTID(&callerTid);
335 // Don't return callerTid unless the call succeeded and returned S_FALSE,
336 // indicating that the caller originates from a different process.
337 if (hr != S_FALSE) {
338 return 0;
341 return callerTid;
344 /* static */
345 HRESULT
346 ProcessRuntime::InitializeSecurity(const ProcessCategory aProcessCategory) {
347 HANDLE rawToken = nullptr;
348 BOOL ok = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken);
349 if (!ok) {
350 return HRESULT_FROM_WIN32(::GetLastError());
352 nsAutoHandle token(rawToken);
354 DWORD len = 0;
355 ok = ::GetTokenInformation(token, TokenUser, nullptr, len, &len);
356 DWORD win32Error = ::GetLastError();
357 if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
358 return HRESULT_FROM_WIN32(win32Error);
361 auto tokenUserBuf = MakeUnique<BYTE[]>(len);
362 TOKEN_USER& tokenUser = *reinterpret_cast<TOKEN_USER*>(tokenUserBuf.get());
363 ok = ::GetTokenInformation(token, TokenUser, tokenUserBuf.get(), len, &len);
364 if (!ok) {
365 return HRESULT_FROM_WIN32(::GetLastError());
368 len = 0;
369 ok = ::GetTokenInformation(token, TokenPrimaryGroup, nullptr, len, &len);
370 win32Error = ::GetLastError();
371 if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
372 return HRESULT_FROM_WIN32(win32Error);
375 auto tokenPrimaryGroupBuf = MakeUnique<BYTE[]>(len);
376 TOKEN_PRIMARY_GROUP& tokenPrimaryGroup =
377 *reinterpret_cast<TOKEN_PRIMARY_GROUP*>(tokenPrimaryGroupBuf.get());
378 ok = ::GetTokenInformation(token, TokenPrimaryGroup,
379 tokenPrimaryGroupBuf.get(), len, &len);
380 if (!ok) {
381 return HRESULT_FROM_WIN32(::GetLastError());
384 SECURITY_DESCRIPTOR sd;
385 if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
386 return HRESULT_FROM_WIN32(::GetLastError());
389 BYTE systemSid[SECURITY_MAX_SID_SIZE];
390 DWORD systemSidSize = sizeof(systemSid);
391 if (!::CreateWellKnownSid(WinLocalSystemSid, nullptr, systemSid,
392 &systemSidSize)) {
393 return HRESULT_FROM_WIN32(::GetLastError());
396 BYTE adminSid[SECURITY_MAX_SID_SIZE];
397 DWORD adminSidSize = sizeof(adminSid);
398 if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, nullptr, adminSid,
399 &adminSidSize)) {
400 return HRESULT_FROM_WIN32(::GetLastError());
403 const bool allowAppContainers =
404 aProcessCategory == ProcessCategory::GeckoBrowserParent &&
405 IsWin8OrLater();
407 BYTE appContainersSid[SECURITY_MAX_SID_SIZE];
408 DWORD appContainersSidSize = sizeof(appContainersSid);
409 if (allowAppContainers) {
410 if (!::CreateWellKnownSid(WinBuiltinAnyPackageSid, nullptr,
411 appContainersSid, &appContainersSidSize)) {
412 return HRESULT_FROM_WIN32(::GetLastError());
416 // Grant access to SYSTEM, Administrators, the user, and when running as the
417 // browser process on Windows 8+, all app containers.
418 const size_t kMaxInlineEntries = 4;
419 mozilla::Vector<EXPLICIT_ACCESS_W, kMaxInlineEntries> entries;
421 Unused << entries.append(EXPLICIT_ACCESS_W{
422 COM_RIGHTS_EXECUTE,
423 GRANT_ACCESS,
424 NO_INHERITANCE,
425 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
426 reinterpret_cast<LPWSTR>(systemSid)}});
428 Unused << entries.append(EXPLICIT_ACCESS_W{
429 COM_RIGHTS_EXECUTE,
430 GRANT_ACCESS,
431 NO_INHERITANCE,
432 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID,
433 TRUSTEE_IS_WELL_KNOWN_GROUP, reinterpret_cast<LPWSTR>(adminSid)}});
435 Unused << entries.append(EXPLICIT_ACCESS_W{
436 COM_RIGHTS_EXECUTE,
437 GRANT_ACCESS,
438 NO_INHERITANCE,
439 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
440 reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}});
442 if (allowAppContainers) {
443 Unused << entries.append(
444 EXPLICIT_ACCESS_W{COM_RIGHTS_EXECUTE,
445 GRANT_ACCESS,
446 NO_INHERITANCE,
447 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID,
448 TRUSTEE_IS_WELL_KNOWN_GROUP,
449 reinterpret_cast<LPWSTR>(appContainersSid)}});
452 PACL rawDacl = nullptr;
453 win32Error =
454 ::SetEntriesInAclW(entries.length(), entries.begin(), nullptr, &rawDacl);
455 if (win32Error != ERROR_SUCCESS) {
456 return HRESULT_FROM_WIN32(win32Error);
459 UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl);
461 if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) {
462 return HRESULT_FROM_WIN32(::GetLastError());
465 if (!::SetSecurityDescriptorOwner(&sd, tokenUser.User.Sid, FALSE)) {
466 return HRESULT_FROM_WIN32(::GetLastError());
469 if (!::SetSecurityDescriptorGroup(&sd, tokenPrimaryGroup.PrimaryGroup,
470 FALSE)) {
471 return HRESULT_FROM_WIN32(::GetLastError());
474 return wrapped::CoInitializeSecurity(
475 &sd, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT,
476 RPC_C_IMP_LEVEL_IDENTIFY, nullptr, EOAC_NONE, nullptr);
479 } // namespace mscom
480 } // namespace mozilla