gecko-dev/security/sandbox/linux/Sandbox.cpp
Haik Aftandilian 8f7ffc84b7 Bug 1274540 - Record sandboxing status in crash reports; r=gcp
Adds content sandbox metadata to parent and child crash reports:
Includes the value of pref security.sandbox.content.level,
whether or not the system is capable of sandboxing, if the
sandbox was successfully turned on, and (on Linux systems)
the sandbox capabilities flags.

New crash report keys:
"ContentSandboxLevel" in parent and content
"ContentSandboxCapable" in parent
"ContentSandboxEnabled" in content
"ContentSandboxCapabilities" in content on Linux
2016-07-25 13:21:00 +02:00

704 lines
23 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "Sandbox.h"
#include "LinuxCapabilities.h"
#include "LinuxSched.h"
#include "SandboxBrokerClient.h"
#include "SandboxChroot.h"
#include "SandboxFilter.h"
#include "SandboxInternal.h"
#include "SandboxLogging.h"
#include "SandboxUtil.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/futex.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <unistd.h>
#include "mozilla/Atomics.h"
#include "mozilla/Maybe.h"
#include "mozilla/SandboxInfo.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/unused.h"
#include "sandbox/linux/bpf_dsl/dump_bpf.h"
#include "sandbox/linux/bpf_dsl/policy.h"
#include "sandbox/linux/bpf_dsl/policy_compiler.h"
#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
#include "sandbox/linux/seccomp-bpf/trap.h"
#if defined(ANDROID)
#include "sandbox/linux/services/android_ucontext.h"
#endif
#include "sandbox/linux/services/linux_syscalls.h"
#ifdef MOZ_ASAN
// Copy libsanitizer declarations to avoid depending on ASAN headers.
// See also bug 1081242 comment #4.
extern "C" {
namespace __sanitizer {
// Win64 uses long long, but this is Linux.
typedef signed long sptr;
} // namespace __sanitizer
typedef struct {
int coverage_sandboxed;
__sanitizer::sptr coverage_fd;
unsigned int coverage_max_block_size;
} __sanitizer_sandbox_arguments;
MOZ_IMPORT_API void
__sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
} // extern "C"
#endif // MOZ_ASAN
// Signal number used to enable seccomp on each thread.
int gSeccompTsyncBroadcastSignum = 0;
namespace mozilla {
#ifdef ANDROID
SandboxCrashFunc gSandboxCrashFunc;
#endif
#ifdef MOZ_GMP_SANDBOX
// For media plugins, we can start the sandbox before we dlopen the
// module, so we have to pre-open the file and simulate the sandboxed
// open().
static SandboxOpenedFile gMediaPluginFile;
#endif
static UniquePtr<SandboxChroot> gChrootHelper;
static void (*gChromiumSigSysHandler)(int, siginfo_t*, void*);
// Test whether a ucontext, interpreted as the state after a syscall,
// indicates the given error. See also sandbox::Syscall::PutValueInUcontext.
static bool
ContextIsError(const ucontext_t *aContext, int aError)
{
// Avoid integer promotion warnings. (The unary addition makes
// the decltype not evaluate to a reference type.)
typedef decltype(+SECCOMP_RESULT(aContext)) reg_t;
#ifdef __mips__
return SECCOMP_PARM4(aContext) != 0
&& SECCOMP_RESULT(aContext) == static_cast<reg_t>(aError);
#else
return SECCOMP_RESULT(aContext) == static_cast<reg_t>(-aError);
#endif
}
/**
* This is the SIGSYS handler function. It delegates to the Chromium
* TrapRegistry handler (see InstallSigSysHandler, below) and, if the
* trap handler installed by the policy would fail with ENOSYS,
* crashes the process. This allows unintentional policy failures to
* be reported as crash dumps and fixed. It also logs information
* about the failed system call.
*
* Note that this could be invoked in parallel on multiple threads and
* that it could be in async signal context (e.g., intercepting an
* open() called from an async signal handler).
*/
static void
SigSysHandler(int nr, siginfo_t *info, void *void_context)
{
ucontext_t *ctx = static_cast<ucontext_t*>(void_context);
// This shouldn't ever be null, but the Chromium handler checks for
// that and refrains from crashing, so let's not crash release builds:
MOZ_DIAGNOSTIC_ASSERT(ctx);
if (!ctx) {
return;
}
// Save a copy of the context before invoking the trap handler,
// which will overwrite one or more registers with the return value.
ucontext_t savedCtx = *ctx;
gChromiumSigSysHandler(nr, info, ctx);
if (!ContextIsError(ctx, ENOSYS)) {
return;
}
pid_t pid = getpid();
unsigned long syscall_nr = SECCOMP_SYSCALL(&savedCtx);
unsigned long args[6];
args[0] = SECCOMP_PARM1(&savedCtx);
args[1] = SECCOMP_PARM2(&savedCtx);
args[2] = SECCOMP_PARM3(&savedCtx);
args[3] = SECCOMP_PARM4(&savedCtx);
args[4] = SECCOMP_PARM5(&savedCtx);
args[5] = SECCOMP_PARM6(&savedCtx);
// TODO, someday when this is enabled on MIPS: include the two extra
// args in the error message.
SANDBOX_LOG_ERROR("seccomp sandbox violation: pid %d, syscall %d,"
" args %d %d %d %d %d %d. Killing process.",
pid, syscall_nr,
args[0], args[1], args[2], args[3], args[4], args[5]);
// Bug 1017393: record syscall number somewhere useful.
info->si_addr = reinterpret_cast<void*>(syscall_nr);
gSandboxCrashFunc(nr, info, &savedCtx);
_exit(127);
}
/**
* This function installs the SIGSYS handler. This is slightly
* complicated because we want to use Chromium's handler to dispatch
* to specific trap handlers defined in the policy, but we also need
* the full original signal context to give to Breakpad for crash
* dumps. So we install Chromium's handler first, then retrieve its
* address so our replacement can delegate to it.
*/
static void
InstallSigSysHandler(void)
{
struct sigaction act;
// Ensure that the Chromium handler is installed.
Unused << sandbox::Trap::Registry();
// If the signal handling state isn't as expected, crash now instead
// of crashing later (and more confusingly) when SIGSYS happens.
if (sigaction(SIGSYS, nullptr, &act) != 0) {
MOZ_CRASH("Couldn't read old SIGSYS disposition");
}
if ((act.sa_flags & SA_SIGINFO) != SA_SIGINFO) {
MOZ_CRASH("SIGSYS not already set to a siginfo handler?");
}
MOZ_RELEASE_ASSERT(act.sa_sigaction);
gChromiumSigSysHandler = act.sa_sigaction;
act.sa_sigaction = SigSysHandler;
// Currently, SA_NODEFER should already be set by the Chromium code,
// but it's harmless to ensure that it's set:
MOZ_ASSERT(act.sa_flags & SA_NODEFER);
act.sa_flags |= SA_NODEFER;
if (sigaction(SIGSYS, &act, nullptr) < 0) {
MOZ_CRASH("Couldn't change SIGSYS disposition");
}
}
/**
* This function installs the syscall filter, a.k.a. seccomp. The
* aUseTSync flag indicates whether this should apply to all threads
* in the process -- which will fail if the kernel doesn't support
* that -- or only the current thread.
*
* SECCOMP_MODE_FILTER is the "bpf" mode of seccomp which allows
* to pass a bpf program (in our case, it contains a syscall
* whitelist).
*
* PR_SET_NO_NEW_PRIVS ensures that it is impossible to grant more
* syscalls to the process beyond this point (even after fork()), and
* prevents gaining capabilities (e.g., by exec'ing a setuid root
* program). The kernel won't allow seccomp-bpf without doing this,
* because otherwise it could be used for privilege escalation attacks.
*
* Returns false (and sets errno) on failure.
*
* @see SandboxInfo
* @see BroadcastSetThreadSandbox
*/
static bool MOZ_MUST_USE
InstallSyscallFilter(const sock_fprog *aProg, bool aUseTSync)
{
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
SANDBOX_LOG_ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %s", strerror(errno));
MOZ_CRASH("prctl(PR_SET_NO_NEW_PRIVS)");
}
if (aUseTSync) {
if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
SECCOMP_FILTER_FLAG_TSYNC, aProg) != 0) {
SANDBOX_LOG_ERROR("thread-synchronized seccomp failed: %s",
strerror(errno));
return false;
}
} else {
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, (unsigned long)aProg, 0, 0)) {
SANDBOX_LOG_ERROR("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER) failed: %s",
strerror(errno));
return false;
}
}
return true;
}
// Use signals for permissions that need to be set per-thread.
// The communication channel from the signal handler back to the main thread.
static mozilla::Atomic<int> gSetSandboxDone;
// Pass the filter itself through a global.
static const sock_fprog* gSetSandboxFilter;
// We have to dynamically allocate the signal number; see bug 1038900.
// This function returns the first realtime signal currently set to
// default handling (i.e., not in use), or 0 if none could be found.
//
// WARNING: if this function or anything similar to it (including in
// external libraries) is used on multiple threads concurrently, there
// will be a race condition.
static int
FindFreeSignalNumber()
{
for (int signum = SIGRTMAX; signum >= SIGRTMIN; --signum) {
struct sigaction sa;
if (sigaction(signum, nullptr, &sa) == 0 &&
(sa.sa_flags & SA_SIGINFO) == 0 &&
sa.sa_handler == SIG_DFL) {
return signum;
}
}
return 0;
}
// Returns true if sandboxing was enabled, or false if sandboxing
// already was enabled. Crashes if sandboxing could not be enabled.
static bool
SetThreadSandbox()
{
if (prctl(PR_GET_SECCOMP, 0, 0, 0, 0) == 0) {
if (!InstallSyscallFilter(gSetSandboxFilter, false)) {
MOZ_CRASH("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER)");
}
return true;
}
return false;
}
static void
SetThreadSandboxHandler(int signum)
{
// The non-zero number sent back to the main thread indicates
// whether action was taken.
if (SetThreadSandbox()) {
gSetSandboxDone = 2;
} else {
gSetSandboxDone = 1;
}
// Wake up the main thread. See the FUTEX_WAIT call, below, for an
// explanation.
syscall(__NR_futex, reinterpret_cast<int*>(&gSetSandboxDone),
FUTEX_WAKE, 1);
}
static void
EnterChroot()
{
if (gChrootHelper) {
gChrootHelper->Invoke();
gChrootHelper = nullptr;
}
}
static void
BroadcastSetThreadSandbox(const sock_fprog* aFilter)
{
pid_t pid, tid, myTid;
DIR *taskdp;
struct dirent *de;
// This function does not own *aFilter, so this global needs to
// always be zeroed before returning.
gSetSandboxFilter = aFilter;
static_assert(sizeof(mozilla::Atomic<int>) == sizeof(int),
"mozilla::Atomic<int> isn't represented by an int");
pid = getpid();
myTid = syscall(__NR_gettid);
taskdp = opendir("/proc/self/task");
if (taskdp == nullptr) {
SANDBOX_LOG_ERROR("opendir /proc/self/task: %s\n", strerror(errno));
MOZ_CRASH();
}
EnterChroot();
// In case this races with a not-yet-deprivileged thread cloning
// itself, repeat iterating over all threads until we find none
// that are still privileged.
bool sandboxProgress;
do {
sandboxProgress = false;
// For each thread...
while ((de = readdir(taskdp))) {
char *endptr;
tid = strtol(de->d_name, &endptr, 10);
if (*endptr != '\0' || tid <= 0) {
// Not a task ID.
continue;
}
if (tid == myTid) {
// Drop this thread's privileges last, below, so we can
// continue to signal other threads.
continue;
}
MOZ_RELEASE_ASSERT(gSeccompTsyncBroadcastSignum != 0);
// Reset the futex cell and signal.
gSetSandboxDone = 0;
if (syscall(__NR_tgkill, pid, tid, gSeccompTsyncBroadcastSignum) != 0) {
if (errno == ESRCH) {
SANDBOX_LOG_ERROR("Thread %d unexpectedly exited.", tid);
// Rescan threads, in case it forked before exiting.
sandboxProgress = true;
continue;
}
SANDBOX_LOG_ERROR("tgkill(%d,%d): %s\n", pid, tid, strerror(errno));
MOZ_CRASH();
}
// It's unlikely, but if the thread somehow manages to exit
// after receiving the signal but before entering the signal
// handler, we need to avoid blocking forever.
//
// Using futex directly lets the signal handler send the wakeup
// from an async signal handler (pthread mutex/condvar calls
// aren't allowed), and to use a relative timeout that isn't
// affected by changes to the system clock (not possible with
// POSIX semaphores).
//
// If a thread doesn't respond within a reasonable amount of
// time, but still exists, we crash -- the alternative is either
// blocking forever or silently losing security, and it
// shouldn't actually happen.
static const int crashDelay = 10; // seconds
struct timespec timeLimit;
clock_gettime(CLOCK_MONOTONIC, &timeLimit);
timeLimit.tv_sec += crashDelay;
while (true) {
static const struct timespec futexTimeout = { 0, 10*1000*1000 }; // 10ms
// Atomically: if gSetSandboxDone == 0, then sleep.
if (syscall(__NR_futex, reinterpret_cast<int*>(&gSetSandboxDone),
FUTEX_WAIT, 0, &futexTimeout) != 0) {
if (errno != EWOULDBLOCK && errno != ETIMEDOUT && errno != EINTR) {
SANDBOX_LOG_ERROR("FUTEX_WAIT: %s\n", strerror(errno));
MOZ_CRASH();
}
}
// Did the handler finish?
if (gSetSandboxDone > 0) {
if (gSetSandboxDone == 2) {
sandboxProgress = true;
}
break;
}
// Has the thread ceased to exist?
if (syscall(__NR_tgkill, pid, tid, 0) != 0) {
if (errno == ESRCH) {
SANDBOX_LOG_ERROR("Thread %d unexpectedly exited.", tid);
}
// Rescan threads, in case it forked before exiting.
// Also, if it somehow failed in a way that wasn't ESRCH,
// and still exists, that will be handled on the next pass.
sandboxProgress = true;
break;
}
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec > timeLimit.tv_sec ||
(now.tv_sec == timeLimit.tv_sec &&
now.tv_nsec > timeLimit.tv_nsec)) {
SANDBOX_LOG_ERROR("Thread %d unresponsive for %d seconds."
" Killing process.",
tid, crashDelay);
MOZ_CRASH();
}
}
}
rewinddir(taskdp);
} while (sandboxProgress);
void (*oldHandler)(int);
oldHandler = signal(gSeccompTsyncBroadcastSignum, SIG_DFL);
gSeccompTsyncBroadcastSignum = 0;
if (oldHandler != SetThreadSandboxHandler) {
// See the comment on FindFreeSignalNumber about race conditions.
SANDBOX_LOG_ERROR("handler for signal %d was changed to %p!",
gSeccompTsyncBroadcastSignum, oldHandler);
MOZ_CRASH();
}
Unused << closedir(taskdp);
// And now, deprivilege the main thread:
SetThreadSandbox();
gSetSandboxFilter = nullptr;
}
static void
ApplySandboxWithTSync(sock_fprog* aFilter)
{
EnterChroot();
// At this point we're committed to using tsync, because the signal
// broadcast workaround needs to access procfs. (Unless chroot
// isn't used... but this failure shouldn't happen in the first
// place, so let's not make extra special cases for it.)
if (!InstallSyscallFilter(aFilter, true)) {
MOZ_CRASH("seccomp+tsync failed, but kernel supports tsync");
}
}
// Common code for sandbox startup.
static void
SetCurrentProcessSandbox(UniquePtr<sandbox::bpf_dsl::Policy> aPolicy)
{
MOZ_ASSERT(gSandboxCrashFunc);
// Note: PolicyCompiler borrows the policy and registry for its
// lifetime, but does not take ownership of them.
sandbox::bpf_dsl::PolicyCompiler compiler(aPolicy.get(),
sandbox::Trap::Registry());
auto program = compiler.Compile();
if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
sandbox::bpf_dsl::DumpBPF::PrintProgram(*program);
}
InstallSigSysHandler();
#ifdef MOZ_ASAN
__sanitizer_sandbox_arguments asanArgs;
asanArgs.coverage_sandboxed = 1;
asanArgs.coverage_fd = -1;
asanArgs.coverage_max_block_size = 0;
__sanitizer_sandbox_on_notify(&asanArgs);
#endif
// The syscall takes a C-style array, so copy the vector into one.
size_t programLen = program->size();
UniquePtr<sock_filter[]> flatProgram(new sock_filter[programLen]);
for (auto i = program->begin(); i != program->end(); ++i) {
flatProgram[i - program->begin()] = *i;
}
sock_fprog fprog;
fprog.filter = flatProgram.get();
fprog.len = static_cast<unsigned short>(programLen);
MOZ_RELEASE_ASSERT(static_cast<size_t>(fprog.len) == programLen);
const SandboxInfo info = SandboxInfo::Get();
if (info.Test(SandboxInfo::kHasSeccompTSync)) {
if (info.Test(SandboxInfo::kVerbose)) {
SANDBOX_LOG_ERROR("using seccomp tsync");
}
ApplySandboxWithTSync(&fprog);
} else {
if (info.Test(SandboxInfo::kVerbose)) {
SANDBOX_LOG_ERROR("no tsync support; using signal broadcast");
}
BroadcastSetThreadSandbox(&fprog);
}
MOZ_RELEASE_ASSERT(!gChrootHelper, "forgot to chroot");
}
void
SandboxEarlyInit(GeckoProcessType aType, bool aIsNuwa)
{
// Bug 1168555: Nuwa isn't reliably single-threaded at this point;
// it starts an IPC I/O thread and then shuts it down before calling
// the plugin-container entry point, but that thread may not have
// finished exiting. If/when any type of sandboxing is used for the
// Nuwa process (e.g., unsharing the network namespace there instead
// of for each content process, to save memory), this will need to be
// changed by moving the SandboxEarlyInit call to an earlier point.
if (aIsNuwa) {
return;
}
const SandboxInfo info = SandboxInfo::Get();
if (info.Test(SandboxInfo::kUnexpectedThreads)) {
return;
}
MOZ_RELEASE_ASSERT(IsSingleThreaded());
// Which kinds of resource isolation (of those that need to be set
// up at this point) can be used by this process?
bool canChroot = false;
bool canUnshareNet = false;
bool canUnshareIPC = false;
switch (aType) {
case GeckoProcessType_Default:
MOZ_ASSERT(false, "SandboxEarlyInit in parent process");
return;
#ifdef MOZ_GMP_SANDBOX
case GeckoProcessType_GMPlugin:
if (!info.Test(SandboxInfo::kEnabledForMedia)) {
break;
}
canUnshareNet = true;
canUnshareIPC = true;
// Need seccomp-bpf to intercept open().
canChroot = info.Test(SandboxInfo::kHasSeccompBPF);
break;
#endif
// In the future, content processes will be able to use some of
// these.
default:
// Other cases intentionally left blank.
break;
}
// If TSYNC is not supported, set up signal handler
// used to enable seccomp on each thread.
if (!info.Test(SandboxInfo::kHasSeccompTSync)) {
gSeccompTsyncBroadcastSignum = FindFreeSignalNumber();
if (gSeccompTsyncBroadcastSignum == 0) {
SANDBOX_LOG_ERROR("No available signal numbers!");
MOZ_CRASH();
}
void (*oldHandler)(int);
oldHandler = signal(gSeccompTsyncBroadcastSignum, SetThreadSandboxHandler);
if (oldHandler != SIG_DFL) {
// See the comment on FindFreeSignalNumber about race conditions.
SANDBOX_LOG_ERROR("signal %d in use by handler %p!\n",
gSeccompTsyncBroadcastSignum, oldHandler);
MOZ_CRASH();
}
}
// If there's nothing to do, then we're done.
if (!canChroot && !canUnshareNet && !canUnshareIPC) {
return;
}
{
LinuxCapabilities existingCaps;
if (existingCaps.GetCurrent() && existingCaps.AnyEffective()) {
SANDBOX_LOG_ERROR("PLEASE DO NOT RUN THIS AS ROOT. Strange things may"
" happen when capabilities are dropped.");
}
}
// If capabilities can't be gained, then nothing can be done.
if (!info.Test(SandboxInfo::kHasUserNamespaces)) {
// Drop any existing capabilities; unsharing the user namespace
// would implicitly drop them, so if we're running in a broken
// configuration where that would matter (e.g., running as root
// from a non-root-owned mode-0700 directory) this means it will
// break the same way on all kernels and be easier to troubleshoot.
LinuxCapabilities().SetCurrent();
return;
}
// The failure cases for the various unshares, and setting up the
// chroot helper, don't strictly need to be fatal -- but they also
// shouldn't fail on any reasonable system, so let's take the small
// risk of breakage over the small risk of quietly providing less
// security than we expect. (Unlike in SandboxInfo, this is in the
// child process, so crashing here isn't as severe a response to the
// unexpected.)
if (!UnshareUserNamespace()) {
SANDBOX_LOG_ERROR("unshare(CLONE_NEWUSER): %s", strerror(errno));
// If CanCreateUserNamespace (SandboxInfo.cpp) returns true, then
// the unshare shouldn't have failed.
MOZ_CRASH("unshare(CLONE_NEWUSER)");
}
// No early returns after this point! We need to drop the
// capabilities that were gained by unsharing the user namesapce.
if (canUnshareIPC && syscall(__NR_unshare, CLONE_NEWIPC) != 0) {
SANDBOX_LOG_ERROR("unshare(CLONE_NEWIPC): %s", strerror(errno));
MOZ_CRASH("unshare(CLONE_NEWIPC)");
}
if (canUnshareNet && syscall(__NR_unshare, CLONE_NEWNET) != 0) {
SANDBOX_LOG_ERROR("unshare(CLONE_NEWNET): %s", strerror(errno));
MOZ_CRASH("unshare(CLONE_NEWNET)");
}
if (canChroot) {
gChrootHelper = MakeUnique<SandboxChroot>();
if (!gChrootHelper->Prepare()) {
SANDBOX_LOG_ERROR("failed to set up chroot helper");
MOZ_CRASH("SandboxChroot::Prepare");
}
}
if (!LinuxCapabilities().SetCurrent()) {
SANDBOX_LOG_ERROR("dropping capabilities: %s", strerror(errno));
MOZ_CRASH("can't drop capabilities");
}
}
#ifdef MOZ_CONTENT_SANDBOX
/**
* Starts the seccomp sandbox for a content process. Should be called
* only once, and before any potentially harmful content is loaded.
*
* Will normally make the process exit on failure.
*/
bool
SetContentProcessSandbox(int aBrokerFd)
{
if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForContent)) {
if (aBrokerFd >= 0) {
close(aBrokerFd);
}
return false;
}
// This needs to live until the process exits.
static Maybe<SandboxBrokerClient> sBroker;
if (aBrokerFd >= 0) {
sBroker.emplace(aBrokerFd);
}
SetCurrentProcessSandbox(GetContentSandboxPolicy(sBroker.ptrOr(nullptr)));
return true;
}
#endif // MOZ_CONTENT_SANDBOX
#ifdef MOZ_GMP_SANDBOX
/**
* Starts the seccomp sandbox for a media plugin process. Should be
* called only once, and before any potentially harmful content is
* loaded -- including the plugin itself, if it's considered untrusted.
*
* The file indicated by aFilePath, if non-null, can be open()ed
* read-only, once, after the sandbox starts; it should be the .so
* file implementing the not-yet-loaded plugin.
*
* Will normally make the process exit on failure.
*/
void
SetMediaPluginSandbox(const char *aFilePath)
{
if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForMedia)) {
return;
}
MOZ_ASSERT(!gMediaPluginFile.mPath);
if (aFilePath) {
gMediaPluginFile.mPath = strdup(aFilePath);
gMediaPluginFile.mFd = open(aFilePath, O_RDONLY | O_CLOEXEC);
if (gMediaPluginFile.mFd == -1) {
SANDBOX_LOG_ERROR("failed to open plugin file %s: %s",
aFilePath, strerror(errno));
MOZ_CRASH();
}
} else {
gMediaPluginFile.mFd = -1;
}
// Finally, start the sandbox.
SetCurrentProcessSandbox(GetMediaSandboxPolicy(&gMediaPluginFile));
}
#endif // MOZ_GMP_SANDBOX
} // namespace mozilla