Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/ipc/chromium/src/chrome/common/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 26 kB image not shown  

Quelle  ipc_channel_mach.cc

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "ipc_channel_mach.h"

#include <mach/mach.h>
#include <sys/fileport.h>

#ifdef XP_MACOSX
#  include <bsm/libbsm.h>
#endif

#include "base/process_util.h"
#include "chrome/common/ipc_channel_utils.h"
#include "mozilla/ipc/ProtocolUtils.h"

using namespace mozilla::ipc;

namespace IPC {

static constexpr mach_msg_id_t kIPDLMessageId = 'IPDL';

const Channel::ChannelKind ChannelMach::sKind{
    .create_raw_pipe = &ChannelMach::CreateRawPipe,
    .num_relayed_attachments = &ChannelMach::NumRelayedAttachments,
    .is_valid_handle = &ChannelMach::IsValidHandle,
};

ChannelMach::ChannelMach(mozilla::UniqueMachReceiveRight receive,
                         mozilla::UniqueMachSendRight send, Mode mode,
                         base::ProcessId other_pid)
    : receive_port_(std::move(receive)),
      send_port_(std::move(send)),
      send_buffer_(mozilla::MakeUnique<char[]>(Channel::kReadBufferSize)),
      receive_buffer_(mozilla::MakeUnique<char[]>(Channel::kReadBufferSize)),
      other_pid_(other_pid) {
  EnqueueHelloMessage();
}

bool ChannelMach::EnqueueHelloMessage() {
  chan_cap_.NoteExclusiveAccess();

  mozilla::UniquePtr<Message> msg(
      new Message(MSG_ROUTING_NONE, HELLO_MESSAGE_TYPE));
  if (!msg->WriteInt(base::GetCurrentProcId())) {
    CloseLocked();
    return false;
  }

  // If we don't have a receive_port_ when we're queueing the "hello" message,
  // build one, and send the corresponding send right in the hello message.
  mozilla::UniqueMachSendRight peer_send;
  if (!receive_port_ && !CreateRawPipe(&receive_port_, &peer_send)) {
    CloseLocked();
    return false;
  }
  if (!msg->WriteMachSendRight(std::move(peer_send))) {
    CloseLocked();
    return false;
  }

  OutputQueuePush(std::move(msg));
  return true;
}

bool ChannelMach::Connect(Listener* listener) {
  IOThread().AssertOnCurrentThread();
  mozilla::MutexAutoLock lock(SendMutex());
  chan_cap_.NoteExclusiveAccess();

  if (!receive_port_) {
    return false;
  }

  listener_ = listener;

  // Mark this port as receiving IPC from our peer process. This allows the
  // kernel to boost the QoS of the receiver based on the QoS of the sender.
  // (ignore failures to set this, as it's non-fatal).
  kern_return_t kr =
      mach_port_set_attributes(mach_task_self(), receive_port_.get(),
                               MACH_PORT_IMPORTANCE_RECEIVER, nullptr, 0);
  if (kr != KERN_SUCCESS) {
    CHROMIUM_LOG(ERROR) << "mach_port_set_attributes failed: "
                        << mach_error_string(kr);
  }

  // Register to receive a notification when all send rights for this port have
  // been destroyed.
  // NOTE: MACH_NOTIFY_NO_SENDERS does not consider send-once rights to be send
  // rights for the purposes of there being "no senders", so the send-once right
  // used for the notification will not prevent it from being sent.
  mozilla::UniqueMachSendRight previous;
  kr = mach_port_request_notification(
      mach_task_self(), receive_port_.get(), MACH_NOTIFY_NO_SENDERS, 0,
      receive_port_.get(), MACH_MSG_TYPE_MAKE_SEND_ONCE,
      mozilla::getter_Transfers(previous));
  if (kr != KERN_SUCCESS) {
    CHROMIUM_LOG(ERROR) << "mach_port_request_notification: "
                        << mach_error_string(kr);
    return false;
  }

  // Begin listening for messages on our receive port.
  MessageLoopForIO::current()->WatchMachReceivePort(receive_port_.get(),
                                                    &watch_controller_, this);

  return ContinueConnect(nullptr);
}

bool ChannelMach::ContinueConnect(mozilla::UniqueMachSendRight send_port) {
  chan_cap_.NoteExclusiveAccess();
  MOZ_ASSERT(receive_port_);

  // If we're still waiting for a mach send right from our peer, don't clear
  // waiting_connect_ yet.
  if (!send_port_) {
    if (!send_port) {
      MOZ_ASSERT(waiting_connect_);
      return true;
    }
    send_port_ = std::move(send_port);
  }

  waiting_connect_ = false;

  return ProcessOutgoingMessages();
}

void ChannelMach::SetOtherPid(base::ProcessId other_pid) {
  IOThread().AssertOnCurrentThread();
  mozilla::MutexAutoLock lock(SendMutex());
  chan_cap_.NoteExclusiveAccess();
  MOZ_RELEASE_ASSERT(
      other_pid_ == base::kInvalidProcessId || other_pid_ == other_pid,
      "Multiple sources of SetOtherPid disagree!");
  other_pid_ = other_pid;
}

namespace {

// Small helper type for safely working with Mach message buffers, which consist
// of a sequence of C structs.
struct MsgBufferHelper {
 public:
  MsgBufferHelper(char* buf, size_t size)
      : start_(buf), current_(start_), end_(start_ + size) {}

