summaryrefslogtreecommitdiffstats
path: root/security/sandbox/linux/SandboxFilter.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /security/sandbox/linux/SandboxFilter.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'security/sandbox/linux/SandboxFilter.cpp')
-rw-r--r--security/sandbox/linux/SandboxFilter.cpp961
1 files changed, 961 insertions, 0 deletions
diff --git a/security/sandbox/linux/SandboxFilter.cpp b/security/sandbox/linux/SandboxFilter.cpp
new file mode 100644
index 000000000..f8db9dc80
--- /dev/null
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -0,0 +1,961 @@
+/* -*- 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 "SandboxFilter.h"
+#include "SandboxFilterUtil.h"
+
+#include "SandboxBrokerClient.h"
+#include "SandboxInfo.h"
+#include "SandboxInternal.h"
+#include "SandboxLogging.h"
+
+#include "mozilla/UniquePtr.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/ipc.h>
+#include <linux/net.h>
+#include <linux/prctl.h>
+#include <linux/sched.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+using namespace sandbox::bpf_dsl;
+#define CASES SANDBOX_BPF_DSL_CASES
+
+// Fill in defines in case of old headers.
+// (Warning: these are wrong on PA-RISC.)
+#ifndef MADV_NOHUGEPAGE
+#define MADV_NOHUGEPAGE 15
+#endif
+#ifndef MADV_DONTDUMP
+#define MADV_DONTDUMP 16
+#endif
+
+// Added in Linux 4.5; see bug 1303813.
+#ifndef MADV_FREE
+#define MADV_FREE 8
+#endif
+
+#ifndef PR_SET_PTRACER
+#define PR_SET_PTRACER 0x59616d61
+#endif
+
+// To avoid visual confusion between "ifdef ANDROID" and "ifndef ANDROID":
+#ifndef ANDROID
+#define DESKTOP
+#endif
+
+// This file defines the seccomp-bpf system call filter policies.
+// See also SandboxFilterUtil.h, for the CASES_FOR_* macros and
+// SandboxFilterBase::Evaluate{Socket,Ipc}Call.
+//
+// One important difference from how Chromium bpf_dsl filters are
+// normally interpreted: returning -ENOSYS from a Trap() handler
+// indicates an unexpected system call; SigSysHandler() in Sandbox.cpp
+// will detect this, request a crash dump, and terminate the process.
+// This does not apply to using Error(ENOSYS) in the policy, so that
+// can be used if returning an actual ENOSYS is needed.
+
+namespace mozilla {
+
+// This class whitelists everything used by the sandbox itself, by the
+// core IPC code, by the crash reporter, or other core code.
+class SandboxPolicyCommon : public SandboxPolicyBase
+{
+protected:
+ typedef const sandbox::arch_seccomp_data& ArgsRef;
+
+ static intptr_t BlockedSyscallTrap(ArgsRef aArgs, void *aux) {
+ MOZ_ASSERT(!aux);
+ return -ENOSYS;
+ }
+
+private:
+#if defined(ANDROID) && ANDROID_VERSION < 16
+ // Bug 1093893: Translate tkill to tgkill for pthread_kill; fixed in
+ // bionic commit 10c8ce59a (in JB and up; API level 16 = Android 4.1).
+ static intptr_t TKillCompatTrap(const sandbox::arch_seccomp_data& aArgs,
+ void *aux)
+ {
+ return syscall(__NR_tgkill, getpid(), aArgs.args[0], aArgs.args[1]);
+ }
+#endif
+
+ static intptr_t SetNoNewPrivsTrap(ArgsRef& aArgs, void* aux) {
+ if (gSetSandboxFilter == nullptr) {
+ // Called after BroadcastSetThreadSandbox finished, therefore
+ // not our doing and not expected.
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ // Signal that the filter is already in place.
+ return -ETXTBSY;
+ }
+
+public:
+ virtual ResultExpr InvalidSyscall() const override {
+ return Trap(BlockedSyscallTrap, nullptr);
+ }
+
+ virtual ResultExpr ClonePolicy(ResultExpr failPolicy) const {
+ // Allow use for simple thread creation (pthread_create) only.
+
+ // WARNING: s390 and cris pass the flags in the second arg -- see
+ // CLONE_BACKWARDS2 in arch/Kconfig in the kernel source -- but we
+ // don't support seccomp-bpf on those archs yet.
+ Arg<int> flags(0);
+
+ // The glibc source hasn't changed the thread creation clone flags
+ // since 2004, so this *should* be safe to hard-code. Bionic's
+ // value has changed a few times, and has converged on the same one
+ // as glibc; allow any of them.
+ static const int flags_common = CLONE_VM | CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM;
+ static const int flags_modern = flags_common | CLONE_SETTLS |
+ CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
+
+ // Can't use CASES here because its decltype magic infers const
+ // int instead of regular int and bizarre voluminous errors issue
+ // forth from the depths of the standard library implementation.
+ return Switch(flags)
+#ifdef ANDROID
+ .Case(flags_common | CLONE_DETACHED, Allow()) // <= JB 4.2
+ .Case(flags_common, Allow()) // JB 4.3 or KK 4.4
+#endif
+ .Case(flags_modern, Allow()) // Android L or glibc
+ .Default(failPolicy);
+ }
+
+ virtual ResultExpr PrctlPolicy() const {
+ // Note: this will probably need PR_SET_VMA if/when it's used on
+ // Android without being overridden by an allow-all policy, and
+ // the constant will need to be defined locally.
+ Arg<int> op(0);
+ return Switch(op)
+ .CASES((PR_GET_SECCOMP, // BroadcastSetThreadSandbox, etc.
+ PR_SET_NAME, // Thread creation
+ PR_SET_DUMPABLE, // Crash reporting
+ PR_SET_PTRACER), // Debug-mode crash handling
+ Allow())
+ .Default(InvalidSyscall());
+ }
+
+ virtual Maybe<ResultExpr> EvaluateSocketCall(int aCall) const override {
+ switch (aCall) {
+ case SYS_RECVMSG:
+ case SYS_SENDMSG:
+ return Some(Allow());
+ default:
+ return Nothing();
+ }
+ }
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ // Timekeeping
+ case __NR_clock_gettime: {
+ Arg<clockid_t> clk_id(0);
+ return If(clk_id == CLOCK_MONOTONIC, Allow())
+#ifdef CLOCK_MONOTONIC_COARSE
+ .ElseIf(clk_id == CLOCK_MONOTONIC_COARSE, Allow())
+#endif
+ .ElseIf(clk_id == CLOCK_PROCESS_CPUTIME_ID, Allow())
+ .ElseIf(clk_id == CLOCK_REALTIME, Allow())
+#ifdef CLOCK_REALTIME_COARSE
+ .ElseIf(clk_id == CLOCK_REALTIME_COARSE, Allow())
+#endif
+ .ElseIf(clk_id == CLOCK_THREAD_CPUTIME_ID, Allow())
+ .Else(InvalidSyscall());
+ }
+ case __NR_gettimeofday:
+#ifdef __NR_time
+ case __NR_time:
+#endif
+ case __NR_nanosleep:
+ return Allow();
+
+ // Thread synchronization
+ case __NR_futex:
+ // FIXME: This could be more restrictive....
+ return Allow();
+
+ // Asynchronous I/O
+ case __NR_epoll_wait:
+ case __NR_epoll_pwait:
+ case __NR_epoll_ctl:
+ case __NR_ppoll:
+ case __NR_poll:
+ return Allow();
+
+ // Used when requesting a crash dump.
+ case __NR_pipe:
+ return Allow();
+
+ // Metadata of opened files
+ CASES_FOR_fstat:
+ return Allow();
+
+ // Simple I/O
+ case __NR_write:
+ case __NR_read:
+ case __NR_readv:
+ case __NR_writev: // see SandboxLogging.cpp
+ CASES_FOR_lseek:
+ return Allow();
+
+ // Memory mapping
+ CASES_FOR_mmap:
+ case __NR_munmap:
+ return Allow();
+
+ // Signal handling
+#if defined(ANDROID) || defined(MOZ_ASAN)
+ case __NR_sigaltstack:
+#endif
+ CASES_FOR_sigreturn:
+ CASES_FOR_sigprocmask:
+ CASES_FOR_sigaction:
+ return Allow();
+
+ // Send signals within the process (raise(), profiling, etc.)
+ case __NR_tgkill: {
+ Arg<pid_t> tgid(0);
+ return If(tgid == getpid(), Allow())
+ .Else(InvalidSyscall());
+ }
+
+#if defined(ANDROID) && ANDROID_VERSION < 16
+ // Polyfill with tgkill; see above.
+ case __NR_tkill:
+ return Trap(TKillCompatTrap, nullptr);
+#endif
+
+ // Yield
+ case __NR_sched_yield:
+ return Allow();
+
+ // Thread creation.
+ case __NR_clone:
+ return ClonePolicy(InvalidSyscall());
+
+ // More thread creation.
+#ifdef __NR_set_robust_list
+ case __NR_set_robust_list:
+ return Allow();
+#endif
+#ifdef ANDROID
+ case __NR_set_tid_address:
+ return Allow();
+#endif
+
+ // prctl
+ case __NR_prctl: {
+ if (SandboxInfo::Get().Test(SandboxInfo::kHasSeccompTSync)) {
+ return PrctlPolicy();
+ }
+
+ Arg<int> option(0);
+ return If(option == PR_SET_NO_NEW_PRIVS,
+ Trap(SetNoNewPrivsTrap, nullptr))
+ .Else(PrctlPolicy());
+ }
+
+ // NSPR can call this when creating a thread, but it will accept a
+ // polite "no".
+ case __NR_getpriority:
+ // But if thread creation races with sandbox startup, that call
+ // could succeed, and then we get one of these:
+ case __NR_setpriority:
+ return Error(EACCES);
+
+ // Stack bounds are obtained via pthread_getattr_np, which calls
+ // this but doesn't actually need it:
+ case __NR_sched_getaffinity:
+ return Error(ENOSYS);
+
+ // Read own pid/tid.
+ case __NR_getpid:
+ case __NR_gettid:
+ return Allow();
+
+ // Discard capabilities
+ case __NR_close:
+ return Allow();
+
+ // Machine-dependent stuff
+#ifdef __arm__
+ case __ARM_NR_breakpoint:
+ case __ARM_NR_cacheflush:
+ case __ARM_NR_usr26: // FIXME: do we actually need this?
+ case __ARM_NR_usr32:
+ case __ARM_NR_set_tls:
+ return Allow();
+#endif
+
+ // Needed when being debugged:
+ case __NR_restart_syscall:
+ return Allow();
+
+ // Terminate threads or the process
+ case __NR_exit:
+ case __NR_exit_group:
+ return Allow();
+
+#ifdef MOZ_ASAN
+ // ASAN's error reporter wants to know if stderr is a tty.
+ case __NR_ioctl: {
+ Arg<int> fd(0);
+ return If(fd == STDERR_FILENO, Allow())
+ .Else(InvalidSyscall());
+ }
+
+ // ...and before compiler-rt r209773, it will call readlink on
+ // /proc/self/exe and use the cached value only if that fails:
+ case __NR_readlink:
+ case __NR_readlinkat:
+ return Error(ENOENT);
+
+ // ...and if it found an external symbolizer, it will try to run it:
+ // (See also bug 1081242 comment #7.)
+ CASES_FOR_stat:
+ return Error(ENOENT);
+#endif
+
+ default:
+ return SandboxPolicyBase::EvaluateSyscall(sysno);
+ }
+ }
+};
+
+// The process-type-specific syscall rules start here:
+
+#ifdef MOZ_CONTENT_SANDBOX
+// The seccomp-bpf filter for content processes is not a true sandbox
+// on its own; its purpose is attack surface reduction and syscall
+// interception in support of a semantic sandboxing layer. On B2G
+// this is the Android process permission model; on desktop,
+// namespaces and chroot() will be used.
+class ContentSandboxPolicy : public SandboxPolicyCommon {
+ SandboxBrokerClient* mBroker;
+
+ // Trap handlers for filesystem brokering.
+ // (The amount of code duplication here could be improved....)
+#ifdef __NR_open
+ static intptr_t OpenTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto flags = static_cast<int>(aArgs.args[1]);
+ return broker->Open(path, flags);
+ }
+#endif
+
+ static intptr_t OpenAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ auto path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto flags = static_cast<int>(aArgs.args[2]);
+ if (fd != AT_FDCWD && path[0] != '/') {
+ SANDBOX_LOG_ERROR("unsupported fd-relative openat(%d, \"%s\", 0%o)",
+ fd, path, flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return broker->Open(path, flags);
+ }
+
+#ifdef __NR_access
+ static intptr_t AccessTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto mode = static_cast<int>(aArgs.args[1]);
+ return broker->Access(path, mode);
+ }
+#endif
+
+ static intptr_t AccessAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ auto path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto mode = static_cast<int>(aArgs.args[2]);
+ // Linux's faccessat syscall has no "flags" argument. Attempting
+ // to handle the flags != 0 case is left to userspace; this is
+ // impossible to do correctly in all cases, but that's not our
+ // problem.
+ if (fd != AT_FDCWD && path[0] != '/') {
+ SANDBOX_LOG_ERROR("unsupported fd-relative faccessat(%d, \"%s\", %d)",
+ fd, path, mode);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return broker->Access(path, mode);
+ }
+
+ static intptr_t StatTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto buf = reinterpret_cast<statstruct*>(aArgs.args[1]);
+ return broker->Stat(path, buf);
+ }
+
+ static intptr_t LStatTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto buf = reinterpret_cast<statstruct*>(aArgs.args[1]);
+ return broker->LStat(path, buf);
+ }
+
+ static intptr_t StatAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ auto path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto buf = reinterpret_cast<statstruct*>(aArgs.args[2]);
+ auto flags = static_cast<int>(aArgs.args[3]);
+ if (fd != AT_FDCWD && path[0] != '/') {
+ SANDBOX_LOG_ERROR("unsupported fd-relative fstatat(%d, \"%s\", %p, %d)",
+ fd, path, buf, flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ if ((flags & ~AT_SYMLINK_NOFOLLOW) != 0) {
+ SANDBOX_LOG_ERROR("unsupported flags %d in fstatat(%d, \"%s\", %p, %d)",
+ (flags & ~AT_SYMLINK_NOFOLLOW), fd, path, buf, flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return (flags & AT_SYMLINK_NOFOLLOW) == 0
+ ? broker->Stat(path, buf)
+ : broker->LStat(path, buf);
+ }
+
+ static intptr_t ChmodTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto mode = static_cast<mode_t>(aArgs.args[1]);
+ return broker->Chmod(path, mode);
+ }
+
+ static intptr_t LinkTrap(ArgsRef aArgs, void *aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto path2 = reinterpret_cast<const char*>(aArgs.args[1]);
+ return broker->Link(path, path2);
+ }
+
+ static intptr_t SymlinkTrap(ArgsRef aArgs, void *aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto path2 = reinterpret_cast<const char*>(aArgs.args[1]);
+ return broker->Symlink(path, path2);
+ }
+
+ static intptr_t RenameTrap(ArgsRef aArgs, void *aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto path2 = reinterpret_cast<const char*>(aArgs.args[1]);
+ return broker->Rename(path, path2);
+ }
+
+ static intptr_t MkdirTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto mode = static_cast<mode_t>(aArgs.args[1]);
+ return broker->Mkdir(path, mode);
+ }
+
+ static intptr_t RmdirTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ return broker->Rmdir(path);
+ }
+
+ static intptr_t UnlinkTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ return broker->Unlink(path);
+ }
+
+ static intptr_t ReadlinkTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto buf = reinterpret_cast<char*>(aArgs.args[1]);
+ auto size = static_cast<size_t>(aArgs.args[2]);
+ return broker->Readlink(path, buf, size);
+ }
+
+ static intptr_t GetPPidTrap(ArgsRef aArgs, void* aux) {
+ // In a pid namespace, getppid() will return 0. We will return 0 instead
+ // of the real parent pid to see what breaks when we introduce the
+ // pid namespace (Bug 1151624).
+ return 0;
+ }
+
+public:
+ explicit ContentSandboxPolicy(SandboxBrokerClient* aBroker):mBroker(aBroker) { }
+ virtual ~ContentSandboxPolicy() { }
+ virtual ResultExpr PrctlPolicy() const override {
+ // Ideally this should be restricted to a whitelist, but content
+ // uses enough things that it's not trivial to determine it.
+ return Allow();
+ }
+ virtual Maybe<ResultExpr> EvaluateSocketCall(int aCall) const override {
+ switch(aCall) {
+ case SYS_RECVFROM:
+ case SYS_SENDTO:
+ return Some(Allow());
+
+ case SYS_SOCKETPAIR: {
+ // See bug 1066750.
+ if (!kSocketCallHasArgs) {
+ // We can't filter the args if the platform passes them by pointer.
+ return Some(Allow());
+ }
+ Arg<int> domain(0), type(1);
+ return Some(If(AllOf(domain == AF_UNIX,
+ AnyOf(type == SOCK_STREAM, type == SOCK_SEQPACKET)),
+ Allow())
+ .Else(InvalidSyscall()));
+ }
+
+#ifdef ANDROID
+ case SYS_SOCKET:
+ return Some(Error(EACCES));
+#else // #ifdef DESKTOP
+ case SYS_RECV:
+ case SYS_SEND:
+ case SYS_SOCKET: // DANGEROUS
+ case SYS_CONNECT: // DANGEROUS
+ case SYS_ACCEPT:
+ case SYS_ACCEPT4:
+ case SYS_BIND:
+ case SYS_LISTEN:
+ case SYS_GETSOCKOPT:
+ case SYS_SETSOCKOPT:
+ case SYS_GETSOCKNAME:
+ case SYS_GETPEERNAME:
+ case SYS_SHUTDOWN:
+ return Some(Allow());
+#endif
+ default:
+ return SandboxPolicyCommon::EvaluateSocketCall(aCall);
+ }
+ }
+
+#ifdef DESKTOP
+ virtual Maybe<ResultExpr> EvaluateIpcCall(int aCall) const override {
+ switch(aCall) {
+ // These are a problem: SysV shared memory follows the Unix
+ // "same uid policy" and can't be restricted/brokered like file
+ // access. But the graphics layer might not be using them
+ // anymore; this needs to be studied.
+ case SHMGET:
+ case SHMCTL:
+ case SHMAT:
+ case SHMDT:
+ case SEMGET:
+ case SEMCTL:
+ case SEMOP:
+ case MSGGET:
+ return Some(Allow());
+ default:
+ return SandboxPolicyCommon::EvaluateIpcCall(aCall);
+ }
+ }
+#endif
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ if (mBroker) {
+ // Have broker; route the appropriate syscalls to it.
+ switch (sysno) {
+ case __NR_open:
+ return Trap(OpenTrap, mBroker);
+ case __NR_openat:
+ return Trap(OpenAtTrap, mBroker);
+ case __NR_access:
+ return Trap(AccessTrap, mBroker);
+ case __NR_faccessat:
+ return Trap(AccessAtTrap, mBroker);
+ CASES_FOR_stat:
+ return Trap(StatTrap, mBroker);
+ CASES_FOR_lstat:
+ return Trap(LStatTrap, mBroker);
+ CASES_FOR_fstatat:
+ return Trap(StatAtTrap, mBroker);
+ case __NR_chmod:
+ return Trap(ChmodTrap, mBroker);
+ case __NR_link:
+ return Trap(LinkTrap, mBroker);
+ case __NR_mkdir:
+ return Trap(MkdirTrap, mBroker);
+ case __NR_symlink:
+ return Trap(SymlinkTrap, mBroker);
+ case __NR_rename:
+ return Trap(RenameTrap, mBroker);
+ case __NR_rmdir:
+ return Trap(RmdirTrap, mBroker);
+ case __NR_unlink:
+ return Trap(UnlinkTrap, mBroker);
+ case __NR_readlink:
+ return Trap(ReadlinkTrap, mBroker);
+ }
+ } else {
+ // No broker; allow the syscalls directly. )-:
+ switch(sysno) {
+ case __NR_open:
+ case __NR_openat:
+ case __NR_access:
+ case __NR_faccessat:
+ CASES_FOR_stat:
+ CASES_FOR_lstat:
+ CASES_FOR_fstatat:
+ case __NR_chmod:
+ case __NR_link:
+ case __NR_mkdir:
+ case __NR_symlink:
+ case __NR_rename:
+ case __NR_rmdir:
+ case __NR_unlink:
+ case __NR_readlink:
+ return Allow();
+ }
+ }
+
+ switch (sysno) {
+#ifdef DESKTOP
+ case __NR_getppid:
+ return Trap(GetPPidTrap, nullptr);
+
+ // Filesystem syscalls that need more work to determine who's
+ // using them, if they need to be, and what we intend to about it.
+ case __NR_getcwd:
+ CASES_FOR_statfs:
+ CASES_FOR_fstatfs:
+ case __NR_quotactl:
+ CASES_FOR_fchown:
+ case __NR_fchmod:
+ case __NR_flock:
+#endif
+ return Allow();
+
+ case __NR_readlinkat:
+#ifdef DESKTOP
+ // Bug 1290896
+ return Allow();
+#else
+ // Workaround for bug 964455:
+ return Error(EINVAL);
+#endif
+
+ CASES_FOR_select:
+ case __NR_pselect6:
+ return Allow();
+
+ CASES_FOR_getdents:
+ CASES_FOR_ftruncate:
+ case __NR_writev:
+ case __NR_pread64:
+#ifdef DESKTOP
+ case __NR_pwrite64:
+ case __NR_readahead:
+#endif
+ return Allow();
+
+ case __NR_ioctl:
+ // ioctl() is for GL. Remove when GL proxy is implemented.
+ // Additionally ioctl() might be a place where we want to have
+ // argument filtering
+ return Allow();
+
+ CASES_FOR_fcntl:
+ // Some fcntls have significant side effects like sending
+ // arbitrary signals, and there's probably nontrivial kernel
+ // attack surface; this should be locked down more if possible.
+ return Allow();
+
+ case __NR_mprotect:
+ case __NR_brk:
+ case __NR_madvise:
+#if !defined(MOZ_MEMORY)
+ // libc's realloc uses mremap (Bug 1286119).
+ case __NR_mremap:
+#endif
+ return Allow();
+
+ case __NR_sigaltstack:
+ return Allow();
+
+#ifdef __NR_set_thread_area
+ case __NR_set_thread_area:
+ return Allow();
+#endif
+
+ case __NR_getrusage:
+ case __NR_times:
+ return Allow();
+
+ case __NR_dup:
+ return Allow();
+
+ CASES_FOR_getuid:
+ CASES_FOR_getgid:
+ CASES_FOR_geteuid:
+ CASES_FOR_getegid:
+ return Allow();
+
+ case __NR_fsync:
+ case __NR_msync:
+ return Allow();
+
+ case __NR_getpriority:
+ case __NR_setpriority:
+ case __NR_sched_get_priority_min:
+ case __NR_sched_get_priority_max:
+ case __NR_sched_getscheduler:
+ case __NR_sched_setscheduler:
+ case __NR_sched_getparam:
+ case __NR_sched_setparam:
+#ifdef DESKTOP
+ case __NR_sched_getaffinity:
+#endif
+ return Allow();
+
+#ifdef DESKTOP
+ case __NR_pipe2:
+ return Allow();
+
+ CASES_FOR_getrlimit:
+ case __NR_clock_getres:
+ CASES_FOR_getresuid:
+ CASES_FOR_getresgid:
+ return Allow();
+
+ case __NR_umask:
+ case __NR_kill:
+ case __NR_wait4:
+#ifdef __NR_waitpid
+ case __NR_waitpid:
+#endif
+#ifdef __NR_arch_prctl
+ case __NR_arch_prctl:
+#endif
+ return Allow();
+
+ case __NR_eventfd2:
+ case __NR_inotify_init1:
+ case __NR_inotify_add_watch:
+ case __NR_inotify_rm_watch:
+ return Allow();
+
+#ifdef __NR_memfd_create
+ case __NR_memfd_create:
+ return Allow();
+#endif
+
+#ifdef __NR_rt_tgsigqueueinfo
+ // Only allow to send signals within the process.
+ case __NR_rt_tgsigqueueinfo: {
+ Arg<pid_t> tgid(0);
+ return If(tgid == getpid(), Allow())
+ .Else(InvalidSyscall());
+ }
+#endif
+
+ case __NR_mlock:
+ case __NR_munlock:
+ return Allow();
+
+ // We can't usefully allow fork+exec, even on a temporary basis;
+ // the child would inherit the seccomp-bpf policy and almost
+ // certainly die from an unexpected SIGSYS. We also can't have
+ // fork() crash, currently, because there are too many system
+ // libraries/plugins that try to run commands. But they can
+ // usually do something reasonable on error.
+ case __NR_clone:
+ return ClonePolicy(Error(EPERM));
+
+#ifdef __NR_fadvise64
+ case __NR_fadvise64:
+ return Allow();
+#endif
+
+#ifdef __NR_fadvise64_64
+ case __NR_fadvise64_64:
+ return Allow();
+#endif
+
+ case __NR_fallocate:
+ return Allow();
+
+ case __NR_get_mempolicy:
+ return Allow();
+
+#endif // DESKTOP
+
+#ifdef __NR_getrandom
+ case __NR_getrandom:
+ return Allow();
+#endif
+
+ // nsSystemInfo uses uname (and we cache an instance, so
+ // the info remains present even if we block the syscall)
+ case __NR_uname:
+#ifdef DESKTOP
+ case __NR_sysinfo:
+#endif
+ return Allow();
+
+#ifdef MOZ_JPROF
+ case __NR_setitimer:
+ return Allow();
+#endif // MOZ_JPROF
+
+ default:
+ return SandboxPolicyCommon::EvaluateSyscall(sysno);
+ }
+ }
+};
+
+UniquePtr<sandbox::bpf_dsl::Policy>
+GetContentSandboxPolicy(SandboxBrokerClient* aMaybeBroker)
+{
+ return UniquePtr<sandbox::bpf_dsl::Policy>(new ContentSandboxPolicy(aMaybeBroker));
+}
+#endif // MOZ_CONTENT_SANDBOX
+
+
+#ifdef MOZ_GMP_SANDBOX
+// Unlike for content, the GeckoMediaPlugin seccomp-bpf policy needs
+// to be an effective sandbox by itself, because we allow GMP on Linux
+// systems where that's the only sandboxing mechanism we can use.
+//
+// Be especially careful about what this policy allows.
+class GMPSandboxPolicy : public SandboxPolicyCommon {
+ static intptr_t OpenTrap(const sandbox::arch_seccomp_data& aArgs,
+ void* aux)
+ {
+ auto plugin = static_cast<SandboxOpenedFile*>(aux);
+ const char* path;
+ int flags;
+
+ switch (aArgs.nr) {
+#ifdef __NR_open
+ case __NR_open:
+ path = reinterpret_cast<const char*>(aArgs.args[0]);
+ flags = static_cast<int>(aArgs.args[1]);
+ break;
+#endif
+ case __NR_openat:
+ // The path has to be absolute to match the pre-opened file (see
+ // assertion in ctor) so the dirfd argument is ignored.
+ path = reinterpret_cast<const char*>(aArgs.args[1]);
+ flags = static_cast<int>(aArgs.args[2]);
+ break;
+ default:
+ MOZ_CRASH("unexpected syscall number");
+ }
+
+ if (strcmp(path, plugin->mPath) != 0) {
+ SANDBOX_LOG_ERROR("attempt to open file %s (flags=0%o) which is not the"
+ " media plugin %s", path, flags, plugin->mPath);
+ return -EPERM;
+ }
+ if ((flags & O_ACCMODE) != O_RDONLY) {
+ SANDBOX_LOG_ERROR("non-read-only open of file %s attempted (flags=0%o)",
+ path, flags);
+ return -EPERM;
+ }
+ int fd = plugin->mFd.exchange(-1);
+ if (fd < 0) {
+ SANDBOX_LOG_ERROR("multiple opens of media plugin file unimplemented");
+ return -ENOSYS;
+ }
+ return fd;
+ }
+
+ static intptr_t SchedTrap(const sandbox::arch_seccomp_data& aArgs,
+ void* aux)
+ {
+ const pid_t tid = syscall(__NR_gettid);
+ if (aArgs.args[0] == static_cast<uint64_t>(tid)) {
+ return syscall(aArgs.nr,
+ 0,
+ aArgs.args[1],
+ aArgs.args[2],
+ aArgs.args[3],
+ aArgs.args[4],
+ aArgs.args[5]);
+ }
+ SANDBOX_LOG_ERROR("unsupported tid in SchedTrap");
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+
+ SandboxOpenedFile* mPlugin;
+public:
+ explicit GMPSandboxPolicy(SandboxOpenedFile* aPlugin)
+ : mPlugin(aPlugin)
+ {
+ MOZ_ASSERT(aPlugin->mPath[0] == '/', "plugin path should be absolute");
+ }
+
+ virtual ~GMPSandboxPolicy() { }
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ // Simulate opening the plugin file.
+#ifdef __NR_open
+ case __NR_open:
+#endif
+ case __NR_openat:
+ return Trap(OpenTrap, mPlugin);
+
+ // ipc::Shmem
+ case __NR_mprotect:
+ return Allow();
+ case __NR_madvise: {
+ Arg<int> advice(2);
+ return If(advice == MADV_DONTNEED, Allow())
+ .ElseIf(advice == MADV_FREE, Allow())
+#ifdef MOZ_ASAN
+ .ElseIf(advice == MADV_NOHUGEPAGE, Allow())
+ .ElseIf(advice == MADV_DONTDUMP, Allow())
+#endif
+ .Else(InvalidSyscall());
+ }
+ case __NR_brk:
+ CASES_FOR_geteuid:
+ return Allow();
+ case __NR_sched_getparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_get_priority_min:
+ case __NR_sched_get_priority_max:
+ case __NR_sched_setscheduler: {
+ Arg<pid_t> pid(0);
+ return If(pid == 0, Allow())
+ .Else(Trap(SchedTrap, nullptr));
+ }
+
+ // For clock(3) on older glibcs; bug 1304220.
+ case __NR_times:
+ return Allow();
+
+ default:
+ return SandboxPolicyCommon::EvaluateSyscall(sysno);
+ }
+ }
+};
+
+UniquePtr<sandbox::bpf_dsl::Policy>
+GetMediaSandboxPolicy(SandboxOpenedFile* aPlugin)
+{
+ return UniquePtr<sandbox::bpf_dsl::Policy>(new GMPSandboxPolicy(aPlugin));
+}
+
+#endif // MOZ_GMP_SANDBOX
+
+}