Skip to content

Commit

Permalink
Bug 1498742 - Part 2 - Start the GMP sandbox earlier during process s…
Browse files Browse the repository at this point in the history
…tartup r=jya,cpearce

Change the Mac GMP process launch to include sandboxing params on the command line to allow the sandbox to be started earlier during GMP process launch. Content, extension, and RDD processes have already been changed to start the sandbox earlier.

Update GMPProcessParent to override GeckoChildProcessHost methods used to construct sandboxing parameters. Pass the plugin path as a sandbox parameter so that the sandbox rules can whitelist the plugin directory which is now read after the sandbox is enabled in the plugin process. On development builds, pass "testingReadPath" params so directories needed during automated tests can be whitelisted.

Update Mac sandboxing code to detect GMP sandbox params on the command line and enable the sandbox with additional arguments needed for early sandbox start.

Allow reverting to the old implementation by setting security.sandbox.gmp.mac.earlyinit to false.

Differential Revision: https://phabricator.services.mozilla.com/D34085
  • Loading branch information
hafta committed Jun 14, 2019
1 parent d1f2ec6 commit 9a10540
Show file tree
Hide file tree
Showing 17 changed files with 439 additions and 52 deletions.
2 changes: 2 additions & 0 deletions browser/app/profile/firefox.js
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,8 @@ pref("security.sandbox.gmp.win32k-disable", false);
pref("security.sandbox.content.mac.earlyinit", true);
// Remove this pref once RDD early init is stable on Release.
pref("security.sandbox.rdd.mac.earlyinit", true);
// Remove this pref once GMP early init is stable on Release.
pref("security.sandbox.gmp.mac.earlyinit", true);

// This pref is discussed in bug 1083344, the naming is inspired from its
// Windows counterpart, but on Mac it's an integer which means:
Expand Down
25 changes: 16 additions & 9 deletions dom/media/gmp/GMPChild.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "gmp-video-decode.h"
#include "gmp-video-encode.h"
#include "GMPPlatform.h"
#include "GMPProcessParent.h"
#include "mozilla/Algorithm.h"
#include "mozilla/ipc/CrashReporterClient.h"
#include "mozilla/ipc/ProcessChild.h"
Expand Down Expand Up @@ -560,16 +561,22 @@ mozilla::ipc::IPCResult GMPChild::AnswerStartPlugin(const nsString& aAdapter) {
return IPC_FAIL(this, "Can't sandbox GMP.");
}
#endif

