Chromium Code Reviews| Index: util/mach/child_port_handshake.h |
| diff --git a/util/mach/child_port_handshake.h b/util/mach/child_port_handshake.h |
| index 734220beffd4b7209f6375596277b252703fcba9..c31798fb8a7111b5d5ab1219a30e5dc3f61895b5 100644 |
| --- a/util/mach/child_port_handshake.h |
| +++ b/util/mach/child_port_handshake.h |
| @@ -21,7 +21,7 @@ |
| #include "base/basictypes.h" |
| #include "base/files/scoped_file.h" |
| -#include "util/mach/child_port_server.h" |
| +#include "util/mach/child_port_types.h" |
| namespace crashpad { |
| @@ -31,80 +31,115 @@ class ChildPortHandshakeTest; |
| } // namespace |
| } // namespace test |
| -//! \brief Implements a handshake protocol that allows a parent process to |
| -//! obtain a Mach port right from a child process. |
| +//! \brief Implements a handshake protocol that allows processes to exchange |
| +//! port rights. |
| //! |
| //! Ordinarily, there is no way for parent and child processes to exchange port |
| //! rights, outside of the rights that children inherit from their parents. |
| //! These include task-special ports and exception ports, but all of these have |
| //! system-defined uses, and cannot reliably be replaced: in a multi-threaded |
| -//! parent, it is impossible to temporarily change one an inheritable port while |
| +//! parent, it is impossible to temporarily change an inheritable port while |
| //! maintaining a guarantee that another thread will not attempt to use it, and |
| //! in children, it difficult to guarantee that nothing will attempt to use an |
| //! inheritable port before it can be replaced with the correct one. This latter |
| //! concern is becoming increasingly more pronounced as system libraries perform |
| -//! more operations that rely on an inheritable port in module initializers. |
| +//! more operations that rely on an inherited port in module initializers. |
| //! |
| -//! The protocol implemented by this class involves a server that runs in the |
| -//! parent process. The server is published with the bootstrap server, which the |
| -//! child has access to because the bootstrap port is one of the inherited |
| -//! task-special ports. The parent and child also share a pipe, which the parent |
| -//! can write to and the child can read from. After launching a child process, |
| -//! the parent will write a random token to this pipe, along with the name under |
| -//! which its server has been registered with the bootstrap server. The child |
| -//! can then obtain a send right to this server with `bootstrap_look_up()`, and |
| -//! send a check-in message containing the token value and the port right of its |
| -//! choice by calling `child_port_check_in()`. |
| +//! The protocol implemented by this class involves a server that runs in one |
| +//! process. The server is published with the bootstrap server, which the other |
| +//! process has access to because the bootstrap port is one of the inherited |
| +//! task-special ports. The two processes also share a pipe, which the server |
| +//! can write to and the client can read from. The server will write a random |
| +//! token to this pipe, along with the name under which its service has been |
| +//! registered with the bootstrap server. The client can then obtain a send |
| +//! right to this service with `bootstrap_look_up()`, and send a check-in |
| +//! message containing the token value and the port right of its choice by |
| +//! calling `child_port_check_in()`. |
| //! |
| -//! The inclusion of the token authenticates the child to its parent. This is |
| +//! The inclusion of the token authenticates the client to the server. This is |
| //! necessary because the service is published with the bootstrap server, which |
| -//! opens up access to it to more than the child process. Because the token is |
| -//! passed to the child by a shared pipe, it constitutes a shared secret not |
| +//! opens up access to it to more than the intended client. Because the token is |
| +//! passed to the client by a shared pipe, it constitutes a shared secret not |
| //! known by other processes that may have incidental access to the server. The |
| //! ChildPortHandshake server considers its randomly-generated token valid until |
| //! a client checks in with it. This mechanism is used instead of examining the |
| //! request message’s audit trailer to verify the sender’s process ID because in |
| -//! some process architectures, it may be impossible to verify the child’s |
| -//! process ID. This may happen when the child disassociates from the parent |
| -//! with a double fork(), and the actual client is the parent’s grandchild. In |
| -//! this case, the child would not check in, but the grandchild, in possession |
| -//! of the token, would check in. |
| +//! some process architectures, it may be impossible to verify the client’s |
| +//! process ID. |
| //! |
| //! The shared pipe serves another purpose: the server monitors it for an |
| //! end-of-file (no readers) condition. Once detected, it will stop its blocking |
| -//! wait for a client to check in. This mechanism was chosen over monitoring a |
| -//! child process directly for exit to account for the possibility that the |
| -//! child might disassociate with a double fork(). |
| +//! wait for a client to check in. This mechanism was also chosen for its |
| +//! ability to function properly in diverse process architectures. |
| //! |
| -//! This class can be used to allow a child process to provide its parent with |
| -//! a send right to its task port, in cases where it is desirable for the parent |
| -//! to have such access. It can also be used to allow a child process to |
| -//! establish its own server and provide its parent with a send right to that |
| -//! server, for cases where a service is provided and it is undesirable or |
| -//! impossible to provide it via the bootstrap or launchd interfaces. |
| -class ChildPortHandshake : public ChildPortServer::Interface { |
| +//! This class can be used to allow a child process to provide its parent with a |
| +//! send right to its task port, in cases where it is desirable for the parent |
| +//! to have such access. It can also be used to allow a parent process to |
| +//! transfer a receive right to a child process that implements the server for |
| +//! that right, or for a child process to establish its own server and provide |
| +//! its parent with a send right to that server, for cases where a service is |
| +//! provided and it is undesirable or impossible to provide it via the bootstrap |
| +//! or launchd interfaces. |
|
Robert Sesek
2015/10/28 21:40:03
Now that there are two ways to start both the serv
Mark Mentovai
2015/10/29 16:53:13
Robert Sesek wrote:
Robert Sesek
2015/10/29 17:45:20
Yeah, that's helpful. I guess it doesn't have to n
|
| +class ChildPortHandshake { |
| public: |
| - //! \brief Initializes the server. |
| - //! |
| - //! This creates the pipe so that the “read” side can be obtained by calling |
| - //! ReadPipeFD(). |
| - ChildPortHandshake(); |
| + //! \brief Controls whether a receive or send right is expected to be |
| + //! obtained from the client by the server’s call to RunServer(). |
| + enum class PortRightType { |
| + //! \brief The server expects to receive a receive right. |
| + kReceiveRight = 0, |
| + //! \brief The server expects to receive a send or send-once right. |
| + kSendRight, |
| + }; |
| + |
| + ChildPortHandshake(); |
| ~ChildPortHandshake(); |
| //! \brief Obtains the “read” side of the pipe, to be used by the client. |
| //! |
| - //! Callers must obtain this file descriptor and arrange for the caller to |
| - //! have access to it before calling RunServer(). |
| + //! This file descriptor must be passed to RunClientForFD(). |
| //! |
| //! \return The file descriptor that the client should read from. |
| - int ReadPipeFD() const; |
| + base::ScopedFD ClientReadFD(); |
| + |
| + //! \brief Obtains the “write” side of the pipe, to be used by the server. |
| + //! |
| + //! This file descriptor must be passed to RunServerForFD(). |
| + //! |
| + //! \return The file descriptor that the server should write to. |
| + base::ScopedFD ServerWriteFD(); |
| + |
| + //! \brief Runs the server. |
| + //! |
| + //! This method closes the “read” side of the pipe in-process, so that the |
| + //! client process holds the only file descriptor that can read from the pipe. |
| + //! It then calls RunServerForFD() using the “write” side of the pipe. If |
| + //! ClientReadFD() has already been called in the server process, the caller |
| + //! must ensure that the file descriptor returned by ClientReadFD() is closed |
| + //! prior to calling this method. |
| + mach_port_t RunServer(PortRightType port_right_type); |
| + |
| + //! \brief Runs the client. |
| + //! |
| + //! This method closes the “write” side of the pipe in-process, so that the |
| + //! server process holds the only file descriptor that can write to the pipe. |
| + //! It then calls RunClientForFD() using the “read” side of the pipe. If |
| + //! ServerWriteFD() has already been called in the client process, the caller |
| + //! must ensure that the file descriptor returned by ServerWriteFD() is closed |
| + //! prior to calling this method. |
| + //! |
| + //! \return `true` on success, `false` on failure with a message logged. |
| + bool RunClient(mach_port_t port, mach_msg_type_name_t right_type); |
| //! \brief Runs the server. |
| //! |
| - //! This method performs these tasks: |
| - //! - Closes the “read” side of the pipe in-process, so that the client |
| - //! process holds the only file descriptor that can read from the pipe. |
| + //! If a ChildPortHandshake object is available, don’t call this static |
| + //! function. Instead, call RunServer(), which wraps this function. When using |
| + //! this function, the caller is responsible for ensuring that the client |
| + //! “read” side of the pipe is closed in the server process prior to calling |
| + //! this function. |
| + //! |
| + //! This function performs these tasks: |
| //! - Creates a random token and sends it via the pipe. |
| //! - Checks its service in with the bootstrap server, and sends the name |
| //! of its bootstrap service mapping via the pipe. |
| @@ -114,33 +149,35 @@ class ChildPortHandshake : public ChildPortServer::Interface { |
| //! interpret and validate it, and if the message is valid, returns the |
| //! port right extracted from the message. If the message is not valid, |
| //! this method will continue waiting for a valid message. Valid messages |
| - //! are properly formatted and have the correct token. If a valid message |
| - //! carries a send or send-once right, it will be returned. If a valid |
| - //! message contains a receive right, it will be destroyed and |
| - //! `MACH_PORT_NULL` will be returned. If a message is not valid, this |
| + //! are properly formatted and have the correct token. The right carried in |
| + //! a valid message will be returned. If a message is not valid, this |
| //! method will continue waiting for pipe EOF or a valid message. |
| //! - When notified of pipe EOF, returns `MACH_PORT_NULL`. |
| //! - Regardless of return value, destroys the server’s receive right and |
| //! closes the pipe. |
| //! |
| - //! \return On success, the send or send-once right to the port provided by |
| - //! the client. The caller takes ownership of this right. On failure, |
| - //! `MACH_PORT_NULL`, indicating that the client did not check in properly |
| - //! before terminating, where termination is detected by noticing that the |
| - //! read side of the shared pipe has closed. On failure, a message |
| - //! indiciating the nature of the failure will be logged. |
| - mach_port_t RunServer(); |
| - |
| - // 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; |
| + //! \param[in] port_right_type The port right type expected to be received |
| + //! from the client. If the port right received from the client does not |
| + //! match the expected type, the received port right will be destroyed, |
| + //! and `MACH_PORT_NULL` will be returned. |
| + //! |
| + //! \return On success, the port right provided by the client. The caller |
| + //! takes ownership of this right. On failure, `MACH_PORT_NULL`, |
| + //! indicating that the client did not check in properly before |
| + //! terminating, where termination is detected by detecting that the read |
| + //! side of the shared pipe has closed. On failure, a message indicating |
| + //! the nature of the failure will be logged. |
| + static mach_port_t RunServerForFD(base::ScopedFD server_write_fd, |
| + PortRightType port_right_type); |
| //! \brief Runs the client. |
| //! |
| + //! If a ChildPortHandshake object is available, don’t call this static |
| + //! function. Instead, call RunClient(), which wraps this function. When using |
| + //! this function, the caller is responsible for ensuring that the server |
| + //! “write” side of the pipe is closed in the client process prior to calling |
| + //! this function. |
| + //! |
| //! This function performs these tasks: |
| //! - Reads the token from the pipe. |
| //! - Reads the bootstrap service name from the pipe. |
| @@ -155,32 +192,42 @@ class ChildPortHandshake : public ChildPortServer::Interface { |
| //! check-in to occur without blocking to wait for a reply. |
| //! |
| //! \param[in] pipe_read The “read” side of the pipe shared with the server |
| - //! process. |
| - //! \param[in] port The port that will be passed to the server by |
| + //! process. This function takes ownership of this file descriptor, and |
| + //! will close it prior to returning. |
| + //! \param[in] port The port right that will be passed to the server by |
| //! `child_port_check_in()`. |
| - //! \param[in] right_type The right type to furnish the parent with. If \a |
| + //! \param[in] right_type The right type to furnish the server with. If \a |
| //! port is a send right, this can be `MACH_MSG_TYPE_COPY_SEND` or |
| //! `MACH_MSG_TYPE_MOVE_SEND`. If \a port is a send-once right, this can |
| - //! be `MACH_MSG_TYPE_MOVE_SEND_ONCE`. If \a port is a receive right, |
| - //! this can be `MACH_MSG_TYPE_MAKE_SEND`. `MACH_MSG_TYPE_MOVE_RECEIVE` |
| - //! is supported by the client interface but will be silently rejected by |
| - //! server run by RunServer(), which expects to receive only send or |
| - //! send-once rights. |
| - static void RunClient(int pipe_read, |
| - mach_port_t port, |
| - mach_msg_type_name_t right_type); |
| + //! be `MACH_MSG_TYPE_MOVE_SEND_ONCE`. If \a port is a receive right, this |
| + //! can be `MACH_MSG_TYPE_MAKE_SEND`, `MACH_MSG_TYPE_MAKE_SEND_ONCE`, or |
| + //! `MACH_MSG_TYPE_MOVE_RECEIVE`. |
| + //! |
| + //! \return `true` on success, `false` on failure with a message logged. On |
| + //! failure, the port right corresponding to a \a right_type of |
| + //! `MACH_MSG_TYPE_MOVE_*` is not consumed, and the caller must dispose of |
| + //! the right if necessary. |
| + static bool RunClientForFD(base::ScopedFD client_read_fd, |
| + mach_port_t port, |
| + mach_msg_type_name_t right_type); |
| private: |
| //! \brief Runs the read-from-pipe portion of the client’s side of the |
| //! handshake. This is an implementation detail of RunClient and is only |
| //! exposed for testing purposes. |
| //! |
| + //! When using this function and RunClientInternal_SendCheckIn(), the caller |
| + //! is responsible for closing \a pipe_read at an appropriate time, normally |
| + //! after calling RunClientInternal_SendCheckIn(). |
| + //! |
| //! \param[in] pipe_read The “read” side of the pipe shared with the server |
| //! process. |
| //! \param[out] token The token value read from \a pipe_read. |
| //! \param[out] service_name The service name as registered with the bootstrap |
| //! server, read from \a pipe_read. |
| - static void RunClientInternal_ReadPipe(int pipe_read, |
| + //! |
| + //! \return `true` on success, `false` on failure with a message logged. |
| + static bool RunClientInternal_ReadPipe(int pipe_read, |
| child_port_token_t* token, |
| std::string* service_name); |
| @@ -188,34 +235,25 @@ class ChildPortHandshake : public ChildPortServer::Interface { |
| //! This is an implementation detail of RunClient and is only exposed for |
| //! testing purposes. |
| //! |
| + //! When using this RunClientInternal_ReadPipe() and this function, the caller |
| + //! is responsible for closing the “read” side of the pipe at an appropriate |
| + //! time, normally after calling this function. |
| + //! |
| //! \param[in] service_name The service name as registered with the bootstrap |
| //! server, to be looked up with `bootstrap_look_up()`. |
| //! \param[in] token The token value to provide during check-in. |
| //! \param[in] port The port that will be passed to the server by |
| //! `child_port_check_in()`. |
| - //! \param[in] right_type The right type to furnish the parent with. |
| - static void RunClientInternal_SendCheckIn(const std::string& service_name, |
| + //! \param[in] right_type The right type to furnish the server with. |
| + //! |
| + //! \return `true` on success, `false` on failure with a message logged. |
| + static bool RunClientInternal_SendCheckIn(const std::string& service_name, |
| child_port_token_t token, |
| mach_port_t port, |
| mach_msg_type_name_t right_type); |
| - // Communicates the token from RunServer(), where it’s generated, to |
| - // HandleChildPortCheckIn(), where it’s validated. |
| - child_port_token_t token_; |
| - |
| - base::ScopedFD pipe_read_; |
| - base::ScopedFD pipe_write_; |
| - |
| - // Communicates the port received from the client from |
| - // HandleChildPortCheckIn(), where it’s received, to RunServer(), where it’s |
| - // returned. This is strongly-owned, but ownership is transferred to |
| - // RunServer()’s caller. |
| - mach_port_t child_port_; |
| - |
| - // Communicates that a check-in with a valid token was received by |
| - // HandleChildPortCheckIn(), and that the value of child_port_ should be |
| - // returned to RunServer()’s caller. |
| - bool checked_in_; |
| + base::ScopedFD client_read_fd_; |
| + base::ScopedFD server_write_fd_; |
| friend class test::ChildPortHandshakeTest; |