/src/mozilla-central/ipc/glue/GeckoChildProcessHost.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #include "GeckoChildProcessHost.h" |
8 | | |
9 | | #include "base/command_line.h" |
10 | | #include "base/string_util.h" |
11 | | #include "base/task.h" |
12 | | #include "chrome/common/chrome_switches.h" |
13 | | #include "chrome/common/process_watcher.h" |
14 | | #ifdef MOZ_WIDGET_COCOA |
15 | | #include "chrome/common/mach_ipc_mac.h" |
16 | | #include "base/rand_util.h" |
17 | | #include "nsILocalFileMac.h" |
18 | | #include "SharedMemoryBasic.h" |
19 | | #endif |
20 | | |
21 | | #include "MainThreadUtils.h" |
22 | | #include "mozilla/Sprintf.h" |
23 | | #include "prenv.h" |
24 | | #include "nsXPCOMPrivate.h" |
25 | | |
26 | | #if defined(MOZ_CONTENT_SANDBOX) |
27 | | #include "mozilla/SandboxSettings.h" |
28 | | #include "nsAppDirectoryServiceDefs.h" |
29 | | #endif |
30 | | |
31 | | #include "nsExceptionHandler.h" |
32 | | |
33 | | #include "nsDirectoryServiceDefs.h" |
34 | | #include "nsIFile.h" |
35 | | #include "nsPrintfCString.h" |
36 | | |
37 | | #include "mozilla/ClearOnShutdown.h" |
38 | | #include "mozilla/ipc/BrowserProcessSubThread.h" |
39 | | #include "mozilla/ipc/EnvironmentMap.h" |
40 | | #include "mozilla/Omnijar.h" |
41 | | #include "mozilla/Telemetry.h" |
42 | | #include "ProtocolUtils.h" |
43 | | #include <sys/stat.h> |
44 | | |
45 | | #ifdef XP_WIN |
46 | | #include "nsIWinTaskbar.h" |
47 | | #include <stdlib.h> |
48 | | #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1" |
49 | | |
50 | | #if defined(MOZ_SANDBOX) |
51 | | #include "mozilla/Preferences.h" |
52 | | #include "mozilla/sandboxing/sandboxLogging.h" |
53 | | #include "WinUtils.h" |
54 | | #endif |
55 | | #endif |
56 | | |
57 | | #if defined(XP_LINUX) && defined(MOZ_SANDBOX) |
58 | | #include "mozilla/SandboxLaunch.h" |
59 | | #endif |
60 | | |
61 | | #include "nsTArray.h" |
62 | | #include "nsClassHashtable.h" |
63 | | #include "nsHashKeys.h" |
64 | | #include "nsNativeCharsetUtils.h" |
65 | | #include "nscore.h" // for NS_FREE_PERMANENT_DATA |
66 | | #include "private/pprio.h" |
67 | | |
68 | | using mozilla::MonitorAutoLock; |
69 | | using mozilla::ipc::GeckoChildProcessHost; |
70 | | |
71 | | #ifdef MOZ_WIDGET_ANDROID |
72 | | #include "AndroidBridge.h" |
73 | | #include "GeneratedJNIWrappers.h" |
74 | | #include "mozilla/jni/Refs.h" |
75 | | #include "mozilla/jni/Utils.h" |
76 | | #endif |
77 | | |
78 | | static bool |
79 | | ShouldHaveDirectoryService() |
80 | 0 | { |
81 | 0 | return GeckoProcessType_Default == XRE_GetProcessType(); |
82 | 0 | } |
83 | | |
84 | | GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType, |
85 | | bool aIsFileContent) |
86 | | : mProcessType(aProcessType), |
87 | | mIsFileContent(aIsFileContent), |
88 | | mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"), |
89 | | mLaunchOptions(MakeUnique<base::LaunchOptions>()), |
90 | | mProcessState(CREATING_CHANNEL), |
91 | | #ifdef XP_WIN |
92 | | mGroupId(u"-"), |
93 | | #endif |
94 | | #if defined(MOZ_SANDBOX) && defined(XP_WIN) |
95 | | mEnableSandboxLogging(false), |
96 | | mSandboxLevel(0), |
97 | | #endif |
98 | | mChildProcessHandle(0) |
99 | | #if defined(MOZ_WIDGET_COCOA) |
100 | | , mChildTask(MACH_PORT_NULL) |
101 | | #endif |
102 | 0 | { |
103 | 0 | MOZ_COUNT_CTOR(GeckoChildProcessHost); |
104 | 0 | } |
105 | | |
106 | | GeckoChildProcessHost::~GeckoChildProcessHost() |
107 | | |
108 | 0 | { |
109 | 0 | AssertIOThread(); |
110 | 0 |
|
111 | 0 | MOZ_COUNT_DTOR(GeckoChildProcessHost); |
112 | 0 |
|
113 | 0 | if (mChildProcessHandle != 0) { |
114 | | #if defined(MOZ_WIDGET_COCOA) |
115 | | SharedMemoryBasic::CleanupForPid(mChildProcessHandle); |
116 | | #endif |
117 | | ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle |
118 | | #ifdef NS_FREE_PERMANENT_DATA |
119 | | // If we're doing leak logging, shutdown can be slow. |
120 | | , false // don't "force" |
121 | | #endif |
122 | | ); |
123 | 0 | } |
124 | 0 |
|
125 | | #if defined(MOZ_WIDGET_COCOA) |
126 | | if (mChildTask != MACH_PORT_NULL) |
127 | | mach_port_deallocate(mach_task_self(), mChildTask); |
128 | | #endif |
129 | |
|
130 | 0 | if (mChildProcessHandle != 0) { |
131 | | #if defined(XP_WIN) |
132 | | CrashReporter::DeregisterChildCrashAnnotationFileDescriptor( |
133 | | base::GetProcId(mChildProcessHandle)); |
134 | | #else |
135 | | CrashReporter::DeregisterChildCrashAnnotationFileDescriptor( |
136 | 0 | mChildProcessHandle); |
137 | 0 | #endif |
138 | 0 | } |
139 | 0 | } |
140 | | |
141 | | //static |
142 | | auto |
143 | | GeckoChildProcessHost::GetPathToBinary(FilePath& exePath, GeckoProcessType processType) -> BinaryPathType |
144 | 0 | { |
145 | 0 | if (sRunSelfAsContentProc && |
146 | 0 | (processType == GeckoProcessType_Content || processType == GeckoProcessType_GPU || |
147 | 0 | processType == GeckoProcessType_VR)) { |
148 | | #if defined(OS_WIN) |
149 | | wchar_t exePathBuf[MAXPATHLEN]; |
150 | | if (!::GetModuleFileNameW(nullptr, exePathBuf, MAXPATHLEN)) { |
151 | | MOZ_CRASH("GetModuleFileNameW failed (FIXME)"); |
152 | | } |
153 | | #if defined(MOZ_SANDBOX) |
154 | | // We need to start the child process using the real path, so that the |
155 | | // sandbox policy rules will match for DLLs loaded from the bin dir after |
156 | | // we have lowered the sandbox. |
157 | | std::wstring exePathStr = exePathBuf; |
158 | | if (widget::WinUtils::ResolveJunctionPointsAndSymLinks(exePathStr)) { |
159 | | exePath = FilePath::FromWStringHack(exePathStr); |
160 | | } else |
161 | | #endif |
162 | | { |
163 | | exePath = FilePath::FromWStringHack(exePathBuf); |
164 | | } |
165 | | #elif defined(OS_POSIX) |
166 | | exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]); |
167 | | #else |
168 | | # error Sorry; target OS not supported yet. |
169 | | #endif |
170 | | return BinaryPathType::Self; |
171 | 0 | } |
172 | 0 |
|
173 | 0 | if (ShouldHaveDirectoryService()) { |
174 | 0 | MOZ_ASSERT(gGREBinPath); |
175 | | #ifdef OS_WIN |
176 | | exePath = FilePath(char16ptr_t(gGREBinPath)); |
177 | | #elif MOZ_WIDGET_COCOA |
178 | | nsCOMPtr<nsIFile> childProcPath; |
179 | | NS_NewLocalFile(nsDependentString(gGREBinPath), false, |
180 | | getter_AddRefs(childProcPath)); |
181 | | |
182 | | // We need to use an App Bundle on OS X so that we can hide |
183 | | // the dock icon. See Bug 557225. |
184 | | childProcPath->AppendNative(NS_LITERAL_CSTRING("plugin-container.app")); |
185 | | childProcPath->AppendNative(NS_LITERAL_CSTRING("Contents")); |
186 | | childProcPath->AppendNative(NS_LITERAL_CSTRING("MacOS")); |
187 | | nsCString tempCPath; |
188 | | childProcPath->GetNativePath(tempCPath); |
189 | | exePath = FilePath(tempCPath.get()); |
190 | | #else |
191 | | nsCString path; |
192 | 0 | NS_CopyUnicodeToNative(nsDependentString(gGREBinPath), path); |
193 | 0 | exePath = FilePath(path.get()); |
194 | 0 | #endif |
195 | 0 | } |
196 | 0 |
|
197 | 0 | if (exePath.empty()) { |
198 | | #ifdef OS_WIN |
199 | | exePath = FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program()); |
200 | | #else |
201 | | exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]); |
202 | 0 | #endif |
203 | 0 | exePath = exePath.DirName(); |
204 | 0 | } |
205 | 0 |
|
206 | 0 | exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME); |
207 | 0 |
|
208 | 0 | return BinaryPathType::PluginContainer; |
209 | 0 | } |
210 | | |
211 | | #ifdef MOZ_WIDGET_COCOA |
212 | | class AutoCFTypeObject { |
213 | | public: |
214 | | explicit AutoCFTypeObject(CFTypeRef object) |
215 | | { |
216 | | mObject = object; |
217 | | } |
218 | | ~AutoCFTypeObject() |
219 | | { |
220 | | ::CFRelease(mObject); |
221 | | } |
222 | | private: |
223 | | CFTypeRef mObject; |
224 | | }; |
225 | | #endif |
226 | | |
227 | | // We start the unique IDs at 1 so that 0 can be used to mean that |
228 | | // a component has no unique ID assigned to it. |
229 | | uint32_t GeckoChildProcessHost::sNextUniqueID = 1; |
230 | | |
231 | | /* static */ |
232 | | uint32_t |
233 | | GeckoChildProcessHost::GetUniqueID() |
234 | 0 | { |
235 | 0 | return sNextUniqueID++; |
236 | 0 | } |
237 | | |
238 | | void |
239 | | GeckoChildProcessHost::PrepareLaunch() |
240 | 0 | { |
241 | 0 | if (CrashReporter::GetEnabled()) { |
242 | 0 | CrashReporter::OOPInit(); |
243 | 0 | } |
244 | 0 |
|
245 | 0 | #if defined(XP_LINUX) && defined(MOZ_SANDBOX) |
246 | 0 | SandboxLaunchPrepare(mProcessType, mLaunchOptions.get()); |
247 | 0 | #endif |
248 | 0 |
|
249 | | #ifdef XP_WIN |
250 | | if (mProcessType == GeckoProcessType_Plugin) { |
251 | | InitWindowsGroupID(); |
252 | | } |
253 | | |
254 | | #if defined(MOZ_CONTENT_SANDBOX) |
255 | | // We need to get the pref here as the process is launched off main thread. |
256 | | if (mProcessType == GeckoProcessType_Content) { |
257 | | mSandboxLevel = GetEffectiveContentSandboxLevel(); |
258 | | mEnableSandboxLogging = |
259 | | Preferences::GetBool("security.sandbox.logging.enabled"); |
260 | | |
261 | | // We currently have to whitelist certain paths for tests to work in some |
262 | | // development configurations. |
263 | | nsAutoString readPaths; |
264 | | nsresult rv = |
265 | | Preferences::GetString("security.sandbox.content.read_path_whitelist", |
266 | | readPaths); |
267 | | if (NS_SUCCEEDED(rv)) { |
268 | | for (const nsAString& readPath : readPaths.Split(',')) { |
269 | | nsString trimmedPath(readPath); |
270 | | trimmedPath.Trim(" ", true, true); |
271 | | std::wstring resolvedPath(trimmedPath.Data()); |
272 | | // Before resolving check if path ends with '\' as this indicates we |
273 | | // want to give read access to a directory and so it needs a wildcard. |
274 | | bool addWildcard = (resolvedPath.back() == L'\\'); |
275 | | if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(resolvedPath)) { |
276 | | NS_ERROR("Failed to resolve test read policy rule."); |
277 | | continue; |
278 | | } |
279 | | |
280 | | if (addWildcard) { |
281 | | resolvedPath.append(L"\\*"); |
282 | | } |
283 | | mAllowedFilesRead.push_back(resolvedPath); |
284 | | } |
285 | | } |
286 | | } |
287 | | #endif |
288 | | |
289 | | #if defined(MOZ_SANDBOX) |
290 | | // For other process types we can't rely on them being launched on main |
291 | | // thread and they may not have access to prefs in the child process, so allow |
292 | | // them to turn on logging via an environment variable. |
293 | | mEnableSandboxLogging = mEnableSandboxLogging |
294 | | || !!PR_GetEnv("MOZ_SANDBOX_LOGGING"); |
295 | | #endif |
296 | | #elif defined(XP_LINUX) |
297 | | #if defined(MOZ_CONTENT_SANDBOX) |
298 | 0 | // Get and remember the path to the per-content-process tmpdir |
299 | 0 | if (ShouldHaveDirectoryService()) { |
300 | 0 | nsCOMPtr<nsIFile> contentTempDir; |
301 | 0 | nsresult rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR, |
302 | 0 | getter_AddRefs(contentTempDir)); |
303 | 0 | if (NS_SUCCEEDED(rv)) { |
304 | 0 | contentTempDir->GetNativePath(mTmpDirName); |
305 | 0 | } |
306 | 0 | } |
307 | 0 | #endif |
308 | 0 | #endif |
309 | 0 | } |
310 | | |
311 | | #ifdef XP_WIN |
312 | | void GeckoChildProcessHost::InitWindowsGroupID() |
313 | | { |
314 | | // On Win7+, pass the application user model to the child, so it can |
315 | | // register with it. This insures windows created by the container |
316 | | // properly group with the parent app on the Win7 taskbar. |
317 | | nsCOMPtr<nsIWinTaskbar> taskbarInfo = |
318 | | do_GetService(NS_TASKBAR_CONTRACTID); |
319 | | if (taskbarInfo) { |
320 | | bool isSupported = false; |
321 | | taskbarInfo->GetAvailable(&isSupported); |
322 | | nsAutoString appId; |
323 | | if (isSupported && NS_SUCCEEDED(taskbarInfo->GetDefaultGroupId(appId))) { |
324 | | MOZ_ASSERT(mGroupId.EqualsLiteral("-")); |
325 | | mGroupId.Assign(appId); |
326 | | } |
327 | | } |
328 | | } |
329 | | #endif |
330 | | |
331 | | bool |
332 | | GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs) |
333 | 0 | { |
334 | 0 | if (!AsyncLaunch(std::move(aExtraOpts))) { |
335 | 0 | return false; |
336 | 0 | } |
337 | 0 | return WaitUntilConnected(aTimeoutMs); |
338 | 0 | } |
339 | | |
340 | | bool |
341 | | GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts) |
342 | 0 | { |
343 | 0 | PrepareLaunch(); |
344 | 0 |
|
345 | 0 | MessageLoop* ioLoop = XRE_GetIOMessageLoop(); |
346 | 0 |
|
347 | 0 | // Currently this can't fail (see the MOZ_ALWAYS_SUCCEEDS in |
348 | 0 | // MessageLoop::PostTask_Helper), but in the future it possibly |
349 | 0 | // could, in which case this method could return false. |
350 | 0 | ioLoop->PostTask(NewNonOwningRunnableMethod<std::vector<std::string>>( |
351 | 0 | "ipc::GeckoChildProcessHost::RunPerformAsyncLaunch", |
352 | 0 | this, |
353 | 0 | &GeckoChildProcessHost::RunPerformAsyncLaunch, |
354 | 0 | aExtraOpts)); |
355 | 0 |
|
356 | 0 | return true; |
357 | 0 | } |
358 | | |
359 | | bool |
360 | | GeckoChildProcessHost::WaitUntilConnected(int32_t aTimeoutMs) |
361 | 0 | { |
362 | 0 | AUTO_PROFILER_LABEL("GeckoChildProcessHost::WaitUntilConnected", OTHER); |
363 | 0 |
|
364 | 0 | // NB: this uses a different mechanism than the chromium parent |
365 | 0 | // class. |
366 | 0 | TimeDuration timeout = (aTimeoutMs > 0) ? |
367 | 0 | TimeDuration::FromMilliseconds(aTimeoutMs) : TimeDuration::Forever(); |
368 | 0 |
|
369 | 0 | MonitorAutoLock lock(mMonitor); |
370 | 0 | TimeStamp waitStart = TimeStamp::Now(); |
371 | 0 | TimeStamp current; |
372 | 0 |
|
373 | 0 | // We'll receive several notifications, we need to exit when we |
374 | 0 | // have either successfully launched or have timed out. |
375 | 0 | while (mProcessState != PROCESS_CONNECTED) { |
376 | 0 | // If there was an error then return it, don't wait out the timeout. |
377 | 0 | if (mProcessState == PROCESS_ERROR) { |
378 | 0 | break; |
379 | 0 | } |
380 | 0 | |
381 | 0 | CVStatus status = lock.Wait(timeout); |
382 | 0 | if (status == CVStatus::Timeout) { |
383 | 0 | break; |
384 | 0 | } |
385 | 0 | |
386 | 0 | if (timeout != TimeDuration::Forever()) { |
387 | 0 | current = TimeStamp::Now(); |
388 | 0 | timeout -= current - waitStart; |
389 | 0 | waitStart = current; |
390 | 0 | } |
391 | 0 | } |
392 | 0 |
|
393 | 0 | return mProcessState == PROCESS_CONNECTED; |
394 | 0 | } |
395 | | |
396 | | bool |
397 | | GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts) |
398 | 0 | { |
399 | 0 | if (!AsyncLaunch(std::move(aExtraOpts))) { |
400 | 0 | return false; |
401 | 0 | } |
402 | 0 | |
403 | 0 | MonitorAutoLock lock(mMonitor); |
404 | 0 | while (mProcessState < PROCESS_CREATED) { |
405 | 0 | lock.Wait(); |
406 | 0 | } |
407 | 0 | MOZ_ASSERT(mProcessState == PROCESS_ERROR || mChildProcessHandle); |
408 | 0 |
|
409 | 0 | return mProcessState < PROCESS_ERROR; |
410 | 0 | } |
411 | | |
412 | | void |
413 | | GeckoChildProcessHost::InitializeChannel() |
414 | 0 | { |
415 | 0 | CreateChannel(); |
416 | 0 |
|
417 | 0 | MonitorAutoLock lock(mMonitor); |
418 | 0 | mProcessState = CHANNEL_INITIALIZED; |
419 | 0 | lock.Notify(); |
420 | 0 | } |
421 | | |
422 | | void |
423 | | GeckoChildProcessHost::Join() |
424 | 0 | { |
425 | 0 | AssertIOThread(); |
426 | 0 |
|
427 | 0 | if (!mChildProcessHandle) { |
428 | 0 | return; |
429 | 0 | } |
430 | 0 | |
431 | 0 | // If this fails, there's nothing we can do. |
432 | 0 | base::KillProcess(mChildProcessHandle, 0, /*wait*/true); |
433 | 0 | SetAlreadyDead(); |
434 | 0 | } |
435 | | |
436 | | void |
437 | | GeckoChildProcessHost::SetAlreadyDead() |
438 | 0 | { |
439 | 0 | if (mChildProcessHandle && |
440 | 0 | mChildProcessHandle != kInvalidProcessHandle) { |
441 | 0 | base::CloseProcessHandle(mChildProcessHandle); |
442 | 0 | } |
443 | 0 |
|
444 | 0 | mChildProcessHandle = 0; |
445 | 0 | } |
446 | | |
447 | | int32_t GeckoChildProcessHost::mChildCounter = 0; |
448 | | |
449 | | void |
450 | | GeckoChildProcessHost::GetChildLogName(const char* origLogName, |
451 | | nsACString &buffer) |
452 | 0 | { |
453 | | #ifdef XP_WIN |
454 | | // On Windows we must expand relative paths because sandboxing rules |
455 | | // bound only to full paths. fopen fowards to NtCreateFile which checks |
456 | | // the path against the sanboxing rules as passed to fopen (left relative). |
457 | | char absPath[MAX_PATH + 2]; |
458 | | if (_fullpath(absPath, origLogName, sizeof(absPath))) { |
459 | | #ifdef MOZ_SANDBOX |
460 | | // We need to make sure the child log name doesn't contain any junction |
461 | | // points or symlinks or the sandbox will reject rules to allow writing. |
462 | | std::wstring resolvedPath(NS_ConvertUTF8toUTF16(absPath).get()); |
463 | | if (widget::WinUtils::ResolveJunctionPointsAndSymLinks(resolvedPath)) { |
464 | | AppendUTF16toUTF8( |
465 | | MakeSpan(reinterpret_cast<const char16_t*>(resolvedPath.data()), |
466 | | resolvedPath.size()), |
467 | | buffer); |
468 | | } else |
469 | | #endif |
470 | | { |
471 | | buffer.Append(absPath); |
472 | | } |
473 | | } else |
474 | | #endif |
475 | | { |
476 | 0 | buffer.Append(origLogName); |
477 | 0 | } |
478 | 0 |
|
479 | 0 | // Append child-specific postfix to name |
480 | 0 | buffer.AppendLiteral(".child-"); |
481 | 0 | buffer.AppendInt(mChildCounter); |
482 | 0 | } |
483 | | |
484 | | bool |
485 | | GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector<std::string> aExtraOpts) |
486 | 0 | { |
487 | 0 | InitializeChannel(); |
488 | 0 |
|
489 | 0 | bool ok = PerformAsyncLaunch(aExtraOpts); |
490 | 0 | if (!ok) { |
491 | 0 | // WaitUntilConnected might be waiting for us to signal. |
492 | 0 | // If something failed let's set the error state and notify. |
493 | 0 | MonitorAutoLock lock(mMonitor); |
494 | 0 | mProcessState = PROCESS_ERROR; |
495 | 0 | lock.Notify(); |
496 | | #ifdef ASYNC_CONTENTPROC_LAUNCH |
497 | | OnProcessLaunchError(); |
498 | | #endif |
499 | 0 | CHROMIUM_LOG(ERROR) << "Failed to launch " << |
500 | 0 | XRE_ChildProcessTypeToString(mProcessType) << " subprocess"; |
501 | 0 | Telemetry::Accumulate(Telemetry::SUBPROCESS_LAUNCH_FAILURE, |
502 | 0 | nsDependentCString(XRE_ChildProcessTypeToString(mProcessType))); |
503 | | #ifdef ASYNC_CONTENTPROC_LAUNCH |
504 | | } else { |
505 | | OnProcessHandleReady(mChildProcessHandle); |
506 | | #endif |
507 | | } |
508 | 0 | return ok; |
509 | 0 | } |
510 | | |
511 | | void |
512 | | #if defined(XP_WIN) |
513 | | AddAppDirToCommandLine(CommandLine& aCmdLine) |
514 | | #else |
515 | | AddAppDirToCommandLine(std::vector<std::string>& aCmdLine) |
516 | | #endif |
517 | 0 | { |
518 | 0 | // Content processes need access to application resources, so pass |
519 | 0 | // the full application directory path to the child process. |
520 | 0 | if (ShouldHaveDirectoryService()) { |
521 | 0 | nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); |
522 | 0 | NS_ASSERTION(directoryService, "Expected XPCOM to be available"); |
523 | 0 | if (directoryService) { |
524 | 0 | nsCOMPtr<nsIFile> appDir; |
525 | 0 | // NS_XPCOM_CURRENT_PROCESS_DIR really means the app dir, not the |
526 | 0 | // current process dir. |
527 | 0 | nsresult rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, |
528 | 0 | NS_GET_IID(nsIFile), |
529 | 0 | getter_AddRefs(appDir)); |
530 | 0 | if (NS_SUCCEEDED(rv)) { |
531 | | #if defined(XP_WIN) |
532 | | nsString path; |
533 | | MOZ_ALWAYS_SUCCEEDS(appDir->GetPath(path)); |
534 | | aCmdLine.AppendLooseValue(UTF8ToWide("-appdir")); |
535 | | std::wstring wpath(path.get()); |
536 | | aCmdLine.AppendLooseValue(wpath); |
537 | | #else |
538 | | nsAutoCString path; |
539 | 0 | MOZ_ALWAYS_SUCCEEDS(appDir->GetNativePath(path)); |
540 | 0 | aCmdLine.push_back("-appdir"); |
541 | 0 | aCmdLine.push_back(path.get()); |
542 | 0 | #endif |
543 | 0 | } |
544 | 0 |
|
545 | | #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) |
546 | | // Full path to the profile dir |
547 | | nsCOMPtr<nsIFile> profileDir; |
548 | | rv = directoryService->Get(NS_APP_USER_PROFILE_50_DIR, |
549 | | NS_GET_IID(nsIFile), |
550 | | getter_AddRefs(profileDir)); |
551 | | if (NS_SUCCEEDED(rv)) { |
552 | | nsAutoCString path; |
553 | | MOZ_ALWAYS_SUCCEEDS(profileDir->GetNativePath(path)); |
554 | | aCmdLine.push_back("-profile"); |
555 | | aCmdLine.push_back(path.get()); |
556 | | } |
557 | | #endif |
558 | | } |
559 | 0 | } |
560 | 0 | } |
561 | | |
562 | | bool |
563 | | GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts) |
564 | 0 | { |
565 | 0 | #ifdef MOZ_GECKO_PROFILER |
566 | 0 | AutoSetProfilerEnvVarsForChildProcess profilerEnvironment; |
567 | 0 | #endif |
568 | 0 |
|
569 | 0 | // - Note: this code is not called re-entrantly, nor are restoreOrig*LogName |
570 | 0 | // or mChildCounter touched by any other thread, so this is safe. |
571 | 0 | ++mChildCounter; |
572 | 0 |
|
573 | 0 | const char* origNSPRLogName = PR_GetEnv("NSPR_LOG_FILE"); |
574 | 0 | const char* origMozLogName = PR_GetEnv("MOZ_LOG_FILE"); |
575 | 0 |
|
576 | 0 | if (origNSPRLogName) { |
577 | 0 | nsAutoCString nsprLogName; |
578 | 0 | GetChildLogName(origNSPRLogName, nsprLogName); |
579 | 0 | mLaunchOptions->env_map[ENVIRONMENT_LITERAL("NSPR_LOG_FILE")] |
580 | 0 | = ENVIRONMENT_STRING(nsprLogName); |
581 | 0 | } |
582 | 0 | if (origMozLogName) { |
583 | 0 | nsAutoCString mozLogName; |
584 | 0 | GetChildLogName(origMozLogName, mozLogName); |
585 | 0 | mLaunchOptions->env_map[ENVIRONMENT_LITERAL("MOZ_LOG_FILE")] |
586 | 0 | = ENVIRONMENT_STRING(mozLogName); |
587 | 0 | } |
588 | 0 |
|
589 | 0 | // `RUST_LOG_CHILD` is meant for logging child processes only. |
590 | 0 | nsAutoCString childRustLog(PR_GetEnv("RUST_LOG_CHILD")); |
591 | 0 | if (!childRustLog.IsEmpty()) { |
592 | 0 | mLaunchOptions->env_map[ENVIRONMENT_LITERAL("RUST_LOG")] |
593 | 0 | = ENVIRONMENT_STRING(childRustLog); |
594 | 0 | } |
595 | 0 |
|
596 | 0 | #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) |
597 | 0 | if (!mTmpDirName.IsEmpty()) { |
598 | 0 | // Point a bunch of things that might want to write from content to our |
599 | 0 | // shiny new content-process specific tmpdir |
600 | 0 | mLaunchOptions->env_map[ENVIRONMENT_LITERAL("TMPDIR")] = |
601 | 0 | ENVIRONMENT_STRING(mTmpDirName); |
602 | 0 | // Partial fix for bug 1380051 (not persistent - should be) |
603 | 0 | mLaunchOptions->env_map[ENVIRONMENT_LITERAL("MESA_GLSL_CACHE_DIR")] = |
604 | 0 | ENVIRONMENT_STRING(mTmpDirName); |
605 | 0 | } |
606 | 0 | #endif |
607 | 0 |
|
608 | 0 | // We rely on the fact that InitializeChannel() has already been processed |
609 | 0 | // on the IO thread before this point is reached. |
610 | 0 | if (!GetChannel()) { |
611 | 0 | return false; |
612 | 0 | } |
613 | 0 | |
614 | 0 | base::ProcessHandle process = 0; |
615 | 0 |
|
616 | 0 | // send the child the PID so that it can open a ProcessHandle back to us. |
617 | 0 | // probably don't want to do this in the long run |
618 | 0 | char pidstring[32]; |
619 | 0 | SprintfLiteral(pidstring, "%d", base::GetCurrentProcId()); |
620 | 0 |
|
621 | 0 | const char* const childProcessType = |
622 | 0 | XRE_ChildProcessTypeToString(mProcessType); |
623 | 0 |
|
624 | 0 | PRFileDesc* crashAnnotationReadPipe; |
625 | 0 | PRFileDesc* crashAnnotationWritePipe; |
626 | 0 | if (PR_CreatePipe(&crashAnnotationReadPipe, &crashAnnotationWritePipe) != PR_SUCCESS) { |
627 | 0 | return false; |
628 | 0 | } |
629 | 0 | |
630 | 0 | //-------------------------------------------------- |
631 | 0 | #if defined(OS_POSIX) |
632 | 0 | // For POSIX, we have to be extremely anal about *not* using |
633 | 0 | // std::wstring in code compiled with Mozilla's -fshort-wchar |
634 | 0 | // configuration, because chromium is compiled with -fno-short-wchar |
635 | 0 | // and passing wstrings from one config to the other is unsafe. So |
636 | 0 | // we split the logic here. |
637 | 0 | |
638 | 0 | # if defined(OS_POSIX) |
639 | 0 | # if defined(MOZ_WIDGET_GTK) |
640 | 0 | if (mProcessType == GeckoProcessType_Content) { |
641 | 0 | // disable IM module to avoid sandbox violation |
642 | 0 | mLaunchOptions->env_map["GTK_IM_MODULE"] = "gtk-im-context-simple"; |
643 | 0 |
|
644 | 0 | // Disable ATK accessibility code in content processes because it conflicts |
645 | 0 | // with the sandbox, and we proxy that information through the main process |
646 | 0 | // anyway. |
647 | 0 | mLaunchOptions->env_map["NO_AT_BRIDGE"] = "1"; |
648 | 0 | } |
649 | 0 | # endif // defined(MOZ_WIDGET_GTK) |
650 | 0 |
|
651 | 0 | // XPCOM may not be initialized in some subprocesses. We don't want |
652 | 0 | // to initialize XPCOM just for the directory service, especially |
653 | 0 | // since LD_LIBRARY_PATH is already set correctly in subprocesses |
654 | 0 | // (meaning that we don't need to set that up in the environment). |
655 | 0 | if (ShouldHaveDirectoryService()) { |
656 | 0 | MOZ_ASSERT(gGREBinPath); |
657 | 0 | nsCString path; |
658 | 0 | NS_CopyUnicodeToNative(nsDependentString(gGREBinPath), path); |
659 | 0 | # if defined(OS_LINUX) || defined(OS_BSD) |
660 | 0 | const char *ld_library_path = PR_GetEnv("LD_LIBRARY_PATH"); |
661 | 0 | nsCString new_ld_lib_path(path.get()); |
662 | 0 |
|
663 | 0 | # ifdef MOZ_WIDGET_GTK |
664 | 0 | if (mProcessType == GeckoProcessType_Plugin) { |
665 | 0 | new_ld_lib_path.AppendLiteral("/gtk2:"); |
666 | 0 | new_ld_lib_path.Append(path.get()); |
667 | 0 | } |
668 | 0 | # endif // MOZ_WIDGET_GTK |
669 | 0 | if (ld_library_path && *ld_library_path) { |
670 | 0 | new_ld_lib_path.Append(':'); |
671 | 0 | new_ld_lib_path.Append(ld_library_path); |
672 | 0 | } |
673 | 0 | mLaunchOptions->env_map["LD_LIBRARY_PATH"] = new_ld_lib_path.get(); |
674 | 0 |
|
675 | | # elif OS_MACOSX // defined(OS_LINUX) || defined(OS_BSD) |
676 | | mLaunchOptions->env_map["DYLD_LIBRARY_PATH"] = path.get(); |
677 | | // XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin |
678 | | // process, and has no effect on other subprocesses (the hooks in |
679 | | // libplugin_child_interpose.dylib become noops). But currently it |
680 | | // gets set when launching any kind of subprocess. |
681 | | // |
682 | | // Trigger "dyld interposing" for the dylib that contains |
683 | | // plugin_child_interpose.mm. This allows us to hook OS calls in the |
684 | | // plugin process (ones that don't work correctly in a background |
685 | | // process). Don't break any other "dyld interposing" that has already |
686 | | // been set up by whatever may have launched the browser. |
687 | | const char* prevInterpose = PR_GetEnv("DYLD_INSERT_LIBRARIES"); |
688 | | nsCString interpose; |
689 | | if (prevInterpose && strlen(prevInterpose) > 0) { |
690 | | interpose.Assign(prevInterpose); |
691 | | interpose.Append(':'); |
692 | | } |
693 | | interpose.Append(path.get()); |
694 | | interpose.AppendLiteral("/libplugin_child_interpose.dylib"); |
695 | | mLaunchOptions->env_map["DYLD_INSERT_LIBRARIES"] = interpose.get(); |
696 | | # endif // defined(OS_LINUX) || defined(OS_BSD) |
697 | | } |
698 | 0 | # endif // defined(OS_POSIX) |
699 | 0 |
|
700 | 0 | FilePath exePath; |
701 | 0 | BinaryPathType pathType = GetPathToBinary(exePath, mProcessType); |
702 | 0 |
|
703 | 0 | // remap the IPC socket fd to a well-known int, as the OS does for |
704 | 0 | // STDOUT_FILENO, for example |
705 | 0 | int srcChannelFd, dstChannelFd; |
706 | 0 | channel().GetClientFileDescriptorMapping(&srcChannelFd, &dstChannelFd); |
707 | 0 | mLaunchOptions->fds_to_remap |
708 | 0 | .push_back(std::pair<int,int>(srcChannelFd, dstChannelFd)); |
709 | 0 |
|
710 | 0 | // no need for kProcessChannelID, the child process inherits the |
711 | 0 | // other end of the socketpair() from us |
712 | 0 |
|
713 | 0 | std::vector<std::string> childArgv; |
714 | 0 |
|
715 | 0 | childArgv.push_back(exePath.value()); |
716 | 0 |
|
717 | 0 | if (pathType == BinaryPathType::Self) { |
718 | 0 | childArgv.push_back("-contentproc"); |
719 | 0 | } |
720 | 0 |
|
721 | 0 | childArgv.insert(childArgv.end(), aExtraOpts.begin(), aExtraOpts.end()); |
722 | 0 |
|
723 | 0 | if (Omnijar::IsInitialized()) { |
724 | 0 | // Make sure that child processes can find the omnijar |
725 | 0 | // See XRE_InitCommandLine in nsAppRunner.cpp |
726 | 0 | nsAutoCString path; |
727 | 0 | nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE); |
728 | 0 | if (file && NS_SUCCEEDED(file->GetNativePath(path))) { |
729 | 0 | childArgv.push_back("-greomni"); |
730 | 0 | childArgv.push_back(path.get()); |
731 | 0 | } |
732 | 0 | file = Omnijar::GetPath(Omnijar::APP); |
733 | 0 | if (file && NS_SUCCEEDED(file->GetNativePath(path))) { |
734 | 0 | childArgv.push_back("-appomni"); |
735 | 0 | childArgv.push_back(path.get()); |
736 | 0 | } |
737 | 0 | } |
738 | 0 |
|
739 | 0 | // Add the application directory path (-appdir path) |
740 | 0 | AddAppDirToCommandLine(childArgv); |
741 | 0 |
|
742 | 0 | // Tmp dir that the GPU process should use for crash reports. This arg is |
743 | 0 | // always populated (but possibly with an empty value) for a GPU child process. |
744 | 0 | if (mProcessType == GeckoProcessType_GPU || mProcessType == GeckoProcessType_VR) { |
745 | 0 | nsCOMPtr<nsIFile> file; |
746 | 0 | CrashReporter::GetChildProcessTmpDir(getter_AddRefs(file)); |
747 | 0 | nsAutoCString path; |
748 | 0 | if (file) { |
749 | 0 | file->GetNativePath(path); |
750 | 0 | } |
751 | 0 | childArgv.push_back(path.get()); |
752 | 0 | } |
753 | 0 |
|
754 | 0 | childArgv.push_back(pidstring); |
755 | 0 |
|
756 | 0 | if (!CrashReporter::IsDummy()) { |
757 | 0 | #if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS) |
758 | 0 | int childCrashFd, childCrashRemapFd; |
759 | 0 | if (!CrashReporter::CreateNotificationPipeForChild(&childCrashFd, |
760 | 0 | &childCrashRemapFd)) { |
761 | 0 | return false; |
762 | 0 | } |
763 | 0 | |
764 | 0 | if (0 <= childCrashFd) { |
765 | 0 | mLaunchOptions->fds_to_remap |
766 | 0 | .push_back(std::pair<int,int>(childCrashFd, childCrashRemapFd)); |
767 | 0 | // "true" == crash reporting enabled |
768 | 0 | childArgv.push_back("true"); |
769 | 0 | } else { |
770 | 0 | // "false" == crash reporting disabled |
771 | 0 | childArgv.push_back("false"); |
772 | 0 | } |
773 | | #elif defined(MOZ_WIDGET_COCOA) // defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS) |
774 | | childArgv.push_back(CrashReporter::GetChildNotificationPipe()); |
775 | | #endif // defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS) |
776 | | } |
777 | 0 |
|
778 | 0 | int fd = PR_FileDesc2NativeHandle(crashAnnotationWritePipe); |
779 | 0 | mLaunchOptions->fds_to_remap.push_back( |
780 | 0 | std::make_pair(fd, CrashReporter::GetAnnotationTimeCrashFd())); |
781 | 0 |
|
782 | | # ifdef MOZ_WIDGET_COCOA |
783 | | // Add a mach port to the command line so the child can communicate its |
784 | | // 'task_t' back to the parent. |
785 | | // |
786 | | // Put a random number into the channel name, so that a compromised renderer |
787 | | // can't pretend being the child that's forked off. |
788 | | std::string mach_connection_name = StringPrintf("org.mozilla.machname.%d", |
789 | | base::RandInt(0, std::numeric_limits<int>::max())); |
790 | | childArgv.push_back(mach_connection_name.c_str()); |
791 | | # endif // MOZ_WIDGET_COCOA |
792 | |
|
793 | 0 | childArgv.push_back(childProcessType); |
794 | 0 |
|
795 | | # ifdef MOZ_WIDGET_COCOA |
796 | | // Register the listening port before launching the child, to ensure |
797 | | // that it's there when the child tries to look it up. |
798 | | ReceivePort parent_recv_port(mach_connection_name.c_str()); |
799 | | # endif // MOZ_WIDGET_COCOA |
800 | |
|
801 | | # if defined(MOZ_WIDGET_ANDROID) |
802 | | LaunchAndroidService(childProcessType, childArgv, |
803 | | mLaunchOptions->fds_to_remap, &process); |
804 | | # else // goes with defined(MOZ_WIDGET_ANDROID) |
805 | | base::LaunchApp(childArgv, *mLaunchOptions, &process); |
806 | 0 | # endif // defined(MOZ_WIDGET_ANDROID) |
807 | 0 |
|
808 | 0 | // We're in the parent and the child was launched. Close the child FD in the |
809 | 0 | // parent as soon as possible, which will allow the parent to detect when the |
810 | 0 | // child closes its FD (either due to normal exit or due to crash). |
811 | 0 | GetChannel()->CloseClientFileDescriptor(); |
812 | 0 |
|
813 | | # ifdef MOZ_WIDGET_COCOA |
814 | | // Wait for the child process to send us its 'task_t' data. |
815 | | const int kTimeoutMs = 10000; |
816 | | |
817 | | MachReceiveMessage child_message; |
818 | | kern_return_t err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs); |
819 | | if (err != KERN_SUCCESS) { |
820 | | std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err)); |
821 | | CHROMIUM_LOG(ERROR) << "parent WaitForMessage() failed: " << errString; |
822 | | return false; |
823 | | } |
824 | | |
825 | | task_t child_task = child_message.GetTranslatedPort(0); |
826 | | if (child_task == MACH_PORT_NULL) { |
827 | | CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(0) failed."; |
828 | | return false; |
829 | | } |
830 | | |
831 | | if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) { |
832 | | CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(1) failed."; |
833 | | return false; |
834 | | } |
835 | | MachPortSender parent_sender(child_message.GetTranslatedPort(1)); |
836 | | |
837 | | if (child_message.GetTranslatedPort(2) == MACH_PORT_NULL) { |
838 | | CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(2) failed."; |
839 | | } |
840 | | auto* parent_recv_port_memory_ack = new MachPortSender(child_message.GetTranslatedPort(2)); |
841 | | |
842 | | if (child_message.GetTranslatedPort(3) == MACH_PORT_NULL) { |
843 | | CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(3) failed."; |
844 | | } |
845 | | auto* parent_send_port_memory = new MachPortSender(child_message.GetTranslatedPort(3)); |
846 | | |
847 | | MachSendMessage parent_message(/* id= */0); |
848 | | if (!parent_message.AddDescriptor(MachMsgPortDescriptor(bootstrap_port))) { |
849 | | CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed."; |
850 | | return false; |
851 | | } |
852 | | |
853 | | auto* parent_recv_port_memory = new ReceivePort(); |
854 | | if (!parent_message.AddDescriptor(MachMsgPortDescriptor(parent_recv_port_memory->GetPort()))) { |
855 | | CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << parent_recv_port_memory->GetPort() << ") failed."; |
856 | | return false; |
857 | | } |
858 | | |
859 | | auto* parent_send_port_memory_ack = new ReceivePort(); |
860 | | if (!parent_message.AddDescriptor(MachMsgPortDescriptor(parent_send_port_memory_ack->GetPort()))) { |
861 | | CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << parent_send_port_memory_ack->GetPort() << ") failed."; |
862 | | return false; |
863 | | } |
864 | | |
865 | | err = parent_sender.SendMessage(parent_message, kTimeoutMs); |
866 | | if (err != KERN_SUCCESS) { |
867 | | std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err)); |
868 | | CHROMIUM_LOG(ERROR) << "parent SendMessage() failed: " << errString; |
869 | | return false; |
870 | | } |
871 | | |
872 | | SharedMemoryBasic::SetupMachMemory(process, parent_recv_port_memory, parent_recv_port_memory_ack, |
873 | | parent_send_port_memory, parent_send_port_memory_ack, false); |
874 | | |
875 | | # endif // MOZ_WIDGET_COCOA |
876 | |
|
877 | 0 | //-------------------------------------------------- |
878 | | #elif defined(OS_WIN) // defined(OS_POSIX) |
879 | | |
880 | | FilePath exePath; |
881 | | BinaryPathType pathType = GetPathToBinary(exePath, mProcessType); |
882 | | |
883 | | CommandLine cmdLine(exePath.ToWStringHack()); |
884 | | |
885 | | if (pathType == BinaryPathType::Self) { |
886 | | cmdLine.AppendLooseValue(UTF8ToWide("-contentproc")); |
887 | | } |
888 | | |
889 | | cmdLine.AppendSwitchWithValue(switches::kProcessChannelID, channel_id()); |
890 | | |
891 | | for (std::vector<std::string>::iterator it = aExtraOpts.begin(); |
892 | | it != aExtraOpts.end(); |
893 | | ++it) { |
894 | | cmdLine.AppendLooseValue(UTF8ToWide(*it)); |
895 | | } |
896 | | |
897 | | if (Omnijar::IsInitialized()) { |
898 | | // Make sure the child process can find the omnijar |
899 | | // See XRE_InitCommandLine in nsAppRunner.cpp |
900 | | nsAutoString path; |
901 | | nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE); |
902 | | if (file && NS_SUCCEEDED(file->GetPath(path))) { |
903 | | cmdLine.AppendLooseValue(UTF8ToWide("-greomni")); |
904 | | cmdLine.AppendLooseValue(path.get()); |
905 | | } |
906 | | file = Omnijar::GetPath(Omnijar::APP); |
907 | | if (file && NS_SUCCEEDED(file->GetPath(path))) { |
908 | | cmdLine.AppendLooseValue(UTF8ToWide("-appomni")); |
909 | | cmdLine.AppendLooseValue(path.get()); |
910 | | } |
911 | | } |
912 | | |
913 | | # if defined(MOZ_SANDBOX) |
914 | | bool shouldSandboxCurrentProcess = false; |
915 | | |
916 | | // XXX: Bug 1124167: We should get rid of the process specific logic for |
917 | | // sandboxing in this class at some point. Unfortunately it will take a bit |
918 | | // of reorganizing so I don't think this patch is the right time. |
919 | | switch (mProcessType) { |
920 | | case GeckoProcessType_Content: |
921 | | # if defined(MOZ_CONTENT_SANDBOX) |
922 | | if (mSandboxLevel > 0) { |
923 | | // For now we treat every failure as fatal in SetSecurityLevelForContentProcess |
924 | | // and just crash there right away. Should this change in the future then we |
925 | | // should also handle the error here. |
926 | | mSandboxBroker.SetSecurityLevelForContentProcess(mSandboxLevel, |
927 | | mIsFileContent); |
928 | | shouldSandboxCurrentProcess = true; |
929 | | } |
930 | | # endif // defined(MOZ_CONTENT_SANDBOX) |
931 | | break; |
932 | | case GeckoProcessType_Plugin: |
933 | | if (mSandboxLevel > 0 && |
934 | | !PR_GetEnv("MOZ_DISABLE_NPAPI_SANDBOX")) { |
935 | | bool ok = mSandboxBroker.SetSecurityLevelForPluginProcess(mSandboxLevel); |
936 | | if (!ok) { |
937 | | return false; |
938 | | } |
939 | | shouldSandboxCurrentProcess = true; |
940 | | } |
941 | | break; |
942 | | #ifdef MOZ_ENABLE_SKIA_PDF |
943 | | case GeckoProcessType_PDFium: |
944 | | if (!PR_GetEnv("MOZ_DISABLE_PDFIUM_SANDBOX")) { |
945 | | bool ok = mSandboxBroker.SetSecurityLevelForPDFiumProcess(); |
946 | | if (!ok) { |
947 | | return false; |
948 | | } |
949 | | shouldSandboxCurrentProcess = true; |
950 | | } |
951 | | break; |
952 | | #endif |
953 | | case GeckoProcessType_IPDLUnitTest: |
954 | | // XXX: We don't sandbox this process type yet |
955 | | break; |
956 | | case GeckoProcessType_GMPlugin: |
957 | | if (!PR_GetEnv("MOZ_DISABLE_GMP_SANDBOX")) { |
958 | | // The Widevine CDM on Windows can only load at USER_RESTRICTED, |
959 | | // not at USER_LOCKDOWN. So look in the command line arguments |
960 | | // to see if we're loading the path to the Widevine CDM, and if |
961 | | // so use sandbox level USER_RESTRICTED instead of USER_LOCKDOWN. |
962 | | bool isWidevine = std::any_of(aExtraOpts.begin(), aExtraOpts.end(), |
963 | | [](const std::string arg) { return arg.find("gmp-widevinecdm") != std::string::npos; }); |
964 | | auto level = isWidevine ? SandboxBroker::Restricted : SandboxBroker::LockDown; |
965 | | bool ok = mSandboxBroker.SetSecurityLevelForGMPlugin(level); |
966 | | if (!ok) { |
967 | | return false; |
968 | | } |
969 | | shouldSandboxCurrentProcess = true; |
970 | | } |
971 | | break; |
972 | | case GeckoProcessType_GPU: |
973 | | if (mSandboxLevel > 0 && !PR_GetEnv("MOZ_DISABLE_GPU_SANDBOX")) { |
974 | | // For now we treat every failure as fatal in SetSecurityLevelForGPUProcess |
975 | | // and just crash there right away. Should this change in the future then we |
976 | | // should also handle the error here. |
977 | | mSandboxBroker.SetSecurityLevelForGPUProcess(mSandboxLevel); |
978 | | shouldSandboxCurrentProcess = true; |
979 | | } |
980 | | break; |
981 | | case GeckoProcessType_VR: |
982 | | if (mSandboxLevel > 0 && !PR_GetEnv("MOZ_DISABLE_VR_SANDBOX")) { |
983 | | // TODO: Implement sandbox for VR process, Bug 1430043. |
984 | | } |
985 | | break; |
986 | | case GeckoProcessType_Default: |
987 | | default: |
988 | | MOZ_CRASH("Bad process type in GeckoChildProcessHost"); |
989 | | break; |
990 | | }; |
991 | | |
992 | | if (shouldSandboxCurrentProcess) { |
993 | | for (auto it = mAllowedFilesRead.begin(); |
994 | | it != mAllowedFilesRead.end(); |
995 | | ++it) { |
996 | | mSandboxBroker.AllowReadFile(it->c_str()); |
997 | | } |
998 | | } |
999 | | # endif // defined(MOZ_SANDBOX) |
1000 | | |
1001 | | // Add the application directory path (-appdir path) |
1002 | | AddAppDirToCommandLine(cmdLine); |
1003 | | |
1004 | | // XXX Command line params past this point are expected to be at |
1005 | | // the end of the command line string, and in a specific order. |
1006 | | // See XRE_InitChildProcess in nsEmbedFunction. |
1007 | | |
1008 | | // Win app model id |
1009 | | cmdLine.AppendLooseValue(mGroupId.get()); |
1010 | | |
1011 | | // Tmp dir that the GPU process should use for crash reports. This arg is |
1012 | | // always populated (but possibly with an empty value) for a GPU child process. |
1013 | | if (mProcessType == GeckoProcessType_GPU) { |
1014 | | nsCOMPtr<nsIFile> file; |
1015 | | CrashReporter::GetChildProcessTmpDir(getter_AddRefs(file)); |
1016 | | nsString path; |
1017 | | if (file) { |
1018 | | MOZ_ALWAYS_SUCCEEDS(file->GetPath(path)); |
1019 | | } |
1020 | | std::wstring wpath(path.get()); |
1021 | | cmdLine.AppendLooseValue(wpath); |
1022 | | } |
1023 | | |
1024 | | // Process id |
1025 | | cmdLine.AppendLooseValue(UTF8ToWide(pidstring)); |
1026 | | |
1027 | | cmdLine.AppendLooseValue( |
1028 | | UTF8ToWide(CrashReporter::GetChildNotificationPipe())); |
1029 | | |
1030 | | if (!CrashReporter::IsDummy()) { |
1031 | | PROsfd h = PR_FileDesc2NativeHandle(crashAnnotationWritePipe); |
1032 | | mLaunchOptions->handles_to_inherit.push_back(reinterpret_cast<HANDLE>(h)); |
1033 | | std::string hStr = std::to_string(h); |
1034 | | cmdLine.AppendLooseValue(UTF8ToWide(hStr)); |
1035 | | } |
1036 | | |
1037 | | // Process type |
1038 | | cmdLine.AppendLooseValue(UTF8ToWide(childProcessType)); |
1039 | | |
1040 | | # if defined(MOZ_SANDBOX) |
1041 | | if (shouldSandboxCurrentProcess) { |
1042 | | // Mark the handles to inherit as inheritable. |
1043 | | for (HANDLE h : mLaunchOptions->handles_to_inherit) { |
1044 | | mSandboxBroker.AddHandleToShare(h); |
1045 | | } |
1046 | | |
1047 | | if (mSandboxBroker.LaunchApp(cmdLine.program().c_str(), |
1048 | | cmdLine.command_line_string().c_str(), |
1049 | | mLaunchOptions->env_map, |
1050 | | mProcessType, |
1051 | | mEnableSandboxLogging, |
1052 | | &process)) { |
1053 | | EnvironmentLog("MOZ_PROCESS_LOG").print( |
1054 | | "==> process %d launched child process %d (%S)\n", |
1055 | | base::GetCurrentProcId(), base::GetProcId(process), |
1056 | | cmdLine.command_line_string().c_str()); |
1057 | | } |
1058 | | } else |
1059 | | # endif // defined(MOZ_SANDBOX) |
1060 | | { |
1061 | | base::LaunchApp(cmdLine, *mLaunchOptions, &process); |
1062 | | |
1063 | | # ifdef MOZ_SANDBOX |
1064 | | // We need to be able to duplicate handles to some types of non-sandboxed |
1065 | | // child processes. |
1066 | | if (mProcessType == GeckoProcessType_Content || |
1067 | | mProcessType == GeckoProcessType_GPU || |
1068 | | mProcessType == GeckoProcessType_VR || |
1069 | | mProcessType == GeckoProcessType_GMPlugin) { |
1070 | | if (!mSandboxBroker.AddTargetPeer(process)) { |
1071 | | NS_WARNING("Failed to add content process as target peer."); |
1072 | | } |
1073 | | } |
1074 | | # endif // MOZ_SANDBOX |
1075 | | } |
1076 | | |
1077 | | #else // goes with defined(OS_POSIX) |
1078 | | # error Sorry |
1079 | | #endif // defined(OS_POSIX) |
1080 | |
|
1081 | 0 | if (!process) { |
1082 | 0 | return false; |
1083 | 0 | } |
1084 | 0 | // NB: on OS X, we block much longer than we need to in order to |
1085 | 0 | // reach this call, waiting for the child process's task_t. The |
1086 | 0 | // best way to fix that is to refactor this file, hard. |
1087 | | #if defined(MOZ_WIDGET_COCOA) |
1088 | | mChildTask = child_task; |
1089 | | #endif // defined(MOZ_WIDGET_COCOA) |
1090 | | |
1091 | 0 | if (!OpenPrivilegedHandle(base::GetProcId(process)) |
1092 | | #ifdef XP_WIN |
1093 | | // If we failed in opening the process handle, try harder by duplicating |
1094 | | // one. |
1095 | | && !::DuplicateHandle(::GetCurrentProcess(), process, |
1096 | | ::GetCurrentProcess(), &mChildProcessHandle, |
1097 | | PROCESS_DUP_HANDLE | PROCESS_TERMINATE | |
1098 | | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | |
1099 | | SYNCHRONIZE, |
1100 | | FALSE, 0) |
1101 | | #endif // XP_WIN |
1102 | 0 | ) { |
1103 | 0 | MOZ_CRASH("cannot open handle to child process"); |
1104 | 0 | } |
1105 | | #if defined(XP_WIN) |
1106 | | CrashReporter::RegisterChildCrashAnnotationFileDescriptor( |
1107 | | base::GetProcId(process), crashAnnotationReadPipe); |
1108 | | #else |
1109 | 0 | CrashReporter::RegisterChildCrashAnnotationFileDescriptor(process, |
1110 | 0 | crashAnnotationReadPipe); |
1111 | 0 | #endif |
1112 | 0 | PR_Close(crashAnnotationWritePipe); |
1113 | 0 |
|
1114 | 0 | MonitorAutoLock lock(mMonitor); |
1115 | 0 | mProcessState = PROCESS_CREATED; |
1116 | 0 | lock.Notify(); |
1117 | 0 |
|
1118 | 0 | mLaunchOptions = nullptr; |
1119 | 0 | return true; |
1120 | 0 | } |
1121 | | |
1122 | | bool |
1123 | | GeckoChildProcessHost::OpenPrivilegedHandle(base::ProcessId aPid) |
1124 | 0 | { |
1125 | 0 | if (mChildProcessHandle) { |
1126 | 0 | MOZ_ASSERT(aPid == base::GetProcId(mChildProcessHandle)); |
1127 | 0 | return true; |
1128 | 0 | } |
1129 | 0 |
|
1130 | 0 | return base::OpenPrivilegedProcessHandle(aPid, &mChildProcessHandle); |
1131 | 0 | } |
1132 | | |
1133 | | void |
1134 | | GeckoChildProcessHost::OnProcessHandleReady(ProcessHandle aProcessHandle) |
1135 | 0 | {} |
1136 | | |
1137 | | void |
1138 | | GeckoChildProcessHost::OnProcessLaunchError() |
1139 | 0 | {} |
1140 | | |
1141 | | void |
1142 | | GeckoChildProcessHost::OnChannelConnected(int32_t peer_pid) |
1143 | 0 | { |
1144 | 0 | if (!OpenPrivilegedHandle(peer_pid)) { |
1145 | 0 | MOZ_CRASH("can't open handle to child process"); |
1146 | 0 | } |
1147 | 0 | MonitorAutoLock lock(mMonitor); |
1148 | 0 | mProcessState = PROCESS_CONNECTED; |
1149 | 0 | lock.Notify(); |
1150 | 0 | } |
1151 | | |
1152 | | void |
1153 | | GeckoChildProcessHost::OnMessageReceived(IPC::Message&& aMsg) |
1154 | 0 | { |
1155 | 0 | // We never process messages ourself, just save them up for the next |
1156 | 0 | // listener. |
1157 | 0 | mQueue.push(std::move(aMsg)); |
1158 | 0 | } |
1159 | | |
1160 | | void |
1161 | | GeckoChildProcessHost::OnChannelError() |
1162 | 0 | { |
1163 | 0 | // Update the process state to an error state if we have a channel |
1164 | 0 | // error before we're connected. This fixes certain failures, |
1165 | 0 | // but does not address the full range of possible issues described |
1166 | 0 | // in the FIXME comment below. |
1167 | 0 | MonitorAutoLock lock(mMonitor); |
1168 | 0 | if (mProcessState < PROCESS_CONNECTED) { |
1169 | 0 | mProcessState = PROCESS_ERROR; |
1170 | 0 | lock.Notify(); |
1171 | 0 | } |
1172 | 0 | // FIXME/bug 773925: save up this error for the next listener. |
1173 | 0 | } |
1174 | | |
1175 | | void |
1176 | | GeckoChildProcessHost::GetQueuedMessages(std::queue<IPC::Message>& queue) |
1177 | 0 | { |
1178 | 0 | // If this is called off the IO thread, bad things will happen. |
1179 | 0 | DCHECK(MessageLoopForIO::current()); |
1180 | 0 | swap(queue, mQueue); |
1181 | 0 | // We expect the next listener to take over processing of our queue. |
1182 | 0 | } |
1183 | | |
1184 | | bool GeckoChildProcessHost::sRunSelfAsContentProc(false); |
1185 | | |
1186 | | #ifdef MOZ_WIDGET_ANDROID |
1187 | | void |
1188 | | GeckoChildProcessHost::LaunchAndroidService(const char* type, |
1189 | | const std::vector<std::string>& argv, |
1190 | | const base::file_handle_mapping_vector& fds_to_remap, |
1191 | | ProcessHandle* process_handle) |
1192 | | { |
1193 | | MOZ_RELEASE_ASSERT((2 <= fds_to_remap.size()) && (fds_to_remap.size() <= 5)); |
1194 | | JNIEnv* const env = mozilla::jni::GetEnvForThread(); |
1195 | | MOZ_ASSERT(env); |
1196 | | |
1197 | | const int argvSize = argv.size(); |
1198 | | jni::ObjectArray::LocalRef jargs = jni::ObjectArray::New<jni::String>(argvSize); |
1199 | | for (int ix = 0; ix < argvSize; ix++) { |
1200 | | jargs->SetElement(ix, jni::StringParam(argv[ix].c_str(), env)); |
1201 | | } |
1202 | | |
1203 | | // XXX: this processing depends entirely on the internals of |
1204 | | // ContentParent::LaunchSubprocess() |
1205 | | // GeckoChildProcessHost::PerformAsyncLaunch(), and the order in |
1206 | | // which they append to fds_to_remap. There must be a better way to do it. |
1207 | | // See bug 1440207. |
1208 | | int32_t prefsFd = fds_to_remap[0].first; |
1209 | | int32_t prefMapFd = fds_to_remap[1].first; |
1210 | | int32_t ipcFd = fds_to_remap[2].first; |
1211 | | int32_t crashFd = -1; |
1212 | | int32_t crashAnnotationFd = -1; |
1213 | | if (fds_to_remap.size() == 4) { |
1214 | | crashAnnotationFd = fds_to_remap[3].first; |
1215 | | } |
1216 | | if (fds_to_remap.size() == 5) { |
1217 | | crashFd = fds_to_remap[3].first; |
1218 | | crashAnnotationFd = fds_to_remap[4].first; |
1219 | | } |
1220 | | |
1221 | | int32_t handle = java::GeckoProcessManager::Start(type, jargs, prefsFd, prefMapFd, ipcFd, crashFd, crashAnnotationFd); |
1222 | | |
1223 | | if (process_handle) { |
1224 | | *process_handle = handle; |
1225 | | } |
1226 | | } |
1227 | | #endif |