Commit a722110a authored by Greg Kerr's avatar Greg Kerr Committed by Commit Bot

Fix non-signal-safe calls in BrokerClient

The current BrokerClient is not signal-safe because of its use of base::Pickle
which uses new and delete, and also its use of std::vector, which is not
technically signal safe either. These are used in messaging IPCs with the
BrokerHost when a syscall is trapped, thus BrokerClient must be made signal
safe. This is discussed in https://crbug.com/255063.

This CL creates a new BrokerSimpleMessage class to handle simple IPC messaging
with strings and ints only. It uses fixed sized messages to simplify logic and
allow it to allocate all its memory on the stack. It creates send, receive, and
a synchronous send-and-receive method. It also adds a basic set of unit tests
to sanity check behavior.

Bug: 255063
Change-Id: I8077a515921b62969a1b8b173d903f2a118ed186
Reviewed-on: https://chromium-review.googlesource.com/553400
Commit-Queue: Greg Kerr <kerrnel@chromium.org>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#558415}
parent 2c4dbd45
...@@ -99,6 +99,7 @@ source_set("sandbox_linux_unittests_sources") { ...@@ -99,6 +99,7 @@ source_set("sandbox_linux_unittests_sources") {
"services/yama_unittests.cc", "services/yama_unittests.cc",
"syscall_broker/broker_file_permission_unittest.cc", "syscall_broker/broker_file_permission_unittest.cc",
"syscall_broker/broker_process_unittest.cc", "syscall_broker/broker_process_unittest.cc",
"syscall_broker/broker_simple_message_unittest.cc",
"tests/main.cc", "tests/main.cc",
"tests/scoped_temporary_file.cc", "tests/scoped_temporary_file.cc",
"tests/scoped_temporary_file.h", "tests/scoped_temporary_file.h",
...@@ -371,6 +372,8 @@ component("sandbox_services") { ...@@ -371,6 +372,8 @@ component("sandbox_services") {
"syscall_broker/broker_permission_list.h", "syscall_broker/broker_permission_list.h",
"syscall_broker/broker_process.cc", "syscall_broker/broker_process.cc",
"syscall_broker/broker_process.h", "syscall_broker/broker_process.h",
"syscall_broker/broker_simple_message.cc",
"syscall_broker/broker_simple_message.h",
] ]
defines = [ "SANDBOX_IMPLEMENTATION" ] defines = [ "SANDBOX_IMPLEMENTATION" ]
...@@ -420,6 +423,8 @@ component("sandbox_services") { ...@@ -420,6 +423,8 @@ component("sandbox_services") {
"syscall_broker/broker_permission_list.h", "syscall_broker/broker_permission_list.h",
"syscall_broker/broker_process.cc", "syscall_broker/broker_process.cc",
"syscall_broker/broker_process.h", "syscall_broker/broker_process.h",
"syscall_broker/broker_simple_message.cc",
"syscall_broker/broker_simple_message.h",
] ]
} else if (!is_android) { } else if (!is_android) {
sources += [ sources += [
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/files/scoped_file.h" #include "base/files/scoped_file.h"
#include "base/macros.h" #include "base/macros.h"
#include "sandbox/sandbox_export.h"
namespace sandbox { namespace sandbox {
...@@ -15,7 +16,7 @@ namespace syscall_broker { ...@@ -15,7 +16,7 @@ namespace syscall_broker {
// A small class to create a pipe-like communication channel. It is based on a // 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 // SOCK_SEQPACKET unix socket, which is connection-based and guaranteed to
// preserve message boundaries. // preserve message boundaries.
class BrokerChannel { class SANDBOX_EXPORT BrokerChannel {
public: public:
typedef base::ScopedFD EndPoint; typedef base::ScopedFD EndPoint;
static void CreatePair(EndPoint* reader, EndPoint* writer); static void CreatePair(EndPoint* reader, EndPoint* writer);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "sandbox/linux/syscall_broker/broker_channel.h" #include "sandbox/linux/syscall_broker/broker_channel.h"
#include "sandbox/linux/syscall_broker/broker_command.h" #include "sandbox/linux/syscall_broker/broker_command.h"
#include "sandbox/linux/syscall_broker/broker_permission_list.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) #if defined(OS_ANDROID) && !defined(MSG_CMSG_CLOEXEC)
#define MSG_CMSG_CLOEXEC 0x40000000 #define MSG_CMSG_CLOEXEC 0x40000000
...@@ -29,13 +30,11 @@ namespace syscall_broker { ...@@ -29,13 +30,11 @@ namespace syscall_broker {
BrokerClient::BrokerClient(const BrokerPermissionList& broker_permission_list, BrokerClient::BrokerClient(const BrokerPermissionList& broker_permission_list,
BrokerChannel::EndPoint ipc_channel, BrokerChannel::EndPoint ipc_channel,
const BrokerCommandSet& allowed_command_set, const BrokerCommandSet& allowed_command_set,
bool fast_check_in_client, bool fast_check_in_client)
bool quiet_failures_for_tests)
: broker_permission_list_(broker_permission_list), : broker_permission_list_(broker_permission_list),
ipc_channel_(std::move(ipc_channel)), ipc_channel_(std::move(ipc_channel)),
allowed_command_set_(allowed_command_set), allowed_command_set_(allowed_command_set),
fast_check_in_client_(fast_check_in_client), fast_check_in_client_(fast_check_in_client) {}
quiet_failures_for_tests_(quiet_failures_for_tests) {}
BrokerClient::~BrokerClient() {} BrokerClient::~BrokerClient() {}
...@@ -85,31 +84,33 @@ int BrokerClient::Readlink(const char* path, char* buf, size_t bufsize) const { ...@@ -85,31 +84,33 @@ int BrokerClient::Readlink(const char* path, char* buf, size_t bufsize) const {
return -broker_permission_list_.denied_errno(); return -broker_permission_list_.denied_errno();
} }
base::Pickle write_pickle; // Message structure:
write_pickle.WriteInt(COMMAND_READLINK); // int: syscall_type
write_pickle.WriteString(path); // char[]: pathname, including '\0' terminator
RAW_CHECK(write_pickle.size() <= kMaxMessageLength); BrokerSimpleMessage message;
RAW_CHECK(message.AddIntToMessage(COMMAND_READLINK));
RAW_CHECK(message.AddStringToMessage(path));
int returned_fd = -1; int returned_fd = -1;
uint8_t reply_buf[kMaxMessageLength]; BrokerSimpleMessage reply;
ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf, ssize_t msg_len =
sizeof(reply_buf), &returned_fd); message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
if (msg_len < 0) if (msg_len < 0)
return msg_len; 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_value = -1;
int return_length = 0; size_t return_length = 0;
const char* return_data = nullptr; const char* return_data = nullptr;
if (!iter.ReadInt(&return_value)) if (!reply.ReadInt(&return_value))
return -ENOMEM; return -ENOMEM;
if (return_value < 0) if (return_value < 0)
return return_value; return return_value;
if (!iter.ReadData(&return_data, &return_length))
if (!reply.ReadData(&return_data, &return_length))
return -ENOMEM; return -ENOMEM;
if (return_length < 0) if (return_length < 0)
return -ENOMEM; return -ENOMEM;
if (static_cast<size_t>(return_length) > bufsize) if (static_cast<size_t>(return_length) > bufsize)
return -ENAMETOOLONG; return -ENAMETOOLONG;
memcpy(buf, return_data, return_length); memcpy(buf, return_data, return_length);
...@@ -126,23 +127,21 @@ int BrokerClient::Rename(const char* oldpath, const char* newpath) const { ...@@ -126,23 +127,21 @@ int BrokerClient::Rename(const char* oldpath, const char* newpath) const {
return -broker_permission_list_.denied_errno(); return -broker_permission_list_.denied_errno();
} }
base::Pickle write_pickle; BrokerSimpleMessage message;
write_pickle.WriteInt(COMMAND_RENAME); RAW_CHECK(message.AddIntToMessage(COMMAND_RENAME));
write_pickle.WriteString(oldpath); RAW_CHECK(message.AddStringToMessage(oldpath));
write_pickle.WriteString(newpath); RAW_CHECK(message.AddStringToMessage(newpath));
RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
int returned_fd = -1; int returned_fd = -1;
uint8_t reply_buf[kMaxMessageLength]; BrokerSimpleMessage reply;
ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf, ssize_t msg_len =
sizeof(reply_buf), &returned_fd); message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
if (msg_len < 0) if (msg_len < 0)
return msg_len; 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_value = -1;
if (!iter.ReadInt(&return_value)) if (!reply.ReadInt(&return_value))
return -ENOMEM; return -ENOMEM;
return return_value; return return_value;
...@@ -198,23 +197,22 @@ int BrokerClient::Unlink(const char* path) const { ...@@ -198,23 +197,22 @@ int BrokerClient::Unlink(const char* path) const {
int BrokerClient::PathOnlySyscall(BrokerCommand syscall_type, int BrokerClient::PathOnlySyscall(BrokerCommand syscall_type,
const char* pathname) const { const char* pathname) const {
base::Pickle write_pickle; BrokerSimpleMessage message;
write_pickle.WriteInt(syscall_type); RAW_CHECK(message.AddIntToMessage(syscall_type));
write_pickle.WriteString(pathname); RAW_CHECK(message.AddStringToMessage(pathname));
RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
int returned_fd = -1; int returned_fd = -1;
uint8_t reply_buf[kMaxMessageLength]; BrokerSimpleMessage reply;
ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf, ssize_t msg_len =
sizeof(reply_buf), &returned_fd); message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
if (msg_len < 0) if (msg_len < 0)
return msg_len; 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_value = -1;
if (!iter.ReadInt(&return_value)) if (!reply.ReadInt(&return_value))
return -ENOMEM; return -ENOMEM;
return return_value; return return_value;
} }
...@@ -225,23 +223,21 @@ int BrokerClient::PathOnlySyscall(BrokerCommand syscall_type, ...@@ -225,23 +223,21 @@ int BrokerClient::PathOnlySyscall(BrokerCommand syscall_type,
int BrokerClient::PathAndFlagsSyscall(BrokerCommand syscall_type, int BrokerClient::PathAndFlagsSyscall(BrokerCommand syscall_type,
const char* pathname, const char* pathname,
int flags) const { int flags) const {
base::Pickle write_pickle; BrokerSimpleMessage message;
write_pickle.WriteInt(syscall_type); RAW_CHECK(message.AddIntToMessage(syscall_type));
write_pickle.WriteString(pathname); RAW_CHECK(message.AddStringToMessage(pathname));
write_pickle.WriteInt(flags); RAW_CHECK(message.AddIntToMessage(flags));
RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
int returned_fd = -1; int returned_fd = -1;
uint8_t reply_buf[kMaxMessageLength]; BrokerSimpleMessage reply;
ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf, ssize_t msg_len =
sizeof(reply_buf), &returned_fd); message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
if (msg_len < 0) 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; int return_value = -1;
if (!iter.ReadInt(&return_value)) if (!reply.ReadInt(&return_value))
return -ENOMEM; return -ENOMEM;
return return_value; return return_value;
...@@ -268,23 +264,21 @@ int BrokerClient::PathAndFlagsSyscallReturningFD(BrokerCommand syscall_type, ...@@ -268,23 +264,21 @@ int BrokerClient::PathAndFlagsSyscallReturningFD(BrokerCommand syscall_type,
flags &= ~kCurrentProcessOpenFlagsMask; flags &= ~kCurrentProcessOpenFlagsMask;
} }
base::Pickle write_pickle; BrokerSimpleMessage message;
write_pickle.WriteInt(syscall_type); RAW_CHECK(message.AddIntToMessage(syscall_type));
write_pickle.WriteString(pathname); RAW_CHECK(message.AddStringToMessage(pathname));
write_pickle.WriteInt(flags); RAW_CHECK(message.AddIntToMessage(flags));
RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
int returned_fd = -1; int returned_fd = -1;
uint8_t reply_buf[kMaxMessageLength]; BrokerSimpleMessage reply;
ssize_t msg_len = SendRecvRequest(write_pickle, recvmsg_flags, reply_buf, ssize_t msg_len = message.SendRecvMsgWithFlags(
sizeof(reply_buf), &returned_fd); ipc_channel_.get(), recvmsg_flags, &returned_fd, &reply);
if (msg_len < 0) 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; int return_value = -1;
if (!iter.ReadInt(&return_value)) if (!reply.ReadInt(&return_value))
return -ENOMEM; return -ENOMEM;
if (return_value < 0) if (return_value < 0)
return return_value; return return_value;
...@@ -302,28 +296,27 @@ int BrokerClient::StatFamilySyscall(BrokerCommand syscall_type, ...@@ -302,28 +296,27 @@ int BrokerClient::StatFamilySyscall(BrokerCommand syscall_type,
const char* pathname, const char* pathname,
void* result_ptr, void* result_ptr,
size_t expected_result_size) const { size_t expected_result_size) const {
base::Pickle write_pickle; BrokerSimpleMessage message;
write_pickle.WriteInt(syscall_type); RAW_CHECK(message.AddIntToMessage(syscall_type));
write_pickle.WriteString(pathname); RAW_CHECK(message.AddStringToMessage(pathname));
RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
int returned_fd = -1; int returned_fd = -1;
uint8_t reply_buf[kMaxMessageLength]; BrokerSimpleMessage reply;
ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf, ssize_t msg_len =
sizeof(reply_buf), &returned_fd); message.SendRecvMsgWithFlags(ipc_channel_.get(), 0, &returned_fd, &reply);
if (msg_len < 0) if (msg_len < 0)
return msg_len; 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_value = -1;
int return_length = 0; size_t return_length = 0;
const char* return_data = nullptr; const char* return_data = nullptr;
if (!iter.ReadInt(&return_value))
if (!reply.ReadInt(&return_value))
return -ENOMEM; return -ENOMEM;
if (return_value < 0) if (return_value < 0)
return return_value; return return_value;
if (!iter.ReadData(&return_data, &return_length)) if (!reply.ReadData(&return_data, &return_length))
return -ENOMEM; return -ENOMEM;
if (static_cast<size_t>(return_length) != expected_result_size) if (static_cast<size_t>(return_length) != expected_result_size)
return -ENOMEM; return -ENOMEM;
...@@ -331,25 +324,5 @@ int BrokerClient::StatFamilySyscall(BrokerCommand syscall_type, ...@@ -331,25 +324,5 @@ int BrokerClient::StatFamilySyscall(BrokerCommand syscall_type,
return return_value; 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 syscall_broker
} // namespace sandbox } // namespace sandbox
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include <unistd.h> #include <unistd.h>
#include "base/macros.h" #include "base/macros.h"
#include "base/pickle.h"
#include "sandbox/linux/syscall_broker/broker_channel.h" #include "sandbox/linux/syscall_broker/broker_channel.h"
#include "sandbox/linux/syscall_broker/broker_command.h" #include "sandbox/linux/syscall_broker/broker_command.h"
...@@ -33,12 +32,10 @@ class BrokerClient { ...@@ -33,12 +32,10 @@ class BrokerClient {
// and save an IPC round trip. // and save an IPC round trip.
// |ipc_channel| needs to be a suitable SOCK_SEQPACKET unix socket. // |ipc_channel| needs to be a suitable SOCK_SEQPACKET unix socket.
// |fast_check_in_client| should be set to true and // |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, BrokerClient(const BrokerPermissionList& policy,
BrokerChannel::EndPoint ipc_channel, BrokerChannel::EndPoint ipc_channel,
const BrokerCommandSet& allowed_command_set, const BrokerCommandSet& allowed_command_set,
bool fast_check_in_client, bool fast_check_in_client);
bool quiet_failures_for_tests);
~BrokerClient(); ~BrokerClient();
// Get the file descriptor used for IPC. This is used for tests. // Get the file descriptor used for IPC. This is used for tests.
...@@ -93,20 +90,12 @@ class BrokerClient { ...@@ -93,20 +90,12 @@ class BrokerClient {
void* result_ptr, void* result_ptr,
size_t expected_result_size) const; 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 BrokerPermissionList& broker_permission_list_;
const BrokerChannel::EndPoint ipc_channel_; const BrokerChannel::EndPoint ipc_channel_;
const BrokerCommandSet allowed_command_set_; const BrokerCommandSet allowed_command_set_;
const bool fast_check_in_client_; // Whether to forward a request that we const bool fast_check_in_client_; // Whether to forward a request that we
// know will be denied to the broker. (Used // know will be denied to the broker. (Used
// for tests). // for tests).
const bool quiet_failures_for_tests_; // Disable certain error message when
// testing for failures.
DISALLOW_COPY_AND_ASSIGN(BrokerClient); DISALLOW_COPY_AND_ASSIGN(BrokerClient);
}; };
......
...@@ -17,8 +17,6 @@ namespace syscall_broker { ...@@ -17,8 +17,6 @@ namespace syscall_broker {
class BrokerPermissionList; class BrokerPermissionList;
constexpr size_t kMaxMessageLength = 4096;
// Some flags are local to the current process and cannot be sent over a Unix // Some flags are local to the current process and cannot be sent over a Unix
// socket. They need special treatment from the client. // socket. They need special treatment from the client.
// O_CLOEXEC is tricky because in theory another thread could call execve() // O_CLOEXEC is tricky because in theory another thread could call execve()
......
...@@ -16,15 +16,13 @@ ...@@ -16,15 +16,13 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector>
#include "base/files/scoped_file.h" #include "base/files/scoped_file.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/pickle.h"
#include "base/posix/eintr_wrapper.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_command.h"
#include "sandbox/linux/syscall_broker/broker_permission_list.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" #include "sandbox/linux/system_headers/linux_syscalls.h"
namespace sandbox { namespace sandbox {
...@@ -41,175 +39,185 @@ int sys_open(const char* pathname, int flags) { ...@@ -41,175 +39,185 @@ int sys_open(const char* pathname, int flags) {
return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode); 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 // 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, void AccessFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list, const BrokerPermissionList& permission_list,
const std::string& requested_filename, const std::string& requested_filename,
int mode, int mode,
base::Pickle* write_pickle) { BrokerSimpleMessage* reply) {
const char* file_to_access = NULL; const char* file_to_access = NULL;
if (!CommandAccessIsSafe(allowed_command_set, permission_list, if (!CommandAccessIsSafe(allowed_command_set, permission_list,
requested_filename.c_str(), mode, &file_to_access)) { requested_filename.c_str(), mode, &file_to_access)) {
write_pickle->WriteInt(-permission_list.denied_errno()); RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return; return;
} }
CHECK(file_to_access); RAW_CHECK(file_to_access);
if (access(file_to_access, mode) < 0) { if (access(file_to_access, mode) < 0) {
write_pickle->WriteInt(-errno); RAW_CHECK(reply->AddIntToMessage(-errno));
return; 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, void MkdirFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list, const BrokerPermissionList& permission_list,
const std::string& filename, const std::string& filename,
int mode, int mode,
base::Pickle* write_pickle) { BrokerSimpleMessage* reply) {
const char* file_to_access = nullptr; const char* file_to_access = nullptr;
if (!CommandMkdirIsSafe(allowed_command_set, permission_list, if (!CommandMkdirIsSafe(allowed_command_set, permission_list,
filename.c_str(), &file_to_access)) { filename.c_str(), &file_to_access)) {
write_pickle->WriteInt(-permission_list.denied_errno()); RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return; return;
} }
if (mkdir(file_to_access, mode) < 0) { if (mkdir(file_to_access, mode) < 0) {
write_pickle->WriteInt(-errno); RAW_CHECK(reply->AddIntToMessage(-errno));
return; return;
} }
write_pickle->WriteInt(0); RAW_CHECK(reply->AddIntToMessage(0));
} }
// Open |requested_filename| with |flags| if allowed by our permission list. // Open |requested_filename| with |flags| if allowed by our permission list.
// Write the syscall return value (-errno) to |write_pickle| and append // Write the syscall return value (-errno) to |reply| and return the
// a file descriptor to |opened_files| if relevant. // file descriptor in the |opened_file| if relevant.
void OpenFileForIPC(const BrokerCommandSet& allowed_command_set, void OpenFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list, const BrokerPermissionList& permission_list,
const std::string& requested_filename, const std::string& requested_filename,
int flags, int flags,
base::Pickle* write_pickle, BrokerSimpleMessage* reply,
std::vector<int>* opened_files) { int* opened_file) {
const char* file_to_open = NULL; const char* file_to_open = NULL;
bool unlink_after_open = false; bool unlink_after_open = false;
if (!CommandOpenIsSafe(allowed_command_set, permission_list, if (!CommandOpenIsSafe(allowed_command_set, permission_list,
requested_filename.c_str(), flags, &file_to_open, requested_filename.c_str(), flags, &file_to_open,
&unlink_after_open)) { &unlink_after_open)) {
write_pickle->WriteInt(-permission_list.denied_errno()); RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return; return;
} }
CHECK(file_to_open); CHECK(file_to_open);
int opened_fd = sys_open(file_to_open, flags); int opened_fd = sys_open(file_to_open, flags);
if (opened_fd < 0) { if (opened_fd < 0) {
write_pickle->WriteInt(-errno); RAW_CHECK(reply->AddIntToMessage(-errno));
return; return;
} }
if (unlink_after_open) if (unlink_after_open)
unlink(file_to_open); unlink(file_to_open);
opened_files->push_back(opened_fd); *opened_file = opened_fd;
write_pickle->WriteInt(0); RAW_CHECK(reply->AddIntToMessage(0));
} }
// Perform rename(2) on |old_filename| to |new_filename| and marshal the // Perform rename(2) on |old_filename| to |new_filename| and write the
// result to |write_pickle|. // result to |return_val|.
void RenameFileForIPC(const BrokerCommandSet& allowed_command_set, void RenameFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list, const BrokerPermissionList& permission_list,
const std::string& old_filename, const std::string& old_filename,
const std::string& new_filename, const std::string& new_filename,
base::Pickle* write_pickle) { BrokerSimpleMessage* reply) {
const char* old_file_to_access = nullptr; const char* old_file_to_access = nullptr;
const char* new_file_to_access = nullptr; const char* new_file_to_access = nullptr;
if (!CommandRenameIsSafe(allowed_command_set, permission_list, if (!CommandRenameIsSafe(allowed_command_set, permission_list,
old_filename.c_str(), new_filename.c_str(), old_filename.c_str(), new_filename.c_str(),
&old_file_to_access, &new_file_to_access)) { &old_file_to_access, &new_file_to_access)) {
write_pickle->WriteInt(-permission_list.denied_errno()); RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return; return;
} }
if (rename(old_file_to_access, new_file_to_access) < 0) { if (rename(old_file_to_access, new_file_to_access) < 0) {
write_pickle->WriteInt(-errno); RAW_CHECK(reply->AddIntToMessage(-errno));
return; return;
} }
write_pickle->WriteInt(0); RAW_CHECK(reply->AddIntToMessage(0));
} }
// Perform readlink(2) on |filename| using a buffer of MAX_PATH bytes. // Perform readlink(2) on |filename| using a buffer of MAX_PATH bytes.
void ReadlinkFileForIPC(const BrokerCommandSet& allowed_command_set, void ReadlinkFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list, const BrokerPermissionList& permission_list,
const std::string& filename, const std::string& filename,
base::Pickle* write_pickle) { BrokerSimpleMessage* reply) {
const char* file_to_access = nullptr; const char* file_to_access = nullptr;
if (!CommandReadlinkIsSafe(allowed_command_set, permission_list, if (!CommandReadlinkIsSafe(allowed_command_set, permission_list,
filename.c_str(), &file_to_access)) { filename.c_str(), &file_to_access)) {
write_pickle->WriteInt(-permission_list.denied_errno()); RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return; return;
} }
char buf[PATH_MAX]; char buf[PATH_MAX];
ssize_t result = readlink(file_to_access, buf, sizeof(buf)); ssize_t result = readlink(file_to_access, buf, sizeof(buf));
if (result < 0) { if (result < 0) {
write_pickle->WriteInt(-errno); RAW_CHECK(reply->AddIntToMessage(-errno));
return; return;
} }
write_pickle->WriteInt(result); RAW_CHECK(reply->AddIntToMessage(result));
write_pickle->WriteData(buf, result); RAW_CHECK(reply->AddDataToMessage(buf, result));
} }
void RmdirFileForIPC(const BrokerCommandSet& allowed_command_set, void RmdirFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list, const BrokerPermissionList& permission_list,
const std::string& requested_filename, const std::string& requested_filename,
base::Pickle* write_pickle) { BrokerSimpleMessage* reply) {
const char* file_to_access = nullptr; const char* file_to_access = nullptr;
if (!CommandRmdirIsSafe(allowed_command_set, permission_list, if (!CommandRmdirIsSafe(allowed_command_set, permission_list,
requested_filename.c_str(), &file_to_access)) { requested_filename.c_str(), &file_to_access)) {
write_pickle->WriteInt(-permission_list.denied_errno()); RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return; return;
} }
if (rmdir(file_to_access) < 0) { if (rmdir(file_to_access) < 0) {
write_pickle->WriteInt(-errno); RAW_CHECK(reply->AddIntToMessage(-errno));
return; return;
} }
write_pickle->WriteInt(0); RAW_CHECK(reply->AddIntToMessage(0));
} }
// Perform stat(2) on |requested_filename| and marshal the result to // Perform stat(2) on |requested_filename| and write the result to
// |write_pickle|. // |return_val|.
void StatFileForIPC(const BrokerCommandSet& allowed_command_set, void StatFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list, const BrokerPermissionList& permission_list,
BrokerCommand command_type, BrokerCommand command_type,
const std::string& requested_filename, const std::string& requested_filename,
base::Pickle* write_pickle) { BrokerSimpleMessage* reply) {
const char* file_to_access = nullptr; const char* file_to_access = nullptr;
if (!CommandStatIsSafe(allowed_command_set, permission_list, if (!CommandStatIsSafe(allowed_command_set, permission_list,
requested_filename.c_str(), &file_to_access)) { requested_filename.c_str(), &file_to_access)) {
write_pickle->WriteInt(-permission_list.denied_errno()); RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
return; return;
} }
if (command_type == COMMAND_STAT) { if (command_type == COMMAND_STAT) {
struct stat sb; struct stat sb;
if (stat(file_to_access, &sb) < 0) { if (stat(file_to_access, &sb) < 0) {
write_pickle->WriteInt(-errno); RAW_CHECK(reply->AddIntToMessage(-errno));
return; return;
} }
write_pickle->WriteInt(0); RAW_CHECK(reply->AddIntToMessage(0));
write_pickle->WriteData(reinterpret_cast<char*>(&sb), sizeof(sb)); RAW_CHECK(
reply->AddDataToMessage(reinterpret_cast<char*>(&sb), sizeof(sb)));
} else { } else {
DCHECK(command_type == COMMAND_STAT64); DCHECK(command_type == COMMAND_STAT64);
#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 #if defined(__ANDROID_API__) && __ANDROID_API__ < 21
// stat64 is not defined for older Android API versions in newer NDK // stat64 is not defined for older Android API versions in newer NDK
// versions. // versions.
write_pickle->WriteInt(-ENOSYS); RAW_CHECK(reply->AddIntToMessage(-ENOSYS));
return; return;
#else #else
struct stat64 sb; struct stat64 sb;
if (stat64(file_to_access, &sb) < 0) { if (stat64(file_to_access, &sb) < 0) {
write_pickle->WriteInt(-errno); RAW_CHECK(reply->AddIntToMessage(-errno));
return; return;
} }
write_pickle->WriteInt(0); RAW_CHECK(reply->AddIntToMessage(0));
write_pickle->WriteData(reinterpret_cast<char*>(&sb), sizeof(sb)); RAW_CHECK(
reply->AddDataToMessage(reinterpret_cast<char*>(&sb), sizeof(sb)));
#endif // defined(__ANDROID_API__) && __ANDROID_API__ < 21 #endif // defined(__ANDROID_API__) && __ANDROID_API__ < 21
} }
} }
...@@ -217,100 +225,108 @@ void StatFileForIPC(const BrokerCommandSet& allowed_command_set, ...@@ -217,100 +225,108 @@ void StatFileForIPC(const BrokerCommandSet& allowed_command_set,
void UnlinkFileForIPC(const BrokerCommandSet& allowed_command_set, void UnlinkFileForIPC(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list, const BrokerPermissionList& permission_list,
const std::string& requested_filename, const std::string& requested_filename,
base::Pickle* write_pickle) { BrokerSimpleMessage* message) {
const char* file_to_access = nullptr; const char* file_to_access = nullptr;
if (!CommandUnlinkIsSafe(allowed_command_set, permission_list, if (!CommandUnlinkIsSafe(allowed_command_set, permission_list,
requested_filename.c_str(), &file_to_access)) { requested_filename.c_str(), &file_to_access)) {
write_pickle->WriteInt(-permission_list.denied_errno()); RAW_CHECK(message->AddIntToMessage(-permission_list.denied_errno()));
return; return;
} }
if (unlink(file_to_access) < 0) { if (unlink(file_to_access) < 0) {
write_pickle->WriteInt(-errno); RAW_CHECK(message->AddIntToMessage(-errno));
return; return;
} }
write_pickle->WriteInt(0); RAW_CHECK(message->AddIntToMessage(0));
} }
// Handle a |command_type| request contained in |iter| and write the reply // 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, bool HandleRemoteCommand(const BrokerCommandSet& allowed_command_set,
const BrokerPermissionList& permission_list, const BrokerPermissionList& permission_list,
base::PickleIterator iter, BrokerSimpleMessage* message,
base::Pickle* write_pickle, BrokerSimpleMessage* reply,
std::vector<int>* opened_files) { int* opened_file) {
// Message structure:
// int: command type
// char[]: pathname
// int: flags
int command_type; int command_type;
if (!iter.ReadInt(&command_type)) if (!message->ReadInt(&command_type))
return false; return false;
switch (command_type) { switch (command_type) {
case COMMAND_ACCESS: { case COMMAND_ACCESS: {
std::string requested_filename; const char* requested_filename;
int flags = 0; int flags = 0;
if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags)) if (!GetPathAndFlags(message, &requested_filename, &flags))
return false; return false;
AccessFileForIPC(allowed_command_set, permission_list, requested_filename, AccessFileForIPC(allowed_command_set, permission_list, requested_filename,
flags, write_pickle); flags, reply);
break; break;
} }
case COMMAND_MKDIR: { case COMMAND_MKDIR: {
std::string requested_filename; const char* requested_filename;
int mode = 0; int mode = 0;
if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&mode)) if (!GetPathAndFlags(message, &requested_filename, &mode))
return false; return false;
MkdirFileForIPC(allowed_command_set, permission_list, requested_filename, MkdirFileForIPC(allowed_command_set, permission_list, requested_filename,
mode, write_pickle); mode, reply);
break; break;
} }
case COMMAND_OPEN: { case COMMAND_OPEN: {
std::string requested_filename; const char* requested_filename;
int flags = 0; int flags = 0;
if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags)) if (!GetPathAndFlags(message, &requested_filename, &flags))
return false; return false;
OpenFileForIPC(allowed_command_set, permission_list, requested_filename, OpenFileForIPC(allowed_command_set, permission_list, requested_filename,
flags, write_pickle, opened_files); flags, reply, opened_file);
break; break;
} }
case COMMAND_READLINK: { case COMMAND_READLINK: {
std::string filename; const char* filename;
if (!iter.ReadString(&filename)) if (!message->ReadString(&filename))
return false; return false;
ReadlinkFileForIPC(allowed_command_set, permission_list, filename,
write_pickle); ReadlinkFileForIPC(allowed_command_set, permission_list, filename, reply);
break; break;
} }
case COMMAND_RENAME: { case COMMAND_RENAME: {
std::string old_filename; const char* old_filename;
std::string new_filename; if (!message->ReadString(&old_filename))
if (!iter.ReadString(&old_filename) || !iter.ReadString(&new_filename)) return false;
const char* new_filename;
if (!message->ReadString(&new_filename))
return false; return false;
RenameFileForIPC(allowed_command_set, permission_list, old_filename, RenameFileForIPC(allowed_command_set, permission_list, old_filename,
new_filename, write_pickle); new_filename, reply);
break; break;
} }
case COMMAND_RMDIR: { case COMMAND_RMDIR: {
std::string requested_filename; const char* requested_filename;
if (!iter.ReadString(&requested_filename)) if (!message->ReadString(&requested_filename))
return false; return false;
RmdirFileForIPC(allowed_command_set, permission_list, requested_filename, RmdirFileForIPC(allowed_command_set, permission_list, requested_filename,
write_pickle); reply);
break; break;
} }
case COMMAND_STAT: case COMMAND_STAT:
case COMMAND_STAT64: { case COMMAND_STAT64: {
std::string requested_filename; const char* requested_filename;
if (!iter.ReadString(&requested_filename)) if (!message->ReadString(&requested_filename))
return false; return false;
StatFileForIPC(allowed_command_set, permission_list, StatFileForIPC(allowed_command_set, permission_list,
static_cast<BrokerCommand>(command_type), static_cast<BrokerCommand>(command_type),
requested_filename, write_pickle); requested_filename, reply);
break; break;
} }
case COMMAND_UNLINK: { case COMMAND_UNLINK: {
std::string requested_filename; const char* requested_filename;
if (!iter.ReadString(&requested_filename)) if (!message->ReadString(&requested_filename))
return false; return false;
UnlinkFileForIPC(allowed_command_set, permission_list, requested_filename, UnlinkFileForIPC(allowed_command_set, permission_list, requested_filename,
write_pickle); reply);
break; break;
} }
default: default:
...@@ -336,48 +352,36 @@ BrokerHost::~BrokerHost() {} ...@@ -336,48 +352,36 @@ BrokerHost::~BrokerHost() {}
// that we will then close. // that we will then close.
// A request should start with an int that will be used as the command type. // A request should start with an int that will be used as the command type.
BrokerHost::RequestStatus BrokerHost::HandleRequest() const { BrokerHost::RequestStatus BrokerHost::HandleRequest() const {
std::vector<base::ScopedFD> fds; BrokerSimpleMessage message;
char buf[kMaxMessageLength];
errno = 0; errno = 0;
const ssize_t msg_len = base::UnixDomainSocket::RecvMsg( base::ScopedFD temporary_ipc;
ipc_channel_.get(), buf, sizeof(buf), &fds); const ssize_t msg_len =
message.RecvMsgWithFlags(ipc_channel_.get(), 0, &temporary_ipc);
if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) { if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) {
// EOF from the client, or the client died, we should die. // EOF from the client, or the client died, we should die.
return RequestStatus::LOST_CLIENT; 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. // 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"; PLOG(ERROR) << "Error reading message from the client";
return RequestStatus::FAILURE; return RequestStatus::FAILURE;
} }
base::ScopedFD temporary_ipc(std::move(fds[0])); BrokerSimpleMessage reply;
int opened_file = -1;
base::Pickle pickle(buf, msg_len);
base::PickleIterator iter(pickle);
base::Pickle write_pickle;
std::vector<int> opened_files;
bool result = bool result =
HandleRemoteCommand(allowed_command_set_, broker_permission_list_, iter, HandleRemoteCommand(allowed_command_set_, broker_permission_list_,
&write_pickle, &opened_files); &message, &reply, &opened_file);
if (result) { ssize_t sent = reply.SendMsg(temporary_ipc.get(), opened_file);
CHECK_LE(write_pickle.size(), kMaxMessageLength); if (sent < 0)
ssize_t sent = base::UnixDomainSocket::SendMsg( LOG(ERROR) << "sent failed";
temporary_ipc.get(), write_pickle.data(), write_pickle.size(),
opened_files);
if (sent <= 0) {
LOG(ERROR) << "Could not send IPC reply";
result = false;
}
}
// Close anything we have opened in this process. if (opened_file >= 0) {
for (int fd : opened_files) { int ret = IGNORE_EINTR(close(opened_file));
int ret = IGNORE_EINTR(close(fd));
DCHECK(!ret) << "Could not close file descriptor"; DCHECK(!ret) << "Could not close file descriptor";
} }
......
...@@ -78,7 +78,7 @@ bool BrokerProcess::Init( ...@@ -78,7 +78,7 @@ bool BrokerProcess::Init(
broker_pid_ = child_pid; broker_pid_ = child_pid;
broker_client_ = std::make_unique<BrokerClient>( broker_client_ = std::make_unique<BrokerClient>(
broker_permission_list_, std::move(ipc_writer), allowed_command_set_, broker_permission_list_, std::move(ipc_writer), allowed_command_set_,
fast_check_in_client_, quiet_failures_for_tests_); fast_check_in_client_);
initialized_ = true; initialized_ = true;
return true; return true;
} }
......
// 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
// 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 { 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_
// 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment