mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 970676 - Turn on sandboxing on all relevant threads. r=dhylands r=bent f=kang
This commit is contained in:
parent
b81ae908f1
commit
cffac485ff
@ -32,13 +32,12 @@
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
#if defined(XP_WIN)
|
||||
#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN)
|
||||
#define TARGET_SANDBOX_EXPORTS
|
||||
#include "mozilla/sandboxTarget.h"
|
||||
#elif defined(XP_UNIX) && !defined(XP_MACOSX)
|
||||
#include "mozilla/Sandbox.h"
|
||||
#endif
|
||||
#if defined(XP_LINUX)
|
||||
#include "mozilla/Sandbox.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/unused.h"
|
||||
@ -659,19 +658,18 @@ ContentChild::RecvSetProcessPrivileges(const ChildPrivileges& aPrivs)
|
||||
ChildPrivileges privs = (aPrivs == PRIVILEGES_DEFAULT) ?
|
||||
GeckoChildProcessHost::DefaultChildPrivileges() :
|
||||
aPrivs;
|
||||
#if defined(XP_LINUX)
|
||||
// SetCurrentProcessSandbox includes SetCurrentProcessPrivileges.
|
||||
// But we may want to move the sandbox initialization somewhere else
|
||||
// at some point; see bug 880808.
|
||||
SetCurrentProcessSandbox(privs);
|
||||
#else
|
||||
// If this fails, we die.
|
||||
SetCurrentProcessPrivileges(privs);
|
||||
|
||||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
#if defined(XP_WIN)
|
||||
mozilla::SandboxTarget::Instance()->StartSandbox();
|
||||
#else if defined(XP_UNIX) && !defined(XP_MACOSX)
|
||||
// SetCurrentProcessSandbox should be moved close to process initialization
|
||||
// time if/when possible. SetCurrentProcessPrivileges should probably be
|
||||
// moved as well. Right now this is set ONLY if we receive the
|
||||
// RecvSetProcessPrivileges message. See bug 880808.
|
||||
SetCurrentProcessSandbox();
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN)
|
||||
mozilla::SandboxTarget::Instance()->StartSandbox();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
* 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/Sandbox.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ptrace.h>
|
||||
@ -11,10 +13,19 @@
|
||||
#include <sys/syscall.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <linux/futex.h>
|
||||
#include <sys/time.h>
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "mozilla/dom/Exceptions.h"
|
||||
#include "nsString.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
#include "nsExceptionHandler.h"
|
||||
@ -26,10 +37,6 @@
|
||||
#endif
|
||||
#include "seccomp_filter.h"
|
||||
|
||||
#include "mozilla/dom/Exceptions.h"
|
||||
#include "nsString.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "linux_seccomp.h"
|
||||
#ifdef MOZ_LOGGING
|
||||
#define FORCE_PR_LOG 1
|
||||
@ -47,6 +54,8 @@ static PRLogModuleInfo* gSeccompSandboxLog;
|
||||
#define LOG_ERROR(args...)
|
||||
#endif
|
||||
|
||||
// Note: this ifdef includes most of the file.
|
||||
#ifdef MOZ_CONTENT_SANDBOX
|
||||
struct sock_filter seccomp_filter[] = {
|
||||
VALIDATE_ARCHITECTURE,
|
||||
EXAMINE_SYSCALL,
|
||||
@ -221,20 +230,201 @@ InstallSyscallFilter(void)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ANDROID) || defined(MOZ_CONTENT_SANDBOX)
|
||||
// Use signals for permissions that need to be set per-thread.
|
||||
static base::ChildPrivileges sSetPrivilegesTo;
|
||||
// The communication channel from the signal handler back to the main thread.
|
||||
static mozilla::Atomic<int> sSetSandboxDone;
|
||||
// about:memory has the first 3 RT signals. (We should allocate
|
||||
// signals centrally instead of hard-coding them like this.)
|
||||
static const int sSetSandboxSignum = SIGRTMIN + 3;
|
||||
#endif
|
||||
|
||||
static bool
|
||||
SetThreadSandbox(base::ChildPrivileges aPrivs, bool aIsMainThread)
|
||||
{
|
||||
bool didAnything = false;
|
||||
bool shouldSetPrivs = aIsMainThread;
|
||||
#if defined(ANDROID)
|
||||
shouldSetPrivs = true;
|
||||
#endif
|
||||
|
||||
if (shouldSetPrivs && (aIsMainThread || geteuid() == 0)) {
|
||||
SetCurrentProcessPrivileges(aPrivs);
|
||||
if (aPrivs != base::PRIVILEGES_INHERIT) {
|
||||
didAnything = true;
|
||||
}
|
||||
}
|
||||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
if (PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX") == nullptr &&
|
||||
prctl(PR_GET_SECCOMP, 0, 0, 0, 0) == 0) {
|
||||
if (InstallSyscallFilter() == 0) {
|
||||
didAnything = true;
|
||||
}
|
||||
/*
|
||||
* Bug 880797: when all B2G devices are required to support
|
||||
* seccomp-bpf, this should exit/crash if InstallSyscallFilter
|
||||
* returns nonzero (ifdef MOZ_WIDGET_GONK).
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
return didAnything;
|
||||
}
|
||||
|
||||
#if defined(ANDROID) || defined(MOZ_CONTENT_SANDBOX)
|
||||
static void
|
||||
SetThreadSandboxHandler(int signum)
|
||||
{
|
||||
// The non-zero number sent back to the main thread indicates
|
||||
// whether action was taken.
|
||||
if (SetThreadSandbox(sSetPrivilegesTo, /* main: */ false)) {
|
||||
sSetSandboxDone = 2;
|
||||
} else {
|
||||
sSetSandboxDone = 1;
|
||||
}
|
||||
// Wake up the main thread. See the FUTEX_WAIT call, below, for an
|
||||
// explanation.
|
||||
syscall(__NR_futex, reinterpret_cast<int*>(&sSetSandboxDone),
|
||||
FUTEX_WAKE, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
BroadcastSetThreadSandbox(base::ChildPrivileges aPrivs)
|
||||
{
|
||||
pid_t pid, tid;
|
||||
DIR *taskdp;
|
||||
struct dirent *de;
|
||||
|
||||
static_assert(sizeof(mozilla::Atomic<int>) == sizeof(int),
|
||||
"mozilla::Atomic<int> isn't represented by an int");
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
pid = getpid();
|
||||
taskdp = opendir("/proc/self/task");
|
||||
if (taskdp == nullptr) {
|
||||
LOG_ERROR("opendir /proc/self/task: %s\n", strerror(errno));
|
||||
MOZ_CRASH();
|
||||
}
|
||||
if (signal(sSetSandboxSignum, SetThreadSandboxHandler) != SIG_DFL) {
|
||||
LOG_ERROR("signal %d in use!\n", sSetSandboxSignum);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
// 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.
|
||||
sSetPrivilegesTo = aPrivs;
|
||||
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 == pid) {
|
||||
// Drop the main thread's privileges last, below, so
|
||||
// we can continue to signal other threads.
|
||||
continue;
|
||||
}
|
||||
// Reset the futex cell and signal.
|
||||
sSetSandboxDone = 0;
|
||||
if (syscall(__NR_tgkill, pid, tid, sSetSandboxSignum) != 0) {
|
||||
if (errno == ESRCH) {
|
||||
LOG_ERROR("Thread %d unexpectedly exited.", tid);
|
||||
// Rescan threads, in case it forked before exiting.
|
||||
sandboxProgress = true;
|
||||
continue;
|
||||
}
|
||||
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 sSetSandboxDone == 0, then sleep.
|
||||
if (syscall(__NR_futex, reinterpret_cast<int*>(&sSetSandboxDone),
|
||||
FUTEX_WAIT, 0, &futexTimeout) != 0) {
|
||||
if (errno != EWOULDBLOCK && errno != ETIMEDOUT && errno != EINTR) {
|
||||
LOG_ERROR("FUTEX_WAIT: %s\n", strerror(errno));
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
// Did the handler finish?
|
||||
if (sSetSandboxDone > 0) {
|
||||
if (sSetSandboxDone == 2) {
|
||||
sandboxProgress = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Has the thread ceased to exist?
|
||||
if (syscall(__NR_tgkill, pid, tid, 0) != 0) {
|
||||
if (errno == ESRCH) {
|
||||
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_nsec ||
|
||||
(now.tv_sec == timeLimit.tv_nsec &&
|
||||
now.tv_nsec > timeLimit.tv_nsec)) {
|
||||
LOG_ERROR("Thread %d unresponsive for %d seconds. Killing process.",
|
||||
tid, crashDelay);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
}
|
||||
rewinddir(taskdp);
|
||||
} while (sandboxProgress);
|
||||
unused << signal(sSetSandboxSignum, SIG_DFL);
|
||||
unused << closedir(taskdp);
|
||||
// And now, deprivilege the main thread:
|
||||
SetThreadSandbox(aPrivs, /* main: */ true);
|
||||
}
|
||||
#else
|
||||
static void
|
||||
BroadcastSetThreadSandbox(base::ChildPrivileges aPrivs)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
SetThreadSandbox(aPrivs, /* main: */ true);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Starts the seccomp sandbox for this process.
|
||||
* Generally called just after SetCurrentProcessPrivileges.
|
||||
* Starts the seccomp sandbox for this process and sets user/group-based privileges.
|
||||
* Should be called only once, and before any potentially harmful content is loaded.
|
||||
*
|
||||
* Should normally make the process exit on failure.
|
||||
*/
|
||||
void
|
||||
SetCurrentProcessSandbox(void)
|
||||
SetCurrentProcessSandbox(base::ChildPrivileges aPrivs)
|
||||
{
|
||||
if (PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX"))
|
||||
return;
|
||||
|
||||
#if !defined(ANDROID) && defined(PR_LOGGING)
|
||||
if (!gSeccompSandboxLog) {
|
||||
gSeccompSandboxLog = PR_NewLogModule("SeccompSandbox");
|
||||
@ -242,29 +432,14 @@ SetCurrentProcessSandbox(void)
|
||||
PR_ASSERT(gSeccompSandboxLog);
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_CONTENT_SANDBOX_REPORTER
|
||||
#if defined(MOZ_CONTENT_SANDBOX) && defined(MOZ_CONTENT_SANDBOX_REPORTER)
|
||||
if (InstallSyscallReporter()) {
|
||||
LOG_ERROR("install_syscall_reporter() failed\n");
|
||||
/* This is disabled so that we do not exit if seccomp-bpf is not available
|
||||
* This will be re-enabled when all B2G devices are required to support seccomp-bpf
|
||||
* See bug 880797 for reversal
|
||||
*/
|
||||
|
||||
/* _exit(127); */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (InstallSyscallFilter()) {
|
||||
LOG_ERROR("install_syscall_filter() failed\n");
|
||||
/* This is disabled so that we do not exit if seccomp-bpf is not available
|
||||
* This will be re-enabled when all B2G devices are required to support seccomp-bpf
|
||||
* See bug 880797 for reversal
|
||||
*/
|
||||
|
||||
/* _exit(127); */
|
||||
}
|
||||
|
||||
BroadcastSetThreadSandbox(aPrivs);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -7,9 +7,11 @@
|
||||
#ifndef mozilla_Sandbox_h
|
||||
#define mozilla_Sandbox_h
|
||||
|
||||
#include "base/process_util.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void SetCurrentProcessSandbox(void);
|
||||
void SetCurrentProcessSandbox(base::ChildPrivileges aPrivs);
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -13,13 +13,17 @@
|
||||
/* Architecture-specific frequently used syscalls */
|
||||
#if defined(__arm__)
|
||||
#define SECCOMP_WHITELIST_ARCH_HIGH \
|
||||
ALLOW_SYSCALL(recvmsg), \
|
||||
ALLOW_SYSCALL(sendmsg), \
|
||||
ALLOW_SYSCALL(mmap2),
|
||||
#elif defined(__i386__)
|
||||
#define SECCOMP_WHITELIST_ARCH_HIGH \
|
||||
ALLOW_SYSCALL(ipc), \
|
||||
ALLOW_SYSCALL(mmap2),
|
||||
#elif defined(__x86_64__)
|
||||
#define SECCOMP_WHITELIST_ARCH_HIGH
|
||||
#define SECCOMP_WHITELIST_ARCH_HIGH \
|
||||
ALLOW_SYSCALL(recvmsg), \
|
||||
ALLOW_SYSCALL(sendmsg),
|
||||
#else
|
||||
#define SECCOMP_WHITELIST_ARCH_HIGH
|
||||
#endif
|
||||
@ -82,7 +86,6 @@
|
||||
ALLOW_SYSCALL(stat64), \
|
||||
ALLOW_SYSCALL(lstat64), \
|
||||
ALLOW_SYSCALL(socketpair), \
|
||||
ALLOW_SYSCALL(sendmsg), \
|
||||
ALLOW_SYSCALL(sigprocmask), \
|
||||
DENY_SYSCALL(socket, EACCES),
|
||||
#elif defined(__i386__)
|
||||
@ -94,7 +97,6 @@
|
||||
#else
|
||||
#define SECCOMP_WHITELIST_ARCH_TOREMOVE \
|
||||
ALLOW_SYSCALL(socketpair), \
|
||||
ALLOW_SYSCALL(sendmsg), \
|
||||
DENY_SYSCALL(socket, EACCES),
|
||||
#endif
|
||||
|
||||
@ -119,12 +121,14 @@
|
||||
|
||||
#define SECCOMP_WHITELIST_B2G_MED \
|
||||
ALLOW_SYSCALL(getpid), \
|
||||
ALLOW_SYSCALL(rt_sigreturn),
|
||||
ALLOW_SYSCALL(rt_sigreturn), \
|
||||
ALLOW_SYSCALL(poll),
|
||||
|
||||
#define SECCOMP_WHITELIST_B2G_LOW \
|
||||
ALLOW_SYSCALL(sendto), \
|
||||
ALLOW_SYSCALL(recvfrom), \
|
||||
ALLOW_SYSCALL(getdents64), \
|
||||
ALLOW_SYSCALL(epoll_ctl), \
|
||||
ALLOW_SYSCALL(sched_yield), \
|
||||
ALLOW_SYSCALL(sched_getscheduler), \
|
||||
ALLOW_SYSCALL(sched_setscheduler),
|
||||
@ -163,7 +167,6 @@
|
||||
ALLOW_SYSCALL(fstat), \
|
||||
ALLOW_SYSCALL(readlink), \
|
||||
ALLOW_SYSCALL(getsockname), \
|
||||
ALLOW_SYSCALL(recvmsg), \
|
||||
/* duplicate rt_sigaction in SECCOMP_WHITELIST_PROFILING */ \
|
||||
ALLOW_SYSCALL(rt_sigaction), \
|
||||
ALLOW_SYSCALL(getuid), \
|
||||
|
@ -6,7 +6,7 @@
|
||||
if CONFIG['LIBXUL_SDK']:
|
||||
error('toolkit.mozbuild is not compatible with --enable-libxul-sdk=')
|
||||
|
||||
if CONFIG['MOZ_CONTENT_SANDBOX']:
|
||||
if CONFIG['MOZ_CONTENT_SANDBOX'] or CONFIG['OS_ARCH'] == 'Linux':
|
||||
add_tier_dir('sandbox', 'security/sandbox')
|
||||
|
||||
# Depends on NSS and NSPR, and must be built after sandbox or else B2G emulator
|
||||
|
Loading…
Reference in New Issue
Block a user