summaryrefslogtreecommitdiff
path: root/chromium/sandbox
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-24 12:15:48 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-28 13:30:04 +0000
commitb014812705fc80bff0a5c120dfcef88f349816dc (patch)
tree25a2e2d9fa285f1add86aa333389a839f81a39ae /chromium/sandbox
parent9f4560b1027ae06fdb497023cdcaf91b8511fa74 (diff)
downloadqtwebengine-chromium-b014812705fc80bff0a5c120dfcef88f349816dc.tar.gz
BASELINE: Update Chromium to 68.0.3440.125
Change-Id: I23f19369e01f688e496f5bf179abb521ad73874f Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/sandbox')
-rw-r--r--chromium/sandbox/linux/BUILD.gn5
-rw-r--r--chromium/sandbox/linux/suid/client/setuid_sandbox_host.cc2
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_channel.h3
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_client.cc159
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_client.h13
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_command.h2
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_host.cc224
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_process.cc2
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_simple_message.cc322
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_simple_message.h106
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc703
-rw-r--r--chromium/sandbox/win/src/address_sanitizer_test.cc2
-rw-r--r--chromium/sandbox/win/src/crosscall_client.h3
-rw-r--r--chromium/sandbox/win/src/file_policy_test.cc38
-rw-r--r--chromium/sandbox/win/src/handle_closer_agent.cc5
-rw-r--r--chromium/sandbox/win/src/process_mitigations_dyncode_unittest.cc15
-rw-r--r--chromium/sandbox/win/src/process_mitigations_unittest.cc27
-rw-r--r--chromium/sandbox/win/src/process_mitigations_win32k_unittest.cc15
-rw-r--r--chromium/sandbox/win/src/sandbox_nt_util.cc31
-rw-r--r--chromium/sandbox/win/src/sandbox_nt_util.h8
-rw-r--r--chromium/sandbox/win/src/sandbox_nt_util_unittest.cc46
-rw-r--r--chromium/sandbox/win/src/security_level.h18
-rw-r--r--chromium/sandbox/win/src/target_interceptions.cc50
-rw-r--r--chromium/sandbox/win/src/target_services.cc36
24 files changed, 1546 insertions, 289 deletions
diff --git a/chromium/sandbox/linux/BUILD.gn b/chromium/sandbox/linux/BUILD.gn
index 2da57fc9947..b409fd6c4da 100644
--- a/chromium/sandbox/linux/BUILD.gn
+++ b/chromium/sandbox/linux/BUILD.gn
@@ -99,6 +99,7 @@ source_set("sandbox_linux_unittests_sources") {
"services/yama_unittests.cc",
"syscall_broker/broker_file_permission_unittest.cc",
"syscall_broker/broker_process_unittest.cc",
+ "syscall_broker/broker_simple_message_unittest.cc",
"tests/main.cc",
"tests/scoped_temporary_file.cc",
"tests/scoped_temporary_file.h",
@@ -371,6 +372,8 @@ component("sandbox_services") {
"syscall_broker/broker_permission_list.h",
"syscall_broker/broker_process.cc",
"syscall_broker/broker_process.h",
+ "syscall_broker/broker_simple_message.cc",
+ "syscall_broker/broker_simple_message.h",
]
defines = [ "SANDBOX_IMPLEMENTATION" ]
@@ -420,6 +423,8 @@ component("sandbox_services") {
"syscall_broker/broker_permission_list.h",
"syscall_broker/broker_process.cc",
"syscall_broker/broker_process.h",
+ "syscall_broker/broker_simple_message.cc",
+ "syscall_broker/broker_simple_message.h",
]
} else if (!is_android) {
sources += [
diff --git a/chromium/sandbox/linux/suid/client/setuid_sandbox_host.cc b/chromium/sandbox/linux/suid/client/setuid_sandbox_host.cc
index 89836aaabb3..6096c3bc370 100644
--- a/chromium/sandbox/linux/suid/client/setuid_sandbox_host.cc
+++ b/chromium/sandbox/linux/suid/client/setuid_sandbox_host.cc
@@ -120,7 +120,7 @@ bool SetuidSandboxHost::IsDisabledViaEnvironment() {
base::FilePath SetuidSandboxHost::GetSandboxBinaryPath() {
base::FilePath sandbox_binary;
base::FilePath exe_dir;
- if (PathService::Get(base::DIR_EXE, &exe_dir)) {
+ if (base::PathService::Get(base::DIR_EXE, &exe_dir)) {
base::FilePath sandbox_candidate = exe_dir.AppendASCII("chrome-sandbox");
if (base::PathExists(sandbox_candidate))
sandbox_binary = sandbox_candidate;
diff --git a/chromium/sandbox/linux/syscall_broker/broker_channel.h b/chromium/sandbox/linux/syscall_broker/broker_channel.h
index 2abdba413a8..7ee39d1418c 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_channel.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_channel.h
@@ -7,6 +7,7 @@
#include "base/files/scoped_file.h"
#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
namespace sandbox {
@@ -15,7 +16,7 @@ namespace syscall_broker {
// A small class to create a pipe-like communication channel. It is based on a
// SOCK_SEQPACKET unix socket, which is connection-based and guaranteed to
// preserve message boundaries.
-class BrokerChannel {
+class SANDBOX_EXPORT BrokerChannel {
public:
typedef base::ScopedFD EndPoint;
static void CreatePair(EndPoint* reader, EndPoint* writer);
diff --git a/chromium/sandbox/linux/syscall_broker/broker_client.cc b/chromium/sandbox/linux/syscall_broker/broker_client.cc
index 6ae93045b2a..50395c09a2a 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_client.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_client.cc
@@ -18,6 +18,7 @@
#include "sandbox/linux/syscall_broker/broker_channel.h"
#include "sandbox/linux/syscall_broker/broker_command.h"
#include "sandbox/linux/syscall_broker/broker_permission_list.h"
+#include "sandbox/linux/syscall_broker/broker_simple_message.h"
#if defined(OS_ANDROID) && !defined(MSG_CMSG_CLOEXEC)
#define MSG_CMSG_CLOEXEC 0x40000000
@@ -29,13 +30,11 @@ namespace syscall_broker {
BrokerClient::BrokerClient(const BrokerPermissionList& broker_permission_list,
BrokerChannel::EndPoint ipc_channel,
const BrokerCommandSet& allowed_command_set,
- bool fast_check_in_client,
- bool quiet_failures_for_tests)
+ bool fast_check_in_client)
: broker_permission_list_(broker_permission_list),
ipc_channel_(std::move(ipc_channel)),
allowed_command_set_(allowed_command_set),
- fast_check_in_client_(fast_check_in_client),
- quiet_failures_for_tests_(quiet_failures_for_tests) {}
+ fast_check_in_client_(fast_check_in_client) {}
BrokerClient::~BrokerClient() {}
@@ -85,31 +84,33 @@ int BrokerClient::Readlink(const char* path, char* buf, size_t bufsize) const {
return -broker_permission_list_.denied_errno();
}
- base::Pickle write_pickle;
- write_pickle.WriteInt(COMMAND_READLINK);
- write_pickle.WriteString(path);
- RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+ // Message structure:
+ // int: syscall_type
+ // char[]: pathname, including '\0' terminator
+ BrokerSimpleMessage message;
+ RAW_CHECK(message.AddIntToMessage(COMMAND_READLINK));
+ RAW_CHECK(message.AddStringToMessage(path));
int returned_fd = -1;
- uint8_t reply_buf[kMaxMessageLength];
- ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf,
- sizeof(reply_buf), &returned_fd);
+ BrokerSimpleMessage reply;
+ ssize_t msg_len =
+ message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
if (msg_len < 0)
return msg_len;
- base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
- base::PickleIterator iter(read_pickle);
int return_value = -1;
- int return_length = 0;
+ size_t return_length = 0;
const char* return_data = nullptr;
- if (!iter.ReadInt(&return_value))
+ if (!reply.ReadInt(&return_value))
return -ENOMEM;
if (return_value < 0)
return return_value;
- if (!iter.ReadData(&return_data, &return_length))
+
+ if (!reply.ReadData(&return_data, &return_length))
return -ENOMEM;
if (return_length < 0)
return -ENOMEM;
+
if (static_cast<size_t>(return_length) > bufsize)
return -ENAMETOOLONG;
memcpy(buf, return_data, return_length);
@@ -126,23 +127,21 @@ int BrokerClient::Rename(const char* oldpath, const char* newpath) const {
return -broker_permission_list_.denied_errno();
}
- base::Pickle write_pickle;
- write_pickle.WriteInt(COMMAND_RENAME);
- write_pickle.WriteString(oldpath);
- write_pickle.WriteString(newpath);
- RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+ BrokerSimpleMessage message;
+ RAW_CHECK(message.AddIntToMessage(COMMAND_RENAME));
+ RAW_CHECK(message.AddStringToMessage(oldpath));
+ RAW_CHECK(message.AddStringToMessage(newpath));
int returned_fd = -1;
- uint8_t reply_buf[kMaxMessageLength];
- ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf,
- sizeof(reply_buf), &returned_fd);
+ BrokerSimpleMessage reply;
+ ssize_t msg_len =
+ message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
+
if (msg_len < 0)
return msg_len;
- base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
- base::PickleIterator iter(read_pickle);
int return_value = -1;
- if (!iter.ReadInt(&return_value))
+ if (!reply.ReadInt(&return_value))
return -ENOMEM;
return return_value;
@@ -198,23 +197,22 @@ int BrokerClient::Unlink(const char* path) const {
int BrokerClient::PathOnlySyscall(BrokerCommand syscall_type,
const char* pathname) const {
- base::Pickle write_pickle;
- write_pickle.WriteInt(syscall_type);
- write_pickle.WriteString(pathname);
- RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+ BrokerSimpleMessage message;
+ RAW_CHECK(message.AddIntToMessage(syscall_type));
+ RAW_CHECK(message.AddStringToMessage(pathname));
int returned_fd = -1;
- uint8_t reply_buf[kMaxMessageLength];
- ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf,
- sizeof(reply_buf), &returned_fd);
+ BrokerSimpleMessage reply;
+ ssize_t msg_len =
+ message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
+
if (msg_len < 0)
return msg_len;
- base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
- base::PickleIterator iter(read_pickle);
int return_value = -1;
- if (!iter.ReadInt(&return_value))
+ if (!reply.ReadInt(&return_value))
return -ENOMEM;
+
return return_value;
}
@@ -225,23 +223,21 @@ int BrokerClient::PathOnlySyscall(BrokerCommand syscall_type,
int BrokerClient::PathAndFlagsSyscall(BrokerCommand syscall_type,
const char* pathname,
int flags) const {
- base::Pickle write_pickle;
- write_pickle.WriteInt(syscall_type);
- write_pickle.WriteString(pathname);
- write_pickle.WriteInt(flags);
- RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+ BrokerSimpleMessage message;
+ RAW_CHECK(message.AddIntToMessage(syscall_type));
+ RAW_CHECK(message.AddStringToMessage(pathname));
+ RAW_CHECK(message.AddIntToMessage(flags));
int returned_fd = -1;
- uint8_t reply_buf[kMaxMessageLength];
- ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf,
- sizeof(reply_buf), &returned_fd);
+ BrokerSimpleMessage reply;
+ ssize_t msg_len =
+ message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
+
if (msg_len < 0)
- return msg_len;
+ return -ENOMEM;
- base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
- base::PickleIterator iter(read_pickle);
int return_value = -1;
- if (!iter.ReadInt(&return_value))
+ if (!reply.ReadInt(&return_value))
return -ENOMEM;
return return_value;
@@ -268,23 +264,21 @@ int BrokerClient::PathAndFlagsSyscallReturningFD(BrokerCommand syscall_type,
flags &= ~kCurrentProcessOpenFlagsMask;
}
- base::Pickle write_pickle;
- write_pickle.WriteInt(syscall_type);
- write_pickle.WriteString(pathname);
- write_pickle.WriteInt(flags);
- RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+ BrokerSimpleMessage message;
+ RAW_CHECK(message.AddIntToMessage(syscall_type));
+ RAW_CHECK(message.AddStringToMessage(pathname));
+ RAW_CHECK(message.AddIntToMessage(flags));
int returned_fd = -1;
- uint8_t reply_buf[kMaxMessageLength];
- ssize_t msg_len = SendRecvRequest(write_pickle, recvmsg_flags, reply_buf,
- sizeof(reply_buf), &returned_fd);
+ BrokerSimpleMessage reply;
+ ssize_t msg_len = message.SendRecvMsgWithFlags(
+ ipc_channel_.get(), recvmsg_flags, &returned_fd, &reply);
+
if (msg_len < 0)
- return msg_len;
+ return -ENOMEM;
- base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
- base::PickleIterator iter(read_pickle);
int return_value = -1;
- if (!iter.ReadInt(&return_value))
+ if (!reply.ReadInt(&return_value))
return -ENOMEM;
if (return_value < 0)
return return_value;
@@ -302,28 +296,27 @@ int BrokerClient::StatFamilySyscall(BrokerCommand syscall_type,
const char* pathname,
void* result_ptr,
size_t expected_result_size) const {
- base::Pickle write_pickle;
- write_pickle.WriteInt(syscall_type);
- write_pickle.WriteString(pathname);
- RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+ BrokerSimpleMessage message;
+ RAW_CHECK(message.AddIntToMessage(syscall_type));
+ RAW_CHECK(message.AddStringToMessage(pathname));
int returned_fd = -1;
- uint8_t reply_buf[kMaxMessageLength];
- ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf,
- sizeof(reply_buf), &returned_fd);
+ BrokerSimpleMessage reply;
+ ssize_t msg_len =
+ message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
+
if (msg_len < 0)
return msg_len;
- base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
- base::PickleIterator iter(read_pickle);
int return_value = -1;
- int return_length = 0;
+ size_t return_length = 0;
const char* return_data = nullptr;
- if (!iter.ReadInt(&return_value))
+
+ if (!reply.ReadInt(&return_value))
return -ENOMEM;
if (return_value < 0)
return return_value;
- if (!iter.ReadData(&return_data, &return_length))
+ if (!reply.ReadData(&return_data, &return_length))
return -ENOMEM;
if (static_cast<size_t>(return_length) != expected_result_size)
return -ENOMEM;
@@ -331,25 +324,5 @@ int BrokerClient::StatFamilySyscall(BrokerCommand syscall_type,
return return_value;
}
-// Send a request (in request_pickle) that will include a new temporary
-// socketpair as well (created internally by SendRecvMsg()). Then read the
-// reply on this new socketpair in |reply_buf| and put an eventual attached
-// file descriptor (if any) in |returned_fd|.
-ssize_t BrokerClient::SendRecvRequest(const base::Pickle& request_pickle,
- int recvmsg_flags,
- uint8_t* reply_buf,
- size_t reply_buf_size,
- int* returned_fd) const {
- ssize_t msg_len = base::UnixDomainSocket::SendRecvMsgWithFlags(
- ipc_channel_.get(), reply_buf, reply_buf_size, recvmsg_flags, returned_fd,
- request_pickle);
- if (msg_len <= 0) {
- if (!quiet_failures_for_tests_)
- RAW_LOG(ERROR, "Could not make request to broker process");
- return -ENOMEM;
- }
- return msg_len;
-}
-
} // 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 fc108f1abb4..ead90d45c05 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_client.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_client.h
@@ -10,7 +10,6 @@
#include <unistd.h>
#include "base/macros.h"
-#include "base/pickle.h"
#include "sandbox/linux/syscall_broker/broker_channel.h"
#include "sandbox/linux/syscall_broker/broker_command.h"
@@ -33,12 +32,10 @@ class BrokerClient {
// and save an IPC round trip.
// |ipc_channel| needs to be a suitable SOCK_SEQPACKET unix socket.
// |fast_check_in_client| should be set to true and
- // |quiet_failures_for_tests| to false unless you are writing tests.
BrokerClient(const BrokerPermissionList& policy,
BrokerChannel::EndPoint ipc_channel,
const BrokerCommandSet& allowed_command_set,
- bool fast_check_in_client,
- bool quiet_failures_for_tests);
+ bool fast_check_in_client);
~BrokerClient();
// Get the file descriptor used for IPC. This is used for tests.
@@ -93,20 +90,12 @@ class BrokerClient {
void* result_ptr,
size_t expected_result_size) const;
- ssize_t SendRecvRequest(const base::Pickle& request_pickle,
- int recvmsg_flags,
- uint8_t* reply_buf,
- size_t reply_buf_size,
- int* returned_fd) const;
-
const BrokerPermissionList& broker_permission_list_;
const BrokerChannel::EndPoint ipc_channel_;
const BrokerCommandSet allowed_command_set_;
const bool fast_check_in_client_; // Whether to forward a request that we
// know will be denied to the broker. (Used
// for tests).
- const bool quiet_failures_for_tests_; // Disable certain error message when
- // testing for failures.
DISALLOW_COPY_AND_ASSIGN(BrokerClient);
};
diff --git a/chromium/sandbox/linux/syscall_broker/broker_command.h b/chromium/sandbox/linux/syscall_broker/broker_command.h
index aa22b94aba7..1c4596778b0 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_command.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_command.h
@@ -17,8 +17,6 @@ namespace syscall_broker {
class BrokerPermissionList;
-constexpr size_t kMaxMessageLength = 4096;
-
// Some flags are local to the current process and cannot be sent over a Unix
// socket. They need special treatment from the client.
// O_CLOEXEC is tricky because in theory another thread could call execve()
diff --git a/chromium/sandbox/linux/syscall_broker/broker_host.cc b/chromium/sandbox/linux/syscall_broker/broker_host.cc
index bdfa8753a6d..7855d98add5 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_host.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_host.cc
@@ -16,15 +16,13 @@
#include <string>
#include <utility>
-#include <vector>
#include "base/files/scoped_file.h"
#include "base/logging.h"
-#include "base/pickle.h"
#include "base/posix/eintr_wrapper.h"
-#include "base/posix/unix_domain_socket.h"
#include "sandbox/linux/syscall_broker/broker_command.h"
#include "sandbox/linux/syscall_broker/broker_permission_list.h"
+#include "sandbox/linux/syscall_broker/broker_simple_message.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"
namespace sandbox {
@@ -41,175 +39,185 @@ int sys_open(const char* pathname, int flags) {
return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode);
}
+bool GetPathAndFlags(BrokerSimpleMessage* message,
+ const char** pathname,
+ int* flags) {
+ return message->ReadString(pathname) && message->ReadInt(flags);
+}
+
// Perform access(2) on |requested_filename| with mode |mode| if allowed by our
-// permission_list. Write the syscall return value (-errno) to |write_pickle|.
+// permission_list. Write the syscall return value (-errno) to |reply|.
void AccessFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list,
const std::string& requested_filename,
int mode,
- base::Pickle* write_pickle) {
+ BrokerSimpleMessage* reply) {
const char* file_to_access = NULL;
if (!CommandAccessIsSafe(allowed_command_set, permission_list,
requested_filename.c_str(), mode, &file_to_access)) {
- write_pickle->WriteInt(-permission_list.denied_errno());
+ RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return;
}
- CHECK(file_to_access);
+ RAW_CHECK(file_to_access);
if (access(file_to_access, mode) < 0) {
- write_pickle->WriteInt(-errno);
+ RAW_CHECK(reply->AddIntToMessage(-errno));
return;
}
- write_pickle->WriteInt(0);
+ RAW_CHECK(reply->AddIntToMessage(0));
}
+// Performs mkdir(2) on |filename| with mode |mode| if allowed by our
+// permission_list. Write the syscall return value (-errno) to |reply|.
void MkdirFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list,
const std::string& filename,
int mode,
- base::Pickle* write_pickle) {
+ BrokerSimpleMessage* reply) {
const char* file_to_access = nullptr;
if (!CommandMkdirIsSafe(allowed_command_set, permission_list,
filename.c_str(), &file_to_access)) {
- write_pickle->WriteInt(-permission_list.denied_errno());
+ RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return;
}
if (mkdir(file_to_access, mode) < 0) {
- write_pickle->WriteInt(-errno);
+ RAW_CHECK(reply->AddIntToMessage(-errno));
return;
}
- write_pickle->WriteInt(0);
+ RAW_CHECK(reply->AddIntToMessage(0));
}
// Open |requested_filename| with |flags| if allowed by our permission list.
-// Write the syscall return value (-errno) to |write_pickle| and append
-// a file descriptor to |opened_files| if relevant.
+// Write the syscall return value (-errno) to |reply| and return the
+// file descriptor in the |opened_file| if relevant.
void OpenFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list,
const std::string& requested_filename,
int flags,
- base::Pickle* write_pickle,
- std::vector<int>* opened_files) {
+ BrokerSimpleMessage* reply,
+ int* opened_file) {
const char* file_to_open = NULL;
bool unlink_after_open = false;
if (!CommandOpenIsSafe(allowed_command_set, permission_list,
requested_filename.c_str(), flags, &file_to_open,
&unlink_after_open)) {
- write_pickle->WriteInt(-permission_list.denied_errno());
+ RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return;
}
CHECK(file_to_open);
int opened_fd = sys_open(file_to_open, flags);
if (opened_fd < 0) {
- write_pickle->WriteInt(-errno);
+ RAW_CHECK(reply->AddIntToMessage(-errno));
return;
}
if (unlink_after_open)
unlink(file_to_open);
- opened_files->push_back(opened_fd);
- write_pickle->WriteInt(0);
+ *opened_file = opened_fd;
+ RAW_CHECK(reply->AddIntToMessage(0));
}
-// Perform rename(2) on |old_filename| to |new_filename| and marshal the
-// result to |write_pickle|.
+// Perform rename(2) on |old_filename| to |new_filename| and write the
+// result to |return_val|.
void RenameFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list,
const std::string& old_filename,
const std::string& new_filename,
- base::Pickle* write_pickle) {
+ BrokerSimpleMessage* reply) {
const char* old_file_to_access = nullptr;
const char* new_file_to_access = nullptr;
if (!CommandRenameIsSafe(allowed_command_set, permission_list,
old_filename.c_str(), new_filename.c_str(),
&old_file_to_access, &new_file_to_access)) {
- write_pickle->WriteInt(-permission_list.denied_errno());
+ RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return;
}
if (rename(old_file_to_access, new_file_to_access) < 0) {
- write_pickle->WriteInt(-errno);
+ RAW_CHECK(reply->AddIntToMessage(-errno));
return;
}
- write_pickle->WriteInt(0);
+ RAW_CHECK(reply->AddIntToMessage(0));
}
// Perform readlink(2) on |filename| using a buffer of MAX_PATH bytes.
void ReadlinkFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list,
const std::string& filename,
- base::Pickle* write_pickle) {
+ BrokerSimpleMessage* reply) {
const char* file_to_access = nullptr;
if (!CommandReadlinkIsSafe(allowed_command_set, permission_list,
filename.c_str(), &file_to_access)) {
- write_pickle->WriteInt(-permission_list.denied_errno());
+ RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return;
}
char buf[PATH_MAX];
ssize_t result = readlink(file_to_access, buf, sizeof(buf));
if (result < 0) {
- write_pickle->WriteInt(-errno);
+ RAW_CHECK(reply->AddIntToMessage(-errno));
return;
}
- write_pickle->WriteInt(result);
- write_pickle->WriteData(buf, result);
+ RAW_CHECK(reply->AddIntToMessage(result));
+ RAW_CHECK(reply->AddDataToMessage(buf, result));
}
void RmdirFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list,
const std::string& requested_filename,
- base::Pickle* write_pickle) {
+ BrokerSimpleMessage* reply) {
const char* file_to_access = nullptr;
if (!CommandRmdirIsSafe(allowed_command_set, permission_list,
requested_filename.c_str(), &file_to_access)) {
- write_pickle->WriteInt(-permission_list.denied_errno());
+ RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return;
}
if (rmdir(file_to_access) < 0) {
- write_pickle->WriteInt(-errno);
+ RAW_CHECK(reply->AddIntToMessage(-errno));
return;
}
- write_pickle->WriteInt(0);
+ RAW_CHECK(reply->AddIntToMessage(0));
}
-// Perform stat(2) on |requested_filename| and marshal the result to
-// |write_pickle|.
+// Perform stat(2) on |requested_filename| and write the result to
+// |return_val|.
void StatFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list,
BrokerCommand command_type,
const std::string& requested_filename,
- base::Pickle* write_pickle) {
+ BrokerSimpleMessage* reply) {
const char* file_to_access = nullptr;
if (!CommandStatIsSafe(allowed_command_set, permission_list,
requested_filename.c_str(), &file_to_access)) {
- write_pickle->WriteInt(-permission_list.denied_errno());
+ RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return;
}
if (command_type == COMMAND_STAT) {
struct stat sb;
if (stat(file_to_access, &sb) < 0) {
- write_pickle->WriteInt(-errno);
+ RAW_CHECK(reply->AddIntToMessage(-errno));
return;
}
- write_pickle->WriteInt(0);
- write_pickle->WriteData(reinterpret_cast<char*>(&sb), sizeof(sb));
+ RAW_CHECK(reply->AddIntToMessage(0));
+ RAW_CHECK(
+ 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.
- write_pickle->WriteInt(-ENOSYS);
+ RAW_CHECK(reply->AddIntToMessage(-ENOSYS));
return;
#else
struct stat64 sb;
if (stat64(file_to_access, &sb) < 0) {
- write_pickle->WriteInt(-errno);
+ RAW_CHECK(reply->AddIntToMessage(-errno));
return;
}
- write_pickle->WriteInt(0);
- write_pickle->WriteData(reinterpret_cast<char*>(&sb), sizeof(sb));
+ RAW_CHECK(reply->AddIntToMessage(0));
+ RAW_CHECK(
+ reply->AddDataToMessage(reinterpret_cast<char*>(&sb), sizeof(sb)));
#endif // defined(__ANDROID_API__) && __ANDROID_API__ < 21
}
}
@@ -217,100 +225,108 @@ void StatFileForIPC(const BrokerCommandSet& allowed_command_set,
void UnlinkFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list,
const std::string& requested_filename,
- base::Pickle* write_pickle) {
+ BrokerSimpleMessage* message) {
const char* file_to_access = nullptr;
if (!CommandUnlinkIsSafe(allowed_command_set, permission_list,
requested_filename.c_str(), &file_to_access)) {
- write_pickle->WriteInt(-permission_list.denied_errno());
+ RAW_CHECK(message->AddIntToMessage(-permission_list.denied_errno()));
return;
}
if (unlink(file_to_access) < 0) {
- write_pickle->WriteInt(-errno);
+ RAW_CHECK(message->AddIntToMessage(-errno));
return;
}
- write_pickle->WriteInt(0);
+ RAW_CHECK(message->AddIntToMessage(0));
}
// Handle a |command_type| request contained in |iter| and write the reply
-// to |write_pickle|, adding any files opened to |opened_files|.
+// to |reply|.
bool HandleRemoteCommand(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list,
- base::PickleIterator iter,
- base::Pickle* write_pickle,
- std::vector<int>* opened_files) {
+ BrokerSimpleMessage* message,
+ BrokerSimpleMessage* reply,
+ int* opened_file) {
+ // Message structure:
+ // int: command type
+ // char[]: pathname
+ // int: flags
int command_type;
- if (!iter.ReadInt(&command_type))
+ if (!message->ReadInt(&command_type))
return false;
switch (command_type) {
case COMMAND_ACCESS: {
- std::string requested_filename;
+ const char* requested_filename;
int flags = 0;
- if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags))
+ if (!GetPathAndFlags(message, &requested_filename, &flags))
return false;
AccessFileForIPC(allowed_command_set, permission_list, requested_filename,
- flags, write_pickle);
+ flags, reply);
break;
}
case COMMAND_MKDIR: {
- std::string requested_filename;
+ const char* requested_filename;
int mode = 0;
- if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&mode))
+ if (!GetPathAndFlags(message, &requested_filename, &mode))
return false;
MkdirFileForIPC(allowed_command_set, permission_list, requested_filename,
- mode, write_pickle);
+ mode, reply);
break;
}
case COMMAND_OPEN: {
- std::string requested_filename;
+ const char* requested_filename;
int flags = 0;
- if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags))
+ if (!GetPathAndFlags(message, &requested_filename, &flags))
return false;
OpenFileForIPC(allowed_command_set, permission_list, requested_filename,
- flags, write_pickle, opened_files);
+ flags, reply, opened_file);
break;
}
case COMMAND_READLINK: {
- std::string filename;
- if (!iter.ReadString(&filename))
+ const char* filename;
+ if (!message->ReadString(&filename))
return false;
- ReadlinkFileForIPC(allowed_command_set, permission_list, filename,
- write_pickle);
+
+ ReadlinkFileForIPC(allowed_command_set, permission_list, filename, reply);
break;
}
case COMMAND_RENAME: {
- std::string old_filename;
- std::string new_filename;
- if (!iter.ReadString(&old_filename) || !iter.ReadString(&new_filename))
+ const char* old_filename;
+ if (!message->ReadString(&old_filename))
+ return false;
+
+ const char* new_filename;
+ if (!message->ReadString(&new_filename))
return false;
+
RenameFileForIPC(allowed_command_set, permission_list, old_filename,
- new_filename, write_pickle);
+ new_filename, reply);
break;
}
case COMMAND_RMDIR: {
- std::string requested_filename;
- if (!iter.ReadString(&requested_filename))
+ const char* requested_filename;
+ if (!message->ReadString(&requested_filename))
return false;
RmdirFileForIPC(allowed_command_set, permission_list, requested_filename,
- write_pickle);
+ reply);
break;
}
case COMMAND_STAT:
case COMMAND_STAT64: {
- std::string requested_filename;
- if (!iter.ReadString(&requested_filename))
+ const char* requested_filename;
+ if (!message->ReadString(&requested_filename))
return false;
StatFileForIPC(allowed_command_set, permission_list,
static_cast<BrokerCommand>(command_type),
- requested_filename, write_pickle);
+ requested_filename, reply);
break;
}
case COMMAND_UNLINK: {
- std::string requested_filename;
- if (!iter.ReadString(&requested_filename))
+ const char* requested_filename;
+ if (!message->ReadString(&requested_filename))
return false;
UnlinkFileForIPC(allowed_command_set, permission_list, requested_filename,
- write_pickle);
+ reply);
break;
}
default:
@@ -336,48 +352,36 @@ BrokerHost::~BrokerHost() {}
// 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 {
- std::vector<base::ScopedFD> fds;
- char buf[kMaxMessageLength];
+ BrokerSimpleMessage message;
errno = 0;
- const ssize_t msg_len = base::UnixDomainSocket::RecvMsg(
- ipc_channel_.get(), buf, sizeof(buf), &fds);
+ 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 should send exactly one file descriptor, on which we
+ // The client sends exactly one file descriptor, on which we
// will write the reply.
- if (msg_len < 0 || fds.size() != 1 || fds[0].get() < 0) {
+ if (msg_len < 0) {
PLOG(ERROR) << "Error reading message from the client";
return RequestStatus::FAILURE;
}
- base::ScopedFD temporary_ipc(std::move(fds[0]));
-
- base::Pickle pickle(buf, msg_len);
- base::PickleIterator iter(pickle);
- base::Pickle write_pickle;
- std::vector<int> opened_files;
+ BrokerSimpleMessage reply;
+ int opened_file = -1;
bool result =
- HandleRemoteCommand(allowed_command_set_, broker_permission_list_, iter,
- &write_pickle, &opened_files);
-
- if (result) {
- CHECK_LE(write_pickle.size(), kMaxMessageLength);
- ssize_t sent = base::UnixDomainSocket::SendMsg(
- temporary_ipc.get(), write_pickle.data(), write_pickle.size(),
- opened_files);
- if (sent <= 0) {
- LOG(ERROR) << "Could not send IPC reply";
- result = false;
- }
- }
+ HandleRemoteCommand(allowed_command_set_, broker_permission_list_,
+ &message, &reply, &opened_file);
+
+ ssize_t sent = reply.SendMsg(temporary_ipc.get(), opened_file);
+ if (sent < 0)
+ LOG(ERROR) << "sent failed";
- // Close anything we have opened in this process.
- for (int fd : opened_files) {
- int ret = IGNORE_EINTR(close(fd));
+ if (opened_file >= 0) {
+ int ret = IGNORE_EINTR(close(opened_file));
DCHECK(!ret) << "Could not close file descriptor";
}
diff --git a/chromium/sandbox/linux/syscall_broker/broker_process.cc b/chromium/sandbox/linux/syscall_broker/broker_process.cc
index 8ebce31669e..40fff3f6130 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_process.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_process.cc
@@ -78,7 +78,7 @@ bool BrokerProcess::Init(
broker_pid_ = child_pid;
broker_client_ = std::make_unique<BrokerClient>(
broker_permission_list_, std::move(ipc_writer), allowed_command_set_,
- fast_check_in_client_, quiet_failures_for_tests_);
+ fast_check_in_client_);
initialized_ = true;
return true;
}
diff --git a/chromium/sandbox/linux/syscall_broker/broker_simple_message.cc b/chromium/sandbox/linux/syscall_broker/broker_simple_message.cc
new file mode 100644
index 00000000000..dd504da5d3c
--- /dev/null
+++ b/chromium/sandbox/linux/syscall_broker/broker_simple_message.cc
@@ -0,0 +1,322 @@
+// Copyright 2018 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/broker_simple_message.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/numerics/safe_math.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket.h"
+#include "base/process/process_handle.h"
+#include "build/build_config.h"
+
+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,
+ BrokerSimpleMessage* reply) {
+ RAW_CHECK(reply);
+
+ // This socketpair is only used for the IPC and is cleaned up before
+ // returning.
+ base::ScopedFD recv_sock;
+ base::ScopedFD send_sock;
+ if (!base::CreateSocketPair(&recv_sock, &send_sock))
+ return -1;
+
+ if (!SendMsg(fd, send_sock.get()))
+ 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);
+ 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) {
+ if (broken_)
+ return false;
+
+ 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(int));
+ char control_buffer[control_len];
+ if (send_fd >= 0) {
+ 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(int));
+ memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(int));
+ msg.msg_controllen = cmsg->cmsg_len;
+ }
+
+ // Avoid a SIGPIPE if the other end breaks the connection.
+ // Due to a bug in the Linux kernel (net/unix/af_unix.c) MSG_NOSIGNAL isn't
+ // regarded for SOCK_SEQPACKET in the AF_UNIX domain, but it is mandated by
+ // POSIX.
+ const int flags = MSG_NOSIGNAL;
+ const ssize_t r = HANDLE_EINTR(sendmsg(fd, &msg, flags));
+ return static_cast<ssize_t>(length_) == r;
+}
+
+ssize_t BrokerSimpleMessage::RecvMsgWithFlags(int fd,
+ int flags,
+ base::ScopedFD* return_fd) {
+ // The message must be fresh and unused.
+ RAW_CHECK(!read_only_ && !write_only_);
+ read_only_ = true; // The message should not be written to again.
+ struct msghdr msg = {};
+ struct iovec iov = {message_, kMaxMessageLength};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+#if defined(OS_NACL_NONSFI)
+ const size_t kControlBufferSize =
+ CMSG_SPACE(sizeof(int) * base::UnixDomainSocket::kMaxFileDescriptors);
+#else
+ const size_t kControlBufferSize =
+ CMSG_SPACE(sizeof(int) * base::UnixDomainSocket::kMaxFileDescriptors) +
+ // The PNaCl toolchain for Non-SFI binary build does not support ucred.
+ CMSG_SPACE(sizeof(struct ucred));
+#endif // defined(OS_NACL_NONSFI)
+
+ char control_buffer[kControlBufferSize];
+ msg.msg_control = control_buffer;
+ msg.msg_controllen = sizeof(control_buffer);
+
+ const ssize_t r = HANDLE_EINTR(recvmsg(fd, &msg, flags));
+ if (r == -1)
+ return -1;
+
+ int* wire_fds = NULL;
+ size_t wire_fds_len = 0;
+ base::ProcessId pid = -1;
+
+ if (msg.msg_controllen > 0) {
+ struct cmsghdr* cmsg;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ 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(int), 0u);
+ DCHECK_EQ(wire_fds, static_cast<void*>(nullptr));
+ wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ wire_fds_len = payload_len / sizeof(int);
+ }
+#if !defined(OS_NACL_NONSFI)
+ // The PNaCl toolchain for Non-SFI binary build does not support
+ // SCM_CREDENTIALS.
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS) {
+ DCHECK_EQ(payload_len, sizeof(struct ucred));
+ DCHECK_EQ(pid, -1);
+ pid = reinterpret_cast<struct ucred*>(CMSG_DATA(cmsg))->pid;
+ }
+#endif
+ }
+ }
+
+ if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) {
+ for (size_t i = 0; i < wire_fds_len; ++i) {
+ close(wire_fds[i]);
+ }
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ 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) {
+ close(wire_fds[i]);
+ }
+ errno = EMSGSIZE;
+ NOTREACHED();
+ return -1;
+ }
+
+ *return_fd = base::ScopedFD(wire_fds[0]);
+ }
+
+ // At this point, |r| is guaranteed to be >= 0.
+ length_ = static_cast<size_t>(r);
+ return r;
+}
+
+bool BrokerSimpleMessage::AddStringToMessage(const char* string) {
+ // strlen() + 1 to always include the '\0' terminating character.
+ return AddDataToMessage(string, strlen(string) + 1);
+}
+
+bool BrokerSimpleMessage::AddDataToMessage(const char* data, size_t length) {
+ if (read_only_ || broken_)
+ return false;
+
+ write_only_ = true; // Message should only be written to going forward.
+
+ base::CheckedNumeric<size_t> safe_length(length);
+ safe_length += length_;
+ safe_length += sizeof(EntryType);
+ safe_length += sizeof(length);
+
+ if (safe_length.ValueOrDie() > kMaxMessageLength) {
+ broken_ = true;
+ return false;
+ }
+
+ EntryType type = EntryType::DATA;
+
+ // Write the type to the message
+ memcpy(write_next_, &type, sizeof(EntryType));
+ write_next_ += sizeof(EntryType);
+ // Write the length of the buffer to the message
+ memcpy(write_next_, &length, sizeof(length));
+ write_next_ += sizeof(length);
+ // Write the data in the buffer to the message
+ memcpy(write_next_, data, length);
+ write_next_ += length;
+ length_ = write_next_ - message_;
+
+ return true;
+}
+
+bool BrokerSimpleMessage::AddIntToMessage(int data) {
+ if (read_only_ || broken_)
+ return false;
+
+ write_only_ = true; // Message should only be written to going forward.
+
+ base::CheckedNumeric<size_t> safe_length(length_);
+ safe_length += sizeof(int);
+ safe_length += sizeof(EntryType);
+
+ if (!safe_length.IsValid() || safe_length.ValueOrDie() > kMaxMessageLength) {
+ broken_ = true;
+ return false;
+ }
+
+ EntryType type = EntryType::INT;
+
+ memcpy(write_next_, &type, sizeof(EntryType));
+ write_next_ += sizeof(EntryType);
+ memcpy(write_next_, &data, sizeof(int));
+ write_next_ += sizeof(int);
+ length_ = write_next_ - message_;
+
+ return true;
+}
+
+bool BrokerSimpleMessage::ReadString(const char** data) {
+ size_t str_len;
+ bool result = ReadData(data, &str_len);
+ return result && (*data)[str_len - 1] == '\0';
+}
+
+bool BrokerSimpleMessage::ReadData(const char** data, size_t* length) {
+ if (write_only_ || broken_)
+ return false;
+
+ read_only_ = true; // Message should not be written to.
+ if (read_next_ > (message_ + length_)) {
+ broken_ = true;
+ return false;
+ }
+
+ if (!ValidateType(EntryType::DATA)) {
+ broken_ = true;
+ return false;
+ }
+
+ // Get the length of the data buffer from the message.
+ if ((read_next_ + sizeof(size_t)) > (message_ + length_)) {
+ broken_ = true;
+ return false;
+ }
+ memcpy(length, read_next_, sizeof(size_t));
+ read_next_ = read_next_ + sizeof(size_t);
+
+ // Get the raw data buffer from the message.
+ if ((read_next_ + *length) > (message_ + length_)) {
+ broken_ = true;
+ return false;
+ }
+ *data = reinterpret_cast<char*>(read_next_);
+ read_next_ = read_next_ + *length;
+ return true;
+}
+
+bool BrokerSimpleMessage::ReadInt(int* result) {
+ if (write_only_ || broken_)
+ return false;
+
+ read_only_ = true; // Message should not be written to.
+ if (read_next_ > (message_ + length_)) {
+ broken_ = true;
+ return false;
+ }
+
+ if (!ValidateType(EntryType::INT)) {
+ broken_ = true;
+ return false;
+ }
+
+ if ((read_next_ + sizeof(int)) > (message_ + length_)) {
+ broken_ = true;
+ return false;
+ }
+ memcpy(result, read_next_, sizeof(int));
+ read_next_ = read_next_ + sizeof(int);
+ return true;
+}
+
+bool BrokerSimpleMessage::ValidateType(EntryType expected_type) {
+ if ((read_next_ + sizeof(EntryType)) > (message_ + length_))
+ return false;
+
+ EntryType type;
+ memcpy(&type, read_next_, sizeof(EntryType));
+ if (type != expected_type)
+ return false;
+
+ read_next_ = read_next_ + sizeof(EntryType);
+ return true;
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/syscall_broker/broker_simple_message.h b/chromium/sandbox/linux/syscall_broker/broker_simple_message.h
new file mode 100644
index 00000000000..d9185b43a77
--- /dev/null
+++ b/chromium/sandbox/linux/syscall_broker/broker_simple_message.h
@@ -0,0 +1,106 @@
+// Copyright 2018 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_BROKER_SIMPLE_MESSAGE_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_SIMPLE_MESSAGE_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "base/files/scoped_file.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace syscall_broker {
+
+// This class is meant to provide a very simple messaging mechanism that is
+// signal-safe for the broker to utilize. This addresses many of the issues
+// outlined in https://crbug.com/255063. In short, the use of the standard
+// base::UnixDomainSockets is not possible because it uses base::Pickle and
+// std::vector, which are not signal-safe.
+//
+// In implementation, much of the code for sending and receiving is taken from
+// base::UnixDomainSockets and re-used below. Thus, ultimately, it might be
+// worthwhile making a first-class base-supported signal-safe set of mechanisms
+// that reduces the code duplication.
+class SANDBOX_EXPORT BrokerSimpleMessage {
+ public:
+ BrokerSimpleMessage();
+
+ // Signal-safe
+ ssize_t SendRecvMsgWithFlags(int fd,
+ int recvmsg_flags,
+ int* send_fd,
+ 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);
+
+ // 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);
+
+ // 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.
+ bool AddStringToMessage(const char* string);
+
+ // Adds a raw data buffer to the message. If the raw data is actually a
+ // string, be sure to have length explicitly include the '\0' terminating
+ // character. Returns true if the internal message buffer has room for the
+ // 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.
+ bool AddIntToMessage(int int_to_add);
+
+ // This returns a pointer to the next available data buffer in |data|. The
+ // pointer is owned by |this| class. The resulting buffer is a string and
+ // terminated with a '\0' character.
+ 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.
+ bool ReadData(const char** data, size_t* length);
+
+ // This reads the next available int from the message and stores it in
+ // |result|.
+ bool ReadInt(int* result);
+
+ // The maximum length of a message in the fixed size buffer.
+ static constexpr size_t kMaxMessageLength = 4096;
+
+ private:
+ friend class BrokerSimpleMessageTestHelper;
+
+ enum class EntryType : uint32_t { DATA = 0xBDBDBD80, INT = 0xBDBDBD81 };
+
+ // 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_;
+ // Set to true once a message is written to, it may never be read from.
+ bool write_only_;
+ // Set when an operation fails, so that all subsequed operations fail,
+ // including any attempt to send the broken message.
+ bool broken_;
+ // 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_;
+ // The statically allocated buffer of size |kMaxMessageLength|.
+ uint8_t message_[kMaxMessageLength];
+};
+
+} // namespace syscall_broker
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_SIMPLE_MESSAGE_H_
diff --git a/chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc b/chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc
new file mode 100644
index 00000000000..9d600ecf0c2
--- /dev/null
+++ b/chromium/sandbox/linux/syscall_broker/broker_simple_message_unittest.cc
@@ -0,0 +1,703 @@
+// Copyright 2018 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/broker_simple_message.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.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/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+namespace {
+
+void PostWaitableEventToThread(base::Thread* thread,
+ base::WaitableEvent* wait_event) {
+ thread->task_runner()->PostTask(FROM_HERE,
+ base::BindOnce(&base::WaitableEvent::Signal,
+ base::Unretained(wait_event)));
+}
+
+} // namespace
+
+class ExpectedResultValue {
+ public:
+ virtual bool NextMessagePieceMatches(BrokerSimpleMessage* message) = 0;
+ virtual size_t Size() = 0;
+};
+
+class ExpectedResultDataValue : public ExpectedResultValue {
+ public:
+ ExpectedResultDataValue(const char* data, size_t length);
+
+ bool NextMessagePieceMatches(BrokerSimpleMessage* message) override;
+ size_t Size() override;
+
+ private:
+ const char* data_;
+ size_t length_;
+ ExpectedResultDataValue();
+};
+
+class ExpectedResultIntValue : public ExpectedResultValue {
+ public:
+ ExpectedResultIntValue(int value);
+
+ bool NextMessagePieceMatches(BrokerSimpleMessage* message) override;
+ size_t Size() override;
+
+ private:
+ int value_;
+
+ ExpectedResultIntValue();
+};
+
+class BrokerSimpleMessageTestHelper {
+ public:
+ static bool MessageContentMatches(const BrokerSimpleMessage& message,
+ const uint8_t* content,
+ size_t length);
+
+ static void SendMsg(int write_fd, BrokerSimpleMessage* message, int fd);
+
+ static void RecvMsg(BrokerChannel::EndPoint* ipc_reader,
+ ExpectedResultValue** expected_values,
+ int expected_values_length);
+
+ static void RecvMsgAndReply(BrokerChannel::EndPoint* ipc_reader,
+ ExpectedResultValue** expected_values,
+ int expected_values_length,
+ const char* response_msg,
+ int fd);
+
+ static void RecvMsgBadRead(BrokerChannel::EndPoint* ipc_reader, int fd);
+
+ // Helper functions to write the respective BrokerSimpleMessage::EntryType to
+ // a buffer. Returns the pointer to the memory right after where the value
+ // was written to in |dst|.
+ static uint8_t* WriteDataType(uint8_t* dst);
+ static uint8_t* WriteIntType(uint8_t* dst);
+
+ static size_t entry_type_size() {
+ return sizeof(BrokerSimpleMessage::EntryType);
+ }
+};
+
+ExpectedResultDataValue::ExpectedResultDataValue() {}
+
+ExpectedResultDataValue::ExpectedResultDataValue(const char* data,
+ size_t length)
+ : data_(data), length_(length) {}
+
+bool ExpectedResultDataValue::NextMessagePieceMatches(
+ BrokerSimpleMessage* message) {
+ const char* next_data;
+ size_t next_length;
+
+ if (!message->ReadData(&next_data, &next_length))
+ return false;
+
+ if (next_length != length_)
+ return false;
+
+ return strncmp(data_, next_data, length_) == 0;
+}
+
+size_t ExpectedResultDataValue::Size() {
+ return sizeof(size_t) + length_ * sizeof(char) +
+ BrokerSimpleMessageTestHelper::entry_type_size();
+}
+
+ExpectedResultIntValue::ExpectedResultIntValue() {}
+
+ExpectedResultIntValue::ExpectedResultIntValue(int value) : value_(value) {}
+
+bool ExpectedResultIntValue::NextMessagePieceMatches(
+ BrokerSimpleMessage* message) {
+ int next_value;
+
+ if (!message->ReadInt(&next_value))
+ return false;
+
+ return next_value == value_;
+}
+
+size_t ExpectedResultIntValue::Size() {
+ return sizeof(int) + BrokerSimpleMessageTestHelper::entry_type_size();
+}
+
+// static
+bool BrokerSimpleMessageTestHelper::MessageContentMatches(
+ const BrokerSimpleMessage& message,
+ const uint8_t* content,
+ size_t length) {
+ return length == message.length_ &&
+ memcmp(message.message_, content, length) == 0;
+}
+
+// static
+void BrokerSimpleMessageTestHelper::SendMsg(int write_fd,
+ BrokerSimpleMessage* message,
+ int fd) {
+ EXPECT_NE(-1, message->SendMsg(write_fd, fd));
+}
+
+// static
+void BrokerSimpleMessageTestHelper::RecvMsg(
+ BrokerChannel::EndPoint* ipc_reader,
+ ExpectedResultValue** expected_values,
+ int expected_values_length) {
+ base::ScopedFD return_fd;
+ BrokerSimpleMessage message;
+ ssize_t len = message.RecvMsgWithFlags(ipc_reader->get(), 0, &return_fd);
+
+ EXPECT_LE(0, len) << "RecvMsgWithFlags response invalid";
+
+ size_t expected_message_size = 0;
+ for (int i = 0; i < expected_values_length; i++) {
+ ExpectedResultValue* expected_result = expected_values[i];
+ EXPECT_TRUE(expected_result->NextMessagePieceMatches(&message));
+ expected_message_size += expected_result->Size();
+ }
+
+ EXPECT_EQ(static_cast<ssize_t>(expected_message_size), len);
+}
+
+// static
+void BrokerSimpleMessageTestHelper::RecvMsgBadRead(
+ BrokerChannel::EndPoint* ipc_reader,
+ int fd) {
+ base::ScopedFD return_fd;
+ BrokerSimpleMessage message;
+ ssize_t len = message.RecvMsgWithFlags(ipc_reader->get(), 0, &return_fd);
+
+ EXPECT_LE(0, len) << "RecvMSgWithFlags response invalid";
+
+ // Try to read a string instead of the int.
+ const char* bad_str;
+ EXPECT_FALSE(message.ReadString(&bad_str));
+
+ // Now try to read the int, the message should be marked as broken at this
+ // point.
+ int expected_int;
+ EXPECT_FALSE(message.ReadInt(&expected_int));
+
+ // Now try to read the actual string.
+ const char* expected_str;
+ EXPECT_FALSE(message.ReadString(&expected_str));
+
+ BrokerSimpleMessage response_message;
+ SendMsg(return_fd.get(), &response_message, -1);
+}
+
+// static
+void BrokerSimpleMessageTestHelper::RecvMsgAndReply(
+ BrokerChannel::EndPoint* ipc_reader,
+ ExpectedResultValue** expected_values,
+ int expected_values_length,
+ const char* response_msg,
+ int fd) {
+ base::ScopedFD return_fd;
+ BrokerSimpleMessage message;
+ ssize_t len = message.RecvMsgWithFlags(ipc_reader->get(), 0, &return_fd);
+
+ EXPECT_LT(0, len);
+
+ size_t expected_message_size = 0;
+ for (int i = 0; i < expected_values_length; i++) {
+ ExpectedResultValue* expected_result = expected_values[i];
+ EXPECT_TRUE(expected_result->NextMessagePieceMatches(&message));
+ expected_message_size += expected_result->Size();
+ }
+
+ EXPECT_EQ(expected_message_size, static_cast<size_t>(len));
+
+ BrokerSimpleMessage response_message;
+ response_message.AddDataToMessage(response_msg, strlen(response_msg) + 1);
+ SendMsg(return_fd.get(), &response_message, -1);
+}
+
+// static
+uint8_t* BrokerSimpleMessageTestHelper::WriteDataType(uint8_t* dst) {
+ BrokerSimpleMessage::EntryType type = BrokerSimpleMessage::EntryType::DATA;
+ memcpy(dst, &type, sizeof(BrokerSimpleMessage::EntryType));
+ return dst + sizeof(BrokerSimpleMessage::EntryType);
+}
+
+// static
+uint8_t* BrokerSimpleMessageTestHelper::WriteIntType(uint8_t* dst) {
+ BrokerSimpleMessage::EntryType type = BrokerSimpleMessage::EntryType::INT;
+ memcpy(dst, &type, sizeof(BrokerSimpleMessage::EntryType));
+ return dst + sizeof(BrokerSimpleMessage::EntryType);
+}
+
+TEST(BrokerSimpleMessage, AddData) {
+ const char data1[] = "hello, world";
+ const char data2[] = "foobar";
+ const int int1 = 42;
+ const int int2 = 24;
+ uint8_t message_content[BrokerSimpleMessage::kMaxMessageLength];
+ uint8_t* next;
+ size_t len;
+
+ // Simple string
+ {
+ BrokerSimpleMessage message;
+ message.AddDataToMessage(data1, strlen(data1));
+
+ next = BrokerSimpleMessageTestHelper::WriteDataType(message_content);
+ len = strlen(data1);
+ memcpy(next, &len, sizeof(len));
+ next = next + sizeof(len);
+ memcpy(next, data1, strlen(data1));
+ next = next + strlen(data1);
+
+ EXPECT_TRUE(BrokerSimpleMessageTestHelper::MessageContentMatches(
+ message, message_content, next - message_content));
+ }
+
+ // Simple int
+ {
+ BrokerSimpleMessage message;
+ message.AddIntToMessage(int1);
+
+ next = BrokerSimpleMessageTestHelper::WriteIntType(message_content);
+ memcpy(next, &int1, sizeof(int));
+ next = next + sizeof(int);
+
+ EXPECT_TRUE(BrokerSimpleMessageTestHelper::MessageContentMatches(
+ message, message_content, next - message_content));
+ }
+
+ // string then int
+ {
+ BrokerSimpleMessage message;
+ message.AddDataToMessage(data1, strlen(data1));
+ message.AddIntToMessage(int1);
+
+ // string
+ next = BrokerSimpleMessageTestHelper::WriteDataType(message_content);
+ len = strlen(data1);
+ memcpy(next, &len, sizeof(len));
+ next = next + sizeof(len);
+ memcpy(next, data1, strlen(data1));
+ next = next + strlen(data1);
+
+ // int
+ next = BrokerSimpleMessageTestHelper::WriteIntType(next);
+ memcpy(next, &int1, sizeof(int));
+ next = next + sizeof(int);
+
+ EXPECT_TRUE(BrokerSimpleMessageTestHelper::MessageContentMatches(
+ message, message_content, next - message_content));
+ }
+
+ // int then string
+ {
+ BrokerSimpleMessage message;
+ message.AddIntToMessage(int1);
+ message.AddDataToMessage(data1, strlen(data1));
+
+ // int
+ next = BrokerSimpleMessageTestHelper::WriteIntType(message_content);
+ memcpy(next, &int1, sizeof(int));
+ next = next + sizeof(int);
+
+ // string
+ next = BrokerSimpleMessageTestHelper::WriteDataType(next);
+ len = strlen(data1);
+ memcpy(next, &len, sizeof(len));
+ next = next + sizeof(len);
+ memcpy(next, data1, strlen(data1));
+ next = next + strlen(data1);
+
+ EXPECT_TRUE(BrokerSimpleMessageTestHelper::MessageContentMatches(
+ message, message_content, next - message_content));
+ }
+
+ // string int string int
+ {
+ BrokerSimpleMessage message;
+ message.AddDataToMessage(data1, strlen(data1));
+ message.AddIntToMessage(int1);
+ message.AddDataToMessage(data2, strlen(data2));
+ message.AddIntToMessage(int2);
+
+ // string
+ next = BrokerSimpleMessageTestHelper::WriteDataType(message_content);
+ len = strlen(data1);
+ memcpy(next, &len, sizeof(len));
+ next = next + sizeof(len);
+ memcpy(next, data1, strlen(data1));
+ next = next + strlen(data1);
+
+ // int
+ next = BrokerSimpleMessageTestHelper::WriteIntType(next);
+ memcpy(next, &int1, sizeof(int));
+ next = next + sizeof(int);
+
+ // string
+ next = BrokerSimpleMessageTestHelper::WriteDataType(next);
+ len = strlen(data2);
+ memcpy(next, &len, sizeof(len));
+ next = next + sizeof(len);
+ memcpy(next, data2, strlen(data2));
+ next = next + strlen(data2);
+
+ // int
+ next = BrokerSimpleMessageTestHelper::WriteIntType(next);
+ memcpy(next, &int2, sizeof(int));
+ next = next + sizeof(int);
+
+ EXPECT_TRUE(BrokerSimpleMessageTestHelper::MessageContentMatches(
+ message, message_content, next - message_content));
+ }
+
+ // Add too much data
+ {
+ BrokerSimpleMessage message;
+
+ char foo[8192];
+ memset(foo, 'x', sizeof(foo));
+
+ EXPECT_FALSE(message.AddDataToMessage(foo, sizeof(foo)));
+ }
+}
+
+TEST(BrokerSimpleMessage, SendAndRecvMsg) {
+ const char data1[] = "hello, world";
+ const char data2[] = "foobar";
+ const int int1 = 42;
+ const int int2 = 24;
+
+ base::Thread message_thread("SendMessageThread");
+ ASSERT_TRUE(message_thread.Start());
+ base::WaitableEvent wait_event(
+ base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+ // Empty message case
+ {
+ SCOPED_TRACE("Empty message case");
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+
+ BrokerSimpleMessage send_message;
+ message_thread.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&BrokerSimpleMessageTestHelper::SendMsg,
+ ipc_writer.get(), &send_message, -1));
+
+ PostWaitableEventToThread(&message_thread, &wait_event);
+
+ BrokerSimpleMessageTestHelper::RecvMsg(&ipc_reader, nullptr, 0);
+
+ wait_event.Wait();
+ }
+
+ // Simple string case
+ {
+ SCOPED_TRACE("Simple string case");
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+
+ BrokerSimpleMessage send_message;
+ send_message.AddDataToMessage(data1, strlen(data1) + 1);
+ message_thread.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&BrokerSimpleMessageTestHelper::SendMsg,
+ ipc_writer.get(), &send_message, -1));
+
+ PostWaitableEventToThread(&message_thread, &wait_event);
+
+ ExpectedResultDataValue data1_value(data1, strlen(data1) + 1);
+ ExpectedResultValue* expected_results[] = {&data1_value};
+
+ BrokerSimpleMessageTestHelper::RecvMsg(&ipc_reader, expected_results,
+ base::size(expected_results));
+
+ wait_event.Wait();
+ }
+
+ // Simple int case
+ {
+ SCOPED_TRACE("Simple int case");
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+
+ BrokerSimpleMessage send_message;
+ send_message.AddIntToMessage(int1);
+ message_thread.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&BrokerSimpleMessageTestHelper::SendMsg,
+ ipc_writer.get(), &send_message, -1));
+
+ PostWaitableEventToThread(&message_thread, &wait_event);
+
+ ExpectedResultIntValue int1_value(int1);
+ ExpectedResultValue* expected_results[] = {&int1_value};
+
+ BrokerSimpleMessageTestHelper::RecvMsg(&ipc_reader, expected_results,
+ base::size(expected_results));
+
+ wait_event.Wait();
+ }
+
+ // Mixed message 1
+ {
+ SCOPED_TRACE("Mixed message 1");
+ base::Thread message_thread("SendMessageThread");
+ ASSERT_TRUE(message_thread.Start());
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+
+ BrokerSimpleMessage send_message;
+ send_message.AddDataToMessage(data1, strlen(data1) + 1);
+ send_message.AddIntToMessage(int1);
+ message_thread.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&BrokerSimpleMessageTestHelper::SendMsg,
+ ipc_writer.get(), &send_message, -1));
+
+ PostWaitableEventToThread(&message_thread, &wait_event);
+
+ ExpectedResultDataValue data1_value(data1, strlen(data1) + 1);
+ ExpectedResultIntValue int1_value(int1);
+ ExpectedResultValue* expected_results[] = {&data1_value, &int1_value};
+
+ BrokerSimpleMessageTestHelper::RecvMsg(&ipc_reader, expected_results,
+ base::size(expected_results));
+
+ wait_event.Wait();
+ }
+
+ // Mixed message 2
+ {
+ SCOPED_TRACE("Mixed message 2");
+ base::Thread message_thread("SendMessageThread");
+ ASSERT_TRUE(message_thread.Start());
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+
+ BrokerSimpleMessage send_message;
+ send_message.AddIntToMessage(int1);
+ send_message.AddDataToMessage(data1, strlen(data1) + 1);
+ send_message.AddDataToMessage(data2, strlen(data2) + 1);
+ send_message.AddIntToMessage(int2);
+ message_thread.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&BrokerSimpleMessageTestHelper::SendMsg,
+ ipc_writer.get(), &send_message, -1));
+
+ PostWaitableEventToThread(&message_thread, &wait_event);
+
+ ExpectedResultDataValue data1_value(data1, strlen(data1) + 1);
+ ExpectedResultDataValue data2_value(data2, strlen(data2) + 1);
+ ExpectedResultIntValue int1_value(int1);
+ ExpectedResultIntValue int2_value(int2);
+ ExpectedResultValue* expected_results[] = {&int1_value, &data1_value,
+ &data2_value, &int2_value};
+
+ BrokerSimpleMessageTestHelper::RecvMsg(&ipc_reader, expected_results,
+ base::size(expected_results));
+
+ wait_event.Wait();
+ }
+}
+
+TEST(BrokerSimpleMessage, SendRecvMsgSynchronous) {
+ const char data1[] = "hello, world";
+ const char data2[] = "foobar";
+ const int int1 = 42;
+ const int int2 = 24;
+ const char reply_data1[] = "baz";
+
+ base::Thread message_thread("SendMessageThread");
+ ASSERT_TRUE(message_thread.Start());
+ base::WaitableEvent wait_event(
+ base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+ // Simple string case
+ {
+ SCOPED_TRACE("Simple string case");
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+
+ ExpectedResultDataValue data1_value(data1, strlen(data1) + 1);
+ ExpectedResultValue* expected_results[] = {&data1_value};
+ message_thread.task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&BrokerSimpleMessageTestHelper::RecvMsgAndReply,
+ &ipc_reader, expected_results,
+ base::size(expected_results), reply_data1, -1));
+
+ PostWaitableEventToThread(&message_thread, &wait_event);
+
+ BrokerSimpleMessage send_message;
+ send_message.AddDataToMessage(data1, strlen(data1) + 1);
+ BrokerSimpleMessage reply_message;
+ int returned_fd;
+ ssize_t len = send_message.SendRecvMsgWithFlags(
+ ipc_writer.get(), 0, &returned_fd, &reply_message);
+
+ ExpectedResultDataValue response_value(reply_data1,
+ strlen(reply_data1) + 1);
+
+ EXPECT_TRUE(response_value.NextMessagePieceMatches(&reply_message));
+ EXPECT_EQ(len, static_cast<ssize_t>(response_value.Size()));
+
+ wait_event.Wait();
+ }
+
+ // Simple int case
+ {
+ SCOPED_TRACE("Simple int case");
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+
+ ExpectedResultIntValue int1_value(int1);
+ ExpectedResultValue* expected_results[] = {&int1_value};
+ message_thread.task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&BrokerSimpleMessageTestHelper::RecvMsgAndReply,
+ &ipc_reader, expected_results,
+ base::size(expected_results), reply_data1, -1));
+
+ PostWaitableEventToThread(&message_thread, &wait_event);
+
+ BrokerSimpleMessage send_message;
+ send_message.AddIntToMessage(int1);
+ BrokerSimpleMessage reply_message;
+ int returned_fd;
+ ssize_t len = send_message.SendRecvMsgWithFlags(
+ ipc_writer.get(), 0, &returned_fd, &reply_message);
+
+ ExpectedResultDataValue response_value(reply_data1,
+ strlen(reply_data1) + 1);
+
+ EXPECT_TRUE(response_value.NextMessagePieceMatches(&reply_message));
+ EXPECT_EQ(len, static_cast<ssize_t>(response_value.Size()));
+
+ wait_event.Wait();
+ }
+
+ // Mixed message 1
+ {
+ SCOPED_TRACE("Mixed message 1");
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+
+ ExpectedResultDataValue data1_value(data1, strlen(data1) + 1);
+ ExpectedResultIntValue int1_value(int1);
+ ExpectedResultValue* expected_results[] = {&data1_value, &int1_value};
+ message_thread.task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&BrokerSimpleMessageTestHelper::RecvMsgAndReply,
+ &ipc_reader, expected_results,
+ base::size(expected_results), reply_data1, -1));
+
+ PostWaitableEventToThread(&message_thread, &wait_event);
+
+ BrokerSimpleMessage send_message;
+ send_message.AddDataToMessage(data1, strlen(data1) + 1);
+ send_message.AddIntToMessage(int1);
+ BrokerSimpleMessage reply_message;
+ int returned_fd;
+ ssize_t len = send_message.SendRecvMsgWithFlags(
+ ipc_writer.get(), 0, &returned_fd, &reply_message);
+
+ ExpectedResultDataValue response_value(reply_data1,
+ strlen(reply_data1) + 1);
+
+ EXPECT_TRUE(response_value.NextMessagePieceMatches(&reply_message));
+ EXPECT_EQ(len, static_cast<ssize_t>(response_value.Size()));
+
+ wait_event.Wait();
+ }
+
+ // Mixed message 2
+ {
+ SCOPED_TRACE("Mixed message 2");
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+
+ ExpectedResultDataValue data1_value(data1, strlen(data1) + 1);
+ ExpectedResultIntValue int1_value(int1);
+ ExpectedResultIntValue int2_value(int2);
+ ExpectedResultDataValue data2_value(data2, strlen(data2) + 1);
+ ExpectedResultValue* expected_results[] = {&data1_value, &int1_value,
+ &int2_value, &data2_value};
+ message_thread.task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&BrokerSimpleMessageTestHelper::RecvMsgAndReply,
+ &ipc_reader, expected_results,
+ base::size(expected_results), reply_data1, -1));
+
+ PostWaitableEventToThread(&message_thread, &wait_event);
+
+ BrokerSimpleMessage send_message;
+ send_message.AddDataToMessage(data1, strlen(data1) + 1);
+ send_message.AddIntToMessage(int1);
+ send_message.AddIntToMessage(int2);
+ send_message.AddDataToMessage(data2, strlen(data2) + 1);
+ BrokerSimpleMessage reply_message;
+ int returned_fd;
+ ssize_t len = send_message.SendRecvMsgWithFlags(
+ ipc_writer.get(), 0, &returned_fd, &reply_message);
+
+ ExpectedResultDataValue response_value(reply_data1,
+ strlen(reply_data1) + 1);
+
+ EXPECT_TRUE(response_value.NextMessagePieceMatches(&reply_message));
+ EXPECT_EQ(len, static_cast<ssize_t>(response_value.Size()));
+
+ wait_event.Wait();
+ }
+
+ // Bad read case
+ {
+ SCOPED_TRACE("Bad read case");
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+
+ message_thread.task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&BrokerSimpleMessageTestHelper::RecvMsgBadRead,
+ &ipc_reader, -1));
+
+ PostWaitableEventToThread(&message_thread, &wait_event);
+
+ BrokerSimpleMessage send_message;
+ EXPECT_TRUE(send_message.AddIntToMessage(5));
+ EXPECT_TRUE(send_message.AddStringToMessage("test"));
+ BrokerSimpleMessage reply_message;
+ int returned_fd;
+ ssize_t len = send_message.SendRecvMsgWithFlags(
+ ipc_writer.get(), 0, &returned_fd, &reply_message);
+
+ EXPECT_GE(len, 0);
+
+ wait_event.Wait();
+ }
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/address_sanitizer_test.cc b/chromium/sandbox/win/src/address_sanitizer_test.cc
index ae1590a0ad3..380307843cd 100644
--- a/chromium/sandbox/win/src/address_sanitizer_test.cc
+++ b/chromium/sandbox/win/src/address_sanitizer_test.cc
@@ -78,7 +78,7 @@ TEST_F(AddressSanitizerTests, TestAddressSanitizer) {
ASSERT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetStderrHandle(tmp_handle.Get()));
base::FilePath exe;
- ASSERT_TRUE(PathService::Get(base::FILE_EXE, &exe));
+ ASSERT_TRUE(base::PathService::Get(base::FILE_EXE, &exe));
base::FilePath pdb_path = exe.DirName().Append(L"*.pdb");
ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY,
pdb_path.value().c_str()));
diff --git a/chromium/sandbox/win/src/crosscall_client.h b/chromium/sandbox/win/src/crosscall_client.h
index c05e4978c0f..7bcfb3cfd94 100644
--- a/chromium/sandbox/win/src/crosscall_client.h
+++ b/chromium/sandbox/win/src/crosscall_client.h
@@ -8,6 +8,7 @@
#include <stddef.h>
#include <stdint.h>
+#include "base/compiler_specific.h"
#include "sandbox/win/src/crosscall_params.h"
#include "sandbox/win/src/sandbox.h"
@@ -150,7 +151,7 @@ class CopyHelper<const wchar_t*> {
// We provide our not very optimized version of wcslen(), since we don't
// want to risk having the linker use the version in the CRT since the CRT
// might not be present when we do an early IPC call.
- static size_t __cdecl StringLength(const wchar_t* wcs) {
+ static size_t CDECL StringLength(const wchar_t* wcs) {
const wchar_t* eos = wcs;
while (*eos++)
;
diff --git a/chromium/sandbox/win/src/file_policy_test.cc b/chromium/sandbox/win/src/file_policy_test.cc
index 3eb1bb64513..74ba62f8165 100644
--- a/chromium/sandbox/win/src/file_policy_test.cc
+++ b/chromium/sandbox/win/src/file_policy_test.cc
@@ -259,6 +259,21 @@ SBOX_TESTS_COMMAND int File_QueryAttributes(int argc, wchar_t** argv) {
return SBOX_TEST_FAILED;
}
+// Tries to create a backup of calc.exe in system32 folder. This should fail
+// with ERROR_ACCESS_DENIED if everything is working as expected.
+SBOX_TESTS_COMMAND int File_CopyFile(int argc, wchar_t** argv) {
+ base::string16 calc_path = MakePathToSys(L"calc.exe", false);
+ base::string16 calc_backup_path = MakePathToSys(L"calc.exe.bak", false);
+
+ if (::CopyFile(calc_path.c_str(), calc_backup_path.c_str(), FALSE))
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (::GetLastError() != ERROR_ACCESS_DENIED)
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
TEST(FilePolicyTest, DenyNtCreateCalc) {
TestRunner runner;
EXPECT_TRUE(
@@ -664,4 +679,27 @@ TEST(FilePolicyTest, CheckMissingNTPrefixEscape) {
EXPECT_STREQ(result.c_str(), L"\\/?/?\\C:\\NAME");
}
+TEST(FilePolicyTest, TestCopyFile) {
+ // Check if the test is running Win8 or newer since
+ // MITIGATION_STRICT_HANDLE_CHECKS is not supported on older systems.
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ TestRunner runner;
+ runner.SetTimeout(2000);
+
+ // Allow read access to calc.exe, this should be on all Windows versions.
+ ASSERT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_READONLY, L"calc.exe"));
+
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ // Set proper mitigation.
+ EXPECT_EQ(
+ policy->SetDelayedProcessMitigations(MITIGATION_STRICT_HANDLE_CHECKS),
+ SBOX_ALL_OK);
+
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CopyFile"));
+}
+
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/handle_closer_agent.cc b/chromium/sandbox/win/src/handle_closer_agent.cc
index ddb6791391b..5a91155c063 100644
--- a/chromium/sandbox/win/src/handle_closer_agent.cc
+++ b/chromium/sandbox/win/src/handle_closer_agent.cc
@@ -134,6 +134,11 @@ bool HandleCloserAgent::CloseHandles() {
if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count))
return false;
+ // Skip closing these handles when Application Verifier is in use in order to
+ // avoid invalid-handle exceptions.
+ if (GetModuleHandleW(L"vrfcore.dll"))
+ return true;
+
// Set up buffers for the type info and the name.
std::vector<BYTE> type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) +
32 * sizeof(wchar_t));
diff --git a/chromium/sandbox/win/src/process_mitigations_dyncode_unittest.cc b/chromium/sandbox/win/src/process_mitigations_dyncode_unittest.cc
index 75b7fe03195..95ea4ee6ecb 100644
--- a/chromium/sandbox/win/src/process_mitigations_dyncode_unittest.cc
+++ b/chromium/sandbox/win/src/process_mitigations_dyncode_unittest.cc
@@ -8,6 +8,8 @@
#include <string>
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/win/windows_version.h"
@@ -308,14 +310,21 @@ void DynamicCodeTestHarness(sandbox::MitigationFlags which_mitigation,
// Ensure sandbox access to the file on disk.
base::FilePath dll_path;
- EXPECT_TRUE(base::PathService::Get(base::DIR_EXE, &dll_path));
+ ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &dll_path));
dll_path = dll_path.Append(hooking_dll::g_hook_dll_file);
+ // File must be writable, so create a writable copy in a temporary directory.
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::FilePath temp_dll_path =
+ temp_dir.GetPath().Append(hooking_dll::g_hook_dll_file);
+ ASSERT_TRUE(base::CopyFile(dll_path, temp_dll_path));
+
EXPECT_TRUE(runner.AddFsRule(sandbox::TargetPolicy::FILES_ALLOW_ANY,
- dll_path.value().c_str()));
+ temp_dll_path.value().c_str()));
test = base::StringPrintf(L"%ls %u \"%ls\"", shared.c_str(), MAPVIEWFILE,
- dll_path.value().c_str());
+ temp_dll_path.value().c_str());
EXPECT_EQ((expect_success ? sandbox::SBOX_TEST_SUCCEEDED
: ERROR_DYNAMIC_CODE_BLOCKED),
runner.RunTest(test.c_str()));
diff --git a/chromium/sandbox/win/src/process_mitigations_unittest.cc b/chromium/sandbox/win/src/process_mitigations_unittest.cc
index b0b472a1b4e..e13b22ab1a0 100644
--- a/chromium/sandbox/win/src/process_mitigations_unittest.cc
+++ b/chromium/sandbox/win/src/process_mitigations_unittest.cc
@@ -95,10 +95,10 @@ void TestWin10MsSigned(bool expect_success,
// Choose the appropriate DLL and make sure the sandbox allows access to it.
base::FilePath dll_path;
if (use_ms_signed_binary) {
- EXPECT_TRUE(PathService::Get(base::DIR_SYSTEM, &dll_path));
+ EXPECT_TRUE(base::PathService::Get(base::DIR_SYSTEM, &dll_path));
dll_path = dll_path.Append(L"gdi32.dll");
} else {
- EXPECT_TRUE(PathService::Get(base::DIR_EXE, &dll_path));
+ EXPECT_TRUE(base::PathService::Get(base::DIR_EXE, &dll_path));
dll_path = dll_path.Append(hooking_dll::g_hook_dll_file);
}
EXPECT_TRUE(runner.AddFsRule(sandbox::TargetPolicy::FILES_ALLOW_READONLY,
@@ -605,31 +605,20 @@ TEST(ProcessMitigationsTest, CheckWin8AslrPolicySuccess) {
base::string16 test_command = L"CheckPolicy ";
test_command += std::to_wstring(TESTPOLICY_ASLR);
+//---------------------------------------------
+// Only test in release for now.
+// TODO(pennymac): overhaul ASLR, crbug/834907.
+//---------------------------------------------
+#if defined(NDEBUG)
TestRunner runner;
sandbox::TargetPolicy* policy = runner.GetPolicy();
-//---------------------------------
-// 1) Test setting pre-startup.
-// **Can only set ASLR pre-startup in release build.
-//---------------------------------
-#if defined(NDEBUG)
EXPECT_EQ(policy->SetProcessMitigations(
MITIGATION_RELOCATE_IMAGE | MITIGATION_RELOCATE_IMAGE_REQUIRED |
MITIGATION_BOTTOM_UP_ASLR | MITIGATION_HIGH_ENTROPY_ASLR),
SBOX_ALL_OK);
EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(test_command.c_str()));
-
-//---------------------------------
-// 2) Test setting post-startup.
-// **Bottom up and high entropy can only be set pre-startup.
-// **Can only set ASLR post-startup in debug build.
-//---------------------------------
-#else
- EXPECT_EQ(policy->SetDelayedProcessMitigations(
- MITIGATION_RELOCATE_IMAGE | MITIGATION_RELOCATE_IMAGE_REQUIRED),
- SBOX_ALL_OK);
- EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(test_command.c_str()));
-#endif // !defined(NDEBUG)
+#endif // defined(NDEBUG)
}
//------------------------------------------------------------------------------
diff --git a/chromium/sandbox/win/src/process_mitigations_win32k_unittest.cc b/chromium/sandbox/win/src/process_mitigations_win32k_unittest.cc
index 70247ae1d74..4e603009bca 100644
--- a/chromium/sandbox/win/src/process_mitigations_win32k_unittest.cc
+++ b/chromium/sandbox/win/src/process_mitigations_win32k_unittest.cc
@@ -629,11 +629,21 @@ TEST(ProcessMitigationsWin32kTest, CheckWin8LockDownFailure) {
EXPECT_NE(SBOX_TEST_SUCCEEDED, runner.RunTest(test_policy_command.c_str()));
}
+// Flaky in Debug. https://crbug.com/840335
+#if !defined(NDEBUG)
+#define MAYBE_CheckWin8LockDownSuccess DISABLED_CheckWin8LockDownSuccess
+#define MAYBE_CheckWin8Redirection DISABLED_CheckWin8Redirection
+#else
+#define MAYBE_CheckWin8LockDownSuccess CheckWin8LockDownSuccess
+#define MAYBE_CheckWin8Redirection CheckWin8Redirection
+#endif
+
// This test validates that setting the MITIGATION_WIN32K_DISABLE mitigation
// along with the policy to fake user32 and gdi32 initialization successfully
// launches the target process.
// The test process itself links against user32/gdi32.
-TEST(ProcessMitigationsWin32kTest, CheckWin8LockDownSuccess) {
+
+TEST(ProcessMitigationsWin32kTest, MAYBE_CheckWin8LockDownSuccess) {
if (base::win::GetVersion() < base::win::VERSION_WIN8)
return;
@@ -659,7 +669,8 @@ TEST(ProcessMitigationsWin32kTest, CheckWin8LockDownSuccess) {
// This test validates the even though we're running under win32k lockdown
// we can use the IPC redirection to enumerate the list of monitors.
-TEST(ProcessMitigationsWin32kTest, CheckWin8Redirection) {
+// Flaky. https://crbug.com/840335
+TEST(ProcessMitigationsWin32kTest, MAYBE_CheckWin8Redirection) {
if (base::win::GetVersion() < base::win::VERSION_WIN8)
return;
diff --git a/chromium/sandbox/win/src/sandbox_nt_util.cc b/chromium/sandbox/win/src/sandbox_nt_util.cc
index 136d757eb1e..171887fb0b1 100644
--- a/chromium/sandbox/win/src/sandbox_nt_util.cc
+++ b/chromium/sandbox/win/src/sandbox_nt_util.cc
@@ -190,14 +190,14 @@ bool InitHeap() {
int TouchMemory(void* buffer, size_t size_bytes, RequiredAccess intent) {
const int kPageSize = 4096;
int dummy = 0;
- char* start = reinterpret_cast<char*>(buffer);
- char* end = start + size_bytes - 1;
+ volatile char* start = reinterpret_cast<char*>(buffer);
+ volatile char* end = start + size_bytes - 1;
if (WRITE == intent) {
for (; start < end; start += kPageSize) {
- *start = 0;
+ *start = *start;
}
- *end = 0;
+ *end = *end;
} else {
for (; start < end; start += kPageSize) {
dummy += *start;
@@ -481,6 +481,29 @@ UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32_t* flags) {
#pragma warning(pop)
}
+const char* GetAnsiImageInfoFromModule(HMODULE module) {
+// PEImage's dtor won't be run during SEH unwinding, but that's OK.
+#pragma warning(push)
+#pragma warning(disable : 4509)
+ const char* out_name = nullptr;
+ __try {
+ do {
+ base::win::PEImage pe(module);
+
+ if (!pe.VerifyMagic())
+ break;
+
+ PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
+ if (exports)
+ out_name = static_cast<const char*>(pe.RVAToAddr(exports->Name));
+ } while (false);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ }
+
+ return out_name;
+#pragma warning(pop)
+}
+
UNICODE_STRING* GetBackingFilePath(PVOID address) {
// We'll start with something close to max_path charactes for the name.
SIZE_T buffer_bytes = MAX_PATH * 2;
diff --git a/chromium/sandbox/win/src/sandbox_nt_util.h b/chromium/sandbox/win/src/sandbox_nt_util.h
index 553e7e47285..f90c635bcc9 100644
--- a/chromium/sandbox/win/src/sandbox_nt_util.h
+++ b/chromium/sandbox/win/src/sandbox_nt_util.h
@@ -100,8 +100,6 @@ enum RequiredAccess { READ, WRITE };
// Performs basic user mode buffer validation. In any case, buffers access must
// be protected by SEH. intent specifies if the buffer should be tested for read
// or write.
-// Note that write intent implies destruction of the buffer content (we actually
-// write)
bool ValidParameter(void* buffer, size_t size, RequiredAccess intent);
// Copies data from a user buffer to our buffer. Returns the operation status.
@@ -144,6 +142,12 @@ enum MappedModuleFlags {
// operator delete(name, NT_ALLOC);
UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32_t* flags);
+// Returns the name and characteristics for a given PE module. The return
+// value is the name as defined by the export table.
+//
+// The returned buffer is within the PE module and must not be freed.
+const char* GetAnsiImageInfoFromModule(HMODULE module);
+
// Returns the full path and filename for a given dll.
// May return nullptr if the provided address is not backed by a named section,
// or if the current OS version doesn't support the call. The returned buffer
diff --git a/chromium/sandbox/win/src/sandbox_nt_util_unittest.cc b/chromium/sandbox/win/src/sandbox_nt_util_unittest.cc
index b751536e7cb..4471f6cdc3e 100644
--- a/chromium/sandbox/win/src/sandbox_nt_util_unittest.cc
+++ b/chromium/sandbox/win/src/sandbox_nt_util_unittest.cc
@@ -47,13 +47,14 @@ TEST(SandboxNtUtil, IsSameProcessDifferentProcess) {
EXPECT_TRUE(TerminateProcess(process_info.process_handle(), 0));
}
-#if defined(_WIN64)
struct VirtualMemDeleter {
void operator()(char* p) { ::VirtualFree(p, 0, MEM_RELEASE); }
};
typedef std::unique_ptr<char, VirtualMemDeleter> unique_ptr_vmem;
+#if defined(_WIN64)
+
void AllocateBlock(SIZE_T size,
SIZE_T free_size,
char** base_address,
@@ -193,5 +194,48 @@ TEST(SandboxNtUtil, NearestAllocator) {
#endif // defined(_WIN64)
+// Test whether function ValidParameter works as expected, that is properly
+// checks access to the buffer and doesn't modify it in any way.
+TEST(SandboxNtUtil, ValidParameter) {
+ static constexpr unsigned int buffer_size = 4096;
+ unique_ptr_vmem buffer_guard(static_cast<char*>(
+ ::VirtualAlloc(nullptr, buffer_size, MEM_COMMIT, PAGE_READWRITE)));
+ ASSERT_NE(nullptr, buffer_guard.get());
+
+ unsigned char* ptr = reinterpret_cast<unsigned char*>(buffer_guard.get());
+
+ // Fill the buffer with some data.
+ for (unsigned int i = 0; i < buffer_size; i++)
+ ptr[i] = (i % 256);
+
+ // Setup verify function.
+ auto verify_buffer = [&]() {
+ for (unsigned int i = 0; i < buffer_size; i++) {
+ if (ptr[i] != (i % 256))
+ return false;
+ }
+
+ return true;
+ };
+
+ // Verify that the buffer can be written to and doesn't change.
+ EXPECT_TRUE(ValidParameter(ptr, buffer_size, RequiredAccess::WRITE));
+ EXPECT_TRUE(verify_buffer());
+
+ DWORD old_protection;
+ // Change the protection of buffer to READONLY.
+ EXPECT_TRUE(
+ ::VirtualProtect(ptr, buffer_size, PAGE_READONLY, &old_protection));
+
+ // Writting to buffer should fail now.
+ EXPECT_FALSE(ValidParameter(ptr, buffer_size, RequiredAccess::WRITE));
+
+ // But reading should be ok.
+ EXPECT_TRUE(ValidParameter(ptr, buffer_size, RequiredAccess::READ));
+
+ // One final check that the buffer hasn't been modified.
+ EXPECT_TRUE(verify_buffer());
+}
+
} // namespace
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/security_level.h b/chromium/sandbox/win/src/security_level.h
index b672aad76ba..b238ad00aa0 100644
--- a/chromium/sandbox/win/src/security_level.h
+++ b/chromium/sandbox/win/src/security_level.h
@@ -9,9 +9,21 @@
namespace sandbox {
-// List of all the integrity levels supported in the sandbox. This is used
-// only on Windows Vista and newer. You can't set the integrity level of the
-// process in the sandbox to a level higher than yours.
+// List of all the integrity levels supported in the sandbox.
+// The integrity level of the sandboxed process can't be set to a level higher
+// than the broker process.
+//
+// Note: These levels map to SIDs under the hood.
+// INTEGRITY_LEVEL_SYSTEM: "S-1-16-16384" System Mandatory Level
+// INTEGRITY_LEVEL_HIGH: "S-1-16-12288" High Mandatory Level
+// INTEGRITY_LEVEL_MEDIUM: "S-1-16-8192" Medium Mandatory Level
+// INTEGRITY_LEVEL_MEDIUM_LOW: "S-1-16-6144"
+// INTEGRITY_LEVEL_LOW: "S-1-16-4096" Low Mandatory Level
+// INTEGRITY_LEVEL_BELOW_LOW: "S-1-16-2048"
+// INTEGRITY_LEVEL_UNTRUSTED: "S-1-16-0" Untrusted Mandatory Level
+//
+// Not defined: "S-1-16-20480" Protected Process Mandatory Level
+// Not defined: "S-1-16-28672" Secure Process Mandatory Level
enum IntegrityLevel {
INTEGRITY_LEVEL_SYSTEM,
INTEGRITY_LEVEL_HIGH,
diff --git a/chromium/sandbox/win/src/target_interceptions.cc b/chromium/sandbox/win/src/target_interceptions.cc
index 5c4f671a5ef..5da25c54442 100644
--- a/chromium/sandbox/win/src/target_interceptions.cc
+++ b/chromium/sandbox/win/src/target_interceptions.cc
@@ -13,6 +13,14 @@ namespace sandbox {
SANDBOX_INTERCEPT NtExports g_nt;
+const char VERIFIER_DLL_NAME[] = "verifier.dll";
+const char KERNEL32_DLL_NAME[] = "kernel32.dll";
+
+enum SectionLoadState {
+ kBeforeKernel32,
+ kAfterKernel32,
+};
+
// Hooks NtMapViewOfSection to detect the load of DLLs. If hot patching is
// required for this dll, this functions patches it.
NTSTATUS WINAPI
@@ -30,21 +38,44 @@ TargetNtMapViewOfSection(NtMapViewOfSectionFunction orig_MapViewOfSection,
NTSTATUS ret = orig_MapViewOfSection(section, process, base, zero_bits,
commit_size, offset, view_size, inherit,
allocation_type, protect);
-
- static int s_load_count = 0;
- if (1 == s_load_count) {
- SandboxFactory::GetTargetServices()->GetState()->SetKernel32Loaded();
- s_load_count = 2;
- }
+ static SectionLoadState s_state = kBeforeKernel32;
do {
if (!NT_SUCCESS(ret))
break;
- if (!InitHeap())
+ if (!IsSameProcess(process))
break;
- if (!IsSameProcess(process))
+ // Only check for verifier.dll or kernel32.dll loading if we haven't moved
+ // past that state yet.
+ if (s_state == kBeforeKernel32) {
+ const char* ansi_module_name =
+ GetAnsiImageInfoFromModule(reinterpret_cast<HMODULE>(*base));
+
+ // _strnicmp below may hit read access violations for some sections. We
+ // find what looks like a valid export directory for a PE module but the
+ // pointer to the module name will be pointing to invalid memory.
+ __try {
+ // Don't initialize the heap if verifier.dll is being loaded. This
+ // indicates Application Verifier is enabled and we should wait until
+ // the next module is loaded.
+ if (ansi_module_name &&
+ (g_nt._strnicmp(ansi_module_name, VERIFIER_DLL_NAME,
+ sizeof(VERIFIER_DLL_NAME)) == 0))
+ break;
+
+ if (ansi_module_name &&
+ (g_nt._strnicmp(ansi_module_name, KERNEL32_DLL_NAME,
+ sizeof(KERNEL32_DLL_NAME)) == 0)) {
+ SandboxFactory::GetTargetServices()->GetState()->SetKernel32Loaded();
+ s_state = kAfterKernel32;
+ }
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ }
+ }
+
+ if (!InitHeap())
break;
if (!IsValidImageSection(section, base, offset, view_size))
@@ -79,9 +110,6 @@ TargetNtMapViewOfSection(NtMapViewOfSectionFunction orig_MapViewOfSection,
} while (false);
- if (!s_load_count)
- s_load_count = 1;
-
return ret;
}
diff --git a/chromium/sandbox/win/src/target_services.cc b/chromium/sandbox/win/src/target_services.cc
index cb095ce3953..c94678312b7 100644
--- a/chromium/sandbox/win/src/target_services.cc
+++ b/chromium/sandbox/win/src/target_services.cc
@@ -62,6 +62,18 @@ bool CsrssDisconnectCleanup() {
return true;
}
+// Used by EnumSystemLocales for warming up.
+static BOOL CALLBACK EnumLocalesProcEx(LPWSTR lpLocaleString,
+ DWORD dwFlags,
+ LPARAM lParam) {
+ return TRUE;
+}
+
+// Additional warmup done just when CSRSS is being disconnected.
+bool CsrssDisconnectWarmup() {
+ return ::EnumSystemLocalesEx(EnumLocalesProcEx, LOCALE_WINDOWS, 0, 0);
+}
+
// Checks if we have handle entries pending and runs the closer.
// Updates is_csrss_connected based on which handle types are closed.
bool CloseOpenHandles(bool* is_csrss_connected) {
@@ -69,7 +81,7 @@ bool CloseOpenHandles(bool* is_csrss_connected) {
HandleCloserAgent handle_closer;
handle_closer.InitializeHandlesToClose(is_csrss_connected);
if (!*is_csrss_connected) {
- if (!CsrssDisconnectCleanup()) {
+ if (!CsrssDisconnectWarmup() || !CsrssDisconnectCleanup()) {
return false;
}
}
@@ -79,11 +91,6 @@ bool CloseOpenHandles(bool* is_csrss_connected) {
return true;
}
-// GetUserDefaultLocaleName is not available on WIN XP. So we'll
-// load it on-the-fly.
-const wchar_t kKernel32DllName[] = L"kernel32.dll";
-typedef decltype(GetUserDefaultLocaleName)* GetUserDefaultLocaleNameFunction;
-
// Warm up language subsystems before the sandbox is turned on.
// Tested on Win8.1 x64:
// This needs to happen after RevertToSelf() is called, because (at least) in
@@ -96,23 +103,8 @@ bool WarmupWindowsLocales() {
// warmup all of these functions, but let's not assume that.
::GetUserDefaultLangID();
::GetUserDefaultLCID();
- static GetUserDefaultLocaleNameFunction GetUserDefaultLocaleName_func =
- nullptr;
- if (!GetUserDefaultLocaleName_func) {
- HMODULE kernel32_dll = ::GetModuleHandle(kKernel32DllName);
- if (!kernel32_dll) {
- return false;
- }
- GetUserDefaultLocaleName_func =
- reinterpret_cast<GetUserDefaultLocaleNameFunction>(
- GetProcAddress(kernel32_dll, "GetUserDefaultLocaleName"));
- if (!GetUserDefaultLocaleName_func) {
- return false;
- }
- }
wchar_t localeName[LOCALE_NAME_MAX_LENGTH] = {0};
- return (0 != GetUserDefaultLocaleName_func(
- localeName, LOCALE_NAME_MAX_LENGTH * sizeof(wchar_t)));
+ return (0 != ::GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH));
}
// Used as storage for g_target_services, because other allocation facilities