| Index: util/mach/child_port_handshake.cc
 | 
| diff --git a/util/mach/child_port_handshake.cc b/util/mach/child_port_handshake.cc
 | 
| index 119a53c02565a72f88fca63243c1e4309b10e198..e45d6aeeee1ff9edc24ca17b18c59b246785c743 100644
 | 
| --- a/util/mach/child_port_handshake.cc
 | 
| +++ b/util/mach/child_port_handshake.cc
 | 
| @@ -32,60 +32,60 @@
 | 
|  #include "base/strings/stringprintf.h"
 | 
|  #include "util/file/file_io.h"
 | 
|  #include "util/mach/child_port.h"
 | 
| +#include "util/mach/child_port_server.h"
 | 
|  #include "util/mach/mach_extensions.h"
 | 
|  #include "util/mach/mach_message.h"
 | 
|  #include "util/mach/mach_message_server.h"
 | 
|  #include "util/misc/implicit_cast.h"
 | 
|  
 | 
|  namespace crashpad {
 | 
| -
 | 
| -ChildPortHandshake::ChildPortHandshake()
 | 
| +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()
 | 
|      : token_(0),
 | 
| -      pipe_read_(),
 | 
| -      pipe_write_(),
 | 
| -      child_port_(MACH_PORT_NULL),
 | 
| +      port_(MACH_PORT_NULL),
 | 
| +      right_type_(MACH_MSG_TYPE_PORT_NONE),
 | 
|        checked_in_(false) {
 | 
| -  // 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";
 | 
| -
 | 
| -  pipe_read_.reset(pipe_fds[0]);
 | 
| -  pipe_write_.reset(pipe_fds[1]);
 | 
| -
 | 
| -  // Simulate pipe() semantics by shutting down the “wrong” sides of the socket.
 | 
| -  PCHECK(shutdown(pipe_write_.get(), SHUT_RD) == 0) << "shutdown";
 | 
| -  PCHECK(shutdown(pipe_read_.get(), SHUT_WR) == 0) << "shutdown";
 | 
| -
 | 
| -  // SIGPIPE is undesirable when writing to this pipe. Allow broken-pipe writes
 | 
| -  // to fail with EPIPE instead.
 | 
| -  const int value = 1;
 | 
| -  PCHECK(setsockopt(
 | 
| -      pipe_write_.get(), SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value)) == 0)
 | 
| -      << "setsockopt";
 | 
|  }
 | 
|  
 | 
| -ChildPortHandshake::~ChildPortHandshake() {
 | 
| -}
 | 
| -
 | 
