#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.h"
#include <errno.h>
#include <fcntl.h>
#include <linux/android/binder.h>
#include <linux/ashmem.h>
#include <linux/incrementalfs.h>
#include <linux/nbd.h>
#include <linux/net.h>
#include <linux/userfaultfd.h>
#include <sched.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include "build/build_config.h"
#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"
#if defined(__x86_64__)
#include <asm/prctl.h>
#endif
using sandbox::bpf_dsl::AllOf;
using sandbox::bpf_dsl::Allow;
using sandbox::bpf_dsl::AnyOf;
using sandbox::bpf_dsl::Arg;
using sandbox::bpf_dsl::BoolConst;
using sandbox::bpf_dsl::BoolExpr;
using sandbox::bpf_dsl::Error;
using sandbox::bpf_dsl::If;
using sandbox::bpf_dsl::ResultExpr;
namespace sandbox {
namespace {
BoolExpr RestrictSocketArguments(const Arg<int>& domain,
const Arg<int>& type,
const Arg<int>& protocol) {
const int kSockFlags = SOCK_CLOEXEC | SOCK_NONBLOCK;
return AllOf(domain == AF_UNIX,
AnyOf((type & ~kSockFlags) == SOCK_DGRAM,
(type & ~kSockFlags) == SOCK_STREAM),
protocol == 0);
}
ResultExpr RestrictAndroidIoctl(bool allow_userfaultfd_ioctls) {
const Arg<unsigned int> request(1);
#ifdef BINDER_IPC_32BIT
const unsigned int kBinderWriteRead32 = BINDER_WRITE_READ;
const unsigned int kBinderWriteRead64 =
(BINDER_WRITE_READ & ~IOCSIZE_MASK) |
((sizeof(binder_write_read) * 2) << _IOC_SIZESHIFT);
#else
const unsigned int kBinderWriteRead64 = BINDER_WRITE_READ;
const unsigned int kBinderWriteRead32 =
(BINDER_WRITE_READ & ~IOCSIZE_MASK) |
((sizeof(binder_write_read) / 2) << _IOC_SIZESHIFT);
#endif
const unsigned int kAndroidAlarmGetTimeElapsedRealtime32 = 0x40086134;
const unsigned int kAndroidAlarmGetTimeElapsedRealtime64 = 0x40106134;
return Switch(request)
.Cases(
{
ASHMEM_SET_NAME, ASHMEM_GET_NAME, ASHMEM_SET_SIZE, ASHMEM_GET_SIZE,
ASHMEM_SET_PROT_MASK, ASHMEM_GET_PROT_MASK, ASHMEM_PIN, ASHMEM_UNPIN,
ASHMEM_GET_PIN_STATUS,
kBinderWriteRead32, kBinderWriteRead64, BINDER_SET_MAX_THREADS,
BINDER_THREAD_EXIT, BINDER_VERSION,
BINDER_ENABLE_ONEWAY_SPAM_DETECTION, BINDER_GET_EXTENDED_ERROR,
INCFS_IOC_READ_FILE_SIGNATURE, INCFS_IOC_GET_FILLED_BLOCKS,
INCFS_IOC_GET_READ_TIMEOUTS, INCFS_IOC_GET_LAST_READ_ERROR,
INCFS_IOC_GET_BLOCK_COUNT, INCFS_IOC_SET_READ_TIMEOUTS},
Allow())
.Cases(
{
UFFDIO_REGISTER, UFFDIO_UNREGISTER, UFFDIO_WAKE, UFFDIO_COPY,
UFFDIO_ZEROPAGE, UFFDIO_CONTINUE,
UFFDIO_MOVE},
If(BoolConst(allow_userfaultfd_ioctls), Allow())
.Else(RestrictIoctl()))
.Cases(
{
kAndroidAlarmGetTimeElapsedRealtime32,
kAndroidAlarmGetTimeElapsedRealtime64,
NBD_CLEAR_SOCK, NBD_SET_BLKSIZE},
Error(EINVAL))
.Default(RestrictIoctl());
}
ResultExpr RestrictCloneParameters() {
const Arg<unsigned long> flags(0);
const uint64_t kForkFlags =
CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD;
const uint64_t kPthreadCreateFlags =
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD |
CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
const BoolExpr is_fork_or_pthread =
AnyOf(flags == kForkFlags, flags == kPthreadCreateFlags);
return If(is_fork_or_pthread, Allow()).Else(CrashSIGSYSClone());
}
bool IsBaselinePolicyAllowed(int sysno) {
switch (sysno) {
case __NR_epoll_pwait:
case __NR_fdatasync:
case __NR_flock:
case __NR_fsync:
#if defined(__LP64__)
case __NR_ftruncate:
case __NR_newfstatat:
case __NR_fstatfs:
#else
case __NR_ftruncate64:
case __NR_fstatat64:
case __NR_fstatfs64:
#endif
#if defined(__arm__) || defined(__aarch64__)
case __NR_getcpu:
#endif
case __NR_getdents64:
case __NR_getpriority:
case __NR_membarrier:
#if defined(__i386__)
case __NR_modify_ldt:
#endif
case __NR_mremap:
case __NR_msync:
case __NR_openat:
case __NR_pwrite64:
case __NR_rt_sigtimedwait:
case __NR_sched_getparam:
case __NR_sched_getscheduler:
case __NR_sched_setscheduler:
case __NR_setpriority:
#if defined(__i386__)
case __NR_set_thread_area:
#endif
case __NR_set_tid_address:
#if defined(__LP64__)
case __NR_getrlimit:
#else
case __NR_ugetrlimit:
#endif
case __NR_getsockopt:
case __NR_connect:
return true;
default:
return false;
}
}
}
BaselinePolicyAndroid::BaselinePolicyAndroid() = default;
BaselinePolicyAndroid::BaselinePolicyAndroid(const RuntimeOptions& options)
: options_(options) {}
BaselinePolicyAndroid::~BaselinePolicyAndroid() = default;
ResultExpr BaselinePolicyAndroid::EvaluateSyscall(int sysno) const {
if (sysno == __NR_clone) {
if (options_.should_restrict_clone_params) {
return RestrictCloneParameters();
}
return Allow();
}
if (sysno == __NR_sched_setaffinity || sysno == __NR_sched_getaffinity) {
return Error(EPERM);
}
if (sysno == __NR_ioctl) {
return RestrictAndroidIoctl(options_.allow_userfaultfd_ioctls);
}
if (sysno == __NR_madvise) {
const Arg<int> advice(2);
return If(advice == MADV_PAGEOUT, Allow())
.Else(BaselinePolicy::EvaluateSyscall(sysno));
}
if (sysno == __NR_ptrace) {
return RestrictPtrace();
}
if (sysno == __NR_process_vm_readv) {
const Arg<pid_t> pid(0);
return If(pid == policy_pid(), Allow())
.Else(Error(EPERM));
}
if (!options_.should_restrict_renderer_syscalls) {
if (sysno == __NR_sysinfo) {
return Allow();
}
if (sysno == __NR_clock_getres) {
return RestrictClockID();
}
}
if (sysno == __NR_getrusage) {
return RestrictGetrusage();
}
#if defined(__x86_64__)
if (sysno == __NR_arch_prctl) {
const Arg<int> code(0);
return If(code == ARCH_SET_GS, Allow()).Else(Error(EPERM));
}
#endif
if (sysno == __NR_socket) {
const Arg<int> domain(0);
const Arg<int> type(1);
const Arg<int> protocol(2);
return If(RestrictSocketArguments(domain, type, protocol), Allow())
.Else(Error(EPERM));
}
if (sysno == __NR_getsockname) {
return Error(EPERM);
}
if (sysno == __NR_setsockopt) {
const Arg<int> level(1);
const Arg<int> option(2);
return If(AllOf(level == SOL_SOCKET,
AnyOf(option == SO_SNDTIMEO,
option == SO_RCVTIMEO,
option == SO_SNDBUF,
option == SO_REUSEADDR,
option == SO_PASSCRED)),
Allow())
.Else(BaselinePolicy::EvaluateSyscall(sysno));
}
#if defined(__i386__)
if (sysno == __NR_socketcall) {
const Arg<int> socketcall(0);
return Switch(socketcall)
.Cases({SYS_CONNECT,
SYS_SOCKET,
SYS_SETSOCKOPT,
SYS_GETSOCKOPT},
Allow())
.Default(BaselinePolicy::EvaluateSyscall(sysno));
}
#endif
if (IsBaselinePolicyAllowed(sysno)) {
return Allow();
}
return BaselinePolicy::EvaluateSyscall(sysno);
}
}