| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <fcntl.h> |
| #include <linux/net.h> |
| #include <sched.h> |
| #include <signal.h> |
| #include <stdint.h> |
| #include <sys/mman.h> |
| #include <sys/prctl.h> |
| #include <sys/resource.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <sys/types.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "sandbox/linux/bpf_dsl/bpf_dsl.h" |
| #include "sandbox/linux/bpf_dsl/seccomp_macros.h" |
| #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" |
| #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" |
| #include "sandbox/linux/system_headers/linux_futex.h" |
| #include "sandbox/linux/system_headers/linux_syscalls.h" |
| #include "sandbox/linux/system_headers/linux_time.h" |
| |
| // PNaCl toolchain does not provide sys/ioctl.h header. |
| #if !defined(OS_NACL_NONSFI) |
| #include <sys/ioctl.h> |
| #endif |
| |
| #if defined(OS_ANDROID) |
| |
| #if !defined(F_DUPFD_CLOEXEC) |
| #define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6) |
| #endif |
| |
| // https://android.googlesource.com/platform/bionic/+/lollipop-release/libc/private/bionic_prctl.h |
| #if !defined(PR_SET_VMA) |
| #define PR_SET_VMA 0x53564d41 |
| #endif |
| |
| // https://android.googlesource.com/platform/system/core/+/lollipop-release/libcutils/sched_policy.c |
| #if !defined(PR_SET_TIMERSLACK_PID) |
| #define PR_SET_TIMERSLACK_PID 41 |
| #endif |
| |
| #endif // defined(OS_ANDROID) |
| |
| #if defined(__arm__) && !defined(MAP_STACK) |
| #define MAP_STACK 0x20000 // Daisy build environment has old headers. |
| #endif |
| |
| #if defined(__mips__) && !defined(MAP_STACK) |
| #define MAP_STACK 0x40000 |
| #endif |
| namespace { |
| |
| inline bool IsArchitectureX86_64() { |
| #if defined(__x86_64__) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| inline bool IsArchitectureI386() { |
| #if defined(__i386__) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| inline bool IsAndroid() { |
| #if defined(OS_ANDROID) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| inline bool IsArchitectureMips() { |
| #if defined(__mips__) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| } // namespace. |
| |
| #define CASES SANDBOX_BPF_DSL_CASES |
| |
| using sandbox::bpf_dsl::Allow; |
| using sandbox::bpf_dsl::Arg; |
| using sandbox::bpf_dsl::BoolExpr; |
| using sandbox::bpf_dsl::Error; |
| using sandbox::bpf_dsl::If; |
| using sandbox::bpf_dsl::ResultExpr; |
| |
| namespace sandbox { |
| |
| #if !defined(OS_NACL_NONSFI) |
| // Allow Glibc's and Android pthread creation flags, crash on any other |
| // thread creation attempts and EPERM attempts to use neither |
| // CLONE_VM, nor CLONE_THREAD, which includes all fork() implementations. |
| ResultExpr RestrictCloneToThreadsAndEPERMFork() { |
| const Arg<unsigned long> flags(0); |
| |
| // TODO(mdempsky): Extend DSL to support (flags & ~mask1) == mask2. |
| const uint64_t kAndroidCloneMask = CLONE_VM | CLONE_FS | CLONE_FILES | |
| CLONE_SIGHAND | CLONE_THREAD | |
| CLONE_SYSVSEM; |
| const uint64_t kObsoleteAndroidCloneMask = kAndroidCloneMask | CLONE_DETACHED; |
| |
| const uint64_t kGlibcPthreadFlags = |
| CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | |
| CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID; |
| const BoolExpr glibc_test = flags == kGlibcPthreadFlags; |
| |
| const BoolExpr android_test = flags == kAndroidCloneMask || |
| flags == kObsoleteAndroidCloneMask || |
| flags == kGlibcPthreadFlags; |
| |
| return If(IsAndroid() ? android_test : glibc_test, Allow()) |
| .ElseIf((flags & (CLONE_VM | CLONE_THREAD)) == 0, Error(EPERM)) |
| .Else(CrashSIGSYSClone()); |
| } |
| |
| ResultExpr RestrictPrctl() { |
| // Will need to add seccomp compositing in the future. PR_SET_PTRACER is |
| // used by breakpad but not needed anymore. |
| const Arg<int> option(0); |
| return Switch(option) |
| .CASES((PR_GET_NAME, PR_SET_NAME, PR_GET_DUMPABLE, PR_SET_DUMPABLE), |
| Allow()) |
| #if defined(OS_ANDROID) |
| .CASES((PR_SET_VMA, PR_SET_TIMERSLACK_PID), Allow()) |
| #endif |
| .Default(CrashSIGSYSPrctl()); |
| } |
| |
| ResultExpr RestrictIoctl() { |
| const Arg<int> request(1); |
| return Switch(request).CASES((TCGETS, FIONREAD), Allow()).Default( |
| CrashSIGSYSIoctl()); |
| } |
| |
| ResultExpr RestrictMmapFlags() { |
| // The flags you see are actually the allowed ones, and the variable is a |
| // "denied" mask because of the negation operator. |
| // Significantly, we don't permit MAP_HUGETLB, or the newer flags such as |
| // MAP_POPULATE. |
| // TODO(davidung), remove MAP_DENYWRITE with updated Tegra libraries. |
| const uint64_t kAllowedMask = MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS | |
| MAP_STACK | MAP_NORESERVE | MAP_FIXED | |
| MAP_DENYWRITE; |
| const Arg<int> flags(3); |
| return If((flags & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS()); |
| } |
| |
| ResultExpr RestrictMprotectFlags() { |
| // The flags you see are actually the allowed ones, and the variable is a |
| // "denied" mask because of the negation operator. |
| // Significantly, we don't permit weird undocumented flags such as |
| // PROT_GROWSDOWN. |
| const uint64_t kAllowedMask = PROT_READ | PROT_WRITE | PROT_EXEC; |
| const Arg<int> prot(2); |
| return If((prot & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS()); |
| } |
| |
| ResultExpr RestrictFcntlCommands() { |
| // We also restrict the flags in F_SETFL. We don't want to permit flags with |
| // a history of trouble such as O_DIRECT. The flags you see are actually the |
| // allowed ones, and the variable is a "denied" mask because of the negation |
| // operator. |
| // Glibc overrides the kernel's O_LARGEFILE value. Account for this. |
| uint64_t kOLargeFileFlag = O_LARGEFILE; |
| if (IsArchitectureX86_64() || IsArchitectureI386() || IsArchitectureMips()) |
| kOLargeFileFlag = 0100000; |
| |
| const Arg<int> cmd(1); |
| const Arg<long> long_arg(2); |
| |
| const uint64_t kAllowedMask = O_ACCMODE | O_APPEND | O_NONBLOCK | O_SYNC | |
| kOLargeFileFlag | O_CLOEXEC | O_NOATIME; |
| return Switch(cmd) |
| .CASES((F_GETFL, |
| F_GETFD, |
| F_SETFD, |
| F_SETLK, |
| F_SETLKW, |
| F_GETLK, |
| F_DUPFD, |
| F_DUPFD_CLOEXEC), |
| Allow()) |
| .Case(F_SETFL, |
| If((long_arg & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS())) |
| .Default(CrashSIGSYS()); |
| } |
| |
| #if defined(__i386__) || defined(__mips__) |
| ResultExpr RestrictSocketcallCommand() { |
| // Unfortunately, we are unable to restrict the first parameter to |
| // socketpair(2). Whilst initially sounding bad, it's noteworthy that very |
| // few protocols actually support socketpair(2). The scary call that we're |
| // worried about, socket(2), remains blocked. |
| const Arg<int> call(0); |
| return Switch(call) |
| .CASES((SYS_SOCKETPAIR, |
| SYS_SHUTDOWN, |
| SYS_RECV, |
| SYS_SEND, |
| SYS_RECVFROM, |
| SYS_SENDTO, |
| SYS_RECVMSG, |
| SYS_SENDMSG), |
| Allow()) |
| .Default(Error(EPERM)); |
| } |
| #endif |
| |
| ResultExpr RestrictKillTarget(pid_t target_pid, int sysno) { |
| switch (sysno) { |
| case __NR_kill: |
| case __NR_tgkill: { |
| const Arg<pid_t> pid(0); |
| return If(pid == target_pid, Allow()).Else(CrashSIGSYSKill()); |
| } |
| case __NR_tkill: |
| return CrashSIGSYSKill(); |
| default: |
| NOTREACHED(); |
| return CrashSIGSYS(); |
| } |
| } |
| |
| ResultExpr RestrictFutex() { |
| const uint64_t kAllowedFutexFlags = FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME; |
| const Arg<int> op(1); |
| return Switch(op & ~kAllowedFutexFlags) |
| .CASES((FUTEX_WAIT, |
| FUTEX_WAKE, |
| FUTEX_REQUEUE, |
| FUTEX_CMP_REQUEUE, |
| FUTEX_WAKE_OP, |
| FUTEX_WAIT_BITSET, |
| FUTEX_WAKE_BITSET), |
| Allow()) |
| .Default(CrashSIGSYSFutex()); |
| } |
| |
| ResultExpr RestrictGetSetpriority(pid_t target_pid) { |
| const Arg<int> which(0); |
| const Arg<int> who(1); |
| return If(which == PRIO_PROCESS, |
| If(who == 0 || who == target_pid, Allow()).Else(Error(EPERM))) |
| .Else(CrashSIGSYS()); |
| } |
| |
| ResultExpr RestrictSchedTarget(pid_t target_pid, int sysno) { |
| switch (sysno) { |
| case __NR_sched_getaffinity: |
| case __NR_sched_getattr: |
| case __NR_sched_getparam: |
| case __NR_sched_getscheduler: |
| case __NR_sched_rr_get_interval: |
| case __NR_sched_setaffinity: |
| case __NR_sched_setattr: |
| case __NR_sched_setparam: |
| case __NR_sched_setscheduler: { |
| const Arg<pid_t> pid(0); |
| return If(pid == 0 || pid == target_pid, Allow()) |
| .Else(RewriteSchedSIGSYS()); |
| } |
| default: |
| NOTREACHED(); |
| return CrashSIGSYS(); |
| } |
| } |
| |
| ResultExpr RestrictPrlimit64(pid_t target_pid) { |
| const Arg<pid_t> pid(0); |
| return If(pid == 0 || pid == target_pid, Allow()).Else(CrashSIGSYS()); |
| } |
| |
| ResultExpr RestrictGetrusage() { |
| const Arg<int> who(0); |
| return If(who == RUSAGE_SELF, Allow()).Else(CrashSIGSYS()); |
| } |
| #endif // !defined(OS_NACL_NONSFI) |
| |
| ResultExpr RestrictClockID() { |
| static_assert(4 == sizeof(clockid_t), "clockid_t is not 32bit"); |
| const Arg<clockid_t> clockid(0); |
| return If( |
| #if defined(OS_CHROMEOS) |
| // Allow the special clock for Chrome OS used by Chrome tracing. |
| clockid == base::TraceTicks::kClockSystemTrace || |
| #endif |
| clockid == CLOCK_MONOTONIC || |
| clockid == CLOCK_MONOTONIC_COARSE || |
| clockid == CLOCK_PROCESS_CPUTIME_ID || |
| clockid == CLOCK_REALTIME || |
| clockid == CLOCK_REALTIME_COARSE || |
| clockid == CLOCK_THREAD_CPUTIME_ID, |
| Allow()).Else(CrashSIGSYS()); |
| } |
| |
| } // namespace sandbox. |