bool isChromium = aAdapter.EqualsLiteral("chromium");
#if defined(MOZ_SANDBOX) && defined(XP_MACOSX)
// Use of the chromium adapter indicates we are going to be
// running the Widevine plugin which requires access to the
// WindowServer in the Mac GMP sandbox policy.
if (!SetMacSandboxInfo(isChromium /* allow-window-server */)) {
NS_WARNING("Failed to set Mac GMP sandbox info");
delete platformAPI;
return IPC_FAIL(
this, nsPrintfCString("Failed to set Mac GMP sandbox info.").get());

#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
// If we started the sandbox at launch ("earlyinit"), then we don't
// need to setup sandbox arguments here with SetMacSandboxInfo().
if (!IsMacSandboxStarted()) {
// Use of the chromium adapter indicates we are going to be
// running the Widevine plugin which requires access to the
// WindowServer in the Mac GMP sandbox policy.
if (!SetMacSandboxInfo(isChromium /* allow-window-server */)) {
NS_WARNING("Failed to set Mac GMP sandbox info");
delete platformAPI;
return IPC_FAIL(
this, nsPrintfCString("Failed to set Mac GMP sandbox info.").get());
}
}
#endif

Expand Down
6 changes: 6 additions & 0 deletions dom/media/gmp/GMPLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ class WinSandboxStarter : public mozilla::gmp::SandboxStarter {
class MacSandboxStarter : public mozilla::gmp::SandboxStarter {
public:
bool Start(const char* aLibPath) override {
// If we started the sandbox at launch ("earlyinit"),
// then don't try to start the sandbox again.
if (IsMacSandboxStarted()) {
return true;
}

std::string err;
bool rv = mozilla::StartMacSandbox(mInfo, err);
if (!rv) {
Expand Down
3 changes: 3 additions & 0 deletions dom/media/gmp/GMPParent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ nsresult GMPParent::LoadProcess() {

if (!mProcess) {
mProcess = new GMPProcessParent(NS_ConvertUTF16toUTF8(path).get());
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
mProcess->SetRequiresWindowServer(mAdapter.EqualsLiteral("chromium"));
#endif
if (!mProcess->Launch(30 * 1000)) {
LOGD("%s: Failed to launch new child process", __FUNCTION__);
mProcess->Delete();
Expand Down
165 changes: 164 additions & 1 deletion dom/media/gmp/GMPProcessParent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@

#include <string>

#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
# include "mozilla/dom/ContentChild.h"
# include "mozilla/Sandbox.h"
# include "mozilla/SandboxSettings.h"
# include "nsMacUtilsImpl.h"
#endif

using std::string;
using std::vector;

Expand All @@ -29,9 +36,47 @@ static const int kInvalidFd = -1;
namespace mozilla {
namespace gmp {

#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
bool GMPProcessParent::sLaunchWithMacSandbox = true;
bool GMPProcessParent::sMacSandboxGMPLogging = false;
# if defined(DEBUG)
bool GMPProcessParent::sIsMainThreadInitDone = false;
# endif
#endif

#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
/* static */
void GMPProcessParent::InitStaticMainThread() {
// The GMPProcessParent constructor is called off the
// main thread. Do main thread initialization here.
MOZ_ASSERT(NS_IsMainThread());
sLaunchWithMacSandbox =
Preferences::GetBool("security.sandbox.gmp.mac.earlyinit", true) &&
(getenv("MOZ_DISABLE_GMP_SANDBOX") == nullptr);
sMacSandboxGMPLogging =
Preferences::GetBool("security.sandbox.logging.enabled") ||
PR_GetEnv("MOZ_SANDBOX_GMP_LOGGING") || PR_GetEnv("MOZ_SANDBOX_LOGGING");
GMP_LOG("GMPProcessParent::InitStaticMainThread: earlyinit=%s, logging=%s",
sLaunchWithMacSandbox ? "true" : "false",
sMacSandboxGMPLogging ? "true" : "false");
# if defined(DEBUG)
sIsMainThreadInitDone = true;
# endif
}
#endif

GMPProcessParent::GMPProcessParent(const std::string& aGMPPath)
: GeckoChildProcessHost(GeckoProcessType_GMPlugin), mGMPPath(aGMPPath) {
: GeckoChildProcessHost(GeckoProcessType_GMPlugin),
mGMPPath(aGMPPath)
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
,
mRequiresWindowServer(false)
#endif
{
MOZ_COUNT_CTOR(GMPProcessParent);
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
MOZ_ASSERT(sIsMainThreadInitDone == true);
#endif
}

GMPProcessParent::~GMPProcessParent() { MOZ_COUNT_DTOR(GMPProcessParent); }
Expand Down Expand Up @@ -68,6 +113,21 @@ bool GMPProcessParent::Launch(int32_t aTimeoutMs) {
}

args.push_back(WideToUTF8(wGMPPath));
#elif defined(XP_MACOSX) && defined(MOZ_SANDBOX)
// Resolve symlinks in the plugin path. The sandbox prevents
// resolving symlinks in the child process if access to link
// source file is denied.
nsAutoCString normalizedPath;
nsresult rv = NormalizePath(mGMPPath.c_str(), normalizedPath);
if (NS_WARN_IF(NS_FAILED(rv))) {
GMP_LOG(
"GMPProcessParent::Launch: "
"plugin path normaliziation failed for path: %s",
mGMPPath.c_str());
args.push_back(mGMPPath);
} else {
args.push_back(normalizedPath.get());
}
#else
args.push_back(mGMPPath);
#endif
Expand Down Expand Up @@ -98,5 +158,108 @@ void GMPProcessParent::DoDelete() {
Destroy();
}

#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
bool GMPProcessParent::IsMacSandboxLaunchEnabled() {
return sLaunchWithMacSandbox;
}

void GMPProcessParent::SetRequiresWindowServer(bool aRequiresWindowServer) {
mRequiresWindowServer = aRequiresWindowServer;
}

bool GMPProcessParent::FillMacSandboxInfo(MacSandboxInfo& aInfo) {
aInfo.type = MacSandboxType_GMP;
aInfo.hasWindowServer = mRequiresWindowServer;
aInfo.shouldLog = (aInfo.shouldLog || sMacSandboxGMPLogging);
nsAutoCString appPath;
if (!nsMacUtilsImpl::GetAppPath(appPath)) {
GMP_LOG("GMPProcessParent::FillMacSandboxInfo: failed to get app path");
return false;
}
aInfo.appPath.assign(appPath.get());

GMP_LOG(
"GMPProcessParent::FillMacSandboxInfo: "
"plugin dir path: %s",
mGMPPath.c_str());
nsCOMPtr<nsIFile> pluginDir;
nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(mGMPPath.c_str()), true,
getter_AddRefs(pluginDir));
if (NS_FAILED(rv)) {
GMP_LOG(
"GMPProcessParent::FillMacSandboxInfo: "
"NS_NewLocalFile failed for plugin dir, rv=%d",
rv);
return false;
}

rv = pluginDir->Normalize();
if (NS_FAILED(rv)) {
GMP_LOG(
"GMPProcessParent::FillMacSandboxInfo: "
"failed to normalize plugin dir path, rv=%d",
rv);
return false;
}

nsAutoCString resolvedPluginPath;
pluginDir->GetNativePath(resolvedPluginPath);
aInfo.pluginPath.assign(resolvedPluginPath.get());
GMP_LOG(
"GMPProcessParent::FillMacSandboxInfo: "
"resolved plugin dir path: %s",
resolvedPluginPath.get());

if (mozilla::IsDevelopmentBuild()) {
GMP_LOG("GMPProcessParent::FillMacSandboxInfo: IsDevelopmentBuild()=true");

// Repo dir
nsCOMPtr<nsIFile> repoDir;
rv = nsMacUtilsImpl::GetRepoDir(getter_AddRefs(repoDir));
if (NS_FAILED(rv)) {
GMP_LOG("GMPProcessParent::FillMacSandboxInfo: failed to get repo dir");
return false;
}
nsCString repoDirPath;
Unused << repoDir->GetNativePath(repoDirPath);
aInfo.testingReadPath1 = repoDirPath.get();
GMP_LOG(
"GMPProcessParent::FillMacSandboxInfo: "
"repo dir path: %s",
repoDirPath.get());

// Object dir
nsCOMPtr<nsIFile> objDir;
rv = nsMacUtilsImpl::GetObjDir(getter_AddRefs(objDir));
if (NS_FAILED(rv)) {
GMP_LOG("GMPProcessParent::FillMacSandboxInfo: failed to get object dir");
return false;
}
nsCString objDirPath;
Unused << objDir->GetNativePath(objDirPath);
aInfo.testingReadPath2 = objDirPath.get();
GMP_LOG(
"GMPProcessParent::FillMacSandboxInfo: "
"object dir path: %s",
objDirPath.get());
}
return true;
}

nsresult GMPProcessParent::NormalizePath(const char* aPath,
nsACString& aNormalizedPath) {
nsCOMPtr<nsIFile> fileOrDir;
nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(aPath), true,
getter_AddRefs(fileOrDir));
NS_ENSURE_SUCCESS(rv, rv);

rv = fileOrDir->Normalize();
NS_ENSURE_SUCCESS(rv, rv);

fileOrDir->GetNativePath(aNormalizedPath);
return NS_OK;
}
#endif

} // namespace gmp
} // namespace mozilla
52 changes: 52 additions & 0 deletions dom/media/gmp/GMPProcessParent.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,28 @@ class GMPProcessParent final : public mozilla::ipc::GeckoChildProcessHost {
bool CanShutdown() override { return true; }
const std::string& GetPluginFilePath() { return mGMPPath; }

#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
// Init static members on the main thread
static void InitStaticMainThread();

// Read prefs and environment variables to determine
// when and if to start the Mac sandbox for the child
// process. Starting the sandbox at launch is the new
// preferred method. Code to support starting the sandbox
// later at plugin start time should be removed once
// starting at launch is stable and shipping.
bool IsMacSandboxLaunchEnabled() override;

// For process sandboxing purposes, set whether or not this
// instance of the GMP process requires access to the macOS
// window server. At present, Widevine requires window server
// access, but OpenH264 decoding does not.
void SetRequiresWindowServer(bool aRequiresWindowServer);

// Return the sandbox type to be used with this process type.
static MacSandboxType GetMacSandboxType() { return MacSandboxType_GMP; };
#endif

using mozilla::ipc::GeckoChildProcessHost::GetChannel;
using mozilla::ipc::GeckoChildProcessHost::GetChildProcessHandle;

Expand All @@ -43,6 +65,36 @@ class GMPProcessParent final : public mozilla::ipc::GeckoChildProcessHost {
std::string mGMPPath;
nsCOMPtr<nsIRunnable> mDeletedCallback;

#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
// Indicates whether we'll start the Mac GMP sandbox during
// process launch (earlyinit) which is the new preferred method
// or later in the process lifetime.
static bool sLaunchWithMacSandbox;

// Whether or not Mac sandbox violation logging is enabled.
static bool sMacSandboxGMPLogging;

// Override so we can set GMP-specific sandbox parameters
bool FillMacSandboxInfo(MacSandboxInfo& aInfo) override;

// For normalizing paths to be compatible with sandboxing.
// We use normalized paths to generate the sandbox ruleset. Once
// the sandbox has been started, resolving symlinks that point to
// allowed directories could require reading paths not allowed by
// the sandbox, so we should only attempt to load plugin libraries
// using normalized paths.
static nsresult NormalizePath(const char* aPath, nsACString& aNormalizedPath);

// Controls whether or not the sandbox will be configured with
// window service access.
bool mRequiresWindowServer;

# if defined(DEBUG)
// Used to assert InitStaticMainThread() is called before the constructor.
static bool sIsMainThreadInitDone;
# endif
#endif

DISALLOW_COPY_AND_ASSIGN(GMPProcessParent);
};

Expand Down
7 changes: 6 additions & 1 deletion dom/media/gmp/GMPService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "GMPVideoDecoderParent.h"
#include "nsIObserverService.h"
#include "GeckoChildProcessHost.h"
#include "GMPProcessParent.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/SyncRunnable.h"
#include "nsXPCOMPrivate.h"
Expand Down Expand Up @@ -102,13 +103,17 @@ class GMPServiceCreateHelper final : public mozilla::Runnable {
new GeckoMediaPluginServiceParent();
service->Init();
sSingletonService = service;
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
// GMPProcessParent should only be instantiated in the parent
// so initialization only needs to be done in the parent.
GMPProcessParent::InitStaticMainThread();
#endif
} else {
RefPtr<GeckoMediaPluginServiceChild> service =
new GeckoMediaPluginServiceChild();
service->Init();
sSingletonService = service;
}

ClearOnShutdown(&sSingletonService);
}

Expand Down
7 changes: 4 additions & 3 deletions dom/media/ipc/RDDProcessHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,15 +261,16 @@ void RDDProcessHost::DestroyProcess() {

#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
/* static */
void RDDProcessHost::StaticFillMacSandboxInfo(MacSandboxInfo& aInfo) {
bool RDDProcessHost::StaticFillMacSandboxInfo(MacSandboxInfo& aInfo) {
GeckoChildProcessHost::StaticFillMacSandboxInfo(aInfo);
if (!aInfo.shouldLog && PR_GetEnv("MOZ_SANDBOX_RDD_LOGGING")) {
aInfo.shouldLog = true;
}
return true;
}

void RDDProcessHost::FillMacSandboxInfo(MacSandboxInfo& aInfo) {
RDDProcessHost::StaticFillMacSandboxInfo(aInfo);
bool RDDProcessHost::FillMacSandboxInfo(MacSandboxInfo& aInfo) {
return RDDProcessHost::StaticFillMacSandboxInfo(aInfo);
}

/* static */
Expand Down
4 changes: 2 additions & 2 deletions dom/media/ipc/RDDProcessHost.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class RDDProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
// To allow filling a MacSandboxInfo from the child
// process without an instance of RDDProcessHost.
// Only needed for late-start sandbox enabling.
static void StaticFillMacSandboxInfo(MacSandboxInfo& aInfo);
static bool StaticFillMacSandboxInfo(MacSandboxInfo& aInfo);

// Return the sandbox type to be used with this process type.
static MacSandboxType GetMacSandboxType();
Expand Down Expand Up @@ -130,7 +130,7 @@ class RDDProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
bool IsMacSandboxLaunchEnabled() override { return sLaunchWithMacSandbox; }

// Override so we can turn on RDD process-specific sandbox logging
void FillMacSandboxInfo(MacSandboxInfo& aInfo) override;
bool FillMacSandboxInfo(MacSandboxInfo& aInfo) override;
#endif

DISALLOW_COPY_AND_ASSIGN(RDDProcessHost);
Expand Down
Loading

0 comments on commit 9a10540

Please sign in to comment.