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

Unified Diff: util/mach/child_port_handshake.cc

Issue 1408473002: ChildPortHandshake: allow receive rights to be received (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Address review feedback Created 5 years, 2 months 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
« no previous file with comments | « util/mach/child_port_handshake.h ('k') | util/mach/child_port_handshake_test.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « util/mach/child_port_handshake.h ('k') | util/mach/child_port_handshake_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698