// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "base/logging.h" #include "base/macros.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 #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 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 = AnyOf(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 option(0); return Switch(option) .CASES((PR_GET_NAME, PR_SET_NAME, PR_GET_DUMPABLE, PR_SET_DUMPABLE #if defined(OS_ANDROID) , PR_SET_VMA, PR_SET_TIMERSLACK_PID #endif ), Allow()) .Default(CrashSIGSYSPrctl()); } ResultExpr RestrictIoctl() { const Arg 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 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 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 cmd(1); const Arg 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 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(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 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 which(0); const Arg who(1); return If(which == PRIO_PROCESS, Switch(who).CASES((0, target_pid), Allow()).Default(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(0); return Switch(pid) .CASES((0, target_pid), Allow()) .Default(RewriteSchedSIGSYS()); } default: NOTREACHED(); return CrashSIGSYS(); } } ResultExpr RestrictPrlimit64(pid_t target_pid) { const Arg pid(0); return Switch(pid).CASES((0, target_pid), Allow()).Default(CrashSIGSYS()); } ResultExpr RestrictGetrusage() { const Arg 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(0); return Switch(clockid) .CASES((CLOCK_MONOTONIC, CLOCK_MONOTONIC_COARSE, CLOCK_PROCESS_CPUTIME_ID, CLOCK_REALTIME, CLOCK_REALTIME_COARSE, CLOCK_THREAD_CPUTIME_ID), Allow()) .Default(CrashSIGSYS()); } } // namespace sandbox.