VirtualBox

source: vbox/trunk/src/VBox/Main/glue/initterm.cpp@ 109128

Last change on this file since 109128 was 109067, checked in by vboxsync, 3 weeks ago

libs/xpcom,Main/glue,++: Bake the automatic re-registration of XPCOM components (in the components directory) into the NS_InitXPCOM2 call to avoid the problem of VBoxXPCOMIPCC.so/dylib being engaged at the end of NS_InitXPCOM2 and unable to re-register if this is done later by the client code. Added a fFlagsInit parameter to indicate whether this auto reg behaviour is required or not and renamed NS_InitXPCOM2 to NS_InitXPCOM2Ex (the latter to prevent confusion). bugref:10896 ticketref:22193

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.5 KB
Line 
1/* $Id: initterm.cpp 109067 2025-04-24 10:08:54Z vboxsync $ */
2/** @file
3 * MS COM / XPCOM Abstraction Layer - Initialization and Termination.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN
29#if !defined(VBOX_WITH_XPCOM)
30
31# if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600
32# undef _WIN32_WINNT
33# define _WIN32_WINNT 0x600 /* GetModuleHandleExW */
34# endif
35# include <iprt/nt/nt-and-windows.h>
36# include <iprt/win/objbase.h>
37# include <iprt/win/rpcproxy.h>
38# include <rpcasync.h>
39
40#else /* !defined(VBOX_WITH_XPCOM) */
41
42# include <stdlib.h>
43
44# include <nsIComponentRegistrar.h>
45# include <nsIServiceManager.h>
46# include <nsCOMPtr.h>
47# include <nsEventQueueUtils.h>
48# include <nsEmbedString.h>
49
50# include <nsILocalFile.h>
51# include <nsIDirectoryService.h>
52# include <nsDirectoryServiceDefs.h>
53
54#endif /* !defined(VBOX_WITH_XPCOM) */
55
56#include "VBox/com/com.h"
57#include "VBox/com/assert.h"
58#include "VBox/com/NativeEventQueue.h"
59#include "VBox/com/AutoLock.h"
60
61#include "../include/LoggingNew.h"
62
63#include <iprt/asm.h>
64#include <iprt/env.h>
65#include <iprt/ldr.h>
66#include <iprt/param.h>
67#include <iprt/path.h>
68#include <iprt/string.h>
69#include <iprt/system.h>
70#include <iprt/thread.h>
71
72#include <VBox/err.h>
73
74namespace com
75{
76
77#if defined(VBOX_WITH_XPCOM)
78
79class DirectoryServiceProvider : public nsIDirectoryServiceProvider
80{
81public:
82
83 NS_DECL_ISUPPORTS
84
85 DirectoryServiceProvider()
86 : mCompRegLocation(NULL), mXPTIDatLocation(NULL)
87 , mComponentDirLocation(NULL), mCurrProcDirLocation(NULL)
88 {}
89
90 virtual ~DirectoryServiceProvider();
91
92 HRESULT init(const char *aCompRegLocation,
93 const char *aXPTIDatLocation,
94 const char *aComponentDirLocation,
95 const char *aCurrProcDirLocation);
96
97 NS_DECL_NSIDIRECTORYSERVICEPROVIDER
98
99private:
100 /** @remarks This is not a UTF-8 string. */
101 char *mCompRegLocation;
102 /** @remarks This is not a UTF-8 string. */
103 char *mXPTIDatLocation;
104 /** @remarks This is not a UTF-8 string. */
105 char *mComponentDirLocation;
106 /** @remarks This is not a UTF-8 string. */
107 char *mCurrProcDirLocation;
108};
109
110NS_IMPL_ISUPPORTS1(DirectoryServiceProvider, nsIDirectoryServiceProvider)
111
112DirectoryServiceProvider::~DirectoryServiceProvider()
113{
114 if (mCompRegLocation)
115 {
116 RTStrFree(mCompRegLocation);
117 mCompRegLocation = NULL;
118 }
119 if (mXPTIDatLocation)
120 {
121 RTStrFree(mXPTIDatLocation);
122 mXPTIDatLocation = NULL;
123 }
124 if (mComponentDirLocation)
125 {
126 RTStrFree(mComponentDirLocation);
127 mComponentDirLocation = NULL;
128 }
129 if (mCurrProcDirLocation)
130 {
131 RTStrFree(mCurrProcDirLocation);
132 mCurrProcDirLocation = NULL;
133 }
134}
135
136/**
137 * @param aCompRegLocation Path to compreg.dat, in Utf8.
138 * @param aXPTIDatLocation Path to xpti.data, in Utf8.
139 */
140HRESULT
141DirectoryServiceProvider::init(const char *aCompRegLocation,
142 const char *aXPTIDatLocation,
143 const char *aComponentDirLocation,
144 const char *aCurrProcDirLocation)
145{
146 AssertReturn(aCompRegLocation, NS_ERROR_INVALID_ARG);
147 AssertReturn(aXPTIDatLocation, NS_ERROR_INVALID_ARG);
148
149/** @todo r=bird: Gotta check how this encoding stuff plays out on darwin!
150 * We get down to [VBoxNsxp]NS_NewNativeLocalFile and that file isn't
151 * nsLocalFileUnix.cpp on 32-bit darwin. On 64-bit darwin it's a question
152 * of what we're doing in IPRT and such... We should probably add a
153 * RTPathConvertToNative for use here. */
154 int vrc = RTStrUtf8ToCurrentCP(&mCompRegLocation, aCompRegLocation);
155 if (RT_SUCCESS(vrc))
156 vrc = RTStrUtf8ToCurrentCP(&mXPTIDatLocation, aXPTIDatLocation);
157 if (RT_SUCCESS(vrc) && aComponentDirLocation)
158 vrc = RTStrUtf8ToCurrentCP(&mComponentDirLocation, aComponentDirLocation);
159 if (RT_SUCCESS(vrc) && aCurrProcDirLocation)
160 vrc = RTStrUtf8ToCurrentCP(&mCurrProcDirLocation, aCurrProcDirLocation);
161
162 return RT_SUCCESS(vrc) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
163}
164
165NS_IMETHODIMP
166DirectoryServiceProvider::GetFile(const char *aProp,
167 PRBool *aPersistent,
168 nsIFile **aRetval)
169{
170 *aRetval = nsnull;
171 *aPersistent = PR_TRUE;
172
173 const char *fileLocation;
174 if (strcmp(aProp, NS_XPCOM_COMPONENT_REGISTRY_FILE) == 0)
175 fileLocation = mCompRegLocation;
176 else if (strcmp(aProp, NS_XPCOM_XPTI_REGISTRY_FILE) == 0)
177 fileLocation = mXPTIDatLocation;
178 else if (mComponentDirLocation && strcmp(aProp, NS_XPCOM_COMPONENT_DIR) == 0)
179 fileLocation = mComponentDirLocation;
180 else if (mCurrProcDirLocation && strcmp(aProp, NS_XPCOM_CURRENT_PROCESS_DIR) == 0)
181 fileLocation = mCurrProcDirLocation;
182 else
183 return NS_ERROR_FAILURE;
184
185 nsCOMPtr<nsILocalFile> localFile;
186 nsresult rv = NS_NewNativeLocalFile(nsEmbedCString(fileLocation),
187 PR_TRUE, getter_AddRefs(localFile));
188 if (NS_FAILED(rv))
189 return rv;
190
191 return localFile->QueryInterface(NS_GET_IID(nsIFile), (void **)aRetval);
192}
193
194/**
195 * Global XPCOM initialization flag (we maintain it ourselves since XPCOM
196 * doesn't provide such functionality)
197 */
198static bool volatile gIsXPCOMInitialized = false;
199
200/**
201 * Number of Initialize() calls on the main thread.
202 */
203static unsigned int gXPCOMInitCount = 0;
204
205#else /* !defined(VBOX_WITH_XPCOM) */
206
207/**
208 * Replacement function for the InvokeStub method for the IRundown stub.
209 */
210static HRESULT STDMETHODCALLTYPE
211Rundown_InvokeStub(IRpcStubBuffer *pThis, RPCOLEMESSAGE *pMsg, IRpcChannelBuffer *pBuf) RT_NOTHROW_DEF
212{
213 /*
214 * Our mission here is to prevent remote calls to methods #8 and #9,
215 * as these contain raw pointers to callback functions.
216 *
217 * Note! APIs like I_RpcServerInqTransportType, I_RpcBindingInqLocalClientPID
218 * and RpcServerInqCallAttributesW are not usable in this context without
219 * a rpc binding handle (latter two).
220 *
221 * P.S. In more recent windows versions, the buffer implements a interface
222 * IID_IRpcChannelBufferMarshalingContext (undocumented) which has a
223 * GetIMarshallingContextAttribute() method that will return the client PID
224 * when asking for attribute #0x8000000e.
225 */
226 uint32_t const iMethod = pMsg->iMethod & 0xffff; /* Uncertain, but there are hints that the upper bits are flags. */
227 HRESULT hrc;
228 if ( ( iMethod != 8
229 && iMethod != 9)
230 || (pMsg->rpcFlags & RPCFLG_LOCAL_CALL) )
231 hrc = CStdStubBuffer_Invoke(pThis, pMsg, pBuf);
232 else
233 {
234 LogRel(("Rundown_InvokeStub: Rejected call to CRundown::%s: rpcFlags=%#x cbBuffer=%#x dataRepresentation=%d buffer=%p:{%.*Rhxs} reserved1=%p reserved2={%p,%p,%p,%p,%p}\n",
235 pMsg->iMethod == 8 ? "DoCallback" : "DoNonreentrantCallback", pMsg->rpcFlags, pMsg->cbBuffer,
236 pMsg->dataRepresentation, pMsg->Buffer, RT_VALID_PTR(pMsg->Buffer) ? pMsg->cbBuffer : 0, pMsg->Buffer,
237 pMsg->reserved1, pMsg->reserved2[0], pMsg->reserved2[1], pMsg->reserved2[2], pMsg->reserved2[3], pMsg->reserved2[4]));
238 hrc = E_ACCESSDENIED;
239 }
240 return hrc;
241}
242
243/**
244 * Replacement function for the InvokeStub method for the IDLLHost stub.
245 */
246static HRESULT STDMETHODCALLTYPE
247DLLHost_InvokeStub(IRpcStubBuffer *pThis, RPCOLEMESSAGE *pMsg, IRpcChannelBuffer *pBuf) RT_NOTHROW_DEF
248{
249 /*
250 * Our mission here is to prevent remote calls to this interface as method #3
251 * contain a raw pointer an DllGetClassObject function. There are only that
252 * method in addition to the IUnknown stuff, and it's ASSUMED that it's
253 * process internal only (cross apartment stuff).
254 */
255 uint32_t const iMethod = pMsg->iMethod & 0xffff; /* Uncertain, but there are hints that the upper bits are flags. */
256 HRESULT hrc;
257 if (pMsg->rpcFlags & RPCFLG_LOCAL_CALL)
258 hrc = CStdStubBuffer_Invoke(pThis, pMsg, pBuf);
259 else
260 {
261 LogRel(("DLLHost_InvokeStub: Rejected call to CDLLHost::%s: iMethod=%#x rpcFlags=%#x cbBuffer=%#x dataRepresentation=%d buffer=%p:{%.*Rhxs} reserved1=%p reserved2={%p,%p,%p,%p,%p}\n",
262 iMethod == 0 ? "QueryInterface" :
263 iMethod == 1 ? "AddRef" :
264 iMethod == 2 ? "ReleaseRef" :
265 iMethod == 3 ? "DllGetClassObject" : "Unknown", pMsg->iMethod, pMsg->rpcFlags, pMsg->cbBuffer,
266 pMsg->dataRepresentation, pMsg->Buffer, RT_VALID_PTR(pMsg->Buffer) ? pMsg->cbBuffer : 0, pMsg->Buffer,
267 pMsg->reserved1, pMsg->reserved2[0], pMsg->reserved2[1], pMsg->reserved2[2], pMsg->reserved2[3], pMsg->reserved2[4]));
268 hrc = E_ACCESSDENIED;
269 }
270 return hrc;
271}
272
273/**
274 * Replaces the IRundown InvokeStub method with Rundown_InvokeStub so we can
275 * reject remote calls to a couple of misdesigned methods.
276 *
277 * Also replaces the IDLLHost for the same reasons.
278 */
279void PatchComBugs(void)
280{
281 static volatile bool s_fPatched = false;
282 if (s_fPatched)
283 return;
284
285 /*
286 * The combase.dll / ole32.dll is exporting a DllGetClassObject function
287 * that is implemented using NdrDllGetClassObject just like our own
288 * proxy/stub DLL. This means we can get at the stub interface lists,
289 * since what NdrDllGetClassObject has CStdPSFactoryBuffer as layout.
290 *
291 * Note! Tried using CoRegisterPSClsid instead of this mess, but no luck.
292 */
293 /* Locate the COM DLL, it must be loaded by now: */
294 HMODULE hmod = GetModuleHandleW(L"COMBASE.DLL");
295 if (!hmod)
296 hmod = GetModuleHandleW(L"OLE32.DLL"); /* w7 */
297 AssertReturnVoid(hmod != NULL);
298
299 /* Resolve the class getter: */
300 LPFNGETCLASSOBJECT pfnGetClassObject = (LPFNGETCLASSOBJECT)GetProcAddress(hmod, "DllGetClassObject");
301 AssertReturnVoid(pfnGetClassObject != NULL);
302
303 /* Get the factory instance: */
304 static const CLSID s_PSOlePrx32ClsId = {0x00000320,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
305 CStdPSFactoryBuffer *pFactoryBuffer = NULL;
306 HRESULT hrc = pfnGetClassObject(s_PSOlePrx32ClsId, IID_IPSFactoryBuffer, (void **)&pFactoryBuffer);
307 AssertMsgReturnVoid(SUCCEEDED(hrc), ("hrc=%Rhrc\n", hrc));
308 AssertReturnVoid(pFactoryBuffer != NULL);
309
310 /*
311 * Search thru the file list for the interface we want to patch.
312 */
313 static const IID s_IID_Rundown = {0x00000134,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
314 static const IID s_IID_DLLHost = {0x00000141,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
315 decltype(CStdStubBuffer_Invoke) *pfnInvoke = (decltype(pfnInvoke))GetProcAddress(hmod, "CStdStubBuffer_Invoke");
316 if (!pfnInvoke)
317 pfnInvoke = (decltype(pfnInvoke))GetProcAddress(GetModuleHandleW(L"RPCRT4.DLL"), "CStdStubBuffer_Invoke");
318
319 unsigned cPatched = 0;
320 unsigned cAlreadyPatched = 0;
321 Assert(pFactoryBuffer->pProxyFileList != NULL);
322 for (ProxyFileInfo const **ppCur = pFactoryBuffer->pProxyFileList; *ppCur != NULL; ppCur++)
323 {
324 ProxyFileInfo const *pCur = *ppCur;
325
326 if (pCur->pStubVtblList)
327 {
328 for (PCInterfaceStubVtblList const *ppCurStub = pCur->pStubVtblList; *ppCurStub != NULL; ppCurStub++)
329 {
330 PCInterfaceStubVtblList const pCurStub = *ppCurStub;
331 IID const *piid = pCurStub->header.piid;
332 if (piid)
333 {
334 if (IsEqualIID(*piid, s_IID_Rundown))
335 {
336 if (pCurStub->Vtbl.Invoke == pfnInvoke)
337 {
338 DWORD fOld = 0;
339 if (VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), PAGE_READWRITE, &fOld))
340 {
341 pCurStub->Vtbl.Invoke = Rundown_InvokeStub;
342 VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), fOld, &fOld);
343 cPatched++;
344 }
345 else
346 AssertMsgFailed(("%d\n", GetLastError()));
347 }
348 else
349 cAlreadyPatched++;
350 }
351 else if (IsEqualIID(*piid, s_IID_DLLHost))
352 {
353 if (pCurStub->Vtbl.Invoke == pfnInvoke)
354 {
355 DWORD fOld = 0;
356 if (VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), PAGE_READWRITE, &fOld))
357 {
358 pCurStub->Vtbl.Invoke = DLLHost_InvokeStub;
359 VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), fOld, &fOld);
360 cPatched++;
361 }
362 else
363 AssertMsgFailed(("%d\n", GetLastError()));
364 }
365 else
366 cAlreadyPatched++;
367 }
368 }
369 }
370 }
371 }
372
373 /* done */
374 pFactoryBuffer->lpVtbl->Release((IPSFactoryBuffer *)pFactoryBuffer);
375
376 /*
377 * If we patched anything we should try prevent being unloaded.
378 */
379 if (cPatched > 0)
380 {
381 s_fPatched = true;
382 HMODULE hmodSelf;
383 AssertLogRelMsg(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
384 (LPCWSTR)(uintptr_t)Rundown_InvokeStub, &hmodSelf),
385 ("last error: %u; Rundown_InvokeStub=%p\n", GetLastError(), Rundown_InvokeStub));
386 }
387 AssertLogRelMsg(cAlreadyPatched + cPatched >= 2,
388 ("COM patching of IRundown/IDLLHost failed! (%d+%d)\n", cAlreadyPatched, cPatched));
389}
390
391
392/**
393 * The COM main thread handle. (The first caller of com::Initialize().)
394 */
395static RTTHREAD volatile gCOMMainThread = NIL_RTTHREAD;
396
397/**
398 * Number of Initialize() calls on the main thread.
399 */
400static uint32_t gCOMMainInitCount = 0;
401
402#endif /* !defined(VBOX_WITH_XPCOM) */
403
404
405/**
406 * Initializes the COM runtime.
407 *
408 * This method must be called on each thread of the client application that
409 * wants to access COM facilities. The initialization must be performed before
410 * calling any other COM method or attempting to instantiate COM objects.
411 *
412 * On platforms using XPCOM, this method uses the following scheme to search for
413 * XPCOM runtime:
414 *
415 * 1. If the VBOX_APP_HOME environment variable is set, the path it specifies
416 * is used to search XPCOM libraries and components. If this method fails to
417 * initialize XPCOM runtime using this path, it will immediately return a
418 * failure and will NOT check for other paths as described below.
419 *
420 * 2. If VBOX_APP_HOME is not set, this methods tries the following paths in the
421 * given order:
422 *
423 * a) Compiled-in application data directory (as returned by
424 * RTPathAppPrivateArch())
425 * b) "/usr/lib/virtualbox" (Linux only)
426 * c) "/opt/VirtualBox" (Linux only)
427 *
428 * The first path for which the initialization succeeds will be used.
429 *
430 * On MS COM platforms, the COM runtime is provided by the system and does not
431 * need to be searched for.
432 *
433 * Once the COM subsystem is no longer necessary on a given thread, Shutdown()
434 * must be called to free resources allocated for it. Note that a thread may
435 * call Initialize() several times but for each of tese calls there must be a
436 * corresponding Shutdown() call.
437 *
438 * @return S_OK on success and a COM result code in case of failure.
439 */
440HRESULT Initialize(uint32_t fInitFlags /*=VBOX_COM_INIT_F_DEFAULT*/)
441{
442 HRESULT hrc = E_FAIL;
443
444#if !defined(VBOX_WITH_XPCOM)
445
446# ifdef VBOX_WITH_AUTO_COM_REG_UPDATE
447 /*
448 * First time we're called in a process, we refresh the VBox COM
449 * registrations. Use a global mutex to prevent updating when there are
450 * API users already active, as that could lead to a bit of a mess.
451 */
452 if ( (fInitFlags & VBOX_COM_INIT_F_AUTO_REG_UPDATE)
453 && gCOMMainThread == NIL_RTTHREAD)
454 {
455 SetLastError(ERROR_SUCCESS);
456 HANDLE hLeakIt = CreateMutexW(NULL/*pSecAttr*/, FALSE, L"Global\\VirtualBoxComLazyRegistrationMutant");
457 DWORD dwErr = GetLastError();
458 AssertMsg(dwErr == ERROR_SUCCESS || dwErr == ERROR_ALREADY_EXISTS || dwErr == ERROR_ACCESS_DENIED, ("%u\n", dwErr));
459 if (dwErr == ERROR_SUCCESS)
460 {
461 char szPath[RTPATH_MAX];
462 int vrc = RTPathAppPrivateArch(szPath, sizeof(szPath));
463 if (RT_SUCCESS(vrc))
464# ifndef VBOX_IN_32_ON_64_MAIN_API
465 vrc = RTPathAppend(szPath, sizeof(szPath), "VBoxProxyStub.dll");
466# else
467 vrc = RTPathAppend(szPath, sizeof(szPath), "x86\\VBoxProxyStub-x86.dll");
468# endif
469 if (RT_SUCCESS(vrc))
470 {
471 RTLDRMOD hMod;
472 vrc = RTLdrLoad(szPath, &hMod);
473 if (RT_SUCCESS(vrc))
474 {
475 union
476 {
477 void *pv;
478 DECLCALLBACKMEMBER(uint32_t, pfnRegUpdate,(void));
479 } u;
480 vrc = RTLdrGetSymbol(hMod, "VbpsUpdateRegistrations", &u.pv);
481 if (RT_SUCCESS(vrc))
482 u.pfnRegUpdate();
483 /* Just keep it loaded. */
484 }
485 }
486 Assert(hLeakIt != NULL); NOREF(hLeakIt);
487 }
488 }
489# endif
490
491 /*
492 * We initialize COM in GUI thread in STA, to be compliant with QT and
493 * OLE requirments (for example to allow D&D), while other threads
494 * initialized in regular MTA. To allow fast proxyless access from
495 * GUI thread to COM objects, we explicitly provide our COM objects
496 * with free threaded marshaller.
497 * !!!!! Please think twice before touching this code !!!!!
498 */
499 DWORD flags = fInitFlags & VBOX_COM_INIT_F_GUI
500 ? COINIT_APARTMENTTHREADED
501 | COINIT_SPEED_OVER_MEMORY
502 : COINIT_MULTITHREADED
503 | COINIT_DISABLE_OLE1DDE
504 | COINIT_SPEED_OVER_MEMORY;
505
506 hrc = CoInitializeEx(NULL, flags);
507
508 /* the overall result must be either S_OK or S_FALSE (S_FALSE means
509 * "already initialized using the same apartment model") */
510 AssertMsg(hrc == S_OK || hrc == S_FALSE, ("hrc=%08X\n", hrc));
511
512#if defined(VBOX_WITH_SDS)
513 // Setup COM Security to enable impersonation
514 HRESULT hrcGUICoInitializeSecurity = CoInitializeSecurity(NULL,
515 -1,
516 NULL,
517 NULL,
518 RPC_C_AUTHN_LEVEL_DEFAULT,
519 RPC_C_IMP_LEVEL_IMPERSONATE,
520 NULL,
521 EOAC_NONE,
522 NULL);
523 NOREF(hrcGUICoInitializeSecurity);
524 Assert(SUCCEEDED(hrcGUICoInitializeSecurity) || hrcGUICoInitializeSecurity == RPC_E_TOO_LATE);
525#endif
526
527 /*
528 * IRundown has unsafe two methods we need to patch to prevent remote access.
529 * Do that before we start using COM and open ourselves to possible attacks.
530 */
531 if (!(fInitFlags & VBOX_COM_INIT_F_NO_COM_PATCHING))
532 PatchComBugs();
533
534 /* To be flow compatible with the XPCOM case, we return here if this isn't
535 * the main thread or if it isn't its first initialization call.
536 * Note! CoInitializeEx and CoUninitialize does it's own reference
537 * counting, so this exercise is entirely for the EventQueue init. */
538 bool fRc;
539 RTTHREAD hSelf = RTThreadSelf();
540 if (hSelf != NIL_RTTHREAD)
541 ASMAtomicCmpXchgHandle(&gCOMMainThread, hSelf, NIL_RTTHREAD, fRc);
542 else
543 fRc = false;
544
545 if (fInitFlags & VBOX_COM_INIT_F_GUI)
546 Assert(RTThreadIsMain(hSelf));
547
548 if (!fRc)
549 {
550 if ( gCOMMainThread == hSelf
551 && SUCCEEDED(hrc))
552 gCOMMainInitCount++;
553
554 AssertComRC(hrc);
555 return hrc;
556 }
557 Assert(RTThreadIsMain(hSelf));
558
559 /* this is the first main thread initialization */
560 Assert(gCOMMainInitCount == 0);
561 if (SUCCEEDED(hrc))
562 gCOMMainInitCount = 1;
563
564#else /* !defined(VBOX_WITH_XPCOM) */
565
566 /* Unused here */
567 RT_NOREF(fInitFlags);
568
569 if (ASMAtomicXchgBool(&gIsXPCOMInitialized, true) == true)
570 {
571 /* XPCOM is already initialized on the main thread, no special
572 * initialization is necessary on additional threads. Just increase
573 * the init counter if it's a main thread again (to correctly support
574 * nested calls to Initialize()/Shutdown() for compatibility with
575 * Win32). */
576
577 nsCOMPtr<nsIEventQueue> eventQ;
578 hrc = NS_GetMainEventQ(getter_AddRefs(eventQ));
579
580 if (NS_SUCCEEDED(hrc))
581 {
582 PRBool isOnMainThread = PR_FALSE;
583 hrc = eventQ->IsOnCurrentThread(&isOnMainThread);
584 if (NS_SUCCEEDED(hrc) && isOnMainThread)
585 ++gXPCOMInitCount;
586 }
587
588 AssertComRC(hrc);
589 return hrc;
590 }
591 Assert(RTThreadIsMain(RTThreadSelf()));
592
593 /* this is the first initialization */
594 gXPCOMInitCount = 1;
595
596 /* prepare paths for registry files */
597 char szCompReg[RTPATH_MAX];
598 char szXptiDat[RTPATH_MAX];
599
600 int vrc = GetVBoxUserHomeDirectory(szCompReg, sizeof(szCompReg));
601 if (vrc == VERR_ACCESS_DENIED)
602 return NS_ERROR_FILE_ACCESS_DENIED;
603 AssertRCReturn(vrc, NS_ERROR_FAILURE);
604 vrc = RTStrCopy(szXptiDat, sizeof(szXptiDat), szCompReg);
605 AssertRCReturn(vrc, NS_ERROR_FAILURE);
606# ifdef VBOX_IN_32_ON_64_MAIN_API
607 vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg-x86.dat");
608 AssertRCReturn(vrc, NS_ERROR_FAILURE);
609 vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti-x86.dat");
610 AssertRCReturn(vrc, NS_ERROR_FAILURE);
611# else
612 vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg.dat");
613 AssertRCReturn(vrc, NS_ERROR_FAILURE);
614 vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti.dat");
615 AssertRCReturn(vrc, NS_ERROR_FAILURE);
616# endif
617
618 LogFlowFunc(("component registry : \"%s\"\n", szCompReg));
619 LogFlowFunc(("XPTI data file : \"%s\"\n", szXptiDat));
620
621 static const char *kAppPathsToProbe[] =
622 {
623 NULL, /* 0: will use VBOX_APP_HOME */
624 NULL, /* 1: will try RTPathAppPrivateArch(), correctly installed release builds will never go further */
625 NULL, /* 2: will try parent directory of RTPathAppPrivateArch(), only for testcases in non-hardened builds */
626 /* There used to be hard coded paths, but they only caused trouble
627 * because they often led to mixing of builds or even versions.
628 * If you feel tempted to add anything here, think again. They would
629 * only be used if option 1 would not work, which is a sign of a big
630 * problem, as it returns a fixed location defined at compile time.
631 * It is better to fail than blindly trying to cover the problem. */
632 };
633
634 /* Find out the directory where VirtualBox binaries are located */
635 for (size_t i = 0; i < RT_ELEMENTS(kAppPathsToProbe); ++ i)
636 {
637 char szAppHomeDir[RTPATH_MAX];
638
639 if (i == 0)
640 {
641 /* Use VBOX_APP_HOME if present */
642 vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_APP_HOME", szAppHomeDir, sizeof(szAppHomeDir), NULL);
643 if (vrc == VERR_ENV_VAR_NOT_FOUND)
644 continue;
645 AssertRC(vrc);
646 }
647 else if (i == 1)
648 {
649 /* Use RTPathAppPrivateArch() first */
650 vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir));
651 AssertRC(vrc);
652 }
653 else if (i == 2)
654 {
655# ifdef VBOX_WITH_HARDENING
656 continue;
657# else /* !VBOX_WITH_HARDENING */
658 /* Use parent of RTPathAppPrivateArch() if ends with "testcase" */
659 vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir));
660 AssertRC(vrc);
661 vrc = RTPathStripTrailingSlash(szAppHomeDir);
662 AssertRC(vrc);
663 char *filename = RTPathFilename(szAppHomeDir);
664 if (!filename || strcmp(filename, "testcase"))
665 continue;
666 RTPathStripFilename(szAppHomeDir);
667# endif /* !VBOX_WITH_HARDENING */
668 }
669 else
670 {
671 /* Iterate over all other paths */
672 RTStrCopy(szAppHomeDir, sizeof(szAppHomeDir), kAppPathsToProbe[i]);
673 vrc = VINF_SUCCESS;
674 }
675 if (RT_FAILURE(vrc))
676 {
677 hrc = NS_ERROR_FAILURE;
678 continue;
679 }
680 char szCompDir[RTPATH_MAX];
681 vrc = RTStrCopy(szCompDir, sizeof(szCompDir), szAppHomeDir);
682 if (RT_FAILURE(vrc))
683 {
684 hrc = NS_ERROR_FAILURE;
685 continue;
686 }
687 vrc = RTPathAppend(szCompDir, sizeof(szCompDir), "components");
688 if (RT_FAILURE(vrc))
689 {
690 hrc = NS_ERROR_FAILURE;
691 continue;
692 }
693 LogFlowFunc(("component directory : \"%s\"\n", szCompDir));
694
695 nsCOMPtr<DirectoryServiceProvider> dsProv;
696 dsProv = new DirectoryServiceProvider();
697 if (dsProv)
698 hrc = dsProv->init(szCompReg, szXptiDat, szCompDir, szAppHomeDir);
699 else
700 hrc = NS_ERROR_OUT_OF_MEMORY;
701 if (NS_FAILED(hrc))
702 break;
703
704 /* Setup the application path for NS_InitXPCOM2Ex. Note that we properly
705 * answer the NS_XPCOM_CURRENT_PROCESS_DIR query in our directory
706 * service provider but it seems to be activated after the directory
707 * service is used for the first time (see the source NS_InitXPCOM2Ex). So
708 * use the same value here to be on the safe side. */
709 nsCOMPtr <nsIFile> appDir;
710 {
711 char *appDirCP = NULL;
712 vrc = RTStrUtf8ToCurrentCP(&appDirCP, szAppHomeDir);
713 if (RT_SUCCESS(vrc))
714 {
715 nsCOMPtr<nsILocalFile> file;
716 hrc = NS_NewNativeLocalFile(nsEmbedCString(appDirCP), PR_FALSE, getter_AddRefs(file));
717 if (NS_SUCCEEDED(hrc))
718 appDir = do_QueryInterface(file, &hrc);
719
720 RTStrFree(appDirCP);
721 }
722 else
723 hrc = NS_ERROR_FAILURE;
724 }
725 if (NS_FAILED(hrc))
726 break;
727
728 /* Set VBOX_XPCOM_HOME to the same app path to make XPCOM sources that
729 * still use it instead of the directory service happy */
730 vrc = RTEnvSetEx(RTENV_DEFAULT, "VBOX_XPCOM_HOME", szAppHomeDir);
731 AssertRC(vrc);
732
733 /* Finally, initialize XPCOM */
734 {
735 nsCOMPtr<nsIServiceManager> serviceManager;
736 hrc = NS_InitXPCOM2Ex(getter_AddRefs(serviceManager), appDir, dsProv,
737 NS_INIT_XPCOM_F_AUTO_REGISTER_COMPONENTS_WITH_STATUS);
738 if (NS_SUCCEEDED(hrc))
739 {
740 /* We succeeded, stop probing paths */
741 LogFlowFunc(("Succeeded.\n"));
742 break;
743 }
744 }
745
746 /* clean up before the new try */
747 HRESULT hrc2 = NS_ShutdownXPCOM(nsnull);
748 if (SUCCEEDED(hrc))
749 hrc = hrc2;
750
751 if (i == 0)
752 {
753 /* We failed with VBOX_APP_HOME, don't probe other paths */
754 break;
755 }
756 }
757
758#endif /* !defined(VBOX_WITH_XPCOM) */
759
760 AssertComRCReturnRC(hrc);
761
762 // for both COM and XPCOM, we only get here if this is the main thread;
763 // only then initialize the autolock system (AutoLock.cpp)
764 Assert(RTThreadIsMain(RTThreadSelf()));
765 util::InitAutoLockSystem();
766
767 /*
768 * Init the main event queue (ASSUMES it cannot fail).
769 */
770 if (SUCCEEDED(hrc))
771 NativeEventQueue::init();
772
773 return hrc;
774}
775
776HRESULT Shutdown()
777{
778 HRESULT hrc = S_OK;
779
780#if !defined(VBOX_WITH_XPCOM)
781
782 /* EventQueue::uninit reference counting fun. */
783 RTTHREAD hSelf = RTThreadSelf();
784 if ( hSelf == gCOMMainThread
785 && hSelf != NIL_RTTHREAD)
786 {
787 if (-- gCOMMainInitCount == 0)
788 {
789 NativeEventQueue::uninit();
790 ASMAtomicWriteHandle(&gCOMMainThread, NIL_RTTHREAD);
791 }
792 }
793
794 CoUninitialize();
795
796#else /* !defined(VBOX_WITH_XPCOM) */
797
798 nsCOMPtr<nsIEventQueue> eventQ;
799 hrc = NS_GetMainEventQ(getter_AddRefs(eventQ));
800
801 if (NS_SUCCEEDED(hrc) || hrc == NS_ERROR_NOT_AVAILABLE)
802 {
803 /* NS_ERROR_NOT_AVAILABLE seems to mean that
804 * nsIEventQueue::StopAcceptingEvents() has been called (see
805 * nsEventQueueService.cpp). We hope that this error code always means
806 * just that in this case and assume that we're on the main thread
807 * (it's a kind of unexpected behavior if a non-main thread ever calls
808 * StopAcceptingEvents() on the main event queue). */
809
810 PRBool isOnMainThread = PR_FALSE;
811 if (NS_SUCCEEDED(hrc))
812 {
813 hrc = eventQ->IsOnCurrentThread(&isOnMainThread);
814 eventQ = nsnull; /* early release before shutdown */
815 }
816 else
817 {
818 isOnMainThread = RTThreadIsMain(RTThreadSelf());
819 hrc = NS_OK;
820 }
821
822 if (NS_SUCCEEDED(hrc) && isOnMainThread)
823 {
824 /* only the main thread needs to uninitialize XPCOM and only if
825 * init counter drops to zero */
826 if (--gXPCOMInitCount == 0)
827 {
828 NativeEventQueue::uninit();
829 hrc = NS_ShutdownXPCOM(nsnull);
830
831 /* This is a thread initialized XPCOM and set gIsXPCOMInitialized to
832 * true. Reset it back to false. */
833 bool wasInited = ASMAtomicXchgBool(&gIsXPCOMInitialized, false);
834 Assert(wasInited == true);
835 NOREF(wasInited);
836 }
837 }
838 }
839
840#endif /* !defined(VBOX_WITH_XPCOM) */
841
842 AssertComRC(hrc);
843
844 return hrc;
845}
846
847} /* namespace com */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette