diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-24 12:15:48 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-28 13:30:04 +0000 |
commit | b014812705fc80bff0a5c120dfcef88f349816dc (patch) | |
tree | 25a2e2d9fa285f1add86aa333389a839f81a39ae /chromium/sandbox | |
parent | 9f4560b1027ae06fdb497023cdcaf91b8511fa74 (diff) | |
download | qtwebengine-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')
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 |