Index: sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc |
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..de59dd888d8f123c7cb7fa9eb6a778c5c33b2940 |
--- /dev/null |
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc |
@@ -0,0 +1,289 @@ |
+// 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/futex.h> |
+#include <linux/net.h> |
+#include <sched.h> |
+#include <signal.h> |
+#include <sys/ioctl.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/basictypes.h" |
+#include "base/logging.h" |
+#include "base/macros.h" |
+#include "base/time/time.h" |
+#include "build/build_config.h" |
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" |
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" |
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" |
+#include "sandbox/linux/services/linux_syscalls.h" |
+ |
+#if defined(OS_ANDROID) |
+ |
+#include "sandbox/linux/services/android_futex.h" |
+ |
+#if !defined(F_DUPFD_CLOEXEC) |
+#define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6) |
+#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 { |
+ |
+// 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()) |
+ .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 RestrictClockID() { |
+ COMPILE_ASSERT(4 == sizeof(clockid_t), clockid_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::TimeTicks::kClockSystemTrace || |
+#endif |
+ clockid == CLOCK_MONOTONIC || |
+ clockid == CLOCK_PROCESS_CPUTIME_ID || |
+ clockid == CLOCK_REALTIME || |
+ clockid == CLOCK_THREAD_CPUTIME_ID, |
+ Allow()).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(); |
+ } |
+} |
+ |
+ |
+} // namespace sandbox. |