diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /security/sandbox/linux/SandboxFilter.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-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.cpp | 961 |
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 + +} |