123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- // 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 <string.h>
- #include <sys/socket.h>
- #include <unistd.h>
- #include "base/check_op.h"
- #include "base/containers/span.h"
- #include "base/files/scoped_file.h"
- #include "base/notreached.h"
- #include "base/numerics/safe_math.h"
- #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 {
- ssize_t BrokerSimpleMessage::SendRecvMsgWithFlags(int fd,
- int recvmsg_flags,
- base::ScopedFD* result_fd,
- BrokerSimpleMessage* reply) {
- return SendRecvMsgWithFlagsMultipleFds(fd, recvmsg_flags, {}, {result_fd, 1},
- reply);
- }
- ssize_t BrokerSimpleMessage::SendRecvMsgWithFlagsMultipleFds(
- int fd,
- int recvmsg_flags,
- base::span<const int> send_fds,
- base::span<base::ScopedFD> result_fds,
- BrokerSimpleMessage* reply) {
- RAW_CHECK(reply);
- RAW_CHECK(send_fds.size() + 1 <= base::UnixDomainSocket::kMaxFileDescriptors);
- // This socketpair is only used for the IPC and is cleaned up before
- // returning.
- base::ScopedFD recv_sock;
- base::ScopedFD send_sock;
- if (!base::CreateSocketPair(&recv_sock, &send_sock))
- return -1;
- int send_fds_with_reply_socket[base::UnixDomainSocket::kMaxFileDescriptors];
- send_fds_with_reply_socket[0] = send_sock.get();
- for (size_t i = 0; i < send_fds.size(); i++) {
- send_fds_with_reply_socket[i + 1] = send_fds[i];
- }
- if (!SendMsgMultipleFds(fd,
- {send_fds_with_reply_socket, send_fds.size() + 1})) {
- return -1;
- }
- // Close the sending end of the socket right away so that if our peer closes
- // it before sending a response (e.g., from exiting), RecvMsgWithFlags() will
- // return EOF instead of hanging.
- send_sock.reset();
- const ssize_t reply_len = reply->RecvMsgWithFlagsMultipleFds(
- recv_sock.get(), recvmsg_flags, result_fds);
- recv_sock.reset();
- if (reply_len == -1)
- return -1;
- return reply_len;
- }
- bool BrokerSimpleMessage::SendMsg(int fd, int send_fd) {
- return SendMsgMultipleFds(
- fd, send_fd == -1 ? base::span<int>() : base::span<int>(&send_fd, 1));
- }
- bool BrokerSimpleMessage::SendMsgMultipleFds(int fd,
- base::span<const int> send_fds) {
- if (broken_)
- return false;
- RAW_CHECK(send_fds.size() <= base::UnixDomainSocket::kMaxFileDescriptors);
- struct msghdr msg = {};
- const void* buf = reinterpret_cast<const void*>(message_);
- struct iovec iov = {const_cast<void*>(buf), length_};
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- const unsigned control_len = CMSG_SPACE(send_fds.size() * sizeof(int));
- char control_buffer[control_len];
- if (send_fds.size() >= 1) {
- struct cmsghdr* cmsg;
- msg.msg_control = control_buffer;
- msg.msg_controllen = control_len;
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- int len = 0;
- for (size_t i = 0; i < send_fds.size(); i++) {
- if (send_fds[i] < 0)
- return false;
- // CMSG_DATA() not guaranteed to be aligned so this must use memcpy.
- memcpy(CMSG_DATA(cmsg) + (sizeof(int) * i), &send_fds[i], sizeof(int));
- len += sizeof(int);
- }
- cmsg->cmsg_len = CMSG_LEN(len);
- msg.msg_controllen = cmsg->cmsg_len;
- }
- // 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) {
- ssize_t ret = RecvMsgWithFlagsMultipleFds(
- fd, flags, base::span<base::ScopedFD>(return_fd, 1));
- return ret;
- }
- ssize_t BrokerSimpleMessage::RecvMsgWithFlagsMultipleFds(
- int fd,
- int flags,
- base::span<base::ScopedFD> return_fds) {
- // The message must be fresh and unused.
- RAW_CHECK(!read_only_ && !write_only_);
- RAW_CHECK(return_fds.size() <= base::UnixDomainSocket::kMaxFileDescriptors);
- read_only_ = true; // The message should not be written to again.
- struct msghdr msg = {};
- struct iovec iov = {message_, kMaxMessageLength};
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- const size_t kControlBufferSize =
- CMSG_SPACE(sizeof(fd) * base::UnixDomainSocket::kMaxFileDescriptors) +
- CMSG_SPACE(sizeof(struct ucred));
- 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 = nullptr;
- 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(fd), 0u);
- DCHECK_EQ(wire_fds, nullptr);
- wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
- wire_fds_len = payload_len / sizeof(fd);
- }
- 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;
- }
- }
- }
- 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 > return_fds.size()) {
- // The number of fds received is limited to return_fds.size(). If there
- // are more in the message than expected, close them and return an error.
- for (size_t i = 0; i < wire_fds_len; ++i) {
- close(wire_fds[i]);
- }
- errno = EMSGSIZE;
- NOTREACHED();
- return -1;
- }
- for (size_t i = 0; i < wire_fds_len; ++i) {
- return_fds[i] = base::ScopedFD(wire_fds[i]);
- }
- }
- // At this point, |r| is guaranteed to be >= 0.
- 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(data);
- 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(data));
- write_next_ += sizeof(data);
- 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_.get());
- 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(*result)) > (message_ + length_)) {
- broken_ = true;
- return false;
- }
- memcpy(result, read_next_, sizeof(*result));
- read_next_ = read_next_ + sizeof(*result);
- 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
|