  template <typename T>
  T* Next() {
    MOZ_RELEASE_ASSERT(Remaining().size() >= sizeof(T));
    T* obj = reinterpret_cast<T*>(current_);
    current_ += sizeof(T);
    return obj;
  }

  template <typename T, typename U>
  T* CastLast(U* previous) {
    static_assert(sizeof(T) >= sizeof(U), "casting to a smaller type?");
    MOZ_RELEASE_ASSERT(reinterpret_cast<char*>(previous + 1) == current_);
    current_ = reinterpret_cast<char*>(previous);
    return Next<T>();
  }

  template <typename T>
  T* SetTrailerOffset(size_t offset) {
    MOZ_RELEASE_ASSERT(offset < size_t(end_ - start_) - sizeof(T));
    // Limit any future reads to the region before the trailer.
    end_ = start_ + offset;
    return reinterpret_cast<T*>(end_);
  }

  template <typename T>
  void WriteDescriptor(mach_msg_base_t* base, T descriptor) {
    MOZ_ASSERT(base->header.msgh_size == Offset(), "unexpected size?");
    *Next<T>() = descriptor;
    base->header.msgh_size = Offset();
    base->header.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
    base->body.msgh_descriptor_count++;
  }

  char* WriteBytes(mach_msg_base_t* base, size_t size) {
    MOZ_ASSERT(base->header.msgh_size == Offset(), "unexpected size?");
    MOZ_RELEASE_ASSERT(Remaining().size() >= round_msg(size));
    char* bytes = current_;
    current_ += round_msg(size);
    base->header.msgh_size = Offset();
    return bytes;
  }

  mozilla::Span<char> Remaining() { return {current_, end_}; }

 private:
  size_t Offset() { return current_ - start_; }

