text: // 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 "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(
                   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.
  