• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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