gecko/content/media/gmp/GMPChild.cpp
Jed Davis 66d1734f54 Bug 1043733 - Require sandboxing to load Gecko Media Plugins on Linux. r=jesup r=kang
Also refactors how sandbox support and disabling are handled, and allows
simulating a lack of sandbox support with an env var (for testing
without rebuilding a kernel).
2014-08-12 21:28:27 -07:00

402 lines
9.0 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "GMPChild.h"
#include "GMPVideoDecoderChild.h"
#include "GMPVideoEncoderChild.h"
#include "GMPAudioDecoderChild.h"
#include "GMPDecryptorChild.h"
#include "GMPVideoHost.h"
#include "nsIFile.h"
#include "nsXULAppAPI.h"
#include "gmp-video-decode.h"
#include "gmp-video-encode.h"
#include "GMPPlatform.h"
#include "mozilla/dom/CrashReporterChild.h"
using mozilla::dom::CrashReporterChild;
#ifdef XP_WIN
#include <stdlib.h> // for _exit()
#else
#include <unistd.h> // for _exit()
#endif
#if defined(XP_WIN)
#define TARGET_SANDBOX_EXPORTS
#include "mozilla/sandboxTarget.h"
#elif defined (MOZ_GMP_SANDBOX)
#if defined(XP_LINUX) || defined(XP_MACOSX)
#include "mozilla/Sandbox.h"
#endif
#endif
namespace mozilla {
namespace gmp {
GMPChild::GMPChild()
: mLib(nullptr)
, mGetAPIFunc(nullptr)
, mGMPMessageLoop(MessageLoop::current())
{
}
GMPChild::~GMPChild()
{
}
static bool
GetPluginBinaryPath(const std::string& aPluginPath,
nsCString &aPluginBinaryPath)
{
nsDependentCString pluginPath(aPluginPath.c_str());
nsCOMPtr<nsIFile> libFile;
nsresult rv = NS_NewNativeLocalFile(pluginPath, true, getter_AddRefs(libFile));
if (NS_FAILED(rv)) {
return false;
}
nsAutoString leafName;
if (NS_FAILED(libFile->GetLeafName(leafName))) {
return false;
}
nsAutoString baseName(Substring(leafName, 4, leafName.Length() - 1));
#if defined(XP_MACOSX)
nsAutoString binaryName = NS_LITERAL_STRING("lib") + baseName + NS_LITERAL_STRING(".dylib");
#elif defined(OS_POSIX)
nsAutoString binaryName = NS_LITERAL_STRING("lib") + baseName + NS_LITERAL_STRING(".so");
#elif defined(XP_WIN)
nsAutoString binaryName = baseName + NS_LITERAL_STRING(".dll");
#else
#error not defined
#endif
libFile->AppendRelativePath(binaryName);
libFile->GetNativePath(aPluginBinaryPath);
return true;
}
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
void
GMPChild::OnChannelConnected(int32_t aPid)
{
MacSandboxInfo info;
info.type = MacSandboxType_Plugin;
info.pluginInfo.type = MacSandboxPluginType_GMPlugin_Default;
info.pluginInfo.pluginPath.Assign(mPluginPath.c_str());
nsAutoCString pluginBinaryPath;
if (!GetPluginBinaryPath(mPluginPath, pluginBinaryPath)) {
MOZ_CRASH("Error scanning plugin path");
}
mPluginBinaryPath.Assign(pluginBinaryPath);
info.pluginInfo.pluginBinaryPath.Assign(pluginBinaryPath);
nsAutoCString err;
if (!mozilla::StartMacSandbox(info, err)) {
NS_WARNING(err.get());
MOZ_CRASH("sandbox_init() failed");
}
if (!LoadPluginLibrary(mPluginPath)) {
err.AppendPrintf("Failed to load GMP plugin \"%s\"",
mPluginPath.c_str());
NS_WARNING(err.get());
MOZ_CRASH("Failed to load GMP plugin");
}
}
#endif // XP_MACOSX && MOZ_GMP_SANDBOX
void
GMPChild::CheckThread()
{
MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
}
bool
GMPChild::Init(const std::string& aPluginPath,
base::ProcessHandle aParentProcessHandle,
MessageLoop* aIOLoop,
IPC::Channel* aChannel)
{
if (!Open(aChannel, aParentProcessHandle, aIOLoop)) {
return false;
}
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
mPluginPath = aPluginPath;
return true;
#endif
#ifdef MOZ_CRASHREPORTER
SendPCrashReporterConstructor(CrashReporter::CurrentThreadId());
#endif
#if defined(XP_WIN)
mozilla::SandboxTarget::Instance()->StartSandbox();
#endif
return LoadPluginLibrary(aPluginPath);
}
bool
GMPChild::LoadPluginLibrary(const std::string& aPluginPath)
{
nsAutoCString nativePath;
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
nativePath.Assign(mPluginBinaryPath);
#else
if (!GetPluginBinaryPath(aPluginPath, nativePath)) {
return false;
}
#endif
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
// Enable sandboxing here -- we know the plugin file's path, but
// this process's execution hasn't been affected by its content yet.
MOZ_ASSERT(mozilla::CanSandboxMediaPlugin());
mozilla::SetMediaPluginSandbox(nativePath.get());
#endif
mLib = PR_LoadLibrary(nativePath.get());
if (!mLib) {
return false;
}
GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
if (!initFunc) {
return false;
}
auto platformAPI = new GMPPlatformAPI();
InitPlatformAPI(*platformAPI, this);
if (initFunc(platformAPI) != GMPNoErr) {
return false;
}
mGetAPIFunc = reinterpret_cast<GMPGetAPIFunc>(PR_FindFunctionSymbol(mLib, "GMPGetAPI"));
if (!mGetAPIFunc) {
return false;
}
return true;
}
MessageLoop*
GMPChild::GMPMessageLoop()
{
return mGMPMessageLoop;
}
void
GMPChild::ActorDestroy(ActorDestroyReason aWhy)
{
if (mLib) {
GMPShutdownFunc shutdownFunc = reinterpret_cast<GMPShutdownFunc>(PR_FindFunctionSymbol(mLib, "GMPShutdown"));
if (shutdownFunc) {
shutdownFunc();
}
}
if (AbnormalShutdown == aWhy) {
NS_WARNING("Abnormal shutdown of GMP process!");
_exit(0);
}
XRE_ShutdownChildProcess();
}
void
GMPChild::ProcessingError(Result aWhat)
{
switch (aWhat) {
case MsgDropped:
_exit(0); // Don't trigger a crash report.
case MsgNotKnown:
MOZ_CRASH("aborting because of MsgNotKnown");
case MsgNotAllowed:
MOZ_CRASH("aborting because of MsgNotAllowed");
case MsgPayloadError:
MOZ_CRASH("aborting because of MsgPayloadError");
case MsgProcessingError:
MOZ_CRASH("aborting because of MsgProcessingError");
case MsgRouteError:
MOZ_CRASH("aborting because of MsgRouteError");
case MsgValueError:
MOZ_CRASH("aborting because of MsgValueError");
default:
MOZ_CRASH("not reached");
}
}
PGMPAudioDecoderChild*
GMPChild::AllocPGMPAudioDecoderChild()
{
return new GMPAudioDecoderChild(this);
}
bool
GMPChild::DeallocPGMPAudioDecoderChild(PGMPAudioDecoderChild* aActor)
{
delete aActor;
return true;
}
mozilla::dom::PCrashReporterChild*
GMPChild::AllocPCrashReporterChild(const NativeThreadId& aThread)
{
return new CrashReporterChild();
}
bool
GMPChild::DeallocPCrashReporterChild(PCrashReporterChild* aCrashReporter)
{
delete aCrashReporter;
return true;
}
PGMPVideoDecoderChild*
GMPChild::AllocPGMPVideoDecoderChild()
{
return new GMPVideoDecoderChild(this);
}
bool
GMPChild::DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor)
{
delete aActor;
return true;
}
PGMPDecryptorChild*
GMPChild::AllocPGMPDecryptorChild()
{
return new GMPDecryptorChild(this);
}
bool
GMPChild::DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor)
{
delete aActor;
return true;
}
bool
GMPChild::RecvPGMPAudioDecoderConstructor(PGMPAudioDecoderChild* aActor)
{
auto vdc = static_cast<GMPAudioDecoderChild*>(aActor);
void* vd = nullptr;
GMPErr err = mGetAPIFunc("decode-audio", &vdc->Host(), &vd);
if (err != GMPNoErr || !vd) {
return false;
}
vdc->Init(static_cast<GMPAudioDecoder*>(vd));
return true;
}
PGMPVideoEncoderChild*
GMPChild::AllocPGMPVideoEncoderChild()
{
return new GMPVideoEncoderChild(this);
}
bool
GMPChild::DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor)
{
delete aActor;
return true;
}
bool
GMPChild::RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor)
{
auto vdc = static_cast<GMPVideoDecoderChild*>(aActor);
void* vd = nullptr;
GMPErr err = mGetAPIFunc("decode-video", &vdc->Host(), &vd);
if (err != GMPNoErr || !vd) {
return false;
}
vdc->Init(static_cast<GMPVideoDecoder*>(vd));
return true;
}
bool
GMPChild::RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor)
{
auto vec = static_cast<GMPVideoEncoderChild*>(aActor);
void* ve = nullptr;
GMPErr err = mGetAPIFunc("encode-video", &vec->Host(), &ve);
if (err != GMPNoErr || !ve) {
return false;
}
vec->Init(static_cast<GMPVideoEncoder*>(ve));
return true;
}
bool
GMPChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor)
{
GMPDecryptorChild* child = static_cast<GMPDecryptorChild*>(aActor);
GMPDecryptorHost* host = static_cast<GMPDecryptorHost*>(child);
void* session = nullptr;
GMPErr err = mGetAPIFunc("eme-decrypt", host, &session);
if (err != GMPNoErr || !session) {
return false;
}
child->Init(static_cast<GMPDecryptor*>(session));
return true;
}
PGMPTimerChild*
GMPChild::AllocPGMPTimerChild()
{
return new GMPTimerChild(this);
}
bool
GMPChild::DeallocPGMPTimerChild(PGMPTimerChild* aActor)
{
MOZ_ASSERT(mTimerChild == static_cast<GMPTimerChild*>(aActor));
mTimerChild = nullptr;
return true;
}
GMPTimerChild*
GMPChild::GetGMPTimers()
{
if (!mTimerChild) {
PGMPTimerChild* sc = SendPGMPTimerConstructor();
if (!sc) {
return nullptr;
}
mTimerChild = static_cast<GMPTimerChild*>(sc);
}
return mTimerChild;
}
bool
GMPChild::RecvCrashPluginNow()
{
MOZ_CRASH();
return true;
}
} // namespace gmp
} // namespace mozilla