VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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

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