diff options
Diffstat (limited to 'chromium/sandbox/linux')
-rw-r--r-- | chromium/sandbox/linux/BUILD.gn | 1 | ||||
-rw-r--r-- | chromium/sandbox/linux/bpf_dsl/policy_compiler.cc | 1 | ||||
-rw-r--r-- | chromium/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc | 36 | ||||
-rw-r--r-- | chromium/sandbox/linux/seccomp-bpf/bpf_tests.h | 2 | ||||
-rw-r--r-- | chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc | 88 | ||||
-rw-r--r-- | chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h | 7 | ||||
-rw-r--r-- | chromium/sandbox/linux/seccomp-bpf/trap.cc | 6 | ||||
-rw-r--r-- | chromium/sandbox/linux/seccomp-bpf/trap.h | 2 | ||||
-rw-r--r-- | chromium/sandbox/linux/syscall_broker/broker_permission_list.cc | 14 | ||||
-rw-r--r-- | chromium/sandbox/linux/system_headers/linux_seccomp.h | 16 |
10 files changed, 128 insertions, 45 deletions
diff --git a/chromium/sandbox/linux/BUILD.gn b/chromium/sandbox/linux/BUILD.gn index c27351f9a6a..7d4d600c5ae 100644 --- a/chromium/sandbox/linux/BUILD.gn +++ b/chromium/sandbox/linux/BUILD.gn @@ -248,6 +248,7 @@ component("seccomp_bpf") { ":sandbox_services", "//base", "//base/third_party/dynamic_annotations", + "//sandbox:sandbox_buildflags", ] if (is_nacl_nonsfi) { diff --git a/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc b/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc index a894e2ad397..2ba3bc9908f 100644 --- a/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc +++ b/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc @@ -10,6 +10,7 @@ #include <sys/syscall.h> #include <limits> +#include <ostream> #include "base/check_op.h" #include "base/stl_util.h" diff --git a/chromium/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc b/chromium/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc index 28604a82797..938b51ae623 100644 --- a/chromium/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc +++ b/chromium/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc @@ -177,7 +177,9 @@ bool IsSyscallForTestHarness(int sysno) { // UBSan_vptr checker needs mmap, munmap, pipe, write. // ASan and MSan don't need any of these for normal operation, but they // require at least mmap & munmap to print a report if an error is detected. - if (sysno == kMMapNr || sysno == __NR_munmap || sysno == __NR_pipe) { + // ASan requires sigaltstack. + if (sysno == kMMapNr || sysno == __NR_munmap || sysno == __NR_pipe || + sysno == __NR_sigaltstack) { return true; } #endif @@ -599,7 +601,7 @@ class PrctlPolicy : public Policy { if (sysno == __NR_prctl) { // Handle prctl() inside an UnsafeTrap() - return UnsafeTrap(PrctlHandler, NULL); + return UnsafeTrap(PrctlHandler, nullptr); } // Allow all other system calls. @@ -661,7 +663,7 @@ ResultExpr RedirectAllSyscallsPolicy::EvaluateSyscall(int sysno) const { // use of UnsafeTrap() if (SandboxBPF::IsRequiredForUnsafeTrap(sysno)) return Allow(); - return UnsafeTrap(AllowRedirectedSyscall, NULL); + return UnsafeTrap(AllowRedirectedSyscall, nullptr); } #if !defined(ADDRESS_SANITIZER) @@ -688,7 +690,7 @@ BPF_TEST_C(SandboxBPF, SigBus, RedirectAllSyscallsPolicy) { struct sigaction sa = {}; sa.sa_sigaction = SigBusHandler; sa.sa_flags = SA_SIGINFO; - BPF_ASSERT(sigaction(SIGBUS, &sa, NULL) == 0); + BPF_ASSERT(sigaction(SIGBUS, &sa, nullptr) == 0); kill(getpid(), SIGBUS); char c = '\000'; BPF_ASSERT(read(fds[0], &c, 1) == 1); @@ -720,8 +722,8 @@ BPF_TEST_C(SandboxBPF, SigMask, RedirectAllSyscallsPolicy) { // Try again, and this time we verify that we can block it. This // requires a second call to sigprocmask(). sigaddset(&mask0, SIGUSR2); - BPF_ASSERT(!sigprocmask(SIG_BLOCK, &mask0, NULL)); - BPF_ASSERT(!sigprocmask(SIG_BLOCK, NULL, &mask2)); + BPF_ASSERT(!sigprocmask(SIG_BLOCK, &mask0, nullptr)); + BPF_ASSERT(!sigprocmask(SIG_BLOCK, nullptr, &mask2)); BPF_ASSERT(sigismember(&mask2, SIGUSR2)); } @@ -837,7 +839,7 @@ class EqualityStressTest { // work isn't impacted by the fact that we are overriding // a lot of different system calls. ++end; - arg_values_.push_back(NULL); + arg_values_.push_back(nullptr); } else { arg_values_.push_back( RandomArgValue(rand() % kMaxArgs, 0, rand() % kMaxArgs)); @@ -955,7 +957,7 @@ class EqualityStressTest { arg_value->tests[n].k_value = k_value; if (!remaining_args || (rand() & 1)) { arg_value->tests[n].err = (rand() % 1000) + 1; - arg_value->tests[n].arg_value = NULL; + arg_value->tests[n].arg_value = nullptr; } else { arg_value->tests[n].err = 0; arg_value->tests[n].arg_value = @@ -967,7 +969,7 @@ class EqualityStressTest { // node, or we can randomly add another couple of tests. if (!remaining_args || (rand() & 1)) { arg_value->err = (rand() % 1000) + 1; - arg_value->arg_value = NULL; + arg_value->arg_value = nullptr; } else { arg_value->err = 0; arg_value->arg_value = @@ -1820,15 +1822,15 @@ ResultExpr PthreadPolicyBitMask::EvaluateSyscall(int sysno) const { static void* ThreadFnc(void* arg) { ++*reinterpret_cast<int*>(arg); Syscall::Call(__NR_futex, arg, FUTEX_WAKE, 1, 0, 0, 0); - return NULL; + return nullptr; } static void PthreadTest() { // Attempt to start a joinable thread. This should succeed. pthread_t thread; int thread_ran = 0; - BPF_ASSERT(!pthread_create(&thread, NULL, ThreadFnc, &thread_ran)); - BPF_ASSERT(!pthread_join(thread, NULL)); + BPF_ASSERT(!pthread_create(&thread, nullptr, ThreadFnc, &thread_ran)); + BPF_ASSERT(!pthread_join(thread, nullptr)); BPF_ASSERT(thread_ran); // Attempt to start a detached thread. This should succeed. @@ -2084,7 +2086,7 @@ class TrapPread64Policy : public Policy { } if (system_call_number == __NR_pread64) { - return UnsafeTrap(ForwardPreadHandler, NULL); + return UnsafeTrap(ForwardPreadHandler, nullptr); } return Allow(); } @@ -2134,7 +2136,7 @@ void* TsyncApplyToTwoThreadsFunc(void* cond_ptr) { BlacklistNanosleepPolicy::AssertNanosleepFails(); - return NULL; + return nullptr; } SANDBOX_TEST(SandboxBPF, Tsync) { @@ -2158,7 +2160,7 @@ SANDBOX_TEST(SandboxBPF, Tsync) { // Create a thread on which to invoke the blocked syscall. pthread_t thread; BPF_ASSERT_EQ( - 0, pthread_create(&thread, NULL, &TsyncApplyToTwoThreadsFunc, &event)); + 0, pthread_create(&thread, nullptr, &TsyncApplyToTwoThreadsFunc, &event)); // Test that nanoseelp success. const struct timespec ts = {0, 0}; @@ -2175,7 +2177,7 @@ SANDBOX_TEST(SandboxBPF, Tsync) { event.Signal(); // Wait for the thread to finish. - BPF_ASSERT_EQ(0, pthread_join(thread, NULL)); + BPF_ASSERT_EQ(0, pthread_join(thread, nullptr)); } class AllowAllPolicy : public Policy { @@ -2237,7 +2239,7 @@ class UnsafeTrapWithCondPolicy : public Policy { case __NR_close: return Allow(); case __NR_getppid: - return UnsafeTrap(NoOpHandler, NULL); + return UnsafeTrap(NoOpHandler, nullptr); default: return Error(EPERM); } diff --git a/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h b/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h index 8b2b12afd8f..5e35e997d72 100644 --- a/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h +++ b/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h @@ -7,7 +7,7 @@ #include <memory> -#include "base/logging.h" +#include "base/check.h" #include "base/macros.h" #include "build/build_config.h" #include "sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h" diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc index 639cd15e07f..3d7bc172f79 100644 --- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc +++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc @@ -14,6 +14,7 @@ #include "base/check_op.h" #include "base/compiler_specific.h" #include "base/files/scoped_file.h" +#include "base/logging.h" #include "base/macros.h" #include "base/notreached.h" #include "base/posix/eintr_wrapper.h" @@ -32,6 +33,7 @@ #include "sandbox/linux/system_headers/linux_filter.h" #include "sandbox/linux/system_headers/linux_seccomp.h" #include "sandbox/linux/system_headers/linux_syscalls.h" +#include "sandbox/sandbox_buildflags.h" namespace sandbox { @@ -76,16 +78,13 @@ bool KernelHasLGBug() { return false; } -// Check if the kernel supports seccomp-filter via the seccomp system call -// and the TSYNC feature to enable seccomp on all threads. -bool KernelSupportsSeccompTsync() { +bool KernelSupportsSeccompFlags(unsigned int flags) { if (KernelHasLGBug()) { return false; } errno = 0; - const int rv = - sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, nullptr); + const int rv = sys_seccomp(SECCOMP_SET_MODE_FILTER, flags, nullptr); if (rv == -1 && errno == EFAULT) { return true; @@ -96,6 +95,20 @@ bool KernelSupportsSeccompTsync() { return false; } +// Check if the kernel supports seccomp-filter via the seccomp system call +// and the TSYNC feature to enable seccomp on all threads. +bool KernelSupportsSeccompTsync() { + return KernelSupportsSeccompFlags(SECCOMP_FILTER_FLAG_TSYNC); +} + +#if BUILDFLAG(DISABLE_SECCOMP_SSBD) +// Check if the kernel supports seccomp-filter via the seccomp system call and +// without spec flaw mitigation. +bool KernelSupportSeccompSpecAllow() { + return KernelSupportsSeccompFlags(SECCOMP_FILTER_FLAG_SPEC_ALLOW); +} +#endif + uint64_t EscapePC() { intptr_t rv = Syscall::Call(-1); if (rv == -1 && errno == ENOSYS) { @@ -170,8 +183,7 @@ bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level) { } // Install the filters. - InstallFilter(supports_tsync || - seccomp_level == SeccompLevel::MULTI_THREADED); + InstallFilter(seccomp_level == SeccompLevel::MULTI_THREADED); return true; } @@ -239,23 +251,67 @@ void SandboxBPF::InstallFilter(bool must_sync_threads) { SANDBOX_DIE("Kernel refuses to enable no-new-privs"); } - // Install BPF filter program. If the thread state indicates multi-threading - // support, then the kernel hass the seccomp system call. Otherwise, fall - // back on prctl, which requires the process to be single-threaded. + // Install BPF filter program. If the thread state indicates that tsync is + // necessary or SECCOMP_FILTER_FLAG_SPEC_ALLOW is supported, then the kernel + // has the seccomp system call. Otherwise, fall back on prctl, which requires + // the process to be single-threaded. + unsigned int seccomp_filter_flags = 0; if (must_sync_threads) { - int rv = - sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &prog); - if (rv) { - SANDBOX_DIE( - "Kernel refuses to turn on and synchronize threads for BPF filters"); - } + seccomp_filter_flags |= SECCOMP_FILTER_FLAG_TSYNC; +#if BUILDFLAG(DISABLE_SECCOMP_SSBD) + // Seccomp will disable indirect branch speculation and speculative store + // bypass simultaneously. To only opt-out SSBD, following steps are needed + // 1. Disable IBSpec with prctl + // 2. Call seccomp with SECCOMP_FILTER_FLAG_SPEC_ALLOW + // As prctl provide a per thread control of the speculation feature, only + // opt-out SSBD when process is single-threaded and tsync is not necessary. + } else if (KernelSupportSeccompSpecAllow()) { + seccomp_filter_flags |= SECCOMP_FILTER_FLAG_SPEC_ALLOW; + DisableIBSpec(); +#endif } else { if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { SANDBOX_DIE("Kernel refuses to turn on BPF filters"); } + sandbox_has_started_ = true; + return; } + int rv = sys_seccomp(SECCOMP_SET_MODE_FILTER, seccomp_filter_flags, &prog); + if (rv) { + SANDBOX_DIE("Kernel refuses to turn on BPF filters"); + } sandbox_has_started_ = true; } +void SandboxBPF::DisableIBSpec() { + // Test if the per-task control of the mitigation is available. If + // PR_SPEC_PRCTL is set, then the per-task control of the mitigation is + // available. If not set, prctl(PR_SET_SPECULATION_CTRL) for the speculation + // misfeature will fail. + const int rv = + prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, 0, 0, 0); + // Kernel control of the speculation misfeature is not supported. + if (rv < 0) { + return; + } + + if (!(rv & PR_SPEC_PRCTL)) { + DLOG(INFO) << "Indirect branch speculation can not be controled by prctl." + << rv; + return; + } + + if (rv & PR_SPEC_FORCE_DISABLE) { + DLOG(INFO) << "Indirect branch speculation is already force disabled." + << rv; + return; + } + + if (prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, + PR_SPEC_FORCE_DISABLE, 0, 0)) { + DPLOG(INFO) << "Kernel failed to force disable indirect branch speculation"; + } +} + } // namespace sandbox diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h index 282852992b2..98475061bf2 100644 --- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h +++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h @@ -101,6 +101,13 @@ class SANDBOX_EXPORT SandboxBPF { // been configured with SetSandboxPolicy(). void InstallFilter(bool must_sync_threads); + // Disable indirect branch speculation by prctl. This will be done by + // seccomp if SECCOMP_FILTER_FLAG_SPEC_ALLOW is not set. Seccomp will + // disable indirect branch speculation and speculative store bypass + // simultaneously. We use prctl in supplement to control the speculation + // features separately. + void DisableIBSpec(); + base::ScopedFD proc_fd_; bool sandbox_has_started_; std::unique_ptr<bpf_dsl::Policy> policy_; diff --git a/chromium/sandbox/linux/seccomp-bpf/trap.cc b/chromium/sandbox/linux/seccomp-bpf/trap.cc index 9884be8bb2c..f5b86a73ac7 100644 --- a/chromium/sandbox/linux/seccomp-bpf/trap.cc +++ b/chromium/sandbox/linux/seccomp-bpf/trap.cc @@ -60,7 +60,7 @@ bool GetIsInSigHandler(const ucontext_t* ctx) { void SetIsInSigHandler() { sigset_t mask; if (sigemptyset(&mask) || sigaddset(&mask, LINUX_SIGBUS) || - sandbox::sys_sigprocmask(LINUX_SIG_BLOCK, &mask, NULL)) { + sandbox::sys_sigprocmask(LINUX_SIG_BLOCK, &mask, nullptr)) { SANDBOX_DIE("Failed to block SIGBUS"); } } @@ -77,7 +77,7 @@ bool IsDefaultSignalAction(const struct sigaction& sa) { namespace sandbox { Trap::Trap() - : trap_array_(NULL), + : trap_array_(nullptr), trap_array_size_(0), trap_array_capacity_(0), has_unsafe_traps_(false) { @@ -104,7 +104,7 @@ Trap::Trap() // Unmask SIGSYS sigset_t mask; if (sigemptyset(&mask) || sigaddset(&mask, LINUX_SIGSYS) || - sys_sigprocmask(LINUX_SIG_UNBLOCK, &mask, NULL)) { + sys_sigprocmask(LINUX_SIG_UNBLOCK, &mask, nullptr)) { SANDBOX_DIE("Failed to configure SIGSYS handler"); } } diff --git a/chromium/sandbox/linux/seccomp-bpf/trap.h b/chromium/sandbox/linux/seccomp-bpf/trap.h index a73d2064b47..6568a9b7b40 100644 --- a/chromium/sandbox/linux/seccomp-bpf/trap.h +++ b/chromium/sandbox/linux/seccomp-bpf/trap.h @@ -41,7 +41,7 @@ class SANDBOX_EXPORT Trap : public bpf_dsl::TrapRegistry { private: struct TrapKey { - TrapKey() : fnc(NULL), aux(NULL), safe(false) {} + TrapKey() : fnc(nullptr), aux(nullptr), safe(false) {} TrapKey(TrapFnc f, const void* a, bool s) : fnc(f), aux(a), safe(s) {} TrapFnc fnc; const void* aux; diff --git a/chromium/sandbox/linux/syscall_broker/broker_permission_list.cc b/chromium/sandbox/linux/syscall_broker/broker_permission_list.cc index 8032b3a9e56..397cfff7630 100644 --- a/chromium/sandbox/linux/syscall_broker/broker_permission_list.cc +++ b/chromium/sandbox/linux/syscall_broker/broker_permission_list.cc @@ -25,7 +25,7 @@ bool CheckCallerArgs(const char** file_to_access) { // Make sure that callers never pass a non-empty string. In case callers // wrongly forget to check the return value and look at the string // instead, this could catch bugs. - RAW_LOG(FATAL, "*file_to_access should be NULL"); + RAW_LOG(FATAL, "*file_to_access should be nullptr"); return false; } return true; @@ -45,7 +45,7 @@ BrokerPermissionList::BrokerPermissionList( if (num_of_permissions_ > 0) { permissions_array_ = &permissions_[0]; } else { - permissions_array_ = NULL; + permissions_array_ = nullptr; } } @@ -56,12 +56,12 @@ BrokerPermissionList::~BrokerPermissionList() {} // Note: access() being a system call to check permissions, this can get a bit // confusing. We're checking if calling access() should even be allowed with // the same policy we would use for open(). -// If |file_to_access| is not NULL, we will return the matching pointer from +// If |file_to_access| is not nullptr, we will return the matching pointer from // the whitelist. For paranoia a caller should then use |file_to_access|. See // GetFileNameIfAllowedToOpen() for more explanation. // return true if calling access() on this file should be allowed, false // otherwise. -// Async signal safe if and only if |file_to_access| is NULL. +// Async signal safe if and only if |file_to_access| is nullptr. bool BrokerPermissionList::GetFileNameIfAllowedToAccess( const char* requested_filename, int requested_mode, @@ -79,13 +79,13 @@ bool BrokerPermissionList::GetFileNameIfAllowedToAccess( } // Check if |requested_filename| can be opened with flags |requested_flags|. -// If |file_to_open| is not NULL, we will return the matching pointer from the -// whitelist. For paranoia, a caller should then use |file_to_open| rather +// If |file_to_open| is not nullptr, we will return the matching pointer from +// the whitelist. For paranoia, a caller should then use |file_to_open| rather // than |requested_filename|, so that it never attempts to open an // attacker-controlled file name, even if an attacker managed to fool the // string comparison mechanism. // Return true if opening should be allowed, false otherwise. -// Async signal safe if and only if |file_to_open| is NULL. +// Async signal safe if and only if |file_to_open| is nullptr. bool BrokerPermissionList::GetFileNameIfAllowedToOpen( const char* requested_filename, int requested_flags, diff --git a/chromium/sandbox/linux/system_headers/linux_seccomp.h b/chromium/sandbox/linux/system_headers/linux_seccomp.h index a60fe2ad3dc..ab1d1fc2b5d 100644 --- a/chromium/sandbox/linux/system_headers/linux_seccomp.h +++ b/chromium/sandbox/linux/system_headers/linux_seccomp.h @@ -67,6 +67,19 @@ #ifndef IPC_64 #define IPC_64 0x0100 #endif +#ifndef PR_SET_SPECULATION_CTRL +#define PR_SET_SPECULATION_CTRL 53 +#define PR_GET_SPECULATION_CTRL 52 +#endif +#ifndef PR_SPEC_INDIRECT_BRANCH +#define PR_SPEC_INDIRECT_BRANCH 1 +#endif +#ifndef PR_SPEC_PRCTL +#define PR_SPEC_PRCTL (1UL << 0) +#endif +#ifndef PR_SPEC_FORCE_DISABLE +#define PR_SPEC_FORCE_DISABLE (1UL << 3) +#endif // In order to build will older tool chains, we currently have to avoid // including <linux/seccomp.h>. Until that can be fixed (if ever). Rely on @@ -86,6 +99,9 @@ #ifndef SECCOMP_FILTER_FLAG_TSYNC #define SECCOMP_FILTER_FLAG_TSYNC 1 #endif +#ifndef SECCOMP_FILTER_FLAG_SPEC_ALLOW +#define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2) +#endif #ifndef SECCOMP_RET_KILL // Return values supported for BPF filter programs. Please note that the |