| -int ChildPortHandshake::ReadPipeFD() const {
 | 
| -  DCHECK_NE(pipe_read_.get(), -1);
 | 
| -  return pipe_read_.get();
 | 
| +ChildPortHandshakeServer::~ChildPortHandshakeServer() {
 | 
|  }
 | 
|  
 | 
| -mach_port_t ChildPortHandshake::RunServer() {
 | 
| -  DCHECK_NE(pipe_read_.get(), -1);
 | 
| -  pipe_read_.reset();
 | 
| -
 | 
| -  // Transfer ownership of the write pipe into this method’s scope.
 | 
| -  base::ScopedFD pipe_write_owner = pipe_write_.Pass();
 | 
| +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());
 | 
|  
 | 
|    // Initialize the token and share it with the client via the pipe.
 | 
|    token_ = base::RandUint64();
 | 
| -  int pipe_write = pipe_write_owner.get();
 | 
| -  if (!LoggingWriteFile(pipe_write, &token_, sizeof(token_))) {
 | 
| +  if (!LoggingWriteFile(server_write_fd.get(), &token_, sizeof(token_))) {
 | 
|      LOG(WARNING) << "no client check-in";
 | 
|      return MACH_PORT_NULL;
 | 
|    }
 | 
| @@ -109,14 +109,15 @@ mach_port_t ChildPortHandshake::RunServer() {
 | 
|  
 | 
|    // Share the service name with the client via the pipe.
 | 
|    uint32_t service_name_length = service_name.size();
 | 
| -  if (!LoggingWriteFile(
 | 
| -          pipe_write, &service_name_length, sizeof(service_name_length))) {
 | 
| +  if (!LoggingWriteFile(server_write_fd.get(),
 | 
| +                        &service_name_length,
 | 
| +                        sizeof(service_name_length))) {
 | 
|      LOG(WARNING) << "no client check-in";
 | 
|      return MACH_PORT_NULL;
 | 
|    }
 | 
|  
 | 
|    if (!LoggingWriteFile(
 | 
| -          pipe_write, service_name.c_str(), service_name_length)) {
 | 
| +          server_write_fd.get(), service_name.c_str(), service_name_length)) {
 | 
|      LOG(WARNING) << "no client check-in";
 | 
|      return MACH_PORT_NULL;
 | 
|    }
 | 
| @@ -147,7 +148,7 @@ mach_port_t ChildPortHandshake::RunServer() {
 | 
|           0,
 | 
|           nullptr);
 | 
|    EV_SET(&changelist[1],
 | 
| -         pipe_write,
 | 
| +         server_write_fd.get(),
 | 
|           EVFILT_WRITE,
 | 
|           EV_ADD | EV_CLEAR,
 | 
|           0,
 | 
| @@ -162,7 +163,7 @@ mach_port_t ChildPortHandshake::RunServer() {
 | 
|    bool blocking = true;
 | 
|    DCHECK(!checked_in_);
 | 
|    while (!checked_in_) {
 | 
| -    DCHECK_EQ(child_port_, kMachPortNull);
 | 
| +    DCHECK_EQ(port_, kMachPortNull);
 | 
|  
 | 
|      // 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
 | 
| @@ -230,7 +231,7 @@ mach_port_t ChildPortHandshake::RunServer() {
 | 
|          // pipe. Ignore that case. Multiple notifications for that situation
 | 
|          // will not be generated because edge triggering (EV_CLEAR) is used
 | 
|          // above.
 | 
| -        DCHECK_EQ(implicit_cast<int>(event.ident), pipe_write);
 | 
| +        DCHECK_EQ(implicit_cast<int>(event.ident), server_write_fd.get());
 | 
|          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
 | 
| @@ -246,19 +247,48 @@ mach_port_t ChildPortHandshake::RunServer() {
 | 
|      }
 | 
|    }
 | 
|  
 | 
| -  mach_port_t child_port = MACH_PORT_NULL;
 | 
| -  std::swap(child_port_, child_port);
 | 
| -  return child_port;
 | 
| +  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;
 | 
|  }
 | 
|  
 | 
| -kern_return_t ChildPortHandshake::HandleChildPortCheckIn(
 | 
| +kern_return_t ChildPortHandshakeServer::HandleChildPortCheckIn(
 | 
|      child_port_server_t server,
 | 
|      const 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) {
 | 
| -  DCHECK_EQ(child_port_, kMachPortNull);
 | 
| +  DCHECK_EQ(port_, kMachPortNull);
 | 
| +  DCHECK(!checked_in_);
 | 
|  
 | 
|    if (token != token_) {
 | 
|      // If the token’s not correct, someone’s attempting to spoof the legitimate
 | 
| @@ -268,19 +298,18 @@ kern_return_t ChildPortHandshake::HandleChildPortCheckIn(
 | 
|    } else {
 | 
|      checked_in_ = true;
 | 
|  
 | 
| -    if (right_type == MACH_MSG_TYPE_PORT_RECEIVE) {
 | 
| -      // The message needs to carry a send right or a send-once right. This
 | 
| -      // isn’t a strict requirement of the protocol, but users of this class
 | 
| -      // expect a send right or a send-once right, both of which can be managed
 | 
| -      // by base::mac::ScopedMachSendRight. It is invalid to store a receive
 | 
| -      // right in that scoper.
 | 
| -      LOG(WARNING) << "ignoring MACH_MSG_TYPE_PORT_RECEIVE";
 | 
| +    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;
 | 
|        *destroy_request = true;
 | 
|      } else {
 | 
| -      // Communicate the child port back to the RunServer().
 | 
| +      // Communicate the child port and right type back to the RunServer().
 | 
|        // *destroy_request is left at false, because RunServer() needs the right
 | 
|        // to remain intact. It gives ownership of the right to its caller.
 | 
| -      child_port_ = port;
 | 
| +      port_ = port;
 | 
| +      right_type_ = right_type;
 | 
|      }
 | 
|    }
 | 
|  
 | 
| @@ -288,40 +317,112 @@ kern_return_t ChildPortHandshake::HandleChildPortCheckIn(
 | 
|    return MIG_NO_REPLY;
 | 
|  }
 | 
|  
 | 
| -// static
 | 
| -void ChildPortHandshake::RunClient(int pipe_read,
 | 
| -                                   mach_port_t port,
 | 
| +}  // 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,
 | 
|                                     mach_msg_type_name_t right_type) {
 | 
| -  base::ScopedFD pipe_read_owner(pipe_read);
 | 
| +  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());
 | 
|  
 | 
|    // Read the token and the service name from the read side of the pipe.
 | 
|    child_port_token_t token;
 | 
|    std::string service_name;
 | 
| -  RunClientInternal_ReadPipe(pipe_read, &token, &service_name);
 | 
| +  if (!RunClientInternal_ReadPipe(
 | 
| +          client_read_fd.get(), &token, &service_name)) {
 | 
| +    return false;
 | 
| +  }
 | 
|  
 | 
|    // Look up the server and check in with it by providing the token and port.
 | 
| -  RunClientInternal_SendCheckIn(service_name, token, port, right_type);
 | 
| +  return RunClientInternal_SendCheckIn(service_name, token, port, right_type);
 | 
|  }
 | 
|  
 | 
|  // static
 | 
| -void ChildPortHandshake::RunClientInternal_ReadPipe(int pipe_read,
 | 
| +bool ChildPortHandshake::RunClientInternal_ReadPipe(int client_read_fd,
 | 
|                                                      child_port_token_t* token,
 | 
|                                                      std::string* service_name) {
 | 
|    // Read the token from the pipe.
 | 
| -  CheckedReadFile(pipe_read, token, sizeof(*token));
 | 
| +  if (!LoggingReadFile(client_read_fd, token, sizeof(*token))) {
 | 
| +    return false;
 | 
| +  }
 | 
|  
 | 
|    // Read the service name from the pipe.
 | 
|    uint32_t service_name_length;
 | 
| -  CheckedReadFile(pipe_read, &service_name_length, sizeof(service_name_length));
 | 
| +  if (!LoggingReadFile(
 | 
| +      client_read_fd, &service_name_length, sizeof(service_name_length))) {
 | 
| +    return false;
 | 
| +  }
 | 
|  
 | 
|    service_name->resize(service_name_length);
 | 
| -  if (!service_name->empty()) {
 | 
| -    CheckedReadFile(pipe_read, &(*service_name)[0], service_name_length);
 | 
| +  if (!service_name->empty() &&
 | 
| +      !LoggingReadFile(
 | 
| +          client_read_fd, &(*service_name)[0], service_name_length)) {
 | 
| +    return false;
 | 
|    }
 | 
| +
 | 
| +  return true;
 | 
|  }
 | 
|  
 | 
|  // static
 | 
| -void ChildPortHandshake::RunClientInternal_SendCheckIn(
 | 
| +bool ChildPortHandshake::RunClientInternal_SendCheckIn(
 | 
|      const std::string& service_name,
 | 
|      child_port_token_t token,
 | 
|      mach_port_t port,
 | 
| @@ -329,12 +430,19 @@ void ChildPortHandshake::RunClientInternal_SendCheckIn(
 | 
|    // Get a send right to the server by looking up the service with the bootstrap
 | 
|    // server by name.
 | 
|    base::mac::ScopedMachSendRight server_port(BootstrapLookUp(service_name));
 | 
| -  CHECK(server_port.is_valid());
 | 
| +  if (server_port == kMachPortNull) {
 | 
| +    return false;
 | 
| +  }
 | 
|  
 | 
|    // Check in with the server.
 | 
| -  kern_return_t kr =
 | 
| -      child_port_check_in(server_port.get(), token, port, right_type);
 | 
| -  MACH_CHECK(kr == KERN_SUCCESS, kr) << "child_port_check_in";
 | 
| +  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;
 | 
|  }
 | 
|  
 | 
|  }  // namespace crashpad
 | 
| 
 |