Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(639)

Unified Diff: third_party/crashpad/crashpad/util/mach/mach_message_server.cc

Issue 1505213004: Copy Crashpad into the Chrome tree instead of importing it via DEPS (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address review comments, update README.chromium Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/crashpad/crashpad/util/mach/mach_message_server.cc
diff --git a/third_party/crashpad/crashpad/util/mach/mach_message_server.cc b/third_party/crashpad/crashpad/util/mach/mach_message_server.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0af190184309fe7eca814ddc4fbc34adbfb4c774
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/mach/mach_message_server.cc
@@ -0,0 +1,280 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/mach_message_server.h"
+
+#include <string.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_vm.h"
+#include "util/mach/mach_message.h"
+
+namespace crashpad {
+
+namespace {
+
+//! \brief Manages a dynamically-allocated buffer to be used for Mach messaging.
+class MachMessageBuffer {
+ public:
+ MachMessageBuffer() : vm_() {}
+
+ ~MachMessageBuffer() {}
+
+ //! \return A pointer to the buffer.
+ mach_msg_header_t* Header() const {
+ return reinterpret_cast<mach_msg_header_t*>(vm_.address());
+ }
+
+ //! \brief Ensures that this object has a buffer of exactly \a size bytes
+ //! available.
+ //!
+ //! If the existing buffer is a different size, it will be reallocated without
+ //! copying any of the old buffer’s contents to the new buffer. The contents
+ //! of the buffer are unspecified after this call, even if no reallocation is
+ //! performed.
+ kern_return_t Reallocate(vm_size_t size) {
+ // This test uses == instead of > so that a large reallocation to receive a
+ // large message doesn’t cause permanent memory bloat for the duration of
+ // a MachMessageServer::Run() loop.
+ if (size != vm_.size()) {
+ // reset() first, so that two allocations don’t exist simultaneously.
+ vm_.reset();
+
+ if (size) {
+ vm_address_t address;
+ kern_return_t kr =
+ vm_allocate(mach_task_self(),
+ &address,
+ size,
+ VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG));
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ vm_.reset(address, size);
+ }
+ }
+
+#if !defined(NDEBUG)
+ // Regardless of whether the allocation was changed, scribble over the
+ // memory to make sure that nothing relies on zero-initialization or stale
+ // contents.
+ memset(Header(), 0x66, size);
+#endif
+
+ return KERN_SUCCESS;
+ }
+
+ private:
+ base::mac::ScopedMachVM vm_;
+
+ DISALLOW_COPY_AND_ASSIGN(MachMessageBuffer);
+};
+
+// Wraps MachMessageWithDeadline(), using a MachMessageBuffer argument which
+// will be resized to |receive_size| (after being page-rounded). MACH_RCV_MSG
+// is always combined into |options|.
+mach_msg_return_t MachMessageAllocateReceive(MachMessageBuffer* request,
+ mach_msg_option_t options,
+ mach_msg_size_t receive_size,
+ mach_port_name_t receive_port,
+ MachMessageDeadline deadline,
+ mach_port_name_t notify_port,
+ bool run_even_if_expired) {
+ mach_msg_size_t request_alloc = round_page(receive_size);
+ kern_return_t kr = request->Reallocate(request_alloc);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ return MachMessageWithDeadline(request->Header(),
+ options | MACH_RCV_MSG,
+ receive_size,
+ receive_port,
+ deadline,
+ notify_port,
+ run_even_if_expired);
+}
+
+} // namespace
+
+// This method implements a server similar to 10.9.4
+// xnu-2422.110.17/libsyscall/mach/mach_msg.c mach_msg_server_once(). The server
+// callback function and |max_size| parameter have been replaced with a C++
+// interface. The |persistent| parameter has been added, allowing this method to
+// serve as a stand-in for mach_msg_server(). The |timeout_ms| parameter has
+// been added, allowing this function to not block indefinitely.
+//
+// static
+mach_msg_return_t MachMessageServer::Run(Interface* interface,
+ mach_port_t receive_port,
+ mach_msg_options_t options,
+ Persistent persistent,
+ ReceiveLarge receive_large,
+ mach_msg_timeout_t timeout_ms) {
+ options &= ~(MACH_RCV_MSG | MACH_SEND_MSG);
+
+ const MachMessageDeadline deadline =
+ MachMessageDeadlineFromTimeout(timeout_ms);
+
+ if (receive_large == kReceiveLargeResize) {
+ options |= MACH_RCV_LARGE;
+ } else {
+ options &= ~MACH_RCV_LARGE;
+ }
+
+ const mach_msg_size_t trailer_alloc = REQUESTED_TRAILER_SIZE(options);
+ const mach_msg_size_t expected_receive_size =
+ round_msg(interface->MachMessageServerRequestSize()) + trailer_alloc;
+ const mach_msg_size_t request_size = (receive_large == kReceiveLargeResize)
+ ? round_page(expected_receive_size)
+ : expected_receive_size;
+ DCHECK_GE(request_size, sizeof(mach_msg_empty_rcv_t));
+
+ // mach_msg_server() and mach_msg_server_once() would consider whether
+ // |options| contains MACH_SEND_TRAILER and include MAX_TRAILER_SIZE in this
+ // computation if it does, but that option is ineffective on OS X.
+ const mach_msg_size_t reply_size = interface->MachMessageServerReplySize();
+ DCHECK_GE(reply_size, sizeof(mach_msg_empty_send_t));
+ const mach_msg_size_t reply_alloc = round_page(reply_size);
+
+ MachMessageBuffer request;
+ MachMessageBuffer reply;
+ bool received_any_request = false;
+ bool retry;
+
+ kern_return_t kr;
+
+ do {
+ retry = false;
+
+ kr = MachMessageAllocateReceive(&request,
+ options,
+ request_size,
+ receive_port,
+ deadline,
+ MACH_PORT_NULL,
+ !received_any_request);
+ if (kr == MACH_RCV_TOO_LARGE) {
+ switch (receive_large) {
+ case kReceiveLargeError:
+ break;
+
+ case kReceiveLargeIgnore:
+ // Try again, even in one-shot mode. The caller is expecting this
+ // method to take action on the first message in the queue, and has
+ // indicated that they want large messages to be ignored. The
+ // alternatives, which might involve returning MACH_MSG_SUCCESS,
+ // MACH_RCV_TIMED_OUT, or MACH_RCV_TOO_LARGE to a caller that
+ // specified one-shot behavior, all seem less correct than retrying.
+ MACH_LOG(WARNING, kr) << "mach_msg: ignoring large message";
+ retry = true;
+ continue;
+
+ case kReceiveLargeResize: {
+ mach_msg_size_t this_request_size = round_page(
+ round_msg(request.Header()->msgh_size) + trailer_alloc);
+ DCHECK_GT(this_request_size, request_size);
+
+ kr = MachMessageAllocateReceive(&request,
+ options & ~MACH_RCV_LARGE,
+ this_request_size,
+ receive_port,
+ deadline,
+ MACH_PORT_NULL,
+ !received_any_request);
+
+ break;
+ }
+ }
+ }
+
+ if (kr != MACH_MSG_SUCCESS) {
+ return kr;
+ }
+
+ received_any_request = true;
+
+ kr = reply.Reallocate(reply_alloc);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ mach_msg_header_t* request_header = request.Header();
+ mach_msg_header_t* reply_header = reply.Header();
+ bool destroy_complex_request = false;
+ interface->MachMessageServerFunction(
+ request_header, reply_header, &destroy_complex_request);
+
+ if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
+ // This only works if the reply message is not complex, because otherwise,
+ // the location of the RetCode field is not known. It should be possible
+ // to locate the RetCode field by looking beyond the descriptors in a
+ // complex reply message, but this is not currently done. This behavior
+ // has not proven itself necessary in practice, and it’s not done by
+ // mach_msg_server() or mach_msg_server_once() either.
+ mig_reply_error_t* reply_mig =
+ reinterpret_cast<mig_reply_error_t*>(reply_header);
+ if (reply_mig->RetCode == MIG_NO_REPLY) {
+ reply_header->msgh_remote_port = MACH_PORT_NULL;
+ } else if (reply_mig->RetCode != KERN_SUCCESS &&
+ request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
+ destroy_complex_request = true;
+ }
+ }
+
+ if (destroy_complex_request &&
+ request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
+ request_header->msgh_remote_port = MACH_PORT_NULL;
+ mach_msg_destroy(request_header);
+ }
+
+ if (reply_header->msgh_remote_port != MACH_PORT_NULL) {
+ // Avoid blocking indefinitely. This duplicates the logic in 10.9.5
+ // xnu-2422.115.4/libsyscall/mach/mach_msg.c mach_msg_server_once(),
+ // although the special provision for sending to a send-once right is not
+ // made, because kernel keeps sends to a send-once right on the fast path
+ // without considering the user-specified timeout. See 10.9.5
+ // xnu-2422.115.4/osfmk/ipc/ipc_mqueue.c ipc_mqueue_send().
+ const MachMessageDeadline send_deadline =
+ deadline == kMachMessageDeadlineWaitIndefinitely
+ ? kMachMessageDeadlineNonblocking
+ : deadline;
+
+ kr = MachMessageWithDeadline(reply_header,
+ options | MACH_SEND_MSG,
+ 0,
+ MACH_PORT_NULL,
+ send_deadline,
+ MACH_PORT_NULL,
+ true);
+
+ if (kr != MACH_MSG_SUCCESS) {
+ if (kr == MACH_SEND_INVALID_DEST ||
+ kr == MACH_SEND_TIMED_OUT ||
+ kr == MACH_SEND_INTERRUPTED) {
+ mach_msg_destroy(reply_header);
+ }
+ return kr;
+ }
+ }
+ } while (persistent == kPersistent || retry);
+
+ return kr;
+}
+
+} // namespace crashpad

Powered by Google App Engine
This is Rietveld 408576698