Bug 1401062 - Delete the old namespace/chroot code and reorganize sandbox init. r=gcp
This is mostly deletion, except for SandboxEarlyInit. The unshare()
parts are going away, and the "unexpected threads" workaround can go away
along with them, but the signal broadcast setup still needs to happen
early so we can prevent blocking the signal.
So, SandboxEarlyInit's contract changes slightly from "call before
any other threads exist" to "before any threads that might block all
signals", and everything that can be deferred to immedately before
sandbox startup is. As a result, some getenv()s change to PR_GetEnv
because there can be threads, and there is now an NSPR dependency.
(This may mean that mozglue can no longer interpose symbols in NSPR,
because libmozsandbox is preloaded, but I don't think we're doing that.)
MozReview-Commit-ID: 7e9u0qBNOqn
--- a/security/sandbox/linux/Sandbox.cpp
+++ b/security/sandbox/linux/Sandbox.cpp
@@ -1,28 +1,25 @@
/* -*- 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"
#ifdef MOZ_GMP_SANDBOX
#include "SandboxOpenedFiles.h"
#endif
#include "SandboxReporterClient.h"
-#include "SandboxUtil.h"
#include <dirent.h>
#ifdef NIGHTLY_BUILD
#include "dlfcn.h"
#endif
#include <errno.h>
#include <fcntl.h>
#include <linux/futex.h>
@@ -39,16 +36,17 @@
#include "mozilla/Array.h"
#include "mozilla/Atomics.h"
#include "mozilla/Range.h"
#include "mozilla/SandboxInfo.h"
#include "mozilla/Span.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
+#include "prenv.h"
#include "sandbox/linux/bpf_dsl/codegen.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/bpf_dsl/seccomp_macros.h"
#include "sandbox/linux/seccomp-bpf/trap.h"
#include "sandbox/linux/system_headers/linux_filter.h"
#include "sandbox/linux/system_headers/linux_seccomp.h"
@@ -83,17 +81,16 @@ mozilla::Atomic<int> gSeccompTsyncBroadc
namespace mozilla {
static bool gSandboxCrashOnError = false;
// This is initialized by SandboxSetCrashFunc().
SandboxCrashFunc gSandboxCrashFunc;
static SandboxReporterClient* gSandboxReporterClient;
-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
@@ -298,20 +295,16 @@ SetThreadSandboxHandler(int signum)
// 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;
@@ -451,22 +444,87 @@ ApplySandboxWithTSync(sock_fprog* aFilte
// 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();
}
}
+#ifdef NIGHTLY_BUILD
+static bool
+IsLibPresent(const char* aName)
+{
+ if (const auto handle = dlopen(aName, RTLD_LAZY | RTLD_NOLOAD)) {
+ dlclose(handle);
+ return true;
+ }
+ return false;
+}
+
+static const Array<const char*, 1> kLibsThatWillCrash {
+ "libesets_pac.so",
+};
+#endif // NIGHTLY_BUILD
+
+void
+SandboxEarlyInit() {
+ // If TSYNC is not supported, set up signal handler
+ // used to enable seccomp on each thread.
+ if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompTSync)) {
+ // The signal number has to be chosen early, so that the
+ // interceptions in SandboxHooks.cpp can prevent it from being
+ // masked.
+ const int tsyncSignum = FindFreeSignalNumber();
+ if (tsyncSignum == 0) {
+ SANDBOX_LOG_ERROR("No available signal numbers!");
+ MOZ_CRASH();
+ }
+ gSeccompTsyncBroadcastSignum = tsyncSignum;
+
+ // ...and the signal handler also needs to be installed now, to
+ // indicate to anything else looking for free signals that it's
+ // claimed.
+ void (*oldHandler)(int);
+ oldHandler = signal(tsyncSignum, SetThreadSandboxHandler);
+ if (oldHandler != SIG_DFL) {
+ // See the comment on FindFreeSignalNumber about race conditions.
+ SANDBOX_LOG_ERROR("signal %d in use by handler %p!\n",
+ tsyncSignum, oldHandler);
+ MOZ_CRASH();
+ }
+ }
+}
+
+static void
+SandboxLateInit() {
+#ifdef NIGHTLY_BUILD
+ gSandboxCrashOnError = true;
+ for (const char* name : kLibsThatWillCrash) {
+ if (IsLibPresent(name)) {
+ gSandboxCrashOnError = false;
+ break;
+ }
+ }
+#endif
+
+ if (const char* envVar = PR_GetEnv("MOZ_SANDBOX_CRASH_ON_ERROR")) {
+ if (envVar[0]) {
+ gSandboxCrashOnError = envVar[0] != '0';
+ }
+ }
+}
+
// Common code for sandbox startup.
static void
SetCurrentProcessSandbox(UniquePtr<sandbox::bpf_dsl::Policy> aPolicy)
{
MOZ_ASSERT(gSandboxCrashFunc);
MOZ_RELEASE_ASSERT(gSandboxReporterClient != nullptr);
+ SandboxLateInit();
// 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());
sandbox::CodeGen::Program program = compiler.Compile();
if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
sandbox::bpf_dsl::DumpBPF::PrintProgram(program);
@@ -501,175 +559,16 @@ SetCurrentProcessSandbox(UniquePtr<sandb
}
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");
-}
-
-#ifdef NIGHTLY_BUILD
-static bool
-IsLibPresent(const char* aName)
-{
- if (const auto handle = dlopen(aName, RTLD_LAZY | RTLD_NOLOAD)) {
- dlclose(handle);
- return true;
- }
- return false;
-}
-
-static const Array<const char*, 1> kLibsThatWillCrash {
- "libesets_pac.so",
-};
-#endif // NIGHTLY_BUILD
-
-void
-SandboxEarlyInit(GeckoProcessType aType)
-{
- const SandboxInfo info = SandboxInfo::Get();
- if (info.Test(SandboxInfo::kUnexpectedThreads)) {
- return;
- }
- MOZ_RELEASE_ASSERT(IsSingleThreaded());
-
- // Set gSandboxCrashOnError if appropriate. This doesn't need to
- // happen this early, but for now it's here so that I don't need to
- // add NSPR dependencies for PR_GetEnv.
- //
- // This also means that users with "unexpected threads" setups won't
- // crash even on nightly.
-#ifdef NIGHTLY_BUILD
- gSandboxCrashOnError = true;
- for (const char* name : kLibsThatWillCrash) {
- if (IsLibPresent(name)) {
- gSandboxCrashOnError = false;
- break;
- }
- }
-#endif
- if (const char* envVar = getenv("MOZ_SANDBOX_CRASH_ON_ERROR")) {
- if (envVar[0]) {
- gSandboxCrashOnError = envVar[0] != '0';
- }
- }
-
- // 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)) {
- const int tsyncSignum = FindFreeSignalNumber();
- if (tsyncSignum == 0) {
- SANDBOX_LOG_ERROR("No available signal numbers!");
- MOZ_CRASH();
- }
- gSeccompTsyncBroadcastSignum = tsyncSignum;
-
- void (*oldHandler)(int);
- oldHandler = signal(tsyncSignum, SetThreadSandboxHandler);
- if (oldHandler != SIG_DFL) {
- // See the comment on FindFreeSignalNumber about race conditions.
- SANDBOX_LOG_ERROR("signal %d in use by handler %p!\n",
- tsyncSignum, 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.
--- a/security/sandbox/linux/Sandbox.h
+++ b/security/sandbox/linux/Sandbox.h
@@ -12,18 +12,19 @@
#include <vector>
// This defines the entry points for a content process to start
// sandboxing itself. See also SandboxInfo.h for what parts of
// sandboxing are enabled/supported.
namespace mozilla {
-// This must be called early, while the process is still single-threaded.
-MOZ_EXPORT void SandboxEarlyInit(GeckoProcessType aType);
+// This must be called early, before glib creates any worker threads.
+// (See bug 1176099.)
+MOZ_EXPORT void SandboxEarlyInit();
#ifdef MOZ_CONTENT_SANDBOX
// Call only if SandboxInfo::CanSandboxContent() returns true.
// (No-op if the sandbox is disabled.)
// aBrokerFd is the filesystem broker client file descriptor,
// or -1 to allow direct filesystem access.
// isFileProcess determines whether we allow system wide file reads.
MOZ_EXPORT bool SetContentProcessSandbox(int aBrokerFd,
deleted file mode 100644
--- a/security/sandbox/linux/SandboxChroot.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-/* -*- 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 "SandboxChroot.h"
-
-#include "SandboxLogging.h"
-#include "LinuxCapabilities.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "base/posix/eintr_wrapper.h"
-#include "mozilla/Assertions.h"
-#include "mozilla/DebugOnly.h"
-#include "mozilla/NullPtr.h"
-
-#define MOZ_ALWAYS_ZERO(e) MOZ_ALWAYS_TRUE((e) == 0)
-
-namespace mozilla {
-
-SandboxChroot::SandboxChroot()
-{
- pthread_mutexattr_t attr;
- MOZ_ALWAYS_ZERO(pthread_mutexattr_init(&attr));
- MOZ_ALWAYS_ZERO(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK));
- MOZ_ALWAYS_ZERO(pthread_mutex_init(&mMutex, &attr));
- MOZ_ALWAYS_ZERO(pthread_cond_init(&mWakeup, nullptr));
- mCommand = NO_THREAD;
-}
-
-SandboxChroot::~SandboxChroot()
-{
- SendCommand(JUST_EXIT);
- MOZ_ALWAYS_ZERO(pthread_mutex_destroy(&mMutex));
- MOZ_ALWAYS_ZERO(pthread_cond_destroy(&mWakeup));
-}
-
-bool
-SandboxChroot::SendCommand(Command aComm)
-{
- MOZ_ALWAYS_ZERO(pthread_mutex_lock(&mMutex));
- if (mCommand == NO_THREAD) {
- MOZ_RELEASE_ASSERT(aComm == JUST_EXIT);
- MOZ_ALWAYS_ZERO(pthread_mutex_unlock(&mMutex));
- return false;
- }
- MOZ_ASSERT(mCommand == NO_COMMAND);
- mCommand = aComm;
- MOZ_ALWAYS_ZERO(pthread_mutex_unlock(&mMutex));
- MOZ_ALWAYS_ZERO(pthread_cond_signal(&mWakeup));
- void *retval;
- if (pthread_join(mThread, &retval) != 0 || retval != nullptr) {
- MOZ_CRASH("Failed to stop privileged chroot thread");
- }
- MOZ_ASSERT(mCommand == NO_THREAD);
-
- return true;
-}
-
-static void
-AlwaysClose(int fd)
-{
- if (IGNORE_EINTR(close(fd)) != 0) {
- SANDBOX_LOG_ERROR("close: %s", strerror(errno));
- MOZ_CRASH("failed to close()");
- }
-}
-
-static int
-OpenDeletedDirectory()
-{
- // We don't need this directory to persist between invocations of
- // the program (nor need it to be cleaned up if something goes wrong
- // here, because mkdtemp will choose a fresh name), so /tmp as
- // specified by FHS is adequate.
- //
- // However, this needs a filesystem where a deleted directory can
- // still be used, and /tmp is sometimes not that; e.g., aufs(5),
- // often used for containers, will cause the chroot() to fail with
- // ESTALE (bug 1162965). So this uses /dev/shm if possible instead.
- char tmpPath[] = "/tmp/mozsandbox.XXXXXX";
- char shmPath[] = "/dev/shm/mozsandbox.XXXXXX";
- char* path;
- if (mkdtemp(shmPath)) {
- path = shmPath;
- } else if (mkdtemp(tmpPath)) {
- path = tmpPath;
- } else {
- SANDBOX_LOG_ERROR("mkdtemp: %s", strerror(errno));
- return -1;
- }
- int fd = HANDLE_EINTR(open(path, O_RDONLY | O_DIRECTORY));
- if (fd < 0) {
- SANDBOX_LOG_ERROR("open %s: %s", path, strerror(errno));
- // Try to clean up. Shouldn't fail, but livable if it does.
- DebugOnly<bool> ok = HANDLE_EINTR(rmdir(path)) == 0;
- MOZ_ASSERT(ok);
- return -1;
- }
- if (HANDLE_EINTR(rmdir(path)) != 0) {
- SANDBOX_LOG_ERROR("rmdir %s: %s", path, strerror(errno));
- AlwaysClose(fd);
- return -1;
- }
- return fd;
-}
-
-bool
-SandboxChroot::Prepare()
-{
- LinuxCapabilities caps;
- if (!caps.GetCurrent() || !caps.Effective(CAP_SYS_CHROOT)) {
- SANDBOX_LOG_ERROR("don't have permission to chroot");
- return false;
- }
- mFd = OpenDeletedDirectory();
- if (mFd < 0) {
- SANDBOX_LOG_ERROR("failed to create empty directory for chroot");
- return false;
- }
- MOZ_ALWAYS_ZERO(pthread_mutex_lock(&mMutex));
- MOZ_ASSERT(mCommand == NO_THREAD);
- if (pthread_create(&mThread, nullptr, StaticThreadMain, this) != 0) {
- MOZ_ALWAYS_ZERO(pthread_mutex_unlock(&mMutex));
- SANDBOX_LOG_ERROR("pthread_create: %s", strerror(errno));
- return false;
- }
- while (mCommand != NO_COMMAND) {
- MOZ_ASSERT(mCommand == NO_THREAD);
- MOZ_ALWAYS_ZERO(pthread_cond_wait(&mWakeup, &mMutex));
- }
- MOZ_ALWAYS_ZERO(pthread_mutex_unlock(&mMutex));
- return true;
-}
-
-void
-SandboxChroot::Invoke()
-{
- MOZ_ALWAYS_TRUE(SendCommand(DO_CHROOT));
-}
-
-static bool
-ChrootToFileDesc(int fd)
-{
- if (fchdir(fd) != 0) {
- SANDBOX_LOG_ERROR("fchdir: %s", strerror(errno));
- return false;
- }
- if (chroot(".") != 0) {
- SANDBOX_LOG_ERROR("chroot: %s", strerror(errno));
- return false;
- }
- return true;
-}
-
-/* static */ void*
-SandboxChroot::StaticThreadMain(void* aVoidPtr)
-{
- static_cast<SandboxChroot*>(aVoidPtr)->ThreadMain();
- return nullptr;
-}
-
-void
-SandboxChroot::ThreadMain()
-{
- // First, drop everything that isn't CAP_SYS_CHROOT. (This code
- // assumes that this thread already has effective CAP_SYS_CHROOT,
- // because Prepare() checked for it before creating this thread.)
- LinuxCapabilities caps;
- caps.Effective(CAP_SYS_CHROOT) = true;
- if (!caps.SetCurrent()) {
- SANDBOX_LOG_ERROR("capset: %s", strerror(errno));
- MOZ_CRASH("Can't limit chroot thread's capabilities");
- }
-
- MOZ_ALWAYS_ZERO(pthread_mutex_lock(&mMutex));
- MOZ_ASSERT(mCommand == NO_THREAD);
- mCommand = NO_COMMAND;
- MOZ_ALWAYS_ZERO(pthread_cond_signal(&mWakeup));
- while (mCommand == NO_COMMAND) {
- MOZ_ALWAYS_ZERO(pthread_cond_wait(&mWakeup, &mMutex));
- }
- if (mCommand == DO_CHROOT) {
- MOZ_ASSERT(mFd >= 0);
- if (!ChrootToFileDesc(mFd)) {
- MOZ_CRASH("Failed to chroot");
- }
- } else {
- MOZ_ASSERT(mCommand == JUST_EXIT);
- }
- if (mFd >= 0) {
- AlwaysClose(mFd);
- mFd = -1;
- }
- mCommand = NO_THREAD;
- MOZ_ALWAYS_ZERO(pthread_mutex_unlock(&mMutex));
- // Drop the remaining capabilities; see note in SandboxChroot.h
- // about the potential unreliability of pthread_join.
- if (!LinuxCapabilities().SetCurrent()) {
- MOZ_CRASH("can't drop capabilities");
- }
-}
-
-} // namespace mozilla
-
-#undef MOZ_ALWAYS_ZERO
deleted file mode 100644
--- a/security/sandbox/linux/SandboxChroot.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_SandboxChroot_h
-#define mozilla_SandboxChroot_h
-
-#include <pthread.h>
-
-#include "mozilla/Attributes.h"
-
-// This class uses the chroot(2) system call and Linux namespaces to
-// revoke the process's access to the filesystem. It requires that
-// the process be able to create user namespaces; this is the
-// kHasUserNamespaces in SandboxInfo.h.
-//
-// Usage: call Prepare() from a thread with CAP_SYS_CHROOT in its
-// effective capability set, then later call Invoke() when ready to
-// drop filesystem access. Prepare() creates a thread to do the
-// chrooting, so the caller can (and should!) drop its own
-// capabilities afterwards. When Invoke() returns, the thread will
-// have exited.
-//
-// (Exception: on Android/B2G <= KitKat, because of how pthread_join
-// is implemented, the thread may still exist, but it will not have
-// capabilities. Accordingly, on such systems, be careful about
-// namespaces or other resources the thread might have inherited.)
-//
-// Prepare() can fail (return false); for example, if it doesn't have
-// CAP_SYS_CHROOT or if it can't create a directory to chroot into.
-//
-// The root directory will be empty and deleted, so the process will
-// not be able to create new entries in it regardless of permissions.
-
-namespace mozilla {
-
-class SandboxChroot final {
-public:
- SandboxChroot();
- ~SandboxChroot();
- bool Prepare();
- void Invoke();
-private:
- enum Command {
- NO_THREAD,
- NO_COMMAND,
- DO_CHROOT,
- JUST_EXIT,
- };
-
- pthread_t mThread;
- pthread_mutex_t mMutex;
- pthread_cond_t mWakeup;
- Command mCommand;
- int mFd;
-
- void ThreadMain();
- static void* StaticThreadMain(void* aVoidPtr);
- bool SendCommand(Command aComm);
-};
-
-} // namespace mozilla
-
-#endif // mozilla_SandboxChroot_h
--- a/security/sandbox/linux/SandboxInfo.cpp
+++ b/security/sandbox/linux/SandboxInfo.cpp
@@ -39,40 +39,16 @@
// to fail without the help of bugs in the kernel or system libraries.
//
// Regardless of assertion type, whatever condition caused it to fail
// should generally also disable the corresponding feature on builds
// that omit the assertion.
namespace mozilla {
-// Bug 1229136: this is copied from ../SandboxUtil.cpp to avoid
-// complicated build issues; renamespaced to avoid the possibility of
-// symbol conflict.
-namespace {
-
-static bool
-IsSingleThreaded()
-{
- // This detects the thread count indirectly. /proc/<pid>/task has a
- // subdirectory for each thread in <pid>'s thread group, and the
- // link count on the "task" directory follows Unix expectations: the
- // link from its parent, the "." link from itself, and the ".." link
- // from each subdirectory; thus, 2+N links for N threads.
- struct stat sb;
- if (stat("/proc/self/task", &sb) < 0) {
- MOZ_DIAGNOSTIC_ASSERT(false, "Couldn't access /proc/self/task!");
- return false;
- }
- MOZ_DIAGNOSTIC_ASSERT(sb.st_nlink >= 3);
- return sb.st_nlink == 3;
-}
-
-} // anonymous namespace
-
static bool
HasSeccompBPF()
{
// Allow simulating the absence of seccomp-bpf support, for testing.
if (getenv("MOZ_FAKE_NO_SANDBOX")) {
return false;
}
@@ -196,38 +172,33 @@ CanCreateUserNamespace()
if (!waitpid_ok) {
return false;
}
setenv(kCacheEnvName, "1", 1);
return true;
}
/* static */
-SandboxInfo SandboxInfo::sSingleton = SandboxInfo();
+const SandboxInfo SandboxInfo::sSingleton = SandboxInfo();
SandboxInfo::SandboxInfo() {
int flags = 0;
static_assert(sizeof(flags) >= sizeof(Flags), "enum Flags fits in an int");
if (HasSeccompBPF()) {
flags |= kHasSeccompBPF;
if (HasSeccompTSync()) {
flags |= kHasSeccompTSync;
}
}
- // Detect the threading-problem signal from the parent process.
- if (getenv("MOZ_SANDBOX_UNEXPECTED_THREADS")) {
- flags |= kUnexpectedThreads;
- } else {
- if (HasUserNamespaceSupport()) {
- flags |= kHasPrivilegedUserNamespaces;
- if (CanCreateUserNamespace()) {
- flags |= kHasUserNamespaces;
- }
+ if (HasUserNamespaceSupport()) {
+ flags |= kHasPrivilegedUserNamespaces;
+ if (CanCreateUserNamespace()) {
+ flags |= kHasUserNamespaces;
}
}
#ifdef MOZ_CONTENT_SANDBOX
// We can't use mozilla::IsContentSandboxEnabled() here because a)
// libmozsandbox can't depend on libxul, and b) this is called in a static
// initializer before the prefences service is ready.
if (!getenv("MOZ_DISABLE_CONTENT_SANDBOX")) {
@@ -244,33 +215,9 @@ SandboxInfo::SandboxInfo() {
#endif
if (getenv("MOZ_SANDBOX_LOGGING")) {
flags |= kVerbose;
}
mFlags = static_cast<Flags>(flags);
}
-/* static */ void
-SandboxInfo::ThreadingCheck()
-{
- // Allow MOZ_SANDBOX_UNEXPECTED_THREADS to be set manually for testing.
- if (IsSingleThreaded() &&
- !getenv("MOZ_SANDBOX_UNEXPECTED_THREADS")) {
- return;
- }
- SANDBOX_LOG_ERROR("unexpected multithreading found; this prevents using"
- " namespace sandboxing.%s",
- // getenv isn't thread-safe, but see below.
- getenv("LD_PRELOAD") ? " (If you're LD_PRELOAD'ing"
- " nVidia GL: that's not necessary for Gecko.)" : "");
-
- // Propagate this information for use by child processes. (setenv
- // isn't thread-safe, but other threads are from non-Gecko code so
- // they wouldn't be using NSPR; we have to hope for the best.)
- setenv("MOZ_SANDBOX_UNEXPECTED_THREADS", "1", 0);
- int flags = sSingleton.mFlags;
- flags |= kUnexpectedThreads;
- flags &= ~(kHasUserNamespaces | kHasPrivilegedUserNamespaces);
- sSingleton.mFlags = static_cast<Flags>(flags);
-}
-
} // namespace mozilla
--- a/security/sandbox/linux/SandboxInfo.h
+++ b/security/sandbox/linux/SandboxInfo.h
@@ -35,18 +35,17 @@ public:
// Kernel can atomically set system call filtering on entire thread group.
kHasSeccompTSync = 1 << 4,
// Can this process create user namespaces? (Man page user_namespaces(7).)
kHasUserNamespaces = 1 << 5,
// Could a more privileged process have user namespaces, even if we can't?
kHasPrivilegedUserNamespaces = 1 << 6,
// Env var MOZ_PERMISSIVE_CONTENT_SANDBOX
kPermissive = 1 << 7,
- // Something is creating threads when we need to still be single-threaded.
- kUnexpectedThreads = 1 << 8,
+ // (1 << 8) was kUnexpectedThreads
};
bool Test(Flags aFlag) const { return (mFlags & aFlag) == aFlag; }
// Returns true if SetContentProcessSandbox may be called.
bool CanSandboxContent() const
{
return !Test(kEnabledForContent) || Test(kHasSeccompBPF);
@@ -58,25 +57,17 @@ public:
return !Test(kEnabledForMedia) || Test(kHasSeccompBPF);
}
// For telemetry / crash annotation uses.
uint32_t AsInteger() const {
return mFlags;
}
- // For bug 1222500 or anything else like it: On desktop, this is
- // called in the parent process at a point when it should still be
- // single-threaded, to check that the SandboxEarlyInit() call in a
- // child process is early enough to be single-threaded. If not,
- // kUnexpectedThreads is set and affected flags (user namespaces;
- // possibly others in the future) are cleared.
- static MOZ_EXPORT void ThreadingCheck();
private:
enum Flags mFlags;
- // This should be const, but has to allow for ThreadingCheck.
- static MOZ_EXPORT SandboxInfo sSingleton;
+ static const MOZ_EXPORT SandboxInfo sSingleton;
SandboxInfo();
};
} // namespace mozilla
#endif // mozilla_SandboxInfo_h
deleted file mode 100644
--- a/security/sandbox/linux/SandboxUtil.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/* -*- 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 "SandboxUtil.h"
-
-#include "LinuxCapabilities.h"
-#include "LinuxSched.h"
-#include "SandboxLogging.h"
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "mozilla/Assertions.h"
-#include "mozilla/Sprintf.h"
-#include "mozilla/Unused.h"
-#include "sandbox/linux/system_headers/linux_syscalls.h"
-
-namespace mozilla {
-
-bool
-IsSingleThreaded()
-{
- // This detects the thread count indirectly. /proc/<pid>/task has a
- // subdirectory for each thread in <pid>'s thread group, and the
- // link count on the "task" directory follows Unix expectations: the
- // link from its parent, the "." link from itself, and the ".." link
- // from each subdirectory; thus, 2+N links for N threads.
- struct stat sb;
- if (stat("/proc/self/task", &sb) < 0) {
- MOZ_DIAGNOSTIC_ASSERT(false, "Couldn't access /proc/self/task!");
- return false;
- }
- MOZ_DIAGNOSTIC_ASSERT(sb.st_nlink >= 3);
- return sb.st_nlink == 3;
-}
-
-static bool
-WriteStringToFile(const char* aPath, const char* aStr, const size_t aLen)
-{
- int fd = open(aPath, O_WRONLY);
- if (fd < 0) {
- return false;
- }
- ssize_t written = write(fd, aStr, aLen);
- if (close(fd) != 0 || written != ssize_t(aLen)) {
- return false;
- }
- return true;
-}
-
-bool
-UnshareUserNamespace()
-{
- // The uid and gid need to be retrieved before the unshare; see
- // below.
- uid_t uid = getuid();
- gid_t gid = getgid();
- char buf[80];
-
- if (syscall(__NR_unshare, CLONE_NEWUSER) != 0) {
- return false;
- }
-
- // As mentioned in the header, this function sets up uid/gid
- // mappings that preserve the process's previous ids. Mapping the
- // uid/gid to something is necessary in order to nest user
- // namespaces (not used yet, but we'll need this in the future for
- // pid namespace support), and leaving the ids unchanged is the
- // least confusing option.
- //
- // In recent kernels (3.19, 3.18.2, 3.17.8), for security reasons,
- // establishing gid mappings will fail unless the process first
- // revokes its ability to call setgroups() by using a /proc node
- // added in the same set of patches.
- //
- // Note that /proc/self points to the thread group leader, not the
- // current thread. However, CLONE_NEWUSER can be unshared only in a
- // single-threaded process, so those are equivalent if we reach this
- // point.
- int len = SprintfLiteral(buf, "%u %u 1\n", uid, uid);
- if (len >= int(sizeof(buf)) || len < 0) {
- return false;
- }
- if (!WriteStringToFile("/proc/self/uid_map", buf, size_t(len))) {
- MOZ_CRASH("Failed to write /proc/self/uid_map");
- }
-
- Unused << WriteStringToFile("/proc/self/setgroups", "deny", 4);
-
- len = SprintfLiteral(buf, "%u %u 1\n", gid, gid);
- if (len >= int(sizeof(buf)) || len < 0) {
- return false;
- }
- if (!WriteStringToFile("/proc/self/gid_map", buf, size_t(len))) {
- MOZ_CRASH("Failed to write /proc/self/gid_map");
- }
- return true;
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/security/sandbox/linux/SandboxUtil.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_SandboxUtil_h
-#define mozilla_SandboxUtil_h
-
-namespace mozilla {
-
-bool IsSingleThreaded();
-
-// Unshare the user namespace, and set up id mappings so that the
-// process's subjective uid and gid are unchanged. This will always
-// fail if the process is multithreaded.
-bool UnshareUserNamespace();
-
-} // namespace mozilla
-
-#endif // mozilla_SandboxUtil_h
deleted file mode 100644
--- a/security/sandbox/linux/gtest/TestSandboxUtil.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/* -*- 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 "gtest/gtest.h"
-
-#include "SandboxUtil.h"
-#include "SandboxInfo.h"
-
-#include <pthread.h>
-
-namespace mozilla {
-
-// In order to test IsSingleThreaded when the test-running process is
-// single-threaded, before assorted XPCOM components have created many
-// additional threads, a static initializer is used.
-
-namespace {
-
-struct EarlyTest {
- bool mWasSingleThreaded;
-
- EarlyTest()
- : mWasSingleThreaded(IsSingleThreaded())
- { }
-};
-
-static const EarlyTest gEarlyTest;
-
-} // namespace
-
-TEST(SandboxUtil, IsSingleThreaded)
-{
- // If the test system if affected by kUnexpectedThreads, (1) there's
- // no point in doing this test, and (2) if that happens on Mozilla
- // CI then burning the tree is an appropriate response.
- ASSERT_FALSE(SandboxInfo::Get().Test(SandboxInfo::kUnexpectedThreads));
- EXPECT_TRUE(gEarlyTest.mWasSingleThreaded);
- EXPECT_FALSE(IsSingleThreaded());
-}
-
-} // namespace mozilla
--- a/security/sandbox/linux/gtest/moz.build
+++ b/security/sandbox/linux/gtest/moz.build
@@ -3,20 +3,18 @@
# 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/.
Library('sandboxtest')
SOURCES = [
'../SandboxBrokerClient.cpp',
- '../SandboxUtil.cpp',
'TestBroker.cpp',
'TestBrokerPolicy.cpp',
- 'TestSandboxUtil.cpp',
]
LOCAL_INCLUDES += [
'/security/sandbox/linux',
]
include('/ipc/chromium/chromium-config.mozbuild')
--- a/security/sandbox/linux/moz.build
+++ b/security/sandbox/linux/moz.build
@@ -8,16 +8,21 @@ SharedLibrary('mozsandbox')
# Depend on mozglue if and only if it's a shared library;
# this needs to match mozglue/build/moz.build:
if CONFIG['OS_TARGET'] == 'Android':
USE_LIBS += [
'mozglue',
]
+USE_LIBS += [
+ # For PR_GetEnv
+ 'nspr',
+]
+
EXPORTS.mozilla += [
'Sandbox.h',
'SandboxInfo.h',
]
SOURCES += [
'../chromium-shim/base/logging.cpp',
'../chromium-shim/base/threading/platform_thread_linux.cpp',
@@ -58,24 +63,22 @@ SOURCES += [
'../chromium/sandbox/linux/seccomp-bpf/die.cc',
'../chromium/sandbox/linux/seccomp-bpf/syscall.cc',
'../chromium/sandbox/linux/seccomp-bpf/trap.cc',
'../chromium/sandbox/linux/services/syscall_wrappers.cc',
'broker/SandboxBrokerCommon.cpp',
'LinuxCapabilities.cpp',
'Sandbox.cpp',
'SandboxBrokerClient.cpp',
- 'SandboxChroot.cpp',
'SandboxFilter.cpp',
'SandboxFilterUtil.cpp',
'SandboxHooks.cpp',
'SandboxInfo.cpp',
'SandboxLogging.cpp',
'SandboxReporterClient.cpp',
- 'SandboxUtil.cpp',
]
if CONFIG['MOZ_GMP_SANDBOX']:
SOURCES += [
'SandboxOpenedFiles.cpp',
]
# This copy of SafeSPrintf doesn't need to avoid the Chromium logging
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -4716,20 +4716,16 @@ XREMain::XRE_mainRun()
*/
int
XREMain::XRE_main(int argc, char* argv[], const BootstrapConfig& aConfig)
{
ScopedLogging log;
mozilla::LogModule::Init();
-#if defined(MOZ_SANDBOX) && defined(XP_LINUX) && !defined(ANDROID)
- SandboxInfo::ThreadingCheck();
-#endif
-
#ifdef MOZ_CODE_COVERAGE
CodeCoverageHandler::Init();
#endif
AUTO_PROFILER_INIT;
AUTO_PROFILER_LABEL("XREMain::XRE_main", OTHER);
nsresult rv = NS_OK;
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -85,19 +85,18 @@
#include "mozilla/sandboxing/loggingCallbacks.h"
#endif
#if defined(MOZ_CONTENT_SANDBOX)
#include "mozilla/SandboxSettings.h"
#include "mozilla/Preferences.h"
#endif
-#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
#include "mozilla/Sandbox.h"
-#include "mozilla/SandboxInfo.h"
#endif
#if defined(XP_LINUX)
#include <sys/prctl.h>
#ifndef PR_SET_PTRACER
#define PR_SET_PTRACER 0x59616d61
#endif
#ifndef PR_SET_PTRACER_ANY
@@ -346,18 +345,18 @@ XRE_InitChildProcess(int aArgc,
const XREChildData* aChildData)
{
NS_ENSURE_ARG_MIN(aArgc, 2);
NS_ENSURE_ARG_POINTER(aArgv);
NS_ENSURE_ARG_POINTER(aArgv[0]);
MOZ_ASSERT(aChildData);
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
- // This has to happen while we're still single-threaded.
- mozilla::SandboxEarlyInit(XRE_GetProcessType());
+ // This has to happen before glib thread pools are started.
+ mozilla::SandboxEarlyInit();
#endif
#ifdef MOZ_JPROF
// Call the code to install our handler
setupProfilingStuff();
#endif
#if defined(XP_WIN)