1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "mojo/core/mach_port_relay.h"
6
7 #include <mach/mach.h>
8
9 #include <utility>
10
11 #include "base/logging.h"
12 #include "base/mac/mach_port_util.h"
13 #include "base/mac/scoped_mach_port.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/process/process.h"
16
17 namespace mojo {
18 namespace core {
19
20 namespace {
21
22 // Errors that can occur in the broker (privileged parent) process.
23 // These match tools/metrics/histograms.xml.
24 // This enum is append-only.
25 enum class BrokerUMAError : int {
26 SUCCESS = 0,
27 // Couldn't get a task port for the process with a given pid.
28 ERROR_TASK_FOR_PID = 1,
29 // Couldn't make a port with receive rights in the destination process.
30 ERROR_MAKE_RECEIVE_PORT = 2,
31 // Couldn't change the attributes of a Mach port.
32 ERROR_SET_ATTRIBUTES = 3,
33 // Couldn't extract a right from the destination.
34 ERROR_EXTRACT_DEST_RIGHT = 4,
35 // Couldn't send a Mach port in a call to mach_msg().
36 ERROR_SEND_MACH_PORT = 5,
37 // Couldn't extract a right from the source.
38 ERROR_EXTRACT_SOURCE_RIGHT = 6,
39 ERROR_MAX
40 };
41
42 // Errors that can occur in a child process.
43 // These match tools/metrics/histograms.xml.
44 // This enum is append-only.
45 enum class ChildUMAError : int {
46 SUCCESS = 0,
47 // An error occurred while trying to receive a Mach port with mach_msg().
48 ERROR_RECEIVE_MACH_MESSAGE = 1,
49 ERROR_MAX
50 };
51
ReportBrokerError(BrokerUMAError error)52 void ReportBrokerError(BrokerUMAError error) {
53 UMA_HISTOGRAM_ENUMERATION("Mojo.MachPortRelay.BrokerError",
54 static_cast<int>(error),
55 static_cast<int>(BrokerUMAError::ERROR_MAX));
56 }
57
ReportChildError(ChildUMAError error)58 void ReportChildError(ChildUMAError error) {
59 UMA_HISTOGRAM_ENUMERATION("Mojo.MachPortRelay.ChildError",
60 static_cast<int>(error),
61 static_cast<int>(ChildUMAError::ERROR_MAX));
62 }
63
64 } // namespace
65
66 // static
ReceiveSendRight(base::mac::ScopedMachReceiveRight port)67 base::mac::ScopedMachSendRight MachPortRelay::ReceiveSendRight(
68 base::mac::ScopedMachReceiveRight port) {
69 // MACH_PORT_NULL doesn't need translation.
70 if (!port.is_valid())
71 return base::mac::ScopedMachSendRight();
72
73 // Take ownership of the receive right. We only need it to read this single
74 // send right, then it can be closed.
75 base::mac::ScopedMachSendRight received_port(
76 base::ReceiveMachPort(port.get()));
77 if (!received_port.is_valid()) {
78 ReportChildError(ChildUMAError::ERROR_RECEIVE_MACH_MESSAGE);
79 DLOG(ERROR) << "Error receiving mach port";
80 return base::mac::ScopedMachSendRight();
81 }
82
83 ReportChildError(ChildUMAError::SUCCESS);
84 return received_port;
85 }
86
MachPortRelay(base::PortProvider * port_provider)87 MachPortRelay::MachPortRelay(base::PortProvider* port_provider)
88 : port_provider_(port_provider) {
89 DCHECK(port_provider);
90 port_provider_->AddObserver(this);
91 }
92
~MachPortRelay()93 MachPortRelay::~MachPortRelay() {
94 port_provider_->RemoveObserver(this);
95 }
96
SendPortsToProcess(Channel::Message * message,base::ProcessHandle process)97 void MachPortRelay::SendPortsToProcess(Channel::Message* message,
98 base::ProcessHandle process) {
99 DCHECK(message);
100 mach_port_t task_port = port_provider_->TaskForPid(process);
101
102 std::vector<PlatformHandleInTransit> handles = message->TakeHandles();
103 // Message should have handles, otherwise there's no point in calling this
104 // function.
105 DCHECK(!handles.empty());
106 for (auto& handle : handles) {
107 if (!handle.handle().is_valid_mach_port())
108 continue;
109
110 if (task_port == MACH_PORT_NULL) {
111 // Callers check the port provider for the task port before calling this
112 // function, in order to queue pending messages. Therefore, if this fails,
113 // it should be considered a genuine, bona fide, electrified, six-car
114 // error.
115 ReportBrokerError(BrokerUMAError::ERROR_TASK_FOR_PID);
116 handle = PlatformHandleInTransit(
117 PlatformHandle(base::mac::ScopedMachSendRight()));
118 continue;
119 }
120
121 mach_port_name_t intermediate_port;
122 base::MachCreateError error_code;
123 intermediate_port = base::CreateIntermediateMachPort(
124 task_port, handle.TakeHandle().TakeMachPort(), &error_code);
125 if (intermediate_port == MACH_PORT_NULL) {
126 BrokerUMAError uma_error;
127 switch (error_code) {
128 case base::MachCreateError::ERROR_MAKE_RECEIVE_PORT:
129 uma_error = BrokerUMAError::ERROR_MAKE_RECEIVE_PORT;
130 break;
131 case base::MachCreateError::ERROR_SET_ATTRIBUTES:
132 uma_error = BrokerUMAError::ERROR_SET_ATTRIBUTES;
133 break;
134 case base::MachCreateError::ERROR_EXTRACT_DEST_RIGHT:
135 uma_error = BrokerUMAError::ERROR_EXTRACT_DEST_RIGHT;
136 break;
137 case base::MachCreateError::ERROR_SEND_MACH_PORT:
138 uma_error = BrokerUMAError::ERROR_SEND_MACH_PORT;
139 break;
140 }
141 ReportBrokerError(uma_error);
142 handle = PlatformHandleInTransit(
143 PlatformHandle(base::mac::ScopedMachSendRight()));
144 continue;
145 }
146
147 handle = PlatformHandleInTransit::CreateForMachPortName(intermediate_port);
148 ReportBrokerError(BrokerUMAError::SUCCESS);
149 }
150 message->SetHandles(std::move(handles));
151 }
152
ExtractPort(mach_port_t port_name,base::ProcessHandle process)153 base::mac::ScopedMachSendRight MachPortRelay::ExtractPort(
154 mach_port_t port_name,
155 base::ProcessHandle process) {
156 // No extraction necessary for MACH_PORT_NULL.
157 if (port_name == MACH_PORT_NULL)
158 return base::mac::ScopedMachSendRight();
159
160 mach_port_t task_port = port_provider_->TaskForPid(process);
161 if (task_port == MACH_PORT_NULL) {
162 ReportBrokerError(BrokerUMAError::ERROR_TASK_FOR_PID);
163 return base::mac::ScopedMachSendRight();
164 }
165
166 mach_port_t extracted_right = MACH_PORT_NULL;
167 mach_msg_type_name_t extracted_right_type;
168 kern_return_t kr =
169 mach_port_extract_right(task_port, port_name, MACH_MSG_TYPE_MOVE_SEND,
170 &extracted_right, &extracted_right_type);
171 if (kr != KERN_SUCCESS) {
172 ReportBrokerError(BrokerUMAError::ERROR_EXTRACT_SOURCE_RIGHT);
173 return base::mac::ScopedMachSendRight();
174 }
175
176 ReportBrokerError(BrokerUMAError::SUCCESS);
177 DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND),
178 extracted_right_type);
179 return base::mac::ScopedMachSendRight(extracted_right);
180 }
181
AddObserver(Observer * observer)182 void MachPortRelay::AddObserver(Observer* observer) {
183 base::AutoLock locker(observers_lock_);
184 bool inserted = observers_.insert(observer).second;
185 DCHECK(inserted);
186 }
187
RemoveObserver(Observer * observer)188 void MachPortRelay::RemoveObserver(Observer* observer) {
189 base::AutoLock locker(observers_lock_);
190 observers_.erase(observer);
191 }
192
OnReceivedTaskPort(base::ProcessHandle process)193 void MachPortRelay::OnReceivedTaskPort(base::ProcessHandle process) {
194 base::AutoLock locker(observers_lock_);
195 for (auto* observer : observers_)
196 observer->OnProcessReady(process);
197 }
198
199 } // namespace core
200 } // namespace mojo
201