summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authoramistry <amistry@chromium.org>2016-03-03 18:04:49 -0800
committerCommit bot <commit-bot@chromium.org>2016-03-04 02:06:59 +0000
commit11ca9a5d48580d5eb98fc4d915ce308d3faf3314 (patch)
treea2333b8282259f8454761ab9e49d067d1842a64a /base
parent26259c7a20085eda544f5301350c0c7f31737236 (diff)
downloadchromium_src-11ca9a5d48580d5eb98fc4d915ce308d3faf3314.zip
chromium_src-11ca9a5d48580d5eb98fc4d915ce308d3faf3314.tar.gz
chromium_src-11ca9a5d48580d5eb98fc4d915ce308d3faf3314.tar.bz2
Move non-content specific parts of content::MachBroker into base::MachPortBroker.
This change only moves mach port functions into a new class, base::MachPortBroker. There is no new functionality, or change to existing behaviour. BUG=582468 Review URL: https://codereview.chromium.org/1755973002 Cr-Commit-Position: refs/heads/master@{#379178}
Diffstat (limited to 'base')
-rw-r--r--base/BUILD.gn4
-rw-r--r--base/base.gyp2
-rw-r--r--base/base.gypi2
-rw-r--r--base/mac/mach_port_broker.h108
-rw-r--r--base/mac/mach_port_broker.mm186
-rw-r--r--base/mac/mach_port_broker_unittest.cc107
6 files changed, 409 insertions, 0 deletions
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 6355bf7..61a36c8 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -413,6 +413,8 @@ component("base") {
"mac/mac_util.mm",
"mac/mach_logging.cc",
"mac/mach_logging.h",
+ "mac/mach_port_broker.h",
+ "mac/mach_port_broker.mm",
"mac/objc_property_releaser.h",
"mac/objc_property_releaser.mm",
"mac/os_crash_dumps.cc",
@@ -1182,6 +1184,7 @@ component("base") {
"trace_event/malloc_dump_provider.cc",
"trace_event/malloc_dump_provider.h",
]
+ libs = [ "bsm" ]
}
# Mac or iOS.
@@ -1626,6 +1629,7 @@ test("base_unittests") {
"mac/foundation_util_unittest.mm",
"mac/libdispatch_task_runner_unittest.cc",
"mac/mac_util_unittest.mm",
+ "mac/mach_port_broker_unittest.cc",
"mac/objc_property_releaser_unittest.mm",
"mac/scoped_nsobject_unittest.mm",
"mac/scoped_objc_class_swizzler_unittest.mm",
diff --git a/base/base.gyp b/base/base.gyp
index 2b93cda..fff51e9 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -202,6 +202,7 @@
'$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
'$(SDKROOT)/System/Library/Frameworks/IOKit.framework',
'$(SDKROOT)/System/Library/Frameworks/Security.framework',
+ '$(SDKROOT)/usr/lib/libbsm.dylib',
],
},
}],
@@ -449,6 +450,7 @@
'mac/foundation_util_unittest.mm',
'mac/libdispatch_task_runner_unittest.cc',
'mac/mac_util_unittest.mm',
+ 'mac/mach_port_broker_unittest.cc',
'mac/objc_property_releaser_unittest.mm',
'mac/scoped_nsobject_unittest.mm',
'mac/scoped_objc_class_swizzler_unittest.mm',
diff --git a/base/base.gypi b/base/base.gypi
index df5d196..9de683e 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -306,6 +306,8 @@
'mac/mac_util.mm',
'mac/mach_logging.cc',
'mac/mach_logging.h',
+ 'mac/mach_port_broker.h',
+ 'mac/mach_port_broker.mm',
'mac/objc_property_releaser.h',
'mac/objc_property_releaser.mm',
'mac/os_crash_dumps.cc',
diff --git a/base/mac/mach_port_broker.h b/base/mac/mach_port_broker.h
new file mode 100644
index 0000000..ba08b6f
--- /dev/null
+++ b/base/mac/mach_port_broker.h
@@ -0,0 +1,108 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MAC_MACH_PORT_BROKER_H_
+#define BASE_MAC_MACH_PORT_BROKER_H_
+
+#include <mach/mach.h>
+
+#include <map>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/mac/dispatch_source_mach.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process/port_provider_mac.h"
+#include "base/process/process_handle.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+
+// On OS X, the task port of a process is required to collect metrics about the
+// process, and to insert Mach ports into the process. Running |task_for_pid()|
+// is only allowed for privileged code. However, a process has port rights to
+// all its subprocesses, so let the child processes send their Mach port to the
+// parent over IPC.
+//
+// Mach ports can only be sent over Mach IPC, not over the |socketpair()| that
+// the regular IPC system uses. Hence, the child processes opens a Mach
+// connection shortly after launching and ipc their mach data to the parent
+// process. A single |MachPortBroker| with a given name is expected to exist in
+// the parent process.
+//
+// Since this data arrives over a separate channel, it is not available
+// immediately after a child process has been started.
+class BASE_EXPORT MachPortBroker : public base::PortProvider {
+ public:
+ // For use in child processes. This will send the task port of the current
+ // process over Mach IPC to the port registered by name (via this class) in
+ // the parent process. Returns true if the message was sent successfully
+ // and false if otherwise.
+ static bool ChildSendTaskPortToParent(const std::string& name);
+
+ // Returns the Mach port name to use when sending or receiving messages.
+ // Does the Right Thing in the browser and in child processes.
+ static std::string GetMachPortName(const std::string& name, bool is_child);
+
+ MachPortBroker(const std::string& name);
+ ~MachPortBroker() override;
+
+ // Performs any initialization work.
+ bool Init();
+
+ // Adds a placeholder to the map for the given pid with MACH_PORT_NULL.
+ // Callers are expected to later update the port with FinalizePid(). Callers
+ // MUST acquire the lock given by GetLock() before calling this method (and
+ // release the lock afterwards).
+ void AddPlaceholderForPid(base::ProcessHandle pid);
+
+ // Removes |pid| from the task port map. Callers MUST acquire the lock given
+ // by GetLock() before calling this method (and release the lock afterwards).
+ void InvalidatePid(base::ProcessHandle pid);
+
+ // The lock that protects this MachPortBroker object. Callers MUST acquire
+ // and release this lock around calls to AddPlaceholderForPid(),
+ // InvalidatePid(), and FinalizePid();
+ base::Lock& GetLock() { return lock_; }
+
+ // Implement |base::PortProvider|.
+ mach_port_t TaskForPid(base::ProcessHandle process) const override;
+
+ private:
+ friend class MachPortBrokerTest;
+
+ // Message handler that is invoked on |dispatch_source_| when an
+ // incoming message needs to be received.
+ void HandleRequest();
+
+ // Updates the mapping for |pid| to include the given |mach_info|. Does
+ // nothing if PlaceholderForPid() has not already been called for the given
+ // |pid|. Callers MUST acquire the lock given by GetLock() before calling
+ // this method (and release the lock afterwards).
+ void FinalizePid(base::ProcessHandle pid, mach_port_t task_port);
+
+ // Name used to identify a particular port broker.
+ const std::string name_;
+
+ // The Mach port on which the server listens.
+ base::mac::ScopedMachReceiveRight server_port_;
+
+ // The dispatch source and queue on which Mach messages will be received.
+ scoped_ptr<base::DispatchSourceMach> dispatch_source_;
+
+ // Stores mach info for every process in the broker.
+ typedef std::map<base::ProcessHandle, mach_port_t> MachMap;
+ MachMap mach_map_;
+
+ // Mutex that guards |mach_map_|.
+ mutable base::Lock lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(MachPortBroker);
+};
+
+} // namespace base
+
+#endif // BASE_MAC_MACH_PORT_BROKER_H_
diff --git a/base/mac/mach_port_broker.mm b/base/mac/mach_port_broker.mm
new file mode 100644
index 0000000..3d563ba
--- /dev/null
+++ b/base/mac/mach_port_broker.mm
@@ -0,0 +1,186 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/mac/mach_port_broker.h"
+
+#include <bsm/libbsm.h>
+#include <servers/bootstrap.h>
+
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/mach_logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+
+namespace base {
+
+namespace {
+
+// Mach message structure used in the child as a sending message.
+struct MachPortBroker_ChildSendMsg {
+ mach_msg_header_t header;
+ mach_msg_body_t body;
+ mach_msg_port_descriptor_t child_task_port;
+};
+
+// Complement to the ChildSendMsg, this is used in the parent for receiving
+// a message. Contains a message trailer with audit information.
+struct MachPortBroker_ParentRecvMsg : public MachPortBroker_ChildSendMsg {
+ mach_msg_audit_trailer_t trailer;
+};
+
+} // namespace
+
+// static
+bool MachPortBroker::ChildSendTaskPortToParent(const std::string& name) {
+ // Look up the named MachPortBroker port that's been registered with the
+ // bootstrap server.
+ mach_port_t parent_port;
+ kern_return_t kr = bootstrap_look_up(bootstrap_port,
+ const_cast<char*>(GetMachPortName(name, true).c_str()), &parent_port);
+ if (kr != KERN_SUCCESS) {
+ BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up";
+ return false;
+ }
+ base::mac::ScopedMachSendRight scoped_right(parent_port);
+
+ // Create the check in message. This will copy a send right on this process'
+ // (the child's) task port and send it to the parent.
+ MachPortBroker_ChildSendMsg msg;
+ bzero(&msg, sizeof(msg));
+ msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND) |
+ MACH_MSGH_BITS_COMPLEX;
+ msg.header.msgh_remote_port = parent_port;
+ msg.header.msgh_size = sizeof(msg);
+ msg.body.msgh_descriptor_count = 1;
+ msg.child_task_port.name = mach_task_self();
+ msg.child_task_port.disposition = MACH_MSG_TYPE_PORT_SEND;
+ msg.child_task_port.type = MACH_MSG_PORT_DESCRIPTOR;
+
+ kr = mach_msg(&msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, sizeof(msg),
+ 0, MACH_PORT_NULL, 100 /*milliseconds*/, MACH_PORT_NULL);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "mach_msg";
+ return false;
+ }
+
+ return true;
+}
+
+// static
+std::string MachPortBroker::GetMachPortName(const std::string& name,
+ bool is_child) {
+ // In child processes, use the parent's pid.
+ const pid_t pid = is_child ? getppid() : getpid();
+ return base::StringPrintf(
+ "%s.%s.%d", base::mac::BaseBundleID(), name.c_str(), pid);
+}
+
+mach_port_t MachPortBroker::TaskForPid(base::ProcessHandle pid) const {
+ base::AutoLock lock(lock_);
+ MachPortBroker::MachMap::const_iterator it = mach_map_.find(pid);
+ if (it == mach_map_.end())
+ return MACH_PORT_NULL;
+ return it->second;
+}
+
+MachPortBroker::MachPortBroker(const std::string& name) : name_(name) {}
+
+MachPortBroker::~MachPortBroker() {}
+
+bool MachPortBroker::Init() {
+ DCHECK(server_port_.get() == MACH_PORT_NULL);
+
+ // Check in with launchd and publish the service name.
+ mach_port_t port;
+ kern_return_t kr = bootstrap_check_in(
+ bootstrap_port, GetMachPortName(name_, false).c_str(), &port);
+ if (kr != KERN_SUCCESS) {
+ BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_check_in";
+ return false;
+ }
+ server_port_.reset(port);
+
+ // Start the dispatch source.
+ std::string queue_name =
+ base::StringPrintf("%s.MachPortBroker", base::mac::BaseBundleID());
+ dispatch_source_.reset(new base::DispatchSourceMach(
+ queue_name.c_str(), server_port_.get(), ^{ HandleRequest(); }));
+ dispatch_source_->Resume();
+
+ return true;
+}
+
+void MachPortBroker::AddPlaceholderForPid(base::ProcessHandle pid) {
+ lock_.AssertAcquired();
+ DCHECK_EQ(0u, mach_map_.count(pid));
+ mach_map_[pid] = MACH_PORT_NULL;
+}
+
+void MachPortBroker::InvalidatePid(base::ProcessHandle pid) {
+ lock_.AssertAcquired();
+
+ MachMap::iterator mach_it = mach_map_.find(pid);
+ if (mach_it != mach_map_.end()) {
+ kern_return_t kr = mach_port_deallocate(mach_task_self(), mach_it->second);
+ MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << "mach_port_deallocate";
+ mach_map_.erase(mach_it);
+ }
+}
+
+void MachPortBroker::HandleRequest() {
+ MachPortBroker_ParentRecvMsg msg;
+ bzero(&msg, sizeof(msg));
+ msg.header.msgh_size = sizeof(msg);
+ msg.header.msgh_local_port = server_port_.get();
+
+ const mach_msg_option_t options = MACH_RCV_MSG |
+ MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) |
+ MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
+
+ kern_return_t kr = mach_msg(&msg.header,
+ options,
+ 0,
+ sizeof(msg),
+ server_port_.get(),
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "mach_msg";
+ return;
+ }
+
+ // Use the kernel audit information to make sure this message is from
+ // a task that this process spawned. The kernel audit token contains the
+ // unspoofable pid of the task that sent the message.
+ //
+ // TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
+ pid_t child_pid;
+ audit_token_to_au32(msg.trailer.msgh_audit,
+ NULL, NULL, NULL, NULL, NULL, &child_pid, NULL, NULL);
+
+ mach_port_t child_task_port = msg.child_task_port.name;
+
+ // Take the lock and update the broker information.
+ base::AutoLock lock(lock_);
+ FinalizePid(child_pid, child_task_port);
+}
+
+void MachPortBroker::FinalizePid(base::ProcessHandle pid,
+ mach_port_t task_port) {
+ lock_.AssertAcquired();
+
+ MachMap::iterator it = mach_map_.find(pid);
+ if (it == mach_map_.end()) {
+ // Do nothing for unknown pids.
+ LOG(ERROR) << "Unknown process " << pid << " is sending Mach IPC messages!";
+ return;
+ }
+
+ DCHECK(it->second == MACH_PORT_NULL);
+ if (it->second == MACH_PORT_NULL)
+ it->second = task_port;
+}
+
+} // namespace base
diff --git a/base/mac/mach_port_broker_unittest.cc b/base/mac/mach_port_broker_unittest.cc
new file mode 100644
index 0000000..2188ea3
--- /dev/null
+++ b/base/mac/mach_port_broker_unittest.cc
@@ -0,0 +1,107 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/mac/mach_port_broker.h"
+
+#include "base/command_line.h"
+#include "base/synchronization/lock.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace base {
+
+namespace {
+const char kBootstrapPortName[] = "thisisatest";
+}
+
+class MachPortBrokerTest : public testing::Test {
+ public:
+ MachPortBrokerTest() : broker_(kBootstrapPortName) {}
+
+ // Helper function to acquire/release locks and call |PlaceholderForPid()|.
+ void AddPlaceholderForPid(base::ProcessHandle pid) {
+ base::AutoLock lock(broker_.GetLock());
+ broker_.AddPlaceholderForPid(pid);
+ }
+
+ // Helper function to acquire/release locks and call |FinalizePid()|.
+ void FinalizePid(base::ProcessHandle pid,
+ mach_port_t task_port) {
+ base::AutoLock lock(broker_.GetLock());
+ broker_.FinalizePid(pid, task_port);
+ }
+
+ protected:
+ MachPortBroker broker_;
+};
+
+TEST_F(MachPortBrokerTest, Locks) {
+ // Acquire and release the locks. Nothing bad should happen.
+ base::AutoLock lock(broker_.GetLock());
+}
+
+TEST_F(MachPortBrokerTest, AddPlaceholderAndFinalize) {
+ // Add a placeholder for PID 1.
+ AddPlaceholderForPid(1);
+ EXPECT_EQ(0u, broker_.TaskForPid(1));
+
+ // Finalize PID 1.
+ FinalizePid(1, 100u);
+ EXPECT_EQ(100u, broker_.TaskForPid(1));
+
+ // Should be no entry for PID 2.
+ EXPECT_EQ(0u, broker_.TaskForPid(2));
+}
+
+TEST_F(MachPortBrokerTest, FinalizeUnknownPid) {
+ // Finalizing an entry for an unknown pid should not add it to the map.
+ FinalizePid(1u, 100u);
+ EXPECT_EQ(0u, broker_.TaskForPid(1u));
+}
+
+MULTIPROCESS_TEST_MAIN(MachPortBrokerTestChild) {
+ CHECK(base::MachPortBroker::ChildSendTaskPortToParent(kBootstrapPortName));
+ return 0;
+}
+
+TEST_F(MachPortBrokerTest, ReceivePortFromChild) {
+ ASSERT_TRUE(broker_.Init());
+ CommandLine command_line(
+ base::GetMultiProcessTestChildBaseCommandLine());
+ broker_.GetLock().Acquire();
+ base::Process test_child_process = base::SpawnMultiProcessTestChild(
+ "MachPortBrokerTestChild", command_line, LaunchOptions());
+ broker_.AddPlaceholderForPid(test_child_process.Handle());
+ broker_.GetLock().Release();
+
+ int rv = -1;
+ ASSERT_TRUE(test_child_process.WaitForExitWithTimeout(
+ TestTimeouts::action_timeout(), &rv));
+ EXPECT_EQ(0, rv);
+
+ EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL),
+ broker_.TaskForPid(test_child_process.Handle()));
+}
+
+TEST_F(MachPortBrokerTest, ReceivePortFromChildWithoutAdding) {
+ ASSERT_TRUE(broker_.Init());
+ CommandLine command_line(
+ base::GetMultiProcessTestChildBaseCommandLine());
+ broker_.GetLock().Acquire();
+ base::Process test_child_process = base::SpawnMultiProcessTestChild(
+ "MachPortBrokerTestChild", command_line, LaunchOptions());
+ broker_.GetLock().Release();
+
+ int rv = -1;
+ ASSERT_TRUE(test_child_process.WaitForExitWithTimeout(
+ TestTimeouts::action_timeout(), &rv));
+ EXPECT_EQ(0, rv);
+
+ EXPECT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL),
+ broker_.TaskForPid(test_child_process.Handle()));
+}
+
+} // namespace base