2014-11-25 14:56:05 -05:00
|
|
|
|
// 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/child_port_handshake.h"
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
#include <sys/event.h>
|
2014-12-03 13:42:06 -05:00
|
|
|
|
#include <sys/socket.h>
|
2014-11-25 14:56:05 -05:00
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
#include "base/logging.h"
|
|
|
|
|
#include "base/mac/mach_logging.h"
|
|
|
|
|
#include "base/mac/scoped_mach_port.h"
|
|
|
|
|
#include "base/posix/eintr_wrapper.h"
|
|
|
|
|
#include "base/rand_util.h"
|
|
|
|
|
#include "base/strings/stringprintf.h"
|
2014-12-17 14:35:18 -08:00
|
|
|
|
#include "util/file/file_io.h"
|
2014-11-25 14:56:05 -05:00
|
|
|
|
#include "util/mach/child_port.h"
|
2015-10-29 14:14:15 -04:00
|
|
|
|
#include "util/mach/child_port_server.h"
|
2014-11-25 14:56:05 -05:00
|
|
|
|
#include "util/mach/mach_extensions.h"
|
2014-12-10 11:11:21 -05:00
|
|
|
|
#include "util/mach/mach_message.h"
|
2014-11-25 14:56:05 -05:00
|
|
|
|
#include "util/mach/mach_message_server.h"
|
2015-09-14 14:51:05 -07:00
|
|
|
|
#include "util/misc/implicit_cast.h"
|
2014-11-25 14:56:05 -05:00
|
|
|
|
|
|
|
|
|
namespace crashpad {
|
2015-10-29 14:14:15 -04:00
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class ChildPortHandshakeServer final : public ChildPortServer::Interface {
|
|
|
|
|
public:
|
|
|
|
|
ChildPortHandshakeServer();
|
|
|
|
|
~ChildPortHandshakeServer();
|
|
|
|
|
|
|
|
|
|
mach_port_t RunServer(base::ScopedFD server_write_fd,
|
|
|
|
|
ChildPortHandshake::PortRightType port_right_type);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// ChildPortServer::Interface:
|
|
|
|
|
kern_return_t HandleChildPortCheckIn(child_port_server_t server,
|
|
|
|
|
child_port_token_t token,
|
|
|
|
|
mach_port_t port,
|
|
|
|
|
mach_msg_type_name_t right_type,
|
|
|
|
|
const mach_msg_trailer_t* trailer,
|
|
|
|
|
bool* destroy_request) override;
|
|
|
|
|
|
|
|
|
|
child_port_token_t token_;
|
|
|
|
|
mach_port_t port_;
|
|
|
|
|
mach_msg_type_name_t right_type_;
|
|
|
|
|
bool checked_in_;
|
|
|
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ChildPortHandshakeServer);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ChildPortHandshakeServer::ChildPortHandshakeServer()
|
2014-11-25 14:56:05 -05:00
|
|
|
|
: token_(0),
|
2015-10-29 14:14:15 -04:00
|
|
|
|
port_(MACH_PORT_NULL),
|
|
|
|
|
right_type_(MACH_MSG_TYPE_PORT_NONE),
|
2014-11-25 14:56:05 -05:00
|
|
|
|
checked_in_(false) {
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-29 14:14:15 -04:00
|
|
|
|
ChildPortHandshakeServer::~ChildPortHandshakeServer() {
|
2014-11-25 14:56:05 -05:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-29 14:14:15 -04:00
|
|
|
|
mach_port_t ChildPortHandshakeServer::RunServer(
|
|
|
|
|
base::ScopedFD server_write_fd,
|
|
|
|
|
ChildPortHandshake::PortRightType port_right_type) {
|
|
|
|
|
DCHECK_EQ(port_, kMachPortNull);
|
|
|
|
|
DCHECK(!checked_in_);
|
|
|
|
|
DCHECK(server_write_fd.is_valid());
|
2014-11-25 14:56:05 -05:00
|
|
|
|
|
|
|
|
|
// Initialize the token and share it with the client via the pipe.
|
|
|
|
|
token_ = base::RandUint64();
|
2015-10-29 14:14:15 -04:00
|
|
|
|
if (!LoggingWriteFile(server_write_fd.get(), &token_, sizeof(token_))) {
|
2014-12-03 13:42:06 -05:00
|
|
|
|
LOG(WARNING) << "no client check-in";
|
2014-11-25 14:56:05 -05:00
|
|
|
|
return MACH_PORT_NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a unique name for the bootstrap service mapping. Make it unguessable
|
|
|
|
|
// to prevent outsiders from grabbing the name first, which would cause
|
|
|
|
|
// bootstrap_check_in() to fail.
|
|
|
|
|
uint64_t thread_id;
|
|
|
|
|
errno = pthread_threadid_np(pthread_self(), &thread_id);
|
|
|
|
|
PCHECK(errno == 0) << "pthread_threadid_np";
|
|
|
|
|
std::string service_name = base::StringPrintf(
|
2015-10-29 18:31:20 -04:00
|
|
|
|
"org.chromium.crashpad.child_port_handshake.%d.%llu.%016llx",
|
2014-11-25 14:56:05 -05:00
|
|
|
|
getpid(),
|
|
|
|
|
thread_id,
|
|
|
|
|
base::RandUint64());
|
|
|
|
|
|
|
|
|
|
// Check the new service in with the bootstrap server, obtaining a receive
|
|
|
|
|
// right for it.
|
2015-10-05 17:07:15 -04:00
|
|
|
|
base::mac::ScopedMachReceiveRight server_port(BootstrapCheckIn(service_name));
|
2015-10-20 11:03:25 -04:00
|
|
|
|
CHECK(server_port.is_valid());
|
2014-11-25 14:56:05 -05:00
|
|
|
|
|
|
|
|
|
// Share the service name with the client via the pipe.
|
|
|
|
|
uint32_t service_name_length = service_name.size();
|
2015-10-29 14:14:15 -04:00
|
|
|
|
if (!LoggingWriteFile(server_write_fd.get(),
|
|
|
|
|
&service_name_length,
|
|
|
|
|
sizeof(service_name_length))) {
|
2014-12-03 13:42:06 -05:00
|
|
|
|
LOG(WARNING) << "no client check-in";
|
2014-11-25 14:56:05 -05:00
|
|
|
|
return MACH_PORT_NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-17 14:35:18 -08:00
|
|
|
|
if (!LoggingWriteFile(
|
2015-10-29 14:14:15 -04:00
|
|
|
|
server_write_fd.get(), service_name.c_str(), service_name_length)) {
|
2014-12-03 13:42:06 -05:00
|
|
|
|
LOG(WARNING) << "no client check-in";
|
2014-11-25 14:56:05 -05:00
|
|
|
|
return MACH_PORT_NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A kqueue cannot monitor a raw Mach receive right with EVFILT_MACHPORT. It
|
|
|
|
|
// requires a port set. Create a new port set and add the receive right to it.
|
2014-12-17 15:10:38 -05:00
|
|
|
|
base::mac::ScopedMachPortSet server_port_set(
|
|
|
|
|
NewMachPort(MACH_PORT_RIGHT_PORT_SET));
|
2015-10-20 11:03:25 -04:00
|
|
|
|
CHECK(server_port_set.is_valid());
|
2014-11-25 14:56:05 -05:00
|
|
|
|
|
2015-10-20 11:03:25 -04:00
|
|
|
|
kern_return_t kr = mach_port_insert_member(
|
|
|
|
|
mach_task_self(), server_port.get(), server_port_set.get());
|
2014-11-25 14:56:05 -05:00
|
|
|
|
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member";
|
|
|
|
|
|
|
|
|
|
// Set up a kqueue to monitor both the server’s receive right and the write
|
|
|
|
|
// side of the pipe. Messages from the client will be received via the receive
|
|
|
|
|
// right, and the pipe will show EOF if the client closes its read side
|
|
|
|
|
// prematurely.
|
|
|
|
|
base::ScopedFD kq(kqueue());
|
|
|
|
|
PCHECK(kq != -1) << "kqueue";
|
|
|
|
|
|
|
|
|
|
struct kevent changelist[2];
|
|
|
|
|
EV_SET(&changelist[0],
|
2015-10-20 11:03:25 -04:00
|
|
|
|
server_port_set.get(),
|
2014-11-25 14:56:05 -05:00
|
|
|
|
EVFILT_MACHPORT,
|
|
|
|
|
EV_ADD | EV_CLEAR,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
EV_SET(&changelist[1],
|
2015-10-29 14:14:15 -04:00
|
|
|
|
server_write_fd.get(),
|
2014-11-25 14:56:05 -05:00
|
|
|
|
EVFILT_WRITE,
|
|
|
|
|
EV_ADD | EV_CLEAR,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
int rv = HANDLE_EINTR(
|
|
|
|
|
kevent(kq.get(), changelist, arraysize(changelist), nullptr, 0, nullptr));
|
|
|
|
|
PCHECK(rv != -1) << "kevent";
|
|
|
|
|
|
|
|
|
|
ChildPortServer child_port_server(this);
|
|
|
|
|
|
|
|
|
|
bool blocking = true;
|
|
|
|
|
DCHECK(!checked_in_);
|
|
|
|
|
while (!checked_in_) {
|
2015-10-29 14:14:15 -04:00
|
|
|
|
DCHECK_EQ(port_, kMachPortNull);
|
2014-11-25 14:56:05 -05:00
|
|
|
|
|
|
|
|
|
// Get a kevent from the kqueue. Block while waiting for an event unless the
|
|
|
|
|
// write pipe has arrived at EOF, in which case the kevent() should be
|
|
|
|
|
// nonblocking. Although the client sends its check-in message before
|
|
|
|
|
// closing the read side of the pipe, this organization allows the events to
|
|
|
|
|
// be delivered out of order and the check-in message will still be
|
|
|
|
|
// processed.
|
|
|
|
|
struct kevent event;
|
|
|
|
|
const timespec nonblocking_timeout = {};
|
|
|
|
|
const timespec* timeout = blocking ? nullptr : &nonblocking_timeout;
|
|
|
|
|
rv = HANDLE_EINTR(kevent(kq.get(), nullptr, 0, &event, 1, timeout));
|
|
|
|
|
PCHECK(rv != -1) << "kevent";
|
|
|
|
|
|
|
|
|
|
if (rv == 0) {
|
|
|
|
|
// Non-blocking kevent() with no events to return.
|
|
|
|
|
DCHECK(!blocking);
|
|
|
|
|
LOG(WARNING) << "no client check-in";
|
|
|
|
|
return MACH_PORT_NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DCHECK_EQ(rv, 1);
|
|
|
|
|
|
|
|
|
|
if (event.flags & EV_ERROR) {
|
|
|
|
|
// kevent() may have put its error here.
|
|
|
|
|
errno = event.data;
|
|
|
|
|
PLOG(FATAL) << "kevent";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (event.filter) {
|
|
|
|
|
case EVFILT_MACHPORT: {
|
|
|
|
|
// There’s something to receive on the port set.
|
2015-10-20 11:03:25 -04:00
|
|
|
|
DCHECK_EQ(event.ident, server_port_set.get());
|
2014-11-25 14:56:05 -05:00
|
|
|
|
|
|
|
|
|
// Run the message server in an inner loop instead of using
|
|
|
|
|
// MachMessageServer::kPersistent. This allows the loop to exit as soon
|
|
|
|
|
// as child_port_ is set, even if other messages are queued. This needs
|
|
|
|
|
// to drain all messages, because the use of edge triggering (EV_CLEAR)
|
|
|
|
|
// means that if more than one message is in the queue when kevent()
|
|
|
|
|
// returns, no more notifications will be generated.
|
|
|
|
|
while (!checked_in_) {
|
|
|
|
|
// If a proper message is received from child_port_check_in(),
|
|
|
|
|
// this will call HandleChildPortCheckIn().
|
|
|
|
|
mach_msg_return_t mr =
|
|
|
|
|
MachMessageServer::Run(&child_port_server,
|
2015-10-20 11:03:25 -04:00
|
|
|
|
server_port_set.get(),
|
2014-11-25 14:56:05 -05:00
|
|
|
|
MACH_MSG_OPTION_NONE,
|
|
|
|
|
MachMessageServer::kOneShot,
|
|
|
|
|
MachMessageServer::kReceiveLargeIgnore,
|
2014-12-10 11:11:21 -05:00
|
|
|
|
kMachMessageTimeoutNonblocking);
|
2014-11-25 14:56:05 -05:00
|
|
|
|
if (mr == MACH_RCV_TIMED_OUT) {
|
|
|
|
|
break;
|
|
|
|
|
} else if (mr != MACH_MSG_SUCCESS) {
|
|
|
|
|
MACH_LOG(ERROR, mr) << "MachMessageServer::Run";
|
|
|
|
|
return MACH_PORT_NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case EVFILT_WRITE:
|
|
|
|
|
// The write pipe is ready to be written to, or it’s at EOF. The former
|
|
|
|
|
// case is uninteresting, but a notification for this may be presented
|
|
|
|
|
// because the write pipe will be ready to be written to, at the latest,
|
|
|
|
|
// when the client reads its messages from the read side of the same
|
|
|
|
|
// pipe. Ignore that case. Multiple notifications for that situation
|
|
|
|
|
// will not be generated because edge triggering (EV_CLEAR) is used
|
|
|
|
|
// above.
|
2015-10-29 14:14:15 -04:00
|
|
|
|
DCHECK_EQ(implicit_cast<int>(event.ident), server_write_fd.get());
|
2014-11-25 14:56:05 -05:00
|
|
|
|
if (event.flags & EV_EOF) {
|
|
|
|
|
// There are no readers attached to the write pipe. The client has
|
|
|
|
|
// closed its side of the pipe. There can be one last shot at
|
|
|
|
|
// receiving messages, in case the check-in message is delivered
|
|
|
|
|
// out of order, after the EOF notification.
|
|
|
|
|
blocking = false;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
NOTREACHED();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-29 14:14:15 -04:00
|
|
|
|
if (port_ == MACH_PORT_NULL) {
|
|
|
|
|
return MACH_PORT_NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool mismatch = false;
|
|
|
|
|
switch (port_right_type) {
|
|
|
|
|
case ChildPortHandshake::PortRightType::kReceiveRight:
|
|
|
|
|
if (right_type_ != MACH_MSG_TYPE_PORT_RECEIVE) {
|
|
|
|
|
LOG(ERROR) << "expected receive right, observed " << right_type_;
|
|
|
|
|
mismatch = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case ChildPortHandshake::PortRightType::kSendRight:
|
|
|
|
|
if (right_type_ != MACH_MSG_TYPE_PORT_SEND &&
|
|
|
|
|
right_type_ != MACH_MSG_TYPE_PORT_SEND_ONCE) {
|
|
|
|
|
LOG(ERROR) << "expected send or send-once right, observed "
|
|
|
|
|
<< right_type_;
|
|
|
|
|
mismatch = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mismatch) {
|
|
|
|
|
MachMessageDestroyReceivedPort(port_, right_type_);
|
|
|
|
|
port_ = MACH_PORT_NULL;
|
|
|
|
|
return MACH_PORT_NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mach_port_t port = MACH_PORT_NULL;
|
|
|
|
|
std::swap(port_, port);
|
|
|
|
|
return port;
|
2014-11-25 14:56:05 -05:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-29 14:14:15 -04:00
|
|
|
|
kern_return_t ChildPortHandshakeServer::HandleChildPortCheckIn(
|
2014-11-25 14:56:05 -05:00
|
|
|
|
child_port_server_t server,
|
|
|
|
|
const child_port_token_t token,
|
|
|
|
|
mach_port_t port,
|
|
|
|
|
mach_msg_type_name_t right_type,
|
2014-12-01 16:06:56 -05:00
|
|
|
|
const mach_msg_trailer_t* trailer,
|
2014-12-15 14:47:47 -05:00
|
|
|
|
bool* destroy_request) {
|
2015-10-29 14:14:15 -04:00
|
|
|
|
DCHECK_EQ(port_, kMachPortNull);
|
|
|
|
|
DCHECK(!checked_in_);
|
2014-11-25 14:56:05 -05:00
|
|
|
|
|
|
|
|
|
if (token != token_) {
|
|
|
|
|
// If the token’s not correct, someone’s attempting to spoof the legitimate
|
|
|
|
|
// client.
|
|
|
|
|
LOG(WARNING) << "ignoring incorrect token";
|
2014-12-15 14:47:47 -05:00
|
|
|
|
*destroy_request = true;
|
2014-11-25 14:56:05 -05:00
|
|
|
|
} else {
|
|
|
|
|
checked_in_ = true;
|
|
|
|
|
|
2015-10-29 14:14:15 -04:00
|
|
|
|
if (right_type != MACH_MSG_TYPE_PORT_RECEIVE &&
|
|
|
|
|
right_type != MACH_MSG_TYPE_PORT_SEND &&
|
|
|
|
|
right_type != MACH_MSG_TYPE_PORT_SEND_ONCE) {
|
|
|
|
|
// The message needs to carry a receive, send, or send-once right.
|
|
|
|
|
LOG(ERROR) << "invalid right type " << right_type;
|
2014-12-15 14:47:47 -05:00
|
|
|
|
*destroy_request = true;
|
2014-11-25 14:56:05 -05:00
|
|
|
|
} else {
|
2015-10-29 14:14:15 -04:00
|
|
|
|
// Communicate the child port and right type back to the RunServer().
|
2014-12-15 14:47:47 -05:00
|
|
|
|
// *destroy_request is left at false, because RunServer() needs the right
|
|
|
|
|
// to remain intact. It gives ownership of the right to its caller.
|
2015-10-29 14:14:15 -04:00
|
|
|
|
port_ = port;
|
|
|
|
|
right_type_ = right_type;
|
2014-11-25 14:56:05 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is a MIG simpleroutine, there is no reply message.
|
|
|
|
|
return MIG_NO_REPLY;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-29 14:14:15 -04:00
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
ChildPortHandshake::ChildPortHandshake()
|
|
|
|
|
: client_read_fd_(),
|
|
|
|
|
server_write_fd_() {
|
|
|
|
|
// Use socketpair() instead of pipe(). There is no way to suppress SIGPIPE on
|
|
|
|
|
// pipes in Mac OS X 10.6, because the F_SETNOSIGPIPE fcntl() command was not
|
|
|
|
|
// introduced until 10.7.
|
|
|
|
|
int pipe_fds[2];
|
|
|
|
|
PCHECK(socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fds) == 0)
|
|
|
|
|
<< "socketpair";
|
|
|
|
|
|
|
|
|
|
client_read_fd_.reset(pipe_fds[0]);
|
|
|
|
|
server_write_fd_.reset(pipe_fds[1]);
|
|
|
|
|
|
|
|
|
|
// Simulate pipe() semantics by shutting down the “wrong” sides of the socket.
|
|
|
|
|
PCHECK(shutdown(server_write_fd_.get(), SHUT_RD) == 0) << "shutdown SHUT_RD";
|
|
|
|
|
PCHECK(shutdown(client_read_fd_.get(), SHUT_WR) == 0) << "shutdown SHUT_WR";
|
|
|
|
|
|
|
|
|
|
// SIGPIPE is undesirable when writing to this pipe. Allow broken-pipe writes
|
|
|
|
|
// to fail with EPIPE instead.
|
|
|
|
|
const int value = 1;
|
|
|
|
|
PCHECK(setsockopt(server_write_fd_.get(),
|
|
|
|
|
SOL_SOCKET,
|
|
|
|
|
SO_NOSIGPIPE,
|
|
|
|
|
&value,
|
|
|
|
|
sizeof(value)) == 0) << "setsockopt";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ChildPortHandshake::~ChildPortHandshake() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
base::ScopedFD ChildPortHandshake::ClientReadFD() {
|
|
|
|
|
DCHECK(client_read_fd_.is_valid());
|
|
|
|
|
return client_read_fd_.Pass();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
base::ScopedFD ChildPortHandshake::ServerWriteFD() {
|
|
|
|
|
DCHECK(server_write_fd_.is_valid());
|
|
|
|
|
return server_write_fd_.Pass();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mach_port_t ChildPortHandshake::RunServer(PortRightType port_right_type) {
|
|
|
|
|
client_read_fd_.reset();
|
|
|
|
|
return RunServerForFD(server_write_fd_.Pass(), port_right_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ChildPortHandshake::RunClient(mach_port_t port,
|
2014-11-25 14:56:05 -05:00
|
|
|
|
mach_msg_type_name_t right_type) {
|
2015-10-29 14:14:15 -04:00
|
|
|
|
server_write_fd_.reset();
|
|
|
|
|
return RunClientForFD(client_read_fd_.Pass(), port, right_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
mach_port_t ChildPortHandshake::RunServerForFD(base::ScopedFD server_write_fd,
|
|
|
|
|
PortRightType port_right_type) {
|
|
|
|
|
ChildPortHandshakeServer server;
|
|
|
|
|
return server.RunServer(server_write_fd.Pass(), port_right_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
bool ChildPortHandshake::RunClientForFD(base::ScopedFD client_read_fd,
|
|
|
|
|
mach_port_t port,
|
|
|
|
|
mach_msg_type_name_t right_type) {
|
|
|
|
|
DCHECK(client_read_fd.is_valid());
|
2014-11-25 14:56:05 -05:00
|
|
|
|
|
|
|
|
|
// Read the token and the service name from the read side of the pipe.
|
|
|
|
|
child_port_token_t token;
|
|
|
|
|
std::string service_name;
|
2015-10-29 14:14:15 -04:00
|
|
|
|
if (!RunClientInternal_ReadPipe(
|
|
|
|
|
client_read_fd.get(), &token, &service_name)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-11-25 14:56:05 -05:00
|
|
|
|
|
|
|
|
|
// Look up the server and check in with it by providing the token and port.
|
2015-10-29 14:14:15 -04:00
|
|
|
|
return RunClientInternal_SendCheckIn(service_name, token, port, right_type);
|
2014-11-25 14:56:05 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
2015-10-29 14:14:15 -04:00
|
|
|
|
bool ChildPortHandshake::RunClientInternal_ReadPipe(int client_read_fd,
|
2014-11-25 14:56:05 -05:00
|
|
|
|
child_port_token_t* token,
|
|
|
|
|
std::string* service_name) {
|
|
|
|
|
// Read the token from the pipe.
|
2015-10-29 14:14:15 -04:00
|
|
|
|
if (!LoggingReadFile(client_read_fd, token, sizeof(*token))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-11-25 14:56:05 -05:00
|
|
|
|
|
|
|
|
|
// Read the service name from the pipe.
|
|
|
|
|
uint32_t service_name_length;
|
2015-10-29 14:14:15 -04:00
|
|
|
|
if (!LoggingReadFile(
|
|
|
|
|
client_read_fd, &service_name_length, sizeof(service_name_length))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-11-25 14:56:05 -05:00
|
|
|
|
|
2015-08-20 11:50:19 -04:00
|
|
|
|
service_name->resize(service_name_length);
|
2015-10-29 14:14:15 -04:00
|
|
|
|
if (!service_name->empty() &&
|
|
|
|
|
!LoggingReadFile(
|
|
|
|
|
client_read_fd, &(*service_name)[0], service_name_length)) {
|
|
|
|
|
return false;
|
2014-11-25 14:56:05 -05:00
|
|
|
|
}
|
2015-10-29 14:14:15 -04:00
|
|
|
|
|
|
|
|
|
return true;
|
2014-11-25 14:56:05 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
2015-10-29 14:14:15 -04:00
|
|
|
|
bool ChildPortHandshake::RunClientInternal_SendCheckIn(
|
2014-11-25 14:56:05 -05:00
|
|
|
|
const std::string& service_name,
|
|
|
|
|
child_port_token_t token,
|
|
|
|
|
mach_port_t port,
|
|
|
|
|
mach_msg_type_name_t right_type) {
|
|
|
|
|
// Get a send right to the server by looking up the service with the bootstrap
|
|
|
|
|
// server by name.
|
2015-10-05 17:07:15 -04:00
|
|
|
|
base::mac::ScopedMachSendRight server_port(BootstrapLookUp(service_name));
|
2015-10-29 14:14:15 -04:00
|
|
|
|
if (server_port == kMachPortNull) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-11-25 14:56:05 -05:00
|
|
|
|
|
|
|
|
|
// Check in with the server.
|
2015-10-29 14:14:15 -04:00
|
|
|
|
kern_return_t kr = child_port_check_in(
|
|
|
|
|
server_port.get(), token, port, right_type);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(ERROR, kr) << "child_port_check_in";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
2014-11-25 14:56:05 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace crashpad
|