summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorrohitrao@chromium.org <rohitrao@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-17 12:28:32 +0000
committerrohitrao@chromium.org <rohitrao@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-17 12:28:32 +0000
commitb88a7498e267ac265b141a2c7693cc37a55c4e9b (patch)
tree9a27b031d474def81658bdadad42ed05778af6d5 /base
parent35e55bffc4738b3dbd68be3f01aa918e56133bc8 (diff)
downloadchromium_src-b88a7498e267ac265b141a2c7693cc37a55c4e9b.zip
chromium_src-b88a7498e267ac265b141a2c7693cc37a55c4e9b.tar.gz
chromium_src-b88a7498e267ac265b141a2c7693cc37a55c4e9b.tar.bz2
[Mac] Replace the existing browser-child mach ipc with a long-lived listener on a well-known port.
Before this CL: Before fork()ing a child, the browser process creates a mach receive port with a random name. After the fork() but before exec(), the child uses mach ipc to transmit send rights to its task port. The child has access to the random name because it inherits it from the browser process. Unfortunately, some of the library functions involved in sending a mach message are not safe to call after fork(). After this CL: Before forking the first child, the browser spins off a new thread that listens on a well-known port for mach ipc from any process. This well-known port is "com.google.Chrome.<browserpid>". When a child process starts up, it sends a mach message to its parent browser's well-known port. On the browser side, we listen for said message, extract the pid of the sending process, and ignore any messages from processes we did not personally fork(). This check is necessary because any arbitrary process on the system could send mach ipc to that port. BUG=35374 TEST=Browser should still start up. The task manager should still show correct cpu/memory data. There should be no perf regressions. TEST=Mac ui_tests and browser_tests should be less flaky. Review URL: http://codereview.chromium.org/3443002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@59782 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r--base/mach_ipc_mac.h33
-rw-r--r--base/mach_ipc_mac.mm13
-rw-r--r--base/process_util.h12
-rw-r--r--base/process_util_posix.cc101
4 files changed, 28 insertions, 131 deletions
diff --git a/base/mach_ipc_mac.h b/base/mach_ipc_mac.h
index 5836c3a..91b9729 100644
--- a/base/mach_ipc_mac.h
+++ b/base/mach_ipc_mac.h
@@ -20,7 +20,7 @@
//
// The three main classes of interest are
//
-// MachMessage: a wrapper for a mach message of the following form
+// MachMessage: a wrapper for a Mach message of the following form
// mach_msg_header_t
// mach_msg_body_t
// optional descriptors
@@ -30,10 +30,10 @@
// and are used instead of MachMessage which is an abstract base class
//
// ReceivePort:
-// Represents a mach port for which we have receive rights
+// Represents a Mach port for which we have receive rights
//
// MachPortSender:
-// Represents a mach port for which we have send rights
+// Represents a Mach port for which we have send rights
//
// Here's an example to receive a message on a server port:
//
@@ -127,7 +127,7 @@ class MachMsgPortDescriptor : public mach_msg_port_descriptor_t {
};
//==============================================================================
-// MachMessage: a wrapper for a mach message
+// MachMessage: a wrapper for a Mach message
// (mach_msg_header_t, mach_msg_body_t, extra data)
//
// This considerably simplifies the construction of a message for sending
@@ -165,7 +165,7 @@ class MachMessage {
int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); }
- // Adds a descriptor (typically a mach port) to be translated
+ // Adds a descriptor (typically a Mach port) to be translated
// returns true if successful, otherwise not enough space
bool AddDescriptor(const MachMsgPortDescriptor &desc);
@@ -175,7 +175,7 @@ class MachMessage {
MachMsgPortDescriptor *GetDescriptor(int n);
- // Convenience method which gets the mach port described by the descriptor
+ // Convenience method which gets the Mach port described by the descriptor
mach_port_t GetTranslatedPort(int n);
// A simple message is one with no descriptors
@@ -212,7 +212,7 @@ class MachMessage {
int CalculateSize();
// Returns total storage size that this object can grow to, this is inclusive
- // of the mach header.
+ // of the Mach header.
size_t MaxSize() const { return storage_length_bytes_; }
protected:
@@ -242,7 +242,7 @@ class MachMessage {
//==============================================================================
// MachReceiveMessage and MachSendMessage are useful to separate the idea
-// of a mach message being sent and being received, and adds increased type
+// of a Mach message being sent and being received, and adds increased type
// safety:
// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage
// MachPortSender::SendMessage() only accepts a MachSendMessage
@@ -271,26 +271,27 @@ class MachSendMessage : public MachMessage {
};
//==============================================================================
-// Represents a mach port for which we have receive rights
+// Represents a Mach port for which we have receive rights
class ReceivePort {
public:
- // Creates a new mach port for receiving messages and registers a name for it
+ // Creates a new Mach port for receiving messages and registers a name for it
explicit ReceivePort(const char *receive_port_name);
- // Given an already existing mach port, use it. We take ownership of the
+ // Given an already existing Mach port, use it. We take ownership of the
// port and deallocate it in our destructor.
explicit ReceivePort(mach_port_t receive_port);
- // Create a new mach port for receiving messages
+ // Create a new Mach port for receiving messages
ReceivePort();
~ReceivePort();
- // Waits on the mach port until message received or timeout
+ // Waits on the Mach port until message received or timeout. If |timeout| is
+ // MACH_MSG_TIMEOUT_NONE, this method waits forever.
kern_return_t WaitForMessage(MachReceiveMessage *out_message,
mach_msg_timeout_t timeout);
- // The underlying mach port that we wrap
+ // The underlying Mach port that we wrap
mach_port_t GetPort() const { return port_; }
private:
@@ -301,14 +302,14 @@ class ReceivePort {
};
//==============================================================================
-// Represents a mach port for which we have send rights
+// Represents a Mach port for which we have send rights
class MachPortSender {
public:
// get a port with send rights corresponding to a named registered service
explicit MachPortSender(const char *receive_port_name);
- // Given an already existing mach port, use it. Does not take ownership of
+ // Given an already existing Mach port, use it. Does not take ownership of
// |send_port|.
explicit MachPortSender(mach_port_t send_port);
diff --git a/base/mach_ipc_mac.mm b/base/mach_ipc_mac.mm
index 973b66a..a0bfdc8 100644
--- a/base/mach_ipc_mac.mm
+++ b/base/mach_ipc_mac.mm
@@ -198,7 +198,12 @@ ReceivePort::ReceivePort(const char *receive_port_name) {
if (init_result_ != KERN_SUCCESS)
return;
- NSPort *ns_port = [NSMachPort portWithMachPort:port_];
+ // Without |NSMachPortDeallocateNone|, the NSMachPort seems to deallocate
+ // receive rights on port when it is eventually released. It is not necessary
+ // to deallocate any rights here as |port_| is fully deallocated in the
+ // ReceivePort destructor.
+ NSPort *ns_port = [NSMachPort portWithMachPort:port_
+ options:NSMachPortDeallocateNone];
NSString *port_name = [NSString stringWithUTF8String:receive_port_name];
[[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name];
}
@@ -252,8 +257,12 @@ kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
out_message->Head()->msgh_reserved = 0;
out_message->Head()->msgh_id = 0;
+ mach_msg_option_t rcv_options = MACH_RCV_MSG;
+ if (timeout != MACH_MSG_TIMEOUT_NONE)
+ rcv_options |= MACH_RCV_TIMEOUT;
+
kern_return_t result = mach_msg(out_message->Head(),
- MACH_RCV_MSG | MACH_RCV_TIMEOUT,
+ rcv_options,
0,
out_message->MaxSize(),
port_,
diff --git a/base/process_util.h b/base/process_util.h
index 4699c9e2..e64d0a7 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -235,18 +235,6 @@ bool LaunchApp(const std::vector<std::string>& argv,
// The returned array is allocated using new[] and must be freed by the caller.
char** AlterEnvironment(const environment_vector& changes,
const char* const* const env);
-
-#if defined(OS_MACOSX)
-// Similar to the above, but also returns the new process's task_t if
-// |task_handle| is not NULL. If |task_handle| is not NULL, the caller is
-// responsible for calling |mach_port_deallocate()| on the returned handle.
-bool LaunchAppAndGetTask(const std::vector<std::string>& argv,
- const environment_vector& environ,
- const file_handle_mapping_vector& fds_to_remap,
- bool wait,
- task_t* task_handle,
- ProcessHandle* process_handle);
-#endif // defined(OS_MACOSX)
#endif // defined(OS_POSIX)
// Executes the application specified by cl. This function delegates to one
diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc
index 8fe64d0..d6171b1 100644
--- a/base/process_util_posix.cc
+++ b/base/process_util_posix.cc
@@ -32,8 +32,6 @@
#if defined(OS_MACOSX)
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
-#include "base/mach_ipc_mac.h"
-#include "base/rand_util.h"
#else
extern char** environ;
#endif
@@ -301,82 +299,6 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
}
}
-#if defined(OS_MACOSX)
-static std::string MachErrorCode(kern_return_t err) {
- return StringPrintf("0x%x %s", err, mach_error_string(err));
-}
-
-// Forks the current process and returns the child's |task_t| in the parent
-// process.
-static pid_t fork_and_get_task(task_t* child_task) {
- const int kTimeoutMs = 100;
- kern_return_t err;
-
- // Put a random number into the channel name, so that a compromised renderer
- // can't pretend being the child that's forked off.
- std::string mach_connection_name = StringPrintf(
- "com.google.Chrome.samplingfork.%p.%d",
- child_task, base::RandInt(0, std::numeric_limits<int>::max()));
-
- // Create the mach receive port before forking to ensure that it exists when
- // the child tries to connect. Mach ports are not duped into the child, so
- // this is safe to set up here.
- ReceivePort parent_recv_port(mach_connection_name.c_str());
-
- // Error handling philosophy: If Mach IPC fails, don't touch |child_task| but
- // return a valid pid. If IPC fails in the child, the parent will have to wait
- // until kTimeoutMs is over. This is not optimal, but I've never seen it
- // happen, and stuff should still mostly work.
- pid_t pid = fork();
- switch (pid) {
- case -1:
- return pid;
- case 0: { // child
- // Must reset signal handlers before doing any mach IPC, as the mach IPC
- // calls can potentially hang forever.
- ResetChildSignalHandlersToDefaults();
- MachSendMessage child_message(/* id= */0);
- if (!child_message.AddDescriptor(mach_task_self())) {
- LOG(ERROR) << "child AddDescriptor(mach_task_self()) failed.";
- return pid;
- }
-
- MachPortSender child_sender(mach_connection_name.c_str());
- err = child_sender.SendMessage(child_message, kTimeoutMs);
- if (err != KERN_SUCCESS) {
- LOG(ERROR) << "child SendMessage() failed: " << MachErrorCode(err);
- return pid;
- }
- break;
- }
- default: { // parent
- MachReceiveMessage child_message;
- err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs);
- if (err != KERN_SUCCESS) {
- LOG(ERROR) << "parent WaitForMessage() failed: " << MachErrorCode(err);
- return pid;
- }
-
- if (child_message.GetTranslatedPort(0) == MACH_PORT_NULL) {
- LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
- return pid;
- }
- *child_task = child_message.GetTranslatedPort(0);
- break;
- }
- }
- return pid;
-}
-
-bool LaunchApp(const std::vector<std::string>& argv,
- const environment_vector& env_changes,
- const file_handle_mapping_vector& fds_to_remap,
- bool wait, ProcessHandle* process_handle) {
- return LaunchAppAndGetTask(
- argv, env_changes, fds_to_remap, wait, NULL, process_handle);
-}
-#endif // defined(OS_MACOSX)
-
char** AlterEnvironment(const environment_vector& changes,
const char* const* const env) {
unsigned count = 0;
@@ -498,18 +420,11 @@ char** AlterEnvironment(const environment_vector& changes,
return ret;
}
-#if defined(OS_MACOSX)
-bool LaunchAppAndGetTask(
-#else
bool LaunchApp(
-#endif
const std::vector<std::string>& argv,
const environment_vector& env_changes,
const file_handle_mapping_vector& fds_to_remap,
bool wait,
-#if defined(OS_MACOSX)
- task_t* task_handle,
-#endif
ProcessHandle* process_handle) {
pid_t pid;
InjectiveMultimap fd_shuffle1, fd_shuffle2;
@@ -518,20 +433,7 @@ bool LaunchApp(
scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
scoped_array<char*> new_environ(AlterEnvironment(env_changes, environ));
-#if defined(OS_MACOSX)
- if (task_handle == NULL) {
- pid = fork();
- } else {
- // On OS X, the task_t for a process is needed for several reasons. Sadly,
- // the function task_for_pid() requires privileges a normal user doesn't
- // have. Instead, a short-lived Mach IPC connection is opened between parent
- // and child, and the child sends its task_t to the parent at fork time.
- *task_handle = MACH_PORT_NULL;
- pid = fork_and_get_task(task_handle);
- }
-#else
pid = fork();
-#endif
if (pid < 0)
return false;
@@ -541,10 +443,7 @@ bool LaunchApp(
RestoreDefaultExceptionHandler();
#endif
- // On mac, the signal handlers are reset in |fork_and_get_task()|.
-#if !defined(OS_MACOSX)
ResetChildSignalHandlersToDefaults();
-#endif
#if 0
// When debugging it can be helpful to check that we really aren't making