gecko-dev/dom/plugins/ipc/PluginProcessParent.cpp
Jed Davis bcff2dd108 Bug 1487287 - Synchronize GeckoChildProcessHost destruction with launching. r=mccr8
In order to enable asynchronous launch, destruction of
GeckoChildProcessHost (and its subclasses) has to be delayed until after
launching (or anything else that might be made asynchronous in the
future) has completed, to prevent use-after-free.  However, there are
other dependencies on process hosts always being destroyed on the I/O
thread, so refcounting would be difficult to use.

Instead, GeckoChildProcessHost now may not be destroyed directly, but
must go through a method that handles the scheduling.

There are also some minor cleanups to the affected headers (removed
duplicate access modifiers, and made PluginProcessParent final).

Depends on D18010

Differential Revision: https://phabricator.services.mozilla.com/D18011

--HG--
extra : moz-landing-system : lando
2019-02-05 00:15:20 +00:00

191 lines
5.4 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=4 et :
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/plugins/PluginProcessParent.h"
#include "base/string_util.h"
#include "base/process_util.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIProperties.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/ipc/BrowserProcessSubThread.h"
#include "mozilla/plugins/PluginMessageUtils.h"
#include "mozilla/Telemetry.h"
#include "nsThreadUtils.h"
using std::string;
using std::vector;
using mozilla::ipc::BrowserProcessSubThread;
using mozilla::ipc::GeckoChildProcessHost;
using mozilla::plugins::LaunchCompleteTask;
using mozilla::plugins::PluginProcessParent;
#ifdef XP_WIN
PluginProcessParent::PidSet* PluginProcessParent::sPidSet = nullptr;
#endif
PluginProcessParent::PluginProcessParent(const std::string& aPluginFilePath)
: GeckoChildProcessHost(GeckoProcessType_Plugin),
mPluginFilePath(aPluginFilePath),
mTaskFactory(this),
mMainMsgLoop(MessageLoop::current())
#ifdef XP_WIN
,
mChildPid(0)
#endif
{
}
PluginProcessParent::~PluginProcessParent() {
#ifdef XP_WIN
if (sPidSet && mChildPid) {
sPidSet->RemoveEntry(mChildPid);
if (sPidSet->IsEmpty()) {
delete sPidSet;
sPidSet = nullptr;
}
}
#endif
}
bool PluginProcessParent::Launch(
mozilla::UniquePtr<LaunchCompleteTask> aLaunchCompleteTask,
int32_t aSandboxLevel, bool aIsSandboxLoggingEnabled) {
#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_SANDBOX)
// At present, the Mac Flash plugin sandbox does not support different
// levels and is enabled via a boolean pref or environment variable.
// On Mac, when |aSandboxLevel| is positive, we enable the sandbox.
# if defined(XP_WIN)
mSandboxLevel = aSandboxLevel;
// The sandbox process sometimes needs read access to the plugin file.
if (aSandboxLevel >= 3) {
std::wstring pluginFile(
NS_ConvertUTF8toUTF16(mPluginFilePath.c_str()).get());
mAllowedFilesRead.push_back(pluginFile);
}
# endif // XP_WIN
#else
if (aSandboxLevel != 0) {
MOZ_ASSERT(false,
"Can't enable an NPAPI process sandbox for platform/build.");
}
#endif
mLaunchCompleteTask = std::move(aLaunchCompleteTask);
vector<string> args;
args.push_back(MungePluginDsoPath(mPluginFilePath));
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
if (aSandboxLevel > 0) {
args.push_back("-flashSandboxLevel");
args.push_back(std::to_string(aSandboxLevel));
if (aIsSandboxLoggingEnabled) {
args.push_back("-flashSandboxLogging");
}
}
#elif defined(XP_WIN) && defined(MOZ_SANDBOX)
nsresult rv;
nsCOMPtr<nsIProperties> dirSvc =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
MOZ_ASSERT(false, "Failed to get directory service.");
return false;
}
nsCOMPtr<nsIFile> dir;
rv = dirSvc->Get(NS_APP_PLUGIN_PROCESS_TEMP_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(dir));
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get plugin process temp directory.");
return false;
}
nsAutoString tempDir;
MOZ_ALWAYS_SUCCEEDS(dir->GetPath(tempDir));
args.push_back(NS_ConvertUTF16toUTF8(tempDir).get());
rv =
dirSvc->Get(NS_WIN_APPDATA_DIR, NS_GET_IID(nsIFile), getter_AddRefs(dir));
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get appdata directory.");
return false;
}
nsAutoString appdataDir;
MOZ_ALWAYS_SUCCEEDS(dir->GetPath(appdataDir));
appdataDir.Append(L"\\Adobe\\");
args.push_back(NS_ConvertUTF16toUTF8(appdataDir).get());
#endif
bool result = AsyncLaunch(args);
if (!result) {
mLaunchCompleteTask = nullptr;
}
return result;
}
/**
* This function exists so that we may provide an additional level of
* indirection between the task being posted to main event loop (a
* RunnableMethod) and the launch complete task itself. This is needed
* for cases when both WaitUntilConnected or OnChannel* race to invoke the
* task.
*/
void PluginProcessParent::RunLaunchCompleteTask() {
if (mLaunchCompleteTask) {
mLaunchCompleteTask->Run();
mLaunchCompleteTask = nullptr;
}
}
bool PluginProcessParent::WaitUntilConnected(int32_t aTimeoutMs) {
bool result = GeckoChildProcessHost::WaitUntilConnected(aTimeoutMs);
if (mLaunchCompleteTask) {
if (result) {
mLaunchCompleteTask->SetLaunchSucceeded();
}
RunLaunchCompleteTask();
}
return result;
}
void PluginProcessParent::OnChannelConnected(int32_t peer_pid) {
#ifdef XP_WIN
mChildPid = static_cast<uint32_t>(peer_pid);
if (!sPidSet) {
sPidSet = new PluginProcessParent::PidSet();
}
sPidSet->PutEntry(mChildPid);
#endif
GeckoChildProcessHost::OnChannelConnected(peer_pid);
}
void PluginProcessParent::OnChannelError() {
GeckoChildProcessHost::OnChannelError();
}
bool PluginProcessParent::IsConnected() {
mozilla::MonitorAutoLock lock(mMonitor);
return mProcessState == PROCESS_CONNECTED;
}
bool PluginProcessParent::IsPluginProcessId(base::ProcessId procId) {
#ifdef XP_WIN
MOZ_ASSERT(XRE_IsParentProcess());
return sPidSet && sPidSet->Contains(static_cast<uint32_t>(procId));
#else
NS_ERROR("IsPluginProcessId not available on this platform.");
return false;
#endif
}