summaryrefslogtreecommitdiff
path: root/chromium/sandbox
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-11-18 16:35:47 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-11-18 15:45:54 +0000
commit32f5a1c56531e4210bc4cf8d8c7825d66e081888 (patch)
treeeeeec6822f4d738d8454525233fd0e2e3a659e6d /chromium/sandbox
parent99677208ff3b216fdfec551fbe548da5520cd6fb (diff)
downloadqtwebengine-chromium-32f5a1c56531e4210bc4cf8d8c7825d66e081888.tar.gz
BASELINE: Update Chromium to 87.0.4280.67
Change-Id: Ib157360be8c2ffb2c73125751a89f60e049c1d54 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/sandbox')
-rw-r--r--chromium/sandbox/linux/BUILD.gn17
-rw-r--r--chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc7
-rw-r--r--chromium/sandbox/linux/bpf_dsl/bpf_dsl.h6
-rw-r--r--chromium/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc7
-rw-r--r--chromium/sandbox/linux/bpf_dsl/dump_bpf.cc2
-rw-r--r--chromium/sandbox/linux/bpf_dsl/trap_registry.h9
-rw-r--r--chromium/sandbox/linux/bpf_dsl/verifier.h2
-rw-r--r--chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc2395
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc8
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc3
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h36
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc14
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc10
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h11
-rw-r--r--chromium/sandbox/linux/services/yama.cc9
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_client.cc33
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_client.h57
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_host.cc78
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_host.h5
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_process.cc286
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_process.h59
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc680
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_simple_message.cc89
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_simple_message.h58
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc199
-rw-r--r--chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler.cc146
-rw-r--r--chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler.h52
-rw-r--r--chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc346
-rw-r--r--chromium/sandbox/linux/syscall_broker/syscall_dispatcher.cc191
-rw-r--r--chromium/sandbox/linux/syscall_broker/syscall_dispatcher.h70
-rw-r--r--chromium/sandbox/linux/system_headers/linux_prctl.h35
-rw-r--r--chromium/sandbox/linux/system_headers/linux_seccomp.h117
-rw-r--r--chromium/sandbox/policy/BUILD.gn7
-rw-r--r--chromium/sandbox/policy/DEPS1
-rw-r--r--chromium/sandbox/policy/features.cc14
-rw-r--r--chromium/sandbox/policy/features.h6
-rw-r--r--chromium/sandbox/policy/linux/bpf_audio_policy_linux.cc6
-rw-r--r--chromium/sandbox/policy/linux/bpf_broker_policy_linux.h2
-rw-r--r--chromium/sandbox/policy/linux/bpf_gpu_policy_linux.cc7
-rw-r--r--chromium/sandbox/policy/linux/bpf_ime_policy_linux.cc6
-rw-r--r--chromium/sandbox/policy/linux/bpf_network_policy_linux.cc9
-rw-r--r--chromium/sandbox/policy/linux/bpf_speech_recognition_policy_linux.cc16
-rw-r--r--chromium/sandbox/policy/linux/bpf_tts_policy_linux.cc13
-rw-r--r--chromium/sandbox/policy/linux/sandbox_linux.cc27
-rw-r--r--chromium/sandbox/policy/linux/sandbox_linux.h13
-rw-r--r--chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc10
-rw-r--r--chromium/sandbox/policy/mac/BUILD.gn1
-rw-r--r--chromium/sandbox/policy/mac/common.sb2
-rw-r--r--chromium/sandbox/policy/mac/gpu_v2.sb1
-rw-r--r--chromium/sandbox/policy/mac/sandbox_mac.h2
-rw-r--r--chromium/sandbox/policy/mac/sandbox_mac.mm8
-rw-r--r--chromium/sandbox/policy/mac/speech_recognition.sb15
-rw-r--r--chromium/sandbox/policy/sandbox.cc3
-rw-r--r--chromium/sandbox/policy/sandbox_type.cc26
-rw-r--r--chromium/sandbox/policy/sandbox_type.h3
-rw-r--r--chromium/sandbox/policy/sandbox_type_unittest.cc14
-rw-r--r--chromium/sandbox/policy/switches.cc2
-rw-r--r--chromium/sandbox/policy/switches.h3
-rw-r--r--chromium/sandbox/policy/win/mf_cdm_sandbox_type_unittest.cc28
-rw-r--r--chromium/sandbox/policy/win/sandbox_win.cc75
-rw-r--r--chromium/sandbox/win/src/broker_services.cc21
61 files changed, 4486 insertions, 892 deletions
diff --git a/chromium/sandbox/linux/BUILD.gn b/chromium/sandbox/linux/BUILD.gn
index d1f3f14d44a..4fd639fd0fa 100644
--- a/chromium/sandbox/linux/BUILD.gn
+++ b/chromium/sandbox/linux/BUILD.gn
@@ -11,6 +11,13 @@ if (is_android) {
import("//build/config/android/rules.gni")
}
+# This file depends on the legacy global sources assignment filter. It should
+# be converted to check target platform before assigning source files to the
+# sources variable. Remove this import and set_sources_assignment_filter call
+# when the file has been converted. See https://crbug.com/1018739 for details.
+import("//build/config/deprecated_default_sources_assignment_filter.gni")
+set_sources_assignment_filter(deprecated_default_sources_assignment_filter)
+
declare_args() {
compile_suid_client = is_linux || is_chromeos
@@ -96,6 +103,7 @@ source_set("sandbox_linux_unittests_sources") {
"syscall_broker/broker_file_permission_unittest.cc",
"syscall_broker/broker_process_unittest.cc",
"syscall_broker/broker_simple_message_unittest.cc",
+ "syscall_broker/remote_syscall_arg_handler_unittest.cc",
"tests/main.cc",
"tests/scoped_temporary_file.cc",
"tests/scoped_temporary_file.h",
@@ -351,6 +359,10 @@ component("sandbox_services") {
"syscall_broker/broker_process.h",
"syscall_broker/broker_simple_message.cc",
"syscall_broker/broker_simple_message.h",
+ "syscall_broker/remote_syscall_arg_handler.cc",
+ "syscall_broker/remote_syscall_arg_handler.h",
+ "syscall_broker/syscall_dispatcher.cc",
+ "syscall_broker/syscall_dispatcher.h",
]
defines = [ "SANDBOX_IMPLEMENTATION" ]
@@ -400,6 +412,10 @@ component("sandbox_services") {
"syscall_broker/broker_process.h",
"syscall_broker/broker_simple_message.cc",
"syscall_broker/broker_simple_message.h",
+ "syscall_broker/remote_syscall_arg_handler.cc",
+ "syscall_broker/remote_syscall_arg_handler.h",
+ "syscall_broker/syscall_dispatcher.cc",
+ "syscall_broker/syscall_dispatcher.h",
]
} else if (!is_android) {
sources += [
@@ -417,6 +433,7 @@ source_set("sandbox_services_headers") {
"system_headers/i386_linux_ucontext.h",
"system_headers/linux_filter.h",
"system_headers/linux_futex.h",
+ "system_headers/linux_prctl.h",
"system_headers/linux_seccomp.h",
"system_headers/linux_signal.h",
"system_headers/linux_syscalls.h",
diff --git a/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc b/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc
index 51aa6ccf8c5..82a40696b87 100644
--- a/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc
+++ b/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc
@@ -33,7 +33,8 @@ class ReturnResultExprImpl : public internal::ResultExprImpl {
bool IsAllow() const override { return IsAction(SECCOMP_RET_ALLOW); }
bool IsDeny() const override {
- return IsAction(SECCOMP_RET_ERRNO) || IsAction(SECCOMP_RET_KILL);
+ return IsAction(SECCOMP_RET_ERRNO) || IsAction(SECCOMP_RET_KILL) ||
+ IsAction(SECCOMP_RET_USER_NOTIF);
}
private:
@@ -262,6 +263,10 @@ ResultExpr UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux) {
false /* unsafe */);
}
+ResultExpr UserNotify() {
+ return std::make_shared<ReturnResultExprImpl>(SECCOMP_RET_USER_NOTIF);
+}
+
BoolExpr BoolConst(bool value) {
return std::make_shared<ConstBoolExprImpl>(value);
}
diff --git a/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h b/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h
index 555d820e017..24261f65021 100644
--- a/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h
+++ b/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h
@@ -120,6 +120,12 @@ SANDBOX_EXPORT ResultExpr
SANDBOX_EXPORT ResultExpr
UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux);
+// UserNotify specifies that the kernel shall notify a listening process that a
+// syscall occurred. The listening process may perform the system call on
+// behalf of the sandboxed process, or may instruct the sandboxed process to
+// continue the system call.
+SANDBOX_EXPORT ResultExpr UserNotify();
+
// BoolConst converts a bool value into a BoolExpr.
SANDBOX_EXPORT BoolExpr BoolConst(bool value);
diff --git a/chromium/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc b/chromium/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
index cae77db0435..ae35b33f0cd 100644
--- a/chromium/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
+++ b/chromium/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
@@ -453,6 +453,10 @@ TEST(BPFDSL, IsAllowDeny) {
EXPECT_FALSE(trap->IsAllow());
EXPECT_TRUE(trap->IsDeny());
+ ResultExpr user_notify = UserNotify();
+ EXPECT_FALSE(user_notify->IsAllow());
+ EXPECT_TRUE(user_notify->IsDeny());
+
const Arg<int> arg(0);
ResultExpr maybe = If(arg == 0, Allow()).Else(Error(EPERM));
EXPECT_FALSE(maybe->IsAllow());
@@ -469,6 +473,9 @@ TEST(BPFDSL, HasUnsafeTraps) {
ResultExpr unsafe = UnsafeTrap(DummyTrap, nullptr);
EXPECT_TRUE(unsafe->HasUnsafeTraps());
+ ResultExpr user_notify = UserNotify();
+ EXPECT_FALSE(allow->HasUnsafeTraps());
+
const Arg<int> arg(0);
ResultExpr maybe = If(arg == 0, allow).Else(unsafe);
EXPECT_TRUE(maybe->HasUnsafeTraps());
diff --git a/chromium/sandbox/linux/bpf_dsl/dump_bpf.cc b/chromium/sandbox/linux/bpf_dsl/dump_bpf.cc
index 2edf592f68f..677e0761aaa 100644
--- a/chromium/sandbox/linux/bpf_dsl/dump_bpf.cc
+++ b/chromium/sandbox/linux/bpf_dsl/dump_bpf.cc
@@ -119,6 +119,8 @@ void AppendInstruction(std::string* dst, size_t pc, const sock_filter& insn) {
} else if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRACE) {
base::StringAppendF(dst, "Trace #%" PRIu32 "\n",
insn.k & SECCOMP_RET_DATA);
+ } else if (insn.k == SECCOMP_RET_USER_NOTIF) {
+ base::StringAppendF(dst, "UserNotif\n");
} else if (insn.k == SECCOMP_RET_ALLOW) {
base::StringAppendF(dst, "Allowed\n");
} else if (insn.k == SECCOMP_RET_KILL) {
diff --git a/chromium/sandbox/linux/bpf_dsl/trap_registry.h b/chromium/sandbox/linux/bpf_dsl/trap_registry.h
index 0a5d2f14ccc..2fc78a6fa56 100644
--- a/chromium/sandbox/linux/bpf_dsl/trap_registry.h
+++ b/chromium/sandbox/linux/bpf_dsl/trap_registry.h
@@ -8,18 +8,11 @@
#include <stdint.h>
#include "base/macros.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
#include "sandbox/sandbox_export.h"
namespace sandbox {
-// This must match the kernel's seccomp_data structure.
-struct arch_seccomp_data {
- int nr;
- uint32_t arch;
- uint64_t instruction_pointer;
- uint64_t args[6];
-};
-
namespace bpf_dsl {
// TrapRegistry provides an interface for registering "trap handlers"
diff --git a/chromium/sandbox/linux/bpf_dsl/verifier.h b/chromium/sandbox/linux/bpf_dsl/verifier.h
index 9b25ab1d714..581f49f9e53 100644
--- a/chromium/sandbox/linux/bpf_dsl/verifier.h
+++ b/chromium/sandbox/linux/bpf_dsl/verifier.h
@@ -12,10 +12,10 @@
#include "base/macros.h"
#include "sandbox/sandbox_export.h"
+struct arch_seccomp_data;
struct sock_filter;
namespace sandbox {
-struct arch_seccomp_data;
namespace bpf_dsl {
diff --git a/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc b/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
index 55ecf588227..fd6cd00bc67 100644
--- a/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
+++ b/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
@@ -9,88 +9,102 @@
#include <unistd.h>
#include <memory>
+#include <tuple>
+#include <type_traits>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/test/bind_test_util.h"
#include "build/build_config.h"
#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
#include "sandbox/linux/bpf_dsl/policy.h"
#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h"
+#include "sandbox/linux/syscall_broker/broker_client.h"
#include "sandbox/linux/syscall_broker/broker_command.h"
#include "sandbox/linux/syscall_broker/broker_file_permission.h"
#include "sandbox/linux/syscall_broker/broker_process.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "sandbox/linux/tests/scoped_temporary_file.h"
+#include "sandbox/linux/tests/test_utils.h"
#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest-param-test.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace sandbox {
-namespace {
-
using bpf_dsl::Allow;
using bpf_dsl::ResultExpr;
using bpf_dsl::Trap;
+using BrokerProcess = syscall_broker::BrokerProcess;
+using BrokerType = syscall_broker::BrokerProcess::BrokerType;
+using BrokerCommandSet = syscall_broker::BrokerCommandSet;
+using BrokerFilePermission = syscall_broker::BrokerFilePermission;
+
// Test a trap handler that makes use of a broker process to open().
class InitializedOpenBroker {
public:
- InitializedOpenBroker() : initialized_(false) {
+ explicit InitializedOpenBroker(
+ BrokerType broker_type = BrokerType::SIGNAL_BASED) {
syscall_broker::BrokerCommandSet command_set;
command_set.set(syscall_broker::COMMAND_OPEN);
command_set.set(syscall_broker::COMMAND_ACCESS);
- std::vector<syscall_broker::BrokerFilePermission> permissions = {
- syscall_broker::BrokerFilePermission::ReadOnly("/proc/allowed"),
- syscall_broker::BrokerFilePermission::ReadOnly("/proc/cpuinfo")};
- broker_process_ = std::make_unique<syscall_broker::BrokerProcess>(
- EPERM, command_set, permissions);
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly("/proc/allowed"),
+ BrokerFilePermission::ReadOnly("/proc/cpuinfo")};
+ broker_process_ = std::make_unique<BrokerProcess>(EPERM, command_set,
+ permissions, broker_type);
BPF_ASSERT(broker_process_->Init(base::BindOnce([]() { return true; })));
- initialized_ = true;
}
- bool initialized() const { return initialized_; }
-
- syscall_broker::BrokerProcess* broker_process() const {
- return broker_process_.get();
- }
+ BrokerProcess* broker_process() const { return broker_process_.get(); }
private:
- bool initialized_;
- std::unique_ptr<syscall_broker::BrokerProcess> broker_process_;
+ std::unique_ptr<BrokerProcess> broker_process_;
DISALLOW_COPY_AND_ASSIGN(InitializedOpenBroker);
};
intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args,
void* aux) {
BPF_ASSERT(aux);
- syscall_broker::BrokerProcess* broker_process =
- static_cast<syscall_broker::BrokerProcess*>(aux);
+ BrokerProcess* broker_process = static_cast<BrokerProcess*>(aux);
switch (args.nr) {
case __NR_faccessat: // access is a wrapper of faccessat in android
BPF_ASSERT(static_cast<int>(args.args[0]) == AT_FDCWD);
- return broker_process->Access(reinterpret_cast<const char*>(args.args[1]),
- static_cast<int>(args.args[2]));
+ return broker_process->GetBrokerClientSignalBased()->Access(
+ reinterpret_cast<const char*>(args.args[1]),
+ static_cast<int>(args.args[2]));
#if defined(__NR_access)
case __NR_access:
- return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
- static_cast<int>(args.args[1]));
+ return broker_process->GetBrokerClientSignalBased()->Access(
+ reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
#endif
#if defined(__NR_open)
case __NR_open:
- return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
- static_cast<int>(args.args[1]));
+ return broker_process->GetBrokerClientSignalBased()->Open(
+ reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
#endif
case __NR_openat:
// We only call open() so if we arrive here, it's because glibc uses
// the openat() system call.
BPF_ASSERT(static_cast<int>(args.args[0]) == AT_FDCWD);
- return broker_process->Open(reinterpret_cast<const char*>(args.args[1]),
- static_cast<int>(args.args[2]));
+ return broker_process->GetBrokerClientSignalBased()->Open(
+ reinterpret_cast<const char*>(args.args[1]),
+ static_cast<int>(args.args[2]));
default:
BPF_ASSERT(false);
return -ENOSYS;
@@ -115,7 +129,7 @@ class DenyOpenPolicy : public bpf_dsl::Policy {
#endif
case __NR_openat:
// We get a InitializedOpenBroker class, but our trap handler wants
- // the syscall_broker::BrokerProcess object.
+ // the BrokerProcess object.
return Trap(BrokerOpenTrapHandler, iob_->broker_process());
default:
return Allow();
@@ -134,15 +148,18 @@ BPF_TEST(SandboxBPF,
UseOpenBroker,
DenyOpenPolicy,
InitializedOpenBroker /* (*BPF_AUX) */) {
- BPF_ASSERT(BPF_AUX->initialized());
- syscall_broker::BrokerProcess* broker_process = BPF_AUX->broker_process();
- BPF_ASSERT(broker_process != NULL);
+ BrokerProcess* broker_process = BPF_AUX->broker_process();
+ BPF_ASSERT(broker_process != nullptr);
// First, use the broker "manually"
- BPF_ASSERT(broker_process->Open("/proc/denied", O_RDONLY) == -EPERM);
- BPF_ASSERT(broker_process->Access("/proc/denied", R_OK) == -EPERM);
- BPF_ASSERT(broker_process->Open("/proc/allowed", O_RDONLY) == -ENOENT);
- BPF_ASSERT(broker_process->Access("/proc/allowed", R_OK) == -ENOENT);
+ BPF_ASSERT(broker_process->GetBrokerClientSignalBased()->Open(
+ "/proc/denied", O_RDONLY) == -EPERM);
+ BPF_ASSERT(broker_process->GetBrokerClientSignalBased()->Access(
+ "/proc/denied", R_OK) == -EPERM);
+ BPF_ASSERT(broker_process->GetBrokerClientSignalBased()->Open(
+ "/proc/allowed", O_RDONLY) == -ENOENT);
+ BPF_ASSERT(broker_process->GetBrokerClientSignalBased()->Access(
+ "/proc/allowed", R_OK) == -ENOENT);
// Now use glibc's open() as an external library would.
BPF_ASSERT(open("/proc/denied", O_RDONLY) == -1);
@@ -172,9 +189,2317 @@ BPF_TEST(SandboxBPF,
int cpu_info_fd = open("/proc/cpuinfo", O_RDONLY);
BPF_ASSERT(cpu_info_fd >= 0);
char buf[1024];
- BPF_ASSERT(read(cpu_info_fd, buf, sizeof(buf)) > 0);
+ BPF_ASSERT(HANDLE_EINTR(read(cpu_info_fd, buf, sizeof(buf))) > 0);
}
+// The rest of the tests do not run under thread sanitizer, as TSAN starts up an
+// extra thread which triggers a sandbox assertion. BPF_TESTs do not run under
+// TSAN.
+#if !defined(THREAD_SANITIZER)
+
+namespace {
+// Our fake errno must be less than 255 or various libc implementations will
+// not accept this as a valid error number. E.g. bionic accepts up to 255, glibc
+// and musl up to 4096.
+const int kFakeErrnoSentinel = 254;
+} // namespace
+
+// There are a variety of ways to make syscalls in a sandboxed process. One is
+// to directly make the syscalls, one is to make the syscalls through libc
+// (which oftens uses different underlying syscalls per platform and kernel
+// version). With the signals-based broker, the sandboxed process can also make
+// calls directly to the broker over the existing IPC channel.
+// This interface encompasses the available syscalls so we can test every method
+// of making syscalls.
+class Syscaller {
+ public:
+ virtual ~Syscaller() = default;
+
+ virtual int Open(const char* filepath, int flags) = 0;
+ virtual int Access(const char* filepath, int mode) = 0;
+ virtual int Stat(const char* filepath,
+ bool follow_links,
+ struct stat* statbuf) = 0;
+ virtual int Rename(const char* oldpath, const char* newpath) = 0;
+ virtual int Readlink(const char* path, char* buf, size_t bufsize) = 0;
+ virtual int Mkdir(const char* pathname, mode_t mode) = 0;
+ virtual int Rmdir(const char* path) = 0;
+ virtual int Unlink(const char* path) = 0;
+};
+
+class IPCSyscaller : public Syscaller {
+ public:
+ explicit IPCSyscaller(BrokerProcess* broker) : broker_(broker) {}
+ ~IPCSyscaller() override = default;
+
+ int Open(const char* filepath, int flags) override {
+ return broker_->GetBrokerClientSignalBased()->Open(filepath, flags);
+ }
+
+ int Access(const char* filepath, int mode) override {
+ return broker_->GetBrokerClientSignalBased()->Access(filepath, mode);
+ }
+
+ int Stat(const char* filepath,
+ bool follow_links,
+ struct stat* statbuf) override {
+ return broker_->GetBrokerClientSignalBased()->Stat(filepath, follow_links,
+ statbuf);
+ }
+
+ int Rename(const char* oldpath, const char* newpath) override {
+ return broker_->GetBrokerClientSignalBased()->Rename(oldpath, newpath);
+ }
+
+ int Readlink(const char* path, char* buf, size_t bufsize) override {
+ return broker_->GetBrokerClientSignalBased()->Readlink(path, buf, bufsize);
+ }
+
+ int Mkdir(const char* pathname, mode_t mode) override {
+ return broker_->GetBrokerClientSignalBased()->Mkdir(pathname, mode);
+ }
+
+ int Rmdir(const char* path) override {
+ return broker_->GetBrokerClientSignalBased()->Rmdir(path);
+ }
+
+ int Unlink(const char* path) override {
+ return broker_->GetBrokerClientSignalBased()->Unlink(path);
+ }
+
+ private:
+ BrokerProcess* broker_;
+};
+
+// Only use syscall(...) on x64 to avoid having to reimplement a libc-like
+// layer that uses different syscalls on different architectures.
+#if (defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)) && \
+ defined(__x86_64__)
+#define DIRECT_SYSCALLER_ENABLED
+#endif
+
+#if defined(DIRECT_SYSCALLER_ENABLED)
+class DirectSyscaller : public Syscaller {
+ public:
+ ~DirectSyscaller() override = default;
+
+ int Open(const char* filepath, int flags) override {
+ int ret = syscall(__NR_open, filepath, flags);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Access(const char* filepath, int mode) override {
+ int ret = syscall(__NR_access, filepath, mode);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Stat(const char* filepath,
+ bool follow_links,
+ struct stat* statbuf) override {
+ int ret = follow_links ? syscall(__NR_stat, filepath, statbuf)
+ : syscall(__NR_lstat, filepath, statbuf);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Rename(const char* oldpath, const char* newpath) override {
+ int ret = syscall(__NR_rename, oldpath, newpath);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Readlink(const char* path, char* buf, size_t bufsize) override {
+ int ret = syscall(__NR_readlink, path, buf, bufsize);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Mkdir(const char* pathname, mode_t mode) override {
+ int ret = syscall(__NR_mkdir, pathname, mode);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Rmdir(const char* path) override {
+ int ret = syscall(__NR_rmdir, path);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Unlink(const char* path) override {
+ int ret = syscall(__NR_unlink, path);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+};
+#endif // defined(DIRECT_SYSCALLER_ENABLED)
+
+class LibcSyscaller : public Syscaller {
+ public:
+ ~LibcSyscaller() override = default;
+
+ int Open(const char* filepath, int flags) override {
+ int ret = HANDLE_EINTR(open(filepath, flags, 0600));
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+ int Access(const char* filepath, int mode) override {
+ int ret = access(filepath, mode);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Stat(const char* filepath,
+ bool follow_links,
+ struct stat* statbuf) override {
+ int ret = follow_links ? stat(filepath, statbuf) : lstat(filepath, statbuf);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Rename(const char* oldpath, const char* newpath) override {
+ int ret = rename(oldpath, newpath);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Readlink(const char* path, char* buf, size_t bufsize) override {
+ int ret = readlink(path, buf, bufsize);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Mkdir(const char* pathname, mode_t mode) override {
+ int ret = mkdir(pathname, mode);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Rmdir(const char* path) override {
+ int ret = rmdir(path);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ int Unlink(const char* path) override {
+ int ret = unlink(path);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+};
+
+enum class SyscallerType { IPCSyscaller = 0, DirectSyscaller, LibcSyscaller };
+
+// The testing infrastructure for the broker integration tests is built on the
+// same infrastructure that BPF_TEST or SANDBOX_TEST uses. Each individual test
+// starts up a child process, which itself starts up a broker process and
+// sandboxes itself. To create a test, implement this delegate and call
+// RunAllBrokerTests() in a TEST(). The bulk of the test body will be in
+// RunTestInSandboxedChild().
+class BrokerTestDelegate {
+ public:
+ struct BrokerParams {
+ int denied_errno = kFakeErrnoSentinel;
+ syscall_broker::BrokerCommandSet allowed_command_set;
+ std::vector<BrokerFilePermission> permissions;
+ };
+
+ virtual ~BrokerTestDelegate() = default;
+
+ // Called in the parent test process before starting the child process. Should
+ // use GTEST's ASSERT macros.
+ virtual void ParentSetUp() {}
+
+ // Sets up the test in the child process before applying the sandbox.
+ // |allowed_command_set| and |permissions| should be filled in with the
+ // desired commands and permissions the broker should allow. Should use
+ // BPF_ASSERT.
+ virtual BrokerParams ChildSetUpPreSandbox() = 0;
+
+ // Gets called in the sandboxed process with the pid of the newly started
+ // broker.
+ virtual void OnBrokerStarted(pid_t broker_pid) {}
+
+ // Runs the test after setting up the sandbox in the forked process.
+ // Assertions should use BPF_ASSERT.
+ virtual void RunTestInSandboxedChild(Syscaller* syscaller) = 0;
+
+ // Called in the parent test process after the child dies. Should perform
+ // cleanup, and can also ASSERT like a normal GTEST. Note that modifications
+ // of class state in the above two functions will not be visible here, as they
+ // ran in the forked child.
+ virtual void ParentTearDown() {
+ ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
+ }
+};
+
+namespace syscall_broker {
+// A BPF policy for our BPF_TEST that defers to the broker for syscalls that
+// take paths, and allows everything else.
+class HandleFilesystemViaBrokerPolicy : public bpf_dsl::Policy {
+ public:
+ explicit HandleFilesystemViaBrokerPolicy(BrokerProcess* broker_process,
+ int denied_errno)
+ : broker_process_(broker_process), denied_errno_(denied_errno) {}
+ ~HandleFilesystemViaBrokerPolicy() override = default;
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Broker everything that we're supposed to broker.
+ if (broker_process_->IsSyscallAllowed(sysno)) {
+ return sandbox::bpf_dsl::Trap(
+ sandbox::syscall_broker::BrokerClient::SIGSYS_Handler,
+ broker_process_->GetBrokerClientSignalBased());
+ }
+
+ // Otherwise, if this is a syscall that takes a pathname but isn't an
+ // allowed command, deny it.
+ if (broker_process_->IsSyscallBrokerable(sysno,
+ /*fast_check_in_client=*/false)) {
+ return bpf_dsl::Error(denied_errno_);
+ }
+
+ // Allow everything else that doesn't take a pathname.
+ return Allow();
+ }
+
+ private:
+ BrokerProcess* broker_process_;
+ int denied_errno_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleFilesystemViaBrokerPolicy);
+};
+} // namespace syscall_broker
+
+// This implements BPFTesterDelegate to layer the broker integration tests on
+// top of BPF_TEST infrastructure.
+class BPFTesterBrokerDelegate : public BPFTesterDelegate {
+ public:
+ explicit BPFTesterBrokerDelegate(bool fast_check_in_client,
+ BrokerTestDelegate* broker_test_delegate,
+ SyscallerType syscaller_type,
+ BrokerType broker_type)
+ : fast_check_in_client_(fast_check_in_client),
+ broker_test_delegate_(broker_test_delegate),
+ syscaller_type_(syscaller_type),
+ broker_type_(broker_type) {}
+ ~BPFTesterBrokerDelegate() override = default;
+
+ std::unique_ptr<bpf_dsl::Policy> GetSandboxBPFPolicy() override {
+ BrokerTestDelegate::BrokerParams broker_params =
+ broker_test_delegate_->ChildSetUpPreSandbox();
+
+ broker_process_ = std::make_unique<BrokerProcess>(
+ broker_params.denied_errno, broker_params.allowed_command_set,
+ broker_params.permissions, broker_type_, fast_check_in_client_);
+ BPF_ASSERT(broker_process_->Init(base::BindOnce([]() { return true; })));
+ broker_test_delegate_->OnBrokerStarted(broker_process_->broker_pid());
+
+ BPF_ASSERT(TestUtils::CurrentProcessHasChildren());
+
+ CreateSyscaller();
+
+ return std::unique_ptr<bpf_dsl::Policy>(
+ new syscall_broker::HandleFilesystemViaBrokerPolicy(
+ broker_process_.get(), broker_params.denied_errno));
+ }
+
+ void RunTestFunction() override {
+ broker_test_delegate_->RunTestInSandboxedChild(syscaller_.get());
+ }
+
+ void CreateSyscaller() {
+ BPF_ASSERT(broker_process_->GetBrokerClientSignalBased());
+ switch (syscaller_type_) {
+ case SyscallerType::IPCSyscaller:
+ syscaller_ = std::make_unique<IPCSyscaller>(broker_process_.get());
+ break;
+ case SyscallerType::DirectSyscaller:
+#if defined(DIRECT_SYSCALLER_ENABLED)
+ syscaller_ = std::make_unique<DirectSyscaller>();
+#else
+ CHECK(false) << "Requested instantiation of DirectSyscaller on a "
+ "platform that doesn't support it";
+#endif
+ break;
+ case SyscallerType::LibcSyscaller:
+ syscaller_ = std::make_unique<LibcSyscaller>();
+ break;
+ }
+ }
+
+ private:
+ bool fast_check_in_client_;
+ BrokerTestDelegate* broker_test_delegate_;
+ SyscallerType syscaller_type_;
+ BrokerType broker_type_;
+
+ std::unique_ptr<BrokerProcess> broker_process_;
+ std::unique_ptr<Syscaller> syscaller_;
+};
+
+namespace {
+struct BrokerTestConfiguration {
+ std::string test_name;
+ bool fast_check_in_client;
+ SyscallerType syscaller_type;
+ BrokerType broker_type;
+};
+
+// Lists out all the broker configurations we want to test.
+const std::vector<BrokerTestConfiguration> broker_test_configs = {
+ {"FastCheckInClient_IPCSyscaller", true, SyscallerType::IPCSyscaller,
+ BrokerType::SIGNAL_BASED},
+#if defined(DIRECT_SYSCALLER_ENABLED)
+ {"FastCheckInClient_DirectSyscaller", true, SyscallerType::DirectSyscaller,
+ BrokerType::SIGNAL_BASED},
+#endif
+ {"FastCheckInClient_LibcSyscaller", true, SyscallerType::LibcSyscaller,
+ BrokerType::SIGNAL_BASED},
+ {"NoFastCheckInClient_IPCSyscaller", false, SyscallerType::IPCSyscaller,
+ BrokerType::SIGNAL_BASED},
+#if defined(DIRECT_SYSCALLER_ENABLED)
+ {"NoFastCheckInClient_DirectSyscaller", false,
+ SyscallerType::DirectSyscaller, BrokerType::SIGNAL_BASED},
+#endif
+ {"NoFastCheckInClient_LibcSyscaller", false, SyscallerType::LibcSyscaller,
+ BrokerType::SIGNAL_BASED}};
} // namespace
+void RunSingleBrokerTest(BrokerTestDelegate* test_delegate,
+ const BrokerTestConfiguration& test_config) {
+ test_delegate->ParentSetUp();
+ sandbox::SandboxBPFTestRunner bpf_test_runner(new BPFTesterBrokerDelegate(
+ test_config.fast_check_in_client, test_delegate,
+ test_config.syscaller_type, test_config.broker_type));
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, DEATH_SUCCESS());
+ test_delegate->ParentTearDown();
+}
+
+template <typename T>
+void RunAllBrokerTests() {
+ for (const BrokerTestConfiguration& test_config : broker_test_configs) {
+ SCOPED_TRACE(test_config.test_name);
+ auto test_delegate = std::make_unique<T>();
+ RunSingleBrokerTest(test_delegate.get(), test_config);
+ }
+}
+
+template <typename T>
+void RunIPCBrokerTests() {
+ for (const BrokerTestConfiguration& test_config : broker_test_configs) {
+ if (test_config.syscaller_type != SyscallerType::IPCSyscaller)
+ continue;
+
+ SCOPED_TRACE(test_config.test_name);
+ auto test_delegate = std::make_unique<T>();
+ RunSingleBrokerTest(test_delegate.get(), test_config);
+ }
+}
+
+// Tests that a SIGNALS_BASED broker responds with -EFAULT when open() or
+// access() are called with nullptr.
+class TestOpenAccessNullDelegate final : public BrokerTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_ACCESS, syscall_broker::COMMAND_OPEN});
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ int fd = syscaller->Open(nullptr, O_RDONLY);
+ BPF_ASSERT_EQ(fd, -EFAULT);
+
+ int ret = syscaller->Access(nullptr, F_OK);
+ BPF_ASSERT_EQ(ret, -EFAULT);
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, TestOpenAccessNull) {
+ RunIPCBrokerTests<TestOpenAccessNullDelegate>();
+}
+
+// Tests open()/access() for files that do not exist, are not allowed by
+// allowlist, and are allowed by allowlist but not accessible.
+template <int DENIED_ERRNO>
+class TestOpenFilePermsDelegate final : public BrokerTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.denied_errno = DENIED_ERRNO;
+
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_ACCESS, syscall_broker::COMMAND_OPEN});
+
+ params.permissions = {
+ BrokerFilePermission::ReadOnly(kR_AllowListed),
+ BrokerFilePermission::ReadOnly(kR_AllowListedButDenied),
+ BrokerFilePermission::WriteOnly(kW_AllowListed),
+ BrokerFilePermission::ReadWrite(kRW_AllowListed)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ int fd = -1;
+ fd = syscaller->Open(kR_AllowListed, O_RDONLY);
+ BPF_ASSERT_EQ(fd, -ENOENT);
+ fd = syscaller->Open(kR_AllowListed, O_WRONLY);
+ BPF_ASSERT_EQ(fd, -DENIED_ERRNO);
+ fd = syscaller->Open(kR_AllowListed, O_RDWR);
+ BPF_ASSERT_EQ(fd, -DENIED_ERRNO);
+ int ret = -1;
+ ret = syscaller->Access(kR_AllowListed, F_OK);
+ BPF_ASSERT_EQ(ret, -ENOENT);
+ ret = syscaller->Access(kR_AllowListed, R_OK);
+ BPF_ASSERT_EQ(ret, -ENOENT);
+ ret = syscaller->Access(kR_AllowListed, W_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(kR_AllowListed, R_OK | W_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(kR_AllowListed, X_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(kR_AllowListed, R_OK | X_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+
+ // Android sometimes runs tests as root.
+ // This part of the test requires a process that doesn't have
+ // CAP_DAC_OVERRIDE. We check against a root euid as a proxy for that.
+ if (geteuid()) {
+ fd = syscaller->Open(kR_AllowListedButDenied, O_RDONLY);
+ // The broker process will allow this, but the normal permission system
+ // won't.
+ BPF_ASSERT_EQ(fd, -EACCES);
+ fd = syscaller->Open(kR_AllowListedButDenied, O_WRONLY);
+ BPF_ASSERT_EQ(fd, -DENIED_ERRNO);
+ fd = syscaller->Open(kR_AllowListedButDenied, O_RDWR);
+ BPF_ASSERT_EQ(fd, -DENIED_ERRNO);
+ ret = syscaller->Access(kR_AllowListedButDenied, F_OK);
+ // The normal permission system will let us check that the file exists.
+ BPF_ASSERT_EQ(ret, 0);
+ ret = syscaller->Access(kR_AllowListedButDenied, R_OK);
+ BPF_ASSERT_EQ(ret, -EACCES);
+ ret = syscaller->Access(kR_AllowListedButDenied, W_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(kR_AllowListedButDenied, R_OK | W_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(kR_AllowListedButDenied, X_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(kR_AllowListedButDenied, R_OK | X_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ }
+
+ fd = syscaller->Open(kW_AllowListed, O_RDONLY);
+ BPF_ASSERT_EQ(fd, -DENIED_ERRNO);
+ fd = syscaller->Open(kW_AllowListed, O_WRONLY);
+ BPF_ASSERT_EQ(fd, -ENOENT);
+ fd = syscaller->Open(kW_AllowListed, O_RDWR);
+ BPF_ASSERT_EQ(fd, -DENIED_ERRNO);
+ ret = syscaller->Access(kW_AllowListed, F_OK);
+ BPF_ASSERT_EQ(ret, -ENOENT);
+ ret = syscaller->Access(kW_AllowListed, R_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(kW_AllowListed, W_OK);
+ BPF_ASSERT_EQ(ret, -ENOENT);
+ ret = syscaller->Access(kW_AllowListed, R_OK | W_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(kW_AllowListed, X_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(kW_AllowListed, R_OK | X_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+
+ fd = syscaller->Open(kRW_AllowListed, O_RDONLY);
+ BPF_ASSERT_EQ(fd, -ENOENT);
+ fd = syscaller->Open(kRW_AllowListed, O_WRONLY);
+ BPF_ASSERT_EQ(fd, -ENOENT);
+ fd = syscaller->Open(kRW_AllowListed, O_RDWR);
+ BPF_ASSERT_EQ(fd, -ENOENT);
+ ret = syscaller->Access(kRW_AllowListed, F_OK);
+ BPF_ASSERT_EQ(ret, -ENOENT);
+ ret = syscaller->Access(kRW_AllowListed, R_OK);
+ BPF_ASSERT_EQ(ret, -ENOENT);
+ ret = syscaller->Access(kRW_AllowListed, W_OK);
+ BPF_ASSERT_EQ(ret, -ENOENT);
+ ret = syscaller->Access(kRW_AllowListed, R_OK | W_OK);
+ BPF_ASSERT_EQ(ret, -ENOENT);
+ ret = syscaller->Access(kRW_AllowListed, X_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(kRW_AllowListed, R_OK | X_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+
+ fd = syscaller->Open(k_NotAllowlisted, O_RDONLY);
+ BPF_ASSERT_EQ(fd, -DENIED_ERRNO);
+ fd = syscaller->Open(k_NotAllowlisted, O_WRONLY);
+ BPF_ASSERT_EQ(fd, -DENIED_ERRNO);
+ fd = syscaller->Open(k_NotAllowlisted, O_RDWR);
+ BPF_ASSERT_EQ(fd, -DENIED_ERRNO);
+ ret = syscaller->Access(k_NotAllowlisted, F_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(k_NotAllowlisted, R_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(k_NotAllowlisted, W_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(k_NotAllowlisted, R_OK | W_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(k_NotAllowlisted, X_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+ ret = syscaller->Access(k_NotAllowlisted, R_OK | X_OK);
+ BPF_ASSERT_EQ(ret, -DENIED_ERRNO);
+
+ // We have some extra sanity check for clearly wrong values.
+ fd = syscaller->Open(kRW_AllowListed, O_RDONLY | O_WRONLY | O_RDWR);
+ BPF_ASSERT_EQ(fd, -DENIED_ERRNO);
+
+ // It makes no sense to allow O_CREAT in a 2-parameters open. Ensure this
+ // is denied.
+ fd = syscaller->Open(kRW_AllowListed, O_RDWR | O_CREAT);
+ BPF_ASSERT_EQ(fd, -DENIED_ERRNO);
+ }
+
+ private:
+ const char* const kR_AllowListed = "/proc/DOESNOTEXIST1";
+ // We can't debug the init process, and shouldn't be able to access
+ // its auxv file.
+ const char* kR_AllowListedButDenied = "/proc/1/auxv";
+ const char* kW_AllowListed = "/proc/DOESNOTEXIST2";
+ const char* kRW_AllowListed = "/proc/DOESNOTEXIST3";
+ const char* k_NotAllowlisted = "/proc/DOESNOTEXIST4";
+};
+
+TEST(BrokerProcessIntegrationTest, TestOpenFilePermsEPERM) {
+ RunAllBrokerTests<TestOpenFilePermsDelegate<EPERM>>();
+}
+
+TEST(BrokerProcessIntegrationTest, TestOpenFilePermsENOENT) {
+ RunAllBrokerTests<TestOpenFilePermsDelegate<ENOENT>>();
+}
+
+class BadPathsDelegate final : public BrokerTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_ACCESS, syscall_broker::COMMAND_OPEN});
+ params.permissions = {BrokerFilePermission::ReadOnlyRecursive("/proc/")};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ // Open cpuinfo via the broker.
+ int cpuinfo_fd = syscaller->Open(kFileCpuInfo, O_RDONLY);
+ base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
+ BPF_ASSERT_GE(cpuinfo_fd, 0);
+
+ int fd = -1;
+ int can_access;
+
+ can_access = syscaller->Access(kNotAbsPath, R_OK);
+ BPF_ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+ fd = syscaller->Open(kNotAbsPath, O_RDONLY);
+ BPF_ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ can_access = syscaller->Access(kDotDotStart, R_OK);
+ BPF_ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+ fd = syscaller->Open(kDotDotStart, O_RDONLY);
+ BPF_ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ can_access = syscaller->Access(kDotDotMiddle, R_OK);
+ BPF_ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+ fd = syscaller->Open(kDotDotMiddle, O_RDONLY);
+ BPF_ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ can_access = syscaller->Access(kDotDotEnd, R_OK);
+ BPF_ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+ fd = syscaller->Open(kDotDotEnd, O_RDONLY);
+ BPF_ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ can_access = syscaller->Access(kTrailingSlash, R_OK);
+ BPF_ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+ fd = syscaller->Open(kTrailingSlash, O_RDONLY);
+ BPF_ASSERT_EQ(fd, -kFakeErrnoSentinel);
+ }
+
+ private:
+ const char* const kFileCpuInfo = "/proc/cpuinfo";
+ const char* const kNotAbsPath = "proc/cpuinfo";
+ const char* const kDotDotStart = "/../proc/cpuinfo";
+ const char* const kDotDotMiddle = "/proc/self/../cpuinfo";
+ const char* const kDotDotEnd = "/proc/..";
+ const char* const kTrailingSlash = "/proc/";
+};
+
+TEST(BrokerProcessIntegrationTest, BadPaths) {
+ RunAllBrokerTests<BadPathsDelegate>();
+}
+
+template <bool recursive>
+class OpenCpuinfoDelegate final : public BrokerTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ // Open cpuinfo directly.
+ int cpu_info_fd = HANDLE_EINTR(open(kFileCpuInfo, O_RDONLY));
+ BPF_ASSERT_GE(cpu_info_fd, 0);
+ memset(cpuinfo_buf_, 1, sizeof(cpuinfo_buf_));
+ read_len_unsandboxed_ =
+ HANDLE_EINTR(read(cpu_info_fd, cpuinfo_buf_, sizeof(cpuinfo_buf_)));
+ BPF_ASSERT_GT(read_len_unsandboxed_, 0);
+
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_ACCESS, syscall_broker::COMMAND_OPEN});
+ params.permissions.push_back(
+ recursive ? BrokerFilePermission::ReadOnlyRecursive(kDirProc)
+ : BrokerFilePermission::ReadOnly(kFileCpuInfo));
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ int fd = syscaller->Open(kFileCpuInfo, O_RDWR);
+ BPF_ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ // Check we can read /proc/cpuinfo.
+ int can_access = syscaller->Access(kFileCpuInfo, R_OK);
+ BPF_ASSERT_EQ(can_access, 0);
+ can_access = syscaller->Access(kFileCpuInfo, W_OK);
+ BPF_ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+ // Check we can not write /proc/cpuinfo.
+
+ // Open cpuinfo via the broker.
+ int cpuinfo_fd = syscaller->Open(kFileCpuInfo, O_RDONLY);
+ base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
+ BPF_ASSERT_GE(cpuinfo_fd, 0);
+ char buf[3];
+ memset(buf, 0, sizeof(buf));
+ int read_len_sandboxed = HANDLE_EINTR(read(cpuinfo_fd, buf, sizeof(buf)));
+ BPF_ASSERT_GT(read_len_sandboxed, 0);
+
+ // The following is not guaranteed true, but will be in practice.
+ BPF_ASSERT_EQ(read_len_sandboxed, read_len_unsandboxed_);
+ // Compare the cpuinfo as returned by the broker with the one we opened
+ // ourselves.
+ BPF_ASSERT_EQ(memcmp(buf, cpuinfo_buf_, read_len_sandboxed), 0);
+ }
+
+ private:
+ const char* const kFileCpuInfo = "/proc/cpuinfo";
+ const char* const kDirProc = "/proc/";
+
+ int read_len_unsandboxed_ = -1;
+ char cpuinfo_buf_[3];
+};
+
+TEST(BrokerProcessIntegrationTest, OpenCpuinfoRecursive) {
+ RunAllBrokerTests<OpenCpuinfoDelegate</*recursive=*/true>>();
+}
+
+TEST(BrokerProcessIntegrationTest, OpenCpuinfoNonRecursive) {
+ RunAllBrokerTests<OpenCpuinfoDelegate</*recursive=*/false>>();
+}
+
+class OpenFileRWDelegate final : public BrokerTestDelegate {
+ public:
+ OpenFileRWDelegate() : tempfile_name_(tempfile.full_file_name()) {}
+
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_ACCESS, syscall_broker::COMMAND_OPEN});
+ params.permissions = {BrokerFilePermission::ReadWrite(tempfile_name_)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ // Check we can access that file with read or write.
+ int can_access = syscaller->Access(tempfile_name_, R_OK | W_OK);
+ BPF_ASSERT_EQ(can_access, 0);
+
+ int tempfile2 = -1;
+ tempfile2 = syscaller->Open(tempfile_name_, O_RDWR);
+ BPF_ASSERT_GE(tempfile2, 0);
+
+ // Write to the descriptor opened by the broker.
+ char test_text[] = "TESTTESTTEST";
+
+ ssize_t len = HANDLE_EINTR(write(tempfile2, test_text, sizeof(test_text)));
+ BPF_ASSERT_EQ(len, static_cast<ssize_t>(sizeof(test_text)));
+
+ // Read back from the original file descriptor what we wrote through
+ // the descriptor provided by the broker.
+ char buf[1024];
+ len = HANDLE_EINTR(read(tempfile.fd(), buf, sizeof(buf)));
+
+ BPF_ASSERT_EQ(len, static_cast<ssize_t>(sizeof(test_text)));
+ BPF_ASSERT_EQ(memcmp(test_text, buf, sizeof(test_text)), 0);
+
+ BPF_ASSERT_EQ(close(tempfile2), 0);
+ }
+
+ private:
+ ScopedTemporaryFile tempfile;
+ const char* const tempfile_name_;
+};
+
+TEST(BrokerProcessIntegrationTest, OpenFileRW) {
+ RunAllBrokerTests<OpenFileRWDelegate>();
+}
+
+class BrokerDiedDelegate final : public BrokerTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_ACCESS, syscall_broker::COMMAND_OPEN});
+ params.permissions = {BrokerFilePermission::ReadOnly(kCpuInfo)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_GT(broker_pid_, 0);
+
+ // Kill our own broker and check that we get ENOSYS or ENOMEM for our
+ // syscalls.
+ BPF_ASSERT_EQ(kill(broker_pid_, SIGKILL), 0);
+
+ int ret = syscaller->Access(kCpuInfo, O_RDONLY);
+ BPF_ASSERT(ret == -ENOSYS || ret == -ENOMEM);
+
+ ret = syscaller->Open(kCpuInfo, O_RDONLY);
+ BPF_ASSERT(ret == -ENOSYS || ret == -ENOMEM);
+ }
+
+ void OnBrokerStarted(pid_t pid) override { broker_pid_ = pid; }
+
+ private:
+ const char* const kCpuInfo = "/proc/cpuinfo";
+
+ pid_t broker_pid_ = -1;
+};
+
+TEST(BrokerProcessIntegrationTest, BrokerDied) {
+ RunAllBrokerTests<BrokerDiedDelegate>();
+}
+
+class OpenComplexFlagsDelegate final : public BrokerTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_ACCESS, syscall_broker::COMMAND_OPEN});
+ params.permissions = {BrokerFilePermission::ReadOnly(kCpuInfo)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ // Tests that files opened without O_CLOEXEC (resp. O_NONBLOCK) do not have
+ // O_CLOEXEC (resp. O_NONBLOCK) on their file description, and vice versa.
+ int fd = -1;
+ int ret = 0;
+ fd = syscaller->Open(kCpuInfo, O_RDONLY);
+ BPF_ASSERT_GE(fd, 0);
+ ret = fcntl(fd, F_GETFL);
+ BPF_ASSERT_NE(-1, ret);
+ // The description shouldn't have the O_CLOEXEC attribute, nor O_NONBLOCK.
+ BPF_ASSERT_EQ(0, ret & (O_CLOEXEC | O_NONBLOCK));
+ ret = fcntl(fd, F_GETFD);
+ BPF_ASSERT_NE(-1, ret);
+ // The descriptor also should not have FD_CLOEXEC.
+ BPF_ASSERT_EQ(FD_CLOEXEC & ret, 0);
+ BPF_ASSERT_EQ(0, close(fd));
+
+ fd = syscaller->Open(kCpuInfo, O_RDONLY | O_CLOEXEC);
+ BPF_ASSERT_GE(fd, 0);
+ ret = fcntl(fd, F_GETFD);
+ BPF_ASSERT_NE(-1, ret);
+ // Important: use F_GETFD, not F_GETFL. The O_CLOEXEC flag in F_GETFL
+ // is actually not used by the kernel.
+ BPF_ASSERT(FD_CLOEXEC & ret);
+ BPF_ASSERT_EQ(0, close(fd));
+
+ fd = syscaller->Open(kCpuInfo, O_RDONLY | O_NONBLOCK);
+ BPF_ASSERT_GE(fd, 0);
+ ret = fcntl(fd, F_GETFL);
+ BPF_ASSERT_NE(-1, ret);
+ BPF_ASSERT(O_NONBLOCK & ret);
+ BPF_ASSERT_EQ(0, close(fd));
+ }
+
+ private:
+ const char* const kCpuInfo = "/proc/cpuinfo";
+};
+
+TEST(BrokerProcessIntegrationTest, OpenComplexFlags) {
+ RunAllBrokerTests<OpenComplexFlagsDelegate>();
+}
+
+class CreateFileDelegate final : public BrokerTestDelegate {
+ public:
+ void ParentSetUp() override {
+ // Create two temporary files and delete them, but store their file paths
+ // for later usage.
+ ScopedTemporaryFile temp_file;
+ ScopedTemporaryFile perm_file;
+ temp_str_ = temp_file.full_file_name();
+ perm_str_ = perm_file.full_file_name();
+ tempfile_name_ = temp_str_.c_str();
+ permfile_name_ = perm_str_.c_str();
+
+ {
+ ScopedTemporaryFile tempfile;
+ existing_temp_file_str_ = tempfile.full_file_name();
+ }
+ // Create a conflict for the temp filename.
+ base::ScopedFD fd(
+ open(existing_temp_file_str_.c_str(), O_RDWR | O_CREAT, 0600));
+ BPF_ASSERT_GE(fd.get(), 0);
+ }
+
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_ACCESS, syscall_broker::COMMAND_OPEN});
+ // Note that "temporariness" is determined by the permissions given below.
+ params.permissions = {
+ BrokerFilePermission::ReadWriteCreateTemporary(existing_temp_file_str_),
+ BrokerFilePermission::ReadWriteCreateTemporary(tempfile_name_),
+ BrokerFilePermission::ReadWriteCreate(permfile_name_),
+ };
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ int fd = -1;
+
+ // Opening a temp file using O_CREAT but not O_EXCL must not be allowed
+ // by the broker so as to prevent spying on any pre-existing files.
+ fd = syscaller->Open(tempfile_name_, O_RDWR | O_CREAT);
+ BPF_ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ // Opening a temp file in a normal way must not be allowed by the broker,
+ // either.
+ fd = syscaller->Open(tempfile_name_, O_RDWR);
+ BPF_ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ // Opening a temp file with both O_CREAT and O_EXCL is allowed since the
+ // file is known not to exist outside the scope of ScopedTemporaryFile.
+ fd = syscaller->Open(tempfile_name_, O_RDWR | O_CREAT | O_EXCL);
+ BPF_ASSERT_GE(fd, 0);
+ close(fd);
+
+ // Opening a temp file with both O_CREAT and O_EXCL is allowed but fails
+ // per the OS when there is a conflict with a pre-existing file.
+ fd = syscaller->Open(existing_temp_file_str_.c_str(),
+ O_RDWR | O_CREAT | O_EXCL);
+ BPF_ASSERT_EQ(fd, -EEXIST);
+
+ // Opening a new permanent file without specifying O_EXCL is allowed.
+ fd = syscaller->Open(permfile_name_, O_RDWR | O_CREAT);
+ BPF_ASSERT_GE(fd, 0);
+ close(fd);
+
+ // Opening an existing permanent file without specifying O_EXCL is allowed.
+ fd = syscaller->Open(permfile_name_, O_RDWR | O_CREAT);
+ BPF_ASSERT_GE(fd, 0);
+ close(fd);
+
+ // Opening an existing file with O_EXCL is allowed but fails per the OS.
+ fd = syscaller->Open(permfile_name_, O_RDWR | O_CREAT | O_EXCL);
+ BPF_ASSERT_EQ(fd, -EEXIST);
+
+ const char kTestText[] = "TESTTESTTEST";
+
+ fd = syscaller->Open(permfile_name_, O_RDWR);
+ BPF_ASSERT_GE(fd, 0);
+ {
+ // Write to the descriptor opened by the broker and close.
+ base::ScopedFD scoped_fd(fd);
+ ssize_t len = HANDLE_EINTR(write(fd, kTestText, sizeof(kTestText)));
+ BPF_ASSERT_EQ(len, static_cast<ssize_t>(sizeof(kTestText)));
+ }
+
+ int fd_check = open(permfile_name_, O_RDONLY);
+ BPF_ASSERT_GE(fd_check, 0);
+ {
+ base::ScopedFD scoped_fd(fd_check);
+ char buf[1024];
+ ssize_t len = HANDLE_EINTR(read(fd_check, buf, sizeof(buf)));
+ BPF_ASSERT_EQ(len, static_cast<ssize_t>(sizeof(kTestText)));
+ BPF_ASSERT_EQ(memcmp(kTestText, buf, sizeof(kTestText)), 0);
+ }
+ }
+
+ void ParentTearDown() override {
+ // Cleanup.
+ unlink(existing_temp_file_str_.c_str());
+ unlink(tempfile_name_);
+ unlink(permfile_name_);
+ BrokerTestDelegate::ParentTearDown();
+ }
+
+ private:
+ std::string existing_temp_file_str_;
+ std::string temp_str_;
+ std::string perm_str_;
+ const char* tempfile_name_;
+ const char* permfile_name_;
+};
+
+TEST(BrokerProcessIntegrationTest, CreateFile) {
+ RunAllBrokerTests<CreateFileDelegate>();
+}
+
+// StatFileDelegate is the base class for all the Stat() tests.
+class StatFileDelegate : public BrokerTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BPF_ASSERT_EQ(12, HANDLE_EINTR(write(tmp_file_.fd(), "blahblahblah", 12)));
+ memset(&sb_, 0, sizeof(sb_));
+ return BrokerParams();
+ }
+
+ protected:
+ ScopedTemporaryFile tmp_file_;
+
+ std::string temp_str_ = tmp_file_.full_file_name();
+ const char* const tempfile_name_ = temp_str_.c_str();
+
+ const char* const nonesuch_name = "/mbogo/fictitious/nonesuch";
+ const char* const leading_path1 = "/mbogo/fictitious";
+ const char* const leading_path2 = "/mbogo";
+ const char* const leading_path3 = "/";
+ const char* const bad_leading_path1 = "/mbog";
+ const char* const bad_leading_path2 = "/mboga";
+ const char* const bad_leading_path3 = "/mbogos";
+ const char* const bad_leading_path4 = "/mbogo/fictitiou";
+ const char* const bad_leading_path5 = "/mbogo/fictitioux";
+ const char* const bad_leading_path6 = "/mbogo/fictitiousa";
+
+ struct stat sb_;
+};
+
+// Actual file with permissions to see file but command not allowed.
+template <bool follow_links>
+class StatFileNoCommandDelegate final : public StatFileDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params = StatFileDelegate::ChildSetUpPreSandbox();
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet({});
+ params.permissions = {BrokerFilePermission::ReadOnly(tempfile_name_)};
+ return params;
+ }
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ int ret = syscaller->Stat(tempfile_name_, follow_links, &sb_);
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, ret);
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, StatFileNoCommandFollowLinks) {
+ RunAllBrokerTests<StatFileNoCommandDelegate<true>>();
+}
+
+TEST(BrokerProcessIntegrationTest, StatFileNoCommandNoFollowLinks) {
+ RunAllBrokerTests<StatFileNoCommandDelegate<false>>();
+}
+
+// Allows the STAT command without any file permissions.
+template <bool follow_links>
+class StatFilesNoPermissionDelegate final : public StatFileDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params = StatFileDelegate::ChildSetUpPreSandbox();
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_STAT});
+ params.permissions = {};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ // Nonexistent file with no permissions to see file.
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(nonesuch_name, follow_links, &sb_));
+ // Actual file with no permission to see file.
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(tempfile_name_, follow_links, &sb_));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, StatFilesNoPermissionFollowLinks) {
+ RunAllBrokerTests<StatFilesNoPermissionDelegate<true>>();
+}
+
+TEST(BrokerProcessIntegrationTest, StatFilesNoPermissionNoFollowLinks) {
+ RunAllBrokerTests<StatFilesNoPermissionDelegate<false>>();
+}
+
+// Nonexistent file with permissions to see file.
+template <bool follow_links>
+class StatNonexistentFileWithPermissionsDelegate final
+ : public StatFileDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params = StatFileDelegate::ChildSetUpPreSandbox();
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_STAT});
+ params.permissions = {BrokerFilePermission::ReadOnly(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-ENOENT, syscaller->Stat(nonesuch_name, follow_links, &sb_));
+
+ // Gets denied all the way back to root since no create permission.
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(leading_path1, follow_links, &sb_));
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(leading_path2, follow_links, &sb_));
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(leading_path3, follow_links, &sb_));
+
+ // Not fooled by substrings.
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(bad_leading_path1, follow_links, &sb_));
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(bad_leading_path2, follow_links, &sb_));
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(bad_leading_path3, follow_links, &sb_));
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(bad_leading_path4, follow_links, &sb_));
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(bad_leading_path5, follow_links, &sb_));
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(bad_leading_path6, follow_links, &sb_));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest,
+ StatNonexistentFileWithPermissionsFollowLinks) {
+ RunAllBrokerTests<StatNonexistentFileWithPermissionsDelegate<true>>();
+}
+
+TEST(BrokerProcessIntegrationTest,
+ StatNonexistentFileWithPermissionsNoFollowLinks) {
+ RunAllBrokerTests<StatNonexistentFileWithPermissionsDelegate<false>>();
+}
+
+// Nonexistent file with permissions to create file.
+template <bool follow_links>
+class StatNonexistentFileWithCreatePermissionsDelegate final
+ : public StatFileDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params = StatFileDelegate::ChildSetUpPreSandbox();
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_STAT});
+ params.permissions = {BrokerFilePermission::ReadWriteCreate(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-ENOENT, syscaller->Stat(nonesuch_name, follow_links, &sb_));
+
+ // Gets ENOENT all the way back to root since it has create permission.
+ BPF_ASSERT_EQ(-ENOENT, syscaller->Stat(leading_path1, follow_links, &sb_));
+ BPF_ASSERT_EQ(-ENOENT, syscaller->Stat(leading_path2, follow_links, &sb_));
+
+ // But can always get the root.
+ BPF_ASSERT_EQ(0, syscaller->Stat(leading_path3, follow_links, &sb_));
+
+ // Not fooled by substrings.
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(bad_leading_path1, follow_links, &sb_));
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(bad_leading_path2, follow_links, &sb_));
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(bad_leading_path3, follow_links, &sb_));
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(bad_leading_path4, follow_links, &sb_));
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(bad_leading_path5, follow_links, &sb_));
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Stat(bad_leading_path6, follow_links, &sb_));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest,
+ StatNonexistentFileWithCreatePermissionsFollowLinks) {
+ RunAllBrokerTests<StatNonexistentFileWithCreatePermissionsDelegate<true>>();
+}
+
+TEST(BrokerProcessIntegrationTest,
+ StatNonexistentFileWithCreatePermissionsNoFollowLinks) {
+ RunAllBrokerTests<StatNonexistentFileWithCreatePermissionsDelegate<false>>();
+}
+
+// Actual file with permissions to see file.
+template <bool follow_links>
+class StatFileWithPermissionsDelegate final : public StatFileDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params = StatFileDelegate::ChildSetUpPreSandbox();
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_STAT});
+ params.permissions = {BrokerFilePermission::ReadOnly(tempfile_name_)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(0, syscaller->Stat(tempfile_name_, follow_links, &sb_));
+
+ // Following fields may never be consistent but should be non-zero.
+ // Don't trust the platform to define fields with any particular
+ // sign.
+ BPF_ASSERT_NE(0u, static_cast<unsigned int>(sb_.st_dev));
+ BPF_ASSERT_NE(0u, static_cast<unsigned int>(sb_.st_ino));
+ BPF_ASSERT_NE(0u, static_cast<unsigned int>(sb_.st_mode));
+ BPF_ASSERT_NE(0u, static_cast<unsigned int>(sb_.st_blksize));
+ BPF_ASSERT_NE(0u, static_cast<unsigned int>(sb_.st_blocks));
+
+ // We are the ones that made the file.
+ BPF_ASSERT_EQ(geteuid(), sb_.st_uid);
+ BPF_ASSERT_EQ(getegid(), sb_.st_gid);
+
+ // Wrote 12 bytes above which should fit in one block.
+ BPF_ASSERT_EQ(12, sb_.st_size);
+
+ // Can't go backwards in time, 1500000000 was some time ago.
+ BPF_ASSERT_LT(1500000000u, static_cast<unsigned int>(sb_.st_atime));
+ BPF_ASSERT_LT(1500000000u, static_cast<unsigned int>(sb_.st_mtime));
+ BPF_ASSERT_LT(1500000000u, static_cast<unsigned int>(sb_.st_ctime));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, StatFileWithPermissionsFollowLinks) {
+ RunAllBrokerTests<StatFileWithPermissionsDelegate<true>>();
+}
+
+TEST(BrokerProcessIntegrationTest, StatFileWithPermissionsNoFollowLinks) {
+ RunAllBrokerTests<StatFileWithPermissionsDelegate<false>>();
+}
+
+class RenameTestDelegate : public BrokerTestDelegate {
+ public:
+ void ParentSetUp() override {
+ {
+ // Just to generate names and ensure they do not exist upon scope exit.
+ ScopedTemporaryFile oldfile;
+ ScopedTemporaryFile newfile;
+ oldpath_ = oldfile.full_file_name();
+ newpath_ = newfile.full_file_name();
+ }
+
+ // Now make a file using old path name.
+ int fd = open(oldpath_.c_str(), O_RDWR | O_CREAT, 0600);
+ EXPECT_TRUE(fd > 0);
+ close(fd);
+
+ EXPECT_TRUE(access(oldpath_.c_str(), F_OK) == 0);
+ EXPECT_TRUE(access(newpath_.c_str(), F_OK) < 0);
+ }
+
+ void ParentTearDown() override {
+ unlink(oldpath_.c_str());
+ unlink(newpath_.c_str());
+ BrokerTestDelegate::ParentTearDown();
+ }
+
+ protected:
+ std::string oldpath_;
+ std::string newpath_;
+};
+
+// Check rename fails with write permissions to both files but command
+// itself is not allowed.
+class RenameNoCommandDelegate final : public RenameTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet({});
+ params.permissions = {BrokerFilePermission::ReadWriteCreate(oldpath_),
+ BrokerFilePermission::ReadWriteCreate(newpath_)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Rename(oldpath_.c_str(), newpath_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ if (true) {
+ EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0);
+ EXPECT_TRUE(access(newpath_.c_str(), 0) < 0);
+ } else {
+ EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0);
+ EXPECT_TRUE(access(newpath_.c_str(), 0) == 0);
+ }
+ RenameTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RenameNoCommand) {
+ RunAllBrokerTests<RenameNoCommandDelegate>();
+}
+// Check rename fails when no permission to new file.
+class RenameNoPermNewDelegate final : public RenameTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RENAME});
+ params.permissions = {BrokerFilePermission::ReadWriteCreate(newpath_)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Rename(oldpath_.c_str(), newpath_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ if (true) {
+ EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0);
+ EXPECT_TRUE(access(newpath_.c_str(), 0) < 0);
+ } else {
+ EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0);
+ EXPECT_TRUE(access(newpath_.c_str(), 0) == 0);
+ }
+ RenameTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RenameNoPermNew) {
+ RunAllBrokerTests<RenameNoPermNewDelegate>();
+}
+// Check rename fails when no permission to old file.
+class RenameNoPermOldDelegate final : public RenameTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RENAME});
+ params.permissions = {BrokerFilePermission::ReadWriteCreate(oldpath_)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Rename(oldpath_.c_str(), newpath_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ if (true) {
+ EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0);
+ EXPECT_TRUE(access(newpath_.c_str(), 0) < 0);
+ } else {
+ EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0);
+ EXPECT_TRUE(access(newpath_.c_str(), 0) == 0);
+ }
+ RenameTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RenameNoPermOld) {
+ RunAllBrokerTests<RenameNoPermOldDelegate>();
+}
+
+// Check rename fails when only read permission to first file.
+class RenameReadPermNewDelegate final : public RenameTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RENAME});
+ params.permissions = {BrokerFilePermission::ReadWriteCreate(oldpath_),
+ BrokerFilePermission::ReadOnly(newpath_)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Rename(oldpath_.c_str(), newpath_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ if (true) {
+ EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0);
+ EXPECT_TRUE(access(newpath_.c_str(), 0) < 0);
+ } else {
+ EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0);
+ EXPECT_TRUE(access(newpath_.c_str(), 0) == 0);
+ }
+ RenameTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RenameReadPermNew) {
+ RunAllBrokerTests<RenameReadPermNewDelegate>();
+}
+
+// Check rename fails when only read permission to second file.
+class RenameReadPermOldDelegate final : public RenameTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RENAME});
+ params.permissions = {BrokerFilePermission::ReadOnly(oldpath_),
+ BrokerFilePermission::ReadWriteCreate(newpath_)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Rename(oldpath_.c_str(), newpath_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ if (true) {
+ EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0);
+ EXPECT_TRUE(access(newpath_.c_str(), 0) < 0);
+ } else {
+ EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0);
+ EXPECT_TRUE(access(newpath_.c_str(), 0) == 0);
+ }
+ RenameTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RenameReadPermOld) {
+ RunAllBrokerTests<RenameReadPermOldDelegate>();
+}
+
+// Check rename passes with write permissions to both files.
+class RenameWritePermsBothDelegate final : public RenameTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RENAME});
+ params.permissions = {BrokerFilePermission::ReadWriteCreate(oldpath_),
+ BrokerFilePermission::ReadWriteCreate(newpath_)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(0, syscaller->Rename(oldpath_.c_str(), newpath_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ if (false) {
+ EXPECT_TRUE(access(oldpath_.c_str(), 0) == 0);
+ EXPECT_TRUE(access(newpath_.c_str(), 0) < 0);
+ } else {
+ EXPECT_TRUE(access(oldpath_.c_str(), 0) < 0);
+ EXPECT_TRUE(access(newpath_.c_str(), 0) == 0);
+ }
+ RenameTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RenameWritePermsBoth) {
+ RunAllBrokerTests<RenameWritePermsBothDelegate>();
+}
+
+// The base class of all the Readlink() tests.
+class ReadlinkTestDelegate : public BrokerTestDelegate {
+ public:
+ void ParentSetUp() override {
+ {
+ // Just to generate names and ensure they do not exist upon scope exit.
+ ScopedTemporaryFile oldfile;
+ ScopedTemporaryFile newfile;
+ oldpath_ = oldfile.full_file_name();
+ newpath_ = newfile.full_file_name();
+ }
+
+ // Now make a link from old to new path name.
+ EXPECT_TRUE(symlink(oldpath_.c_str(), newpath_.c_str()) == 0);
+ }
+
+ void ParentTearDown() override {
+ unlink(oldpath_.c_str());
+ unlink(newpath_.c_str());
+ BrokerTestDelegate::ParentTearDown();
+ }
+
+ protected:
+ const char* const nonesuch_name = "/mbogo/nonesuch";
+
+ std::string oldpath_;
+ std::string newpath_;
+
+ char readlink_buf_[1024];
+};
+
+// Actual file with permissions to see file but command itself not allowed.
+class ReadlinkNoCommandDelegate final : public ReadlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet({});
+ params.permissions = {BrokerFilePermission::ReadOnly(newpath_)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override;
+};
+
+TEST(BrokerProcessIntegrationTest, ReadlinkNoCommand) {
+ RunAllBrokerTests<ReadlinkNoCommandDelegate>();
+}
+
+void ReadlinkNoCommandDelegate::RunTestInSandboxedChild(Syscaller* syscaller) {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Readlink(newpath_.c_str(), readlink_buf_,
+ sizeof(readlink_buf_)));
+}
+
+// Nonexistent file with no permissions to see file.
+class ReadlinkNonexistentNoPermissionsDelegate final
+ : public ReadlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_READLINK});
+ params.permissions = {};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Readlink(nonesuch_name, readlink_buf_,
+ sizeof(readlink_buf_)));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, ReadlinkNonexistentNoPermissions) {
+ RunAllBrokerTests<ReadlinkNonexistentNoPermissionsDelegate>();
+}
+
+// Actual file with no permissions to see file.
+class ReadlinkNoPermissionsDelegate final : public ReadlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_READLINK});
+ params.permissions = {};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel,
+ syscaller->Readlink(newpath_.c_str(), readlink_buf_,
+ sizeof(readlink_buf_)));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, ReadlinkNoPermissions) {
+ RunAllBrokerTests<ReadlinkNoPermissionsDelegate>();
+}
+
+// Nonexistent file with permissions to see file.
+class ReadlinkNonexistentWithPermissionsDelegate final
+ : public ReadlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_READLINK});
+ params.permissions = {BrokerFilePermission::ReadOnly(nonesuch_name)};
+ return params;
+ }
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-ENOENT, syscaller->Readlink(nonesuch_name, readlink_buf_,
+ sizeof(readlink_buf_)));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, ReadlinkNonexistentWithPermissions) {
+ RunAllBrokerTests<ReadlinkNonexistentWithPermissionsDelegate>();
+}
+
+// Actual file with permissions to see file.
+class ReadlinkFileWithPermissionsDelegate final : public ReadlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_READLINK});
+ params.permissions = {BrokerFilePermission::ReadOnly(newpath_)};
+ return params;
+ }
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ ssize_t retlen = syscaller->Readlink(newpath_.c_str(), readlink_buf_,
+ sizeof(readlink_buf_));
+ BPF_ASSERT(retlen == static_cast<ssize_t>(oldpath_.length()));
+ BPF_ASSERT_EQ(0, memcmp(oldpath_.c_str(), readlink_buf_, retlen));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, ReadlinkFileWithPermissions) {
+ RunAllBrokerTests<ReadlinkFileWithPermissionsDelegate>();
+}
+
+// Actual file with permissions to see file, but too small a buffer.
+class ReadlinkFileWithPermissionsSmallBufferDelegate final
+ : public ReadlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet(
+ {syscall_broker::COMMAND_READLINK});
+ params.permissions = {BrokerFilePermission::ReadOnly(newpath_)};
+ return params;
+ }
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(4, syscaller->Readlink(newpath_.c_str(), readlink_buf_, 4));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, ReadlinkFileWithPermissionsSmallBuffer) {
+ RunAllBrokerTests<ReadlinkFileWithPermissionsSmallBufferDelegate>();
+}
+
+// The base class of all the Mkdir() tests.
+class MkdirTestDelegate : public BrokerTestDelegate {
+ public:
+ void ParentSetUp() override {
+ ScopedTemporaryFile file;
+ path_ = file.full_file_name();
+ }
+
+ void ParentTearDown() override {
+ rmdir(path_.c_str());
+ BrokerTestDelegate::ParentTearDown();
+ }
+
+ protected:
+ const char* nonesuch_name = "/mbogo/nonesuch";
+
+ std::string path_;
+};
+
+// Actual file with permissions to use but command itself not allowed.
+class MkdirNoCommandDelegate final : public MkdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet({});
+ params.permissions = {BrokerFilePermission::ReadWrite(path_),
+ BrokerFilePermission::ReadWrite(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Mkdir(path_.c_str(), 0600));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, MkdirNoCommand) {
+ RunAllBrokerTests<MkdirNoCommandDelegate>();
+}
+
+// Nonexistent file with no permissions to see file.
+class MkdirNonexistentNoPermissionsDelegate final : public MkdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_MKDIR});
+ params.permissions = {};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Mkdir(nonesuch_name, 0600));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, MkdirNonexistentNoPermissions) {
+ RunAllBrokerTests<MkdirNonexistentNoPermissionsDelegate>();
+}
+
+// Actual file with no permissions to see file.
+class MkdirFileNoPermissionsDelegate final : public MkdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_MKDIR});
+ params.permissions = {};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Mkdir(path_.c_str(), 0600));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, MkdirFileNoPermissions) {
+ RunAllBrokerTests<MkdirFileNoPermissionsDelegate>();
+}
+
+// Nonexistent file with insufficient permissions to see file.
+class MkdirNonexistentROPermissionsDelegate final : public MkdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_MKDIR});
+ params.permissions = {BrokerFilePermission::ReadOnly(path_),
+ BrokerFilePermission::ReadOnly(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Mkdir(nonesuch_name, 0600));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, MkdirNonexistentROPermissions) {
+ RunAllBrokerTests<MkdirNonexistentROPermissionsDelegate>();
+}
+
+// Actual file with insufficient permissions to see file.
+class MkdirFileROPermissionsDelegate final : public MkdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_MKDIR});
+ params.permissions = {BrokerFilePermission::ReadOnly(path_),
+ BrokerFilePermission::ReadOnly(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Mkdir(path_.c_str(), 0600));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, MkdirFileROPermissions) {
+ RunAllBrokerTests<MkdirFileROPermissionsDelegate>();
+}
+
+// Nonexistent file with insufficient permissions to see file, case 2.
+class MkdirNonExistentRWPermissionsDelegate final : public MkdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_MKDIR});
+ params.permissions = {BrokerFilePermission::ReadWrite(path_),
+ BrokerFilePermission::ReadWrite(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Mkdir(nonesuch_name, 0600));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, MkdirNonExistentRWPermissions) {
+ RunAllBrokerTests<MkdirNonExistentRWPermissionsDelegate>();
+}
+
+// Actual file with insufficient permissions to see file, case 2.
+class MkdirFileRWPermissionsDelegate final : public MkdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_MKDIR});
+ params.permissions = {BrokerFilePermission::ReadWrite(path_),
+ BrokerFilePermission::ReadWrite(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Mkdir(path_.c_str(), 0600));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, MkdirFileRWPermissions) {
+ RunAllBrokerTests<MkdirFileRWPermissionsDelegate>();
+}
+
+// Nonexistent file with permissions to see file.
+class MkdirNonExistentRWCPermissionsDelegate final : public MkdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_MKDIR});
+ params.permissions = {BrokerFilePermission::ReadWriteCreate(path_),
+ BrokerFilePermission::ReadWriteCreate(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-2, syscaller->Mkdir(nonesuch_name, 0600));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, MkdirNonExistentRWCPermissions) {
+ RunAllBrokerTests<MkdirNonExistentRWCPermissionsDelegate>();
+}
+
+// Actual file with permissions to see file.
+class MkdirFileRWCPermissionsDelegate final : public MkdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_MKDIR});
+ params.permissions = {BrokerFilePermission::ReadWriteCreate(path_),
+ BrokerFilePermission::ReadWriteCreate(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(0, syscaller->Mkdir(path_.c_str(), 0600));
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, MkdirFileRWCPermissions) {
+ RunAllBrokerTests<MkdirFileRWCPermissionsDelegate>();
+}
+
+class RmdirTestDelegate : public BrokerTestDelegate {
+ public:
+ void ParentSetUp() override {
+ {
+ // Generate name and ensure it does not exist upon scope exit.
+ ScopedTemporaryFile file;
+ path_ = file.full_file_name();
+ }
+
+ const char* const path_name = path_.c_str();
+
+ EXPECT_EQ(0, mkdir(path_name, 0600));
+ EXPECT_EQ(0, access(path_name, F_OK));
+ }
+
+ void ParentTearDown() override {
+ rmdir(path_.c_str());
+ BrokerTestDelegate::ParentTearDown();
+ }
+
+ protected:
+ const char* const nonesuch_name = "/mbogo/nonesuch";
+
+ std::string path_;
+};
+
+// Actual file with permissions to use but command itself not allowed.
+class RmdirNoCommandDelegate final : public RmdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet({});
+ params.permissions = {BrokerFilePermission::ReadWrite(path_),
+ BrokerFilePermission::ReadWrite(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Rmdir(path_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ RmdirTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RmdirNoCommand) {
+ RunAllBrokerTests<RmdirNoCommandDelegate>();
+}
+
+// Nonexistent file with no permissions to see file.
+class RmdirNonexistentNoPermissionsDelegate final : public RmdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RMDIR});
+ params.permissions = {};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Rmdir(nonesuch_name));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ RmdirTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RmdirNonexistentNoPermissions) {
+ RunAllBrokerTests<RmdirNonexistentNoPermissionsDelegate>();
+}
+
+// Actual file with no permissions to see file.
+class RmdirFileNoPermissionsDelegate final : public RmdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RMDIR});
+ params.permissions = {};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Rmdir(path_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ RmdirTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RmdirFileNoPermissions) {
+ RunAllBrokerTests<RmdirFileNoPermissionsDelegate>();
+}
+
+// Nonexistent file with insufficient permissions to see file.
+class RmdirNonexistentROPermissionsDelegate final : public RmdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RMDIR});
+ params.permissions = {BrokerFilePermission::ReadOnly(path_),
+ BrokerFilePermission::ReadOnly(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Rmdir(nonesuch_name));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ RmdirTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RmdirNonexistentROPermissions) {
+ RunAllBrokerTests<RmdirNonexistentROPermissionsDelegate>();
+}
+
+// Actual file with insufficient permissions to see file.
+class RmdirFileROPermissionsDelegate final : public RmdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RMDIR});
+ params.permissions = {BrokerFilePermission::ReadOnly(path_),
+ BrokerFilePermission::ReadOnly(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Rmdir(path_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ RmdirTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RmdirFileROPermissions) {
+ RunAllBrokerTests<RmdirFileROPermissionsDelegate>();
+}
+
+// Nonexistent file with insufficient permissions to see file, case 2.
+class RmdirNonExistentRWPermissionsDelegate final : public RmdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RMDIR});
+ params.permissions = {BrokerFilePermission::ReadWrite(path_),
+ BrokerFilePermission::ReadWrite(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Rmdir(nonesuch_name));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ RmdirTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RmdirNonExistentRWPermissions) {
+ RunAllBrokerTests<RmdirNonExistentRWPermissionsDelegate>();
+}
+
+// Actual file with insufficient permissions to see file, case 2.
+class RmdirFileRWPermissionsDelegate final : public RmdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RMDIR});
+ params.permissions = {BrokerFilePermission::ReadWrite(path_),
+ BrokerFilePermission::ReadWrite(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Rmdir(path_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ RmdirTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RmdirFileRWPermissions) {
+ RunAllBrokerTests<RmdirFileRWPermissionsDelegate>();
+}
+
+// Nonexistent file with permissions to see file.
+class RmdirNonExistentRWCPermissionsDelegate final : public RmdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RMDIR});
+ params.permissions = {BrokerFilePermission::ReadWriteCreate(path_),
+ BrokerFilePermission::ReadWriteCreate(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-2, syscaller->Rmdir(nonesuch_name));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ RmdirTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RmdirNonExistentRWCPermissions) {
+ RunAllBrokerTests<RmdirNonExistentRWCPermissionsDelegate>();
+}
+
+// Actual file with permissions to see file.
+class RmdirFileRWCPermissionsDelegate final : public RmdirTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_RMDIR});
+ params.permissions = {BrokerFilePermission::ReadWriteCreate(path_),
+ BrokerFilePermission::ReadWriteCreate(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(0, syscaller->Rmdir(path_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(-1, access(path_.c_str(), 0));
+ RmdirTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, RmdirFileRWCPermissions) {
+ RunAllBrokerTests<RmdirFileRWCPermissionsDelegate>();
+}
+
+// The base class for all the Unlink() tests.
+class UnlinkTestDelegate : public BrokerTestDelegate {
+ public:
+ void ParentSetUp() override {
+ {
+ // Generate name and ensure it does not exist upon scope exit.
+ ScopedTemporaryFile file;
+ path_ = file.full_file_name();
+ }
+
+ int fd = open(path_.c_str(), O_RDWR | O_CREAT, 0600);
+ EXPECT_TRUE(fd >= 0);
+ close(fd);
+ EXPECT_EQ(0, access(path_.c_str(), F_OK));
+ }
+
+ void ParentTearDown() override {
+ unlink(path_.c_str());
+ BrokerTestDelegate::ParentTearDown();
+ }
+
+ protected:
+ const char* const nonesuch_name = "/mbogo/nonesuch";
+
+ std::string path_;
+};
+
+// Actual file with permissions to use but command itself not allowed.
+class UnlinkNoCommandDelegate final : public UnlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set = syscall_broker::MakeBrokerCommandSet({});
+ params.permissions = {BrokerFilePermission::ReadWrite(path_),
+ BrokerFilePermission::ReadWrite(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Unlink(path_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ UnlinkTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, UnlinkNoCommand) {
+ RunAllBrokerTests<UnlinkNoCommandDelegate>();
+}
+
+// Nonexistent file with no permissions to see file.
+class UnlinkNonexistentNoPermissionsDelegate final : public UnlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_UNLINK});
+ params.permissions = {};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Unlink(nonesuch_name));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ UnlinkTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, UnlinkNonexistentNoPermissions) {
+ RunAllBrokerTests<UnlinkNonexistentNoPermissionsDelegate>();
+}
+
+// Actual file with no permissions to see file.
+class UnlinkFileNoPermissionsDelegate final : public UnlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_UNLINK});
+ params.permissions = {};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Unlink(path_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ UnlinkTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, UnlinkFileNoPermissions) {
+ RunAllBrokerTests<UnlinkFileNoPermissionsDelegate>();
+}
+
+// Nonexistent file with insufficient permissions to see file.
+class UnlinkNonexistentROPermissionsDelegate final : public UnlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_UNLINK});
+ params.permissions = {BrokerFilePermission::ReadOnly(path_),
+ BrokerFilePermission::ReadOnly(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Unlink(nonesuch_name));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ UnlinkTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, UnlinkNonexistentROPermissions) {
+ RunAllBrokerTests<UnlinkNonexistentROPermissionsDelegate>();
+}
+
+// Actual file with insufficient permissions to see file.
+class UnlinkFileROPermissionsDelegate final : public UnlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_UNLINK});
+ params.permissions = {BrokerFilePermission::ReadOnly(path_),
+ BrokerFilePermission::ReadOnly(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Unlink(path_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ UnlinkTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, UnlinkFileROPermissions) {
+ RunAllBrokerTests<UnlinkFileROPermissionsDelegate>();
+}
+
+// Nonexistent file with insufficient permissions to see file, case 2.
+class UnlinkNonExistentRWPermissionsDelegate final : public UnlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_UNLINK});
+ params.permissions = {BrokerFilePermission::ReadWrite(path_),
+ BrokerFilePermission::ReadWrite(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Unlink(nonesuch_name));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ UnlinkTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, UnlinkNonExistentRWPermissions) {
+ RunAllBrokerTests<UnlinkNonExistentRWPermissionsDelegate>();
+}
+
+// Actual file with insufficient permissions to see file, case 2.
+class UnlinkFileRWPermissionsDelegate final : public UnlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_UNLINK});
+ params.permissions = {BrokerFilePermission::ReadWrite(path_),
+ BrokerFilePermission::ReadWrite(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-kFakeErrnoSentinel, syscaller->Unlink(path_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ UnlinkTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, UnlinkFileRWPermissions) {
+ RunAllBrokerTests<UnlinkFileRWPermissionsDelegate>();
+}
+
+// Nonexistent file with permissions to see file.
+class UnlinkNonExistentRWCPermissionsDelegate final
+ : public UnlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_UNLINK});
+ params.permissions = {BrokerFilePermission::ReadWriteCreate(path_),
+ BrokerFilePermission::ReadWriteCreate(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(-2, syscaller->Unlink(nonesuch_name));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(0, access(path_.c_str(), 0));
+ UnlinkTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, UnlinkNonExistentRWCPermissions) {
+ RunAllBrokerTests<UnlinkNonExistentRWCPermissionsDelegate>();
+}
+
+// Actual file with permissions to see file.
+class UnlinkFileRWCPermissionsDelegate final : public UnlinkTestDelegate {
+ public:
+ BrokerParams ChildSetUpPreSandbox() override {
+ BrokerParams params;
+ params.allowed_command_set =
+ syscall_broker::MakeBrokerCommandSet({syscall_broker::COMMAND_UNLINK});
+ params.permissions = {BrokerFilePermission::ReadWriteCreate(path_),
+ BrokerFilePermission::ReadWriteCreate(nonesuch_name)};
+ return params;
+ }
+
+ void RunTestInSandboxedChild(Syscaller* syscaller) override {
+ BPF_ASSERT_EQ(0, syscaller->Unlink(path_.c_str()));
+ }
+
+ void ParentTearDown() override {
+ EXPECT_EQ(-1, access(path_.c_str(), 0));
+ UnlinkTestDelegate::ParentTearDown();
+ }
+};
+
+TEST(BrokerProcessIntegrationTest, UnlinkFileRWCPermissions) {
+ RunAllBrokerTests<UnlinkFileRWCPermissionsDelegate>();
+}
+
+#endif // !defined(THREAD_SANITIZER)
} // namespace sandbox
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
index 222158c643c..35ab90e3f3c 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
@@ -112,9 +112,11 @@ ResultExpr BaselinePolicyAndroid::EvaluateSyscall(int sysno) const {
case __NR_openat:
case __NR_pwrite64:
case __NR_rt_sigtimedwait:
- // sched_setaffinity() is required for an experiment to schedule all
- // Chromium threads onto LITTLE cores (crbug.com/1111789). Should be removed
- // or reconsidered once the experiment is complete.
+ // sched_getaffinity() and sched_setaffinity() are required for an
+ // experiment to schedule all Chromium threads onto LITTLE cores
+ // (crbug.com/1111789). Should be removed or reconsidered once
+ // the experiment is complete.
+ case __NR_sched_getaffinity:
case __NR_sched_setaffinity:
case __NR_sched_getparam:
case __NR_sched_getscheduler:
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
index b968c04aa34..76eb32493f5 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
@@ -21,6 +21,7 @@
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
#include "sandbox/linux/seccomp-bpf/syscall.h"
#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"
#if defined(__mips__)
@@ -147,7 +148,7 @@ class NumberToHex {
// Records the syscall number and first four arguments in a crash key, to help
// debug the failure.
-void SetSeccompCrashKey(const struct sandbox::arch_seccomp_data& args) {
+void SetSeccompCrashKey(const struct arch_seccomp_data& args) {
#if !defined(OS_NACL_NONSFI)
NumberToHex<int> nr(args.nr);
NumberToHex<uint64_t> arg1(args.args[0]);
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
index baac3b61447..7a958b93b27 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
@@ -15,43 +15,43 @@
// guaranteed to be async-signal safe.
// See sandbox/linux/seccomp-bpf/trap.h to see how they work.
-namespace sandbox {
-
struct arch_seccomp_data;
+namespace sandbox {
+
// This handler will crash the currently running process. The crashing address
// will be the number of the current system call, extracted from |args|.
// This handler will also print to stderr the number of the crashing syscall.
-SANDBOX_EXPORT intptr_t
- CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux);
+SANDBOX_EXPORT intptr_t CrashSIGSYS_Handler(const arch_seccomp_data& args,
+ void* aux);
// The following seven handlers are suitable to report failures for specific
// system calls with additional information.
// The crashing address will be (clone_flags & 0xFFFFFF), where clone_flags is
// the clone(2) argument, extracted from |args|.
-SANDBOX_EXPORT intptr_t
- SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux);
+SANDBOX_EXPORT intptr_t SIGSYSCloneFailure(const arch_seccomp_data& args,
+ void* aux);
// The crashing address will be (option & 0xFFF), where option is the prctl(2)
// argument.
-SANDBOX_EXPORT intptr_t
- SIGSYSPrctlFailure(const struct arch_seccomp_data& args, void* aux);
+SANDBOX_EXPORT intptr_t SIGSYSPrctlFailure(const arch_seccomp_data& args,
+ void* aux);
// The crashing address will be request & 0xFFFF, where request is the ioctl(2)
// argument.
-SANDBOX_EXPORT intptr_t
- SIGSYSIoctlFailure(const struct arch_seccomp_data& args, void* aux);
+SANDBOX_EXPORT intptr_t SIGSYSIoctlFailure(const arch_seccomp_data& args,
+ void* aux);
// The crashing address will be (pid & 0xFFF), where pid is the first
// argument (and can be a tid).
-SANDBOX_EXPORT intptr_t
- SIGSYSKillFailure(const struct arch_seccomp_data& args, void* aux);
+SANDBOX_EXPORT intptr_t SIGSYSKillFailure(const arch_seccomp_data& args,
+ void* aux);
// The crashing address will be (op & 0xFFF), where op is the second
// argument.
-SANDBOX_EXPORT intptr_t
- SIGSYSFutexFailure(const struct arch_seccomp_data& args, void* aux);
+SANDBOX_EXPORT intptr_t SIGSYSFutexFailure(const arch_seccomp_data& args,
+ void* aux);
// The crashing address will be (op & 0xFFF), where op is the second
// argument.
-SANDBOX_EXPORT intptr_t
-SIGSYSPtraceFailure(const struct arch_seccomp_data& args, void* aux);
+SANDBOX_EXPORT intptr_t SIGSYSPtraceFailure(const arch_seccomp_data& args,
+ void* aux);
// If the syscall is not being called on the current tid, crashes in the same
// way as CrashSIGSYS_Handler. Otherwise, returns the result of calling the
// syscall with the pid argument set to 0 (which for these calls means the
@@ -60,8 +60,8 @@ SIGSYSPtraceFailure(const struct arch_seccomp_data& args, void* aux);
// sched_getaffinity(), sched_getattr(), sched_getparam(), sched_getscheduler(),
// sched_rr_get_interval(), sched_setaffinity(), sched_setattr(),
// sched_setparam(), sched_setscheduler()
-SANDBOX_EXPORT intptr_t
- SIGSYSSchedHandler(const struct arch_seccomp_data& args, void* aux);
+SANDBOX_EXPORT intptr_t SIGSYSSchedHandler(const arch_seccomp_data& args,
+ void* aux);
// Variants of the above functions for use with bpf_dsl.
SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYS();
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
index f51915edc36..a1002483d78 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -28,6 +28,7 @@
#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_prctl.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"
#include "sandbox/linux/system_headers/linux_time.h"
@@ -51,19 +52,6 @@
#define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6)
#endif
-#if !defined(PR_SET_TIMERSLACK)
-#define PR_SET_TIMERSLACK 29
-#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
-
-#ifndef PR_SET_PTRACER
-#define PR_SET_PTRACER 0x59616d61
-#endif
-
#endif // defined(OS_ANDROID)
#if defined(__arm__) && !defined(MAP_STACK)
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
index 85bbe65748a..3d7bc172f79 100644
--- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -145,7 +145,7 @@ bool SandboxBPF::SupportsSeccompSandbox(SeccompLevel level) {
return false;
}
-bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level, bool enable_ibpb) {
+bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level) {
DCHECK(policy_);
CHECK(seccomp_level == SeccompLevel::SINGLE_THREADED ||
seccomp_level == SeccompLevel::MULTI_THREADED);
@@ -183,7 +183,7 @@ bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level, bool enable_ibpb) {
}
// Install the filters.
- InstallFilter(seccomp_level == SeccompLevel::MULTI_THREADED, enable_ibpb);
+ InstallFilter(seccomp_level == SeccompLevel::MULTI_THREADED);
return true;
}
@@ -222,7 +222,7 @@ CodeGen::Program SandboxBPF::AssembleFilter() {
return compiler.Compile();
}
-void SandboxBPF::InstallFilter(bool must_sync_threads, bool enable_ibpb) {
+void SandboxBPF::InstallFilter(bool must_sync_threads) {
// We want to be very careful in not imposing any requirements on the
// policies that are set with SetSandboxPolicy(). This means, as soon as
// the sandbox is active, we shouldn't be relying on libraries that could
@@ -267,9 +267,7 @@ void SandboxBPF::InstallFilter(bool must_sync_threads, bool enable_ibpb) {
// opt-out SSBD when process is single-threaded and tsync is not necessary.
} else if (KernelSupportSeccompSpecAllow()) {
seccomp_filter_flags |= SECCOMP_FILTER_FLAG_SPEC_ALLOW;
- if (enable_ibpb) {
- DisableIBSpec();
- }
+ DisableIBSpec();
#endif
} else {
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h
index 3b3d7ba7e6a..eb64d978859 100644
--- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -15,9 +15,10 @@
#include "sandbox/linux/bpf_dsl/policy.h"
#include "sandbox/sandbox_export.h"
-namespace sandbox {
struct arch_seccomp_data;
+namespace sandbox {
+
// This class can be used to apply a syscall sandboxing policy expressed in a
// bpf_dsl::Policy object to the current process.
// Syscall sandboxing policies get inherited by subprocesses and, once applied,
@@ -61,11 +62,7 @@ class SANDBOX_EXPORT SandboxBPF {
// disallowed.
// Finally, stacking does add more kernel overhead than having a single
// combined policy. So, it should only be used if there are no alternatives.
- //
- // |enable_ibpb| controls if the sandbox will forcibly enable indirect branch
- // prediction barrier through prctl(2) to mitigate Spectre variant 2.
- bool StartSandbox(SeccompLevel level,
- bool enable_ibpb = true) WARN_UNUSED_RESULT;
+ bool StartSandbox(SeccompLevel level) WARN_UNUSED_RESULT;
// The sandbox needs to be able to access files in "/proc/self/". If
// this directory is not accessible when "StartSandbox()" gets called, the
@@ -103,7 +100,7 @@ class SANDBOX_EXPORT SandboxBPF {
// Assembles and installs a filter based on the policy that has previously
// been configured with SetSandboxPolicy().
- void InstallFilter(bool must_sync_threads, bool enable_ibpb);
+ 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
diff --git a/chromium/sandbox/linux/services/yama.cc b/chromium/sandbox/linux/services/yama.cc
index e67671249fe..a2b33f2b381 100644
--- a/chromium/sandbox/linux/services/yama.cc
+++ b/chromium/sandbox/linux/services/yama.cc
@@ -17,14 +17,7 @@
#include "base/files/scoped_file.h"
#include "base/notreached.h"
#include "base/posix/eintr_wrapper.h"
-
-#if !defined(PR_SET_PTRACER_ANY)
-#define PR_SET_PTRACER_ANY ((unsigned long)-1)
-#endif
-
-#if !defined(PR_SET_PTRACER)
-#define PR_SET_PTRACER 0x59616d61
-#endif
+#include "sandbox/linux/system_headers/linux_prctl.h"
namespace sandbox {
diff --git a/chromium/sandbox/linux/syscall_broker/broker_client.cc b/chromium/sandbox/linux/syscall_broker/broker_client.cc
index fb897eb6ff8..6b1b5be4338 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_client.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_client.cc
@@ -93,7 +93,7 @@ int BrokerClient::Readlink(const char* path, char* buf, size_t bufsize) const {
RAW_CHECK(message.AddIntToMessage(COMMAND_READLINK));
RAW_CHECK(message.AddStringToMessage(path));
- int returned_fd = -1;
+ base::ScopedFD returned_fd;
BrokerSimpleMessage reply;
ssize_t msg_len =
message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
@@ -112,11 +112,14 @@ int BrokerClient::Readlink(const char* path, char* buf, size_t bufsize) const {
return -ENOMEM;
if (return_length < 0)
return -ENOMEM;
+ // Sanity check that our broker is behaving correctly.
+ RAW_CHECK(return_length == static_cast<size_t>(return_value));
- if (static_cast<size_t>(return_length) > bufsize)
- return -ENAMETOOLONG;
+ if (return_length > bufsize) {
+ return_length = bufsize;
+ }
memcpy(buf, return_data, return_length);
- return return_value;
+ return return_length;
}
int BrokerClient::Rename(const char* oldpath, const char* newpath) const {
@@ -134,7 +137,7 @@ int BrokerClient::Rename(const char* oldpath, const char* newpath) const {
RAW_CHECK(message.AddStringToMessage(oldpath));
RAW_CHECK(message.AddStringToMessage(newpath));
- int returned_fd = -1;
+ base::ScopedFD returned_fd;
BrokerSimpleMessage reply;
ssize_t msg_len =
message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
@@ -209,7 +212,7 @@ int BrokerClient::PathOnlySyscall(BrokerCommand syscall_type,
RAW_CHECK(message.AddIntToMessage(syscall_type));
RAW_CHECK(message.AddStringToMessage(pathname));
- int returned_fd = -1;
+ base::ScopedFD returned_fd;
BrokerSimpleMessage reply;
ssize_t msg_len =
message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
@@ -236,7 +239,7 @@ int BrokerClient::PathAndFlagsSyscall(BrokerCommand syscall_type,
RAW_CHECK(message.AddStringToMessage(pathname));
RAW_CHECK(message.AddIntToMessage(flags));
- int returned_fd = -1;
+ base::ScopedFD returned_fd;
BrokerSimpleMessage reply;
ssize_t msg_len =
message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
@@ -277,7 +280,7 @@ int BrokerClient::PathAndFlagsSyscallReturningFD(BrokerCommand syscall_type,
RAW_CHECK(message.AddStringToMessage(pathname));
RAW_CHECK(message.AddIntToMessage(flags));
- int returned_fd = -1;
+ base::ScopedFD returned_fd;
BrokerSimpleMessage reply;
ssize_t msg_len = message.SendRecvMsgWithFlags(
ipc_channel_.get(), recvmsg_flags, &returned_fd, &reply);
@@ -292,8 +295,8 @@ int BrokerClient::PathAndFlagsSyscallReturningFD(BrokerCommand syscall_type,
return return_value;
// We have a real file descriptor to return.
- RAW_CHECK(returned_fd >= 0);
- return returned_fd;
+ RAW_CHECK(returned_fd.is_valid());
+ return returned_fd.release();
}
// Make a remote system call over IPC for syscalls that take a path
@@ -310,7 +313,7 @@ int BrokerClient::StatFamilySyscall(BrokerCommand syscall_type,
RAW_CHECK(message.AddStringToMessage(pathname));
RAW_CHECK(message.AddIntToMessage(static_cast<int>(follow_links)));
- int returned_fd = -1;
+ base::ScopedFD returned_fd;
BrokerSimpleMessage reply;
ssize_t msg_len =
message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
@@ -334,5 +337,13 @@ int BrokerClient::StatFamilySyscall(BrokerCommand syscall_type,
return return_value;
}
+// static
+intptr_t BrokerClient::SIGSYS_Handler(const arch_seccomp_data& args,
+ void* aux_broker_client) {
+ RAW_CHECK(aux_broker_client);
+ auto* broker_client = static_cast<BrokerClient*>(aux_broker_client);
+ return broker_client->DispatchSyscall(args);
+}
+
} // namespace syscall_broker
} // namespace sandbox
diff --git a/chromium/sandbox/linux/syscall_broker/broker_client.h b/chromium/sandbox/linux/syscall_broker/broker_client.h
index 842ee16a0c7..05e14c83f20 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_client.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_client.h
@@ -12,6 +12,8 @@
#include "base/macros.h"
#include "sandbox/linux/syscall_broker/broker_channel.h"
#include "sandbox/linux/syscall_broker/broker_command.h"
+#include "sandbox/linux/syscall_broker/syscall_dispatcher.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
namespace syscall_broker {
@@ -25,8 +27,13 @@ class BrokerPermissionList;
// thread-safe and async-signal safe way. The goal is to be able to use it to
// replace the open() or access() system calls happening anywhere in a process
// (as allowed for instance by seccomp-bpf's SIGSYS mechanism).
-class BrokerClient {
+class SANDBOX_EXPORT BrokerClient : public SyscallDispatcher {
public:
+ // Handler to be used with a bpf_dsl Trap() function to forward system calls
+ // to the methods below.
+ static intptr_t SIGSYS_Handler(const arch_seccomp_data& args,
+ void* aux_broker_process);
+
// |policy| needs to match the policy used by BrokerHost. This
// allows to predict some of the requests which will be denied
// and save an IPC round trip.
@@ -36,43 +43,29 @@ class BrokerClient {
BrokerChannel::EndPoint ipc_channel,
const BrokerCommandSet& allowed_command_set,
bool fast_check_in_client);
- ~BrokerClient();
+ ~BrokerClient() override;
- // Get the file descriptor used for IPC. This is used for tests.
- int GetIPCDescriptor() const { return ipc_channel_.get(); }
+ // Get the file descriptor used for IPC.
+ int GetIPCDescriptorForTesting() const { return ipc_channel_.get(); }
// The following public methods can be used in place of the equivalently
// name system calls. They all return -errno on errors. They are all async
// signal safe so they may be called from a SIGSYS trap handler.
- // Can be used in place of access().
- // X_OK will always return an error in practice since the broker process
- // doesn't support execute permissions.
- int Access(const char* pathname, int mode) const;
-
- // Can be used in place of mkdir().
- int Mkdir(const char* path, int mode) const;
-
- // Can be used in place of open().
- // The implementation only supports certain white listed flags and will
- // return -EPERM on other flags.
- int Open(const char* pathname, int flags) const;
-
- // Can be used in place of Readlink().
- int Readlink(const char* path, char* buf, size_t bufsize) const;
-
- // Can be used in place of rename().
- int Rename(const char* oldpath, const char* newpath) const;
-
- // Can be used in place of rmdir().
- int Rmdir(const char* path) const;
-
- // Can be used in place of stat()/stat64()/lstat()/lstat64()
- int Stat(const char* pathname, bool follow_links, struct stat* sb) const;
- int Stat64(const char* pathname, bool folllow_links, struct stat64* sb) const;
-
- // Can be used in place of unlink().
- int Unlink(const char* unlink) const;
+ // SyscallDispatcher implementation:
+ int Access(const char* pathname, int mode) const override;
+ int Mkdir(const char* path, int mode) const override;
+ int Open(const char* pathname, int flags) const override;
+ int Readlink(const char* path, char* buf, size_t bufsize) const override;
+ int Rename(const char* oldpath, const char* newpath) const override;
+ int Rmdir(const char* path) const override;
+ int Stat(const char* pathname,
+ bool follow_links,
+ struct stat* sb) const override;
+ int Stat64(const char* pathname,
+ bool follow_links,
+ struct stat64* sb) const override;
+ int Unlink(const char* unlink) const override;
private:
int PathOnlySyscall(BrokerCommand syscall_type, const char* pathname) const;
diff --git a/chromium/sandbox/linux/syscall_broker/broker_host.cc b/chromium/sandbox/linux/syscall_broker/broker_host.cc
index 631c90b350c..1cd03a18df8 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_host.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_host.cc
@@ -96,7 +96,7 @@ void OpenFileForIPC(const BrokerCommandSet& allowed_command_set,
const std::string& requested_filename,
int flags,
BrokerSimpleMessage* reply,
- int* opened_file) {
+ base::ScopedFD* opened_file) {
const char* file_to_open = NULL;
bool unlink_after_open = false;
if (!CommandOpenIsSafe(allowed_command_set, permission_list,
@@ -107,8 +107,8 @@ void OpenFileForIPC(const BrokerCommandSet& allowed_command_set,
}
CHECK(file_to_open);
- int opened_fd = sys_open(file_to_open, flags);
- if (opened_fd < 0) {
+ opened_file->reset(sys_open(file_to_open, flags));
+ if (!opened_file->is_valid()) {
RAW_CHECK(reply->AddIntToMessage(-errno));
return;
}
@@ -116,7 +116,6 @@ void OpenFileForIPC(const BrokerCommandSet& allowed_command_set,
if (unlink_after_open)
unlink(file_to_open);
- *opened_file = opened_fd;
RAW_CHECK(reply->AddIntToMessage(0));
}
@@ -207,12 +206,6 @@ void StatFileForIPC(const BrokerCommandSet& allowed_command_set,
reply->AddDataToMessage(reinterpret_cast<char*>(&sb), sizeof(sb)));
} else {
DCHECK(command_type == COMMAND_STAT64);
-#if defined(__ANDROID_API__) && __ANDROID_API__ < 21
- // stat64 is not defined for older Android API versions in newer NDK
- // versions.
- RAW_CHECK(reply->AddIntToMessage(-ENOSYS));
- return;
-#else
struct stat64 sb;
int sts = follow_links ? stat64(file_to_access, &sb)
: lstat64(file_to_access, &sb);
@@ -223,7 +216,6 @@ void StatFileForIPC(const BrokerCommandSet& allowed_command_set,
RAW_CHECK(reply->AddIntToMessage(0));
RAW_CHECK(
reply->AddDataToMessage(reinterpret_cast<char*>(&sb), sizeof(sb)));
-#endif // defined(__ANDROID_API__) && __ANDROID_API__ < 21
}
}
@@ -250,7 +242,7 @@ bool HandleRemoteCommand(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list,
BrokerSimpleMessage* message,
BrokerSimpleMessage* reply,
- int* opened_file) {
+ base::ScopedFD* opened_file) {
// Message structure:
// int: command type
// char[]: pathname
@@ -353,47 +345,45 @@ BrokerHost::BrokerHost(const BrokerPermissionList& broker_permission_list,
allowed_command_set_(allowed_command_set),
ipc_channel_(std::move(ipc_channel)) {}
-BrokerHost::~BrokerHost() {}
+BrokerHost::~BrokerHost() = default;
// Handle a request on the IPC channel ipc_channel_.
// A request should have a file descriptor attached on which we will reply and
// that we will then close.
// A request should start with an int that will be used as the command type.
-BrokerHost::RequestStatus BrokerHost::HandleRequest() const {
- BrokerSimpleMessage message;
- errno = 0;
- base::ScopedFD temporary_ipc;
- const ssize_t msg_len =
- message.RecvMsgWithFlags(ipc_channel_.get(), 0, &temporary_ipc);
-
- if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) {
- // EOF from the client, or the client died, we should die.
- return RequestStatus::LOST_CLIENT;
- }
-
- // The client sends exactly one file descriptor, on which we
- // will write the reply.
- if (msg_len < 0) {
- PLOG(ERROR) << "Error reading message from the client";
- return RequestStatus::FAILURE;
- }
+void BrokerHost::LoopAndHandleRequests() {
+ for (;;) {
+ BrokerSimpleMessage message;
+ errno = 0;
+ base::ScopedFD temporary_ipc;
+ const ssize_t msg_len =
+ message.RecvMsgWithFlags(ipc_channel_.get(), 0, &temporary_ipc);
+
+ if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) {
+ // EOF from the client, or the client died, we should finish looping.
+ return;
+ }
- BrokerSimpleMessage reply;
- int opened_file = -1;
- bool result =
- HandleRemoteCommand(allowed_command_set_, broker_permission_list_,
- &message, &reply, &opened_file);
+ // The client sends exactly one file descriptor, on which we
+ // will write the reply.
+ if (msg_len < 0) {
+ PLOG(ERROR) << "Error reading message from the client";
+ continue;
+ }
- ssize_t sent = reply.SendMsg(temporary_ipc.get(), opened_file);
- if (sent < 0)
- LOG(ERROR) << "sent failed";
+ BrokerSimpleMessage reply;
+ base::ScopedFD opened_file;
+ if (!HandleRemoteCommand(allowed_command_set_, broker_permission_list_,
+ &message, &reply, &opened_file)) {
+ // Does not exit if we received a malformed message.
+ LOG(ERROR) << "Received malformed message from the client";
+ continue;
+ }
- if (opened_file >= 0) {
- int ret = IGNORE_EINTR(close(opened_file));
- DCHECK(!ret) << "Could not close file descriptor";
+ ssize_t sent = reply.SendMsg(temporary_ipc.get(), opened_file.get());
+ if (sent < 0)
+ LOG(ERROR) << "sent failed";
}
-
- return result ? RequestStatus::SUCCESS : RequestStatus::FAILURE;
}
} // namespace syscall_broker
diff --git a/chromium/sandbox/linux/syscall_broker/broker_host.h b/chromium/sandbox/linux/syscall_broker/broker_host.h
index b54e32d3cf7..4b0f3e32587 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_host.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_host.h
@@ -20,14 +20,13 @@ class BrokerPermissionList;
// |ipc_channel| according to |broker_permission_list|.
class BrokerHost {
public:
- enum class RequestStatus { LOST_CLIENT = 0, SUCCESS, FAILURE };
-
BrokerHost(const BrokerPermissionList& broker_permission_list,
const BrokerCommandSet& allowed_command_set,
BrokerChannel::EndPoint ipc_channel);
~BrokerHost();
- RequestStatus HandleRequest() const;
+ // Receive system call requests and handle them forevermore.
+ void LoopAndHandleRequests();
private:
const BrokerPermissionList& broker_permission_list_;
diff --git a/chromium/sandbox/linux/syscall_broker/broker_process.cc b/chromium/sandbox/linux/syscall_broker/broker_process.cc
index 8321d23798d..d72c9d2388f 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_process.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_process.cc
@@ -34,10 +34,12 @@ BrokerProcess::BrokerProcess(
int denied_errno,
const syscall_broker::BrokerCommandSet& allowed_command_set,
const std::vector<syscall_broker::BrokerFilePermission>& permissions,
+ BrokerType broker_type,
bool fast_check_in_client,
bool quiet_failures_for_tests)
: initialized_(false),
broker_pid_(-1),
+ broker_type_(broker_type),
fast_check_in_client_(fast_check_in_client),
quiet_failures_for_tests_(quiet_failures_for_tests),
allowed_command_set_(allowed_command_set),
@@ -58,16 +60,12 @@ BrokerProcess::~BrokerProcess() {
}
}
-bool BrokerProcess::Init(
+bool BrokerProcess::ForkSignalBasedBroker(
base::OnceCallback<bool(void)> broker_process_init_callback) {
- CHECK(!initialized_);
BrokerChannel::EndPoint ipc_reader;
BrokerChannel::EndPoint ipc_writer;
BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
-#if !defined(THREAD_SANITIZER)
- DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
-#endif
int child_pid = fork();
if (child_pid == -1)
return false;
@@ -80,9 +78,11 @@ bool BrokerProcess::Init(
// We are the parent and we have just forked our broker process.
ipc_reader.reset();
broker_pid_ = child_pid;
+
broker_client_ = std::make_unique<BrokerClient>(
broker_permission_list_, std::move(ipc_writer), allowed_command_set_,
fast_check_in_client_);
+
initialized_ = true;
return true;
}
@@ -95,59 +95,68 @@ bool BrokerProcess::Init(
// We are the broker process. Make sure to close the writer's end so that
// we get notified if the client disappears.
ipc_writer.reset();
+
CHECK(std::move(broker_process_init_callback).Run());
- BrokerHost broker_host(broker_permission_list_, allowed_command_set_,
- std::move(ipc_reader));
- for (;;) {
- switch (broker_host.HandleRequest()) {
- case BrokerHost::RequestStatus::LOST_CLIENT:
- _exit(1);
- case BrokerHost::RequestStatus::SUCCESS:
- case BrokerHost::RequestStatus::FAILURE:
- continue;
- }
- }
+
+ BrokerHost broker_host_signal_based(
+ broker_permission_list_, allowed_command_set_, std::move(ipc_reader));
+ broker_host_signal_based.LoopAndHandleRequests();
+ _exit(1);
+ NOTREACHED();
+ return false;
+}
+
+bool BrokerProcess::Init(
+ base::OnceCallback<bool(void)> broker_process_init_callback) {
+ CHECK(!initialized_);
+
+#if !defined(THREAD_SANITIZER)
+ DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
+#endif
+
+ return ForkSignalBasedBroker(std::move(broker_process_init_callback));
}
bool BrokerProcess::IsSyscallAllowed(int sysno) const {
+ return IsSyscallBrokerable(sysno, fast_check_in_client_);
+}
+
+bool BrokerProcess::IsSyscallBrokerable(int sysno, bool fast_check) const {
switch (sysno) {
#if !defined(__aarch64__)
case __NR_access:
#endif
case __NR_faccessat:
- return !fast_check_in_client_ ||
- allowed_command_set_.test(COMMAND_ACCESS);
+ return !fast_check || allowed_command_set_.test(COMMAND_ACCESS);
#if !defined(__aarch64__)
case __NR_mkdir:
#endif
case __NR_mkdirat:
- return !fast_check_in_client_ || allowed_command_set_.test(COMMAND_MKDIR);
+ return !fast_check || allowed_command_set_.test(COMMAND_MKDIR);
#if !defined(__aarch64__)
case __NR_open:
#endif
case __NR_openat:
- return !fast_check_in_client_ || allowed_command_set_.test(COMMAND_OPEN);
+ return !fast_check || allowed_command_set_.test(COMMAND_OPEN);
#if !defined(__aarch64__)
case __NR_readlink:
#endif
case __NR_readlinkat:
- return !fast_check_in_client_ ||
- allowed_command_set_.test(COMMAND_READLINK);
+ return !fast_check || allowed_command_set_.test(COMMAND_READLINK);
#if !defined(__aarch64__)
case __NR_rename:
#endif
case __NR_renameat:
case __NR_renameat2:
- return !fast_check_in_client_ ||
- allowed_command_set_.test(COMMAND_RENAME);
+ return !fast_check || allowed_command_set_.test(COMMAND_RENAME);
#if !defined(__aarch64__)
case __NR_rmdir:
- return !fast_check_in_client_ || allowed_command_set_.test(COMMAND_RMDIR);
+ return !fast_check || allowed_command_set_.test(COMMAND_RMDIR);
#endif
#if !defined(__aarch64__)
@@ -157,10 +166,13 @@ bool BrokerProcess::IsSyscallAllowed(int sysno) const {
#if defined(__NR_fstatat)
case __NR_fstatat:
#endif
+#if defined(__NR_fstatat64)
+ case __NR_fstatat64:
+#endif
#if defined(__x86_64__) || defined(__aarch64__)
case __NR_newfstatat:
#endif
- return !fast_check_in_client_ || allowed_command_set_.test(COMMAND_STAT);
+ return !fast_check || allowed_command_set_.test(COMMAND_STAT);
#if defined(__i386__) || defined(__arm__) || \
(defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS))
@@ -169,14 +181,16 @@ bool BrokerProcess::IsSyscallAllowed(int sysno) const {
// For security purposes, map stat64 to COMMAND_STAT permission. The
// separate COMMAND_STAT64 only exists to broker different-sized
// argument structs.
- return !fast_check_in_client_ || allowed_command_set_.test(COMMAND_STAT);
+ return !fast_check || allowed_command_set_.test(COMMAND_STAT);
#endif
#if !defined(__aarch64__)
case __NR_unlink:
+ return !fast_check || allowed_command_set_.test(COMMAND_UNLINK);
#endif
case __NR_unlinkat:
- return !fast_check_in_client_ ||
+ // If rmdir() doesn't exist, unlinkat is used with AT_REMOVEDIR.
+ return !fast_check || allowed_command_set_.test(COMMAND_RMDIR) ||
allowed_command_set_.test(COMMAND_UNLINK);
default:
@@ -188,221 +202,5 @@ void BrokerProcess::CloseChannel() {
broker_client_.reset();
}
-int BrokerProcess::Access(const char* pathname, int mode) const {
- RAW_CHECK(initialized_);
- return broker_client_->Access(pathname, mode);
-}
-
-int BrokerProcess::Mkdir(const char* path, int mode) const {
- RAW_CHECK(initialized_);
- return broker_client_->Mkdir(path, mode);
-}
-
-int BrokerProcess::Open(const char* pathname, int flags) const {
- RAW_CHECK(initialized_);
- return broker_client_->Open(pathname, flags);
-}
-
-int BrokerProcess::Readlink(const char* path, char* buf, size_t bufsize) const {
- RAW_CHECK(initialized_);
- return broker_client_->Readlink(path, buf, bufsize);
-}
-
-int BrokerProcess::Rename(const char* oldpath, const char* newpath) const {
- RAW_CHECK(initialized_);
- return broker_client_->Rename(oldpath, newpath);
-}
-
-int BrokerProcess::Rmdir(const char* pathname) const {
- RAW_CHECK(initialized_);
- return broker_client_->Rmdir(pathname);
-}
-
-int BrokerProcess::Stat(const char* pathname,
- bool follow_links,
- struct stat* sb) const {
- RAW_CHECK(initialized_);
- return broker_client_->Stat(pathname, follow_links, sb);
-}
-
-int BrokerProcess::Stat64(const char* pathname,
- bool follow_links,
- struct stat64* sb) const {
- RAW_CHECK(initialized_);
- return broker_client_->Stat64(pathname, follow_links, sb);
-}
-
-int BrokerProcess::Unlink(const char* pathname) const {
- RAW_CHECK(initialized_);
- return broker_client_->Unlink(pathname);
-}
-
-#if defined(MEMORY_SANITIZER)
-#define BROKER_UNPOISON_STRING(x) __msan_unpoison_string(x)
-#else
-#define BROKER_UNPOISON_STRING(x)
-#endif
-
-// static
-intptr_t BrokerProcess::SIGSYS_Handler(const sandbox::arch_seccomp_data& args,
- void* aux_broker_process) {
- RAW_CHECK(aux_broker_process);
- auto* broker_process = static_cast<BrokerProcess*>(aux_broker_process);
- switch (args.nr) {
-#if defined(__NR_access)
- case __NR_access:
- return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
- static_cast<int>(args.args[1]));
-#endif
-#if defined(__NR_faccessat)
- case __NR_faccessat:
- if (static_cast<int>(args.args[0]) != AT_FDCWD)
- return -EPERM;
- return broker_process->Access(reinterpret_cast<const char*>(args.args[1]),
- static_cast<int>(args.args[2]));
-#endif
-#if defined(__NR_mkdir)
- case __NR_mkdir:
- return broker_process->Mkdir(reinterpret_cast<const char*>(args.args[0]),
- static_cast<int>(args.args[1]));
-#endif
-#if defined(__NR_mkdirat)
- case __NR_mkdirat:
- if (static_cast<int>(args.args[0]) != AT_FDCWD)
- return -EPERM;
- return broker_process->Mkdir(reinterpret_cast<const char*>(args.args[1]),
- static_cast<int>(args.args[2]));
-#endif
-#if defined(__NR_open)
- case __NR_open:
- // http://crbug.com/372840
- BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0]));
- return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
- static_cast<int>(args.args[1]));
-#endif
-#if defined(__NR_openat)
- case __NR_openat:
- if (static_cast<int>(args.args[0]) != AT_FDCWD)
- return -EPERM;
- // http://crbug.com/372840
- BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[1]));
- return broker_process->Open(reinterpret_cast<const char*>(args.args[1]),
- static_cast<int>(args.args[2]));
-#endif
-#if defined(__NR_readlink)
- case __NR_readlink:
- return broker_process->Readlink(
- reinterpret_cast<const char*>(args.args[0]),
- reinterpret_cast<char*>(args.args[1]),
- static_cast<size_t>(args.args[2]));
-#endif
-#if defined(__NR_readlinkat)
- case __NR_readlinkat:
- if (static_cast<int>(args.args[0]) != AT_FDCWD)
- return -EPERM;
- return broker_process->Readlink(
- reinterpret_cast<const char*>(args.args[1]),
- reinterpret_cast<char*>(args.args[2]),
- static_cast<size_t>(args.args[3]));
-#endif
-#if defined(__NR_rename)
- case __NR_rename:
- return broker_process->Rename(
- reinterpret_cast<const char*>(args.args[0]),
- reinterpret_cast<const char*>(args.args[1]));
-#endif
-#if defined(__NR_renameat)
- case __NR_renameat:
- if (static_cast<int>(args.args[0]) != AT_FDCWD ||
- static_cast<int>(args.args[2]) != AT_FDCWD) {
- return -EPERM;
- }
- return broker_process->Rename(
- reinterpret_cast<const char*>(args.args[1]),
- reinterpret_cast<const char*>(args.args[3]));
-#endif
-#if defined(__NR_renameat2)
- case __NR_renameat2:
- if (static_cast<int>(args.args[0]) != AT_FDCWD ||
- static_cast<int>(args.args[2]) != AT_FDCWD) {
- return -EPERM;
- }
- if (static_cast<int>(args.args[4]) != 0)
- return -EINVAL;
- return broker_process->Rename(
- reinterpret_cast<const char*>(args.args[1]),
- reinterpret_cast<const char*>(args.args[3]));
-#endif
-#if defined(__NR_rmdir)
- case __NR_rmdir:
- return broker_process->Rmdir(reinterpret_cast<const char*>(args.args[0]));
-#endif
-#if defined(__NR_stat)
- case __NR_stat:
- return broker_process->Stat(reinterpret_cast<const char*>(args.args[0]),
- true,
- reinterpret_cast<struct stat*>(args.args[1]));
-#endif
-#if defined(__NR_stat64)
- case __NR_stat64:
- return broker_process->Stat64(
- reinterpret_cast<const char*>(args.args[0]), true,
- reinterpret_cast<struct stat64*>(args.args[1]));
-#endif
-#if defined(__NR_lstat)
- case __NR_lstat:
- // See https://crbug.com/847096
- BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0]));
- return broker_process->Stat(reinterpret_cast<const char*>(args.args[0]),
- false,
- reinterpret_cast<struct stat*>(args.args[1]));
-#endif
-#if defined(__NR_lstat64)
- case __NR_lstat64:
- // See https://crbug.com/847096
- BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0]));
- return broker_process->Stat64(
- reinterpret_cast<const char*>(args.args[0]), false,
- reinterpret_cast<struct stat64*>(args.args[1]));
-#endif
-#if defined(__NR_fstatat)
- case __NR_fstatat:
- if (static_cast<int>(args.args[0]) != AT_FDCWD)
- return -EPERM;
- if (static_cast<int>(args.args[3]) != 0)
- return -EINVAL;
- return broker_process->Stat(reinterpret_cast<const char*>(args.args[1]),
- true,
- reinterpret_cast<struct stat*>(args.args[2]));
-#endif
-#if defined(__NR_newfstatat)
- case __NR_newfstatat:
- if (static_cast<int>(args.args[0]) != AT_FDCWD)
- return -EPERM;
- if (static_cast<int>(args.args[3]) != 0)
- return -EINVAL;
- return broker_process->Stat(reinterpret_cast<const char*>(args.args[1]),
- true,
- reinterpret_cast<struct stat*>(args.args[2]));
-#endif
-#if defined(__NR_unlink)
- case __NR_unlink:
- return broker_process->Unlink(
- reinterpret_cast<const char*>(args.args[0]));
-#endif
-#if defined(__NR_unlinkat)
- case __NR_unlinkat:
- // TODO(tsepez): does not support AT_REMOVEDIR flag.
- if (static_cast<int>(args.args[0]) != AT_FDCWD)
- return -EPERM;
- return broker_process->Unlink(
- reinterpret_cast<const char*>(args.args[1]));
-#endif
- default:
- RAW_CHECK(false);
- return -ENOSYS;
- }
-}
-
} // namespace syscall_broker
} // namespace sandbox
diff --git a/chromium/sandbox/linux/syscall_broker/broker_process.h b/chromium/sandbox/linux/syscall_broker/broker_process.h
index 022b5e220fa..67f0ca50cfd 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_process.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_process.h
@@ -39,10 +39,7 @@ class BrokerFilePermission;
// 4. Use open_broker.Open() to open files.
class SANDBOX_EXPORT BrokerProcess {
public:
- // Handler to be used with a bpf_dsl Trap() function to forward system calls
- // to the methods below.
- static intptr_t SIGSYS_Handler(const arch_seccomp_data& args,
- void* aux_broker_process);
+ enum class BrokerType { SIGNAL_BASED };
// |denied_errno| is the error code returned when methods such as Open()
// or Access() are invoked on a file which is not in the allowlist (EACCESS
@@ -62,6 +59,7 @@ class SANDBOX_EXPORT BrokerProcess {
int denied_errno,
const syscall_broker::BrokerCommandSet& allowed_command_set,
const std::vector<syscall_broker::BrokerFilePermission>& permissions,
+ BrokerType broker_type,
bool fast_check_in_client = true,
bool quiet_failures_for_tests = false);
@@ -84,49 +82,34 @@ class SANDBOX_EXPORT BrokerProcess {
// calls are forwarded to the broker process for handling.
bool IsSyscallAllowed(int sysno) const;
- // The following methods are used in place of the equivalently-named
- // syscalls by the trap handler. They, in turn, forward the call onto
- // |broker_client_| for further processing. They will all be async signal
- // safe. They all return -errno on errors.
-
- // Can be used in place of access().
- // X_OK will always return an error in practice since the broker process
- // doesn't support execute permissions.
- int Access(const char* pathname, int mode) const;
-
- // Can be used in place of mkdir().
- int Mkdir(const char* path, int mode) const;
-
- // Can be used in place of open()
- // The implementation only supports certain white listed flags and will
- // return -EPERM on other flags.
- int Open(const char* pathname, int flags) const;
-
- // Can be used in place of readlink().
- int Readlink(const char* path, char* buf, size_t bufsize) const;
-
- // Can be used in place of rename().
- int Rename(const char* oldpath, const char* newpath) const;
-
- // Can be used in place of rmdir().
- int Rmdir(const char* path) const;
-
- // Can be used in place of stat()/stat64()/lstat()/lstat64().
- int Stat(const char* pathname, bool follow_links, struct stat* sb) const;
- int Stat64(const char* pathname, bool follow_links, struct stat64* sb) const;
-
- // Can be used in place of unlink().
- int Unlink(const char* path) const;
+ // Gets the signal-based BrokerClient created by Init().
+ syscall_broker::BrokerClient* GetBrokerClientSignalBased() const {
+ return broker_client_.get();
+ }
private:
friend class BrokerProcessTestHelper;
+ friend class HandleFilesystemViaBrokerPolicy;
+
+ // IsSyscallBrokerable() answers the same question as IsSyscallAllowed(),
+ // but takes |fast_check| as a parameter. If |fast_check| is false, do not
+ // check |allowed_command_set_| before returning true for a syscall that is
+ // brokerable.
+ bool IsSyscallBrokerable(int sysno, bool fast_check) const;
// Close the IPC channel with the other party. This should only be used
- // by tests an none of the class methods should be used afterwards.
+ // by tests and none of the class methods should be used afterwards.
void CloseChannel();
+ // Forks the signal-based broker, where syscall emulation is performed using
+ // signals in the sandboxed process that connect to the broker via Unix
+ // socket.
+ bool ForkSignalBasedBroker(
+ base::OnceCallback<bool(void)> broker_process_init_callback);
+
bool initialized_; // Whether we've been through Init() yet.
pid_t broker_pid_; // The PID of the broker (child) created in Init().
+ const BrokerType broker_type_;
const bool fast_check_in_client_;
const bool quiet_failures_for_tests_;
syscall_broker::BrokerCommandSet allowed_command_set_;
diff --git a/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc b/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc
index e4a8b04672e..08096b95aa3 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc
@@ -21,6 +21,8 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
@@ -30,6 +32,7 @@
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "sandbox/linux/syscall_broker/broker_client.h"
+#include "sandbox/linux/syscall_broker/broker_command.h"
#include "sandbox/linux/tests/scoped_temporary_file.h"
#include "sandbox/linux/tests/test_utils.h"
#include "sandbox/linux/tests/unit_tests.h"
@@ -38,19 +41,19 @@
namespace sandbox {
namespace syscall_broker {
+using BrokerType = BrokerProcess::BrokerType;
+
class BrokerProcessTestHelper {
public:
static void CloseChannel(BrokerProcess* broker) { broker->CloseChannel(); }
- // Get the client's IPC descriptor to send IPC requests directly.
- // TODO(jln): refator tests to get rid of this.
- static int GetIPCDescriptor(const BrokerProcess* broker) {
- return broker->broker_client_->GetIPCDescriptor();
- }
};
namespace {
-const int kFakeErrnoSentinel = 99999;
+// Our fake errno must be less than 255 or various libc implementations will
+// not accept this as a valid error number. E.g. bionic accepts up to 255, glibc
+// and musl up to 4096.
+constexpr int kFakeErrnoSentinel = 254;
bool NoOpCallback() {
return true;
@@ -63,7 +66,7 @@ TEST(BrokerProcess, CreateAndDestroy) {
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnly("/proc/cpuinfo")};
BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
- permissions);
+ permissions, BrokerType::SIGNAL_BASED);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
}
@@ -76,13 +79,14 @@ TEST(BrokerProcess, TestOpenAccessNull) {
MakeBrokerCommandSet({COMMAND_ACCESS, COMMAND_OPEN});
std::vector<BrokerFilePermission> empty;
- BrokerProcess open_broker(kFakeErrnoSentinel, command_set, empty);
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, empty,
+ BrokerType::SIGNAL_BASED);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- int fd = open_broker.Open(NULL, O_RDONLY);
+ int fd = open_broker.GetBrokerClientSignalBased()->Open(nullptr, O_RDONLY);
ASSERT_EQ(fd, -EFAULT);
- int ret = open_broker.Access(NULL, F_OK);
+ int ret = open_broker.GetBrokerClientSignalBased()->Access(nullptr, F_OK);
ASSERT_EQ(ret, -EFAULT);
}
@@ -93,7 +97,7 @@ void TestOpenFilePerms(bool fast_check_in_client, int denied_errno) {
const char kR_AllowListedButDenied[] = "/proc/1/auxv";
const char kW_AllowListed[] = "/proc/DOESNOTEXIST2";
const char kRW_AllowListed[] = "/proc/DOESNOTEXIST3";
- const char k_NotAllowlisted[] = "/proc/DOESNOTEXIST4";
+ const char k_NotAllowListed[] = "/proc/DOESNOTEXIST4";
BrokerCommandSet command_set =
MakeBrokerCommandSet({COMMAND_ACCESS, COMMAND_OPEN});
@@ -104,121 +108,148 @@ void TestOpenFilePerms(bool fast_check_in_client, int denied_errno) {
BrokerFilePermission::WriteOnly(kW_AllowListed),
BrokerFilePermission::ReadWrite(kRW_AllowListed)};
BrokerProcess open_broker(denied_errno, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
int fd = -1;
- fd = open_broker.Open(kR_AllowListed, O_RDONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kR_AllowListed, O_RDONLY);
ASSERT_EQ(fd, -ENOENT);
- fd = open_broker.Open(kR_AllowListed, O_WRONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kR_AllowListed, O_WRONLY);
ASSERT_EQ(fd, -denied_errno);
- fd = open_broker.Open(kR_AllowListed, O_RDWR);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kR_AllowListed, O_RDWR);
ASSERT_EQ(fd, -denied_errno);
int ret = -1;
- ret = open_broker.Access(kR_AllowListed, F_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kR_AllowListed, F_OK);
ASSERT_EQ(ret, -ENOENT);
- ret = open_broker.Access(kR_AllowListed, R_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kR_AllowListed, R_OK);
ASSERT_EQ(ret, -ENOENT);
- ret = open_broker.Access(kR_AllowListed, W_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kR_AllowListed, W_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(kR_AllowListed, R_OK | W_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kR_AllowListed,
+ R_OK | W_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(kR_AllowListed, X_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kR_AllowListed, X_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(kR_AllowListed, R_OK | X_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kR_AllowListed,
+ R_OK | X_OK);
ASSERT_EQ(ret, -denied_errno);
// Android sometimes runs tests as root.
// This part of the test requires a process that doesn't have
// CAP_DAC_OVERRIDE. We check against a root euid as a proxy for that.
if (geteuid()) {
- fd = open_broker.Open(kR_AllowListedButDenied, O_RDONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kR_AllowListedButDenied,
+ O_RDONLY);
// The broker process will allow this, but the normal permission system
// won't.
ASSERT_EQ(fd, -EACCES);
- fd = open_broker.Open(kR_AllowListedButDenied, O_WRONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kR_AllowListedButDenied,
+ O_WRONLY);
ASSERT_EQ(fd, -denied_errno);
- fd = open_broker.Open(kR_AllowListedButDenied, O_RDWR);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kR_AllowListedButDenied,
+ O_RDWR);
ASSERT_EQ(fd, -denied_errno);
- ret = open_broker.Access(kR_AllowListedButDenied, F_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(
+ kR_AllowListedButDenied, F_OK);
// The normal permission system will let us check that the file exists.
ASSERT_EQ(ret, 0);
- ret = open_broker.Access(kR_AllowListedButDenied, R_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(
+ kR_AllowListedButDenied, R_OK);
ASSERT_EQ(ret, -EACCES);
- ret = open_broker.Access(kR_AllowListedButDenied, W_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(
+ kR_AllowListedButDenied, W_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(kR_AllowListedButDenied, R_OK | W_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(
+ kR_AllowListedButDenied, R_OK | W_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(kR_AllowListedButDenied, X_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(
+ kR_AllowListedButDenied, X_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(kR_AllowListedButDenied, R_OK | X_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(
+ kR_AllowListedButDenied, R_OK | X_OK);
ASSERT_EQ(ret, -denied_errno);
}
- fd = open_broker.Open(kW_AllowListed, O_RDONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kW_AllowListed, O_RDONLY);
ASSERT_EQ(fd, -denied_errno);
- fd = open_broker.Open(kW_AllowListed, O_WRONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kW_AllowListed, O_WRONLY);
ASSERT_EQ(fd, -ENOENT);
- fd = open_broker.Open(kW_AllowListed, O_RDWR);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kW_AllowListed, O_RDWR);
ASSERT_EQ(fd, -denied_errno);
- ret = open_broker.Access(kW_AllowListed, F_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kW_AllowListed, F_OK);
ASSERT_EQ(ret, -ENOENT);
- ret = open_broker.Access(kW_AllowListed, R_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kW_AllowListed, R_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(kW_AllowListed, W_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kW_AllowListed, W_OK);
ASSERT_EQ(ret, -ENOENT);
- ret = open_broker.Access(kW_AllowListed, R_OK | W_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kW_AllowListed,
+ R_OK | W_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(kW_AllowListed, X_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kW_AllowListed, X_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(kW_AllowListed, R_OK | X_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kW_AllowListed,
+ R_OK | X_OK);
ASSERT_EQ(ret, -denied_errno);
- fd = open_broker.Open(kRW_AllowListed, O_RDONLY);
+ fd =
+ open_broker.GetBrokerClientSignalBased()->Open(kRW_AllowListed, O_RDONLY);
ASSERT_EQ(fd, -ENOENT);
- fd = open_broker.Open(kRW_AllowListed, O_WRONLY);
+ fd =
+ open_broker.GetBrokerClientSignalBased()->Open(kRW_AllowListed, O_WRONLY);
ASSERT_EQ(fd, -ENOENT);
- fd = open_broker.Open(kRW_AllowListed, O_RDWR);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kRW_AllowListed, O_RDWR);
ASSERT_EQ(fd, -ENOENT);
- ret = open_broker.Access(kRW_AllowListed, F_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kRW_AllowListed, F_OK);
ASSERT_EQ(ret, -ENOENT);
- ret = open_broker.Access(kRW_AllowListed, R_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kRW_AllowListed, R_OK);
ASSERT_EQ(ret, -ENOENT);
- ret = open_broker.Access(kRW_AllowListed, W_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kRW_AllowListed, W_OK);
ASSERT_EQ(ret, -ENOENT);
- ret = open_broker.Access(kRW_AllowListed, R_OK | W_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kRW_AllowListed,
+ R_OK | W_OK);
ASSERT_EQ(ret, -ENOENT);
- ret = open_broker.Access(kRW_AllowListed, X_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kRW_AllowListed, X_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(kRW_AllowListed, R_OK | X_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(kRW_AllowListed,
+ R_OK | X_OK);
ASSERT_EQ(ret, -denied_errno);
- fd = open_broker.Open(k_NotAllowlisted, O_RDONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(k_NotAllowListed,
+ O_RDONLY);
ASSERT_EQ(fd, -denied_errno);
- fd = open_broker.Open(k_NotAllowlisted, O_WRONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(k_NotAllowListed,
+ O_WRONLY);
ASSERT_EQ(fd, -denied_errno);
- fd = open_broker.Open(k_NotAllowlisted, O_RDWR);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(k_NotAllowListed, O_RDWR);
ASSERT_EQ(fd, -denied_errno);
- ret = open_broker.Access(k_NotAllowlisted, F_OK);
+ ret =
+ open_broker.GetBrokerClientSignalBased()->Access(k_NotAllowListed, F_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(k_NotAllowlisted, R_OK);
+ ret =
+ open_broker.GetBrokerClientSignalBased()->Access(k_NotAllowListed, R_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(k_NotAllowlisted, W_OK);
+ ret =
+ open_broker.GetBrokerClientSignalBased()->Access(k_NotAllowListed, W_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(k_NotAllowlisted, R_OK | W_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(k_NotAllowListed,
+ R_OK | W_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(k_NotAllowlisted, X_OK);
+ ret =
+ open_broker.GetBrokerClientSignalBased()->Access(k_NotAllowListed, X_OK);
ASSERT_EQ(ret, -denied_errno);
- ret = open_broker.Access(k_NotAllowlisted, R_OK | X_OK);
+ ret = open_broker.GetBrokerClientSignalBased()->Access(k_NotAllowListed,
+ R_OK | X_OK);
ASSERT_EQ(ret, -denied_errno);
// We have some extra sanity check for clearly wrong values.
- fd = open_broker.Open(kRW_AllowListed, O_RDONLY | O_WRONLY | O_RDWR);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(
+ kRW_AllowListed, O_RDONLY | O_WRONLY | O_RDWR);
ASSERT_EQ(fd, -denied_errno);
// It makes no sense to allow O_CREAT in a 2-parameters open. Ensure this
// is denied.
- fd = open_broker.Open(kRW_AllowListed, O_RDWR | O_CREAT);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kRW_AllowListed,
+ O_RDWR | O_CREAT);
ASSERT_EQ(fd, -denied_errno);
}
@@ -263,40 +294,46 @@ void TestBadPaths(bool fast_check_in_client) {
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnlyRecursive("/proc/")};
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
// Open cpuinfo via the broker.
- int cpuinfo_fd = open_broker.Open(kFileCpuInfo, O_RDONLY);
+ int cpuinfo_fd =
+ open_broker.GetBrokerClientSignalBased()->Open(kFileCpuInfo, O_RDONLY);
base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
ASSERT_GE(cpuinfo_fd, 0);
int fd = -1;
int can_access;
- can_access = open_broker.Access(kNotAbsPath, R_OK);
+ can_access =
+ open_broker.GetBrokerClientSignalBased()->Access(kNotAbsPath, R_OK);
ASSERT_EQ(can_access, -kFakeErrnoSentinel);
- fd = open_broker.Open(kNotAbsPath, O_RDONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kNotAbsPath, O_RDONLY);
ASSERT_EQ(fd, -kFakeErrnoSentinel);
- can_access = open_broker.Access(kDotDotStart, R_OK);
+ can_access =
+ open_broker.GetBrokerClientSignalBased()->Access(kDotDotStart, R_OK);
ASSERT_EQ(can_access, -kFakeErrnoSentinel);
- fd = open_broker.Open(kDotDotStart, O_RDONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kDotDotStart, O_RDONLY);
ASSERT_EQ(fd, -kFakeErrnoSentinel);
- can_access = open_broker.Access(kDotDotMiddle, R_OK);
+ can_access =
+ open_broker.GetBrokerClientSignalBased()->Access(kDotDotMiddle, R_OK);
ASSERT_EQ(can_access, -kFakeErrnoSentinel);
- fd = open_broker.Open(kDotDotMiddle, O_RDONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kDotDotMiddle, O_RDONLY);
ASSERT_EQ(fd, -kFakeErrnoSentinel);
- can_access = open_broker.Access(kDotDotEnd, R_OK);
+ can_access =
+ open_broker.GetBrokerClientSignalBased()->Access(kDotDotEnd, R_OK);
ASSERT_EQ(can_access, -kFakeErrnoSentinel);
- fd = open_broker.Open(kDotDotEnd, O_RDONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kDotDotEnd, O_RDONLY);
ASSERT_EQ(fd, -kFakeErrnoSentinel);
- can_access = open_broker.Access(kTrailingSlash, R_OK);
+ can_access =
+ open_broker.GetBrokerClientSignalBased()->Access(kTrailingSlash, R_OK);
ASSERT_EQ(can_access, -kFakeErrnoSentinel);
- fd = open_broker.Open(kTrailingSlash, O_RDONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kTrailingSlash, O_RDONLY);
ASSERT_EQ(fd, -kFakeErrnoSentinel);
}
@@ -326,21 +363,25 @@ void TestOpenCpuinfo(bool fast_check_in_client, bool recursive) {
: BrokerFilePermission::ReadOnly(kFileCpuInfo));
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- int fd = open_broker.Open(kFileCpuInfo, O_RDWR);
+ int fd =
+ open_broker.GetBrokerClientSignalBased()->Open(kFileCpuInfo, O_RDWR);
ASSERT_EQ(fd, -kFakeErrnoSentinel);
// Check we can read /proc/cpuinfo.
- int can_access = open_broker.Access(kFileCpuInfo, R_OK);
+ int can_access =
+ open_broker.GetBrokerClientSignalBased()->Access(kFileCpuInfo, R_OK);
EXPECT_EQ(can_access, 0);
- can_access = open_broker.Access(kFileCpuInfo, W_OK);
+ can_access =
+ open_broker.GetBrokerClientSignalBased()->Access(kFileCpuInfo, W_OK);
EXPECT_EQ(can_access, -kFakeErrnoSentinel);
// Check we can not write /proc/cpuinfo.
// Open cpuinfo via the broker.
- int cpuinfo_fd = open_broker.Open(kFileCpuInfo, O_RDONLY);
+ int cpuinfo_fd =
+ open_broker.GetBrokerClientSignalBased()->Open(kFileCpuInfo, O_RDONLY);
base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
EXPECT_GE(cpuinfo_fd, 0);
char buf[3];
@@ -404,15 +445,18 @@ TEST(BrokerProcess, OpenFileRW) {
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadWrite(tempfile_name)};
- BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions);
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ BrokerType::SIGNAL_BASED);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
// Check we can access that file with read or write.
- int can_access = open_broker.Access(tempfile_name, R_OK | W_OK);
+ int can_access = open_broker.GetBrokerClientSignalBased()->Access(
+ tempfile_name, R_OK | W_OK);
ASSERT_EQ(can_access, 0);
int tempfile2 = -1;
- tempfile2 = open_broker.Open(tempfile_name, O_RDWR);
+ tempfile2 =
+ open_broker.GetBrokerClientSignalBased()->Open(tempfile_name, O_RDWR);
ASSERT_GE(tempfile2, 0);
// Write to the descriptor opened by the broker.
@@ -441,25 +485,26 @@ SANDBOX_TEST(BrokerProcess, BrokerDied) {
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnly(kCpuInfo)};
- BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- true /* fast_check_in_client */,
- true /* quiet_failures_for_tests */);
+ BrokerProcess open_broker(
+ kFakeErrnoSentinel, command_set, permissions, BrokerType::SIGNAL_BASED,
+ true /* fast_check_in_client */, true /* quiet_failures_for_tests */);
SANDBOX_ASSERT(open_broker.Init(base::BindOnce(&NoOpCallback)));
const pid_t broker_pid = open_broker.broker_pid();
SANDBOX_ASSERT(kill(broker_pid, SIGKILL) == 0);
// Now we check that the broker has been signaled, but do not reap it.
siginfo_t process_info;
- SANDBOX_ASSERT(HANDLE_EINTR(waitid(
- P_PID, broker_pid, &process_info, WEXITED | WNOWAIT)) ==
- 0);
+ SANDBOX_ASSERT(HANDLE_EINTR(waitid(P_PID, broker_pid, &process_info,
+ WEXITED | WNOWAIT)) == 0);
SANDBOX_ASSERT(broker_pid == process_info.si_pid);
SANDBOX_ASSERT(CLD_KILLED == process_info.si_code);
SANDBOX_ASSERT(SIGKILL == process_info.si_status);
// Check that doing Open with a dead broker won't SIGPIPE us.
- SANDBOX_ASSERT(open_broker.Open(kCpuInfo, O_RDONLY) == -ENOMEM);
- SANDBOX_ASSERT(open_broker.Access(kCpuInfo, O_RDONLY) == -ENOMEM);
+ SANDBOX_ASSERT(open_broker.GetBrokerClientSignalBased()->Open(
+ kCpuInfo, O_RDONLY) == -ENOMEM);
+ SANDBOX_ASSERT(open_broker.GetBrokerClientSignalBased()->Access(
+ kCpuInfo, O_RDONLY) == -ENOMEM);
}
void TestOpenComplexFlags(bool fast_check_in_client) {
@@ -471,21 +516,26 @@ void TestOpenComplexFlags(bool fast_check_in_client) {
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnly(kCpuInfo)};
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
// Test that we do the right thing for O_CLOEXEC and O_NONBLOCK.
int fd = -1;
int ret = 0;
- fd = open_broker.Open(kCpuInfo, O_RDONLY);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kCpuInfo, O_RDONLY);
ASSERT_GE(fd, 0);
ret = fcntl(fd, F_GETFL);
- ASSERT_NE(-1, ret);
- // The descriptor shouldn't have the O_CLOEXEC attribute, nor O_NONBLOCK.
+ ASSERT_NE(-1, ret) << errno;
+ // The description shouldn't have the O_CLOEXEC attribute, nor O_NONBLOCK.
ASSERT_EQ(0, ret & (O_CLOEXEC | O_NONBLOCK));
+ ret = fcntl(fd, F_GETFD);
+ ASSERT_NE(-1, ret) << errno;
+ // The descriptor also should not have FD_CLOEXEC.
+ ASSERT_EQ(FD_CLOEXEC & ret, 0);
ASSERT_EQ(0, close(fd));
- fd = open_broker.Open(kCpuInfo, O_RDONLY | O_CLOEXEC);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kCpuInfo,
+ O_RDONLY | O_CLOEXEC);
ASSERT_GE(fd, 0);
ret = fcntl(fd, F_GETFD);
ASSERT_NE(-1, ret);
@@ -494,7 +544,8 @@ void TestOpenComplexFlags(bool fast_check_in_client) {
ASSERT_TRUE(FD_CLOEXEC & ret);
ASSERT_EQ(0, close(fd));
- fd = open_broker.Open(kCpuInfo, O_RDONLY | O_NONBLOCK);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(kCpuInfo,
+ O_RDONLY | O_NONBLOCK);
ASSERT_GE(fd, 0);
ret = fcntl(fd, F_GETFL);
ASSERT_NE(-1, ret);
@@ -561,10 +612,12 @@ SANDBOX_TEST_ALLOW_NOISE(BrokerProcess, MAYBE_RecvMsgDescriptorLeak) {
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnly(kCpuInfo)};
- BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions);
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ BrokerType::SIGNAL_BASED);
SANDBOX_ASSERT(open_broker.Init(base::BindOnce(&NoOpCallback)));
- const int ipc_fd = BrokerProcessTestHelper::GetIPCDescriptor(&open_broker);
+ const int ipc_fd =
+ open_broker.GetBrokerClientSignalBased()->GetIPCDescriptorForTesting();
SANDBOX_ASSERT(ipc_fd >= 0);
static const char kBogus[] = "not a pickle";
@@ -578,7 +631,8 @@ SANDBOX_TEST_ALLOW_NOISE(BrokerProcess, MAYBE_RecvMsgDescriptorLeak) {
base::UnixDomainSocket::SendMsg(ipc_fd, kBogus, sizeof(kBogus), fds));
}
- const int fd = open_broker.Open(kCpuInfo, O_RDONLY);
+ const int fd =
+ open_broker.GetBrokerClientSignalBased()->Open(kCpuInfo, O_RDONLY);
SANDBOX_ASSERT(fd >= 0);
SANDBOX_ASSERT(0 == IGNORE_EINTR(close(fd)));
}
@@ -612,9 +666,9 @@ TEST(BrokerProcess, BrokerDiesOnClosedChannel) {
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnly("/proc/cpuinfo")};
- BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- true /* fast_check_in_client */,
- false /* quiet_failures_for_tests */);
+ BrokerProcess open_broker(
+ kFakeErrnoSentinel, command_set, permissions, BrokerType::SIGNAL_BASED,
+ true /* fast_check_in_client */, false /* quiet_failures_for_tests */);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&CloseFD, lifeline_fds[0])));
// Make sure the writing end only exists in the broker process.
@@ -641,6 +695,8 @@ TEST(BrokerProcess, BrokerDiesOnClosedChannel) {
}
TEST(BrokerProcess, CreateFile) {
+ // Create two temporary files, grab their file names, and then delete the
+ // files themselves.
std::string temp_str;
std::string perm_str;
{
@@ -660,24 +716,27 @@ TEST(BrokerProcess, CreateFile) {
BrokerFilePermission::ReadWriteCreate(permfile_name),
};
- BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions);
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ BrokerType::SIGNAL_BASED);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
int fd = -1;
// Opening a temp file using O_CREAT but not O_EXCL must not be allowed
// by the broker so as to prevent spying on any pre-existing files.
- fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(tempfile_name,
+ O_RDWR | O_CREAT);
ASSERT_EQ(fd, -kFakeErrnoSentinel);
// Opening a temp file in a normal way must not be allowed by the broker,
// either.
- fd = open_broker.Open(tempfile_name, O_RDWR);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(tempfile_name, O_RDWR);
ASSERT_EQ(fd, -kFakeErrnoSentinel);
// Opening a temp file with both O_CREAT and O_EXCL is allowed since the
// file is known not to exist outside the scope of ScopedTemporaryFile.
- fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT | O_EXCL);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(
+ tempfile_name, O_RDWR | O_CREAT | O_EXCL);
ASSERT_GE(fd, 0);
close(fd);
@@ -688,26 +747,30 @@ TEST(BrokerProcess, CreateFile) {
// Opening a temp file with both O_CREAT and O_EXCL is allowed but fails
// per the OS when there is a conflict with a pre-existing file.
- fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT | O_EXCL);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(
+ tempfile_name, O_RDWR | O_CREAT | O_EXCL);
ASSERT_EQ(fd, -EEXIST);
// Opening a new permanent file without specifying O_EXCL is allowed.
- fd = open_broker.Open(permfile_name, O_RDWR | O_CREAT);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(permfile_name,
+ O_RDWR | O_CREAT);
ASSERT_GE(fd, 0);
close(fd);
// Opening an existing permanent file without specifying O_EXCL is allowed.
- fd = open_broker.Open(permfile_name, O_RDWR | O_CREAT);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(permfile_name,
+ O_RDWR | O_CREAT);
ASSERT_GE(fd, 0);
close(fd);
// Opening an existing file with O_EXCL is allowed but fails per the OS.
- fd = open_broker.Open(permfile_name, O_RDWR | O_CREAT | O_EXCL);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(
+ permfile_name, O_RDWR | O_CREAT | O_EXCL);
ASSERT_EQ(fd, -EEXIST);
const char kTestText[] = "TESTTESTTEST";
- fd = open_broker.Open(permfile_name, O_RDWR);
+ fd = open_broker.GetBrokerClientSignalBased()->Open(permfile_name, O_RDWR);
ASSERT_GE(fd, 0);
{
// Write to the descriptor opened by the broker and close.
@@ -755,12 +818,14 @@ void TestStatHelper(bool fast_check_in_client, bool follow_links) {
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnly(tempfile_name)};
BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
- permissions, fast_check_in_client);
+ permissions, BrokerType::SIGNAL_BASED,
+ fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(tempfile_name, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ tempfile_name, follow_links, &sb));
}
BrokerCommandSet command_set;
@@ -770,99 +835,122 @@ void TestStatHelper(bool fast_check_in_client, bool follow_links) {
// Nonexistent file with no permissions to see file.
std::vector<BrokerFilePermission> permissions;
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(nonesuch_name, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ nonesuch_name, follow_links, &sb));
}
{
// Actual file with no permission to see file.
std::vector<BrokerFilePermission> permissions;
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(tempfile_name, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ tempfile_name, follow_links, &sb));
}
{
// Nonexistent file with permissions to see file.
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnly(nonesuch_name)};
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
- EXPECT_EQ(-ENOENT, open_broker.Stat(nonesuch_name, follow_links, &sb));
+ EXPECT_EQ(-ENOENT, open_broker.GetBrokerClientSignalBased()->Stat(
+ nonesuch_name, follow_links, &sb));
// Gets denied all the way back to root since no create permission.
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(leading_path1, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ leading_path1, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(leading_path2, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ leading_path2, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(leading_path3, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ leading_path3, follow_links, &sb));
// Not fooled by substrings.
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(bad_leading_path1, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ bad_leading_path1, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(bad_leading_path2, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ bad_leading_path2, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(bad_leading_path3, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ bad_leading_path3, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(bad_leading_path4, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ bad_leading_path4, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(bad_leading_path5, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ bad_leading_path5, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(bad_leading_path6, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ bad_leading_path6, follow_links, &sb));
}
{
// Nonexistent file with permissions to create file.
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadWriteCreate(nonesuch_name)};
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
- EXPECT_EQ(-ENOENT, open_broker.Stat(nonesuch_name, follow_links, &sb));
+ EXPECT_EQ(-ENOENT, open_broker.GetBrokerClientSignalBased()->Stat(
+ nonesuch_name, follow_links, &sb));
// Gets ENOENT all the way back to root since it has create permission.
- EXPECT_EQ(-ENOENT, open_broker.Stat(leading_path1, follow_links, &sb));
- EXPECT_EQ(-ENOENT, open_broker.Stat(leading_path2, follow_links, &sb));
+ EXPECT_EQ(-ENOENT, open_broker.GetBrokerClientSignalBased()->Stat(
+ leading_path1, follow_links, &sb));
+ EXPECT_EQ(-ENOENT, open_broker.GetBrokerClientSignalBased()->Stat(
+ leading_path2, follow_links, &sb));
// But can always get the root.
- EXPECT_EQ(0, open_broker.Stat(leading_path3, follow_links, &sb));
+ EXPECT_EQ(0, open_broker.GetBrokerClientSignalBased()->Stat(
+ leading_path3, follow_links, &sb));
// Not fooled by substrings.
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(bad_leading_path1, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ bad_leading_path1, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(bad_leading_path2, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ bad_leading_path2, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(bad_leading_path3, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ bad_leading_path3, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(bad_leading_path4, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ bad_leading_path4, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(bad_leading_path5, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ bad_leading_path5, follow_links, &sb));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Stat(bad_leading_path6, follow_links, &sb));
+ open_broker.GetBrokerClientSignalBased()->Stat(
+ bad_leading_path6, follow_links, &sb));
}
{
// Actual file with permissions to see file.
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnly(tempfile_name)};
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
- EXPECT_EQ(0, open_broker.Stat(tempfile_name, follow_links, &sb));
+ EXPECT_EQ(0, open_broker.GetBrokerClientSignalBased()->Stat(
+ tempfile_name, follow_links, &sb));
// Following fields may never be consistent but should be non-zero.
// Don't trust the platform to define fields with any particular sign.
@@ -923,10 +1011,12 @@ void TestRenameHelper(bool fast_check_in_client) {
// Check rename fails with write permissions to both files but command
// itself is not allowed.
BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
- rwc_permissions, fast_check_in_client);
+ rwc_permissions, BrokerType::SIGNAL_BASED,
+ fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+ open_broker.GetBrokerClientSignalBased()->Rename(
+ oldpath.c_str(), newpath.c_str()));
// ... and no files moved around.
EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
@@ -940,10 +1030,11 @@ void TestRenameHelper(bool fast_check_in_client) {
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadWriteCreate(oldpath)};
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+ open_broker.GetBrokerClientSignalBased()->Rename(
+ oldpath.c_str(), newpath.c_str()));
// ... and no files moved around.
EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
@@ -954,10 +1045,11 @@ void TestRenameHelper(bool fast_check_in_client) {
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadWriteCreate(newpath)};
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+ open_broker.GetBrokerClientSignalBased()->Rename(
+ oldpath.c_str(), newpath.c_str()));
// ... and no files moved around.
EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
@@ -969,10 +1061,11 @@ void TestRenameHelper(bool fast_check_in_client) {
BrokerFilePermission::ReadOnly(oldpath),
BrokerFilePermission::ReadWriteCreate(newpath)};
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+ open_broker.GetBrokerClientSignalBased()->Rename(
+ oldpath.c_str(), newpath.c_str()));
// ... and no files moved around.
EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
@@ -984,10 +1077,11 @@ void TestRenameHelper(bool fast_check_in_client) {
BrokerFilePermission::ReadWriteCreate(oldpath),
BrokerFilePermission::ReadOnly(newpath)};
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+ open_broker.GetBrokerClientSignalBased()->Rename(
+ oldpath.c_str(), newpath.c_str()));
// ... and no files moved around.
EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
@@ -996,9 +1090,10 @@ void TestRenameHelper(bool fast_check_in_client) {
{
// Check rename passes with write permissions to both files.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(0, open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+ EXPECT_EQ(0, open_broker.GetBrokerClientSignalBased()->Rename(
+ oldpath.c_str(), newpath.c_str()));
// ... and files were moved around.
EXPECT_TRUE(access(oldpath.c_str(), F_OK) < 0);
@@ -1040,10 +1135,12 @@ void TestReadlinkHelper(bool fast_check_in_client) {
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnly(newpath_name)};
BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
- permissions, fast_check_in_client);
+ permissions, BrokerType::SIGNAL_BASED,
+ fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Readlink(newpath_name, buf, sizeof(buf)));
+ open_broker.GetBrokerClientSignalBased()->Readlink(
+ newpath_name, buf, sizeof(buf)));
}
BrokerCommandSet command_set;
@@ -1053,37 +1150,41 @@ void TestReadlinkHelper(bool fast_check_in_client) {
// Nonexistent file with no permissions to see file.
std::vector<BrokerFilePermission> permissions;
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Readlink(nonesuch_name, buf, sizeof(buf)));
+ open_broker.GetBrokerClientSignalBased()->Readlink(
+ nonesuch_name, buf, sizeof(buf)));
}
{
// Actual file with no permissions to see file.
std::vector<BrokerFilePermission> permissions;
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
EXPECT_EQ(-kFakeErrnoSentinel,
- open_broker.Readlink(newpath_name, buf, sizeof(buf)));
+ open_broker.GetBrokerClientSignalBased()->Readlink(
+ newpath_name, buf, sizeof(buf)));
}
{
// Nonexistent file with permissions to see file.
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnly(nonesuch_name)};
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-ENOENT, open_broker.Readlink(nonesuch_name, buf, sizeof(buf)));
+ EXPECT_EQ(-ENOENT, open_broker.GetBrokerClientSignalBased()->Readlink(
+ nonesuch_name, buf, sizeof(buf)));
}
{
// Actual file with permissions to see file.
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnly(newpath_name)};
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- ssize_t retlen = open_broker.Readlink(newpath_name, buf, sizeof(buf));
+ ssize_t retlen = open_broker.GetBrokerClientSignalBased()->Readlink(
+ newpath_name, buf, sizeof(buf));
EXPECT_TRUE(retlen == static_cast<ssize_t>(strlen(oldpath_name)));
EXPECT_EQ(0, memcmp(oldpath_name, buf, retlen));
}
@@ -1092,9 +1193,10 @@ void TestReadlinkHelper(bool fast_check_in_client) {
std::vector<BrokerFilePermission> permissions = {
BrokerFilePermission::ReadOnly(newpath_name)};
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-ENAMETOOLONG, open_broker.Readlink(newpath_name, buf, 4));
+ EXPECT_EQ(4, open_broker.GetBrokerClientSignalBased()->Readlink(
+ newpath_name, buf, 4));
}
// Cleanup both paths.
@@ -1136,9 +1238,11 @@ void TestMkdirHelper(bool fast_check_in_client) {
{
// Actual file with permissions to use but command itself not allowed.
BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
- rw_permissions, fast_check_in_client);
+ rw_permissions, BrokerType::SIGNAL_BASED,
+ fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(path_name, 0600));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Mkdir(path_name, 0600));
}
BrokerCommandSet command_set = MakeBrokerCommandSet({COMMAND_MKDIR});
@@ -1146,58 +1250,69 @@ void TestMkdirHelper(bool fast_check_in_client) {
{
// Nonexistent file with no permissions to see file.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, no_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(nonesuch_name, 0600));
+ EXPECT_EQ(
+ -kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Mkdir(nonesuch_name, 0600));
}
{
// Actual file with no permissions to see file.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, no_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(path_name, 0600));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Mkdir(path_name, 0600));
}
{
// Nonexistent file with insufficient permissions to see file.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, ro_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(nonesuch_name, 0600));
+ EXPECT_EQ(
+ -kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Mkdir(nonesuch_name, 0600));
}
{
// Actual file with insufficient permissions to see file.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, ro_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(path_name, 0600));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Mkdir(path_name, 0600));
}
{
// Nonexistent file with insufficient permissions to see file, case 2.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rw_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(nonesuch_name, 0600));
+ EXPECT_EQ(
+ -kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Mkdir(nonesuch_name, 0600));
}
{
// Actual file with insufficient permissions to see file, case 2.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rw_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(path_name, 0600));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Mkdir(path_name, 0600));
}
{
// Nonexistent file with permissions to see file.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-ENOENT, open_broker.Mkdir(nonesuch_name, 0600));
+ EXPECT_EQ(-ENOENT, open_broker.GetBrokerClientSignalBased()->Mkdir(
+ nonesuch_name, 0600));
}
{
// Actual file with permissions to see file.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(0, open_broker.Mkdir(path_name, 0600));
+ EXPECT_EQ(0,
+ open_broker.GetBrokerClientSignalBased()->Mkdir(path_name, 0600));
}
// Cleanup.
@@ -1241,9 +1356,11 @@ void TestRmdirHelper(bool fast_check_in_client) {
{
// Actual dir with permissions to use but command itself not allowed.
BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
- rw_permissions, fast_check_in_client);
+ rw_permissions, BrokerType::SIGNAL_BASED,
+ fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(path_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Rmdir(path_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
@@ -1252,72 +1369,79 @@ void TestRmdirHelper(bool fast_check_in_client) {
{
// Nonexistent dir with no permissions to see dir.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, no_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(nonesuch_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Rmdir(nonesuch_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Actual dir with no permissions to see dir.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, no_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(path_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Rmdir(path_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Nonexistent dir with insufficient permissions to see dir.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, ro_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(nonesuch_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Rmdir(nonesuch_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Actual dir with insufficient permissions to see dir.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, ro_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(path_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Rmdir(path_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Nonexistent dir with insufficient permissions to see dir, case 2.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rw_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(nonesuch_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Rmdir(nonesuch_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Actual dir with insufficient permissions to see dir, case 2.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rw_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(path_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Rmdir(path_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Nonexistent dir with permissions to see dir.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_TRUE(open_broker.Rmdir(nonesuch_name) < 0);
+ EXPECT_TRUE(open_broker.GetBrokerClientSignalBased()->Rmdir(nonesuch_name) <
+ 0);
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Actual dir with permissions to see dir.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(0, open_broker.Rmdir(path_name));
+ EXPECT_EQ(0, open_broker.GetBrokerClientSignalBased()->Rmdir(path_name));
}
// Confirm it was erased.
EXPECT_EQ(-1, access(path_name, F_OK));
@@ -1331,6 +1455,7 @@ TEST(BrokerProcess, RmdirHost) {
TestRmdirHelper(false);
}
+// Will have to split this into many tests, and the "cleanup" will have ASSERTs.
void TestUnlinkHelper(bool fast_check_in_client) {
std::string path;
{
@@ -1362,9 +1487,11 @@ void TestUnlinkHelper(bool fast_check_in_client) {
{
// Actual file with permissions to use but command itself not allowed.
BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
- rwc_permissions, fast_check_in_client);
+ rwc_permissions, BrokerType::SIGNAL_BASED,
+ fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(path_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Unlink(path_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
@@ -1373,72 +1500,79 @@ void TestUnlinkHelper(bool fast_check_in_client) {
{
// Nonexistent file with no permissions to see file.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, no_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(nonesuch_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Unlink(nonesuch_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Actual file with no permissions to see file.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, no_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(path_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Unlink(path_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Nonexistent file with insufficient permissions to see file.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, ro_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(nonesuch_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Unlink(nonesuch_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Actual file with insufficient permissions to see file.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, ro_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(path_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Unlink(path_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Nonexistent file with insufficient permissions to see file, case 2.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rw_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(nonesuch_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Unlink(nonesuch_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Actual file with insufficient permissions to see file, case 2.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rw_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(path_name));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.GetBrokerClientSignalBased()->Unlink(path_name));
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Nonexistent file with permissions to see file.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_TRUE(open_broker.Unlink(nonesuch_name) < 0);
+ EXPECT_TRUE(
+ open_broker.GetBrokerClientSignalBased()->Unlink(nonesuch_name) < 0);
}
EXPECT_EQ(0, access(path_name, F_OK));
{
// Actual file with permissions to see file.
BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
- fast_check_in_client);
+ BrokerType::SIGNAL_BASED, fast_check_in_client);
ASSERT_TRUE(open_broker.Init(base::BindOnce(&NoOpCallback)));
- EXPECT_EQ(0, open_broker.Unlink(path_name));
+ EXPECT_EQ(0, open_broker.GetBrokerClientSignalBased()->Unlink(path_name));
}
// Confirm it was erased.
EXPECT_EQ(-1, access(path_name, F_OK));
@@ -1453,80 +1587,110 @@ TEST(BrokerProcess, UnlinkHost) {
}
TEST(BrokerProcess, IsSyscallAllowed) {
- const struct {
- int sysno;
- BrokerCommand command;
- } kSyscallToCommandMap[] = {
+ const base::flat_map<BrokerCommand, base::flat_set<int>> kSysnosForCommand = {
+ {COMMAND_ACCESS,
+ {__NR_faccessat,
#if defined(__NR_access)
- {__NR_access, COMMAND_ACCESS},
+ __NR_access
#endif
- {__NR_faccessat, COMMAND_ACCESS},
+ }},
+ {COMMAND_MKDIR,
+ {__NR_mkdirat,
#if defined(__NR_mkdir)
- {__NR_mkdir, COMMAND_MKDIR},
+ __NR_mkdir
#endif
- {__NR_mkdirat, COMMAND_MKDIR},
+ }},
+ {COMMAND_OPEN,
+ {__NR_openat,
#if defined(__NR_open)
- {__NR_open, COMMAND_OPEN},
+ __NR_open
#endif
- {__NR_openat, COMMAND_OPEN},
+ }},
+ {COMMAND_READLINK,
+ {__NR_readlinkat,
#if defined(__NR_readlink)
- {__NR_readlink, COMMAND_READLINK},
+ __NR_readlink
#endif
- {__NR_readlinkat, COMMAND_READLINK},
+ }},
+ {COMMAND_RENAME,
+ {__NR_renameat,
#if defined(__NR_rename)
- {__NR_rename, COMMAND_RENAME},
+ __NR_rename
#endif
- {__NR_renameat, COMMAND_RENAME},
+ }},
+ {COMMAND_UNLINK,
+ {__NR_unlinkat,
+#if defined(__NR_unlink)
+ __NR_unlink
+#endif
+ }},
+ {COMMAND_RMDIR,
+ {__NR_unlinkat,
#if defined(__NR_rmdir)
- {__NR_rmdir, COMMAND_RMDIR},
+ __NR_rmdir
#endif
+ }},
+ {COMMAND_STAT,
+ {
#if defined(__NR_stat)
- {__NR_stat, COMMAND_STAT},
+ __NR_stat,
#endif
#if defined(__NR_lstat)
- {__NR_lstat, COMMAND_STAT},
+ __NR_lstat,
#endif
#if defined(__NR_fstatat)
- {__NR_fstatat, COMMAND_STAT},
+ __NR_fstatat,
+#endif
+#if defined(__NR_fstatat64)
+ __NR_fstatat64,
#endif
#if defined(__NR_newfstatat)
- {__NR_newfstatat, COMMAND_STAT},
+ __NR_newfstatat,
#endif
#if defined(__NR_stat64)
- {__NR_stat64, COMMAND_STAT},
+ __NR_stat64,
#endif
#if defined(__NR_lstat64)
- {__NR_lstat64, COMMAND_STAT},
+ __NR_lstat64,
#endif
-#if defined(__NR_unlink)
- {__NR_unlink, COMMAND_UNLINK},
-#endif
- {__NR_unlinkat, COMMAND_UNLINK},
- };
+ }}};
+
+ // First gather up all the syscalls numbers we want to test.
+ base::flat_set<int> all_sysnos;
+ for (const auto& command_sysno_set_pair : kSysnosForCommand) {
+ all_sysnos.insert(command_sysno_set_pair.second.begin(),
+ command_sysno_set_pair.second.end());
+ }
- for (const auto& test : kSyscallToCommandMap) {
+ for (const auto& test : kSysnosForCommand) {
// Test with fast_check_in_client.
{
- SCOPED_TRACE(base::StringPrintf("fast check, sysno=%d", test.sysno));
- BrokerProcess process(ENOSYS, MakeBrokerCommandSet({test.command}), {},
- true, true);
- EXPECT_TRUE(process.IsSyscallAllowed(test.sysno));
- for (const auto& other : kSyscallToCommandMap) {
- SCOPED_TRACE(base::StringPrintf("others test, sysno=%d", other.sysno));
- EXPECT_EQ(other.command == test.command,
- process.IsSyscallAllowed(other.sysno));
+ BrokerCommand command = test.first;
+ const base::flat_set<int>& sysnos = test.second;
+ SCOPED_TRACE(base::StringPrintf("fast check, command=%d", command));
+ BrokerProcess process(ENOSYS, MakeBrokerCommandSet({command}), {},
+ BrokerType::SIGNAL_BASED,
+ /*fast_check_in_client=*/true,
+ /*quiet_failures_for_tests=*/true);
+ // Check that only the correct system calls are allowed.
+ for (int sysno : all_sysnos) {
+ SCOPED_TRACE(base::StringPrintf("test syscalls, sysno=%d", sysno));
+ EXPECT_EQ(sysnos.count(sysno) > 0, process.IsSyscallAllowed(sysno));
}
}
// Test without fast_check_in_client.
{
- SCOPED_TRACE(base::StringPrintf("no fast check, sysno=%d", test.sysno));
- BrokerProcess process(ENOSYS, MakeBrokerCommandSet({test.command}), {},
- false, true);
- EXPECT_TRUE(process.IsSyscallAllowed(test.sysno));
- for (const auto& other : kSyscallToCommandMap) {
- SCOPED_TRACE(base::StringPrintf("others test, sysno=%d", other.sysno));
- EXPECT_TRUE(process.IsSyscallAllowed(other.sysno));
+ BrokerCommand command = test.first;
+ SCOPED_TRACE(base::StringPrintf("no fast check, command=%d", command));
+ BrokerProcess process(ENOSYS, MakeBrokerCommandSet({command}), {},
+ BrokerType::SIGNAL_BASED,
+ /*fast_check_in_client=*/false,
+ /*quiet_failures_for_tests=*/true);
+ // Check that all system calls are allowed.
+ for (int sysno : all_sysnos) {
+ SCOPED_TRACE(base::StringPrintf("test syscalls, sysno=%d", sysno));
+ EXPECT_TRUE(process.IsSyscallAllowed(sysno));
}
}
}
diff --git a/chromium/sandbox/linux/syscall_broker/broker_simple_message.cc b/chromium/sandbox/linux/syscall_broker/broker_simple_message.cc
index 6b447a4ad03..193b485aeff 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_simple_message.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_simple_message.cc
@@ -10,6 +10,7 @@
#include <unistd.h>
#include "base/check_op.h"
+#include "base/containers/span.h"
#include "base/files/scoped_file.h"
#include "base/notreached.h"
#include "base/numerics/safe_math.h"
@@ -22,19 +23,22 @@ namespace sandbox {
namespace syscall_broker {
-BrokerSimpleMessage::BrokerSimpleMessage()
- : read_only_(false),
- write_only_(false),
- broken_(false),
- length_(0),
- read_next_(message_),
- write_next_(message_) {}
-
ssize_t BrokerSimpleMessage::SendRecvMsgWithFlags(int fd,
int recvmsg_flags,
- int* result_fd,
+ base::ScopedFD* result_fd,
BrokerSimpleMessage* reply) {
+ return SendRecvMsgWithFlagsMultipleFds(fd, recvmsg_flags, {}, {result_fd, 1},
+ reply);
+}
+
+ssize_t BrokerSimpleMessage::SendRecvMsgWithFlagsMultipleFds(
+ int fd,
+ int recvmsg_flags,
+ base::span<const int> send_fds,
+ base::span<base::ScopedFD> result_fds,
+ BrokerSimpleMessage* reply) {
RAW_CHECK(reply);
+ RAW_CHECK(send_fds.size() + 1 <= base::UnixDomainSocket::kMaxFileDescriptors);
// This socketpair is only used for the IPC and is cleaned up before
// returning.
@@ -43,48 +47,68 @@ ssize_t BrokerSimpleMessage::SendRecvMsgWithFlags(int fd,
if (!base::CreateSocketPair(&recv_sock, &send_sock))
return -1;
- if (!SendMsg(fd, send_sock.get()))
+ int send_fds_with_reply_socket[base::UnixDomainSocket::kMaxFileDescriptors];
+ send_fds_with_reply_socket[0] = send_sock.get();
+ for (size_t i = 0; i < send_fds.size(); i++) {
+ send_fds_with_reply_socket[i + 1] = send_fds[i];
+ }
+ if (!SendMsgMultipleFds(fd,
+ {send_fds_with_reply_socket, send_fds.size() + 1})) {
return -1;
+ }
// Close the sending end of the socket right away so that if our peer closes
// it before sending a response (e.g., from exiting), RecvMsgWithFlags() will
// return EOF instead of hanging.
send_sock.reset();
- base::ScopedFD recv_fd;
- const ssize_t reply_len =
- reply->RecvMsgWithFlags(recv_sock.get(), recvmsg_flags, &recv_fd);
+ const ssize_t reply_len = reply->RecvMsgWithFlagsMultipleFds(
+ recv_sock.get(), recvmsg_flags, result_fds);
recv_sock.reset();
if (reply_len == -1)
return -1;
- if (result_fd)
- *result_fd = (recv_fd == -1) ? -1 : recv_fd.release();
-
return reply_len;
}
bool BrokerSimpleMessage::SendMsg(int fd, int send_fd) {
+ return SendMsgMultipleFds(
+ fd, send_fd == -1 ? base::span<int>() : base::span<int>(&send_fd, 1));
+}
+
+bool BrokerSimpleMessage::SendMsgMultipleFds(int fd,
+ base::span<const int> send_fds) {
if (broken_)
return false;
+ RAW_CHECK(send_fds.size() <= base::UnixDomainSocket::kMaxFileDescriptors);
+
struct msghdr msg = {};
const void* buf = reinterpret_cast<const void*>(message_);
struct iovec iov = {const_cast<void*>(buf), length_};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
- const unsigned control_len = CMSG_SPACE(sizeof(send_fd));
+ const unsigned control_len = CMSG_SPACE(send_fds.size() * sizeof(int));
char control_buffer[control_len];
- if (send_fd >= 0) {
+ if (send_fds.size() >= 1) {
struct cmsghdr* cmsg;
msg.msg_control = control_buffer;
msg.msg_controllen = control_len;
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
- memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(send_fd));
+ int len = 0;
+
+ for (size_t i = 0; i < send_fds.size(); i++) {
+ if (send_fds[i] < 0)
+ return false;
+
+ // CMSG_DATA() not guaranteed to be aligned so this must use memcpy.
+ memcpy(CMSG_DATA(cmsg) + (sizeof(int) * i), &send_fds[i], sizeof(int));
+ len += sizeof(int);
+ }
+ cmsg->cmsg_len = CMSG_LEN(len);
msg.msg_controllen = cmsg->cmsg_len;
}
@@ -100,8 +124,18 @@ bool BrokerSimpleMessage::SendMsg(int fd, int send_fd) {
ssize_t BrokerSimpleMessage::RecvMsgWithFlags(int fd,
int flags,
base::ScopedFD* return_fd) {
+ ssize_t ret = RecvMsgWithFlagsMultipleFds(
+ fd, flags, base::span<base::ScopedFD>(return_fd, 1));
+ return ret;
+}
+
+ssize_t BrokerSimpleMessage::RecvMsgWithFlagsMultipleFds(
+ int fd,
+ int flags,
+ base::span<base::ScopedFD> return_fds) {
// The message must be fresh and unused.
RAW_CHECK(!read_only_ && !write_only_);
+ RAW_CHECK(return_fds.size() <= base::UnixDomainSocket::kMaxFileDescriptors);
read_only_ = true; // The message should not be written to again.
struct msghdr msg = {};
struct iovec iov = {message_, kMaxMessageLength};
@@ -126,7 +160,7 @@ ssize_t BrokerSimpleMessage::RecvMsgWithFlags(int fd,
if (r == -1)
return -1;
- int* wire_fds = NULL;
+ int* wire_fds = nullptr;
size_t wire_fds_len = 0;
base::ProcessId pid = -1;
@@ -136,7 +170,7 @@ ssize_t BrokerSimpleMessage::RecvMsgWithFlags(int fd,
const size_t payload_len = cmsg->cmsg_len - CMSG_LEN(0);
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
DCHECK_EQ(payload_len % sizeof(fd), 0u);
- DCHECK_EQ(wire_fds, static_cast<void*>(nullptr));
+ DCHECK_EQ(wire_fds, nullptr);
wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
wire_fds_len = payload_len / sizeof(fd);
}
@@ -162,9 +196,10 @@ ssize_t BrokerSimpleMessage::RecvMsgWithFlags(int fd,
}
if (wire_fds) {
- if (wire_fds_len > 1) {
- // Only one FD is accepted by this receive.
- for (unsigned i = 0; i < wire_fds_len; ++i) {
+ if (wire_fds_len > return_fds.size()) {
+ // The number of fds received is limited to return_fds.size(). If there
+ // are more in the message than expected, close them and return an error.
+ for (size_t i = 0; i < wire_fds_len; ++i) {
close(wire_fds[i]);
}
errno = EMSGSIZE;
@@ -172,7 +207,9 @@ ssize_t BrokerSimpleMessage::RecvMsgWithFlags(int fd,
return -1;
}
- *return_fd = base::ScopedFD(wire_fds[0]);
+ for (size_t i = 0; i < wire_fds_len; ++i) {
+ return_fds[i] = base::ScopedFD(wire_fds[i]);
+ }
}
// At this point, |r| is guaranteed to be >= 0.
diff --git a/chromium/sandbox/linux/syscall_broker/broker_simple_message.h b/chromium/sandbox/linux/syscall_broker/broker_simple_message.h
index d9185b43a77..6203c999591 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_simple_message.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_simple_message.h
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include "base/containers/span.h"
#include "base/files/scoped_file.h"
#include "sandbox/sandbox_export.h"
@@ -26,25 +27,46 @@ namespace syscall_broker {
// that reduces the code duplication.
class SANDBOX_EXPORT BrokerSimpleMessage {
public:
- BrokerSimpleMessage();
+ BrokerSimpleMessage() = default;
// Signal-safe
+ // A synchronous version of SendMsg/RecvMsgWithFlags that creates and sends a
+ // temporary IPC socket over |fd|, then listens for a response on the IPC
+ // socket using reply->RecvMsgWithFlags(temporary_ipc_socket, recvmsg_flags,
+ // result_fd);
ssize_t SendRecvMsgWithFlags(int fd,
int recvmsg_flags,
- int* send_fd,
+ base::ScopedFD* result_fd,
BrokerSimpleMessage* reply);
+ // Same as SendRecvMsgWithFlags(), but allows sending and receiving a variable
+ // number of fds. The temporary IPC return socket is always sent as the first
+ // fd in the cmsg.
+ ssize_t SendRecvMsgWithFlagsMultipleFds(int fd,
+ int recvmsg_flags,
+ base::span<const int> send_fds,
+ base::span<base::ScopedFD> result_fds,
+ BrokerSimpleMessage* reply);
+
// Use sendmsg to write the given msg and the file descriptor |send_fd|.
// Returns true if successful. Signal-safe.
bool SendMsg(int fd, int send_fd);
+ // Same as SendMsg() but allows sending more than one fd.
+ bool SendMsgMultipleFds(int fd, base::span<const int> send_fds);
+
// Similar to RecvMsg, but allows to specify |flags| for recvmsg(2).
// Guaranteed to return either 1 or 0 fds. Signal-safe.
ssize_t RecvMsgWithFlags(int fd, int flags, base::ScopedFD* return_fd);
+ // Same as RecvMsgWithFlags() but allows receiving more than one fd.
+ ssize_t RecvMsgWithFlagsMultipleFds(int fd,
+ int flags,
+ base::span<base::ScopedFD> return_fds);
+
// Adds a NUL-terminated C-style string to the message as a raw buffer.
- // Returns true if the internal message buffer has room for the data, and the
- // data is successfully appended.
+ // Returns true if the internal message buffer has room for the data, and
+ // the data is successfully appended.
bool AddStringToMessage(const char* string);
// Adds a raw data buffer to the message. If the raw data is actually a
@@ -53,8 +75,8 @@ class SANDBOX_EXPORT BrokerSimpleMessage {
// data, and the data is successfully appended.
bool AddDataToMessage(const char* buffer, size_t length);
- // Adds an int to the message. Returns true if the internal message buffer has
- // room for the int and the int is successfully added.
+ // Adds an int to the message. Returns true if the internal message buffer
+ // has room for the int and the int is successfully added.
bool AddIntToMessage(int int_to_add);
// This returns a pointer to the next available data buffer in |data|. The
@@ -63,8 +85,8 @@ class SANDBOX_EXPORT BrokerSimpleMessage {
bool ReadString(const char** string);
// This returns a pointer to the next available data buffer in the message
- // in |data|, and the length of the buffer in |length|. The buffer is owned by
- // |this| class.
+ // in |data|, and the length of the buffer in |length|. The buffer is owned
+ // by |this| class.
bool ReadData(const char** data, size_t* length);
// This reads the next available int from the message and stores it in
@@ -79,25 +101,25 @@ class SANDBOX_EXPORT BrokerSimpleMessage {
enum class EntryType : uint32_t { DATA = 0xBDBDBD80, INT = 0xBDBDBD81 };
- // Returns whether or not the next available entry matches the expected entry
- // type.
+ // Returns whether or not the next available entry matches the expected
+ // entry type.
bool ValidateType(EntryType expected_type);
// Set to true once a message is read from, it may never be written to.
- bool read_only_;
+ bool read_only_ = false;
// Set to true once a message is written to, it may never be read from.
- bool write_only_;
+ bool write_only_ = false;
// Set when an operation fails, so that all subsequed operations fail,
// including any attempt to send the broken message.
- bool broken_;
+ bool broken_ = false;
// The current length of the contents in the |message_| buffer.
- size_t length_;
- // The pointer to the next location in the |message_| buffer to read from.
- uint8_t* read_next_;
- // The pointer to the next location in the |message_| buffer to write from.
- uint8_t* write_next_;
+ size_t length_ = 0;
// The statically allocated buffer of size |kMaxMessageLength|.
uint8_t message_[kMaxMessageLength];
+ // The pointer to the next location in the |message_| buffer to read from.
+ uint8_t* read_next_ = message_;
+ // The pointer to the next location in the |message_| buffer to write from.
+ uint8_t* write_next_ = message_;
};
} // namespace syscall_broker
diff --git a/chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc b/chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc
index 9d600ecf0c2..721617db22c 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc
@@ -4,14 +4,25 @@
#include "sandbox/linux/syscall_broker/broker_simple_message.h"
+#include <linux/kcmp.h>
+#include <unistd.h>
+
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "base/callback_forward.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
#include "base/macros.h"
-#include "base/single_thread_task_runner.h"
+#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
+#include "base/task/thread_pool.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_timeouts.h"
#include "base/threading/thread.h"
#include "sandbox/linux/syscall_broker/broker_channel.h"
#include "sandbox/linux/syscall_broker/broker_simple_message.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
#include "sandbox/linux/tests/test_utils.h"
#include "sandbox/linux/tests/unit_tests.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -549,7 +560,7 @@ TEST(BrokerSimpleMessage, SendRecvMsgSynchronous) {
BrokerSimpleMessage send_message;
send_message.AddDataToMessage(data1, strlen(data1) + 1);
BrokerSimpleMessage reply_message;
- int returned_fd;
+ base::ScopedFD returned_fd;
ssize_t len = send_message.SendRecvMsgWithFlags(
ipc_writer.get(), 0, &returned_fd, &reply_message);
@@ -582,7 +593,7 @@ TEST(BrokerSimpleMessage, SendRecvMsgSynchronous) {
BrokerSimpleMessage send_message;
send_message.AddIntToMessage(int1);
BrokerSimpleMessage reply_message;
- int returned_fd;
+ base::ScopedFD returned_fd;
ssize_t len = send_message.SendRecvMsgWithFlags(
ipc_writer.get(), 0, &returned_fd, &reply_message);
@@ -617,7 +628,7 @@ TEST(BrokerSimpleMessage, SendRecvMsgSynchronous) {
send_message.AddDataToMessage(data1, strlen(data1) + 1);
send_message.AddIntToMessage(int1);
BrokerSimpleMessage reply_message;
- int returned_fd;
+ base::ScopedFD returned_fd;
ssize_t len = send_message.SendRecvMsgWithFlags(
ipc_writer.get(), 0, &returned_fd, &reply_message);
@@ -657,7 +668,7 @@ TEST(BrokerSimpleMessage, SendRecvMsgSynchronous) {
send_message.AddIntToMessage(int2);
send_message.AddDataToMessage(data2, strlen(data2) + 1);
BrokerSimpleMessage reply_message;
- int returned_fd;
+ base::ScopedFD returned_fd;
ssize_t len = send_message.SendRecvMsgWithFlags(
ipc_writer.get(), 0, &returned_fd, &reply_message);
@@ -688,7 +699,7 @@ TEST(BrokerSimpleMessage, SendRecvMsgSynchronous) {
EXPECT_TRUE(send_message.AddIntToMessage(5));
EXPECT_TRUE(send_message.AddStringToMessage("test"));
BrokerSimpleMessage reply_message;
- int returned_fd;
+ base::ScopedFD returned_fd;
ssize_t len = send_message.SendRecvMsgWithFlags(
ipc_writer.get(), 0, &returned_fd, &reply_message);
@@ -698,6 +709,182 @@ TEST(BrokerSimpleMessage, SendRecvMsgSynchronous) {
}
}
+namespace {
+// Adds a gtest failure and returns false iff any of the following conditions
+// are true:
+// 1. |fd1| or |fd2| are invalid fds
+// 2. Kcmp fails
+// 3. fd1 and fd2 do not compare equal under kcmp.
+bool CheckKcmpResult(int fd1, int fd2) {
+ if (fd1 < 0) {
+ ADD_FAILURE() << "fd1 invalid";
+ return false;
+ }
+ if (fd2 < 0) {
+ ADD_FAILURE() << "fd2 invalid";
+ return false;
+ }
+ pid_t pid = getpid();
+ int ret = syscall(__NR_kcmp, pid, pid, KCMP_FILE, fd1, fd2);
+ if (ret < 0) {
+ ADD_FAILURE() << "Kcmp failed, errno = " << errno;
+ return false;
+ }
+ if (ret != 0) {
+ ADD_FAILURE() << "File description did not compare equal to stdout. Kcmp("
+ << fd1 << ", " << fd2 << ") = " << ret;
+ return false;
+ }
+
+ return true;
+}
+
+// Receives an fd over |ipc_reader|, and if it does not point to the same
+// description as stdout, prints a message and returns false.
+// On any other error, also prints a message and returns false.
+void ReceiveStdoutDupFd(BrokerChannel::EndPoint* ipc_reader) {
+ // Receive an fd from |ipc_reader|.
+ base::ScopedFD recv_fd;
+
+ BrokerSimpleMessage msg;
+ ssize_t len = msg.RecvMsgWithFlags(ipc_reader->get(), 0, &recv_fd);
+ ASSERT_GE(len, 0) << "Error on RecvMsgWithFlags, errno = " << errno;
+
+ CheckKcmpResult(STDOUT_FILENO, recv_fd.get());
+}
+
+void ReceiveTwoDupFds(BrokerChannel::EndPoint* ipc_reader) {
+ // Receive two fds from |ipc_reader|.
+ BrokerSimpleMessage msg;
+ base::ScopedFD recv_fds[2];
+ ssize_t len =
+ msg.RecvMsgWithFlagsMultipleFds(ipc_reader->get(), 0, {recv_fds});
+ ASSERT_GE(len, 0) << "Error on RecvMsgWithFlags, errno = " << errno;
+
+ CheckKcmpResult(STDOUT_FILENO, recv_fds[0].get());
+ CheckKcmpResult(STDIN_FILENO, recv_fds[1].get());
+}
+
+void ReceiveThreeFdsSendTwoBack(BrokerChannel::EndPoint* ipc_reader) {
+ // Receive two fds from |ipc_reader|.
+ BrokerSimpleMessage msg;
+ base::ScopedFD recv_fds[3];
+ ssize_t len =
+ msg.RecvMsgWithFlagsMultipleFds(ipc_reader->get(), 0, {recv_fds});
+ ASSERT_GE(len, 0) << "Error on RecvMsgWithFlags, errno = " << errno;
+ ASSERT_TRUE(recv_fds[0].is_valid());
+
+ if (!CheckKcmpResult(STDOUT_FILENO, recv_fds[1].get()) ||
+ !CheckKcmpResult(STDIN_FILENO, recv_fds[2].get())) {
+ return;
+ }
+
+ BrokerSimpleMessage resp;
+ int send_fds[2];
+ send_fds[0] = recv_fds[1].get();
+ send_fds[1] = recv_fds[2].get();
+ resp.AddIntToMessage(0); // Dummy int to send message
+ ASSERT_TRUE(resp.SendMsgMultipleFds(recv_fds[0].get(), {send_fds}));
+}
+} // namespace
+
+class BrokerSimpleMessageFdTest : public testing::Test {
+ public:
+ void SetUp() override {
+#if !defined(SANDBOX_USES_BASE_TEST_SUITE)
+ // TaskEnvironment requires initialized TestTimeouts, which are already
+ // enabled if using the base test suite.
+ TestTimeouts::Initialize();
+#endif
+ task_environment_ = std::make_unique<base::test::TaskEnvironment>();
+ }
+
+ bool SkipIfKcmpNotSupported() {
+ pid_t pid = getpid();
+ if (syscall(__NR_kcmp, pid, pid, KCMP_FILE, STDOUT_FILENO, STDOUT_FILENO) <
+ 0) {
+ LOG(INFO) << "Skipping test, kcmp not supported.";
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ std::unique_ptr<base::test::TaskEnvironment> task_environment_;
+};
+
+// Passes one fd with RecvMsg, SendMsg.
+TEST_F(BrokerSimpleMessageFdTest, PassOneFd) {
+ if (!SkipIfKcmpNotSupported())
+ return;
+
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+ base::RunLoop run_loop;
+
+ base::ThreadPool::PostTaskAndReply(
+ FROM_HERE, base::BindOnce(&ReceiveStdoutDupFd, &ipc_reader),
+ run_loop.QuitClosure());
+
+ BrokerSimpleMessage msg;
+ msg.AddIntToMessage(0); // Must add a dummy value to send the message.
+ ASSERT_TRUE(msg.SendMsg(ipc_writer.get(), STDOUT_FILENO));
+
+ run_loop.Run();
+}
+
+TEST_F(BrokerSimpleMessageFdTest, PassTwoFds) {
+ if (!SkipIfKcmpNotSupported())
+ return;
+
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+ base::RunLoop run_loop;
+
+ base::ThreadPool::PostTaskAndReply(
+ FROM_HERE, base::BindOnce(&ReceiveTwoDupFds, &ipc_reader),
+ run_loop.QuitClosure());
+
+ BrokerSimpleMessage msg;
+ msg.AddIntToMessage(0); // Must add a dummy value to send the message.
+ int send_fds[2];
+ send_fds[0] = STDOUT_FILENO;
+ send_fds[1] = STDIN_FILENO;
+ ASSERT_TRUE(msg.SendMsgMultipleFds(ipc_writer.get(), {send_fds}));
+
+ run_loop.Run();
+}
+
+TEST_F(BrokerSimpleMessageFdTest, SynchronousPassTwoFds) {
+ if (!SkipIfKcmpNotSupported())
+ return;
+
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+ base::RunLoop run_loop;
+
+ base::ThreadPool::PostTaskAndReply(
+ FROM_HERE, base::BindOnce(&ReceiveThreeFdsSendTwoBack, &ipc_reader),
+ run_loop.QuitClosure());
+
+ BrokerSimpleMessage msg, reply;
+ msg.AddIntToMessage(0); // Must add a dummy value to send the message.
+ int send_fds[2];
+ send_fds[0] = STDOUT_FILENO;
+ send_fds[1] = STDIN_FILENO;
+ base::ScopedFD result_fds[2];
+ msg.SendRecvMsgWithFlagsMultipleFds(ipc_writer.get(), 0, {send_fds},
+ {result_fds}, &reply);
+
+ run_loop.Run();
+
+ ASSERT_TRUE(CheckKcmpResult(STDOUT_FILENO, result_fds[0].get()));
+ ASSERT_TRUE(CheckKcmpResult(STDIN_FILENO, result_fds[1].get()));
+}
+
} // namespace syscall_broker
} // namespace sandbox
diff --git a/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler.cc b/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler.cc
new file mode 100644
index 00000000000..f73113c771a
--- /dev/null
+++ b/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler.cc
@@ -0,0 +1,146 @@
+// Copyright 2020 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/syscall_broker/remote_syscall_arg_handler.h"
+
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "base/bits.h"
+#include "base/check_op.h"
+#include "base/containers/span.h"
+#include "base/logging.h"
+#include "base/process/process_metrics.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+#if defined(MEMORY_SANITIZER)
+#include <sanitizer/msan_interface.h>
+#endif
+
+namespace sandbox {
+namespace syscall_broker {
+
+RemoteProcessIOResult WriteRemoteData(pid_t pid,
+ uintptr_t remote_addr,
+ size_t remote_size,
+ base::span<char> data) {
+ CHECK_GE(remote_size, data.size());
+
+ base::span<char> remote_span(reinterpret_cast<char*>(remote_addr),
+ remote_size);
+ struct iovec local_iov = {};
+ struct iovec remote_iov = {};
+
+ while (!data.empty()) {
+ local_iov.iov_base = data.data();
+ local_iov.iov_len = data.size();
+ remote_iov.iov_base = remote_span.data();
+ remote_iov.iov_len = data.size();
+
+ ssize_t bytes_written = syscall(__NR_process_vm_writev, pid, &local_iov,
+ 1ul, &remote_iov, 1ul, 0ul);
+ if (bytes_written < 0) {
+ if (errno == EFAULT)
+ return RemoteProcessIOResult::kRemoteMemoryInvalid;
+ if (errno == ESRCH)
+ return RemoteProcessIOResult::kRemoteExited;
+ PLOG(ERROR)
+ << "process_vm_writev() failed with unknown error code! Write to pid "
+ << pid << " at remote address " << remote_iov.iov_base
+ << " of length " << data.size() << ". ";
+ return RemoteProcessIOResult::kUnknownError;
+ }
+
+ remote_span = remote_span.subspan(bytes_written);
+ data = data.subspan(bytes_written);
+ }
+
+ return RemoteProcessIOResult::kSuccess;
+}
+
+RemoteProcessIOResult ReadFilePathFromRemoteProcess(pid_t pid,
+ const void* remote_addr,
+ std::string* out_str) {
+ // Most pathnames will be small so avoid copying PATH_MAX bytes every time,
+ // by reading in chunks and checking if the the string ends within the
+ // chunk.
+ char buffer[PATH_MAX];
+ base::span<char> buffer_span(buffer);
+
+ struct iovec local_iov = {};
+ struct iovec remote_iov = {};
+
+ uintptr_t remote_ptr = reinterpret_cast<uintptr_t>(remote_addr);
+
+ for (;;) {
+ uintptr_t bytes_left_in_page = internal::NumBytesLeftInPage(remote_ptr);
+
+ // Read the minimum of the chunk size, remaining local buffer size, and
+ // the number of bytes left in the remote page.
+ size_t bytes_to_read = std::min(
+ {internal::kNumBytesPerChunk, buffer_span.size(), bytes_left_in_page});
+
+ // Set up the iovecs.
+ local_iov.iov_base = buffer_span.data();
+ local_iov.iov_len = bytes_to_read;
+
+ remote_iov.iov_base = reinterpret_cast<void*>(remote_ptr);
+ remote_iov.iov_len = bytes_to_read;
+
+ // The arguments below must include the ul suffix since they need to be
+ // 64-bit values, but syscall() takes varargs and doesn't know to promote
+ // them from 32-bit to 64-bit.
+ ssize_t bytes_read = syscall(__NR_process_vm_readv, pid, &local_iov, 1ul,
+ &remote_iov, 1ul, 0ul);
+ if (bytes_read < 0) {
+ if (errno == EFAULT)
+ return RemoteProcessIOResult::kRemoteMemoryInvalid;
+ if (errno == ESRCH)
+ return RemoteProcessIOResult::kRemoteExited;
+ PLOG(ERROR)
+ << "process_vm_readv() failed with unknown error code! Read from pid "
+ << pid << " at remote address " << remote_iov.iov_base
+ << " of length " << bytes_to_read << ". ";
+ return RemoteProcessIOResult::kUnknownError;
+ }
+
+ // We successfully performed a read.
+#if defined(MEMORY_SANITIZER)
+ // Msan does not hook syscall(__NR_process_vm_readv, ...)
+ __msan_unpoison(local_iov.iov_base, bytes_read);
+#endif
+ remote_ptr += bytes_read;
+ buffer_span = buffer_span.subspan(bytes_read);
+
+ // Check for null byte.
+ char* null_byte_ptr =
+ static_cast<char*>(memchr(local_iov.iov_base, '\0', bytes_read));
+ if (null_byte_ptr) {
+ *out_str = std::string(buffer, null_byte_ptr);
+ return RemoteProcessIOResult::kSuccess;
+ }
+
+ if (buffer_span.empty()) {
+ // If we haven't found a null byte yet and our available buffer space is
+ // empty, stop.
+ LOG(ERROR) << "Read PATH_MAX bytes in sandboxed process and did not find "
+ "expected null byte.";
+ return RemoteProcessIOResult::kExceededPathMax;
+ }
+ }
+}
+
+namespace internal {
+uintptr_t NumBytesLeftInPage(uintptr_t addr) {
+ const uintptr_t page_end = base::bits::Align(addr + 1, base::GetPageSize());
+ return page_end - addr;
+}
+} // namespace internal
+} // namespace syscall_broker
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler.h b/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler.h
new file mode 100644
index 00000000000..3c06287b7eb
--- /dev/null
+++ b/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler.h
@@ -0,0 +1,52 @@
+// Copyright 2020 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.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_REMOTE_SYSCALL_ARG_HANDLER_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_REMOTE_SYSCALL_ARG_HANDLER_H_
+
+#include <unistd.h>
+
+#include "base/containers/span.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace syscall_broker {
+
+enum class RemoteProcessIOResult {
+ kSuccess,
+ kRemoteExited,
+ kExceededPathMax,
+ kRemoteMemoryInvalid,
+ kUnknownError
+};
+
+// Writes |data| at |remote_addr| in |pid|'s address space. Returns the
+// appropriate result.
+SANDBOX_EXPORT RemoteProcessIOResult WriteRemoteData(pid_t pid,
+ uintptr_t remote_addr,
+ size_t remote_size,
+ base::span<char> data);
+
+// Reads a filepath from |remote_addr| (which points into process |pid|'s memory
+// space) into |*out_str|. Returns the appropriate result.
+// Safety checks should occur before usage of any system call arguments read
+// from a remote address space, so callers should use RemoteSyscallFilepathArgs
+// instead of calling this directly.
+SANDBOX_EXPORT RemoteProcessIOResult
+ReadFilePathFromRemoteProcess(pid_t pid,
+ const void* remote_addr,
+ std::string* out_str);
+
+namespace internal {
+// The number of bytes we read from a remote process at a time when reading a
+// remote filepath, to avoid reading PATH_MAX bytes every time.
+const size_t kNumBytesPerChunk = 256;
+
+// Calculates the number of bytes left in a page for a particular address.
+uintptr_t NumBytesLeftInPage(uintptr_t addr);
+} // namespace internal
+} // namespace syscall_broker
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_REMOTE_SYSCALL_ARG_HANDLER_H_
diff --git a/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc b/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc
new file mode 100644
index 00000000000..c452d1d02d6
--- /dev/null
+++ b/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc
@@ -0,0 +1,346 @@
+// Copyright 2020 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/syscall_broker/remote_syscall_arg_handler.h"
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <algorithm>
+#include <cstring>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_forward.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/posix/unix_domain_socket.h"
+#include "base/process/process_metrics.h"
+#include "base/test/bind_test_util.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+namespace syscall_broker {
+
+namespace {
+const char kPathPart[] = "/i/am/path";
+
+void FillBufferWithPath(char* buf, size_t size, bool null_terminate) {
+ SANDBOX_ASSERT_LE(size, static_cast<size_t>(PATH_MAX));
+ size_t str_len = strlen(kPathPart);
+ size_t len_left_to_write = size;
+ char* curr_buf_pos = buf;
+ while (len_left_to_write > 0) {
+ size_t bytes_to_write = std::min(str_len, len_left_to_write);
+ memcpy(curr_buf_pos, kPathPart, bytes_to_write);
+ curr_buf_pos += bytes_to_write;
+ len_left_to_write -= bytes_to_write;
+ }
+
+ if (null_terminate) {
+ buf[size - 1] = '\0';
+ }
+}
+
+void VerifyCorrectString(std::string str, size_t size) {
+ SANDBOX_ASSERT_EQ(str.size(), size);
+ size_t curr_path_part_pos = 0;
+ for (char ch : str) {
+ SANDBOX_ASSERT(ch == kPathPart[curr_path_part_pos]);
+ curr_path_part_pos++;
+ curr_path_part_pos %= strlen(kPathPart);
+ }
+}
+
+void* MapPagesOrDie(size_t num_pages) {
+ void* addr = mmap(nullptr, num_pages * base::GetPageSize(),
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ PCHECK(addr);
+ return addr;
+}
+
+void MprotectLastPageOrDie(char* addr, size_t num_pages) {
+ size_t last_page_offset = (num_pages - 1) * base::GetPageSize();
+ PCHECK(mprotect(addr + last_page_offset, base::GetPageSize(), PROT_NONE) >=
+ 0);
+}
+
+pid_t ForkWaitingChild(base::OnceCallback<void(int)>
+ after_parent_signals_callback = base::DoNothing(),
+ base::ScopedFD* parent_sync_fd = nullptr) {
+ base::ScopedFD parent_sync, child_sync;
+ base::CreateSocketPair(&parent_sync, &child_sync);
+
+ pid_t pid = fork();
+ if (!pid) {
+ parent_sync.reset();
+ char dummy_char = 'a';
+ std::vector<base::ScopedFD> empty_fd_vec;
+ // Wait for parent to exit before exiting ourselves.
+ base::UnixDomainSocket::RecvMsg(child_sync.get(), &dummy_char, 1,
+ &empty_fd_vec);
+ std::move(after_parent_signals_callback).Run(child_sync.get());
+ _exit(1);
+ }
+
+ child_sync.reset();
+
+ if (parent_sync_fd)
+ *parent_sync_fd = std::move(parent_sync);
+ else
+ ignore_result(parent_sync.release()); // Closes when parent dies.
+ return pid;
+}
+
+struct ReadTestConfig {
+ size_t start_at = 0;
+ size_t total_size = strlen(kPathPart) + 1;
+ bool include_null_byte = true;
+ bool last_page_inaccessible = false;
+ RemoteProcessIOResult result = RemoteProcessIOResult::kSuccess;
+};
+
+void ReadTest(const ReadTestConfig& test_config) {
+ // Map exactly the right number of pages for the config parameters.
+ size_t total_pages = (test_config.start_at + test_config.total_size +
+ base::GetPageSize() - 1) /
+ base::GetPageSize();
+ char* mmap_addr = static_cast<char*>(MapPagesOrDie(total_pages));
+ char* addr = mmap_addr + test_config.start_at;
+ FillBufferWithPath(addr, test_config.total_size,
+ test_config.include_null_byte);
+
+ if (test_config.last_page_inaccessible)
+ MprotectLastPageOrDie(mmap_addr, total_pages);
+
+ pid_t pid = ForkWaitingChild();
+ munmap(mmap_addr, base::GetPageSize() * total_pages);
+
+ std::string out_str;
+ SANDBOX_ASSERT_EQ(ReadFilePathFromRemoteProcess(pid, addr, &out_str),
+ test_config.result);
+ if (test_config.result == RemoteProcessIOResult::kSuccess) {
+ VerifyCorrectString(std::move(out_str), test_config.total_size - 1);
+ }
+}
+} // namespace
+
+// | path + null_byte |
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, BasicRead) {
+ ReadTest(ReadTestConfig());
+}
+
+// | zero + path... | ...path + null_byte + zero |
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, MultipageRead) {
+ ReadTestConfig config;
+ CHECK(PATH_MAX / 2 <= base::GetPageSize());
+ config.start_at = base::GetPageSize() - (PATH_MAX / 2);
+ config.total_size = PATH_MAX;
+
+ ReadTest(config);
+}
+
+// | path... | ...path |
+SANDBOX_TEST_ALLOW_NOISE(BrokerRemoteSyscallArgHandler, ReadExceededPathMax) {
+ ReadTestConfig config;
+ config.total_size = PATH_MAX * 2;
+ config.result = RemoteProcessIOResult::kExceededPathMax;
+}
+// | path... | null_byte + zero |
+SANDBOX_TEST_ALLOW_NOISE(BrokerRemoteSyscallArgHandler,
+ ReadBarelyExceededPathMax) {
+ ReadTestConfig config;
+ config.total_size = PATH_MAX + 1;
+ config.result = RemoteProcessIOResult::kExceededPathMax;
+}
+
+// | zero + path... | INACCESSIBLE |
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadUnreadablePage) {
+ ReadTestConfig config;
+ config.start_at = base::GetPageSize() - (PATH_MAX / 2);
+ config.total_size = PATH_MAX / 2;
+ config.last_page_inaccessible = true;
+ config.include_null_byte = false;
+ config.result = RemoteProcessIOResult::kRemoteMemoryInvalid;
+
+ ReadTest(config);
+}
+
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunkMinus1) {
+ ReadTestConfig config;
+ config.total_size = internal::kNumBytesPerChunk - 1;
+
+ ReadTest(config);
+}
+
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunk) {
+ ReadTestConfig config;
+ config.total_size = internal::kNumBytesPerChunk;
+
+ ReadTest(config);
+}
+
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunkPlus1) {
+ ReadTestConfig config;
+ config.total_size = internal::kNumBytesPerChunk + 1;
+
+ ReadTest(config);
+}
+
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunkEndingAtPage) {
+ ReadTestConfig config;
+ config.start_at = base::GetPageSize() - internal::kNumBytesPerChunk;
+ config.total_size = internal::kNumBytesPerChunk;
+
+ ReadTest(config);
+}
+
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunkEndingOnePastPage) {
+ ReadTestConfig config;
+ config.start_at = base::GetPageSize() - internal::kNumBytesPerChunk + 1;
+ config.total_size = internal::kNumBytesPerChunk;
+
+ ReadTest(config);
+}
+
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunkPlus1EndingOnePastPage) {
+ ReadTestConfig config;
+ config.start_at = base::GetPageSize() - internal::kNumBytesPerChunk;
+ config.total_size = internal::kNumBytesPerChunk + 1;
+
+ ReadTest(config);
+}
+
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChildExited) {
+ void* addr = MapPagesOrDie(1);
+ FillBufferWithPath(static_cast<char*>(addr), strlen(kPathPart) + 1, true);
+
+ base::ScopedFD parent_sync, child_sync;
+ base::CreateSocketPair(&parent_sync, &child_sync);
+
+ pid_t pid = fork();
+ if (!pid) {
+ parent_sync.reset();
+ _exit(1);
+ }
+
+ child_sync.reset();
+
+ // Wait for child to exit before reading memory.
+ char dummy_char = 'a';
+ std::vector<base::ScopedFD> empty_fd_vec;
+ base::UnixDomainSocket::RecvMsg(parent_sync.get(), &dummy_char, 1,
+ &empty_fd_vec);
+
+ munmap(addr, base::GetPageSize());
+
+ std::string out_str;
+ SANDBOX_ASSERT_EQ(ReadFilePathFromRemoteProcess(pid, addr, &out_str),
+ RemoteProcessIOResult::kRemoteExited);
+}
+
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, BasicWrite) {
+ void* read_from = MapPagesOrDie(1);
+ const size_t write_size = base::GetPageSize();
+ FillBufferWithPath(static_cast<char*>(read_from), write_size, false);
+ char* write_to = static_cast<char*>(MapPagesOrDie(1));
+ base::ScopedFD parent_signal_fd;
+ const std::vector<int> empty_fd_vec;
+
+ pid_t pid =
+ ForkWaitingChild(base::BindLambdaForTesting([=](int child_sync_fd) {
+ // Check correct result received and tell parent about
+ // success.
+ int res = memcmp(read_from, write_to, write_size);
+
+ base::UnixDomainSocket::SendMsg(
+ child_sync_fd, &res, sizeof(res), empty_fd_vec);
+ _exit(1);
+ }),
+ &parent_signal_fd);
+
+ RemoteProcessIOResult result = WriteRemoteData(
+ pid, reinterpret_cast<uintptr_t>(write_to), write_size,
+ base::span<char>(static_cast<char*>(read_from), write_size));
+ SANDBOX_ASSERT_EQ(result, RemoteProcessIOResult::kSuccess);
+
+ // Release child.
+ char dummy_char = 'a';
+ base::UnixDomainSocket::SendMsg(parent_signal_fd.get(), &dummy_char, 1,
+ empty_fd_vec);
+
+ // Read result of memcmp and assert.
+ int memcmp_res;
+ std::vector<base::ScopedFD> dummy_fd_vec;
+ base::UnixDomainSocket::RecvMsg(parent_signal_fd.get(), &memcmp_res,
+ sizeof(memcmp_res), &dummy_fd_vec);
+ SANDBOX_ASSERT_EQ(memcmp_res, 0);
+}
+
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WriteToInvalidAddress) {
+ char* write_to = static_cast<char*>(MapPagesOrDie(1));
+ MprotectLastPageOrDie(write_to, 1);
+ base::ScopedFD parent_signal_fd;
+ const std::vector<int> empty_fd_vec;
+
+ pid_t pid = ForkWaitingChild();
+ munmap(write_to, base::GetPageSize());
+
+ char buf[5];
+ memset(buf, 'a', sizeof(buf));
+ RemoteProcessIOResult result =
+ WriteRemoteData(pid, reinterpret_cast<uintptr_t>(write_to), sizeof(buf),
+ base::span<char>(buf, sizeof(buf)));
+ SANDBOX_ASSERT_EQ(result, RemoteProcessIOResult::kRemoteMemoryInvalid);
+}
+
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WritePartiallyToInvalidAddress) {
+ char* read_from = static_cast<char*>(MapPagesOrDie(2));
+ const size_t write_size = base::GetPageSize();
+ FillBufferWithPath(static_cast<char*>(read_from), write_size, false);
+ char* write_to = static_cast<char*>(MapPagesOrDie(2));
+ MprotectLastPageOrDie(write_to, 2);
+ write_to += base::GetPageSize() / 2;
+ base::ScopedFD parent_signal_fd;
+ const std::vector<int> empty_fd_vec;
+
+ pid_t pid = ForkWaitingChild();
+ munmap(write_to, base::GetPageSize());
+
+ RemoteProcessIOResult result =
+ WriteRemoteData(pid, reinterpret_cast<uintptr_t>(write_to), write_size,
+ base::span<char>(read_from, write_size));
+ SANDBOX_ASSERT_EQ(result, RemoteProcessIOResult::kRemoteMemoryInvalid);
+}
+
+SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WriteChildExited) {
+ char* addr = static_cast<char*>(MapPagesOrDie(1));
+ FillBufferWithPath(static_cast<char*>(addr), strlen(kPathPart) + 1, true);
+
+ base::ScopedFD parent_sync, child_sync;
+ base::CreateSocketPair(&parent_sync, &child_sync);
+
+ pid_t pid = fork();
+ if (!pid) {
+ parent_sync.reset();
+ _exit(1);
+ }
+
+ child_sync.reset();
+
+ // Wait for child to exit before writing memory.
+ char dummy_char = 'a';
+ std::vector<base::ScopedFD> empty_fd_vec;
+ base::UnixDomainSocket::RecvMsg(parent_sync.get(), &dummy_char, 1,
+ &empty_fd_vec);
+
+ std::string out_str;
+ SANDBOX_ASSERT_EQ(
+ WriteRemoteData(pid, reinterpret_cast<uintptr_t>(addr), strlen(kPathPart),
+ base::span<char>(addr, strlen(kPathPart))),
+ RemoteProcessIOResult::kRemoteExited);
+}
+
+} // namespace syscall_broker
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/syscall_broker/syscall_dispatcher.cc b/chromium/sandbox/linux/syscall_broker/syscall_dispatcher.cc
new file mode 100644
index 00000000000..b9ee93c14ac
--- /dev/null
+++ b/chromium/sandbox/linux/syscall_broker/syscall_dispatcher.cc
@@ -0,0 +1,191 @@
+// Copyright 2020 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/syscall_broker/syscall_dispatcher.h"
+
+#include <fcntl.h>
+
+#include "base/check.h"
+#include "base/logging.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace sandbox {
+namespace syscall_broker {
+
+#if defined(MEMORY_SANITIZER)
+#define BROKER_UNPOISON_STRING(x) __msan_unpoison_string(x)
+#else
+#define BROKER_UNPOISON_STRING(x)
+#endif
+
+int SyscallDispatcher::PerformStatat(const arch_seccomp_data& args,
+ bool arch64) {
+ if (static_cast<int>(args.args[0]) != AT_FDCWD)
+ return -EPERM;
+ // Only allow the AT_SYMLINK_NOFOLLOW flag which is used by some libc
+ // implementations for lstat().
+ if ((static_cast<int>(args.args[3]) & ~AT_SYMLINK_NOFOLLOW) != 0)
+ return -EINVAL;
+
+ const bool follow_links =
+ !(static_cast<int>(args.args[3]) & AT_SYMLINK_NOFOLLOW);
+ if (arch64) {
+ return Stat64(reinterpret_cast<const char*>(args.args[1]), follow_links,
+ reinterpret_cast<struct stat64*>(args.args[2]));
+ }
+
+ return Stat(reinterpret_cast<const char*>(args.args[1]), follow_links,
+ reinterpret_cast<struct stat*>(args.args[2]));
+}
+
+int SyscallDispatcher::DispatchSyscall(const arch_seccomp_data& args) {
+ switch (args.nr) {
+#if defined(__NR_access)
+ case __NR_access:
+ return Access(reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
+#endif
+#if defined(__NR_faccessat)
+ case __NR_faccessat:
+ if (static_cast<int>(args.args[0]) != AT_FDCWD)
+ return -EPERM;
+ return Access(reinterpret_cast<const char*>(args.args[1]),
+ static_cast<int>(args.args[2]));
+#endif
+#if defined(__NR_mkdir)
+ case __NR_mkdir:
+ return Mkdir(reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
+#endif
+#if defined(__NR_mkdirat)
+ case __NR_mkdirat:
+ if (static_cast<int>(args.args[0]) != AT_FDCWD)
+ return -EPERM;
+ return Mkdir(reinterpret_cast<const char*>(args.args[1]),
+ static_cast<int>(args.args[2]));
+#endif
+#if defined(__NR_open)
+ case __NR_open:
+ // http://crbug.com/372840
+ BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0]));
+ return Open(reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
+#endif
+#if defined(__NR_openat)
+ case __NR_openat:
+ if (static_cast<int>(args.args[0]) != AT_FDCWD)
+ return -EPERM;
+ // http://crbug.com/372840
+ BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[1]));
+ return Open(reinterpret_cast<const char*>(args.args[1]),
+ static_cast<int>(args.args[2]));
+#endif
+#if defined(__NR_readlink)
+ case __NR_readlink:
+ return Readlink(reinterpret_cast<const char*>(args.args[0]),
+ reinterpret_cast<char*>(args.args[1]),
+ static_cast<size_t>(args.args[2]));
+#endif
+#if defined(__NR_readlinkat)
+ case __NR_readlinkat:
+ if (static_cast<int>(args.args[0]) != AT_FDCWD)
+ return -EPERM;
+ return Readlink(reinterpret_cast<const char*>(args.args[1]),
+ reinterpret_cast<char*>(args.args[2]),
+ static_cast<size_t>(args.args[3]));
+#endif
+#if defined(__NR_rename)
+ case __NR_rename:
+ return Rename(reinterpret_cast<const char*>(args.args[0]),
+ reinterpret_cast<const char*>(args.args[1]));
+#endif
+#if defined(__NR_renameat)
+ case __NR_renameat:
+ if (static_cast<int>(args.args[0]) != AT_FDCWD ||
+ static_cast<int>(args.args[2]) != AT_FDCWD) {
+ return -EPERM;
+ }
+ return Rename(reinterpret_cast<const char*>(args.args[1]),
+ reinterpret_cast<const char*>(args.args[3]));
+#endif
+#if defined(__NR_renameat2)
+ case __NR_renameat2:
+ if (static_cast<int>(args.args[0]) != AT_FDCWD ||
+ static_cast<int>(args.args[2]) != AT_FDCWD) {
+ return -EPERM;
+ }
+ if (static_cast<int>(args.args[4]) != 0)
+ return -EINVAL;
+ return Rename(reinterpret_cast<const char*>(args.args[1]),
+ reinterpret_cast<const char*>(args.args[3]));
+#endif
+#if defined(__NR_rmdir)
+ case __NR_rmdir:
+ return Rmdir(reinterpret_cast<const char*>(args.args[0]));
+#endif
+#if defined(__NR_stat)
+ case __NR_stat:
+ return Stat(reinterpret_cast<const char*>(args.args[0]), true,
+ reinterpret_cast<struct stat*>(args.args[1]));
+#endif
+#if defined(__NR_stat64)
+ case __NR_stat64:
+ return Stat64(reinterpret_cast<const char*>(args.args[0]), true,
+ reinterpret_cast<struct stat64*>(args.args[1]));
+#endif
+#if defined(__NR_lstat)
+ case __NR_lstat:
+ // See https://crbug.com/847096
+ BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0]));
+ return Stat(reinterpret_cast<const char*>(args.args[0]), false,
+ reinterpret_cast<struct stat*>(args.args[1]));
+#endif
+#if defined(__NR_lstat64)
+ case __NR_lstat64:
+ // See https://crbug.com/847096
+ BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0]));
+ return Stat64(reinterpret_cast<const char*>(args.args[0]), false,
+ reinterpret_cast<struct stat64*>(args.args[1]));
+#endif
+#if defined(__NR_fstatat)
+ case __NR_fstatat:
+ return PerformStatat(args, /*arch64=*/false);
+#endif
+#if defined(__NR_fstatat64)
+ case __NR_fstatat64:
+ return PerformStatat(args, /*arch64=*/true);
+#endif
+#if defined(__NR_newfstatat)
+ case __NR_newfstatat:
+ return PerformStatat(args, /*arch64=*/false);
+#endif
+#if defined(__NR_unlink)
+ case __NR_unlink:
+ return Unlink(reinterpret_cast<const char*>(args.args[0]));
+#endif
+#if defined(__NR_unlinkat)
+ case __NR_unlinkat: {
+ if (static_cast<int>(args.args[0]) != AT_FDCWD)
+ return -EPERM;
+
+ int flags = static_cast<int>(args.args[2]);
+
+ if (flags == AT_REMOVEDIR) {
+ return Rmdir(reinterpret_cast<const char*>(args.args[1]));
+ }
+
+ if (flags != 0)
+ return -EPERM;
+
+ return Unlink(reinterpret_cast<const char*>(args.args[1]));
+ }
+#endif // defined(__NR_unlinkat)
+ default:
+ RAW_CHECK(false);
+ return -ENOSYS;
+ }
+}
+
+} // namespace syscall_broker
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/syscall_broker/syscall_dispatcher.h b/chromium/sandbox/linux/syscall_broker/syscall_dispatcher.h
new file mode 100644
index 00000000000..d8b8874ad9c
--- /dev/null
+++ b/chromium/sandbox/linux/syscall_broker/syscall_dispatcher.h
@@ -0,0 +1,70 @@
+// Copyright 2020 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.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_SYSCALL_DISPATCHER_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_SYSCALL_DISPATCHER_H_
+
+#include <sys/stat.h>
+#include <cstddef>
+
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+
+namespace sandbox {
+namespace syscall_broker {
+
+// An abstract class that defines all the system calls we perform for the
+// sandboxed process.
+class SyscallDispatcher {
+ public:
+ // Emulates access()/faccessat().
+ // X_OK will always return an error in practice since the broker process
+ // doesn't support execute permissions.
+ virtual int Access(const char* pathname, int mode) const = 0;
+
+ // Emulates mkdir()/mkdirat.
+ virtual int Mkdir(const char* path, int mode) const = 0;
+
+ // Emulates open()/openat().
+ // The implementation only supports certain white listed flags and will
+ // return -EPERM on other flags.
+ virtual int Open(const char* pathname, int flags) const = 0;
+
+ // Emulates readlink()/readlinkat().
+ virtual int Readlink(const char* path, char* buf, size_t bufsize) const = 0;
+
+ // Emulates rename()/renameat()/renameat2().
+ virtual int Rename(const char* oldpath, const char* newpath) const = 0;
+
+ // Emulates rmdir().
+ virtual int Rmdir(const char* path) const = 0;
+
+ // Emulates stat()/stat64()/lstat()/lstat64()/fstatat()/newfstatat().
+ virtual int Stat(const char* pathname,
+ bool follow_links,
+ struct stat* sb) const = 0;
+ virtual int Stat64(const char* pathname,
+ bool follow_links,
+ struct stat64* sb) const = 0;
+
+ // Emulates unlink()/unlinkat().
+ virtual int Unlink(const char* unlink) const = 0;
+
+ // Validates the args passed to a *statat*() syscall and performs the syscall
+ // using Stat() or Stat64().
+ int PerformStatat(const arch_seccomp_data& args, bool arch64);
+
+ // Reads the syscall number and arguments, imposes some policy (e.g. the *at()
+ // system calls must only allow AT_FDCWD as the first argument), and
+ // dispatches to the correct method from above.
+ // Async-signal-safe since this might be called in a signal handler.
+ int DispatchSyscall(const arch_seccomp_data& args);
+
+ protected:
+ virtual ~SyscallDispatcher() = default;
+};
+
+} // namespace syscall_broker
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_SYSCALL_DISPATCHER_H_
diff --git a/chromium/sandbox/linux/system_headers/linux_prctl.h b/chromium/sandbox/linux/system_headers/linux_prctl.h
new file mode 100644
index 00000000000..50f639b0fef
--- /dev/null
+++ b/chromium/sandbox/linux/system_headers/linux_prctl.h
@@ -0,0 +1,35 @@
+// Copyright 2020 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.
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_PRCTL_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_PRCTL_H_
+
+#include "build/build_config.h"
+
+#if !defined(PR_SET_PDEATHSIG)
+#define PR_SET_PDEATHSIG 1
+#endif
+
+#if !defined(PR_SET_TIMERSLACK)
+#define PR_SET_TIMERSLACK 29
+#endif
+
+#if defined(OS_ANDROID)
+
+// https://android.googlesource.com/platform/bionic/+/lollipop-release/libc/private/bionic_prctl.h
+#if !defined(PR_SET_VMA)
+#define PR_SET_VMA 0x53564d41
+#endif
+
+#endif // defined(OS_ANDROID)
+
+#if !defined(PR_SET_PTRACER)
+#define PR_SET_PTRACER 0x59616d61
+#endif
+
+#if !defined(PR_SET_PTRACER_ANY)
+#define PR_SET_PTRACER_ANY ((unsigned long)-1)
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_PRCTL_H_
diff --git a/chromium/sandbox/linux/system_headers/linux_seccomp.h b/chromium/sandbox/linux/system_headers/linux_seccomp.h
index ab1d1fc2b5d..1fa47ed09ff 100644
--- a/chromium/sandbox/linux/system_headers/linux_seccomp.h
+++ b/chromium/sandbox/linux/system_headers/linux_seccomp.h
@@ -5,6 +5,14 @@
#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SECCOMP_H_
#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SECCOMP_H_
+#include <stdint.h>
+
+#include "build/build_config.h"
+
+#if !defined(OS_NACL_NONSFI)
+#include <sys/ioctl.h>
+#endif
+
// The Seccomp2 kernel ABI is not part of older versions of glibc.
// As we can't break compilation with these versions of the library,
// we explicitly define all missing symbols.
@@ -17,15 +25,19 @@
#ifndef EM_ARM
#define EM_ARM 40
#endif
+
#ifndef EM_386
#define EM_386 3
#endif
+
#ifndef EM_X86_64
#define EM_X86_64 62
#endif
+
#ifndef EM_MIPS
#define EM_MIPS 8
#endif
+
#ifndef EM_AARCH64
#define EM_AARCH64 183
#endif
@@ -33,24 +45,31 @@
#ifndef __AUDIT_ARCH_64BIT
#define __AUDIT_ARCH_64BIT 0x80000000
#endif
+
#ifndef __AUDIT_ARCH_LE
#define __AUDIT_ARCH_LE 0x40000000
#endif
+
#ifndef AUDIT_ARCH_ARM
#define AUDIT_ARCH_ARM (EM_ARM|__AUDIT_ARCH_LE)
#endif
+
#ifndef AUDIT_ARCH_I386
#define AUDIT_ARCH_I386 (EM_386|__AUDIT_ARCH_LE)
#endif
+
#ifndef AUDIT_ARCH_X86_64
#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
#endif
+
#ifndef AUDIT_ARCH_MIPSEL
#define AUDIT_ARCH_MIPSEL (EM_MIPS|__AUDIT_ARCH_LE)
#endif
+
#ifndef AUDIT_ARCH_MIPSEL64
#define AUDIT_ARCH_MIPSEL64 (EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
#endif
+
#ifndef AUDIT_ARCH_AARCH64
#define AUDIT_ARCH_AARCH64 (EM_AARCH64 | __AUDIT_ARCH_64BIT | __AUDIT_ARCH_LE)
#endif
@@ -60,23 +79,29 @@
#define PR_SET_SECCOMP 22
#define PR_GET_SECCOMP 21
#endif
+
#ifndef PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS 38
#define PR_GET_NO_NEW_PRIVS 39
#endif
+
#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
@@ -96,25 +121,101 @@
#ifndef SECCOMP_SET_MODE_FILTER
#define SECCOMP_SET_MODE_FILTER 1
#endif
+#ifndef SECCOMP_GET_NOTIF_SIZES
+#define SECCOMP_GET_NOTIF_SIZES 3
+#endif
+
#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_FILTER_FLAG_NEW_LISTENER
+#define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3)
+#endif
+
+#ifndef SECCOMP_FILTER_FLAG_TSYNC_ESRCH
+#define SECCOMP_FILTER_FLAG_TSYNC_ESRCH (1UL << 4)
+#endif
+
+#ifndef SECCOMP_ADDFD_FLAG_SETFD
+#define SECCOMP_ADDFD_FLAG_SETFD (1UL << 0)
+#endif
+
+// In the future, if we add fields to these structs and then access them, they
+// might be out-of-bounds on an older kernel. So before adding to these structs,
+// make sure to annotate them with a comment that it may be unsafe to access
+// those fields on older kernels.
+struct arch_seccomp_data {
+ int nr;
+ uint32_t arch;
+ uint64_t instruction_pointer;
+ uint64_t args[6];
+};
+
+struct seccomp_notif_sizes {
+ uint16_t seccomp_notif;
+ uint16_t seccomp_notif_resp;
+ uint16_t seccomp_data;
+};
+
+struct seccomp_notif {
+ uint64_t id;
+ uint32_t pid;
+ uint32_t flags;
+ struct arch_seccomp_data data;
+};
+
+struct seccomp_notif_resp {
+ uint64_t id;
+ int64_t val;
+ int32_t error;
+ uint32_t flags;
+};
+
+struct seccomp_notif_addfd {
+ uint64_t id;
+ uint32_t flags;
+ uint32_t srcfd;
+ uint32_t newfd;
+ uint32_t newfd_flags;
+};
+
+// sys/ioctl.h is not available in pnacl toolchain.
+#if !defined(OS_NACL_NONSFI)
+#define SECCOMP_IOC_MAGIC '!'
+#define SECCOMP_IO(nr) _IO(SECCOMP_IOC_MAGIC, nr)
+#define SECCOMP_IOR(nr, type) _IOR(SECCOMP_IOC_MAGIC, nr, type)
+#define SECCOMP_IOW(nr, type) _IOW(SECCOMP_IOC_MAGIC, nr, type)
+#define SECCOMP_IOWR(nr, type) _IOWR(SECCOMP_IOC_MAGIC, nr, type)
+
+// Flags for seccomp notification fd ioctl.
+#define SECCOMP_IOCTL_NOTIF_RECV SECCOMP_IOWR(0, struct seccomp_notif)
+#define SECCOMP_IOCTL_NOTIF_SEND SECCOMP_IOWR(1, struct seccomp_notif_resp)
+// Note: SECCOMP_IOCTL_NOTIF_ID_VALID is now defined with SECCOMP_IOW, but
+// kernels are expected to support the (now incorrect) ioctl number for the
+// foreseeable future.
+#define SECCOMP_IOCTL_NOTIF_ID_VALID SECCOMP_IOR(2, uint64_t)
+// On success, the return value is the remote process's added fd number
+#define SECCOMP_IOCTL_NOTIF_ADDFD SECCOMP_IOW(3, struct seccomp_notif_addfd)
+#endif // !defined(OS_NACL_NONSFI)
+
#ifndef SECCOMP_RET_KILL
// Return values supported for BPF filter programs. Please note that the
// "illegal" SECCOMP_RET_INVALID is not supported by the kernel, should only
// ever be used internally, and would result in the kernel killing our process.
-#define SECCOMP_RET_KILL 0x00000000U // Kill the task immediately
-#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
-#define SECCOMP_RET_TRAP 0x00030000U // Disallow and force a SIGSYS
-#define SECCOMP_RET_ERRNO 0x00050000U // Returns an errno
-#define SECCOMP_RET_TRACE 0x7ff00000U // Pass to a tracer or disallow
-#define SECCOMP_RET_ALLOW 0x7fff0000U // Allow
-#define SECCOMP_RET_ACTION 0xffff0000U // Masks for the return value
-#define SECCOMP_RET_DATA 0x0000ffffU // sections
+#define SECCOMP_RET_KILL 0x00000000U // Kill the task immediately
+#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
+#define SECCOMP_RET_TRAP 0x00030000U // Disallow and force a SIGSYS
+#define SECCOMP_RET_ERRNO 0x00050000U // Returns an errno
+#define SECCOMP_RET_USER_NOTIF 0x7fc00000U // Notifies userspace
+#define SECCOMP_RET_TRACE 0x7ff00000U // Pass to a tracer or disallow
+#define SECCOMP_RET_ALLOW 0x7fff0000U // Allow
+#define SECCOMP_RET_ACTION 0xffff0000U // Masks for the return value
+#define SECCOMP_RET_DATA 0x0000ffffU // sections
#else
#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
#endif
diff --git a/chromium/sandbox/policy/BUILD.gn b/chromium/sandbox/policy/BUILD.gn
index 6a148bb4c81..ccb1bd1fd8d 100644
--- a/chromium/sandbox/policy/BUILD.gn
+++ b/chromium/sandbox/policy/BUILD.gn
@@ -21,12 +21,12 @@ component("policy") {
"switches.h",
]
defines = [ "SANDBOX_POLICY_IMPL" ]
- public_deps = [ "//services/service_manager/embedder:embedder_switches" ]
deps = [
":sanitizer_buildflags",
"//base",
"//sandbox:common",
]
+ public_deps = []
if (is_linux || is_chromeos) {
sources += [
"linux/bpf_audio_policy_linux.cc",
@@ -159,7 +159,10 @@ source_set("tests") {
]
if (is_win) {
- sources += [ "win/sandbox_win_unittest.cc" ]
+ sources += [
+ "win/mf_cdm_sandbox_type_unittest.cc",
+ "win/sandbox_win_unittest.cc",
+ ]
deps += [ "//sandbox/win:sandbox" ]
data = [
"//base/test/data/pe_image/pe_image_test_32.dll",
diff --git a/chromium/sandbox/policy/DEPS b/chromium/sandbox/policy/DEPS
index 0f5bfa1df86..804a308655f 100644
--- a/chromium/sandbox/policy/DEPS
+++ b/chromium/sandbox/policy/DEPS
@@ -1,5 +1,4 @@
include_rules = [
"+sandbox/constants.h",
"+sandbox",
- "+services/service_manager/embedder/switches.h",
]
diff --git a/chromium/sandbox/policy/features.cc b/chromium/sandbox/policy/features.cc
index 7930f18b027..bbb78b3b23d 100644
--- a/chromium/sandbox/policy/features.cc
+++ b/chromium/sandbox/policy/features.cc
@@ -36,20 +36,6 @@ const base::Feature kGpuLPAC{"GpuLPAC", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kXRSandbox{"XRSandbox", base::FEATURE_ENABLED_BY_DEFAULT};
#endif // !defined(OS_ANDROID)
-#if defined(OS_CHROMEOS)
-// Controls whether the Spectre variant 2 mitigation is enabled. We use a USE
-// flag on some Chrome OS boards to disable the mitigation by disabling this
-// feature in exchange for system performance.
-const base::Feature kSpectreVariant2Mitigation{
- "SpectreVariant2Mitigation", base::FEATURE_ENABLED_BY_DEFAULT};
-
-// An override for the Spectre variant 2 default behavior. Security sensitive
-// users can enable this feature to ensure that the mitigation is always
-// enabled.
-const base::Feature kForceSpectreVariant2Mitigation{
- "ForceSpectreVariant2Mitigation", base::FEATURE_DISABLED_BY_DEFAULT};
-#endif // defined(OS_CHROMEOS)
-
} // namespace features
} // namespace policy
} // namespace sandbox
diff --git a/chromium/sandbox/policy/features.h b/chromium/sandbox/policy/features.h
index b86d0938b61..cc9b5e68162 100644
--- a/chromium/sandbox/policy/features.h
+++ b/chromium/sandbox/policy/features.h
@@ -30,12 +30,6 @@ SANDBOX_POLICY_EXPORT extern const base::Feature kGpuLPAC;
SANDBOX_POLICY_EXPORT extern const base::Feature kXRSandbox;
#endif // !defined(OS_ANDROID)
-#if defined(OS_CHROMEOS)
-SANDBOX_POLICY_EXPORT extern const base::Feature kSpectreVariant2Mitigation;
-SANDBOX_POLICY_EXPORT extern const base::Feature
- kForceSpectreVariant2Mitigation;
-#endif // defined(OS_CHROMEOS)
-
} // namespace features
} // namespace policy
} // namespace sandbox
diff --git a/chromium/sandbox/policy/linux/bpf_audio_policy_linux.cc b/chromium/sandbox/policy/linux/bpf_audio_policy_linux.cc
index fba1b9eb089..d7737303d9b 100644
--- a/chromium/sandbox/policy/linux/bpf_audio_policy_linux.cc
+++ b/chromium/sandbox/policy/linux/bpf_audio_policy_linux.cc
@@ -128,9 +128,9 @@ ResultExpr AudioProcessPolicy::EvaluateSyscall(int system_call_number) const {
return Allow();
#endif
- auto* broker_process = SandboxLinux::GetInstance()->broker_process();
- if (broker_process->IsSyscallAllowed(system_call_number))
- return Trap(BrokerProcess::SIGSYS_Handler, broker_process);
+ auto* sandbox_linux = SandboxLinux::GetInstance();
+ if (sandbox_linux->ShouldBrokerHandleSyscall(system_call_number))
+ return sandbox_linux->HandleViaBroker();
return BPFBasePolicy::EvaluateSyscall(system_call_number);
}
diff --git a/chromium/sandbox/policy/linux/bpf_broker_policy_linux.h b/chromium/sandbox/policy/linux/bpf_broker_policy_linux.h
index 0fe3515a985..1d394c551a5 100644
--- a/chromium/sandbox/policy/linux/bpf_broker_policy_linux.h
+++ b/chromium/sandbox/policy/linux/bpf_broker_policy_linux.h
@@ -14,7 +14,7 @@ namespace sandbox {
namespace policy {
// A broker policy is one for a privileged syscall broker that allows
-// access, open, openat, and (in the non-Chrome OS case) unlink.
+// a limited set of filesystem calls.
class SANDBOX_POLICY_EXPORT BrokerProcessPolicy : public BPFBasePolicy {
public:
explicit BrokerProcessPolicy(
diff --git a/chromium/sandbox/policy/linux/bpf_gpu_policy_linux.cc b/chromium/sandbox/policy/linux/bpf_gpu_policy_linux.cc
index 127989715b3..24081195c4a 100644
--- a/chromium/sandbox/policy/linux/bpf_gpu_policy_linux.cc
+++ b/chromium/sandbox/policy/linux/bpf_gpu_policy_linux.cc
@@ -100,10 +100,9 @@ ResultExpr GpuProcessPolicy::EvaluateSyscall(int sysno) const {
return Allow();
#endif
- auto* broker_process = SandboxLinux::GetInstance()->broker_process();
- if (broker_process->IsSyscallAllowed(sysno)) {
- return Trap(BrokerProcess::SIGSYS_Handler, broker_process);
- }
+ auto* sandbox_linux = SandboxLinux::GetInstance();
+ if (sandbox_linux->ShouldBrokerHandleSyscall(sysno))
+ return sandbox_linux->HandleViaBroker();
// Default on the baseline policy.
return BPFBasePolicy::EvaluateSyscall(sysno);
diff --git a/chromium/sandbox/policy/linux/bpf_ime_policy_linux.cc b/chromium/sandbox/policy/linux/bpf_ime_policy_linux.cc
index 4d6147a2ebd..3fcdbcc188c 100644
--- a/chromium/sandbox/policy/linux/bpf_ime_policy_linux.cc
+++ b/chromium/sandbox/policy/linux/bpf_ime_policy_linux.cc
@@ -39,9 +39,9 @@ ResultExpr ImeProcessPolicy::EvaluateSyscall(int sysno) const {
return RestrictGetrusage();
#endif
default:
- auto* broker_process = SandboxLinux::GetInstance()->broker_process();
- if (broker_process->IsSyscallAllowed(sysno))
- return Trap(BrokerProcess::SIGSYS_Handler, broker_process);
+ auto* sandbox_linux = SandboxLinux::GetInstance();
+ if (sandbox_linux->ShouldBrokerHandleSyscall(sysno))
+ return sandbox_linux->HandleViaBroker();
return BPFBasePolicy::EvaluateSyscall(sysno);
}
diff --git a/chromium/sandbox/policy/linux/bpf_network_policy_linux.cc b/chromium/sandbox/policy/linux/bpf_network_policy_linux.cc
index 2cdcc797d8e..56e2b4a8518 100644
--- a/chromium/sandbox/policy/linux/bpf_network_policy_linux.cc
+++ b/chromium/sandbox/policy/linux/bpf_network_policy_linux.cc
@@ -33,12 +33,11 @@ NetworkProcessPolicy::NetworkProcessPolicy() {}
NetworkProcessPolicy::~NetworkProcessPolicy() {}
ResultExpr NetworkProcessPolicy::EvaluateSyscall(int sysno) const {
- auto* broker_process = SandboxLinux::GetInstance()->broker_process();
- if (broker_process->IsSyscallAllowed(sysno)) {
- return Trap(BrokerProcess::SIGSYS_Handler, broker_process);
- }
+ auto* sandbox_linux = SandboxLinux::GetInstance();
+ if (sandbox_linux->ShouldBrokerHandleSyscall(sysno))
+ return sandbox_linux->HandleViaBroker();
- // TODO(tsepez): FIX this.
+ // TODO(mpdenton): FIX this.
return Allow();
}
diff --git a/chromium/sandbox/policy/linux/bpf_speech_recognition_policy_linux.cc b/chromium/sandbox/policy/linux/bpf_speech_recognition_policy_linux.cc
index d02983ca53f..4e62fadfafc 100644
--- a/chromium/sandbox/policy/linux/bpf_speech_recognition_policy_linux.cc
+++ b/chromium/sandbox/policy/linux/bpf_speech_recognition_policy_linux.cc
@@ -23,10 +23,20 @@ SpeechRecognitionProcessPolicy::~SpeechRecognitionProcessPolicy() = default;
ResultExpr SpeechRecognitionProcessPolicy::EvaluateSyscall(
int system_call_number) const {
switch (system_call_number) {
+ // Required by the Speech On-Device API (SODA) binary to find the
+ // appropriate configuration file to use within a language pack directory.
+#if defined(__NR_getdents64)
+ case __NR_getdents64:
+ return Allow();
+#endif
+#if defined(__NR_getdents)
+ case __NR_getdents:
+ return Allow();
+#endif
default:
- auto* broker_process = SandboxLinux::GetInstance()->broker_process();
- if (broker_process->IsSyscallAllowed(system_call_number))
- return Trap(BrokerProcess::SIGSYS_Handler, broker_process);
+ auto* sandbox_linux = SandboxLinux::GetInstance();
+ if (sandbox_linux->ShouldBrokerHandleSyscall(system_call_number))
+ return sandbox_linux->HandleViaBroker();
// Default on the content baseline policy.
return BPFBasePolicy::EvaluateSyscall(system_call_number);
diff --git a/chromium/sandbox/policy/linux/bpf_tts_policy_linux.cc b/chromium/sandbox/policy/linux/bpf_tts_policy_linux.cc
index f39a05de9f3..07a80f18ad0 100644
--- a/chromium/sandbox/policy/linux/bpf_tts_policy_linux.cc
+++ b/chromium/sandbox/policy/linux/bpf_tts_policy_linux.cc
@@ -25,9 +25,16 @@ TtsProcessPolicy::TtsProcessPolicy() {}
TtsProcessPolicy::~TtsProcessPolicy() {}
ResultExpr TtsProcessPolicy::EvaluateSyscall(int sysno) const {
- auto* broker_process = SandboxLinux::GetInstance()->broker_process();
- if (broker_process->IsSyscallAllowed(sysno))
- return Trap(BrokerProcess::SIGSYS_Handler, broker_process);
+ switch (sysno) {
+ case __NR_sched_setscheduler:
+ return RestrictSchedTarget(GetPolicyPid(), sysno);
+ default:
+ break;
+ }
+
+ auto* sandbox_linux = SandboxLinux::GetInstance();
+ if (sandbox_linux->ShouldBrokerHandleSyscall(sysno))
+ return sandbox_linux->HandleViaBroker();
return BPFBasePolicy::EvaluateSyscall(sysno);
}
diff --git a/chromium/sandbox/policy/linux/sandbox_linux.cc b/chromium/sandbox/policy/linux/sandbox_linux.cc
index a2f1150a0b6..c4231d0b964 100644
--- a/chromium/sandbox/policy/linux/sandbox_linux.cc
+++ b/chromium/sandbox/policy/linux/sandbox_linux.cc
@@ -40,6 +40,7 @@
#include "sandbox/linux/services/thread_helpers.h"
#include "sandbox/linux/services/yama.h"
#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
+#include "sandbox/linux/syscall_broker/broker_client.h"
#include "sandbox/linux/syscall_broker/broker_command.h"
#include "sandbox/linux/syscall_broker/broker_process.h"
#include "sandbox/policy/linux/bpf_broker_policy_linux.h"
@@ -61,7 +62,7 @@ namespace {
void LogSandboxStarted(const std::string& sandbox_name) {
const std::string process_type =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- service_manager::switches::kProcessType);
+ switches::kProcessType);
const std::string activated_sandbox =
"Activated " + sandbox_name +
" sandbox for process type: " + process_type + ".";
@@ -106,8 +107,8 @@ bool UpdateProcessTypeAndEnableSandbox(
base::CommandLine::ForCurrentProcess()->InitFromArgv(exec);
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- std::string new_process_type = command_line->GetSwitchValueASCII(
- service_manager::switches::kProcessType);
+ std::string new_process_type =
+ command_line->GetSwitchValueASCII(switches::kProcessType);
if (!new_process_type.empty()) {
new_process_type.append("-broker");
} else {
@@ -116,8 +117,7 @@ bool UpdateProcessTypeAndEnableSandbox(
VLOG(3) << "UpdateProcessTypeAndEnableSandbox: Updating process type to "
<< new_process_type;
- command_line->AppendSwitchASCII(service_manager::switches::kProcessType,
- new_process_type);
+ command_line->AppendSwitchASCII(switches::kProcessType, new_process_type);
if (broker_side_hook)
CHECK(std::move(broker_side_hook).Run(options));
@@ -323,8 +323,8 @@ bool SandboxLinux::InitializeSandbox(SandboxType sandbox_type,
initialize_sandbox_ran_ = true;
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- const std::string process_type = command_line->GetSwitchValueASCII(
- service_manager::switches::kProcessType);
+ const std::string process_type =
+ command_line->GetSwitchValueASCII(switches::kProcessType);
// We need to make absolutely sure that our sandbox is "sealed" before
// returning.
@@ -491,7 +491,8 @@ void SandboxLinux::StartBrokerProcess(
const Options& options) {
// Leaked at shutdown, so use bare |new|.
broker_process_ = new syscall_broker::BrokerProcess(
- BPFBasePolicy::GetFSDeniedErrno(), allowed_command_set, permissions);
+ BPFBasePolicy::GetFSDeniedErrno(), allowed_command_set, permissions,
+ syscall_broker::BrokerProcess::BrokerType::SIGNAL_BASED);
// The initialization callback will perform generic initialization and then
// call broker_sandboxer_callback.
@@ -500,6 +501,16 @@ void SandboxLinux::StartBrokerProcess(
options, allowed_command_set)));
}
+bool SandboxLinux::ShouldBrokerHandleSyscall(int sysno) const {
+ return broker_process_->IsSyscallAllowed(sysno);
+}
+
+sandbox::bpf_dsl::ResultExpr SandboxLinux::HandleViaBroker() const {
+ return sandbox::bpf_dsl::Trap(
+ sandbox::syscall_broker::BrokerClient::SIGSYS_Handler,
+ broker_process_->GetBrokerClientSignalBased());
+}
+
bool SandboxLinux::HasOpenDirectories() const {
return ProcUtil::HasOpenDirectory(proc_fd_);
}
diff --git a/chromium/sandbox/policy/linux/sandbox_linux.h b/chromium/sandbox/policy/linux/sandbox_linux.h
index 3f818b9ad3a..4b32dc48c27 100644
--- a/chromium/sandbox/policy/linux/sandbox_linux.h
+++ b/chromium/sandbox/policy/linux/sandbox_linux.h
@@ -233,9 +233,16 @@ class SANDBOX_POLICY_EXPORT SandboxLinux {
PreSandboxHook broker_side_hook,
const Options& options);
- syscall_broker::BrokerProcess* broker_process() const {
- return broker_process_;
- }
+ // Returns true if the broker should handle a particular syscall indicated by
+ // |sysno|. This will typically return true for system calls that take
+ // filepaths as arguments.
+ bool ShouldBrokerHandleSyscall(int sysno) const;
+
+ // Returns an expression that indicates the syscall in question should be
+ // handled transparently by the broker process. This is useful for file
+ // syscalls that take pathnames, so we can enforce pathname whitelisting.
+ // Only usable if StartBrokerProcess() was already called.
+ bpf_dsl::ResultExpr HandleViaBroker() const;
private:
friend struct base::DefaultSingletonTraits<SandboxLinux>;
diff --git a/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc b/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc
index ab45dc7766a..cf9bd192105 100644
--- a/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc
+++ b/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc
@@ -53,7 +53,6 @@
#endif // !defined(OS_NACL_NONSFI)
#if defined(OS_CHROMEOS)
-#include "sandbox/policy/features.h"
#include "sandbox/policy/linux/bpf_ime_policy_linux.h"
#include "sandbox/policy/linux/bpf_tts_policy_linux.h"
#endif // defined(OS_CHROMEOS)
@@ -260,14 +259,7 @@ bool SandboxSeccompBPF::StartSandboxWithExternalPolicy(
// doing so does not stop the sandbox.
SandboxBPF sandbox(std::move(policy));
sandbox.SetProcFd(std::move(proc_fd));
- bool enable_ibpb = true;
-#if defined(OS_CHROMEOS)
- enable_ibpb =
- base::FeatureList::IsEnabled(
- features::kForceSpectreVariant2Mitigation) ||
- base::FeatureList::IsEnabled(features::kSpectreVariant2Mitigation);
-#endif // defined(OS_CHROMEOS)
- CHECK(sandbox.StartSandbox(seccomp_level, enable_ibpb));
+ CHECK(sandbox.StartSandbox(seccomp_level));
return true;
}
#endif // BUILDFLAG(USE_SECCOMP_BPF)
diff --git a/chromium/sandbox/policy/mac/BUILD.gn b/chromium/sandbox/policy/mac/BUILD.gn
index c3d9df3d6a6..373b7e0f7b0 100644
--- a/chromium/sandbox/policy/mac/BUILD.gn
+++ b/chromium/sandbox/policy/mac/BUILD.gn
@@ -15,6 +15,7 @@ action_foreach("package_sb_files") {
"ppapi.sb",
"print_compositor.sb",
"renderer.sb",
+ "speech_recognition.sb",
"utility.sb",
]
outputs = [
diff --git a/chromium/sandbox/policy/mac/common.sb b/chromium/sandbox/policy/mac/common.sb
index 27b5a183d0e..349fc080221 100644
--- a/chromium/sandbox/policy/mac/common.sb
+++ b/chromium/sandbox/policy/mac/common.sb
@@ -199,10 +199,12 @@
(sysctl-name "hw.vectorunit")
(sysctl-name "kern.hostname")
(sysctl-name "kern.maxfilesperproc")
+ (sysctl-name "kern.osproductversion")
(sysctl-name "kern.osrelease")
(sysctl-name "kern.ostype")
(sysctl-name "kern.osvariant_status")
(sysctl-name "kern.osversion")
+ (sysctl-name "kern.secure_kernel")
(sysctl-name "kern.usrstack64")
(sysctl-name "kern.version")
(sysctl-name "sysctl.proc_cputype")
diff --git a/chromium/sandbox/policy/mac/gpu_v2.sb b/chromium/sandbox/policy/mac/gpu_v2.sb
index 543a2724a1f..4d7222c548d 100644
--- a/chromium/sandbox/policy/mac/gpu_v2.sb
+++ b/chromium/sandbox/policy/mac/gpu_v2.sb
@@ -27,6 +27,7 @@
(global-name "com.apple.PowerManagement.control")
(global-name "com.apple.SecurityServer")
(global-name "com.apple.system.notification_center")
+ (global-name "com.apple.system.opendirectoryd.membership") ; https://crbug.com/1126350#c5
(global-name "com.apple.tsm.uiserver")
(global-name "com.apple.windowserver.active")
)
diff --git a/chromium/sandbox/policy/mac/sandbox_mac.h b/chromium/sandbox/policy/mac/sandbox_mac.h
index 37fe74b86fa..864825b43d2 100644
--- a/chromium/sandbox/policy/mac/sandbox_mac.h
+++ b/chromium/sandbox/policy/mac/sandbox_mac.h
@@ -46,6 +46,8 @@ class SANDBOX_POLICY_EXPORT SandboxMac {
static const char* kSandboxBrowserPID;
static const char* kSandboxBundlePath;
static const char* kSandboxChromeBundleId;
+ static const char* kSandboxSodaComponentPath;
+ static const char* kSandboxSodaLanguagePackPath;
static const char* kSandboxComponentPath;
static const char* kSandboxDisableDenialLogging;
static const char* kSandboxEnableLogging;
diff --git a/chromium/sandbox/policy/mac/sandbox_mac.mm b/chromium/sandbox/policy/mac/sandbox_mac.mm
index 0decae680c2..9a175edb74b 100644
--- a/chromium/sandbox/policy/mac/sandbox_mac.mm
+++ b/chromium/sandbox/policy/mac/sandbox_mac.mm
@@ -48,6 +48,7 @@
#include "sandbox/policy/mac/ppapi.sb.h"
#include "sandbox/policy/mac/print_compositor.sb.h"
#include "sandbox/policy/mac/renderer.sb.h"
+#include "sandbox/policy/mac/speech_recognition.sb.h"
#include "sandbox/policy/mac/utility.sb.h"
#include "sandbox/policy/sandbox_type.h"
#include "sandbox/policy/switches.h"
@@ -59,6 +60,9 @@ namespace policy {
const char* SandboxMac::kSandboxBrowserPID = "BROWSER_PID";
const char* SandboxMac::kSandboxBundlePath = "BUNDLE_PATH";
const char* SandboxMac::kSandboxChromeBundleId = "BUNDLE_ID";
+const char* SandboxMac::kSandboxSodaComponentPath = "SODA_COMPONENT_PATH";
+const char* SandboxMac::kSandboxSodaLanguagePackPath =
+ "SODA_LANGUAGE_PACK_PATH";
const char* SandboxMac::kSandboxComponentPath = "COMPONENT_PATH";
const char* SandboxMac::kSandboxDisableDenialLogging =
"DISABLE_SANDBOX_DENIAL_LOGGING";
@@ -254,6 +258,9 @@ std::string SandboxMac::GetSandboxProfile(SandboxType sandbox_type) {
case SandboxType::kPrintCompositor:
profile += kSeatbeltPolicyString_print_compositor;
break;
+ case SandboxType::kSpeechRecognition:
+ profile += kSeatbeltPolicyString_speech_recognition;
+ break;
case SandboxType::kUtility:
profile += kSeatbeltPolicyString_utility;
break;
@@ -262,7 +269,6 @@ std::string SandboxMac::GetSandboxProfile(SandboxType sandbox_type) {
break;
case SandboxType::kNoSandbox:
case SandboxType::kVideoCapture:
- case SandboxType::kSpeechRecognition:
CHECK(false);
break;
}
diff --git a/chromium/sandbox/policy/mac/speech_recognition.sb b/chromium/sandbox/policy/mac/speech_recognition.sb
new file mode 100644
index 00000000000..799c2be3c9b
--- /dev/null
+++ b/chromium/sandbox/policy/mac/speech_recognition.sb
@@ -0,0 +1,15 @@
+; Copyright 2020 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.
+
+; --- The contents of common.sb implicitly included here. ---
+
+; Required to load the libsoda.so binary downloaded by the component
+; updater.
+(define soda-component-path "SODA_COMPONENT_PATH")
+(allow file-read* (subpath (param soda-component-path)))
+
+; Required to load the language pack files used by the Speech On-Device
+; API (SODA).
+(define soda-language-pack-path "SODA_LANGUAGE_PACK_PATH")
+(allow file-read* (subpath (param soda-language-pack-path))) \ No newline at end of file
diff --git a/chromium/sandbox/policy/sandbox.cc b/chromium/sandbox/policy/sandbox.cc
index ff135818c72..590ecc9cf3f 100644
--- a/chromium/sandbox/policy/sandbox.cc
+++ b/chromium/sandbox/policy/sandbox.cc
@@ -81,8 +81,7 @@ bool Sandbox::Initialize(SandboxType sandbox_type,
// static
bool Sandbox::IsProcessSandboxed() {
auto* command_line = base::CommandLine::ForCurrentProcess();
- bool is_browser =
- !command_line->HasSwitch(service_manager::switches::kProcessType);
+ bool is_browser = !command_line->HasSwitch(switches::kProcessType);
if (!is_browser &&
base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoSandbox)) {
diff --git a/chromium/sandbox/policy/sandbox_type.cc b/chromium/sandbox/policy/sandbox_type.cc
index bbd13f05991..c96b8713198 100644
--- a/chromium/sandbox/policy/sandbox_type.cc
+++ b/chromium/sandbox/policy/sandbox_type.cc
@@ -28,6 +28,7 @@ bool IsUnsandboxedSandboxType(SandboxType sandbox_type) {
case SandboxType::kProxyResolver:
case SandboxType::kPdfConversion:
case SandboxType::kIconReader:
+ case SandboxType::kMediaFoundationCdm:
return false;
#endif
case SandboxType::kAudio:
@@ -75,8 +76,7 @@ void SetCommandLineFlagsForSandboxType(base::CommandLine* command_line,
SandboxType sandbox_type) {
switch (sandbox_type) {
case SandboxType::kNoSandbox:
- if (command_line->GetSwitchValueASCII(
- service_manager::switches::kProcessType) ==
+ if (command_line->GetSwitchValueASCII(switches::kProcessType) ==
switches::kUtilityProcess) {
DCHECK(!command_line->HasSwitch(switches::kServiceSandboxType));
command_line->AppendSwitchASCII(
@@ -92,24 +92,20 @@ void SetCommandLineFlagsForSandboxType(base::CommandLine* command_line,
break;
#endif
case SandboxType::kRenderer:
- DCHECK(command_line->GetSwitchValueASCII(
- service_manager::switches::kProcessType) ==
+ DCHECK(command_line->GetSwitchValueASCII(switches::kProcessType) ==
switches::kRendererProcess);
break;
case SandboxType::kGpu:
- DCHECK(command_line->GetSwitchValueASCII(
- service_manager::switches::kProcessType) ==
+ DCHECK(command_line->GetSwitchValueASCII(switches::kProcessType) ==
switches::kGpuProcess);
break;
case SandboxType::kPpapi:
- if (command_line->GetSwitchValueASCII(
- service_manager::switches::kProcessType) ==
+ if (command_line->GetSwitchValueASCII(switches::kProcessType) ==
switches::kUtilityProcess) {
command_line->AppendSwitchASCII(switches::kServiceSandboxType,
switches::kPpapiSandbox);
} else {
- DCHECK(command_line->GetSwitchValueASCII(
- service_manager::switches::kProcessType) ==
+ DCHECK(command_line->GetSwitchValueASCII(switches::kProcessType) ==
switches::kPpapiPluginProcess);
}
break;
@@ -124,6 +120,7 @@ void SetCommandLineFlagsForSandboxType(base::CommandLine* command_line,
case SandboxType::kProxyResolver:
case SandboxType::kPdfConversion:
case SandboxType::kIconReader:
+ case SandboxType::kMediaFoundationCdm:
#endif // defined(OS_WIN)
#if defined(OS_CHROMEOS)
case SandboxType::kIme:
@@ -133,8 +130,7 @@ void SetCommandLineFlagsForSandboxType(base::CommandLine* command_line,
case SandboxType::kSharingService:
#endif
case SandboxType::kSpeechRecognition:
- DCHECK(command_line->GetSwitchValueASCII(
- service_manager::switches::kProcessType) ==
+ DCHECK(command_line->GetSwitchValueASCII(switches::kProcessType) ==
switches::kUtilityProcess);
DCHECK(!command_line->HasSwitch(switches::kServiceSandboxType));
command_line->AppendSwitchASCII(
@@ -166,7 +162,7 @@ SandboxType SandboxTypeFromCommandLine(const base::CommandLine& command_line) {
#endif
std::string process_type =
- command_line.GetSwitchValueASCII(service_manager::switches::kProcessType);
+ command_line.GetSwitchValueASCII(switches::kProcessType);
if (process_type.empty())
return SandboxType::kNoSandbox;
@@ -249,6 +245,8 @@ std::string StringFromUtilitySandboxType(SandboxType sandbox_type) {
return switches::kPdfConversionSandbox;
case SandboxType::kIconReader:
return switches::kIconReaderSandbox;
+ case SandboxType::kMediaFoundationCdm:
+ return switches::kMediaFoundationCdmSandbox;
#endif // defined(OS_WIN)
#if defined(OS_CHROMEOS)
case SandboxType::kIme:
@@ -303,6 +301,8 @@ SandboxType UtilitySandboxTypeFromString(const std::string& sandbox_string) {
return SandboxType::kPdfConversion;
if (sandbox_string == switches::kIconReaderSandbox)
return SandboxType::kIconReader;
+ if (sandbox_string == switches::kMediaFoundationCdmSandbox)
+ return SandboxType::kMediaFoundationCdm;
#endif
if (sandbox_string == switches::kAudioSandbox)
return SandboxType::kAudio;
diff --git a/chromium/sandbox/policy/sandbox_type.h b/chromium/sandbox/policy/sandbox_type.h
index b133d3ab5eb..00e23062366 100644
--- a/chromium/sandbox/policy/sandbox_type.h
+++ b/chromium/sandbox/policy/sandbox_type.h
@@ -34,6 +34,9 @@ enum class SandboxType {
// The icon reader service.
kIconReader,
+
+ // The MediaFoundation CDM service process.
+ kMediaFoundationCdm,
#endif
#if defined(OS_FUCHSIA)
diff --git a/chromium/sandbox/policy/sandbox_type_unittest.cc b/chromium/sandbox/policy/sandbox_type_unittest.cc
index 51a470663f6..363475740df 100644
--- a/chromium/sandbox/policy/sandbox_type_unittest.cc
+++ b/chromium/sandbox/policy/sandbox_type_unittest.cc
@@ -36,7 +36,7 @@ TEST(SandboxTypeTest, Empty) {
TEST(SandboxTypeTest, Renderer) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
- command_line.AppendSwitchASCII(service_manager::switches::kProcessType,
+ command_line.AppendSwitchASCII(switches::kProcessType,
switches::kRendererProcess);
EXPECT_EQ(SandboxType::kRenderer, SandboxTypeFromCommandLine(command_line));
@@ -51,7 +51,7 @@ TEST(SandboxTypeTest, Renderer) {
TEST(SandboxTypeTest, Utility) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
- command_line.AppendSwitchASCII(service_manager::switches::kProcessType,
+ command_line.AppendSwitchASCII(switches::kProcessType,
switches::kUtilityProcess);
EXPECT_EQ(SandboxType::kUtility, SandboxTypeFromCommandLine(command_line));
@@ -123,8 +123,7 @@ TEST(SandboxTypeTest, Utility) {
TEST(SandboxTypeTest, GPU) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
- command_line.AppendSwitchASCII(service_manager::switches::kProcessType,
- switches::kGpuProcess);
+ command_line.AppendSwitchASCII(switches::kProcessType, switches::kGpuProcess);
SetCommandLineFlagsForSandboxType(&command_line, SandboxType::kGpu);
EXPECT_EQ(SandboxType::kGpu, SandboxTypeFromCommandLine(command_line));
@@ -137,7 +136,7 @@ TEST(SandboxTypeTest, GPU) {
TEST(SandboxTypeTest, PPAPIBroker) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
- command_line.AppendSwitchASCII(service_manager::switches::kProcessType,
+ command_line.AppendSwitchASCII(switches::kProcessType,
switches::kPpapiBrokerProcess);
EXPECT_EQ(SandboxType::kNoSandbox, SandboxTypeFromCommandLine(command_line));
@@ -150,7 +149,7 @@ TEST(SandboxTypeTest, PPAPIBroker) {
TEST(SandboxTypeTest, PPAPIPlugin) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
- command_line.AppendSwitchASCII(service_manager::switches::kProcessType,
+ command_line.AppendSwitchASCII(switches::kProcessType,
switches::kPpapiPluginProcess);
SetCommandLineFlagsForSandboxType(&command_line, SandboxType::kPpapi);
EXPECT_EQ(SandboxType::kPpapi, SandboxTypeFromCommandLine(command_line));
@@ -164,8 +163,7 @@ TEST(SandboxTypeTest, PPAPIPlugin) {
TEST(SandboxTypeTest, Nonesuch) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
- command_line.AppendSwitchASCII(service_manager::switches::kProcessType,
- "nonesuch");
+ command_line.AppendSwitchASCII(switches::kProcessType, "nonesuch");
// If tested here would CHECK.
command_line.AppendSwitchASCII(switches::kServiceSandboxType, "network");
diff --git a/chromium/sandbox/policy/switches.cc b/chromium/sandbox/policy/switches.cc
index e8e567d55b8..3afb8768843 100644
--- a/chromium/sandbox/policy/switches.cc
+++ b/chromium/sandbox/policy/switches.cc
@@ -38,6 +38,7 @@ const char kPdfConversionSandbox[] = "pdf_conversion";
const char kProxyResolverSandbox[] = "proxy_resolver";
const char kXrCompositingSandbox[] = "xr_compositing";
const char kIconReaderSandbox[] = "icon_reader";
+const char kMediaFoundationCdmSandbox[] = "mf_cdm";
#endif // OS_WIN
#if defined(OS_CHROMEOS)
@@ -109,6 +110,7 @@ const char kEnableSandboxLogging[] = "enable-sandbox-logging";
#endif
// Flags spied upon from other layers.
+const char kProcessType[] = "type";
const char kGpuProcess[] = "gpu-process";
const char kNaClBrokerProcess[] = "nacl-broker";
const char kNaClLoaderProcess[] = "nacl-loader";
diff --git a/chromium/sandbox/policy/switches.h b/chromium/sandbox/policy/switches.h
index 5fc71c805e1..e096e96308a 100644
--- a/chromium/sandbox/policy/switches.h
+++ b/chromium/sandbox/policy/switches.h
@@ -7,7 +7,6 @@
#include "build/build_config.h"
#include "sandbox/policy/export.h"
-#include "services/service_manager/embedder/switches.h"
namespace sandbox {
namespace policy {
@@ -36,6 +35,7 @@ SANDBOX_POLICY_EXPORT extern const char kPdfConversionSandbox[];
SANDBOX_POLICY_EXPORT extern const char kProxyResolverSandbox[];
SANDBOX_POLICY_EXPORT extern const char kXrCompositingSandbox[];
SANDBOX_POLICY_EXPORT extern const char kIconReaderSandbox[];
+SANDBOX_POLICY_EXPORT extern const char kMediaFoundationCdmSandbox[];
#endif // OS_WIN
#if defined(OS_CHROMEOS)
@@ -67,6 +67,7 @@ SANDBOX_POLICY_EXPORT extern const char kEnableSandboxLogging[];
#endif
// Flags spied upon from other layers.
+SANDBOX_POLICY_EXPORT extern const char kProcessType[];
SANDBOX_POLICY_EXPORT extern const char kGpuProcess[];
SANDBOX_POLICY_EXPORT extern const char kNaClBrokerProcess[];
SANDBOX_POLICY_EXPORT extern const char kNaClLoaderProcess[];
diff --git a/chromium/sandbox/policy/win/mf_cdm_sandbox_type_unittest.cc b/chromium/sandbox/policy/win/mf_cdm_sandbox_type_unittest.cc
new file mode 100644
index 00000000000..b2988859fbb
--- /dev/null
+++ b/chromium/sandbox/policy/win/mf_cdm_sandbox_type_unittest.cc
@@ -0,0 +1,28 @@
+// Copyright 2020 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/policy/sandbox_type.h"
+
+#include "base/command_line.h"
+#include "sandbox/policy/switches.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+TEST(SandboxTypeTest, Utility) {
+ // Setup to have '--type=utility' first.
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ command_line.AppendSwitchASCII(sandbox::policy::switches::kProcessType,
+ sandbox::policy::switches::kUtilityProcess);
+ EXPECT_EQ(sandbox::policy::SandboxType::kUtility,
+ sandbox::policy::SandboxTypeFromCommandLine(command_line));
+
+ base::CommandLine command_line2(command_line);
+ SetCommandLineFlagsForSandboxType(
+ &command_line2, sandbox::policy::SandboxType::kMediaFoundationCdm);
+ EXPECT_EQ(sandbox::policy::SandboxType::kMediaFoundationCdm,
+ sandbox::policy::SandboxTypeFromCommandLine(command_line2));
+}
+
+} // namespace media
diff --git a/chromium/sandbox/policy/win/sandbox_win.cc b/chromium/sandbox/policy/win/sandbox_win.cc
index d63d942c7e3..8c1e3aa58b6 100644
--- a/chromium/sandbox/policy/win/sandbox_win.cc
+++ b/chromium/sandbox/policy/win/sandbox_win.cc
@@ -621,12 +621,22 @@ ResultCode SetJobMemoryLimit(const base::CommandLine& cmd_line,
// isn't a security concern.
base::string16 GetAppContainerProfileName(const std::string& appcontainer_id,
SandboxType sandbox_type) {
- DCHECK(sandbox_type == SandboxType::kGpu ||
- sandbox_type == SandboxType::kXrCompositing);
+ std::string sandbox_base_name;
+ switch (sandbox_type) {
+ case SandboxType::kXrCompositing:
+ sandbox_base_name = std::string("cr.sb.xr");
+ break;
+ case SandboxType::kGpu:
+ sandbox_base_name = std::string("cr.sb.gpu");
+ break;
+ case SandboxType::kMediaFoundationCdm:
+ sandbox_base_name = std::string("cr.sb.cdm");
+ break;
+ default:
+ DCHECK(0);
+ }
+
auto sha1 = base::SHA1HashString(appcontainer_id);
- std::string sandbox_base_name = (sandbox_type == SandboxType::kXrCompositing)
- ? std::string("cr.sb.xr")
- : std::string("cr.sb.gpu");
std::string profile_name = base::StrCat(
{sandbox_base_name, base::HexEncode(sha1.data(), sha1.size())});
// CreateAppContainerProfile requires that the profile name is at most 64
@@ -640,7 +650,8 @@ base::string16 GetAppContainerProfileName(const std::string& appcontainer_id,
ResultCode SetupAppContainerProfile(AppContainerProfile* profile,
const base::CommandLine& command_line,
SandboxType sandbox_type) {
- if (sandbox_type != SandboxType::kGpu &&
+ if (sandbox_type != SandboxType::kMediaFoundationCdm &&
+ sandbox_type != SandboxType::kGpu &&
sandbox_type != SandboxType::kXrCompositing)
return SBOX_ERROR_UNSUPPORTED;
@@ -666,6 +677,36 @@ ResultCode SetupAppContainerProfile(AppContainerProfile* profile,
return SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY;
}
+ if (sandbox_type == SandboxType::kMediaFoundationCdm) {
+ // Please refer to the following design doc on why we add the capabilities:
+ // https://docs.google.com/document/d/19Y4Js5v3BlzA5uSuiVTvcvPNIOwmxcMSFJWtuc1A-w8/edit#heading=h.iqvhsrml3gl9
+ if (!profile->AddCapability(
+ sandbox::WellKnownCapabilities::kPrivateNetworkClientServer) ||
+ !profile->AddCapability(
+ sandbox::WellKnownCapabilities::kInternetClient)) {
+ DLOG(ERROR)
+ << "AppContainerProfile::AddCapability() - "
+ << "SandboxType::kMediaFoundationCdm internet capabilities failed";
+ return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY;
+ }
+
+ if (!profile->AddCapability(L"lpacCom") ||
+ !profile->AddCapability(L"lpacIdentityServices") ||
+ !profile->AddCapability(L"lpacMedia") ||
+ !profile->AddCapability(L"lpacPnPNotifications") ||
+ !profile->AddCapability(L"lpacServicesManagement") ||
+ !profile->AddCapability(L"lpacSessionManagement") ||
+ !profile->AddCapability(L"lpacAppExperience") ||
+ !profile->AddCapability(L"lpacAppServices") ||
+ !profile->AddCapability(L"lpacCryptoServices") ||
+ !profile->AddCapability(L"lpacEnterprisePolicyChangeNotifications")) {
+ DLOG(ERROR)
+ << "AppContainerProfile::AddCapability() - "
+ << "SandboxType::kMediaFoundationCdm lpac capabilities failed";
+ return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY;
+ }
+ }
+
std::vector<base::string16> base_caps = {
L"lpacChromeInstallFiles",
L"registryRead",
@@ -698,6 +739,9 @@ ResultCode SetupAppContainerProfile(AppContainerProfile* profile,
profile->SetEnableLowPrivilegeAppContainer(true);
}
+ if (sandbox_type == SandboxType::kMediaFoundationCdm)
+ profile->SetEnableLowPrivilegeAppContainer(true);
+
return SBOX_ALL_OK;
}
@@ -813,10 +857,14 @@ ResultCode SandboxWin::AddAppContainerProfileToPolicy(
bool SandboxWin::IsAppContainerEnabledForSandbox(
const base::CommandLine& command_line,
SandboxType sandbox_type) {
- if (sandbox_type != SandboxType::kGpu)
- return false;
if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
return false;
+
+ if (sandbox_type == SandboxType::kMediaFoundationCdm)
+ return true;
+
+ if (sandbox_type != SandboxType::kGpu)
+ return false;
return base::FeatureList::IsEnabled(features::kGpuAppContainer);
}
@@ -1016,6 +1064,15 @@ ResultCode SandboxWin::StartSandboxedProcess(
}
}
+ if (sandbox_type == SandboxType::kMediaFoundationCdm) {
+ // Set a policy that would normally allow for process creation. This allows
+ // the mf cdm process to launch the protected media pipeline process
+ // (mfpmp.exe) without process interception.
+ result = policy->SetJobLevel(JOB_INTERACTIVE, 0);
+ if (result != SBOX_ALL_OK)
+ return result;
+ }
+
#if !defined(OFFICIAL_BUILD)
// If stdout/stderr point to a Windows console, these calls will
// have no effect. These calls can fail with SBOX_ERROR_BAD_PARAMS.
@@ -1120,6 +1177,8 @@ std::string SandboxWin::GetSandboxTypeInEnglish(SandboxType sandbox_type) {
return "Proxy Resolver";
case SandboxType::kPdfConversion:
return "PDF Conversion";
+ case SandboxType::kMediaFoundationCdm:
+ return "Media Foundation CDM";
case SandboxType::kSharingService:
return "Sharing";
case SandboxType::kVideoCapture:
diff --git a/chromium/sandbox/win/src/broker_services.cc b/chromium/sandbox/win/src/broker_services.cc
index a00d1b15600..9f3d5395834 100644
--- a/chromium/sandbox/win/src/broker_services.cc
+++ b/chromium/sandbox/win/src/broker_services.cc
@@ -18,6 +18,7 @@
#include "base/win/scoped_handle.h"
#include "base/win/scoped_process_information.h"
#include "base/win/windows_version.h"
+#include "build/build_config.h"
#include "sandbox/win/src/app_container_profile.h"
#include "sandbox/win/src/process_mitigations.h"
#include "sandbox/win/src/sandbox.h"
@@ -138,8 +139,17 @@ ResultCode BrokerServicesBase::Init() {
no_targets_.Set(::CreateEventW(nullptr, true, false, nullptr));
- job_thread_.Set(::CreateThread(nullptr, 0, // Default security and stack.
- TargetEventsThread, this, 0, nullptr));
+#if defined(ARCH_CPU_32_BITS)
+ // Conserve address space in 32-bit Chrome. This thread uses a small and
+ // consistent amount and doesn't need the default of 1.5 MiB.
+ constexpr unsigned flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
+ constexpr size_t stack_size = 128 * 1024;
+#else
+ constexpr unsigned int flags = 0;
+ constexpr size_t stack_size = 0;
+#endif
+ job_thread_.Set(::CreateThread(nullptr, stack_size, // Default security.
+ TargetEventsThread, this, flags, nullptr));
if (!job_thread_.IsValid())
return SBOX_ERROR_CANNOT_INIT_BROKERSERVICES;
@@ -328,12 +338,13 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) {
::UnregisterWait(tracker->wait_handle);
tracker->wait_handle = INVALID_HANDLE_VALUE;
-
+ // Copy process_id so that we can legally reference it even after we have
+ // found the ProcessTracker object to delete.
+ const DWORD process_id = tracker->process_id;
// PID is unique until the process handle is closed in dtor.
processes.erase(std::remove_if(processes.begin(), processes.end(),
[&](auto&& p) -> bool {
- return p->process_id ==
- tracker->process_id;
+ return p->process_id == process_id;
}),
processes.end());