  char* start_;
  char* current_;
  char* end_;
};

}  // namespace

static bool SenderIs(mach_msg_audit_trailer_t* trailer,
                     const audit_token_t& expected) {
  return memcmp(&trailer->msgh_audit, &expected, sizeof(audit_token_t)) == 0;
}

bool ChannelMach::ProcessIncomingMessage() {
  chan_cap_.NoteOnTarget();

  MsgBufferHelper buf_helper(receive_buffer_.get(), Channel::kReadBufferSize);

  auto* header = buf_helper.Next<mach_msg_header_t>();
  *header = mach_msg_header_t{.msgh_size = Channel::kReadBufferSize,
                              .msgh_local_port = receive_port_.get()};
  auto destroy_msg =
      mozilla::MakeScopeExit([header] { mach_msg_destroy(header); });

  kern_return_t kr = mach_msg(
      header,
      MACH_RCV_MSG | MACH_RCV_TIMEOUT |
          MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
          MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_VOUCHER,
      0, header->msgh_size, receive_port_.get(), /* timeout */ 0,
      MACH_PORT_NULL);
  if (kr != KERN_SUCCESS) {
    if (kr == MACH_RCV_TIMED_OUT) {
      return true;
    }
    CHROMIUM_LOG(ERROR) << "mach_msg receive failed: " << mach_error_string(kr);
    return false;
  }

  // Get a pointer to the message audit trailer. This contains information
  // about which entitiy sent the particular notification.
  auto* trailer =
      buf_helper.SetTrailerOffset<mach_msg_audit_trailer_t>(header->msgh_size);
  if (!trailer) {
    CHROMIUM_LOG(ERROR) << "buffer doesn't have space for audit trailer";
    return false;
  }

  // Respond to notifications from the kernel.
  if (SenderIs(trailer, KERNEL_AUDIT_TOKEN_VALUE)) {
    // If we've received MACH_NOTIFY_NO_SENDERS, the other side has gone away,
    // so we return `false` to close the channel. Otherwise the notification is
    // ignored, and we return `true`.
    return header->msgh_id != MACH_NOTIFY_NO_SENDERS;
  }

  if (header->msgh_id != kIPDLMessageId) {
    CHROMIUM_LOG(ERROR) << "unknown mach message type from peer: "
                        << header->msgh_id;
    return false;
  }

  // If we have an audit token for our peer, ensure it matches the one we
  // recorded from our HELLO message.
  if (peer_audit_token_ && !SenderIs(trailer, *peer_audit_token_)) {
    CHROMIUM_LOG(ERROR) << "message not sent by expected peer";
    return false;
  }

  if (buf_helper.Remaining().Length() < sizeof(mach_msg_body_t)) {
    CHROMIUM_LOG(ERROR) << "message is too small";
    return false;
  }

  // Read out descriptors from the sent message.
  auto* msg_body = buf_helper.Next<mach_msg_body_t>();
  if ((msg_body->msgh_descriptor_count > 0) !=
      MACH_MSGH_BITS_IS_COMPLEX(header->msgh_bits)) {
    CHROMIUM_LOG(ERROR)
        << "expected msgh_descriptor_count to match MACH_MSGH_BITS_COMPLEX";
    return false;
  }

  mach_msg_ool_descriptor_t* ool_descr = nullptr;
  nsTArray<mozilla::UniqueMachSendRight> send_rights;
  nsTArray<mozilla::UniqueMachReceiveRight> receive_rights;
  for (size_t i = 0; i < msg_body->msgh_descriptor_count; ++i) {
    auto* descr = buf_helper.Next<mach_msg_type_descriptor_t>();
    switch (descr->type) {
      case MACH_MSG_OOL_DESCRIPTOR: {
        if (ool_descr) {
          CHROMIUM_LOG(ERROR) << "unexpected duplicate MACH_MSG_OOL_DESCRIPTOR";
          return false;
        }
        ool_descr = buf_helper.CastLast<mach_msg_ool_descriptor_t>(descr);
        break;
      }
      case MACH_MSG_PORT_DESCRIPTOR: {
        auto* port_descr =
            buf_helper.CastLast<mach_msg_port_descriptor_t>(descr);
        switch (port_descr->disposition) {
          case MACH_MSG_TYPE_MOVE_SEND:
            send_rights.EmplaceBack(
                std::exchange(port_descr->name, MACH_PORT_NULL));
            break;
          case MACH_MSG_TYPE_MOVE_RECEIVE:
            receive_rights.EmplaceBack(
                std::exchange(port_descr->name, MACH_PORT_NULL));
            break;
          default:
            CHROMIUM_LOG(ERROR) << "unexpected port descriptor disposition";
            return false;
        }
        break;
      }
      case MACH_MSG_OOL_PORTS_DESCRIPTOR: {
        auto* ool_ports_descr =
            buf_helper.CastLast<mach_msg_ool_ports_descriptor_t>(descr);
        mozilla::Span<mach_port_t> names(
            reinterpret_cast<mach_port_t*>(ool_ports_descr->address),
            ool_ports_descr->count);
        switch (ool_ports_descr->disposition) {
          case MACH_MSG_TYPE_MOVE_SEND:
            send_rights.SetCapacity(names.Length());
            for (mach_port_t& name : names) {
              send_rights.EmplaceBack(std::exchange(name, MACH_PORT_NULL));
            }
            break;
          case MACH_MSG_TYPE_MOVE_RECEIVE:
            receive_rights.SetCapacity(names.Length());
            for (mach_port_t& name : names) {
              receive_rights.EmplaceBack(std::exchange(name, MACH_PORT_NULL));
            }
            break;
          default:
            CHROMIUM_LOG(ERROR) << "unexpected port descriptor disposition";
            return false;
        }
        break;
      }
      default:
        CHROMIUM_LOG(ERROR) << "unexpected descriptor type";
        return false;
    }
  }

  // If we have an OOL descriptor, the payload is in that buffer, otherwise, it
  // is the remainder of the message buffer.
  mozilla::Span<const char> payload =
      ool_descr
          ? mozilla::Span{reinterpret_cast<const char*>(ool_descr->address),
                          static_cast<size_t>(ool_descr->size)}
          : buf_helper.Remaining();

  // Check that the payload contains a complete message of the expected size
  // before constructing it.
  uint32_t hdr_size =
      Message::MessageSize(payload.data(), payload.data() + payload.size());
  if (!hdr_size || round_msg(hdr_size) != payload.size()) {
    CHROMIUM_LOG(ERROR) << "Message size does not match transferred payload";
    return false;
  }

  mozilla::UniquePtr<Message> message =
      mozilla::MakeUnique<Message>(payload.data(), payload.size());

  // Transfer ownership of the voucher port into the IPC::Message.
  if (MACH_MSGH_BITS_VOUCHER(header->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND) {
    message->mach_voucher_.reset(header->msgh_voucher_port);
    header->msgh_voucher_port = MACH_PORT_NULL;
    header->msgh_bits &= ~MACH_MSGH_BITS_VOUCHER_MASK;
  }

  // Unwrap any fileports attached to this message into FDs. FDs are always
  // added after other mach port rights, so we can assume the last
  // |num_handles| rights are fileports.
  if (message->header()->num_handles > send_rights.Length()) {
    CHROMIUM_LOG(ERROR) << "Missing send rights in message";
    return false;
  }
  for (auto& wrapped_fd :
       mozilla::Span{send_rights}.Last(message->header()->num_handles)) {
    message->attached_handles_.AppendElement(
        mozilla::UniqueFileHandle(fileport_makefd(wrapped_fd.get())));
  }
  send_rights.TruncateLength(send_rights.Length() -
                             message->header()->num_handles);
  message->attached_send_rights_ = std::move(send_rights);
  message->attached_receive_rights_ = std::move(receive_rights);

  // Note: We set other_pid_ below when we receive a Hello message (which has no
  // routing ID), but we only emit a profiler marker for messages with a routing
  // ID, so there's no conflict here.
  AddIPCProfilerMarker(*message, other_pid_, MessageDirection::eReceiving,
                       MessagePhase::TransferEnd);

#ifdef IPC_MESSAGE_DEBUG_EXTRA
  DLOG(INFO) << "received message on channel @" << this << " with type "
             << message->type();
#endif

  if (message->routing_id() == MSG_ROUTING_NONE &&
      message->type() == HELLO_MESSAGE_TYPE) {
    // The hello message contains the process ID, as well as an optional
    // send_port if the channel was initialized with a receive port.
    if (peer_audit_token_) {
      CHROMIUM_LOG(ERROR) << "Unexpected duplicate HELLO message";
      return false;
    }
    peer_audit_token_.emplace(trailer->msgh_audit);

    // Read the hello message.
    IPC::MessageReader reader(*message);
    int32_t other_pid = -1;
    mozilla::UniqueMachSendRight send_port;
    if (!reader.ReadInt(&other_pid) ||
        !reader.ConsumeMachSendRight(&send_port)) {
      return false;
    }
    if (send_port_ && send_port) {
      CHROMIUM_LOG(ERROR) << "Unexpected send_port in HELLO message";
      return false;
    }
    if (!send_port_ && !send_port) {
      CHROMIUM_LOG(ERROR) << "Expected send_port in HELLO message";
      return false;
    }
#ifdef XP_MACOSX
    if (XRE_IsParentProcess() &&
        audit_token_to_pid(trailer->msgh_audit) != other_pid) {
      CHROMIUM_LOG(ERROR) << "audit token does not correspond to given pid";
      return false;
    }
#endif

    SetOtherPid(other_pid);
    if (!send_port_) {
      mozilla::MutexAutoLock lock(SendMutex());
      if (!ContinueConnect(std::move(send_port))) {
        CHROMIUM_LOG(ERROR) << "ContinueConnect failed";
        return false;
      }
    }

    listener_->OnChannelConnected(other_pid);
  } else {
    if (!peer_audit_token_) {
      CHROMIUM_LOG(ERROR) << "Unexpected message before HELLO message";
      return false;
    }

    mozilla::LogIPCMessage::Run run(message.get());
    listener_->OnMessageReceived(std::move(message));
  }
  return true;
}

template <class T>
static T* VmAllocateBuffer(size_t count) {
  size_t bytes = count * sizeof(T);
  vm_address_t address;
  kern_return_t kr =
      vm_allocate(mach_task_self(), &address, bytes,
                  VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | VM_FLAGS_ANYWHERE);
  if (kr != KERN_SUCCESS) {
    NS_ABORT_OOM(bytes);
  }
  return reinterpret_cast<T*>(address);
}

template <class UniquePort>
static void WritePorts(MsgBufferHelper& buf_helper, mach_msg_base_t* base,
                       mach_msg_type_name_t disposition,
                       nsTArray<UniquePort>& attachments, bool send_inline) {
  if (send_inline) {
    for (auto& port : attachments) {
      buf_helper.WriteDescriptor<mach_msg_port_descriptor_t>(
          base, {
                    .name = port.release(),
                    .disposition = disposition,
                    .type = MACH_MSG_PORT_DESCRIPTOR,
                });
    }
  } else if (!attachments.IsEmpty()) {
    mach_port_t* ports = VmAllocateBuffer<mach_port_t>(attachments.Length());
    for (size_t i = 0; i < attachments.Length(); ++i) {
      ports[i] = attachments[i].release();
    }
    buf_helper.WriteDescriptor<mach_msg_ool_ports_descriptor_t>(
        base, {
                  .address = ports,
                  .deallocate = true,
                  .copy = MACH_MSG_VIRTUAL_COPY,
                  .disposition = disposition,
                  .type = MACH_MSG_OOL_PORTS_DESCRIPTOR,
                  .count = (mach_msg_size_t)attachments.Length(),
              });
  }
}

bool ChannelMach::ProcessOutgoingMessages() {
  chan_cap_.NoteLockHeld();

  DCHECK(!waiting_connect_);  // Why are we trying to send messages if there's
                              // no connection?

  while (!output_queue_.IsEmpty()) {
    if (!send_port_) {
      return false;
    }

    Message* msg = output_queue_.FirstElement().get();

    if (!send_buffer_has_message_) {
      AddIPCProfilerMarker(*msg, other_pid_, MessageDirection::eSending,
                           MessagePhase::TransferStart);

      // Reserve |sizeof(mach_msg_audit_trailer_t)| bytes at the end of the
      // buffer, as the receiving side will need enough space for the trailer.
      mach_msg_size_t max_size =
          Channel::kReadBufferSize - sizeof(mach_msg_audit_trailer_t);
      MsgBufferHelper buf_helper(send_buffer_.get(), max_size);

      // Clear out the message header to an initial state so we can build it.
      auto* base = buf_helper.Next<mach_msg_base_t>();
      *base = {{
          .msgh_bits = MACH_MSGH_BITS(/* remote */ MACH_MSG_TYPE_COPY_SEND,
                                      /* local */ 0),
          .msgh_size = sizeof(mach_msg_base_t),
          .msgh_remote_port = send_port_.get(),
          .msgh_id = kIPDLMessageId,
      }};
      send_buffer_has_message_ = true;

      // Convert FDs to send rights using `fileport_makeport`.
      // The number of handles is recorded in the header so that they can be
      // split out on the other side.
      msg->header()->num_handles = msg->attached_handles_.Length();
      msg->attached_send_rights_.SetCapacity(
          msg->attached_send_rights_.Length() +
          msg->attached_handles_.Length());
      for (auto& fd : msg->attached_handles_) {
        mach_port_t fileport;
        kern_return_t kr = fileport_makeport(fd.get(), &fileport);
        if (kr != KERN_SUCCESS) {
          CHROMIUM_LOG(ERROR)
              << "fileport_makeport failed: " << mach_error_string(kr);
          return false;
        }
        msg->attached_send_rights_.AppendElement(
            mozilla::UniqueMachSendRight(fileport));
      }

      // Check if there's enough space in the buffer to fit a port descriptor
      // for every attached handle + a mach_msg_ool_descriptor_t for the
      // payload. If there isn't, send them out-of-line.
      size_t inline_descr_size = sizeof(mach_msg_port_descriptor_t) *
                                     (msg->attached_send_rights_.Length() +
                                      msg->attached_receive_rights_.Length()) +
                                 sizeof(mach_msg_ool_descriptor_t);
      bool send_inline = buf_helper.Remaining().size() > inline_descr_size;
      WritePorts(buf_helper, base, MACH_MSG_TYPE_MOVE_SEND,
                 msg->attached_send_rights_, send_inline);
      WritePorts(buf_helper, base, MACH_MSG_TYPE_MOVE_RECEIVE,
                 msg->attached_receive_rights_, send_inline);

      // Determine where to write the message payload. We'll write it inline if
      // there's space, otherwise it'll be sent out-of-line.
      char* payload = nullptr;
      if (buf_helper.Remaining().size() > msg->size()) {
        payload = buf_helper.WriteBytes(base, msg->size());
      } else {
        // NOTE: If `msg` holds the message in a single buffer, we could pass it
        // down without copying by passing a pointer in an ool descriptor with
        // `deallocate = false`.
        payload = VmAllocateBuffer<char>(round_msg(msg->size()));
        buf_helper.WriteDescriptor<mach_msg_ool_descriptor_t>(
            base, {
                      .address = payload,
                      .deallocate = true,
                      .copy = MACH_MSG_VIRTUAL_COPY,
                      .type = MACH_MSG_OOL_DESCRIPTOR,
                      .size = (mach_msg_size_t)round_msg(msg->size()),
                  });
      }

      // Write the full message payload into the payload buffer.
      auto iter = msg->Buffers().Iter();
      MOZ_ALWAYS_TRUE(msg->Buffers().ReadBytes(iter, payload, msg->size()));
    }

    MOZ_ASSERT(send_buffer_has_message_, "Failed to build a message?");
    auto* header = reinterpret_cast<mach_msg_header_t*>(send_buffer_.get());
    kern_return_t kr = mach_msg(header, MACH_SEND_MSG | MACH_SEND_TIMEOUT,
                                header->msgh_size, 0, MACH_PORT_NULL,
                                /* timeout */ 0, MACH_PORT_NULL);
    if (kr == KERN_SUCCESS) {
      // Don't clean up the message anymore.
      send_buffer_has_message_ = false;

      AddIPCProfilerMarker(*msg, other_pid_, MessageDirection::eSending,
                           MessagePhase::TransferEnd);

#ifdef IPC_MESSAGE_DEBUG_EXTRA
      DLOG(INFO) << "sent message @" << msg << " on channel @" << this
                 << " with type " << msg->type();
#endif

      OutputQueuePop();
    } else {
      if (kr == MACH_SEND_TIMED_OUT) {
        // The message timed out, set up a runnable to re-try the send on the
        // IPC I/O thread.
        //
        // NOTE: It'd be nice to use MACH_NOTIFY_SEND_POSSIBLE here, but using
        // it naively can lead to port leaks when the port becomes a DEAD_NAME
        // due to issues in the port subsystem.
        XRE_GetAsyncIOEventTarget()->Dispatch(NS_NewRunnableFunction(
            "ChannelMach::Retry", [self = RefPtr{this}]() {
              mozilla::MutexAutoLock lock(self->SendMutex());
              self->chan_cap_.NoteLockHeld();
              if (self->receive_port_) {
                self->ProcessOutgoingMessages();
              }
            }));
        return true;
      }

      if (kr != MACH_SEND_INVALID_DEST) {
        CHROMIUM_LOG(ERROR)
            << "mach_msg send failed: " << mach_error_string(kr);
      }
      return false;
    }
  }
  return true;
}

bool ChannelMach::Send(mozilla::UniquePtr<Message> message) {
  // NOTE: This method may be called on threads other than `IOThread()`.
  mozilla::MutexAutoLock lock(SendMutex());
  chan_cap_.NoteLockHeld();

#ifdef IPC_MESSAGE_DEBUG_EXTRA
  DLOG(INFO) << "sending message @" << message.get() << " on channel @" << this
             << " with type " << message->type() << " ("
             << output_queue_.Count() << " in queue)";
#endif

  // If the channel has been closed, ProcessOutgoingMessages() is never going
  // to pop anything off output_queue; output_queue will only get emptied when
  // the channel is destructed.  We might as well delete message now, instead
  // of waiting for the channel to be destructed.
  if (!receive_port_) {
    if (mozilla::ipc::LoggingEnabled()) {
      printf_stderr("Can't send message %s, because this channel is closed.\n",
                    message->name());
    }
    return false;
  }

  OutputQueuePush(std::move(message));
  if (!waiting_connect_ && !send_buffer_has_message_) {
    return ProcessOutgoingMessages();
  }

  return true;
}

void ChannelMach::OnMachMessageReceived(mach_port_t port) {
  IOThread().AssertOnCurrentThread();
  chan_cap_.NoteOnTarget();

  if (receive_port_) {
    if (!ProcessIncomingMessage()) {
      Close();
      listener_->OnChannelError();
      // The OnChannelError() call may delete this, so we need to exit now.
      return;
    }
  }
}

void ChannelMach::OutputQueuePush(mozilla::UniquePtr<Message> msg) {
  chan_cap_.NoteLockHeld();

  mozilla::LogIPCMessage::LogDispatchWithPid(msg.get(), other_pid_);

  MOZ_DIAGNOSTIC_ASSERT(receive_port_);
  msg->AssertAsLargeAsHeader();
  output_queue_.Push(std::move(msg));
}

void ChannelMach::OutputQueuePop() {
  if (send_buffer_has_message_) {
    mach_msg_destroy(reinterpret_cast<mach_msg_header_t*>(send_buffer_.get()));
    send_buffer_has_message_ = false;
  }

  mozilla::UniquePtr<Message> message = output_queue_.Pop();
}

void ChannelMach::Close() {
  IOThread().AssertOnCurrentThread();
  mozilla::MutexAutoLock lock(SendMutex());
  CloseLocked();
}

void ChannelMach::CloseLocked() {
  chan_cap_.NoteExclusiveAccess();

  // Close can be called multiple times, so we need to make sure we're
  // idempotent.

  watch_controller_.StopWatchingMachPort();
  receive_port_ = nullptr;
  send_port_ = nullptr;

  while (!output_queue_.IsEmpty()) {
    OutputQueuePop();
  }
}

// static
bool ChannelMach::CreateRawPipe(ChannelHandle* server, ChannelHandle* client) {
  return CreateRawPipe(&server->emplace<mozilla::UniqueMachReceiveRight>(),
                       &client->emplace<mozilla::UniqueMachSendRight>());
}

// static
bool ChannelMach::CreateRawPipe(mozilla::UniqueMachReceiveRight* server,
                                mozilla::UniqueMachSendRight* client) {
  mach_port_options_t options{};
  options.flags = MPO_INSERT_SEND_RIGHT | MPO_QLIMIT;
  options.mpl.mpl_qlimit = MACH_PORT_QLIMIT_LARGE;

  mach_port_t port = MACH_PORT_NULL;
  kern_return_t kr = mach_port_construct(mach_task_self(), &options, 0, &port);
  if (kr != KERN_SUCCESS) {
    CHROMIUM_LOG(ERROR) << "mach_port_construct failed: "
                        << mach_error_string(kr);
    return false;
  }

  // Within a single task, all references to the same port have the same name.
  // Thanks to `MPO_INSERT_SEND_RIGHT`, both a send and receive right were
  // inserted for this name.
  server->reset(port);
  client->reset(port);
  return true;
}

// static
uint32_t ChannelMach::NumRelayedAttachments(const Message& message) {
  return 0;
}

// static
bool ChannelMach::IsValidHandle(const ChannelHandle& handle) {
  if (const auto* server =
          std::get_if<mozilla::UniqueMachReceiveRight>(&handle)) {
    return !!*server;
  }
  if (const auto* client =
          std::get_if<mozilla::UniqueMachReceiveRight>(&handle)) {
    return !!*client;
  }
  return false;
}

}  // namespace IPC

Messung V0.5 in Prozent
C=88 H=99 G=93

¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.